home *** CD-ROM | disk | FTP | other *** search
/ The C Users' Group Library 1994 August / wc-cdrom-cusersgrouplibrary-1994-08.iso / vol_300 / 318_01 / redcmnd.c < prev    next >
C/C++ Source or Header  |  1990-06-18  |  30KB  |  1,489 lines

  1. /*
  2.     RED command mode commands -- Full C version
  3.  
  4.     Source:  redcmnd.c
  5.     Version: February 4, 1985; August 8, 1986; January 18, 1990
  6.  
  7.     Written by
  8.     
  9.         Edward K. Ream
  10.         166 N. Prospect
  11.         Madison WI 53705
  12.         (608) 257-0802
  13.  
  14.  
  15.     PUBLIC DOMAIN SOFTWARE
  16.  
  17.     This software is in the public domain.
  18.  
  19.     See red.h for a disclaimer of warranties and other information.
  20. */
  21. #include "red.h"
  22.  
  23. /*
  24.     Declare the routines in this file.
  25. */
  26. static int    srch1    (int s_start,int s_end,int r_flag,int w_flag,int c_flag);
  27. static int    srch2    (char *old_pat,char *new_pat,int r_flag,int c_flag,int *a_flag,int s_anchor,int e_anchor,int plen);
  28. static int    sepchar    (int c);
  29. static void    append    (char *fname,int where,char *prompt);
  30. static void    xtrct    (char *fname,int start,int finish,char *prompt);
  31. static void    movecopy(char *args,char *usage,char *prompt,int erase);
  32. static char *    skiparg    (char *args);
  33. static char *    skipbl    (char *args);
  34. static int    chkckey    (void);
  35. static int    replace    (char *oldline,char *newline,char *oldpat,char *newpat,int col);
  36.  
  37. /*
  38.     Do the last find/change/search again.
  39. */
  40. void
  41. again(void)
  42. {
  43.     TICKB("again");
  44.  
  45.     if (again_flag) {
  46.         if (    a_start == bufln() &&
  47.             a_rflag == FALSE &&
  48.             a_wflag == TRUE &&
  49.             a_cflag == FALSE
  50.         ) {
  51.             if (a_rev) {
  52.                 /* Redo a findr. */
  53.                 a_start--;
  54.             }
  55.             else {
  56.                 /* Redo a find. */
  57.                 a_start++;
  58.             }
  59.         }
  60.         else {
  61.             a_start = bufln();
  62.         }
  63.         srch1(a_start, a_end, a_rflag, a_wflag, a_cflag);
  64.     }
  65.     else {
  66.         cmndmess("no change/find/change active");
  67.     }
  68.  
  69.     TICKX("again");
  70. }
  71.  
  72. /*
  73.     Changes lines.
  74. */
  75. void
  76. change(char *args)
  77. {
  78.     int  from, to, junk;
  79.  
  80.     TRACEPB("change", sl_lpout(); sl_sout(args); sl_rpout());
  81.  
  82.     /* Check the arguments. */
  83.     if (get3args(args,&from,1,&to,HUGE,&junk,-1)==ERROR) {
  84.         RETURN_VOID("change");
  85.     }
  86.  
  87.     /* rflag = TRUE;  wflag = FALSE;  cflag = TRUE */
  88.     again_flag = FALSE;
  89.     srch1(from, to, TRUE, FALSE, TRUE);
  90.     again_flag = TRUE;
  91.  
  92.     TICKX("change");
  93. }
  94.  
  95. /*
  96.     Clear main buffer and file name.
  97.     WARNING:  clear() is an AZTEC library function.
  98. */
  99. void
  100. clear1(void)
  101. {
  102.     TICKB("clear1");
  103.  
  104.     /* Make sure it is ok to clear buffer. */
  105.     if (chkbuf() == TRUE) {
  106.         g_file [0] = 0;
  107.         outclr();
  108.         bufnew();
  109.         edgetln();
  110.         outxy(0, 1);
  111.         bufout(1, 1, 2);
  112.         pmtzap();        /* 1/7/84 */
  113.     }
  114.  
  115.     TICKX("clear1");
  116. }
  117.  
  118. /*
  119.     Copy a block of memory without erasing it.
  120. */
  121. void
  122. copy(char *args)
  123. {
  124.     TRACEPB("copy", sl_lpout(); sl_sout(args); sl_rpout());
  125.  
  126.     movecopy(args, "usage: copy <block> <n>", "-- copying --", FALSE);
  127.  
  128.     TICKX("copy");
  129. }
  130.  
  131. /*
  132.     Delete multiple lines.
  133. */
  134. void
  135. delete(char *args)
  136. {
  137.     int from, to, dummy;
  138.  
  139.     TRACEPB("delete", sl_lpout(); sl_sout(args); sl_rpout());
  140.  
  141.     /* Check the request. */
  142.     if(get3args(args,&from,bufln(),&to,-1,&dummy,-1)==ERROR){
  143.         RETURN_VOID("delete");
  144.     }
  145.     if (to == -1) {
  146.         to = from;
  147.     }
  148.  
  149.     /* Say we've started. */
  150.     pmtmode("-- deleting --");
  151.  
  152.     /* Go to first line to be deleted. */
  153.     bufgo(from);
  154.  
  155.     /* Delete all line between from and to. */
  156.     bufdeln(to-from+1);
  157.  
  158.     /* Redraw the screen. */
  159.     bufgo(from);
  160.     edclr();
  161.     edgo(from, 0);
  162.  
  163.     TICKX("delete");
  164. }
  165.  
  166. /*
  167.     Extract command.
  168.     Copy lines to a file.
  169. */
  170. void
  171. extract(char *args)
  172. {
  173.     char locfn [SYSFNMAX];
  174.     int  from, to, junk;
  175.  
  176.     TRACEPB("extract", sl_lpout(); sl_sout(args); sl_rpout());
  177.  
  178.     /* Get file name which follows command. */
  179.     if (name1(args, locfn) == ERROR) {
  180.         RETURN_VOID("extract");
  181.     }
  182.     if (locfn [0] == '\0') {
  183.         cmndmess("usage: extract <filename> first last");
  184.         RETURN_VOID("extract");
  185.     }
  186.  
  187.     /* Skip over command,  so get3args() will skip filename. */
  188.     args = skiparg(args);
  189.     args = skipbl (args);
  190.  
  191.     /* Get optional line numbers. */
  192.     get3args(args, &from, bufln(), &to, -1, &junk, -1);
  193.     if (to == -1) {
  194.         to = from;
  195.     }
  196.  
  197.     /* Extract the file. */
  198.     xtrct(locfn, from, to, "-- extracting --");
  199.  
  200.     TICKX("extract");
  201. }
  202.  
  203. /*
  204.     Search for a pattern.
  205.     find -- search forward.  findr -- search backwards.
  206. */
  207. void
  208. find(char *args)
  209. {
  210.     int  start, last, junk;
  211.  
  212.     TRACEPB("find", sl_lpout(); sl_sout(args); sl_rpout());
  213.  
  214.     /* Get starting place for search. */
  215.     if(get3args(args,&start,bufln()+1,&last,HUGE,&junk,-1)==ERROR){
  216.         RETURN_VOID("find");
  217.     }
  218.  
  219.     /* rflag = FALSE;  wflag = TRUE;  cflag = FALSE */
  220.     again_flag = FALSE;
  221.     srch1 (start, last, FALSE, TRUE, FALSE);
  222.     again_flag = TRUE;
  223.     a_rev = FALSE;
  224.  
  225.     TICKX("find");
  226. }
  227.  
  228. /*
  229.     Same as find(),  but in reverse.
  230. */
  231. void
  232. findr(char *args)
  233. {
  234.     int  start, junk;
  235.  
  236.     TRACEPB("findr", sl_lpout(); sl_sout(args); sl_rpout());
  237.  
  238.     /* Get starting place for search. */
  239.     if (get3args(args,&start,bufln()-1,&junk,-1,&junk,-1)==ERROR){
  240.         RETURN_VOID("findr");
  241.     }
  242.  
  243.     /* rflag = FALSE;  wflag = TRUE;  cflag = FALSE */
  244.     again_flag = FALSE;
  245.     srch1 (max(start,1), 1, FALSE, TRUE, FALSE);
  246.     again_flag = TRUE;
  247.     a_rev = TRUE;
  248.  
  249.     TICKX("findr");
  250. }
  251.  
  252. #define MESS message
  253.  
  254. void
  255. help(void)
  256. {
  257.     SL_DISABLE();
  258.  
  259. MESS  ("Command Default args   Description");
  260. MESS  ("");
  261. MESS  ("change 1 9999        Change all lines in <line range>");
  262. MESS  ("clear            Reset the editor");
  263. MESS  ("copy   <block> <n>    Copy lines in <block> after line <n>");
  264. MESS  ("delete <current line>    Delete one or more lines");
  265. MESS  ("exit            Exit from the editor");
  266. MESS  ("extract <file> <block>    Create a file from a block");
  267. MESS  ("find    <current line>    Search for a pattern");
  268. MESS  ("findr    <current line>    Backward find");
  269. MESS  ("g <n>            Enter edit mode at line <n>");
  270. MESS  ("help            Print this message");
  271. MESS  ("inject <file>           Add a line to the buffer");
  272. MESS  ("list    1 9999        List lines to the printer");
  273. MESS  ("load   <file>        Replace the buffer with <filename>");
  274. MESS  ("move   <block> <n>    Move lines of <block> after line <n>");
  275. MESS  ("name   <file>        Set filename");
  276. MESS  ("nowrap            Disable line wrapping");
  277. #ifdef SUSPEND
  278. MESS  ("quit            Exit and save work file");
  279. #endif
  280. MESS  ("resave            Save the buffer to an existing file");
  281. MESS  ("save            Save the buffer to a new file");
  282. MESS  ("search    1 9999        Search for pattern");
  283. MESS  ("tabs    8        Set tabs to every <n> columns");
  284. MESS  ("wrap            Enable line wrapping");
  285.  
  286.     /* Wait for any character and redraw the screen. */
  287.     pmtupd();
  288.     syscin();
  289.     pmtzap();
  290.     edclr();
  291.     edgo(bufln(), 0);
  292.  
  293. }
  294.  
  295. /*
  296.     Inject command.
  297.     Load a file into main buffer at current location.
  298.     This command does NOT change the current file name.
  299. */
  300. void
  301. inject(char *args)
  302. {
  303.     char locfn [SYSFNMAX];
  304.     int  oldline, junk;
  305.  
  306.     TRACEPB("inject", sl_lpout(); sl_sout(args); sl_rpout());
  307.  
  308.     /* Get file name which follows command. */
  309.     if (name1(args, locfn) == ERROR) {
  310.         RETURN_VOID("inject");
  311.     }
  312.     if (locfn [0] == '\0') {
  313.         cmndmess("usage: inject <filename> line");
  314.         RETURN_VOID("inject");
  315.     }
  316.  
  317.     /* Skip over command,  so get3args() will skip filename. */
  318.     args = skiparg(args);
  319.     args = skipbl (args);
  320.  
  321.     /* Get optional line number. */
  322.     get3args(args, &oldline, bufln(), &junk, -1, &junk, -1);
  323.  
  324.     /* Load the file at oldline. */
  325.     append(locfn, oldline, "-- injecting --");
  326.  
  327.     /* Redraw the screen. */
  328.     bufgo(oldline);
  329.     edclr();
  330.     edgo(oldline, 0);
  331.  
  332.     TICKX("inject");
  333. }
  334.  
  335. /*
  336.     Print lines to list device.
  337. */
  338. void
  339. list(char *args)
  340. {
  341.     char linebuf [MAXLEN1];
  342.     int n;
  343.     int from, to, dummy, line, oldline;
  344.  
  345.     TRACEPB("list", sl_lpout(); sl_sout(args); sl_rpout());
  346.  
  347.     /* Save the buffer's current line. */
  348.     oldline = bufln();
  349.  
  350.     /* Get starting, ending lines to print. */
  351.     if (get3args(args,&from,1,&to,HUGE,&dummy,-1)==ERROR) {
  352.         RETURN_VOID("list");
  353.     }
  354.  
  355.     /* Say we've started. */
  356.     pmtmode("-- listing --");
  357.  
  358.     /* Print lines one at a time to list device. */
  359.     line = from;
  360.     while (line <= to) {
  361.  
  362.         /* Make sure prompt goes to console. */
  363.         fmtassn(FALSE);
  364.  
  365.         /* Check for interrupt. */
  366.         if (chkckey() == TRUE) {
  367.             break;
  368.         }
  369.  
  370.         /* Print line to list device. */
  371.         fmtassn(TRUE);
  372.  
  373.         bufgo(line++);
  374.         if (bufatbot()) {
  375.             break;
  376.         }
  377.         n = bufgetln(linebuf,MAXLEN1);
  378.         n = min(n,MAXLEN);
  379.         linebuf [n] = '\n';
  380.         fmtsout(linebuf,0);
  381.         fmtcrlf();
  382.     }
  383.  
  384.     /* Redirect output to console. */
  385.     fmtassn(FALSE);
  386.  
  387.     /* Restore cursor. */
  388.     bufgo(oldline);
  389.  
  390.     TICKX("list");
  391. }
  392.  
  393. /*
  394.     Load file into buffer.
  395.     Return TRUE if the file was loaded correctly.
  396. */
  397. int
  398. load (char *args)
  399. {
  400.     char buffer [MAXLEN];    /* disk line buffer */
  401.     char locfn  [SYSFNMAX];  /* file name */
  402.     int n;
  403.     int topline;
  404.  
  405.     TRACEPB("load", sl_lpout(); sl_sout(args); sl_rpout());
  406.  
  407.     /* Get filename following command. */
  408.     if (name1(args,locfn) == ERROR) {
  409.         RETURN_INT("load", FALSE);
  410.     }
  411.  
  412.     if (locfn [0] == '\0') {
  413.         cmndmess("No file argument.");
  414.         RETURN_INT("load", FALSE);
  415.     }
  416.  
  417.     /* Give user a chance to save the buffer. */
  418.     if (chkbuf() == FALSE) {
  419.         RETURN_INT("load", FALSE);
  420.     }
  421.  
  422.     /* Open the new file. */
  423.     if (sysexists(locfn) == FALSE) {
  424.         cmndmess("File not found.");
  425.         RETURN_INT("load", FALSE);
  426.     }
  427.  
  428.     /* Say we've started. */
  429.     pmtmode("-- loading --");
  430.  
  431.     /* Update file name. */
  432.     syscopfn(locfn, g_file);
  433.     pmtfn();
  434.     pmtupd();
  435.  
  436.     /* Clear the buffer. */
  437.     bufnew();
  438.  
  439.     /* Read the whole file into the buffer. */
  440.     read_file(g_file);
  441.  
  442.     /* Indicate that the buffer is fresh. */
  443.     bufsaved();
  444.  
  445.     /*    Set current line to line 1.
  446.         Redraw the screen.
  447.     */
  448.     bufgo(1);
  449.     edclr();
  450.     edgo (1, 0);
  451.  
  452.     again_flag = FALSE;
  453.  
  454.     RETURN_INT("load", TRUE);
  455. }
  456.  
  457. /*
  458.     Move a block of lines.
  459. */
  460. void
  461. move(char *args)
  462. {
  463.     TRACEPB("move", sl_lpout(); sl_sout(args); sl_rpout());
  464.  
  465.     /* Copy a block,  then delete it. */
  466.     movecopy(args, "usage: move <block> <n>", "-- moving --", TRUE);
  467.  
  468.     TICKX("move");
  469. }
  470.  
  471. /*
  472.     Change current file name.
  473. */
  474. void
  475. name(char *args)
  476. {
  477.     TRACEPB("name", sl_lpout(); sl_sout(args); sl_rpout());
  478.  
  479.     name1(args, g_file);
  480.  
  481.     TICKX("name");
  482. }
  483.  
  484. static int
  485. name1(char *args, char *filename)
  486. {
  487.     TRACEPB("name1",  sl_lpout();
  488.         sl_sout(args);     sl_csout();
  489.         sl_sout(filename); sl_rpout());
  490.  
  491.     /* Skip command. */
  492.     args = skiparg(args);
  493.     args = skipbl(args);
  494.  
  495.     /* Copy filename. */
  496.     syscopfn(args, filename);
  497.  
  498.     RETURN_INT("name1", OK);
  499. }
  500.  
  501. /*
  502.     Save the buffer in an already existing file.
  503. */
  504. int        /* 11/11/85 RD: make resave return success (TRUE/FALSE) */
  505. resave(void)
  506. {
  507.     int n, oldline;
  508.  
  509.     TICKB("resave");
  510.  
  511.     /* Save line number. */
  512.     oldline = bufln();
  513.  
  514.     /* Make sure file has a name. */
  515.     if (g_file [0] == '\0') {
  516.         cmndmess("File not named.");
  517.         RETURN_INT("resave", FALSE);
  518.     }
  519.  
  520.     /* The file must exist for resave. */
  521.     if (sysexists(g_file) == FALSE) {
  522.         cmndmess("File not found.");
  523.         RETURN_INT("resave", FALSE); 
  524.     }
  525.  
  526.     /* Say we've started. */
  527.     pmtmode("-- resaving --");
  528.  
  529.     /* Write out the whole buffer. */
  530.     write_file(g_file);
  531.  
  532.     /* Indicate that the buffer has been saved. */
  533.     bufsaved();
  534.  
  535.     /* Restore line number. */
  536.     bufgo(oldline);
  537.  
  538.     RETURN_INT("resave", TRUE);
  539. }
  540.  
  541. /*
  542.     Save the buffer in a new file.
  543.     Return TRUE if all went well.
  544. */
  545. int
  546. save(void)
  547. {
  548.     int file, n, oldline;
  549.  
  550.     TICKB("save");
  551.  
  552.     /* Save current line number. */
  553.     oldline = bufln();
  554.  
  555.     /* Make sure the file is named. */
  556.     if (g_file [0] == '\0') {
  557.         cmndmess("File not named.");
  558.         RETURN_INT("save", FALSE);
  559.     }
  560.  
  561.     /* File must NOT exist for save. */
  562.     if (sysexists(g_file) == TRUE) {
  563.         cmndmess("File exists.");
  564.         RETURN_INT("save", FALSE);
  565.     }
  566.  
  567.     /* Say we've started. */
  568.     pmtmode("-- saving --");
  569.  
  570.     /* Write out the whole buffer. */
  571.     write_file(g_file);
  572.  
  573.     /* Indicate buffer saved. */
  574.     bufsaved();
  575.  
  576.     /* Restore line number. */
  577.     bufgo(oldline);
  578.  
  579.     RETURN_INT("save", TRUE);
  580. }
  581.  
  582. /*
  583.     Search for a pattern.
  584. */
  585. void
  586. search(char *args)
  587. {
  588.     int from, to, junk;
  589.  
  590.     TRACEPB("search", sl_lpout(); sl_sout(args); sl_rpout());
  591.  
  592.     /* Check the request. */
  593.     if (get3args(args,&from,1,&to,HUGE,&junk,-1)==ERROR) {
  594.         RETURN_VOID("search");
  595.     }
  596.  
  597.     /* r_flag = FALSE,  w_flag = FALSE, c_flag = TRUE. */
  598.     again_flag = FALSE;
  599.     srch1(from, to, FALSE, FALSE, TRUE);
  600.     again_flag = TRUE;
  601.  
  602.     TICKX("search");
  603. }
  604.  
  605. /*
  606.     Search/change utility routine.
  607.     Redraw the screen if the pattern is found.
  608.  
  609.     s_pat[]   contains the search pattern.
  610.     r_pat[]   contains the change pattern.
  611.     s_start   is first line to search.
  612.     s_end     is last  line to search.
  613.     r_flag    is TRUE if change pattern is used.
  614.     w_flag    is TRUE if search wraps around.
  615.     c_flag    is TRUE if search continues after a match.
  616. */
  617. static int
  618. srch1(int s_start, int s_end, int r_flag, int w_flag, int c_flag)
  619. {
  620.     char s_pat [MAXLEN1];
  621.     char r_pat [MAXLEN1];
  622.  
  623.     int oldbuf;
  624.     int oldstart;
  625.     int a_flag, old_plen;
  626.     int start_anchor,  end_anchor;
  627.     int code;
  628.  
  629.     TRACEPB("srch1",  sl_lpout();
  630.         sl_iout(s_start); sl_csout();
  631.         sl_iout(s_end);   sl_csout(); 
  632.         sl_iout(r_flag);  sl_csout();
  633.         sl_iout(w_flag);  sl_csout(); 
  634.         sl_iout(c_flag);  sl_rpout());
  635.  
  636.     /* Save again parameters. */
  637.     a_start = s_start;
  638.     a_end   = s_end;
  639.     a_rflag = r_flag;
  640.     a_wflag = w_flag;
  641.     a_cflag = c_flag;
  642.  
  643.     if (again_flag == FALSE) {
  644.         /* Get search mask. */
  645.         pmtmode("Search mask? ");
  646.         getcmnd(s_pat);
  647.         if (strlen(s_pat) == 0) {
  648.             RETURN_VOID("srch1");
  649.         }
  650.         strcpy(a_spat, s_pat);
  651.     
  652.         if (r_flag) {
  653.             pmtmode("Change mask? ");
  654.             getcmnd(r_pat);
  655.             strcpy(a_rpat, r_pat);
  656.         }
  657.     }
  658.     else {
  659.         strcpy(s_pat, a_spat);
  660.         strcpy(r_pat, a_rpat);
  661.     }
  662.  
  663.     /* Remember the current line. */
  664.     oldbuf = bufln();
  665.  
  666.     /* Go to first line. */
  667.     bufgo(s_start);
  668.     if (bufatbot()) {
  669.         bufup();
  670.     }
  671.  
  672.     /* Remember the initial params. */
  673.     oldstart = bufln();
  674.     a_start  = s_start  = bufln();
  675.     a_end    = s_end;
  676.     old_plen = strlen(s_pat);
  677.  
  678.     /* Set start-of-line anchor. */
  679.     start_anchor = (s_pat [0] == '^') ? 1 : 0;
  680.  
  681.     /* Set end-of-line anchor. */
  682.     end_anchor = (s_pat [old_plen - 1] == '$') ? 1 : 0;
  683.  
  684.     /* Delete trailing anchor from search pattern. */
  685.     s_pat [old_plen - end_anchor] = '\0';
  686.  
  687.     /* Adjust plen to reflect only non-anchor characters. */
  688.     old_plen -= (start_anchor + end_anchor);
  689.  
  690.     /* Enable prompts in srch2(). */
  691.     a_flag = 0;
  692.  
  693.     pmtmode("-- searching --");
  694.  
  695.     /* Search all lines in between s_start and s_end. */
  696.     if (s_start <= s_end) {
  697.         while (s_start <= s_end) {
  698.  
  699.             /* Check for user abort. */
  700.             if (chkckey() == TRUE) {
  701.                 break;
  702.             }
  703.  
  704.             code = srch2( s_pat, r_pat, r_flag,
  705.                           c_flag, &a_flag, 
  706.                       start_anchor,  end_anchor,
  707.                       old_plen);
  708.  
  709.             if (code == TRUE) {
  710.                 RETURN_VOID("srch1");
  711.             }
  712.             else if (code == FALSE) {
  713.                 /* Remember the last match point. */
  714.                 oldbuf = bufln();
  715.                 pmtmode("-- searching --");
  716.             }
  717.  
  718.             if (bufnrbot() && w_flag == FALSE) {
  719.                 break;
  720.             }
  721.             else if (bufnrbot() && w_flag == TRUE) {
  722.                 /* Wrap around search. */
  723.                 w_flag = FALSE;
  724.                 bufgo(1);
  725.                 a_start = s_start = 1;
  726.                 a_end   = s_end   = oldstart;
  727.             }
  728.             else {
  729.                 bufgo(++s_start);
  730.                 a_start++;
  731.             }
  732.  
  733.         }
  734.  
  735.         /*  Return to the last line with a match. */
  736.         if (a_flag == 'a') {    /* 4/24/84 */
  737.             /* Force redraw. */
  738.             edclr();
  739.         }
  740.         edgo(oldbuf, 0);
  741.         RETURN_VOID("srch1");
  742.     }
  743.     else {
  744.         a_end = s_end = max(1, s_end);
  745.         while(s_start >= s_end) {
  746.  
  747.             /* Check for user abort. */
  748.             if (chkckey() == TRUE) {
  749.                 break;
  750.             }
  751.  
  752.             code = srch2( s_pat, r_pat, r_flag,
  753.                           c_flag, &a_flag,
  754.                       start_anchor,  end_anchor,
  755.                       old_plen);
  756.  
  757.             if (code == TRUE) {
  758.                 RETURN_VOID("srch1");
  759.             }
  760.             else if (code == FALSE) {
  761.                 oldbuf = bufln();
  762.             }
  763.  
  764.             if (bufln() == 1 && w_flag == FALSE) {
  765.                 break;
  766.             }
  767.             else if (bufln() == 1 && w_flag == TRUE) {
  768.                 w_flag = FALSE;
  769.                 bufgo(HUGE);
  770.                 bufup();
  771.                 a_start = s_start = bufln();
  772.                 a_end   = s_end   = oldstart;
  773.             }
  774.             else {
  775.                 bufgo(--s_start);
  776.                 a_start--;
  777.             }
  778.         }
  779.  
  780.         /* Return to the last line that matched. */
  781.         if (a_flag == 'a') {    /* 4/25/84 */
  782.             /* Force redraw of screen. */
  783.             edclr();
  784.         }
  785.         edgo(oldbuf, 0);
  786.         RETURN_VOID("srch1");
  787.     }
  788. }
  789.  
  790. /*
  791.     Search one line for all instances of old_pat.
  792.     If r_flag is TRUE, replace them by new_pat.
  793.     If c_flag is FALSE,  exit after finding the first instance.
  794.     If a_flag is 'a', do not prompt the user.
  795.     plen == strlen(old_pat).
  796.  
  797.     Return TRUE   if search should stop.
  798.     Return No    if search should continue.
  799.     Return ERROR if no match found.
  800. */
  801. static int
  802. srch2(
  803.     char *old_pat, char *new_pat,
  804.     int  r_flag, int c_flag, int *a_flag,
  805.     int  s_anchor, int e_anchor, int plen)
  806. {
  807.     char old_line [MAXLEN1];
  808.     char new_line [MAXLEN1];
  809.     int  old_length, col, mode, match, match1, xpos;
  810.     char * line, * pat;
  811.  
  812.     TRACEPB("srch2",  sl_lpout();
  813.         sl_sout(old_pat);  sl_csout(); 
  814.         sl_sout(new_pat);  sl_csout();
  815.         sl_iout(r_flag);   sl_csout(); 
  816.         sl_iout(c_flag);   sl_csout();
  817.         sl_pout(a_flag);   sl_csout(); 
  818.         sl_iout(s_anchor); sl_csout(); 
  819.         sl_iout(e_anchor); sl_csout();
  820.         sl_iout(plen);     sl_rpout());
  821.  
  822.     /* Get the current line into oldline[] */
  823.     old_length = bufgetln(old_line, MAXLEN1);
  824.     old_length = min(old_length, MAXLEN);
  825.     old_line [old_length] = '\0';
  826.  
  827.     /* No match is possible if old_pat[] is too long. */
  828.     if (s_anchor && e_anchor && plen != old_length) {
  829.         RETURN_INT("srch2", ERROR);
  830.     }
  831.     else if (plen > old_length) {
  832.         RETURN_INT("srch2", ERROR);
  833.     }
  834.  
  835.     /* Set starting column. */
  836.     col = (e_anchor) ? old_length - plen : 0;
  837.  
  838.     /* Set prompting mode. */
  839.     mode = * a_flag;
  840.  
  841.     /* Remember whether any match was seen on this line. */
  842.     match = FALSE;
  843.  
  844.     /* Search column by column. */
  845.     while (col < old_length) {
  846.         line = old_line + col;
  847.         pat = old_pat + s_anchor;
  848.  
  849.         /* Check for beginning of a word if required. */
  850.         if (hasword == FALSE || sepchar(*(line-1)) || col == 0) {
  851.  
  852.             /* Check character matches and keep advancing if so. */
  853.             match1 = TRUE;
  854.             while(*pat != '\0') {
  855.                 if (*pat == *line) {
  856.                     pat++;
  857.                     line++;
  858.                 }
  859.                 else if (*pat == '?' && *line != '\0') {
  860.                     pat++;
  861.                     line++;
  862.                 }
  863.                 else {
  864.                     match1 = FALSE;
  865.                     break;
  866.                 }
  867.             }
  868.  
  869.             /* Check for end of word if required. */
  870.             match1 = match1 && (hasword == FALSE || sepchar(*line));
  871.         }
  872.         else {
  873.             match1 = FALSE;
  874.         }
  875.  
  876.         if (!match1) {
  877.             if (s_anchor || e_anchor) {
  878.                 RETURN_INT("srch2", ERROR);
  879.             }
  880.             else {
  881.                 col++;
  882.                 continue;
  883.             }
  884.         }
  885.         else {
  886.             /* Remember that a match was seen. */
  887.             match = TRUE;
  888.         }
  889.  
  890.         /* Show the screen before any replacement. */
  891.         if (mode != 'a') {     /* 3/1/84 */
  892.             edgo(bufln(), col);
  893.             syswait();
  894.             xpos = outx;
  895.         }
  896.  
  897.         /* Draw the proposed change on the screen. */
  898.         if (r_flag == TRUE) {
  899.             replace(old_line, new_line,
  900.                 old_pat + s_anchor, new_pat, col);
  901.             bufrepl(new_line, strlen(new_line));
  902.             if (mode != 'a') {    /* 3/1/84 */
  903.                 outxy(0, outy);
  904.                 bufoutln(bufln());
  905.                 outxy(xpos, outy);
  906.             }
  907.         }
  908.  
  909.         /* Stop the search if continue flag is FALSE. */
  910.         if (c_flag == FALSE) {
  911.             RETURN_INT("srch2", TRUE);
  912.         }
  913.  
  914.         /* Prompt the user unless in 'all' mode. */
  915.         if (mode == 'a') {
  916.             /* Update the search line. */
  917.             old_length = bufgetln(old_line, MAXLEN1);
  918.             old_length = min(old_length, MAXLEN);
  919.             old_line [old_length] = '\0';
  920.             col     += strlen(new_pat);
  921.         }
  922.         else if (r_flag == FALSE) {
  923.             /* Give search-mode prompt. */
  924.             pmtmode("next, exit? ");
  925.             mode = syscin();
  926.             mode = tolower(mode);
  927.             if (mode != 'n' && mode != 'y') {
  928.                 RETURN_INT("srch2", TRUE);
  929.             }
  930.             else {
  931.                 /* Do not rescan matched pattern. */
  932.                 col += plen;
  933.             }
  934.         }
  935.         else {
  936.             /* Give change-mode prompt. */
  937.             pmtmode("yes, no, all, exit? ");
  938.             mode = syscin();
  939.             mode = tolower(mode);
  940.             *a_flag = mode;
  941.  
  942.             if (mode == 'y' || mode == 'a') {
  943.                 /* Update the search pattern. */
  944.                 old_length = bufgetln(old_line, MAXLEN1);
  945.                 old_length = min(old_length, MAXLEN);
  946.                 old_line [old_length] = '\0';
  947.             }
  948.             else {
  949.                 /* Undo the change. */
  950.                 bufrepl(old_line, strlen(old_line));
  951.                 outxy(0, outy);
  952.                 bufoutln(bufln());
  953.                 outxy(xpos, outy);
  954.             }
  955.     
  956.             if (mode == 'y' || mode == 'a') {
  957.                 /* Do not rescan replacement text. */
  958.                 col += strlen(new_pat);
  959.             }
  960.             else if (mode == 'n') {
  961.                 /* Continue the search on this line. */
  962.                 col += strlen(old_pat);
  963.             }
  964.             else {
  965.                 /* Default is 'e' */
  966.                 RETURN_INT("srch2", TRUE);
  967.             }
  968.         }
  969.  
  970.         /* Anchored searches examine line only once. */
  971.         if (s_anchor || e_anchor) {
  972.             RETURN_INT("srch2", FALSE);
  973.         }
  974.     }
  975.  
  976.     /* Indicate whether any match was found on the line. */
  977.  
  978.     RETURN_INT("srch2", (match) ? FALSE : ERROR);
  979. }
  980.  
  981. /*
  982.     Check for a separator character.
  983.     Return TRUE if the character cannot be part of a "word."
  984.     Here "word" is taken to mean "C identifier."
  985. */
  986. static int
  987. sepchar(int c)
  988. {
  989.     return (isalpha(c) || isdigit(c) || c == '_') ? FALSE : TRUE;
  990. }
  991.  
  992. /*
  993.     Set tab stops for fmt routines.
  994. */
  995. void
  996. tabs(char *args)
  997. {
  998.     int n, junk;
  999.  
  1000.     TRACEPB("tabs", sl_lpout(); sl_sout(args); sl_rpout());
  1001.  
  1002.     /* Default is every 8 columns. */
  1003.     if (get3args(args,&n,8,&junk,-1,&junk,-1)==ERROR){
  1004.         RETURN_VOID("tabs");
  1005.     }
  1006.     fmtset(n);
  1007.  
  1008.     /* Redraw the screen. */
  1009.     edclr();
  1010.     edgo(bufln(), 0);
  1011.  
  1012.     TICKX("tabs");
  1013. }
  1014.  
  1015. /*
  1016.     ----- Utility routines start here -----
  1017. */
  1018.  
  1019. /*
  1020.     Insert file whose name is fname after line where.
  1021.     (where can be zero,  in which case insert at start of file.)
  1022.     Use promt as the promt line mode while doing so.
  1023. */
  1024. static void
  1025. append(char *fname, int where, char *prompt)
  1026. {
  1027.     FILE *fd;
  1028.     char buffer [MAXLEN];    /* disk line buffer */
  1029.     int  oldline, n;
  1030.  
  1031.     TRACEPB("append",  sl_lpout();
  1032.         sl_sout(fname);  sl_csout();
  1033.         sl_iout(where);  sl_csout(); 
  1034.         sl_sout(prompt); sl_rpout());
  1035.  
  1036.     /* Open the new file. */
  1037.     fd = sysfopen(fname);
  1038.     if (fd == NULL) {
  1039.         cmndmess("File not found.");
  1040.         RETURN_VOID("append");
  1041.     }
  1042.  
  1043.     /* Say that we've started. */
  1044.     pmtmode(prompt);
  1045.  
  1046.     /* Go to after proper line,  unless line is zero. */
  1047.     bufgo(where);
  1048.     if (where) {
  1049.         bufdn();
  1050.     }
  1051.  
  1052.     /* Read the file into the buffer. */
  1053.     while ((n = sysfgets(fd,buffer,MAXLEN)) >= 0) {
  1054.         if (n > MAXLEN) {
  1055.             cmndmess("line truncated.");
  1056.             n = MAXLEN;
  1057.         }
  1058.         bufins(buffer,n);
  1059.         bufdn();
  1060.     }
  1061.  
  1062.     /* Close the file. */
  1063.     sysfclose(fd);
  1064.  
  1065.     TICKX("append");
  1066. }
  1067.  
  1068. /*
  1069.     Return TRUE if buffer may be drastically changed.
  1070. */
  1071. int
  1072. chkbuf(void)
  1073. {
  1074.     int c;
  1075.     int x, y;
  1076.  
  1077.     SL_DISABLE();
  1078.  
  1079.     /* Save cursor position. */
  1080.     x = outx;
  1081.     y = outy;
  1082.  
  1083.     if (bufchng() == FALSE) {
  1084.         return TRUE;
  1085.     }
  1086.  
  1087.     pmtmess("", "Buffer not saved.  Proceed ?  ");
  1088.     c = syscin();
  1089.     outchar(c);
  1090.  
  1091.     /* Restore cursor postion. */
  1092.     outxy(x, y);
  1093.  
  1094.     /* Watch out:  tolower may be a macro. */
  1095.     if (tolower(c) == 'y') {
  1096.         return TRUE;
  1097.     }
  1098.     else {
  1099.         return FALSE;
  1100.     }
  1101. }
  1102.  
  1103. /*
  1104.     Return TRUE if the user has pressed any key.
  1105. */
  1106. int
  1107. chkkey(void)
  1108. {
  1109.     int c;
  1110.  
  1111.     SL_DISABLE();
  1112.  
  1113.     c = syscstat();
  1114.     return (c == -1) ? FALSE : TRUE;
  1115. }
  1116.  
  1117. /*
  1118.     Return TRUE if the user has pressed any control key.
  1119. */
  1120. static int
  1121. chkckey(void)
  1122. {
  1123.     int c;
  1124.  
  1125.     SL_DISABLE();
  1126.  
  1127.     c = syscstat();
  1128.     return (c != -1 && ((c & 0x7f) < 32)) ? TRUE : FALSE;
  1129. }
  1130.  
  1131. /*
  1132.     Print a cmndmess on the command line and wait for a key.
  1133. */
  1134. void
  1135. cmndmess(char * mess)
  1136. {
  1137.     int x, y;
  1138.  
  1139.     SL_DISABLE();
  1140.  
  1141.     /* Save cursor. */
  1142.     x = outx;
  1143.     y = outy;
  1144.  
  1145.     pmtmess("", mess);
  1146.  
  1147.     /* Wait for any key. */
  1148.     syscin();
  1149.  
  1150.     /* Restore cursor. */
  1151.     outxy(x, y);
  1152. }
  1153.  
  1154. /*
  1155.     Get one, two or three arguments.
  1156.     Missing arguments are set to default values.
  1157. */
  1158. int
  1159. get3args(
  1160.     char *args,
  1161.     int *val1, int def1,
  1162.     int *val2, int def2,
  1163.     int *val3, int def3)
  1164. {
  1165.     TRACEPB("get3args", sl_lpout();
  1166.         sl_sout(args); sl_csout();
  1167.         sl_pout(val1); sl_csout(); 
  1168.         sl_iout(def1); sl_csout();
  1169.         sl_pout(val2); sl_csout(); 
  1170.         sl_iout(def2); sl_csout();
  1171.         sl_pout(val3); sl_csout(); 
  1172.         sl_iout(def3); sl_rpout());
  1173.  
  1174.     /* Skip the command. */
  1175.     args = skiparg (args);
  1176.     args = skipbl (args);
  1177.  
  1178.     /* Set defaults. */
  1179.     *val1 = def1;
  1180.     *val2 = def2;
  1181.     *val3 = def3;
  1182.  
  1183.     /* Check first arg. */
  1184.     if (*args == '\0') {
  1185.         RETURN_INT("get3args", OK);
  1186.     }
  1187.     if (number (args, val1) == FALSE) {
  1188.         cmndmess("Bad argument.");
  1189.         RETURN_INT("get3args", ERROR);
  1190.     }
  1191.  
  1192.     /* Skip over first argument. */
  1193.     args = skiparg(args);
  1194.     args = skipbl(args);
  1195.  
  1196.     /* Check second argument. */
  1197.     if (*args == '\0') {
  1198.         RETURN_INT("get3args", OK);
  1199.     }
  1200.     if (number(args, val2) == FALSE) {
  1201.         cmndmess("Bad argument.");
  1202.         RETURN_INT("get3args", ERROR);
  1203.     }
  1204.  
  1205.     /* Skip over third argument. */
  1206.     args = skiparg(args);
  1207.     args = skipbl(args);
  1208.  
  1209.     /* Check third argument. */
  1210.     if (*args == '\0') {
  1211.         RETURN_INT("get3args", OK);
  1212.     }
  1213.  
  1214.     if (number (args, val3) == FALSE) {
  1215.         cmndmess("Bad argument.");
  1216.         RETURN_INT("get3args", ERROR);
  1217.     }
  1218.     else {
  1219.         RETURN_INT("get3args", OK);
  1220.     }
  1221. }
  1222.  
  1223. /*
  1224.     Print message.
  1225. */
  1226. void
  1227. message(char *s)
  1228. {
  1229.     SL_DISABLE();
  1230.  
  1231.     fmtsout(s,0);
  1232.     fmtcrlf();
  1233. }
  1234.  
  1235. /*
  1236.     Move or copy a block of lines.
  1237. */
  1238. static void
  1239. movecopy(char *args, char *usage, char *prompt, int erase)
  1240. {
  1241.     int count, i, length;
  1242.     int fstart, fend, tstart;
  1243.     char buffer [MAXLEN1];
  1244.  
  1245.     TRACEPB("movecopy", sl_lpout();
  1246.         sl_sout(args);   sl_csout();
  1247.         sl_sout(usage);  sl_csout(); 
  1248.         sl_sout(prompt); sl_csout();
  1249.         sl_iout(erase);  sl_rpout());
  1250.  
  1251.     /* Get two or three args. */
  1252.     if (get3args(args,&fstart,-1,&fend,-1,&tstart,-1)==ERROR){
  1253.         RETURN_VOID("movecopy");
  1254.     }
  1255.  
  1256.     TRACEP("movecopy",
  1257.         sl_sout("fstart ");   sl_iout(fstart);
  1258.         sl_sout(", fend ");   sl_iout(fend);
  1259.         sl_sout(", tstart "); sl_iout(tstart);
  1260.         sl_cout('\n'));
  1261.  
  1262.     if (fstart == -1 || fend == -1) {
  1263.         cmndmess(usage);
  1264.         RETURN_VOID("movecopy");
  1265.     }
  1266.     if (tstart == -1) {
  1267.         tstart = fend;
  1268.         fend   = fstart;
  1269.     }
  1270.  
  1271.     /* Make sure the last line exists. */
  1272.     bufgo(max(fstart, tstart));
  1273.     if (bufatbot()) {
  1274.         bufup();
  1275.         if (fstart >= tstart) {
  1276.             fend = bufln();
  1277.         }
  1278.         else {
  1279.             tstart = bufln();
  1280.         }
  1281.     }        
  1282.  
  1283.     /*
  1284.     The 'to block' and 'from block' must not overlap.
  1285.     fstart must be > 0, tstart must be >=  0.
  1286.     */
  1287.     if (fend < fstart ||
  1288.         fstart <= 0 ||
  1289.         tstart < 0 ||
  1290.         (tstart >=  fstart && tstart < fend)
  1291.        ) {
  1292.         cmndmess(usage);
  1293.         RETURN_VOID("movecopy");
  1294.     }
  1295.  
  1296.     /* Extract block to TEMP_FILE. */
  1297.     xtrct(TEMP_FILE, fstart, fend, prompt);
  1298.  
  1299.     /* Inject TEMP_FILE at tstart. */
  1300.     append(TEMP_FILE, tstart, prompt);
  1301.  
  1302.     if (erase) {
  1303.         count = fend - fstart + 1;
  1304.         /* Erase 'from block'. */
  1305.         if (fstart < tstart) {
  1306.             bufgo(fstart);
  1307.         }
  1308.         else {
  1309.             bufgo(fstart + count);
  1310.         }
  1311.         bufdeln(count);
  1312.     }
  1313.  
  1314.     /* Erase the TEMP_FILE. */
  1315.     sysunlink(TEMP_FILE);
  1316.  
  1317.     /* Redraw the screen. */
  1318.     bufgo(tstart);
  1319.     edclr();
  1320.     edgo(tstart, 0);
  1321.  
  1322.     TICKX("movecopy");
  1323. }
  1324.  
  1325. /*
  1326.     Replace oldpat in oldline by newpat starting at col.
  1327.     Put result in newline.
  1328.     Return number of characters in newline.
  1329. */
  1330. static int
  1331. replace(char *oldline, char *newline, char *oldpat, char *newpat, int col)
  1332. {
  1333.     int k;
  1334.     char *tail, *pat;
  1335.  
  1336.     TRACEPB("replace",  sl_lpout();
  1337.         sl_sout(oldline); sl_csout(); 
  1338.         sl_sout(newline); sl_csout();
  1339.         sl_sout(oldpat);  sl_csout(); 
  1340.         sl_sout(newpat);  sl_csout();
  1341.         sl_iout(col);     sl_rpout());
  1342.  
  1343.     /* Copy oldline preceding col to newline. */
  1344.     k = 0;
  1345.     while (k < col) {
  1346.         newline [k++] = *oldline++;
  1347.     }
  1348.  
  1349.     /* Remember where end of oldpat in oldline is. */
  1350.     tail = oldline;
  1351.     pat = oldpat;
  1352.     while (*pat++ != '\0') {
  1353.         tail++;
  1354.     }
  1355.  
  1356.     /*
  1357.         Copy newpat to newline.
  1358.         Use oldline and oldpat to resolve question
  1359.         marks in newpat.
  1360.     */
  1361.     while (*newpat != '\0') {
  1362.         if (k > MAXLEN-1) {
  1363.             cmndmess("New line too long.");
  1364.             RETURN_INT("replace", ERROR);
  1365.         }
  1366.         if (*newpat != '?') {
  1367.             /* Copy newpat to newline. */
  1368.             newline [k++] = *newpat++;
  1369.             continue;
  1370.         }
  1371.  
  1372.         /* Scan for '?' in oldpat. */
  1373.         while (*oldpat != '?') {
  1374.             if (*oldpat == '\0') {
  1375.                 cmndmess(
  1376.                 "Too many ?'s in change mask."
  1377.                 );
  1378.                 RETURN_INT("replace", ERROR);
  1379.             }
  1380.             oldpat++;
  1381.             oldline++;
  1382.         }
  1383.  
  1384.         /* Copy char from oldline to newline. */
  1385.         newline [k++] = *oldline++;
  1386.         oldpat++;
  1387.         newpat++;
  1388.     }
  1389.  
  1390.     /* Copy oldline after oldpat to newline. */
  1391.     while (*tail != '\0') {
  1392.         if (k >=  MAXLEN-1) {
  1393.             cmndmess("New line too long.");
  1394.             RETURN_INT("replace", ERROR);
  1395.         }
  1396.         newline [k++] = *tail++;
  1397.     }
  1398.     newline [k] = '\0';
  1399.  
  1400.     RETURN_INT("replace", k);
  1401. }
  1402.  
  1403. /*
  1404.     Skip over all except '\0', and blanks.
  1405. */
  1406. static char *
  1407. skiparg(char *args)
  1408. {
  1409.     SL_DISABLE();
  1410.  
  1411.     while (*args != '\0' && *args != ' ') {
  1412.         args++;
  1413.     }
  1414.     return args;
  1415. }
  1416.  
  1417. /*
  1418.     Skip over all blanks.
  1419. */
  1420. static char *
  1421. skipbl(char *args)
  1422. {
  1423.     SL_DISABLE();
  1424.  
  1425.     while (*args == ' ') {
  1426.         args++;
  1427.     }
  1428.     return args;
  1429. }
  1430.  
  1431. /*
  1432.     Create a file named fname from start to finish.
  1433.     Set the prompt mode to prompt while doing so.
  1434. */
  1435. void
  1436. xtrct(char * fname, int start, int finish, char * prompt)
  1437. {
  1438.     FILE *fd;
  1439.     int i;
  1440.     int count, length;
  1441.     int oldline;
  1442.     char buffer [MAXLEN1];
  1443.  
  1444.     TRACEPB("xtrct",  sl_lpout();
  1445.         sl_sout(fname);  sl_csout();
  1446.         sl_iout(start);  sl_csout(); 
  1447.         sl_iout(finish); sl_csout();
  1448.         sl_sout(prompt); sl_rpout());
  1449.  
  1450.     /* Say we've started. */
  1451.     pmtmode(prompt);
  1452.  
  1453.     /* Open a temporary file. */
  1454.     fd = sysfcreat(fname);
  1455.     if (fd == NULL) {
  1456.         cmndmess("Can not open temporary file.");
  1457.         RETURN_VOID("xtrct");
  1458.     }
  1459.  
  1460.     /* Save the current line. */
  1461.     oldline = bufln();
  1462.  
  1463.     /* Copy the block to the temp file. */
  1464.     bufgo(start);
  1465.     for (count = finish - start + 1; count; count--) {
  1466.         length = bufgetln(buffer, MAXLEN);
  1467.         if (length < 0) {
  1468.             cmndmess("Error reading line.");
  1469.         }
  1470.         for (i = 0; i < length; i++) {
  1471.             sysputc(buffer [i], fd);
  1472.         }
  1473.         sysputc('\r', fd);
  1474.         sysputc('\n', fd);
  1475.         bufdn();
  1476.         if (bufatbot()) {
  1477.             break;
  1478.         }
  1479.     }
  1480.  
  1481.     /* Close the file. */
  1482.     sysfclose(fd);
  1483.  
  1484.     /* Restore the current line. */
  1485.     bufgo(oldline);
  1486.  
  1487.     TICKX("xtrct");
  1488. }
  1489.