home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 22 gnu / 22-gnu.zip / less2912.zip / edit.c < prev    next >
C/C++ Source or Header  |  1995-04-10  |  13KB  |  667 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995  Mark Nudelman
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice in the documentation and/or other materials provided with 
  12.  *    the distribution.
  13.  *
  14.  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
  15.  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  16.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
  17.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE
  18.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
  19.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
  20.  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 
  21.  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
  22.  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
  23.  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 
  24.  * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25.  */
  26.  
  27.  
  28. #include "less.h"
  29.  
  30. public int fd0 = 0;
  31.  
  32. extern int new_file;
  33. extern int errmsgs;
  34. extern int quit_at_eof;
  35. extern int cbufs;
  36. extern char *every_first_cmd;
  37. extern int any_display;
  38. extern int force_open;
  39. extern int is_tty;
  40. extern IFILE curr_ifile;
  41. extern IFILE old_ifile;
  42. extern struct scrpos initial_scrpos;
  43.  
  44. #if LOGFILE
  45. extern int logfile;
  46. extern int force_logfile;
  47. extern char *namelogfile;
  48. #endif
  49.  
  50. char *curr_altfilename = NULL;
  51. static void *curr_altpipe;
  52.  
  53.  
  54. /*
  55.  * Textlist functions deal with a list of words separated by spaces.
  56.  * init_textlist sets up a textlist structure.
  57.  * forw_textlist uses that structure to iterate thru the list of
  58.  * words, returning each one as a standard null-terminated string.
  59.  * back_textlist does the same, but runs thru the list backwards.
  60.  */
  61.     public void
  62. init_textlist(tlist, str)
  63.     struct textlist *tlist;
  64.     char *str;
  65. {
  66.     char *s;
  67.     
  68.     tlist->string = skipsp(str);
  69.     tlist->endstring = tlist->string + strlen(tlist->string);
  70.     for (s = str;  s < tlist->endstring;  s++)
  71.     {
  72.         if (*s == ' ')
  73.             *s = '\0';
  74.     }
  75. }
  76.  
  77.     public char *
  78. forw_textlist(tlist, prev)
  79.     struct textlist *tlist;
  80.     char *prev;
  81. {
  82.     char *s;
  83.     
  84.     /*
  85.      * prev == NULL means return the first word in the list.
  86.      * Otherwise, return the word after "prev".
  87.      */
  88.     if (prev == NULL)
  89.         s = tlist->string;
  90.     else
  91.         s = prev + strlen(prev);
  92.     if (s >= tlist->endstring)
  93.         return (NULL);
  94.     while (*s == '\0')
  95.         s++;
  96.     if (s >= tlist->endstring)
  97.         return (NULL);
  98.     return (s);
  99. }
  100.  
  101.     public char *
  102. back_textlist(tlist, prev)
  103.     struct textlist *tlist;
  104.     char *prev;
  105. {
  106.     char *s;
  107.     
  108.     /*
  109.      * prev == NULL means return the last word in the list.
  110.      * Otherwise, return the word before "prev".
  111.      */
  112.     if (prev == NULL)
  113.         s = tlist->endstring;
  114.     else if (prev <= tlist->string)
  115.         return (NULL);
  116.     else
  117.         s = prev - 1;
  118.     while (*s == '\0')
  119.         s--;
  120.     if (s <= tlist->string)
  121.         return (NULL);
  122.     while (s[-1] != '\0' && s > tlist->string)
  123.         s--;
  124.     return (s);
  125. }
  126.  
  127. /*
  128.  * Close the current input file.
  129.  */
  130.     static void
  131. close_file()
  132. {
  133.     struct scrpos scrpos;
  134.     
  135.     if (curr_ifile == NULL_IFILE)
  136.         return;
  137.     /*
  138.      * Save the current position so that we can return to
  139.      * the same position if we edit this file again.
  140.      */
  141.     get_scrpos(&scrpos);
  142.     if (scrpos.pos != NULL_POSITION)
  143.     {
  144.         store_pos(curr_ifile, &scrpos);
  145.         lastmark();
  146.     }
  147.     /*
  148.      * Close the file descriptor, unless it is a pipe.
  149.      */
  150.     ch_close();
  151.     /*
  152.      * If we opened a file using an alternate name,
  153.      * do special stuff to close it.
  154.      */
  155.     if (curr_altfilename != NULL)
  156.     {
  157.         close_altfile(curr_altfilename, get_filename(curr_ifile),
  158.             curr_altpipe);
  159.         free(curr_altfilename);
  160.         curr_altfilename = NULL;
  161.     }
  162.     curr_ifile = NULL_IFILE;
  163. }
  164.  
  165. /*
  166.  * Edit a new file (given its name).
  167.  * Filename == "-" means standard input.
  168.  * Filename == NULL means just close the current file.
  169.  */
  170.     public int
  171. edit(filename)
  172.     char *filename;
  173. {
  174.     if (filename == NULL)
  175.         return (edit_ifile(NULL_IFILE));
  176.     return (edit_ifile(get_ifile(filename, curr_ifile)));
  177. }
  178.     
  179. /*
  180.  * Edit a new file (given its IFILE).
  181.  * ifile == NULL means just close the current file.
  182.  */
  183.     public int
  184. edit_ifile(ifile)
  185.     IFILE ifile;
  186. {
  187.     int f;
  188.     int answer;
  189.     int no_display;
  190.     int chflags;
  191.     char *filename;
  192.     char *open_filename;
  193.     char *alt_filename;
  194.     void *alt_pipe;
  195.     IFILE was_curr_ifile;
  196.     PARG parg;
  197.         
  198.     if (ifile == curr_ifile)
  199.     {
  200.         /*
  201.          * Already have the correct file open.
  202.          */
  203.         return (0);
  204.     }
  205.  
  206.     /*
  207.      * We must close the currently open file now.
  208.      * This is necessary to make the open_altfile/close_altfile pairs
  209.      * nest properly (or rather to avoid nesting at all).
  210.      * {{ Some stupid implementations of popen() mess up if you do:
  211.      *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
  212.      */
  213. #if LOGFILE
  214.     end_logfile();
  215. #endif
  216.     was_curr_ifile = curr_ifile;
  217.     if (curr_ifile != NULL_IFILE)
  218.     {
  219.         close_file();
  220.     }
  221.  
  222.     if (ifile == NULL_IFILE)
  223.     {
  224.         /*
  225.          * No new file to open.
  226.          * (Don't set old_ifile, because if you call edit_ifile(NULL),
  227.          *  you're supposed to have saved curr_ifile yourself,
  228.          *  and you'll restore it if necessary.)
  229.          */
  230.         return (0);
  231.     }
  232.  
  233.     filename = get_filename(ifile);
  234.     /*
  235.      * See if LESSOPEN specifies an "alternate" file to open.
  236.      */
  237.     alt_pipe = NULL;
  238.     alt_filename = open_altfile(filename, &f, &alt_pipe);
  239.     open_filename = (alt_filename != NULL) ? alt_filename : filename;
  240.  
  241.     chflags = 0;
  242.     if (alt_pipe != NULL)
  243.     {
  244.         /*
  245.          * The alternate "file" is actually a pipe.
  246.          * f has already been set to the file descriptor of the pipe
  247.          * in the call to open_altfile above.
  248.          * Keep the file descriptor open because it was opened 
  249.          * via popen(), and pclose() wants to close it.
  250.          */
  251.         chflags |= CH_POPENED;
  252.     } else if (strcmp(open_filename, "-") == 0)
  253.     {
  254.         /* 
  255.          * Use standard input.
  256.          * Keep the file descriptor open because we can't reopen it.
  257.          */
  258.         f = fd0;
  259.         chflags |= CH_KEEPOPEN;
  260.     } else if ((parg.p_string = bad_file(open_filename)) != NULL)
  261.     {
  262.         /*
  263.          * It looks like a bad file.  Don't try to open it.
  264.          */
  265.         error("%s", &parg);
  266.         free(parg.p_string);
  267.         err1:
  268.         if (alt_filename != NULL)
  269.         {
  270.             close_altfile(alt_filename, filename, alt_pipe);
  271.             free(alt_filename);
  272.         }
  273.         del_ifile(ifile);
  274.         /*
  275.          * Re-open the current file.
  276.          */
  277.         (void) edit_ifile(was_curr_ifile);
  278.         return (1);
  279.         } else if ((answer = isZfile(filename)) >= 0)
  280.     {
  281.         if ((f = Zopen(filename, answer)) < 0)
  282.         {
  283.                         parg.p_string = errno_message(filename);
  284.             error("%s", &parg);
  285.             free(parg.p_string);
  286.             return (1);
  287.                 }
  288.         chflags |= CH_COMPRESSED;
  289.     } else if ((f = open(open_filename, OPEN_READ)) < 0)
  290.     {
  291.         /*
  292.          * Got an error trying to open it.
  293.          */
  294.         parg.p_string = errno_message(filename);
  295.         error("%s", &parg);
  296.         free(parg.p_string);
  297.             goto err1;
  298.     } else if (!force_open && !opened(ifile) && bin_file(f))
  299.     {
  300.         /*
  301.          * Looks like a binary file.  Ask user if we should proceed.
  302.          */
  303.         parg.p_string = filename;
  304.         answer = query("\"%s\" may be a binary file.  See it anyway? ",
  305.             &parg);
  306.         if (answer != 'y' && answer != 'Y')
  307.         {
  308.             close(f);
  309.             goto err1;
  310.         }
  311.     }
  312.  
  313.     /*
  314.      * Get the new ifile.
  315.      * Get the saved position for the file.
  316.      */
  317.     if (was_curr_ifile != NULL_IFILE)
  318.         old_ifile = was_curr_ifile;
  319.     curr_ifile = ifile;
  320.     curr_altfilename = alt_filename;
  321.     curr_altpipe = alt_pipe;
  322.     set_open(curr_ifile); /* File has been opened */
  323.     get_pos(curr_ifile, &initial_scrpos);
  324.     new_file = TRUE;
  325.     ch_init(f, chflags);
  326. #if LOGFILE
  327.     if (namelogfile != NULL && is_tty)
  328.         use_logfile(namelogfile);
  329. #endif
  330.  
  331.     if (every_first_cmd != NULL)
  332.         ungetsc(every_first_cmd);
  333.  
  334.     no_display = !any_display;
  335.     flush();
  336.     any_display = TRUE;
  337.  
  338.     if (is_tty)
  339.     {
  340.         /*
  341.          * Output is to a real tty.
  342.          */
  343.  
  344.         /*
  345.          * Indicate there is nothing displayed yet.
  346.          */
  347.         pos_clear();
  348.         clr_linenum();
  349. #if HILITE_SEARCH
  350.         clr_hilite();
  351. #endif
  352.         if (no_display && errmsgs > 0)
  353.         {
  354.             /*
  355.              * We displayed some messages on error output
  356.              * (file descriptor 2; see error() function).
  357.              * Before erasing the screen contents,
  358.              * display the file name and wait for a keystroke.
  359.              */
  360.             parg.p_string = filename;
  361.             error("%s", &parg);
  362.         }
  363.     }
  364.     return (0);
  365. }
  366.  
  367. /*
  368.  * Edit a space-separated list of files.
  369.  * For each filename in the list, enter it into the ifile list.
  370.  * Then edit the first one.
  371.  */
  372.     public int
  373. edit_list(filelist)
  374.     char *filelist;
  375. {
  376.     IFILE save_curr_ifile;
  377.     char *good_filename;
  378.     char *filename;
  379.     char *gfilelist;
  380.     char *gfilename;
  381.     struct textlist tl_files;
  382.     struct textlist tl_gfiles;
  383.  
  384.     save_curr_ifile = curr_ifile;
  385.     good_filename = NULL;
  386.     
  387.     /*
  388.      * Run thru each filename in the list.
  389.      * Try to glob the filename.  
  390.      * If it doesn't expand, just try to open the filename.
  391.      * If it does expand, try to open each name in that list.
  392.      */
  393.     init_textlist(&tl_files, filelist);
  394.     filename = NULL;
  395.     while ((filename = forw_textlist(&tl_files, filename)) != NULL)
  396.     {
  397.         gfilelist = glob(filename);
  398.         init_textlist(&tl_gfiles, gfilelist);
  399.         gfilename = NULL;
  400.         while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
  401.         {
  402.             if (edit(gfilename) == 0 && good_filename == NULL)
  403.                 good_filename = get_filename(curr_ifile);
  404.         }
  405.         free(gfilelist);
  406.     }
  407.     /*
  408.      * Edit the first valid filename in the list.
  409.      */
  410.     if (good_filename == NULL)
  411.         return (1);
  412.     if (get_ifile(good_filename, curr_ifile) == curr_ifile)
  413.         /*
  414.          * Trying to edit the current file; don't reopen it.
  415.          */
  416.         return (0);
  417.     if (edit_ifile(save_curr_ifile))
  418.         quit(QUIT_ERROR);
  419.     return (edit(good_filename));
  420. }
  421.  
  422. /*
  423.  * Edit the first file in the command line (ifile) list.
  424.  */
  425.     public int
  426. edit_first()
  427. {
  428.     curr_ifile = NULL_IFILE;
  429.     return (edit_next(1));
  430. }
  431.  
  432. /*
  433.  * Edit the last file in the command line (ifile) list.
  434.  */
  435.     public int
  436. edit_last()
  437. {
  438.     curr_ifile = NULL_IFILE;
  439.     return (edit_prev(1));
  440. }
  441.  
  442.  
  443. /*
  444.  * Edit the next file in the command line (ifile) list.
  445.  */
  446.     public int
  447. edit_next(n)
  448.     int n;
  449. {
  450.     IFILE h;
  451.     IFILE next;
  452.  
  453.     h = curr_ifile;
  454.     /*
  455.      * Skip n filenames, then try to edit each filename.
  456.      */
  457.     for (;;)
  458.     {
  459.         next = next_ifile(h);
  460.         if (--n < 0)
  461.         {
  462.             if (edit_ifile(h) == 0)
  463.                 break;
  464.         }
  465.         if (next == NULL_IFILE)
  466.         {
  467.             /*
  468.              * Reached end of the ifile list.
  469.              */
  470.             return (1);
  471.         }
  472.         h = next;
  473.     } 
  474.     /*
  475.      * Found a file that we can edit.
  476.      */
  477.     return (0);
  478. }
  479.  
  480. /*
  481.  * Edit the previous file in the command line list.
  482.  */
  483.     public int
  484. edit_prev(n)
  485.     int n;
  486. {
  487.     IFILE h;
  488.     IFILE next;
  489.  
  490.     h = curr_ifile;
  491.     /*
  492.      * Skip n filenames, then try to edit each filename.
  493.      */
  494.     for (;;)
  495.     {
  496.         next = prev_ifile(h);
  497.         if (--n < 0)
  498.         {
  499.             if (edit_ifile(h) == 0)
  500.                 break;
  501.         }
  502.         if (next == NULL_IFILE)
  503.         {
  504.             /*
  505.              * Reached beginning of the ifile list.
  506.              */
  507.             return (1);
  508.         }
  509.         h = next;
  510.     } 
  511.     /*
  512.      * Found a file that we can edit.
  513.      */
  514.     return (0);
  515. }
  516.  
  517. /*
  518.  * Edit a specific file in the command line (ifile) list.
  519.  */
  520.     public int
  521. edit_index(n)
  522.     int n;
  523. {
  524.     IFILE h;
  525.  
  526.     h = NULL_IFILE;
  527.     do
  528.     {
  529.         if ((h = next_ifile(h)) == NULL_IFILE)
  530.         {
  531.             /*
  532.              * Reached end of the list without finding it.
  533.              */
  534.             return (1);
  535.         }
  536.     } while (get_index(h) != n);
  537.  
  538.     return (edit_ifile(h));
  539. }
  540.  
  541. /*
  542.  * Edit standard input.
  543.  */
  544.     public int
  545. edit_stdin()
  546. {
  547.     if (isatty(fd0))
  548.     {
  549. #if MSOFTC || OS2
  550.         error("Missing filename (\"less -?\" for help)", NULL_PARG);
  551. #else
  552.         error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
  553. #endif
  554.         quit(QUIT_OK);
  555.     }
  556.     return (edit("-"));
  557. }
  558.  
  559. /*
  560.  * Copy a file directly to standard output.
  561.  * Used if standard output is not a tty.
  562.  */
  563.     public void
  564. cat_file()
  565. {
  566.     register int c;
  567.  
  568.     while ((c = ch_forw_get()) != EOI)
  569.         putchr(c);
  570.     flush();
  571. }
  572.  
  573. #if LOGFILE
  574.  
  575. /*
  576.  * If the user asked for a log file and our input file
  577.  * is standard input, create the log file.  
  578.  * We take care not to blindly overwrite an existing file.
  579.  */
  580.     public void
  581. use_logfile(filename)
  582.     char *filename;
  583. {
  584.     register int exists;
  585.     register int answer;
  586.     PARG parg;
  587.  
  588.     if (ch_getflags() & CH_CANSEEK)
  589.         /*
  590.          * Can't currently use a log file on a file that can seek.
  591.          */
  592.         return;
  593.  
  594.     /*
  595.      * {{ We could use access() here. }}
  596.      */
  597.     exists = open(filename, OPEN_READ);
  598.     close(exists);
  599.     exists = (exists >= 0);
  600.  
  601.     /*
  602.      * Decide whether to overwrite the log file or append to it.
  603.      * If it doesn't exist we "overwrite" it.
  604.      */
  605.     if (!exists || force_logfile)
  606.     {
  607.         /*
  608.          * Overwrite (or create) the log file.
  609.          */
  610.         answer = 'O';
  611.     } else
  612.     {
  613.         /*
  614.          * Ask user what to do.
  615.          */
  616.         parg.p_string = filename;
  617.         answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
  618.     }
  619.  
  620. loop:
  621.     switch (answer)
  622.     {
  623.     case 'O': case 'o':
  624.         /*
  625.          * Overwrite: create the file.
  626.          */
  627.         logfile = creat(filename, 0644);
  628.         break;
  629.     case 'A': case 'a':
  630.         /*
  631.          * Append: open the file and seek to the end.
  632.          */
  633.         logfile = open(filename, OPEN_APPEND);
  634.         if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
  635.         {
  636.             close(logfile);
  637.             logfile = -1;
  638.         }
  639.         break;
  640.     case 'D': case 'd':
  641.         /*
  642.          * Don't do anything.
  643.          */
  644.         return;
  645.     case 'q':
  646.         quit(QUIT_OK);
  647.         /*NOTREACHED*/
  648.     default:
  649.         /*
  650.          * Eh?
  651.          */
  652.         answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
  653.         goto loop;
  654.     }
  655.  
  656.     if (logfile < 0)
  657.     {
  658.         /*
  659.          * Error in opening logfile.
  660.          */
  661.         parg.p_string = filename;
  662.         error("Cannot write to \"%s\"", &parg);
  663.     }
  664. }
  665.  
  666. #endif
  667.