home *** CD-ROM | disk | FTP | other *** search
/ The Pier Shareware 6 / The_Pier_Shareware_Number_6_(The_Pier_Exchange)_(1995).iso / 036 / less232.zip / EDIT.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  12KB  |  654 lines

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