home *** CD-ROM | disk | FTP | other *** search
/ Just Call Me Internet / Just Call Me Internet.iso / prog / atari / c / uemlsrc / search.c < prev    next >
Encoding:
C/C++ Source or Header  |  1987-08-24  |  25.0 KB  |  764 lines

  1. /*
  2.  * The functions in this file
  3.  * implement commands that search in the
  4.  * forward and backward directions. There are
  5.  * no special characters in the search strings.
  6.  * Probably should have a regular expression
  7.  * search, or something like that.
  8.  */
  9. #include        <stdio.h>
  10. #include        <ctype.h>
  11. #include        "ed.h"
  12.  
  13.  
  14. #define CCHR(x)  ((x)-'@')
  15.  
  16. #define SRCH_BEGIN      (0)                  /* Search sub-codes.    */
  17. #define SRCH_FORW       (-1)
  18. #define SRCH_BACK       (-2)
  19. #define SRCH_PREV       (-3)
  20. #define SRCH_NEXT       (-4)
  21. #define SRCH_NOPR       (-5)
  22. #define SRCH_ACCM       (-6)
  23. #define NSRCH   128                     /* Undoable search commands.    */
  24.  
  25. typedef struct  {
  26.         int     s_code;
  27.         LINE    *s_dotp;
  28.         int     s_doto;
  29. }       SRCHCOM;
  30.  
  31. static  SRCHCOM cmds[NSRCH];
  32. static  int     cip;
  33.  
  34. int     srch_lastdir = SRCH_NOPR;              /* Last search flags.   */
  35.  
  36. /*
  37.  * Search forward.
  38.  * Get a search string from the user, and search for it,
  39.  * starting at ".". If found, "." gets moved to just after the
  40.  * matched characters, and display does all the hard stuff.
  41.  * If not found, it just prints a message.
  42.  */
  43. forwsearch(f, n)
  44. register int f, n;
  45. {
  46.  
  47.         if ((f=readpattern("Search", &pat)) != TRUE)
  48.                 return (f);
  49.         if (forwsrch() == FALSE) {
  50.                 mlwrite("Not found");
  51.                 return (FALSE);
  52.         }
  53.         return (TRUE);
  54. }
  55.  
  56. /*
  57.  * Reverse search.
  58.  * Get a search string from the  user, and search, starting at "."
  59.  * and proceeding toward the front of the buffer. If found "." is left
  60.  * pointing at the first character of the pattern [the last character that
  61.  * was matched].
  62.  */
  63. backsearch(f, n)
  64. register int f, n;
  65. {
  66.  
  67.         if ((f=readpattern("Reverse search", &pat)) != TRUE)
  68.                 return (f);
  69.         if (backsrch() == FALSE) {
  70.                 mlwrite("Not found");
  71.                 return (FALSE);
  72.         }
  73.         return (TRUE);
  74. }
  75.  
  76.  
  77. /*
  78.  * This routine does the real work of a
  79.  * forward search. The pattern is sitting in the external
  80.  * variable "pat". If found, dot is updated, the window system
  81.  * is notified of the change, and TRUE is returned. If the
  82.  * string isn't found, FALSE is returned.
  83.  */
  84. forwsrch()
  85. {
  86.         register LINE   *clp;
  87.         register int    cbo;
  88.         register LINE   *tlp;
  89.         register int    tbo;
  90.         register char   *pp;
  91.         register int    c;
  92.  
  93.         clp = curwp->w_dotp;
  94.         cbo = curwp->w_doto;
  95.         while (clp != curbp->b_linep) {
  96.                 if (cbo == llength(clp)) {
  97.                         clp = lforw(clp);
  98.                         cbo = 0;
  99.                         c = '\n';
  100.                 } else
  101.                         c = lgetc(clp, cbo++);
  102.                 if (eq(c, pat[0]) != FALSE) {
  103.                         tlp = clp;
  104.                         tbo = cbo;
  105.                         pp  = &pat[1];
  106.                         while (*pp != 0) {
  107.                                 if (tlp == curbp->b_linep)
  108.                                         goto fail;
  109.                                 if (tbo == llength(tlp)) {
  110.                                         tlp = lforw(tlp);
  111.                                         if (tlp == curbp->b_linep)
  112.                                                 goto fail;
  113.                                         tbo = 0;
  114.                                         c = '\n';
  115.                                 } else
  116.                                         c = lgetc(tlp, tbo++);
  117.                                 if (eq(c, *pp++) == FALSE)
  118.                                         goto fail;
  119.                         }
  120.                         curwp->w_dotp  = tlp;
  121.                         curwp->w_doto  = tbo;
  122.                         curwp->w_flag |= WFMOVE;
  123.                         return (TRUE);
  124.                 }
  125.         fail:   ;
  126.         }
  127.         return (FALSE);
  128. }
  129.  
  130. /*
  131.  * This routine does the real work of a
  132.  * backward search. The pattern is sitting in the external
  133.  * variable "pat". If found, dot is updated, the window system
  134.  * is notified of the change, and TRUE is returned. If the
  135.  * string isn't found, FALSE is returned.
  136.  */
  137. backsrch()
  138. {
  139.         register LINE   *clp;
  140.         register int    cbo;
  141.         register LINE   *tlp;
  142.         register int    tbo;
  143.         register int    c;
  144.         register char   *epp;
  145.         register char   *pp;
  146.  
  147.         for (epp = &pat[0]; epp[1] != 0; ++epp)
  148.                 ;
  149.         clp = curwp->w_dotp;
  150.         cbo = curwp->w_doto;
  151.         for (;;) {
  152.                 if (cbo == 0) {
  153.                         clp = lback(clp);
  154.                         if (clp == curbp->b_linep)
  155.                                 return (FALSE);
  156.                         cbo = llength(clp)+1;
  157.                 }
  158.                 if (--cbo == llength(clp))
  159.                         c = '\n';
  160.                 else
  161.                         c = lgetc(clp,cbo);
  162.                 if (eq(c, *epp) != FALSE) {
  163.                         tlp = clp;
  164.                         tbo = cbo;
  165.                         pp  = epp;
  166.                         while (pp != &pat[0]) {
  167.                                 if (tbo == 0) {
  168.                                         tlp = lback(tlp);
  169.                                         if (tlp == curbp->b_linep)
  170.                                                 goto fail;
  171.                                         tbo = llength(tlp)+1;
  172.                                 }
  173.                                 if (--tbo == llength(tlp))
  174.                                         c = '\n';
  175.                                 else
  176.                                         c = lgetc(tlp,tbo);
  177.                                 if (eq(c, *--pp) == FALSE)
  178.                                         goto fail;
  179.                         }
  180.                         curwp->w_dotp  = tlp;
  181.                         curwp->w_doto  = tbo;
  182.                         curwp->w_flag |= WFMOVE;
  183.                         return (TRUE);
  184.                 }
  185.         fail:   ;
  186.         }
  187. }
  188.  
  189. /*
  190.  * Compare two characters.
  191.  * The "bc" comes from the buffer.
  192.  * It has it's case folded out. The
  193.  * "pc" is from the pattern.
  194.  */
  195. eq(bc, pc)
  196. register int    bc;
  197. register int    pc;
  198. {
  199.         if (bc>='a' && bc<='z')
  200.                 bc -= 0x20;
  201.         if (pc>='a' && pc<='z')
  202.                 pc -= 0x20;
  203.         if (bc == pc)
  204.                 return (TRUE);
  205.         return (FALSE);
  206. }
  207.  
  208. /*
  209.  * Read a pattern.
  210.  * Stash it in an external
  211.  * variable. The calling function
  212.  * passes the address of inpat. The "pat" is
  213.  * not updated if the user types in
  214.  * an empty line. If the user typed
  215.  * an empty line, and there is no
  216.  * old pattern, it is an error.
  217.  * Display the old pattern, in the
  218.  * style of Jeff Lomicka. There is
  219.  * some do-it-yourself control
  220.  * expansion.
  221.  */
  222. readpattern(prompt, inpat)
  223. char    *prompt;
  224. char    *inpat;
  225. {
  226.         register char   *cp1;
  227.         register char   *cp2;
  228.         register int    c;
  229.         register int    s;
  230.         char            tpat[NPAT+20];
  231.  
  232.         cp1 = &tpat[0];                         /* Copy prompt          */
  233.         cp2 = prompt;
  234.         while ((c = *cp2++) != '\0')
  235.                 *cp1++ = c;
  236.         if (inpat[0] != '\0') {                 /* Old pattern          */
  237.                 *cp1++ = ' ';
  238.                 *cp1++ = '[';
  239.                 cp2 = &inpat[0];
  240.                 while ((c = *cp2++) != 0) {
  241.                         if (cp1 < &tpat[NPAT+20-6]) {   /* "??]: \0"    */
  242.                                 if (c<0x20 || c==0x7F) {
  243.                                         *cp1++ = '^';
  244.                                         c ^= 0x40;
  245.                                 } else if (c == '%')    /* Map "%" to   */
  246.                                         *cp1++ = c;     /* "%%".        */
  247.                                 *cp1++ = c;
  248.                         }
  249.                 }
  250.                 *cp1++ = ']';
  251.         }
  252.         *cp1++ = ':';                           /* Finish prompt        */
  253.         *cp1++ = ' ';
  254.         *cp1++ = '\0';
  255.         s = mlreply(tpat, tpat, NPAT);          /* Read pattern         */
  256.         if (s == TRUE)                          /* Specified            */
  257.                 strcpy(inpat, tpat);
  258.         else if (s==FALSE && inpat[0]!=0)               /* CR, but old one */
  259.                 s = TRUE;
  260.         return (s);
  261. }
  262.  
  263. /*
  264.  * Use incremental searching, initially in the forward direction.
  265.  * isearch ignores any explicit arguments.
  266.  */
  267. forwisearch(f, n)
  268. register int f, n;
  269. {
  270.         return (isearch(SRCH_FORW));
  271. }
  272.  
  273. /*
  274.  * Use incremental searching, initially in the reverse direction.
  275.  * isearch ignores any explicit arguments.
  276.  */
  277. backisearch(f, n)
  278. register int f, n;
  279. {
  280.         return (isearch(SRCH_BACK));
  281. }
  282.  
  283. /*
  284.  * Incremental Search.
  285.  *      dir is used as the initial direction to search.
  286.  *      ^N      find next occurance  (if first thing typed reuse old string).
  287.  *      ^P      find prev occurance  (if first thing typed reuse old string).
  288.  *      ^S      switch direction to forward, find next
  289.  *      ^R      switch direction to reverse, find prev
  290.  *      ^Q      quote next character (allows searching for ^N etc.)
  291.  *      <ESC>   exit from Isearch.
  292.  *      <DEL>   undoes last character typed. (tricky job to do this correctly).
  293.  *      else    accumulate into search string
  294.  */
  295. isearch(dir)
  296. int dir;
  297. {
  298.         register int    c;
  299.         register LINE   *clp;
  300.         register int    cbo;
  301.         register int    success;
  302.         int          pptr;
  303.  
  304.         for (cip=0; cip<NSRCH; cip++)
  305.                 cmds[cip].s_code = SRCH_NOPR;
  306.         cip = 0;
  307.         pptr = -1;
  308.         clp = curwp->w_dotp;
  309.         cbo = curwp->w_doto;
  310.         is_lpush();
  311.         is_cpush(SRCH_BEGIN);
  312.         success = TRUE;
  313.         is_prompt(dir, TRUE, success);
  314.         for (;;) {
  315.                 update();
  316.                 switch (c = ttgetc()) {
  317.                 case CCHR('M'):
  318.                 case METACH:
  319.                         srch_lastdir = dir;
  320.                         mlwrite("[Done]");
  321.                         return (TRUE);
  322.  
  323.                 case CCHR('G'):
  324.                         curwp->w_dotp = clp;
  325.                         curwp->w_doto = cbo;
  326.                         curwp->w_flag |= WFMOVE;
  327.                         srch_lastdir = dir;
  328.                         ctrlg(FALSE, 0);
  329.                         return (FALSE);
  330.  
  331.                 case CCHR('S'):
  332.                 case CCHR('F'):
  333.                         if (dir == SRCH_BACK) {
  334.                                 dir = SRCH_FORW;
  335.                                 is_lpush();
  336.                                 is_cpush(SRCH_FORW);
  337.                                 success = TRUE;
  338.                         }
  339.                         /* Drop through to find next. */
  340.                 case CCHR('N'):
  341.                         if (success==FALSE && dir==SRCH_FORW)
  342.                                 break;
  343.                         is_lpush();
  344.                         forwchar(FALSE, 1);
  345.                         if (is_find(SRCH_NEXT) != FALSE) {
  346.                                 is_cpush(SRCH_NEXT);
  347.                                 pptr = strlen(pat);
  348.                         } else {
  349.                                 backchar(FALSE, 1);
  350.                                 (*term.t_beep)();
  351.                                 success = FALSE;
  352.                         }
  353.                         is_prompt(dir, FALSE, success);
  354.                         break;
  355.  
  356.                 case CCHR('R'):
  357.                 case CCHR('B'):
  358.                         if (dir == SRCH_FORW) {
  359.                                 dir = SRCH_BACK;
  360.                                 is_lpush();
  361.                                 is_cpush(SRCH_BACK);
  362.                                 success = TRUE;
  363.                         }
  364.                         /* Drop through to find previous. */
  365.                 case CCHR('P'):
  366.                         if (success==FALSE && dir==SRCH_BACK)
  367.                                 break;
  368.                         is_lpush();
  369.                         backchar(FALSE, 1);
  370.                         if (is_find(SRCH_PREV) != FALSE) {
  371.                                 is_cpush(SRCH_PREV);
  372.                                 pptr = strlen(pat);
  373.                         } else {
  374.                                 forwchar(FALSE, 1);
  375.                                 (*term.t_beep)();
  376.                                 success = FALSE;
  377.                         }
  378.                         is_prompt(dir,FALSE,success);
  379.                         break;
  380.  
  381.                 case 0x7F:
  382.                         if (is_undo(&pptr, &dir) != TRUE)
  383.                                 return (ABORT);
  384.                         if (is_peek() != SRCH_ACCM)
  385.                                 success = TRUE;
  386.                         is_prompt(dir, FALSE, success);
  387.                         break;
  388.  
  389.                 case CCHR('^'):
  390.                 case CCHR('Q'):
  391.                         c = ttgetc();
  392.                 case CCHR('U'):
  393.                 case CCHR('X'):
  394.                 case CCHR('J'):
  395.                         goto  addchar;
  396.  
  397.                 default:
  398.                         if (iscntrl(c) != FALSE) {
  399.                                 c += '@';
  400.                                 c |= CTRL;
  401.                                 success = execute(c, FALSE, 1);
  402.                                 curwp->w_flag |= WFMOVE;
  403.                                 return (success);
  404.                         }
  405.                 addchar:
  406.                         if (pptr == -1)
  407.                                 pptr = 0;
  408.                         if (pptr == 0)
  409.                                 success = TRUE;
  410.                         pat[pptr++] = c;
  411.                         if (pptr == NPAT) {
  412.                                 mlwrite("Pattern too long");
  413.                                 ctrlg(FALSE, 0);
  414.                                 return (ABORT);
  415.                         }
  416.                         pat[pptr] = '\0';
  417.                         is_lpush();
  418.                         if (success != FALSE) {
  419.                                 if (is_find(dir) != FALSE)
  420.                                         is_cpush(c);
  421.                                 else {
  422.                                         success = FALSE;
  423.                                         (*term.t_beep)();
  424.                                         is_cpush(SRCH_ACCM);
  425.                                 }
  426.                         } else
  427.                                 is_cpush(SRCH_ACCM);
  428.                         is_prompt(dir, FALSE, success);
  429.                 }
  430.         }
  431. }
  432.  
  433. is_cpush(cmd)
  434. register int    cmd;
  435. {
  436.         if (++cip >= NSRCH)
  437.                 cip = 0;
  438.         cmds[cip].s_code = cmd;
  439. }
  440.  
  441. is_lpush()
  442. {
  443.         register int    ctp;
  444.  
  445.         ctp = cip+1;
  446.         if (ctp >= NSRCH)
  447.                 ctp = 0;
  448.         cmds[ctp].s_code = SRCH_NOPR;
  449.         cmds[ctp].s_doto = curwp->w_doto;
  450.         cmds[ctp].s_dotp = curwp->w_dotp;
  451. }
  452.  
  453. is_pop()
  454. {
  455.         if (cmds[cip].s_code != SRCH_NOPR) {
  456.                 curwp->w_doto  = cmds[cip].s_doto;
  457.                 curwp->w_dotp  = cmds[cip].s_dotp;
  458.                 curwp->w_flag |= WFMOVE;
  459.                 cmds[cip].s_code = SRCH_NOPR;
  460.         }
  461.         if (--cip <= 0)
  462.                 cip = NSRCH-1;
  463. }
  464.  
  465. is_peek()
  466. {
  467.         if (cip == 0)
  468.                 return (cmds[NSRCH-1].s_code);
  469.         else
  470.                 return (cmds[cip-1].s_code);
  471. }
  472.  
  473. is_undo(pptr, dir)
  474. register int    *pptr;
  475. register int    *dir;
  476. {
  477.         switch (cmds[cip].s_code) {
  478.         case SRCH_NOPR:
  479.         case SRCH_BEGIN:
  480.         case SRCH_NEXT:
  481.         case SRCH_PREV:
  482.                 break;
  483.  
  484.         case SRCH_FORW:
  485.                 *dir = SRCH_BACK;
  486.                 break;
  487.  
  488.         case SRCH_BACK:
  489.                 *dir = SRCH_FORW;
  490.                 break;
  491.  
  492.         case SRCH_ACCM:
  493.         default:
  494.                 *pptr -= 1;
  495.                 if (*pptr < 0)
  496.                         *pptr = 0;
  497.                 pat[*pptr] = '\0';
  498.                 break;
  499.         }
  500.         is_pop();
  501.         return (TRUE);
  502. }
  503.  
  504. is_find(dir)
  505. register int    dir;
  506. {
  507.         register int    plen;
  508.  
  509.         plen = strlen(pat);
  510.         if (plen != 0) {
  511.                 if (dir==SRCH_FORW || dir==SRCH_NEXT) {
  512.                         backchar(FALSE, plen);
  513.                         if (forwsrch() == FALSE) {
  514.                                 forwchar(FALSE, plen);
  515.                                 return (FALSE);
  516.                         }
  517.                         return (TRUE);
  518.                 }
  519.                 if (dir==SRCH_BACK || dir==SRCH_PREV) {
  520.                         forwchar(FALSE, plen);
  521.                         if (backsrch() == FALSE) {
  522.                                 backchar(FALSE, plen);
  523.                                 return (FALSE);
  524.                         }
  525.                         return (TRUE);
  526.                 }
  527.                 mlwrite("bad call to is_find");
  528.                 ctrlg(FALSE, 0);
  529.                 return (FALSE);
  530.         }
  531.         return (FALSE);
  532. }
  533.  
  534. /*
  535.  * If called with "dir" not one of SRCH_FORW
  536.  * or SRCH_BACK, this routine used to print an error
  537.  * message. It also used to return TRUE or FALSE,
  538.  * depending on if it liked the "dir". However, none
  539.  * of the callers looked at the status, so I just
  540.  * made the checking vanish.
  541.  */
  542. is_prompt(dir, flag, success)
  543. {
  544.         if (dir == SRCH_FORW) {
  545.                 if (success != FALSE)
  546.                         is_dspl("i-search forward", flag);
  547.                 else
  548.                         is_dspl("failing i-search forward", flag);
  549.         } else if (dir == SRCH_BACK) {
  550.                 if (success != FALSE)
  551.                         is_dspl("i-search backward", flag);
  552.                 else
  553.                         is_dspl("failing i-search backward", flag);
  554.         }
  555. }
  556.  
  557. /*
  558.  * Prompt writing routine for the incremental search.
  559.  * The "prompt" is just a string. The "flag" determines
  560.  * if a "[ ]" or ":" embelishment is used.
  561.  */
  562. is_dspl(prompt, flag)
  563. char    *prompt;
  564. {
  565.         if (flag != FALSE)
  566.                 mlwrite("%s [%s]", prompt, pat);
  567.         else
  568.                 mlwrite("%s: %s", prompt, pat);
  569. }
  570.  
  571. /* META Command Search and replace in forward direction only.  Prompts
  572.  * before replacement allows user to ABORT or continue.  Calling with an
  573.  * argument prevents case distinctions.  Bound to M-R.
  574.  */
  575.  
  576. replace(f, n)
  577. register int f, n;
  578. {
  579.         register int    s;
  580.         register int    kludge;         /* Watch for saved line move    */
  581.         register LINE   *clp;           /* saved line pointer           */
  582.         char         toprompt[81];      /* temporary string             */
  583.         char         prompt[81];        /* final prompt                 */
  584.         char         *perptr, *index(); /* remap '%' to '%%' in prompt  */
  585.         int          cbo;               /* offset into the saved line   */
  586.         int          rcnt = 0;          /* Replacements made so far     */
  587.         int          plen;              /* length of found string       */
  588.  
  589.         strcpy(prompt,"Query Replace ");
  590.         if ((s=readpattern(prompt, &pat)) != TRUE)
  591.                 return (s);
  592.         strcat(prompt,pat);
  593.         strcat(prompt," with");
  594.         if ((perptr=index(prompt,'%'))!=NULL)
  595.                 {
  596.                 *perptr = '\0';                 /* terminate prompt     */
  597.                 strcpy(toprompt,"%%");          /* setup mapping chars  */
  598.                 strcat(toprompt,++perptr);      /* append end of prompt */
  599.                 strcat(prompt,toprompt);        /* append end to prompt */
  600.                 }
  601.         if ((s=readpattern(prompt, &rpat)) == ABORT)
  602.                 return (s);
  603.         if (s == FALSE)
  604.                 rpat[0] = '\0';
  605.         plen = strlen(pat);
  606.  
  607.         /*
  608.          * Search forward repeatedly, checking each time whether to insert
  609.          * or not.  The "!" case makes the check always true, so it gets put
  610.          * into a tighter loop for efficiency.
  611.          *
  612.          * If we change the line that is the remembered value of dot, then
  613.          * it is possible for the remembered value to move.  This causes great
  614.          * pain when trying to return to the non-existant line.
  615.          *
  616.          * possible fixes:
  617.          * 1) put a single, relocated marker in the WINDOW structure, handled
  618.          *    like mark.  The problem now becomes a what if two are needed...
  619.          * 2) link markers into a list that gets updated (auto structures for
  620.          *    the nodes)
  621.          * 3) Expand the mark into a stack of marks and add pushmark, popmark.
  622.          */
  623.  
  624.         clp = curwp->w_dotp;        /* save the return location     */
  625.         cbo = curwp->w_doto;
  626.         while (forwsrch() == TRUE) {
  627.         retry:
  628.                 update();
  629.                 switch (ttgetc()) {
  630.                 case ' ':
  631.                 case ',':
  632.                         kludge = (curwp->w_dotp == clp);
  633.                         if (lreplace(plen, rpat, f) == FALSE)
  634.                                 return (FALSE);
  635.                         rcnt++;
  636.                         if (kludge != FALSE)
  637.                                 clp = curwp->w_dotp;
  638.                         break;
  639.  
  640.                 case '.':
  641.                         kludge = (curwp->w_dotp == clp);
  642.                         if (lreplace(plen, rpat, f) == FALSE)
  643.                                 return (FALSE);
  644.                         rcnt++;
  645.                         if (kludge != FALSE)
  646.                                 clp = curwp->w_dotp;
  647.                         goto stopsearch;
  648.  
  649.                 case 0x07:
  650.                         ctrlg(FALSE, 0);
  651.                         goto stopsearch;
  652.  
  653.                 case '!':
  654.                         do {
  655.                                 kludge = (curwp->w_dotp == clp);
  656.                                 if (lreplace(plen, rpat, f) == FALSE)
  657.                                         return (FALSE);
  658.                                 rcnt++;
  659.                                 if (kludge != FALSE)
  660.                                         clp = curwp->w_dotp;
  661.                         } while (forwsrch() == TRUE);
  662.                         goto stopsearch;
  663.  
  664.                 case 'n':
  665.                         break;
  666.  
  667.                 default:
  668. mlwrite("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
  669.                         goto retry;
  670.                 }
  671.         }
  672. stopsearch:
  673.         curwp->w_dotp = clp;
  674.         curwp->w_doto = cbo;
  675.         curwp->w_flag |= WFHARD;
  676.         update();
  677.         if (rcnt == 0)
  678.                 mlwrite("[No replacements done]");
  679.         else if (rcnt == 1)
  680.                 mlwrite("[1 replacement done]");
  681.         else
  682.                 mlwrite("[%d replacements done]", rcnt);
  683.         return (TRUE);
  684. }
  685.  
  686. /*
  687.  * Replace plen characters before dot with argument string.
  688.  * Control-J characters in st are interpreted as newlines.
  689.  * There is a casehack disable flag (normally it likes to match
  690.  * case of replacement to what was there).
  691.  */
  692. lreplace(plen, st, f)
  693. register int    plen;              /* length to remove       */
  694. char        *st;                    /* replacement string          */
  695. int          f;               /* case hack disable          */
  696. {
  697.         register int    rlen;      /* replacement length           */
  698.         register int    rtype;    /* capitalization            */
  699.         register int    c;            /* used for random characters   */
  700.         register int    doto;      /* offset into line       */
  701.  
  702.         /*
  703.          * Find the capitalization of the word that was found.
  704.          * f says use exact case of replacement string (same thing that
  705.          * happens with lowercase found), so bypass check.
  706.          */
  707.         backchar(TRUE, plen);
  708.         rtype = __l;
  709.         c = lgetc(curwp->w_dotp, curwp->w_doto);
  710.         if (isupper(c)!=FALSE  &&  f==FALSE) {
  711.                 rtype = __u|__l;
  712.                 if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
  713.                         c = lgetc(curwp->w_dotp, curwp->w_doto+1);
  714.                         if (isupper(c) != FALSE) {
  715.                                 rtype = __u;
  716.                         }
  717.                 }
  718.         }
  719.  
  720.         /*
  721.          * make the string lengths match (either pad the line
  722.          * so that it will fit, or scrunch out the excess).
  723.          * be careful with dot's offset.
  724.          */
  725.         rlen = strlen(st);
  726.         doto = curwp->w_doto;
  727.         if (plen > rlen)
  728.                 ldelete(plen-rlen, FALSE);
  729.         else if (plen < rlen) {
  730.                 if (linsert(rlen-plen, ' ') == FALSE)
  731.                         return (FALSE);
  732.         }
  733.         curwp->w_doto = doto;
  734.  
  735.         /*
  736.          * do the replacement:  If was capital, then place first
  737.          * char as if upper, and subsequent chars as if lower.
  738.          * If inserting upper, check replacement for case.
  739.          */
  740.         while ((c = *st++&0xff) != '\0') {
  741.                 if ((rtype&__u)!=0  &&  islower(c)!=0)
  742.                         c = toupper(c);
  743.                 if (rtype == (__u|__l))
  744.                         rtype = __l;
  745.                 if (c == '\n') {
  746.                         if (curwp->w_doto == llength(curwp->w_dotp))
  747.                                 forwchar(FALSE, 1);
  748.                         else {
  749.                                 ldelete(1, FALSE);
  750.                                 lnewline();
  751.                         }
  752.                 } else if (curwp->w_dotp == curbp->b_linep) {
  753.                         linsert(1, c);
  754.                 } else if (curwp->w_doto == llength(curwp->w_dotp)) {
  755.                         ldelete(1, FALSE);
  756.                         linsert(1, c);
  757.                 } else
  758.                         lputc(curwp->w_dotp, curwp->w_doto++, c);
  759.         }
  760.         lchange(WFHARD);
  761.         return (TRUE);
  762. }
  763.  
  764.