home *** CD-ROM | disk | FTP | other *** search
/ rtsi.com / 2014.01.www.rtsi.com.tar / www.rtsi.com / OS9 / OSK / CMDS / memacs400_src.lzh / MEMACS400 / SRC / word.c < prev    next >
Text File  |  1996-04-25  |  17KB  |  701 lines

  1. /*
  2.  * The routines in this file implement commands that work word or a
  3.  * paragraph at a time.  There are all sorts of word mode commands.  If I
  4.  * do any sentence mode commands, they are likely to be put in this file. 
  5.  */
  6.  
  7. #include    <stdio.h>
  8. #include    "estruct.h"
  9. #include    "eproto.h"
  10. #include    "edef.h"
  11. #include    "elang.h"
  12.  
  13. /* Word wrap on n-spaces. Back-over whatever precedes the point on the current
  14.  * line and stop on the first word-break or the beginning of the line. If we
  15.  * reach the beginning of the line, jump back to the end of the word and start
  16.  * a new line.    Otherwise, break the line at the word-break, eat it, and jump
  17.  * back to the end of the word. Make sure we force the display back to the
  18.  * left edge of the current window
  19.  * Returns TRUE on success, FALSE on errors.
  20.  */
  21. PASCAL NEAR wrapword(f, n)
  22.  
  23. int f;        /* default flag */
  24. int n;        /* numeric argument */
  25.  
  26. {
  27.     register int cnt;    /* size of word wrapped to next line */
  28.     register int c;        /* charector temporary */
  29.  
  30.     /* backup from the <NL> 1 char */
  31.     if (!backchar(FALSE, 1))
  32.         return(FALSE);
  33.  
  34.     /* back up until we aren't in a word,
  35.        make sure there is a break in the line */
  36.     cnt = 0;
  37.     while (((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ')
  38.                 && (c != '\t')) {
  39.         cnt++;
  40.         if (!backchar(FALSE, 1))
  41.             return(FALSE);
  42.         /* if we make it to the beginning, start a new line */
  43.         if (curwp->w_doto == 0) {
  44.             gotoeol(FALSE, 0);
  45.             return(lnewline());
  46.         }
  47.     }
  48.  
  49.     /* delete the forward white space */
  50.     if (!forwdel(0, 1))
  51.         return(FALSE);
  52.  
  53.     /* put in a end of line */
  54.     if (!lnewline())
  55.         return(FALSE);
  56.  
  57.     /* and past the first word */
  58.     while (cnt-- > 0) {
  59.         if (forwchar(FALSE, 1) == FALSE)
  60.             return(FALSE);
  61.     }
  62.  
  63.     /* make sure the display is not horizontally scrolled */
  64.     if (curwp->w_fcol != 0) {
  65.         curwp->w_fcol = 0;
  66.         curwp->w_flag |= WFHARD | WFMOVE | WFMODE;
  67.     }
  68.  
  69.     return(TRUE);
  70. }
  71.  
  72. /*
  73.  * Move the cursor backward by "n" words. All of the details of motion are
  74.  * performed by the "backchar" and "forwchar" routines. Error if you try to
  75.  * move beyond the buffers.
  76.  */
  77. PASCAL NEAR backword(f, n)
  78.  
  79. int f,n;    /* prefix flag and argument */
  80.  
  81. {
  82.     if (n < 0)
  83.         return(forwword(f, -n));
  84.     if (backchar(FALSE, 1) == FALSE)
  85.         return(FALSE);
  86.     while (n--) {
  87.         while (inword() == FALSE) {
  88.             if (backchar(FALSE, 1) == FALSE)
  89.                 return(FALSE);
  90.         }
  91.         while (inword() != FALSE) {
  92.             if (backchar(FALSE, 1) == FALSE)
  93.                 return(FALSE);
  94.         }
  95.     }
  96.     return(forwchar(FALSE, 1));
  97. }
  98.  
  99. /*
  100.  * Move the cursor forward by the specified number of words. All of the motion
  101.  * is done by "forwchar". Error if you try and move beyond the buffer's end.
  102.  */
  103. PASCAL NEAR forwword(f, n)
  104.  
  105. int f,n;    /* prefix flag and argument */
  106.  
  107. {
  108.     if (n < 0)
  109.         return(backword(f, -n));
  110.     while (n--) {
  111.         /* scan through the current word */
  112.         while (inword() == TRUE) {
  113.             if (forwchar(FALSE, 1) == FALSE)
  114.                 return(FALSE);
  115.         }
  116.  
  117.         /* scan through the intervening white space */
  118.         while (inword() == FALSE) {
  119.             if (forwchar(FALSE, 1) == FALSE)
  120.                 return(FALSE);
  121.         }
  122.     }
  123.     return(TRUE);
  124. }
  125.  
  126. /*
  127.  * Move forward to the end of the nth next word. Error if you move past
  128.  * the end of the buffer.
  129.  */
  130. PASCAL NEAR endword(f, n)
  131.  
  132. int f,n;    /* prefix flag and argument */
  133.  
  134. {
  135.     if (n < 0)
  136.         return(backword(f, -n));
  137.     while (n--) {
  138.         /* scan through the intervening white space */
  139.         while (inword() == FALSE) {
  140.             if (forwchar(FALSE, 1) == FALSE)
  141.                 return(FALSE);
  142.         }
  143.  
  144.         /* scan through the current word */
  145.         while (inword() == TRUE) {
  146.             if (forwchar(FALSE, 1) == FALSE)
  147.                 return(FALSE);
  148.         }
  149.     }
  150.     return(TRUE);
  151. }
  152.  
  153. /*
  154.  * Move the cursor forward by the specified number of words. As you move,
  155.  * convert any characters to upper case. Error if you try and move beyond the
  156.  * end of the buffer. Bound to "M-U".
  157.  */
  158. PASCAL NEAR upperword(f, n)
  159.  
  160. int f,n;    /* prefix flag and argument */
  161.  
  162. {
  163.     int c;
  164.  
  165.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  166.         return(rdonly());    /* we are in read only mode    */
  167.     if (n < 0)
  168.         return(FALSE);
  169.     while (n--) {
  170.         while (inword() == FALSE) {
  171.             if (forwchar(FALSE, 1) == FALSE)
  172.                 return(FALSE);
  173.         }
  174.         while (inword() != FALSE) {
  175.             c = lgetc(curwp->w_dotp, curwp->w_doto);
  176.             if (is_lower(c)) {
  177.                 obj.obj_char = c;
  178.                 c = upperc(c);
  179.                 lputc(curwp->w_dotp, curwp->w_doto, c);
  180.                 undo_insert(OP_REPC, 1L, obj);
  181.                 lchange(WFHARD);
  182.             }
  183.             if (forwchar(FALSE, 1) == FALSE)
  184.                 return(FALSE);
  185.         }
  186.     }
  187.     return(TRUE);
  188. }
  189.  
  190. /*
  191.  * Move the cursor forward by the specified number of words. As you move
  192.  * convert characters to lower case. Error if you try and move over the end of
  193.  * the buffer. Bound to "M-L".
  194.  */
  195. PASCAL NEAR lowerword(f, n)
  196.  
  197. int f,n;    /* prefix flag and argument */
  198.  
  199. {
  200.     int c;
  201.  
  202.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  203.         return(rdonly());    /* we are in read only mode    */
  204.     if (n < 0)
  205.         return(FALSE);
  206.     while (n--) {
  207.         while (inword() == FALSE) {
  208.             if (forwchar(FALSE, 1) == FALSE)
  209.                 return(FALSE);
  210.         }
  211.         while (inword() != FALSE) {
  212.             c = lgetc(curwp->w_dotp, curwp->w_doto);
  213.             if (is_upper(c)) {
  214.                 obj.obj_char = c;
  215.                 c = lowerc(c);
  216.                 lputc(curwp->w_dotp, curwp->w_doto, c);
  217.                 undo_insert(OP_REPC, 1L, obj);
  218.                 lchange(WFHARD);
  219.             }
  220.             if (forwchar(FALSE, 1) == FALSE)
  221.                 return(FALSE);
  222.         }
  223.     }
  224.     return(TRUE);
  225. }
  226.  
  227. /*
  228.  * Move the cursor forward by the specified number of words. As you move
  229.  * convert the first character of the word to upper case, and subsequent
  230.  * characters to lower case. Error if you try and move past the end of the
  231.  * buffer. Bound to "M-C".
  232.  */
  233. PASCAL NEAR capword(f, n)
  234.  
  235. int f,n;    /* prefix flag and argument */
  236.  
  237. {
  238.     int c;
  239.  
  240.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  241.         return(rdonly());    /* we are in read only mode    */
  242.     if (n < 0)
  243.         return(FALSE);
  244.     while (n--) {
  245.         while (inword() == FALSE) {
  246.             if (forwchar(FALSE, 1) == FALSE)
  247.                 return(FALSE);
  248.         }
  249.         if (inword() != FALSE) {
  250.             c = lgetc(curwp->w_dotp, curwp->w_doto);
  251.             if (is_lower(c)) {
  252.                 obj.obj_char = c;
  253.                 c = upperc(c);
  254.                 lputc(curwp->w_dotp, curwp->w_doto, c);
  255.                 undo_insert(OP_REPC, 1L, obj);
  256.                 lchange(WFHARD);
  257.             }
  258.             if (forwchar(FALSE, 1) == FALSE)
  259.                 return(FALSE);
  260.             while (inword() != FALSE) {
  261.                 c = lgetc(curwp->w_dotp, curwp->w_doto);
  262.                 if (is_upper(c)) {
  263.                     obj.obj_char = c;
  264.                     c = lowerc(c);
  265.                     lputc(curwp->w_dotp, curwp->w_doto, c);
  266.                     undo_insert(OP_REPC, 1L, obj);
  267.                     lchange(WFHARD);
  268.                 }
  269.                 if (forwchar(FALSE, 1) == FALSE)
  270.                     return(FALSE);
  271.             }
  272.         }
  273.     }
  274.     return(TRUE);
  275. }
  276.  
  277. /*
  278.  * Kill forward by "n" words. Remember the location of dot. Move forward by
  279.  * the right number of words. Put dot back where it was and issue the kill
  280.  * command for the right number of characters. With a zero argument, just
  281.  * kill one word and no whitespace. Bound to "M-D".
  282.  */
  283. PASCAL NEAR delfword(f, n)
  284.  
  285. int f,n;    /* prefix flag and argument */
  286.  
  287. {
  288.     register LINE    *dotp;    /* original cursor line */
  289.     register int    doto;    /*    and row */
  290.     register int c;        /* temp char */
  291.     long size;        /* # of chars to delete */
  292.  
  293.     /* don't allow this command if we are in read only mode */
  294.     if (curbp->b_mode&MDVIEW)
  295.         return(rdonly());
  296.  
  297.     /* ignore the command if there is a negative argument */
  298.     if (n < 0)
  299.         return(FALSE);
  300.  
  301.     /* Clear the kill buffer if last command wasn't a kill */
  302.     if ((lastflag&CFKILL) == 0)
  303.         next_kill();
  304.     thisflag |= CFKILL;    /* this command is a kill */
  305.  
  306.     /* save the current cursor position */
  307.     dotp = curwp->w_dotp;
  308.     doto = curwp->w_doto;
  309.  
  310.     /* figure out how many characters to give the axe */
  311.     size = 0;
  312.  
  313.     /* get us into a word.... */
  314.     while (inword() == FALSE) {
  315.         if (forwchar(FALSE, 1) == FALSE)
  316.             return(FALSE);
  317.         ++size;
  318.     }
  319.  
  320.     if (n == 0) {
  321.         /* skip one word, no whitespace! */
  322.         while (inword() == TRUE) {
  323.             if (forwchar(FALSE, 1) == FALSE)
  324.                 return(FALSE);
  325.             ++size;
  326.         }
  327.     } else {
  328.         /* skip n words.... */
  329.         while (n--) {
  330.     
  331.             /* if we are at EOL; skip to the beginning of the next */
  332.             while (curwp->w_doto == lused(curwp->w_dotp)) {
  333.                 if (forwchar(FALSE, 1) == FALSE)
  334.                     return(FALSE);
  335.                 ++size;
  336.             }
  337.     
  338.             /* move forward till we are at the end of the word */
  339.             while (inword() == TRUE) {
  340.                 if (forwchar(FALSE, 1) == FALSE)
  341.                     return(FALSE);
  342.                 ++size;
  343.             }
  344.     
  345.             /* if there are more words, skip the interword stuff */
  346.             if (n != 0)
  347.                 while (inword() == FALSE) {
  348.                     if (forwchar(FALSE, 1) == FALSE)
  349.                         return(FALSE);
  350.                     ++size;
  351.                 }
  352.         }
  353.     
  354.         /* skip whitespace and newlines */
  355.         while ((curwp->w_doto == lused(curwp->w_dotp)) ||
  356.             ((c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ') ||
  357.             (c == '\t')) {
  358.                 if (forwchar(FALSE, 1) == FALSE)
  359.                     break;
  360.                 ++size;
  361.         }
  362.     }
  363.  
  364.     /* restore the original position and delete the words */
  365.     curwp->w_dotp = dotp;
  366.     curwp->w_doto = doto;
  367.     return(ldelete(size, TRUE));
  368. }
  369.  
  370. /*
  371.  * Kill backwards by "n" words. Move backwards by the desired number of words,
  372.  * counting the characters. When dot is finally moved to its resting place,
  373.  * fire off the kill command. Bound to "M-Rubout" and to "M-Backspace".
  374.  */
  375. PASCAL NEAR delbword(f, n)
  376.  
  377. int f,n;    /* prefix flag and argument */
  378.  
  379. {
  380.     long size;
  381.  
  382.     /* don't allow this command if we are in read only mode */
  383.     if (curbp->b_mode&MDVIEW)
  384.         return(rdonly());
  385.  
  386.     /* ignore the command if there is a nonpositive argument */
  387.     if (n <= 0)
  388.         return(FALSE);
  389.  
  390.     /* Clear the kill buffer if last command wasn't a kill */
  391.     if ((lastflag&CFKILL) == 0)
  392.         next_kill();
  393.     thisflag |= CFKILL;    /* this command is a kill */
  394.  
  395.     /* make sure the cursor gets back to the right place on an undo */
  396.     undo_insert(OP_CPOS, 0L, obj);
  397.  
  398.     /* backup for the deletion */
  399.     if (backchar(FALSE, 1) == FALSE)
  400.         return(FALSE);
  401.     size = 0;
  402.     while (n--) {
  403.         while (inword() == FALSE) {
  404.             if (backchar(FALSE, 1) == FALSE)
  405.                 return(FALSE);
  406.             ++size;
  407.         }
  408.         while (inword() != FALSE) {
  409.             ++size;
  410.             if (backchar(FALSE, 1) == FALSE)
  411.                 goto bckdel;
  412.         }
  413.     }
  414.     if (forwchar(FALSE, 1) == FALSE)
  415.         return(FALSE);
  416. bckdel:    if (forwchar(FALSE, size) == FALSE)
  417.         return(FALSE);
  418.     return(ldelete(-size, TRUE));
  419. }
  420.  
  421. /*
  422.  * Return TRUE if the character at dot is a character that is considered to be
  423.  * part of a word. The default word character list is hard coded. If $wchars
  424.  * has been set by the user, use that instead
  425.  */
  426.  
  427. int PASCAL NEAR inword()
  428.  
  429. {
  430.     /* the end of a line is never in a word */
  431.     if (curwp->w_doto == lused(curwp->w_dotp))
  432.         return(FALSE);
  433.  
  434.     /* grab the word to check */
  435.     return(isinword(lgetc(curwp->w_dotp, curwp->w_doto)));
  436. }
  437.  
  438. int PASCAL NEAR isinword(c)
  439.  
  440. char c;
  441.  
  442. {
  443.     /* if we are using the table.... */
  444.     if (wlflag)
  445.         return(wordlist[c]);
  446.  
  447.     /* else use the default hard coded check */
  448.     if (is_letter(c))
  449.         return(TRUE);
  450.     if (c>='0' && c<='9')
  451.         return(TRUE);
  452.     if (c == '_')
  453.         return(TRUE);
  454.     return(FALSE);
  455. }
  456.  
  457. PASCAL NEAR fillpara(f, n)    /* Fill the current paragraph according to the
  458.                current fill column */
  459.  
  460. int f, n;    /* Default flag and Numeric argument */
  461.  
  462. {
  463.     register char *pp;    /* ptr into paragraph being reformed */
  464.     register char *para;    /* malloced buffer for paragraph */
  465.     register LINE *lp;    /* ptr to current line */
  466.     register int line_bytes;/* bytes in current line */
  467.     register char *txtptr;    /* ptr into current line */
  468.     LINE *ptline;        /* line the point started on */
  469.     int ptoff;        /* offset of original point */
  470.     int back;        /* # of characters from origin point to eop */
  471.     int status;        /* return status from linstr() */
  472.     int psize;        /* byte size of paragraph */
  473.     LINE *bop;        /* ptr to beg of paragraph */
  474.     LINE *eop;        /* pointer to line just past EOP */
  475.  
  476.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  477.         return(rdonly());    /* we are in read only mode    */
  478.     if (fillcol == 0) {    /* no fill column set */
  479.         mlwrite(TEXT98);
  480. /*                      "No fill column set" */
  481.         return(FALSE);
  482.     }
  483.  
  484.     /* save the original point */
  485.     ptline = curwp->w_dotp;
  486.     ptoff = curwp->w_doto;
  487.  
  488.     /* record the pointer to the line just past the EOP */
  489.     gotoeop(FALSE, 1);
  490.     eop = lforw(curwp->w_dotp);
  491.  
  492.     /* and back top the beginning of the paragraph */
  493.     gotobop(FALSE, 1);
  494.     bop = lp = curwp->w_dotp;
  495.  
  496.     /* ok, how big is this paragraph? */
  497.     psize = 0;
  498.     while (lp != eop) {
  499.         psize += lused(lp) + 1;
  500.         lp = lforw(lp);
  501.     }
  502.  
  503.     /* must have size! */
  504.     if (psize == 0)
  505.         return(TRUE);
  506.  
  507.     /* create a buffer to hold this stuff */
  508.     para = room(psize + 10);    /* this probably could be + 1 */
  509.     if (para == NULL) {
  510.         mlabort(TEXT94);
  511. /*                      "%%Out of memory" */
  512.         return(FALSE);
  513.     }
  514.  
  515.     /* now, grab all the text into a string */
  516.     back = 0;    /* counting the distance to backup when done */
  517.     lp = bop;
  518.     pp = para;
  519.     while (lp != eop) {
  520.         line_bytes = lused(lp);
  521.         if (back == 0) {
  522.             if (lp == ptline)
  523.                 back = line_bytes - ptoff + 1;
  524.         } else
  525.             back += line_bytes + 1;
  526.         txtptr = ltext(lp);
  527.         while (line_bytes--)            /* copy a line */
  528.             *pp++ = *txtptr++;
  529.         *pp++ = ' ';            /* turn the NL to a space */
  530.         lp = lforw(lp);
  531.         lfree(lback(lp));        /* free the old line */
  532.     }
  533.     *(--pp) = 0;    /* truncate the last space */
  534.  
  535.     /* reformat the paragraph in the buffer */
  536.     reform(para);
  537.  
  538.     /* insert the reformatted paragraph back into the current buffer */
  539.     status = linstr(para);
  540.     lnewline();        /* add the last newline to our paragraph */
  541.     if (status == TRUE)    /* reposition us to the same place */
  542.         status = backchar(FALSE, back);
  543.  
  544.     /* make sure the display is not horizontally scrolled */
  545.     if (curwp->w_fcol != 0) {
  546.         curwp->w_fcol = 0;
  547.         curwp->w_flag |= WFHARD | WFMOVE | WFMODE;
  548.     }
  549.  
  550.     /* free the buffer and return */
  551.     free(para);
  552.     return(status);
  553. }
  554.  
  555. VOID PASCAL NEAR reform(para)    /* reformat a paragraph as stored in a string */
  556.  
  557. char *para;    /* string buffer containing paragraph */
  558.  
  559. {
  560.     register char *sp;        /* string scan pointer */
  561.     register int col;        /* current colomn position */
  562.     register char *lastword;    /* ptr to end of last word */
  563.  
  564.     /* scan string, replacing some whitespace with newlines */
  565.     sp = para;
  566.     lastword = para;
  567.     col = 0;
  568.     while (*sp) {
  569.         /* if we are at white space.... */
  570.         if ((*sp == ' ') || (*sp == '\t')) {
  571.             if (*sp == '\t')
  572.                 col = (col + 8) & (~7);
  573.             else
  574.                 col++;
  575.  
  576.             /* break on whitespace? */
  577.             if (col > fillcol) {
  578.                 *sp = '\r';
  579.                 col = 0;
  580.             }
  581.  
  582.             /* onward, resetting the most recent begin of word */
  583.             ++sp;
  584.             lastword = sp;
  585.  
  586.         } else {    /* a non-blank to process */
  587.  
  588.             ++sp;
  589.             ++col;
  590.             if (col > fillcol) {
  591.                 /* line break here! */
  592.                 if ((lastword > para) &&
  593.                    (*(lastword - 1) != '\r')) {
  594.                        *(lastword - 1) = '\r';
  595.                        sp = lastword;
  596.                        col = 0;
  597.                 }
  598.             }
  599.         }
  600.     }
  601. }
  602.  
  603. PASCAL NEAR killpara(f, n)    /* delete n paragraphs starting with the current one */
  604.  
  605. int f;    /* default flag */
  606. int n;    /* # of paras to delete */
  607.  
  608. {
  609.     register int status;    /* returned status of functions */
  610.  
  611.     while (n--) {        /* for each paragraph to delete */
  612.  
  613.         /* mark out the end and beginning of the para to delete */
  614.         gotoeop(FALSE, 1);
  615.  
  616.         /* set the mark here */
  617.         curwp->w_markp[0] = curwp->w_dotp;
  618.         curwp->w_marko[0] = curwp->w_doto;
  619.  
  620.         /* go to the beginning of the paragraph */
  621.         gotobop(FALSE, 1);
  622.         curwp->w_doto = 0;    /* force us to the beginning of line */
  623.     
  624.         /* and delete it */
  625.         if ((status = killregion(FALSE, 1)) != TRUE)
  626.             return(status);
  627.  
  628.         /* and clean up the 2 extra lines */
  629.         ldelete(2L, TRUE);
  630.     }
  631.     return(TRUE);
  632. }
  633.  
  634. /*    wordcount:    count the # of words in the marked region,
  635.             along with average word sizes, # of chars, etc,
  636.             and report on them.            */
  637.  
  638. PASCAL NEAR wordcount(f, n)
  639.  
  640. int f, n;    /* ignored numeric arguments */
  641.  
  642. {
  643.     register LINE *lp;    /* current line to scan */
  644.     register int offset;    /* current char to scan */
  645.     long size;        /* size of region left to count */
  646.     register int ch;    /* current character to scan */
  647.     register int wordflag;    /* are we in a word now? */
  648.     register int lastword;    /* were we just in a word? */
  649.     long nwords;        /* total # of words */
  650.     long nchars;        /* total number of chars */
  651.     int nlines;        /* total number of lines in region */
  652.     int avgch;        /* average number of chars/word */
  653.     int status;        /* status return code */
  654.     REGION region;        /* region to look at */
  655.  
  656.     /* make sure we have a region to count */
  657.     if ((status = getregion(®ion)) != TRUE)
  658.         return(status);
  659.     lp = region.r_linep;
  660.     offset = region.r_offset;
  661.     size = region.r_size;
  662.  
  663.     /* count up things */
  664.     lastword = FALSE;
  665.     nchars = 0L;
  666.     nwords = 0L;
  667.     nlines = 0;
  668.     while (size--) {
  669.  
  670.         /* get the current character */
  671.         if (offset == lused(lp)) {    /* end of line */
  672.             ch = '\r';
  673.             lp = lforw(lp);
  674.             offset = 0;
  675.             ++nlines;
  676.         } else {
  677.             ch = lgetc(lp, offset);
  678.             ++offset;
  679.         }
  680.  
  681.         /* and tabulate it */
  682.         wordflag = (is_letter(ch) ||
  683.                 (ch >= '0' && ch <= '9'));
  684.         if (wordflag == TRUE && lastword == FALSE)
  685.             ++nwords;
  686.         lastword = wordflag;
  687.         ++nchars;
  688.     }
  689.  
  690.     /* and report on the info */
  691.     if (nwords > 0L)
  692.         avgch = (int)((100L * nchars) / nwords);
  693.     else
  694.         avgch = 0;
  695.  
  696.     mlwrite(TEXT100,
  697. /*              "Words %D Chars %D Lines %d Avg chars/word %f" */
  698.         nwords, nchars, nlines + 1, avgch);
  699.     return(TRUE);
  700. }
  701.