www.pudn.com > commutil.zip > FCONVERT.CPP


// ******************************************************************** // 
//                                                                      // 
//      FCONVERT.CPP                                                    // 
//      Copyright (c) 1993, Michael Holmes and Bob Flanders             // 
//      C++ Communication Utilities                                     // 
//                                                                      // 
//      This file contains the routines for converting to and           // 
//      from the G3 fax format.                                         // 
//                                                                      // 
// ******************************************************************** // 
 
 
// 
//  Conversion Routine Globals 
// 
 
int     f_handle = -1,                      // G3 file handle 
        f_pgcnt,                            // maximum page count 
        f_write_flag,                       // write_out bit buffer flag 
        f_write_cnt;                        // write_out global counter 
 
char   *f_buffer,                           // work buffer 
       *f_ptr,                              // ..and current pointer 
        f_filename[MAX_PATH];               // fax filename 
 
long    f_page;                             // current G3 page size 
 
#define HDR_LEN     128                     // header length 
#define BUF_SIZE    256                     // buffer size 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_locate() -- find a G3 formatted file and check its integrity 
 * 
 *  returns: 0 = file opened 
 *           1 = file not found 
 * 
 * ******************************************************************** */ 
 
int     f_locate(char *s)                   // source filename 
{ 
long    file_size,                          // file size 
        pos;                                // current file position 
 
 
if (f_handle != -1)                         // q. already open? 
    { 
    close(f_handle);                        // a. yes .. close file 
    f_handle = -1;                          // ..and clear flag 
    } 
 
if (NOT first_nonblank(s))                  // q. empty string? 
    return(1);                              // a. yes .. just return 
 
if ((f_handle = open(s,                     // q. find and open file ok? 
            O_RDONLY | O_BINARY)) == -1) 
    return(1);                              // a. no .. return w/error 
 
file_size = lseek(f_handle, 0L, SEEK_END);  // get file size 
lseek(f_handle, 0L, SEEK_SET);              // ..and back to file start 
 
if ((read(f_handle, page, 128) != 128) ||   // q. file header available 
        strncmp((char *) page, "G3", 2))    // ..and our format? 
    { 
    close(f_handle);                        // a. no .. just close file 
    f_handle = -1;                          // ..clear flag 
    return(1);                              // ..and return to caller 
    } 
 
pos = lseek(f_handle, 0L, SEEK_CUR);        // get starting position 
 
for (f_pgcnt = 0;;)                         // count available fax pages 
    { 
    if (read(f_handle, (char *) &f_page,    // q. read in enough to cover 
            sizeof(long)) != sizeof(long))  // ..the page length field? 
        break;                              // a. no .. exit loop 
 
    if ((f_page < 1) ||                     // q. page size non-positive? 
        ((f_page + pos + 4) > file_size))   // ..or bigger than the file? 
        break;                              // a. yes .. exit loop 
 
    if ((pos = lseek(f_handle, f_page,      // q. properly position to the 
            SEEK_CUR)) == -1L)              // ..start of the next page? 
        break;                              // a. no .. exit loop 
 
    f_pgcnt++;                              // count this page 
    } 
 
strcpy(f_filename, s);                      // save filename .. 
return(0);                                  // ..then return all ok 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_next_byte() -- get the next byte from the G3 file 
 * 
 *  returns: -1 = end of data or error 
 *            n = next byte 
 * 
 * ******************************************************************** */ 
 
int     f_get_next_byte(void) 
{ 
static 
int     rb;                                 // remaining buffer count 
 
 
if (f_ptr == 0 || rb == 0)                  // q. 1st call or out of data? 
    {                                       // a. yes .. get the next blk 
    if (f_page == 0)                        // q. out of data to read? 
        return(-1);                         // a. yes .. return all done 
 
    if (f_buffer == 0)                      // q. buffer allocated? 
        f_buffer = (char *)                 // a. no .. get a buffer 
                 malloc_chk(BUF_SIZE); 
 
    rb = (int)((f_page > BUF_SIZE) ?        // set count to smaller of 
                BUF_SIZE : f_page);         // ..what's left or buffer size 
 
    if (read(f_handle, f_buffer, rb) != rb) // q. file read ok? 
        quit_with(read_error);              // a. no .. give error message 
 
    f_page -= rb;                           // deduct what was read 
    f_ptr = f_buffer;                       // set up character pointer 
    } 
 
rb--;                                       // decriment remaining count 
return(*(unsigned char *) f_ptr++);         // ..and return character 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_byte() -- get the next value from the G3 file handling DLEs 
 * 
 *  returns: -1 = end of data or error 
 *            n = next byte 
 * 
 * ******************************************************************** */ 
 
int     f_get_byte(void) 
{ 
int     v;                                  // work value 
 
 
while (1) 
    { 
    if ((v = f_get_next_byte()) == -1)      // q. out of data? 
        return(-1);                         // a. yes .. return EOF 
 
    if (v == DLE)                           // q. DLE character? 
        {                                   // a. yes .. get another 
        if ((v = f_get_next_byte()) == -1)  // q. get another char ok? 
            return(-1);                     // a. no .. return EOF 
 
        if (v == DLE)                       // q. another DLE? 
            return(v);                      // a. yes .. return the goods 
        } 
     else 
        return(v);                          // else .. return w/character 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_bit() -- get the next bit from the G3 file 
 * 
 *  returns: -1 = end of data or error 
 *            0 = zero bit found 
 *            1 = one bit found 
 * 
 * ******************************************************************** */ 
 
int     f_get_bit(void) 
{ 
static 
int     w,                                  // work byte 
        c = 0;                              // bit count 
 
 
w >>= 1;                                    // move next bit into place 
 
if (f_ptr == 0 || c == 0)                   // q. 1st call or out of data? 
    {                                       // a. yes .. get a byte 
    if ((w = f_get_byte()) == -1)           // q. out of data? 
        return(-1);                         // a. yes .. return EOF 
 
    c = 8;                                  // number of available bits 
    } 
 
c--;                                        // show another one used 
return(w & 1);                              // ..then rtn a bit to caller 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_search() -- search for a codeword match 
 * 
 *  returns: -2 = end of line or error 
 *           -1 = no match found 
 *            n = match found, n is nbr of bits in codeword 
 * 
 * ******************************************************************** */ 
 
int     f_search(int type,                  // type of code 
                                            //  0 = white space 
                                            //  1 = black space 
                 int l,                     // length in bits 
                 int a)                     // accumulator 
{ 
struct  code_entry *e;                      // codeword table entries 
 
 
if (l == 12 && a == 1)                      // q. end of line found? 
    return(-2);                             // a. yes .. return to caller 
 
if ((e = code_table[l - 1][type]) != 0)     // q. table entry found? 
    {                                       // a. yes .. check table 
    for (; e->code; e++)                    // scan a codeword table 
        if (e->code == a)                   // q. find a codeword match? 
            return(e->value);               // a. yes .. return 
    } 
 
if (l == 13)                                // q. reach max codeword size? 
    return(-2);                             // a. yes .. return w/error 
 else 
    return(-1);                             // else .. rtn nothing found 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_code() -- get the next codeword from the G3 stream 
 * 
 *  returns: 0 = converted next word ok 
 *           1 = error or end of line 
 * 
 * ******************************************************************** */ 
 
int     f_get_code(int type,                // type of code 
                                            //  0 = white space 
                                            //  1 = black space 
                   int *len)                // length in bits 
{ 
int     a = 0,                              // accumulator 
        b,                                  // one bit work area 
        l = 0;                              // working length 
 
 
while (1) 
    { 
    if ((b = f_get_bit()) == -1)            // q. get a bit successfully? 
        return(1);                          // a. no .. must be out of data 
 
    a = (a << 1) | b;                       // shift in a new bit 
    l++;                                    // ..tally up bits received 
 
    switch (*len = f_search(type, l, a))    // check for match 
        { 
        case -2:                            // end of line or error 
            return(1);                      // ..just return 
 
        case -1:                            // nothing found 
            break;                          // ..continue looping 
 
        default:                            // found codeword 
            return(0);                      // ..return ok 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_pels() -- get the next group of PELS from the G3 stream 
 * 
 *  returns: 0 = converted next word ok 
 *           1 = error or end of line 
 * 
 * ******************************************************************** */ 
 
int     f_get_pels(int type,                // type of code 
                                            //  0 = white space 
                                            //  1 = black space 
                   int *len)                // length in bits 
{ 
int     a = 0;                              // accumulator 
 
 
while (1) 
    { 
    if (f_get_code(type, len))              // q. get a code word? 
        return(1);                          // a. no .. must be out of data 
 
    if (*len > 63)                          // q. get a makeup code? 
        a = *len;                           // a. yes .. save for later 
     else 
        { 
        *len += a;                          // else .. add in any makeups 
        return(0);                          // ..and return all ok 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_get_eol() -- read to end of line in current page 
 * 
 * ******************************************************************** */ 
 
void    f_get_eol(void) 
{ 
int     a,                                  // accumulator 
        b;                                  // work bit 
 
 
for (a = 0xfff; a != 1;)                    // try to find an EOL bit 
    {                                       // ..pattern of 000000000001 
    if ((b = f_get_bit()) == -1)            // q. get a bit successfully? 
        return;                             // a. no .. must be out of data 
 
    a = ((a << 1) | b) & 0xfff;             // move in new bit 
    } 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_read_g3() -- read a page and convert from G3 format 
 * 
 *  returns: 0 = page converted sucessfully 
 *           1 = page not found or error 
 * 
 * ******************************************************************** */ 
 
int     f_read_g3(int n)                    // page number 
{ 
int     pels,                               // pixels counter 
        lines,                              // lines counter 
        type,                               // type of codeword 
        codes,                              // codewords processed 
        eol,                                // consecutive EOLs found 
        l = 0;                              // work length 
char    huge *p;                            // bitmap line pointer 
 
 
lseek(f_handle, HDR_LEN, SEEK_SET);         // get to start of 1st page 
 
while(1)                                    // loop to find requested page 
    { 
    if (read(f_handle, (char *) &f_page,    // q. read in enough to cover 
            sizeof(long)) != sizeof(long))  // ..the page length field? 
        return(1);                          // a. no .. return not found 
 
    if (--n == 0)                           // q. find target page? 
        break;                              // a. yes .. exit loop 
 
    if (lseek(f_handle, f_page,             // q. properly position to the 
            SEEK_CUR) == -1L)               // ..start of the next page? 
        return(1);                          // a. no .. return not found 
    } 
 
f_ptr = 0;                                  // set global pointer 
f_get_eol();                                // find end of first line 
 
for (lines = 0, p = page;                   // for the whole bitmap 
            lines < LINES;                  // ..run through line 
            lines++, p += LINE)             // ..by line 
    memset(p, 0, LINE);                     // ..and clear white space 
 
for (lines = 0, p = page, eol = 0;          // fill in each line in the 
            lines < LINES;                  // ..bitmap page by processing 
            lines++, p += LINE)             // ..one line at a time 
    { 
    for (pels = 0, type = 0, codes = 0;     // for each bit available 
                pels < PELS;                // ..in the bitmap line get 
                pels += l, codes++)         // ..codewords and convert 
        { 
        if (f_get_pels(type, &l))           // q. end of line found? 
            break;                          // a. yes .. exit loop 
 
        if (l > (PELS - pels))              // q. too much data? 
            l = PELS - pels;                // a. yes .. set to max 
 
        if (type)                           // q. black bits? 
            set_bits((char *) p, pels, l);  // a. yes .. set bits on 
 
        type = (type + 1) & 1;              // switch back and forth 
        }                                   // ..between black and white 
 
    f_get_eol();                            // find the end of this line 
 
    if (NOT codes)                          // q. empty line? 
        { 
        eol++;                              // a. yes .. tally empty lines 
 
        if (eol == 6)                       // q. find the end of page? 
            break;                          // a. yes .. finish looping 
        } 
 
     else 
        eol = 0;                            // else .. clear EOL counter 
    } 
 
return(0);                                  // finally, return ok 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_write_hdr() -- write a fax file header 
 * 
 * ******************************************************************** */ 
 
void    f_write_hdr(void) 
{ 
char    buf[HDR_LEN];                       // header buffer 
 
 
memset(buf, 0, HDR_LEN);                    // clear buffer to nulls 
strcpy(buf, "G3");                          // ..and put in our marker 
write(f_handle, buf, HDR_LEN);              // write header 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_write_out() -- write bits to output fax file 
 * 
 * ******************************************************************** */ 
 
void    f_write_out(int bits,               // output bits 
                    int len)                // ..and number of bits 
{ 
static  unsigned 
char    w;                                  // byte wide queue 
 
 
if (len > 16)                               // q. too many bits? 
    return;                                 // a. yes .. just return 
 
bits <<= 16 - len;                          // shift to MSB end of word 
 
while (len--)                               // while there is input 
    { 
    w >>= 1;                                // shift queue over by one 
 
    if (bits & 0x8000)                      // q. source bit on? 
        w |= 0x80;                          // a. yes .. turn on dest 
 
    if (++f_write_flag == 8)                // q. reach limit? 
        { 
        write(f_handle, (void *) &w, 1);    // a. yes .. write a byte 
 
        if (w == DLE)                       // q. special character? 
            write(f_handle, (void *) &w, 1);// a. yes .. write it again 
 
        f_write_flag = 0;                   // clear the flag 
        f_write_cnt++;                      // ..and tally the byte 
        } 
 
    bits <<= 1;                             // shift source by one bit 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_encode() -- encode a string of bits into G3 format 
 * 
 * ******************************************************************** */ 
 
void    f_encode(int cnt,                   // number of bits from bitmap 
                 int type)                  // ..and bit color 
{ 
struct  encode_entry *ee;                   // encode entry 
 
 
if (cnt > 63)                                   // q. big run of bits? 
    { 
    ee = &encode_table[(cnt / 64) + 63][type];  // a. yes .. get pointer 
    f_write_out(ee->code, ee->bits);            // ..write make-up code 
    cnt %= 64;                                  // ..and update bit count 
    } 
 
ee = &encode_table[cnt][type];              // get element pointer 
f_write_out(ee->code, ee->bits);            // write terminating code 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_scan_out() -- scan the bitmap and output a codeword 
 * 
 * ******************************************************************** */ 
 
int     f_scan_out(char huge *p,            // bitmap line 
                   int  i,                  // starting bit offset 
                   int  type)               // type to search 
{ 
int     cnt;                                // work counter 
 
 
for (cnt = 0; i < PELS; i++, cnt++)         // scan bitmap line 
    if (get_bit(                            // q. find a bit which is not 
            (unsigned char *) p, i) != type)// ..the same as type? 
        break;                              // a. yes .. exit loop 
 
f_encode(cnt, type);                        // build/output codeword 
return(cnt);                                // ..then return with count 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_write_page() -- write a fax file page 
 * 
 * ******************************************************************** */ 
 
void    f_write_page(Window *w)             // window to display msgs in 
{ 
int     i, j = 0,                           // loop counter 
        line,                               // line counter 
        type;                               // pixel type 
char    buf[60],                            // work buffer 
        huge *p;                            // ..and pointer 
long    start_pos,                          // page starting position 
        page_len;                           // ..and page length 
static 
long    percent,                            // percent complete 
        last,                               // last display time 
        far *timer = (long far *)           // BIOS timer tick counter 
                        MK_FP(0x40, 0x6c);  // ..down in low memory 
 
 
lseek(f_handle, 0L, SEEK_END);              // goto end of file 
start_pos = tell(f_handle);                 // ..and get position info 
 
memset(buf, 0, sizeof(buf));                // clear buffer to nulls 
write(f_handle, buf, 4);                    // write page length 
write(f_handle, buf, 10);                   // ..and some nulls 
 
f_write_flag = 0;                           // clear output flag 
f_write_out(1, 12);                         // ..and write an EOL 
 
for (p = page, line = 0; line < LINES;      // for each line in the bitmap 
            line++, p += LINE)              // ..encode into G3 format 
    { 
    f_write_cnt = 0;                        // clear output byte count 
 
    if ((*timer - last) > 9)                // q. has half a second past? 
        { 
        percent = (line * 100L) / LINES;    // a. yes .. get percentage 
        sprintf(buf, write_msg,             // build status message 
                f_pgcnt, percent);          // ..into a work buffer 
        w->Display(buf);                    // ..display the message 
        last = *timer;                      // ..and pickup current time 
        } 
 
    if (reverse_scan((char *) p, 0, LINE))  // q. anything on line? 
        {                                   // a. yes .. process 
        for (i = type = 0; i < PELS; i += j)// scan across the bitmap line 
            { 
            j = f_scan_out(p, i, type);     // scan bitmap for pixels 
 
            type = (type + 1) & 1;          // flip-flop the type code 
            } 
        } 
     else 
        f_encode(1728, 0);                  // else .. put out blank line 
 
    if (f_write_flag)                       // q. any residual bits? 
        f_write_out(0, 8 - f_write_flag);   // a. yes .. put out the rest 
 
    if (i < 40)                             // q. need to pad out line? 
        write(f_handle, (void *) &buf,      // a. yes .. write a string 
                40 - i);                    // ..of up to 40 null bytes 
 
    f_write_out(1, 12);                     // then put out an EOL 
    } 
 
for (i = 0; i < 5; i++)                     // at end of page 
    f_write_out(1, 12);                     // ..put out 5 more EOLs 
 
buf[0] = DLE;                               // set up termination 
buf[1] = ETX;                               // ..with DLE ETX 
write(f_handle, buf, 2);                    // ..then put them out 
 
page_len = tell(f_handle) - start_pos - 4;  // compute page data length 
lseek(f_handle, start_pos, SEEK_SET);       // position to length field 
write(f_handle, (void *) &page_len, 4);     // ..and update field 
 
} 
 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_build_char() -- fill in bitmap position with a character 
 * 
 * ******************************************************************** */ 
 
void    f_build_char(char cc,               // character to use 
                     int  c, int r)         // column and row 
{ 
UINT   *ce,                                 // table entry pointer 
       i;                                   // loop counter 
char   huge *p,                             // bitmap pointer 
       ch1, ch2;                            // high and low bytes 
 
 
ce = &ascii_map[cc][0];                     // get entry in table 
 
p = &page[((r * 22L) * LINE) + (c * 2)];    // get starting point 
 
for (i = 0; i < 11; i++, ce++)              // for each of the chars rows 
    { 
    if (*ce)                                // q. anything to put in bitmap? 
        {                                   // a. yes .. process bits 
        ch1 = *ce >> 8;                     // get high order byte 
        ch2 = *ce & 0xff;                   // ..and low order byte 
 
        *p |= ch1;                          // "or" in each cols bits 
        *(p + 1) |= ch2;                    // ..for both bytes 
        p += LINE;                          // move to next line 
 
        *p |= ch1;                          // "or" in each cols bits 
        *(p + 1) |= ch2;                    // ..for both bytes 
        p += LINE;                          // move to next line 
        } 
     else 
        p += LINE * 2;                      // else .. skip two bitmap lines 
    } 
 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_build_page() -- read an ASCII text file and build a bitmap 
 * 
 *  returns: 0 = end of file reached 
 *           n = characters encoded 
 * 
 * ******************************************************************** */ 
 
int     f_build_page(FILE *f)               // input file pointer 
{ 
int     i,                                  // chars processed 
        lines,                              // lines counter 
        c, r,                               // column and row (0 based) 
        cc;                                 // character buffer 
char    huge *p;                            // bitmap line pointer 
 
 
for (lines = 0, p = page;                   // for the whole bitmap 
            lines < LINES;                  // ..run through line 
            lines++, p += LINE)             // ..by line 
    memset(p, 0, LINE);                     // ..and clear white space 
 
c = r = 0;                                  // start column & row at top 
 
for (i = 0; ; i++)                          // loop building bitmap 
    { 
    if ((cc = fgetc(f)) == EOF)             // q. get a good character? 
        return(i);                          // a. no .. return w/count 
 
    f_build_char(cc, c, r);                 // bld bitmap at column & row 
 
    switch (cc)                             // update column & row 
        { 
        case LF:                            // line feed 
            if (++r >= ROWS)                // q. reach bottom limit? 
                return(i);                  // a. yes .. then page is done 
 
            break;                          // else .. get next character 
 
        case CR:                            // carriage return 
            c = 0;                          // start at next beginning 
            break;                          // ..and get next character 
 
        case 12:                            // form feed 
            return(++i);                    // return w/nbr chars processed 
 
        case BACKSPACE:                     // backspace 
            if (--c < 0)                    // q. backup too far? 
                c = 0;                      // a. yes .. goto column 0 
 
            break;                          // else .. get next character 
 
        case TAB:                           // tab 
            c = (c & ~0x7) + 8;             // move to next tab stop 
 
            if (c >= COLUMNS)               // q. too far out? 
                { 
                c = 0;                      // a. yes .. just wrap around 
 
                if (++r >= ROWS)            // q. reach bottom of page? 
                    return(i);              // a. yes .. stop here 
                } 
 
            break;                          // else .. get next character 
 
        default:                            // everything else 
            if (++c >= COLUMNS)             // q. reach right margin? 
                { 
                c = 0;                      // a. yes .. just wrap around 
 
                if (++r >= ROWS)            // q. reach bottom of page? 
                    return(i);              // a. yes .. stop here 
                } 
        } 
    } 
} 
 
 
 
/* ******************************************************************** * 
 * 
 *  f_build_fax() -- build a fax file from an ASCII text file 
 * 
 * ******************************************************************** */ 
 
void    f_build_fax(char *f,                // file name 
                    Window *w)              // window to update 
{ 
FILE   *fi;                                 // input file 
int     i;                                  // string index 
char    buf[60], buf2[60],                  // format buffers 
       *p;                                  // ..and pointer 
 
 
if (f_handle != -1)                         // q. already open? 
    { 
    close(f_handle);                        // a. yes .. close file 
    f_handle = -1;                          // ..and clear flag 
    } 
 
if ((fi = fopen(f, "rb")) == 0)             // q. open ok? 
    return;                                 // a. no .. just return 
 
strcpy(buf, f);                             // copy base filename 
 
if ((p = strrchr(buf, '.')) == 0)           // q. file extension found? 
    p = &buf[strlen(buf)];                  // a. no .. point to end 
 
strcpy(p, ".fax");                          // copy in extension 
 
if ((f_handle = open(buf,                   // q. open new file ok? 
            O_BINARY | O_TRUNC | O_RDWR | O_CREAT)) == -1) 
    return;                                 // a. no .. just return 
 
sprintf(buf2, create_msg, buf);             // build display message 
w->Display(buf2);                           // ..and send to user window 
 
if ((i = 34 - (strlen(f) +                  // q. able to center the 
        strlen(status_conv) - 2)) < 0)      // .."Converting: XXXX" msg? 
    i = 0;                                  // a. no .. flush left 
 
memset(buf2, ' ', sizeof(buf2));            // clear area to blanks 
sprintf(&buf2[i / 2], status_conv, f);      // format build information 
status_line(status, buf2);                  // update status line 
 
f_write_hdr();                              // write header information 
 
for (f_pgcnt = 1; ; f_pgcnt++)              // loop building and writing 
    { 
    if (f_build_page(fi) == 0)              // q. read ASCII ok? 
        break;                              // a. no .. end of file 
 
    f_write_page(w);                        // encode bitmap & write file 
    } 
 
fclose(fi);                                 // close input file 
close(f_handle);                            // ..and output file 
f_handle = -1;                              // ..and reset global 
 
f_locate(buf);                              // set up to view/print file 
sprintf(buf2, status_file,                  // format file information 
        buf, f_pgcnt);                      // ..for status line 
 
if (f_pgcnt > 1)                            // q. more than 1 page fax? 
    strcat(buf2, "s");                      // a. yes .. pluralize page 
 
status_line(status, buf2);                  // update status line 
 
}