home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 8 / FreshFishVol8-CD1.bin / gnu / src / amiga / ed-0.1-src.lha / ed-0.1 / main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-19  |  32.8 KB  |  1,528 lines

  1. /* main.c: This file contains the main control and user-interface routines
  2.    for the ed line editor. */
  3. /* ed line editor.
  4.    Copyright (C) 1993 Andrew Moore, Talke Studio
  5.    All Rights Reserved
  6.  
  7.    This program is free software; you can redistribute it and/or modify
  8.    it under the terms of the GNU General Public License as published by
  9.    the Free Software Foundation; either version 2, or (at your option)
  10.    any later version.
  11.  
  12.    This program is distributed in the hope that it will be useful, but
  13.    WITHOUT ANY WARRANTY; without even the implied warranty of
  14.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15.    General Public License for more details.
  16.  
  17.    You should have received a copy of the GNU General Public License
  18.    along with this program; if not, write to the Free Software
  19.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20. */
  21.  
  22. #ifndef lint
  23. char *copyright =
  24. "@(#) Copyright (c) 1993 Andrew Moore, Talke Studio. \n\
  25.  All rights reserved.\n";
  26. #endif /* not lint */
  27.  
  28. #ifndef lint
  29. static char *rcsid = "@(#)$Id: main.c,v 1.8 1994/04/19 17:54:52 alm Exp alm $";
  30. #endif /* not lint */
  31.  
  32. /*
  33.  * CREDITS
  34.  *
  35.  *      This program is based on the editor algorithm described in
  36.  *      Brian W. Kernighan and P. J. Plauger's book "Software Tools
  37.  *      in Pascal," Addison-Wesley, 1981.
  38.  *
  39.  *      The buffering algorithm is attributed to Rodney Ruddock of
  40.  *      the University of Guelph, Guelph, Ontario.
  41.  *
  42.  */
  43.  
  44. #include <sys/ioctl.h>
  45. #include <ctype.h>
  46. #include <setjmp.h>
  47. #include <pwd.h>
  48.  
  49. #include "ed.h"
  50. #include "getopt.h"
  51.  
  52.  
  53. #ifdef HAVE_SIGSETJMP
  54. sigjmp_buf env;
  55. #else
  56. jmp_buf env;
  57. #endif
  58.  
  59. /* static buffers */
  60. char *errmsg;            /* error message buffer */
  61. char stdinbuf[1];        /* stdin buffer */
  62. char *shcmd;            /* shell command buffer */
  63. int shcmdsz;            /* shell command buffer size */
  64. int shcmdi;            /* shell command buffer index */
  65. char *ibuf;            /* ed command-line buffer */
  66. int ibufsz;            /* ed command-line buffer size */
  67. char *ibufp;            /* pointer to ed command-line buffer */
  68.  
  69. /* global flags */
  70. int traditional = 0;        /* if set, be backwards compatible */
  71. int garrulous = 0;        /* if set, print all error messages */
  72. int isbinary;            /* if set, buffer contains ASCII NULs */
  73. int isglobal;            /* if set, doing a global command */
  74. int modified;            /* if set, buffer modified since last write */
  75. int mutex = 0;            /* if set, signals set "sigflags" */
  76. int red = 0;            /* if set, restrict shell/directory access */
  77. int scripted = 0;        /* if set, suppress diagnostics */
  78. int sigactive = 0;        /* if set, signal handlers are enabled */
  79. int sigflags = 0;        /* if set, signals received while mutex set */
  80.  
  81. char *old_filename;        /* default filename */
  82. long current_addr;        /* current address in editor buffer */
  83. long addr_last;            /* last address in editor buffer */
  84. int lineno;            /* script line number */
  85. char *prompt;            /* command-line prompt */
  86. char *dps = "*";        /* default command-line prompt */
  87.  
  88. /* The name this program was run with. */
  89. char *program_name;
  90.  
  91. /* If non-zero, display usage information and exit.  */
  92. int show_help = 0;
  93.  
  94. /* If non-zero, print the version on standard output and exit.  */
  95. int show_version = 0;
  96.  
  97. /* Long options equivalences.  */
  98. struct option long_options[] =
  99. {
  100.   {"help", no_argument, &show_help, 1},
  101.   {"prompt", required_argument, NULL, 'p'},
  102.   {"quiet", no_argument, NULL, 's'},
  103.   {"silent", no_argument, NULL, 's'},
  104.   {"traditional", no_argument, NULL, 'G'},
  105.   {"version", no_argument, &show_version, 1},
  106.   {0, 0, 0, 0},
  107. };
  108.  
  109. extern char version_string[];
  110. extern int optind;
  111. extern char *optarg;
  112.  
  113. /* usage: explain usage */
  114.  
  115. void
  116. usage (status)
  117.      int status;
  118. {
  119.   if (status != 0)
  120.     fprintf (stderr, "Try `%s --help' for more information.\n", program_name);
  121.   else
  122.     {
  123.       printf ("Usage: %s [OPTION]... [FILE]\n", program_name);
  124.       printf ("\
  125. \n\
  126.   -G, --traditional          use a few backward compatible features\n\
  127.   -p, --prompt=STRING        use STRING as an interactive prompt\n\
  128.   -s, -, --quiet, --silent   suppress diagnostics\n");
  129.       printf ("\
  130.       --help                 display this help\n\
  131.       --version              output version information\n\
  132. \n\
  133. Start edit by reading in FILE if given.  Read output of shell command\n\
  134. if FILE begins with a `!'\n");
  135.     }
  136.   exit (status);
  137. }
  138.  
  139. /* ed: line editor */
  140. int
  141. main (argc, argv)
  142.      int argc;
  143.      char **argv;
  144. {
  145.   int c, n;
  146.   long status = 0;
  147.  
  148.   program_name = argv[0];
  149.   red = (n = strlen (argv[0])) > 2 && argv[0][n - 3] == 'r';
  150. top:
  151.   while ((c = getopt_long (argc, argv, "Gp:s", long_options, NULL)) != EOF)
  152.     switch (c)
  153.       {
  154.       default:
  155.     usage (1);
  156.       case 0:
  157.     break;
  158.       case 'G':            /* backward compatibility */
  159.     traditional = 1;
  160.     break;
  161.       case 'p':            /* set prompt */
  162.     prompt = optarg;
  163.     break;
  164.       case 's':            /* run script */
  165.     scripted = 1;
  166.     break;
  167.       }
  168.   if (show_version)
  169.     {
  170.       printf ("%s\n", version_string);
  171.       exit (0);
  172.     }
  173.   if (show_help)
  174.     usage (0);
  175.   argv += optind;
  176.   argc -= optind;
  177.   if (argc && **argv == '-')
  178.     {
  179.       scripted = 1;
  180.       if (argc > 1)
  181.     {
  182.       optind = 0;
  183.       goto top;
  184.     }
  185.       argv++;
  186.       argc--;
  187.     }
  188.   init_buffers ();
  189.  
  190.   /* assert: reliable signals! */
  191. #ifdef SIGWINCH
  192.   handle_winch (SIGWINCH);
  193.   if (isatty (0))
  194.     reliable_signal (SIGWINCH, handle_winch);
  195. #endif
  196.   reliable_signal (SIGHUP, signal_hup);
  197.   reliable_signal (SIGQUIT, SIG_IGN);
  198.   reliable_signal (SIGINT, signal_int);
  199. #ifdef HAVE_SIGSETJMP
  200.   if (status = sigsetjmp (env, 1))
  201. #else
  202.   if (status = setjmp (env))
  203. #endif
  204.     {
  205.       fputs ("\n?\n", stderr);
  206.       sprintf (errmsg, "interrupt");
  207.     }
  208.   else
  209.     {
  210.       sigactive = 1;        /* enable signal handlers */
  211.       if (argc && **argv && is_legal_filename (*argv))
  212.     {
  213.       if (read_file (*argv, 0) < 0 && !isatty (0))
  214.         quit (2);
  215.       else if (**argv != '!')
  216.         strcpy (old_filename, *argv);
  217.     }
  218.       else if (argc)
  219.     {
  220.       fputs ("?\n", stderr);
  221.       if (**argv == '\0')
  222.         sprintf (errmsg, "invalid filename");
  223.       if (!isatty (0))
  224.         quit (2);
  225.     }
  226.     }
  227.   for (;;)
  228.     {
  229.       if (status < 0 && garrulous)
  230.     fprintf (stderr, "%s\n", errmsg);
  231.       if (prompt)
  232.     {
  233.       printf ("%s", prompt);
  234.       fflush (stdout);
  235.     }
  236.       if ((n = get_tty_line ()) < 0)
  237.     {
  238.       status = ERR;
  239.       continue;
  240.     }
  241.       else if (n == 0)
  242.     {
  243.       if (modified && !scripted)
  244.         {
  245.           fputs ("?\n", stderr);
  246.           sprintf (errmsg, "warning: file modified");
  247.           if (!isatty (0))
  248.         {
  249.           fprintf (stderr, garrulous ?
  250.                "script, line %d: %s\n" :
  251.                "", lineno, errmsg);
  252.           quit (2);
  253.         }
  254.           clearerr (stdin);
  255.           modified = 0;
  256.           status = EMOD;
  257.           continue;
  258.         }
  259.       else
  260.         quit (0);
  261.     }
  262.       else if (ibuf[n - 1] != '\n')
  263.     {
  264.       /* discard line */
  265.       sprintf (errmsg, "unexpected end-of-file");
  266.       clearerr (stdin);
  267.       status = ERR;
  268.       continue;
  269.     }
  270.       isglobal = 0;
  271.       if ((status = extract_addr_range ()) >= 0 &&
  272.       (status = exec_command ()) >= 0)
  273.     if (!status || status &&
  274.         (status = display_lines (current_addr, current_addr,
  275.                      status)) >= 0)
  276.       continue;
  277.       switch (status)
  278.     {
  279.     case EOF:
  280.       quit (0);
  281.     case EMOD:
  282.       modified = 0;
  283.       fputs ("?\n", stderr);    /* give warning */
  284.       sprintf (errmsg, "warning: file modified");
  285.       if (!isatty (0))
  286.         {
  287.           fprintf (stderr, garrulous ?
  288.                "script, line %d: %s\n" :
  289.                "", lineno, errmsg);
  290.           quit (2);
  291.         }
  292.       break;
  293.     case FATAL:
  294.       if (!isatty (0))
  295.         fprintf (stderr, garrulous ?
  296.              "script, line %d: %s\n" : "",
  297.              lineno, errmsg);
  298.       else
  299.         fprintf (stderr, garrulous ? "%s\n" : "",
  300.              errmsg);
  301.       quit (3);
  302.     default:
  303.       fputs ("?\n", stderr);
  304.       if (!isatty (0))
  305.         {
  306.           fprintf (stderr, garrulous ?
  307.                "script, line %d: %s\n" : "",
  308.                lineno, errmsg);
  309.           quit (2);
  310.         }
  311.       break;
  312.     }
  313.     }
  314.   /*NOTREACHED */
  315. }
  316.  
  317. long first_addr, second_addr, addr_cnt;
  318.  
  319. /* extract_addr_range: get line addresses from the command buffer until an
  320.    illegal address is seen; return status */
  321. int
  322. extract_addr_range ()
  323. {
  324.   long addr;
  325.  
  326.   addr_cnt = 0;
  327.   first_addr = second_addr = current_addr;
  328.   while ((addr = next_addr ()) >= 0)
  329.     {
  330.       addr_cnt++;
  331.       first_addr = second_addr;
  332.       second_addr = addr;
  333.       if (*ibufp != ',' && *ibufp != ';')
  334.     break;
  335.       else if (*ibufp++ == ';')
  336.     current_addr = addr;
  337.     }
  338.   if ((addr_cnt = min (addr_cnt, 2)) == 1 || second_addr != addr)
  339.     first_addr = second_addr;
  340.   return (addr == ERR) ? ERR : 0;
  341. }
  342.  
  343.  
  344. #define SKIP_BLANKS() \
  345.   while (isspace (*ibufp) && *ibufp != '\n')                \
  346.     ibufp++
  347.  
  348. #define MUST_BE_FIRST() \
  349.   do                                     \
  350.     {                                    \
  351.       if (!first)                            \
  352.     {                                \
  353.       sprintf (errmsg, "invalid address");                \
  354.       return ERR;                            \
  355.     }                                \
  356.     }                                    \
  357.   while (0)
  358.  
  359. /*  next_addr: return the next line address in the command buffer */
  360. long
  361. next_addr ()
  362. {
  363.   char *hd;
  364.   long addr = current_addr;
  365.   long n;
  366.   int first = 1;
  367.   int c;
  368.  
  369.   SKIP_BLANKS ();
  370.   for (hd = ibufp;; first = 0)
  371.     switch (c = *ibufp)
  372.       {
  373.       case '+':
  374.       case '\t':
  375.       case ' ':
  376.       case '-':
  377.       case '^':
  378.     ibufp++;
  379.     SKIP_BLANKS ();
  380.     if (isdigit (*ibufp))
  381.       {
  382.         STRTOL (n, ibufp);
  383.         addr += (c == '-' || c == '^') ? -n : n;
  384.       }
  385.     else if (!isspace (c))
  386.       addr += (c == '-' || c == '^') ? -1 : 1;
  387.     break;
  388.       case '0':
  389.       case '1':
  390.       case '2':
  391.       case '3':
  392.       case '4':
  393.       case '5':
  394.       case '6':
  395.       case '7':
  396.       case '8':
  397.       case '9':
  398.     MUST_BE_FIRST ();
  399.     STRTOL (addr, ibufp);
  400.     break;
  401.       case '.':
  402.       case '$':
  403.     MUST_BE_FIRST ();
  404.     ibufp++;
  405.     addr = (c == '.') ? current_addr : addr_last;
  406.     break;
  407.       case '/':
  408.       case '?':
  409.     MUST_BE_FIRST ();
  410.     if ((addr = get_matching_node_addr (
  411.                     get_compiled_pattern (), c == '/')) < 0)
  412.       return ERR;
  413.     else if (c == *ibufp)
  414.       ibufp++;
  415.     break;
  416.       case '\'':
  417.     MUST_BE_FIRST ();
  418.     ibufp++;
  419.     if ((addr = get_marked_node_addr (*ibufp++)) < 0)
  420.       return ERR;
  421.     break;
  422.       case '%':
  423.       case ',':
  424.       case ';':
  425.     if (first)
  426.       {
  427.         ibufp++;
  428.         addr_cnt++;
  429.         second_addr = (c == ';') ? current_addr : 1;
  430.         addr = addr_last;
  431.         break;
  432.       }
  433.     /* FALL THROUGH */
  434.       default:
  435.     if (ibufp == hd)
  436.       return EOF;
  437.     else if (addr < 0 || addr_last < addr)
  438.       {
  439.         sprintf (errmsg, "invalid address");
  440.         return ERR;
  441.       }
  442.     else
  443.       return addr;
  444.       }
  445.   /* NOTREACHED */
  446. }
  447.  
  448.  
  449. /* GET_THIRD_ADDR: get a legal address from the command buffer */
  450. #define GET_THIRD_ADDR(addr) \
  451.   do                                    \
  452.     {                                    \
  453.       long ol1, ol2;                            \
  454.       ol1 = first_addr, ol2 = second_addr;                \
  455.       if (extract_addr_range () < 0)                    \
  456.     return ERR;                            \
  457.       else if (traditional && addr_cnt == 0)                \
  458.     {                                \
  459.       sprintf (errmsg, "destination expected");            \
  460.       return ERR;                            \
  461.     }                                \
  462.       else if (second_addr < 0 || addr_last < second_addr)        \
  463.     {                                \
  464.       sprintf (errmsg, "invalid address");                \
  465.       return ERR;                            \
  466.     }                                \
  467.       (addr) = second_addr;                        \
  468.       first_addr = ol1, second_addr = ol2;                \
  469.     }                                    \
  470.   while (0)
  471.  
  472. /* GET_COMMAND_SUFFIX: verify the command suffix in the command buffer */
  473. #define GET_COMMAND_SUFFIX() \
  474.   do                                    \
  475.     {                                    \
  476.       int done = 0;                            \
  477.       do                                \
  478.     {                                \
  479.       switch (*ibufp)                        \
  480.         {                                \
  481.         case 'p':                            \
  482.           gflag |= GPR, ibufp++;                    \
  483.           break;                            \
  484.         case 'l':                            \
  485.           gflag |= GLS, ibufp++;                    \
  486.           break;                            \
  487.         case 'n':                            \
  488.           gflag |= GNP, ibufp++;                    \
  489.           break;                            \
  490.         default:                            \
  491.           done++;                            \
  492.         }                                \
  493.     }                                \
  494.       while (!done);                            \
  495.       if (*ibufp++ != '\n')                        \
  496.     {                                \
  497.       sprintf (errmsg, "invalid command suffix");            \
  498.       return ERR;                            \
  499.     }                                \
  500.     }                                    \
  501.   while (0)
  502.  
  503. /* sflags */
  504. #define SGG 001            /* complement previous global substitute suffix */
  505. #define SGP 002            /* complement previous print suffix */
  506. #define SGR 004            /* use last regex instead of last pat */
  507. #define SGF 010            /* repeat last substitution */
  508.  
  509. int patlock = 0;        /* if set, pattern not freed by get_compiled_pattern() */
  510.  
  511. long rows = 22;            /* scroll length: ws_row - 2 */
  512.  
  513. /* exec_command: execute the next command in command buffer; return print
  514.    request, if any */
  515. int
  516. exec_command ()
  517. {
  518.   extern long u_current_addr;
  519.   extern long u_addr_last;
  520.  
  521.   static pattern_t *pat = NULL;
  522.   static int sgflag = 0;
  523.   static int sgnum = 0;
  524.  
  525.   pattern_t *tpat;
  526.   char *fnp;
  527.   int gflag = 0;
  528.   int sflags = 0;
  529.   long addr = 0;
  530.   int n = 0;
  531.   int c;
  532.  
  533.   SKIP_BLANKS ();
  534.   switch (c = *ibufp++)
  535.     {
  536.     case 'a':
  537.       GET_COMMAND_SUFFIX ();
  538.       if (!isglobal)
  539.     clear_undo_stack ();
  540.       if (append_lines (second_addr) < 0)
  541.     return ERR;
  542.       break;
  543.     case 'c':
  544.       if (check_addr_range (current_addr, current_addr) < 0)
  545.     return ERR;
  546.       GET_COMMAND_SUFFIX ();
  547.       if (!isglobal)
  548.     clear_undo_stack ();
  549.       if (delete_lines (first_addr, second_addr) < 0 ||
  550.       append_lines (current_addr) < 0)
  551.     return ERR;
  552.       break;
  553.     case 'd':
  554.       if (check_addr_range (current_addr, current_addr) < 0)
  555.     return ERR;
  556.       GET_COMMAND_SUFFIX ();
  557.       if (!isglobal)
  558.     clear_undo_stack ();
  559.       if (delete_lines (first_addr, second_addr) < 0)
  560.     return ERR;
  561.       else if ((addr = INC_MOD (current_addr, addr_last)) != 0)
  562.     current_addr = addr;
  563.       break;
  564.     case 'e':
  565.       if (modified && !scripted)
  566.     return EMOD;
  567.       /* fall through */
  568.     case 'E':
  569.       if (addr_cnt > 0)
  570.     {
  571.       sprintf (errmsg, "unexpected address");
  572.       return ERR;
  573.     }
  574.       else if (!isspace (*ibufp))
  575.     {
  576.       sprintf (errmsg, "unexpected command suffix");
  577.       return ERR;
  578.     }
  579.       else if ((fnp = get_filename ()) == NULL)
  580.     return ERR;
  581.       GET_COMMAND_SUFFIX ();
  582.       if (delete_lines (1, addr_last) < 0)
  583.     return ERR;
  584.       clear_undo_stack ();
  585.       if (close_sbuf () < 0)
  586.     return ERR;
  587.       else if (open_sbuf () < 0)
  588.     return FATAL;
  589.       if (*fnp && *fnp != '!')
  590.     strcpy (old_filename, fnp);
  591.       if (traditional && *fnp == '\0' && *old_filename == '\0')
  592.     {
  593.       sprintf (errmsg, "no current filename");
  594.       return ERR;
  595.     }
  596.       else if (read_file (*fnp ? fnp : old_filename, 0) < 0)
  597.     return ERR;
  598.       clear_undo_stack ();
  599.       modified = 0;
  600.       u_current_addr = u_addr_last = -1;
  601.       break;
  602.     case 'f':
  603.       if (addr_cnt > 0)
  604.     {
  605.       sprintf (errmsg, "unexpected address");
  606.       return ERR;
  607.     }
  608.       else if (!isspace (*ibufp))
  609.     {
  610.       sprintf (errmsg, "unexpected command suffix");
  611.       return ERR;
  612.     }
  613.       else if ((fnp = get_filename ()) == NULL)
  614.     return ERR;
  615.       else if (*fnp == '!')
  616.     {
  617.       sprintf (errmsg, "invalid redirection");
  618.       return ERR;
  619.     }
  620.       GET_COMMAND_SUFFIX ();
  621.       if (*fnp)
  622.     strcpy (old_filename, fnp);
  623.       printf ("%s\n", strip_escapes (old_filename));
  624.       break;
  625.     case 'g':
  626.     case 'v':
  627.     case 'G':
  628.     case 'V':
  629.       if (isglobal)
  630.     {
  631.       sprintf (errmsg, "cannot nest global commands");
  632.       return ERR;
  633.     }
  634.       else if (check_addr_range (1, addr_last) < 0)
  635.     return ERR;
  636.       else if (build_active_list (c == 'g' || c == 'G') < 0)
  637.     return ERR;
  638.       else if (n = (c == 'G' || c == 'V'))
  639.     GET_COMMAND_SUFFIX ();
  640.       isglobal++;
  641.       if (exec_global (n, gflag) < 0)
  642.     return ERR;
  643.       break;
  644.     case 'h':
  645.       if (addr_cnt > 0)
  646.     {
  647.       sprintf (errmsg, "unexpected address");
  648.       return ERR;
  649.     }
  650.       GET_COMMAND_SUFFIX ();
  651.       if (*errmsg)
  652.     fprintf (stderr, "%s\n", errmsg);
  653.       break;
  654.     case 'H':
  655.       if (addr_cnt > 0)
  656.     {
  657.       sprintf (errmsg, "unexpected address");
  658.       return ERR;
  659.     }
  660.       GET_COMMAND_SUFFIX ();
  661.       if ((garrulous = 1 - garrulous) && *errmsg)
  662.     fprintf (stderr, "%s\n", errmsg);
  663.       break;
  664.     case 'i':
  665.       if (second_addr == 0)
  666.     {
  667.       sprintf (errmsg, "invalid address");
  668.       return ERR;
  669.     }
  670.       GET_COMMAND_SUFFIX ();
  671.       if (!isglobal)
  672.     clear_undo_stack ();
  673.       if (append_lines (second_addr - 1) < 0)
  674.     return ERR;
  675.       break;
  676.     case 'j':
  677.       if (check_addr_range (current_addr, current_addr + 1) < 0)
  678.     return ERR;
  679.       GET_COMMAND_SUFFIX ();
  680.       if (!isglobal)
  681.     clear_undo_stack ();
  682.       if (first_addr != second_addr &&
  683.       join_lines (first_addr, second_addr) < 0)
  684.     return ERR;
  685.       break;
  686.     case 'k':
  687.       c = *ibufp++;
  688.       if (second_addr == 0)
  689.     {
  690.       sprintf (errmsg, "invalid address");
  691.       return ERR;
  692.     }
  693.       GET_COMMAND_SUFFIX ();
  694.       if (mark_line_node (get_addressed_line_node (second_addr), c) < 0)
  695.     return ERR;
  696.       break;
  697.     case 'l':
  698.       if (check_addr_range (current_addr, current_addr) < 0)
  699.     return ERR;
  700.       GET_COMMAND_SUFFIX ();
  701.       if (display_lines (first_addr, second_addr, gflag | GLS) < 0)
  702.     return ERR;
  703.       gflag = 0;
  704.       break;
  705.     case 'm':
  706.       if (check_addr_range (current_addr, current_addr) < 0)
  707.     return ERR;
  708.       GET_THIRD_ADDR (addr);
  709.       if (first_addr <= addr && addr < second_addr)
  710.     {
  711.       sprintf (errmsg, "invalid destination");
  712.       return ERR;
  713.     }
  714.       GET_COMMAND_SUFFIX ();
  715.       if (!isglobal)
  716.     clear_undo_stack ();
  717.       if (move_lines (addr) < 0)
  718.     return ERR;
  719.       break;
  720.     case 'n':
  721.       if (check_addr_range (current_addr, current_addr) < 0)
  722.     return ERR;
  723.       GET_COMMAND_SUFFIX ();
  724.       if (display_lines (first_addr, second_addr, gflag | GNP) < 0)
  725.     return ERR;
  726.       gflag = 0;
  727.       break;
  728.     case 'p':
  729.       if (check_addr_range (current_addr, current_addr) < 0)
  730.     return ERR;
  731.       GET_COMMAND_SUFFIX ();
  732.       if (display_lines (first_addr, second_addr, gflag | GPR) < 0)
  733.     return ERR;
  734.       gflag = 0;
  735.       break;
  736.     case 'P':
  737.       if (addr_cnt > 0)
  738.     {
  739.       sprintf (errmsg, "unexpected address");
  740.       return ERR;
  741.     }
  742.       GET_COMMAND_SUFFIX ();
  743.       prompt = prompt ? NULL : optarg ? optarg : dps;
  744.       break;
  745.     case 'q':
  746.     case 'Q':
  747.       if (addr_cnt > 0)
  748.     {
  749.       sprintf (errmsg, "unexpected address");
  750.       return ERR;
  751.     }
  752.       GET_COMMAND_SUFFIX ();
  753.       gflag = (modified && !scripted && c == 'q') ? EMOD : EOF;
  754.       break;
  755.     case 'r':
  756.       if (!isspace (*ibufp))
  757.     {
  758.       sprintf (errmsg, "unexpected command suffix");
  759.       return ERR;
  760.     }
  761.       else if (addr_cnt == 0)
  762.     second_addr = addr_last;
  763.       if ((fnp = get_filename ()) == NULL)
  764.     return ERR;
  765.       GET_COMMAND_SUFFIX ();
  766.       if (!isglobal)
  767.     clear_undo_stack ();
  768.       if (*old_filename == '\0' && *fnp != '!')
  769.     strcpy (old_filename, fnp);
  770.       if (traditional && *fnp == '\0' && *old_filename == '\0')
  771.     {
  772.       sprintf (errmsg, "no current filename");
  773.       return ERR;
  774.     }
  775.       if ((addr = read_file (*fnp ? fnp : old_filename, second_addr)) < 0)
  776.     return ERR;
  777.       else if (addr && addr != addr_last)
  778.     modified = 1;
  779.       break;
  780.     case 's':
  781.       do
  782.     {
  783.       switch (*ibufp)
  784.         {
  785.         case '\n':
  786.           sflags |= SGF;
  787.           break;
  788.         case 'g':
  789.           sflags |= SGG;
  790.           ibufp++;
  791.           break;
  792.         case 'p':
  793.           sflags |= SGP;
  794.           ibufp++;
  795.           break;
  796.         case 'r':
  797.           sflags |= SGR;
  798.           ibufp++;
  799.           break;
  800.         case '0':
  801.         case '1':
  802.         case '2':
  803.         case '3':
  804.         case '4':
  805.         case '5':
  806.         case '6':
  807.         case '7':
  808.         case '8':
  809.         case '9':
  810.           STRTOL (sgnum, ibufp);
  811.           sflags |= SGF;
  812.           sgflag &= ~GSG;    /* override GSG */
  813.           break;
  814.         default:
  815.           if (sflags)
  816.         {
  817.           sprintf (errmsg, "invalid command suffix");
  818.           return ERR;
  819.         }
  820.         }
  821.     }
  822.       while (sflags && *ibufp != '\n');
  823.       if (sflags && !pat)
  824.     {
  825.       sprintf (errmsg, "no previous substitution");
  826.       return ERR;
  827.     }
  828.       else if (sflags & SGG)
  829.     sgnum = 0;        /* override numeric arg */
  830.       if (*ibufp != '\n' && *(ibufp + 1) == '\n')
  831.     {
  832.       sprintf (errmsg, "invalid pattern delimiter");
  833.       return ERR;
  834.     }
  835.       tpat = pat;
  836.       SPL1 ();
  837.       if ((!sflags || (sflags & SGR)) &&
  838.       (tpat = get_compiled_pattern ()) == NULL)
  839.     {
  840.       SPL0 ();
  841.       return ERR;
  842.     }
  843.       else if (tpat != pat)
  844.     {
  845.       if (pat)
  846.         {
  847.           regfree (pat);
  848.           free (pat);
  849.         }
  850.       pat = tpat;
  851.       patlock = 1;        /* reserve pattern */
  852.     }
  853.       SPL0 ();
  854.       if (!sflags && extract_subst_tail (&sgflag, &sgnum) < 0)
  855.     return ERR;
  856.       else if (isglobal)
  857.     sgflag |= GLB;
  858.       else
  859.     sgflag &= ~GLB;
  860.       if (sflags & SGG)
  861.     sgflag ^= GSG;
  862.       if (sflags & SGP)
  863.     sgflag ^= GPR, sgflag &= ~(GLS | GNP);
  864.       do
  865.     {
  866.       switch (*ibufp)
  867.         {
  868.         case 'p':
  869.           sgflag |= GPR, ibufp++;
  870.           break;
  871.         case 'l':
  872.           sgflag |= GLS, ibufp++;
  873.           break;
  874.         case 'n':
  875.           sgflag |= GNP, ibufp++;
  876.           break;
  877.         default:
  878.           n++;
  879.         }
  880.     }
  881.       while (!n);
  882.       if (check_addr_range (current_addr, current_addr) < 0)
  883.     return ERR;
  884.       GET_COMMAND_SUFFIX ();
  885.       if (!isglobal)
  886.     clear_undo_stack ();
  887.       if (search_and_replace (pat, sgflag, sgnum) < 0)
  888.     return ERR;
  889.       break;
  890.     case 't':
  891.       if (check_addr_range (current_addr, current_addr) < 0)
  892.     return ERR;
  893.       GET_THIRD_ADDR (addr);
  894.       GET_COMMAND_SUFFIX ();
  895.       if (!isglobal)
  896.     clear_undo_stack ();
  897.       if (copy_lines (addr) < 0)
  898.     return ERR;
  899.       break;
  900.     case 'u':
  901.       if (addr_cnt > 0)
  902.     {
  903.       sprintf (errmsg, "unexpected address");
  904.       return ERR;
  905.     }
  906.       GET_COMMAND_SUFFIX ();
  907.       if (pop_undo_stack () < 0)
  908.     return ERR;
  909.       break;
  910.     case 'w':
  911.     case 'W':
  912.       if ((n = *ibufp) == 'q' || n == 'Q')
  913.     {
  914.       gflag = EOF;
  915.       ibufp++;
  916.     }
  917.       if (!isspace (*ibufp))
  918.     {
  919.       sprintf (errmsg, "unexpected command suffix");
  920.       return ERR;
  921.     }
  922.       else if ((fnp = get_filename ()) == NULL)
  923.     return ERR;
  924.       if (addr_cnt == 0 && !addr_last)
  925.     first_addr = second_addr = 0;
  926.       else if (check_addr_range (1, addr_last) < 0)
  927.     return ERR;
  928.       GET_COMMAND_SUFFIX ();
  929.       if (*old_filename == '\0' && *fnp != '!')
  930.     strcpy (old_filename, fnp);
  931.       if (traditional && *fnp == '\0' && *old_filename == '\0')
  932.     {
  933.       sprintf (errmsg, "no current filename");
  934.       return ERR;
  935.     }
  936.       if ((addr = write_file (*fnp ? fnp : old_filename,
  937.               (c == 'W') ? "a" : "w", first_addr, second_addr)) < 0)
  938.     return ERR;
  939.       else if (addr == addr_last)
  940.     modified = 0;
  941.       else if (modified && !scripted && n == 'q')
  942.     gflag = EMOD;
  943.       break;
  944.     case 'z':
  945.       if (check_addr_range
  946.          (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0)
  947.     return ERR;
  948.       else if ('0' < *ibufp && *ibufp <= '9')
  949.     STRTOL (rows, ibufp);
  950.       GET_COMMAND_SUFFIX ();
  951.       if (display_lines (second_addr, min (addr_last,
  952.                        second_addr + rows), gflag) < 0)
  953.     return ERR;
  954.       gflag = 0;
  955.       break;
  956.     case '=':
  957.       GET_COMMAND_SUFFIX ();
  958.       printf ("%d\n", addr_cnt ? second_addr : addr_last);
  959.       break;
  960.     case '!':
  961.       if (addr_cnt > 0)
  962.     {
  963.       sprintf (errmsg, "unexpected address");
  964.       return ERR;
  965.     }
  966.       else if ((sflags = get_shell_command ()) < 0)
  967.     return ERR;
  968.       GET_COMMAND_SUFFIX ();
  969.       if (sflags)
  970.     printf ("%s\n", shcmd + 1);
  971.       system (shcmd + 1);
  972.       if (!scripted)
  973.     printf ("!\n");
  974.       break;
  975.     case '\n':
  976.       if (check_addr_range
  977.          (first_addr = 1, current_addr + (traditional ? 1 : !isglobal)) < 0 ||
  978.          display_lines (second_addr, second_addr, 0) < 0)
  979.     return ERR;
  980.       break;
  981.     case '#':
  982.       while (*ibufp++ != '\n')
  983.      ;
  984.       break;
  985.     default:
  986.       sprintf (errmsg, "unknown command");
  987.       return ERR;
  988.     }
  989.   return gflag;
  990. }
  991.  
  992.  
  993. /* check_addr_range: return status of address range check */
  994. int
  995. check_addr_range (n, m)
  996.      long n, m;
  997. {
  998.   if (addr_cnt == 0)
  999.     {
  1000.       first_addr = n;
  1001.       second_addr = m;
  1002.     }
  1003.   if (first_addr > second_addr || 1 > first_addr ||
  1004.       second_addr > addr_last)
  1005.     {
  1006.       sprintf (errmsg, "invalid address");
  1007.       return ERR;
  1008.     }
  1009.   return 0;
  1010. }
  1011.  
  1012.  
  1013. /* get_matching_node_addr: return the address of the next line matching a
  1014.    pattern in a given direction.  wrap around begin/end of editor buffer if
  1015.    necessary */
  1016. long
  1017. get_matching_node_addr (pat, dir)
  1018.      pattern_t *pat;
  1019.      int dir;
  1020. {
  1021.   char *s;
  1022.   long n = current_addr;
  1023.   line_t *lp;
  1024.  
  1025.   if (!pat)
  1026.     return ERR;
  1027.   do
  1028.     {
  1029.       if (n = dir ? INC_MOD (n, addr_last) : DEC_MOD (n, addr_last))
  1030.     {
  1031.       lp = get_addressed_line_node (n);
  1032.       if ((s = get_sbuf_line (lp)) == NULL)
  1033.         return ERR;
  1034.       if (isbinary)
  1035.         NUL_TO_NEWLINE (s, lp->len);
  1036.       if (!regexec (pat, s, 0, NULL, 0))
  1037.         return n;
  1038.     }
  1039.     }
  1040.   while (n != current_addr);
  1041.   sprintf (errmsg, "no match");
  1042.   return ERR;
  1043. }
  1044.  
  1045.  
  1046. /* get_filename: return pointer to copy of filename in the command buffer */
  1047. char *
  1048. get_filename ()
  1049. {
  1050.   static char *file = NULL;
  1051.   static int filesz = 0;
  1052.  
  1053.   int n;
  1054.  
  1055.   if (*ibufp != '\n')
  1056.     {
  1057.       SKIP_BLANKS ();
  1058.       if (*ibufp == '\n')
  1059.     {
  1060.       sprintf (errmsg, "invalid filename");
  1061.       return NULL;
  1062.     }
  1063.       else if ((ibufp = get_extended_line (&n, 1)) == NULL)
  1064.     return NULL;
  1065.       else if (*ibufp == '!')
  1066.     {
  1067.       ibufp++;
  1068.       if ((n = get_shell_command ()) < 0)
  1069.         return NULL;
  1070.       if (n)
  1071.         printf ("%s\n", shcmd + 1);
  1072.       return shcmd;
  1073.     }
  1074.       else if (n > PATH_MAX)
  1075.     {
  1076.       sprintf (errmsg, "filename too long");
  1077.       return NULL;
  1078.     }
  1079.     }
  1080.   else if (!traditional && *old_filename == '\0')
  1081.     {
  1082.       sprintf (errmsg, "no current filename");
  1083.       return NULL;
  1084.     }
  1085.   REALLOC (file, filesz, PATH_MAX + 1, NULL);
  1086.   for (n = 0; *ibufp != '\n';)
  1087.     file[n++] = *ibufp++;
  1088.   file[n] = '\0';
  1089.   return is_legal_filename (file) ? file : NULL;
  1090. }
  1091.  
  1092.  
  1093. /* get_shell_command: read a shell command from stdin; return substitution
  1094.    status */
  1095. int
  1096. get_shell_command ()
  1097. {
  1098.   static char *buf = NULL;
  1099.   static int n = 0;
  1100.  
  1101.   char *s;            /* substitution char pointer */
  1102.   int i = 0;
  1103.   int j = 0;
  1104.  
  1105.   if (red)
  1106.     {
  1107.       sprintf (errmsg, "shell access restricted");
  1108.       return ERR;
  1109.     }
  1110.   else if ((s = ibufp = get_extended_line (&j, 1)) == NULL)
  1111.     return ERR;
  1112.   REALLOC (buf, n, j + 1, ERR);
  1113.   buf[i++] = '!';        /* prefix command w/ bang */
  1114.   while (*ibufp != '\n')
  1115.     switch (*ibufp)
  1116.       {
  1117.       default:
  1118.     REALLOC (buf, n, i + 2, ERR);
  1119.     buf[i++] = *ibufp;
  1120.     if (*ibufp++ == '\\')
  1121.       buf[i++] = *ibufp++;
  1122.     break;
  1123.       case '!':
  1124.     if (s != ibufp)
  1125.       {
  1126.         REALLOC (buf, n, i + 1, ERR);
  1127.         buf[i++] = *ibufp++;
  1128.       }
  1129.     else if (shcmd == NULL || (traditional && *(shcmd + 1) == '\0'))
  1130.       {
  1131.         sprintf (errmsg, "no previous command");
  1132.         return ERR;
  1133.       }
  1134.     else
  1135.       {
  1136.         REALLOC (buf, n, i + shcmdi, ERR);
  1137.         for (s = shcmd + 1; s < shcmd + shcmdi;)
  1138.           buf[i++] = *s++;
  1139.         s = ibufp++;
  1140.       }
  1141.     break;
  1142.       case '%':
  1143.     if (*old_filename == '\0')
  1144.       {
  1145.         sprintf (errmsg, "no current filename");
  1146.         return ERR;
  1147.       }
  1148.     j = strlen (s = strip_escapes (old_filename));
  1149.     REALLOC (buf, n, i + j, ERR);
  1150.     while (j--)
  1151.       buf[i++] = *s++;
  1152.     s = ibufp++;
  1153.     break;
  1154.       }
  1155.   REALLOC (shcmd, shcmdsz, i + 1, ERR);
  1156.   memcpy (shcmd, buf, i);
  1157.   shcmd[shcmdi = i] = '\0';
  1158.   return *s == '!' || *s == '%';
  1159. }
  1160.  
  1161.  
  1162. /* append_lines: insert text from stdin to after line n; stop when either a
  1163.    single period is read or EOF; return status */
  1164. int
  1165. append_lines (n)
  1166.      long n;
  1167. {
  1168.   int l;
  1169.   char *lp = ibuf;
  1170.   char *eot;
  1171.   undo_t *up = NULL;
  1172.  
  1173.   for (current_addr = n;;)
  1174.     {
  1175.       if (!isglobal)
  1176.     {
  1177.       if ((l = get_tty_line ()) < 0)
  1178.         return ERR;
  1179.       else if (l == 0 || ibuf[l - 1] != '\n')
  1180.         {
  1181.           clearerr (stdin);
  1182.           return l ? EOF : 0;
  1183.         }
  1184.       lp = ibuf;
  1185.     }
  1186.       else if (*(lp = ibufp) == '\0')
  1187.     return 0;
  1188.       else
  1189.     {
  1190.       while (*ibufp++ != '\n')
  1191.         ;
  1192.       l = ibufp - lp;
  1193.     }
  1194.       if (l == 2 && lp[0] == '.' && lp[1] == '\n')
  1195.     {
  1196.       return 0;
  1197.     }
  1198.       eot = lp + l;
  1199.       SPL1 ();
  1200.       do
  1201.     {
  1202.       if ((lp = put_sbuf_line (lp)) == NULL)
  1203.         {
  1204.           SPL0 ();
  1205.           return ERR;
  1206.         }
  1207.       else if (up)
  1208.         up->t = get_addressed_line_node (current_addr);
  1209.       else if ((up = push_undo_stack (UADD, current_addr,
  1210.                       current_addr)) == NULL)
  1211.         {
  1212.           SPL0 ();
  1213.           return ERR;
  1214.         }
  1215.     }
  1216.       while (lp != eot);
  1217.       modified = 1;
  1218.       SPL0 ();
  1219.     }
  1220.   /* NOTREACHED */
  1221. }
  1222.  
  1223.  
  1224. /* join_lines: replace a range of lines with the joined text of those lines */
  1225. int
  1226. join_lines (from, to)
  1227.      long from;
  1228.      long to;
  1229. {
  1230.   static char *buf = NULL;
  1231.   static int n;
  1232.  
  1233.   char *s;
  1234.   int size = 0;
  1235.   line_t *bp, *ep;
  1236.  
  1237.   ep = get_addressed_line_node (INC_MOD (to, addr_last));
  1238.   bp = get_addressed_line_node (from);
  1239.   for (; bp != ep; bp = bp->q_forw)
  1240.     {
  1241.       if ((s = get_sbuf_line (bp)) == NULL)
  1242.     return ERR;
  1243.       REALLOC (buf, n, size + bp->len, ERR);
  1244.       memcpy (buf + size, s, bp->len);
  1245.       size += bp->len;
  1246.     }
  1247.   REALLOC (buf, n, size + 2, ERR);
  1248.   memcpy (buf + size, "\n", 2);
  1249.   if (delete_lines (from, to) < 0)
  1250.     return ERR;
  1251.   current_addr = from - 1;
  1252.   SPL1 ();
  1253.   if (put_sbuf_line (buf) == NULL ||
  1254.       push_undo_stack (UADD, current_addr, current_addr) == NULL)
  1255.     {
  1256.       SPL0 ();
  1257.       return ERR;
  1258.     }
  1259.   modified = 1;
  1260.   SPL0 ();
  1261.   return 0;
  1262. }
  1263.  
  1264.  
  1265. /* move_lines: move a range of lines */
  1266. int
  1267. move_lines (addr)
  1268.      long addr;
  1269. {
  1270.   line_t *b1, *a1, *b2, *a2;
  1271.   long n = INC_MOD (second_addr, addr_last);
  1272.   long p = first_addr - 1;
  1273.   int done = (addr == first_addr - 1 || addr == second_addr);
  1274.  
  1275.   SPL1 ();
  1276.   if (done)
  1277.     {
  1278.       a2 = get_addressed_line_node (n);
  1279.       b2 = get_addressed_line_node (p);
  1280.       current_addr = second_addr;
  1281.     }
  1282.   else if (push_undo_stack (UMOV, p, n) == NULL ||
  1283.        push_undo_stack (UMOV, addr, INC_MOD (addr, addr_last)) == NULL)
  1284.     {
  1285.       SPL0 ();
  1286.       return ERR;
  1287.     }
  1288.   else
  1289.     {
  1290.       a1 = get_addressed_line_node (n);
  1291.       if (addr < first_addr)
  1292.     {
  1293.       b1 = get_addressed_line_node (p);
  1294.       b2 = get_addressed_line_node (addr);
  1295.       /* this get_addressed_line_node last! */
  1296.     }
  1297.       else
  1298.     {
  1299.       b2 = get_addressed_line_node (addr);
  1300.       b1 = get_addressed_line_node (p);
  1301.       /* this get_addressed_line_node last! */
  1302.     }
  1303.       a2 = b2->q_forw;
  1304.       REQUE (b2, b1->q_forw);
  1305.       REQUE (a1->q_back, a2);
  1306.       REQUE (b1, a1);
  1307.       current_addr = addr + ((addr < first_addr) ?
  1308.                  second_addr - first_addr + 1 : 0);
  1309.     }
  1310.   if (isglobal)
  1311.     unset_active_nodes (b2->q_forw, a2);
  1312.   modified = 1;
  1313.   SPL0 ();
  1314.   return 0;
  1315. }
  1316.  
  1317.  
  1318. /* copy_lines: copy a range of lines; return status */
  1319. int
  1320. copy_lines (addr)
  1321.      long addr;
  1322. {
  1323.   line_t *lp, *np = get_addressed_line_node (first_addr);
  1324.   undo_t *up = NULL;
  1325.   long n = second_addr - first_addr + 1;
  1326.   long m = 0;
  1327.  
  1328.   current_addr = addr;
  1329.   if (first_addr <= addr && addr < second_addr)
  1330.     {
  1331.       n = addr - first_addr + 1;
  1332.       m = second_addr - addr;
  1333.     }
  1334.   for (; n > 0; n = m, m = 0, np = get_addressed_line_node (current_addr + 1))
  1335.     for (; n-- > 0; np = np->q_forw)
  1336.       {
  1337.     SPL1 ();
  1338.     if ((lp = dup_line_node (np)) == NULL)
  1339.       {
  1340.         SPL0 ();
  1341.         return ERR;
  1342.       }
  1343.     add_line_node (lp);
  1344.     if (up)
  1345.       up->t = lp;
  1346.     else if ((up = push_undo_stack (UADD, current_addr,
  1347.                     current_addr)) == NULL)
  1348.       {
  1349.         SPL0 ();
  1350.         return ERR;
  1351.       }
  1352.     modified = 1;
  1353.     SPL0 ();
  1354.       }
  1355.   return 0;
  1356. }
  1357.  
  1358.  
  1359. /* delete_lines: delete a range of lines */
  1360. int
  1361. delete_lines (from, to)
  1362.      long from, to;
  1363. {
  1364.   line_t *n, *p;
  1365.  
  1366.   SPL1 ();
  1367.   if (push_undo_stack (UDEL, from, to) == NULL)
  1368.     {
  1369.       SPL0 ();
  1370.       return ERR;
  1371.     }
  1372.   n = get_addressed_line_node (INC_MOD (to, addr_last));
  1373.   p = get_addressed_line_node (from - 1);
  1374.   /* this get_addressed_line_node last! */
  1375.   if (isglobal)
  1376.     unset_active_nodes (p->q_forw, n);
  1377.   REQUE (p, n);
  1378.   addr_last -= to - from + 1;
  1379.   current_addr = from - 1;
  1380.   modified = 1;
  1381.   SPL0 ();
  1382.   return 0;
  1383. }
  1384.  
  1385.  
  1386. /* display_lines: print a range of lines to stdout */
  1387. int
  1388. display_lines (from, to, gflag)
  1389.      long from;
  1390.      long to;
  1391.      int gflag;
  1392. {
  1393.   line_t *bp;
  1394.   line_t *ep;
  1395.   char *s;
  1396.  
  1397.   if (!from)
  1398.     {
  1399.       sprintf (errmsg, "invalid address");
  1400.       return ERR;
  1401.     }
  1402.   ep = get_addressed_line_node (INC_MOD (to, addr_last));
  1403.   bp = get_addressed_line_node (from);
  1404.   for (; bp != ep; bp = bp->q_forw)
  1405.     {
  1406.       if ((s = get_sbuf_line (bp)) == NULL)
  1407.     return ERR;
  1408.       if (put_tty_line (s, bp->len, current_addr = from++, gflag) < 0)
  1409.     return ERR;
  1410.     }
  1411.   return 0;
  1412. }
  1413.  
  1414.  
  1415. #define MAXMARK 26        /* max number of marks */
  1416.  
  1417. line_t *mark[MAXMARK];        /* line markers */
  1418. int markno;            /* line marker count */
  1419.  
  1420. /* mark_line_node: set a line node mark */
  1421. int
  1422. mark_line_node (lp, n)
  1423.      line_t *lp;
  1424.      int n;
  1425. {
  1426.   if (!islower (n))
  1427.     {
  1428.       sprintf (errmsg, "invalid mark character");
  1429.       return ERR;
  1430.     }
  1431.   else if (mark[n - 'a'] == NULL)
  1432.     markno++;
  1433.   mark[n - 'a'] = lp;
  1434.   return 0;
  1435. }
  1436.  
  1437.  
  1438. /* get_marked_node_addr: return address of a marked line */
  1439. long
  1440. get_marked_node_addr (n)
  1441.      int n;
  1442. {
  1443.   if (!islower (n))
  1444.     {
  1445.       sprintf (errmsg, "invalid mark character");
  1446.       return ERR;
  1447.     }
  1448.   return get_line_node_addr (mark[n - 'a']);
  1449. }
  1450.  
  1451.  
  1452. /* unmark_line_node: clear line node mark */
  1453. void
  1454. unmark_line_node (lp)
  1455.      line_t *lp;
  1456. {
  1457.   int i;
  1458.  
  1459.   for (i = 0; markno && i < MAXMARK; i++)
  1460.     if (mark[i] == lp)
  1461.       {
  1462.     mark[i] = NULL;
  1463.     markno--;
  1464.       }
  1465. }
  1466.  
  1467.  
  1468. /* dup_line_node: return a pointer to a copy of a line node */
  1469. line_t *
  1470. dup_line_node (lp)
  1471.      line_t *lp;
  1472. {
  1473.   line_t *np;
  1474.  
  1475.   if ((np = (line_t *) malloc (sizeof (line_t))) == NULL)
  1476.     {
  1477.       fprintf (stderr, "%s\n", strerror (errno));
  1478.       sprintf (errmsg, "out of memory");
  1479.       return NULL;
  1480.     }
  1481.   np->seek = lp->seek;
  1482.   np->len = lp->len;
  1483.   return np;
  1484. }
  1485.  
  1486.  
  1487. /* has_trailing_escape:  return the parity of escapes preceding a character
  1488.    in a string */
  1489. int
  1490. has_trailing_escape (s, t)
  1491.      char *s;
  1492.      char *t;
  1493. {
  1494.   return (s == t || *(t - 1) != '\\') ? 0 : !has_trailing_escape (s, t - 1);
  1495. }
  1496.  
  1497.  
  1498. /* strip_escapes: return copy of escaped string of at most length PATH_MAX */
  1499. char *
  1500. strip_escapes (s)
  1501.      char *s;
  1502. {
  1503.   static char *file = NULL;
  1504.   static int filesz = 0;
  1505.  
  1506.   int i = 0;
  1507.  
  1508.   REALLOC (file, filesz, PATH_MAX + 1, NULL);
  1509.   /* assert: no trailing escape */
  1510.   while (file[i++] = (*s == '\\') ? *++s : *s)
  1511.     s++;
  1512.   return file;
  1513. }
  1514.  
  1515.  
  1516. /* is_legal_filename: return a legal filename */
  1517. int
  1518. is_legal_filename (s)
  1519.      char *s;
  1520. {
  1521.   if (red && (*s == '!' || !strcmp (s, "..") || strchr (s, '/')))
  1522.     {
  1523.       sprintf (errmsg, "shell access restricted");
  1524.       return 0;
  1525.     }
  1526.   return 1;
  1527. }
  1528.