home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / less-321-src.tgz / tar.out / fsf / less / edit.c < prev    next >
C/C++ Source or Header  |  1996-09-28  |  15KB  |  729 lines

  1. /*
  2.  * Copyright (c) 1984,1985,1989,1994,1995,1996  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 cbufs;
  35. extern char *every_first_cmd;
  36. extern int any_display;
  37. extern int force_open;
  38. extern int is_tty;
  39. extern IFILE curr_ifile;
  40. extern IFILE old_ifile;
  41. extern struct scrpos initial_scrpos;
  42.  
  43. #if LOGFILE
  44. extern int logfile;
  45. extern int force_logfile;
  46. extern char *namelogfile;
  47. #endif
  48.  
  49. char *curr_altfilename = NULL;
  50. static void *curr_altpipe;
  51.  
  52.  
  53. /*
  54.  * Textlist functions deal with a list of words separated by spaces.
  55.  * init_textlist sets up a textlist structure.
  56.  * forw_textlist uses that structure to iterate thru the list of
  57.  * words, returning each one as a standard null-terminated string.
  58.  * back_textlist does the same, but runs thru the list backwards.
  59.  */
  60.     public void
  61. init_textlist(tlist, str)
  62.     struct textlist *tlist;
  63.     char *str;
  64. {
  65.     char *s;
  66.     
  67.     tlist->string = skipsp(str);
  68.     tlist->endstring = tlist->string + strlen(tlist->string);
  69.     for (s = str;  s < tlist->endstring;  s++)
  70.     {
  71.         if (*s == ' ')
  72.             *s = '\0';
  73.     }
  74. }
  75.  
  76.     public char *
  77. forw_textlist(tlist, prev)
  78.     struct textlist *tlist;
  79.     char *prev;
  80. {
  81.     char *s;
  82.     
  83.     /*
  84.      * prev == NULL means return the first word in the list.
  85.      * Otherwise, return the word after "prev".
  86.      */
  87.     if (prev == NULL)
  88.         s = tlist->string;
  89.     else
  90.         s = prev + strlen(prev);
  91.     if (s >= tlist->endstring)
  92.         return (NULL);
  93.     while (*s == '\0')
  94.         s++;
  95.     if (s >= tlist->endstring)
  96.         return (NULL);
  97.     return (s);
  98. }
  99.  
  100.     public char *
  101. back_textlist(tlist, prev)
  102.     struct textlist *tlist;
  103.     char *prev;
  104. {
  105.     char *s;
  106.     
  107.     /*
  108.      * prev == NULL means return the last word in the list.
  109.      * Otherwise, return the word before "prev".
  110.      */
  111.     if (prev == NULL)
  112.         s = tlist->endstring;
  113.     else if (prev <= tlist->string)
  114.         return (NULL);
  115.     else
  116.         s = prev - 1;
  117.     while (*s == '\0')
  118.         s--;
  119.     if (s <= tlist->string)
  120.         return (NULL);
  121.     while (s[-1] != '\0' && s > tlist->string)
  122.         s--;
  123.     return (s);
  124. }
  125.  
  126. /*
  127.  * Close the current input file.
  128.  */
  129.     static void
  130. close_file()
  131. {
  132.     struct scrpos scrpos;
  133.     
  134.     if (curr_ifile == NULL_IFILE)
  135.         return;
  136.  
  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.         chflags = ch_getflags();
  220.         close_file();
  221.         if (chflags & CH_HELPFILE)
  222.         {
  223.             /*
  224.              * Don't keep the help file in the ifile list.
  225.              */
  226.             del_ifile(was_curr_ifile);
  227.             was_curr_ifile = old_ifile;
  228.         }
  229.     }
  230.  
  231.     if (ifile == NULL_IFILE)
  232.     {
  233.         /*
  234.          * No new file to open.
  235.          * (Don't set old_ifile, because if you call edit_ifile(NULL),
  236.          *  you're supposed to have saved curr_ifile yourself,
  237.          *  and you'll restore it if necessary.)
  238.          */
  239.         return (0);
  240.     }
  241.  
  242.     filename = get_filename(ifile);
  243.     /*
  244.      * See if LESSOPEN specifies an "alternate" file to open.
  245.      */
  246.     alt_pipe = NULL;
  247.     alt_filename = open_altfile(filename, &f, &alt_pipe);
  248.     open_filename = (alt_filename != NULL) ? alt_filename : filename;
  249.  
  250.     chflags = 0;
  251.     if (alt_pipe != NULL)
  252.     {
  253.         /*
  254.          * The alternate "file" is actually a pipe.
  255.          * f has already been set to the file descriptor of the pipe
  256.          * in the call to open_altfile above.
  257.          * Keep the file descriptor open because it was opened 
  258.          * via popen(), and pclose() wants to close it.
  259.          */
  260.         chflags |= CH_POPENED;
  261.     } else if (strcmp(open_filename, "-") == 0)
  262.     {
  263.         /* 
  264.          * Use standard input.
  265.          * Keep the file descriptor open because we can't reopen it.
  266.          */
  267.         f = fd0;
  268.         chflags |= CH_KEEPOPEN;
  269. #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==WIN32C
  270.         /*
  271.          * Must switch stdin to BINARY mode.
  272.          */
  273.         setmode(f, O_BINARY);
  274. #endif
  275. #if MSDOS_COMPILER==MSOFTC
  276.         _setmode(f, _O_BINARY);
  277. #endif
  278.     } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
  279.     {
  280.         f = -1;
  281.         chflags |= CH_HELPFILE;
  282.     } else if ((parg.p_string = bad_file(open_filename)) != NULL)
  283.     {
  284.         /*
  285.          * It looks like a bad file.  Don't try to open it.
  286.          */
  287.         error("%s", &parg);
  288.         free(parg.p_string);
  289.         err1:
  290.         if (alt_filename != NULL)
  291.         {
  292.             close_altfile(alt_filename, filename, alt_pipe);
  293.             free(alt_filename);
  294.         }
  295.         del_ifile(ifile);
  296.         /*
  297.          * Re-open the current file.
  298.          */
  299.         (void) edit_ifile(was_curr_ifile);
  300.         return (1);
  301.     } else if ((f = open(open_filename, OPEN_READ)) < 0)
  302.     {
  303.         /*
  304.          * Got an error trying to open it.
  305.          */
  306.         parg.p_string = errno_message(filename);
  307.         error("%s", &parg);
  308.         free(parg.p_string);
  309.             goto err1;
  310.     } else if (!force_open && !opened(ifile) && bin_file(f))
  311.     {
  312.         /*
  313.          * Looks like a binary file.  Ask user if we should proceed.
  314.          */
  315.         parg.p_string = filename;
  316.         answer = query("\"%s\" may be a binary file.  See it anyway? ",
  317.             &parg);
  318.         if (answer != 'y' && answer != 'Y')
  319.         {
  320.             close(f);
  321.             goto err1;
  322.         }
  323.     }
  324.  
  325.     /*
  326.      * Get the new ifile.
  327.      * Get the saved position for the file.
  328.      */
  329.     if (was_curr_ifile != NULL_IFILE)
  330.         old_ifile = was_curr_ifile;
  331.     curr_ifile = ifile;
  332.     curr_altfilename = alt_filename;
  333.     curr_altpipe = alt_pipe;
  334.     set_open(curr_ifile); /* File has been opened */
  335.     get_pos(curr_ifile, &initial_scrpos);
  336.     new_file = TRUE;
  337.     ch_init(f, chflags);
  338.  
  339.     if (!(chflags & CH_HELPFILE))
  340.     {
  341. #if LOGFILE
  342.         if (namelogfile != NULL && is_tty)
  343.             use_logfile(namelogfile);
  344. #endif
  345.         if (every_first_cmd != NULL)
  346.             ungetsc(every_first_cmd);
  347.     }
  348.  
  349.     no_display = !any_display;
  350.     flush();
  351.     any_display = TRUE;
  352.  
  353.     if (is_tty)
  354.     {
  355.         /*
  356.          * Output is to a real tty.
  357.          */
  358.  
  359.         /*
  360.          * Indicate there is nothing displayed yet.
  361.          */
  362.         pos_clear();
  363.         clr_linenum();
  364. #if HILITE_SEARCH
  365.         clr_hilite();
  366. #endif
  367.         if (no_display && errmsgs > 0)
  368.         {
  369.             /*
  370.              * We displayed some messages on error output
  371.              * (file descriptor 2; see error() function).
  372.              * Before erasing the screen contents,
  373.              * display the file name and wait for a keystroke.
  374.              */
  375.             parg.p_string = filename;
  376.             error("%s", &parg);
  377.         }
  378.     }
  379.     return (0);
  380. }
  381.  
  382. /*
  383.  * Edit a space-separated list of files.
  384.  * For each filename in the list, enter it into the ifile list.
  385.  * Then edit the first one.
  386.  */
  387.     public int
  388. edit_list(filelist)
  389.     char *filelist;
  390. {
  391.     IFILE save_curr_ifile;
  392.     char *good_filename;
  393.     char *filename;
  394.     char *gfilelist;
  395.     char *gfilename;
  396.     struct textlist tl_files;
  397.     struct textlist tl_gfiles;
  398.  
  399.     save_curr_ifile = curr_ifile;
  400.     good_filename = NULL;
  401.     
  402.     /*
  403.      * Run thru each filename in the list.
  404.      * Try to glob the filename.  
  405.      * If it doesn't expand, just try to open the filename.
  406.      * If it does expand, try to open each name in that list.
  407.      */
  408.     init_textlist(&tl_files, filelist);
  409.     filename = NULL;
  410.     while ((filename = forw_textlist(&tl_files, filename)) != NULL)
  411.     {
  412.         gfilelist = lglob(filename);
  413.         init_textlist(&tl_gfiles, gfilelist);
  414.         gfilename = NULL;
  415.         while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
  416.         {
  417.             if (edit(gfilename) == 0 && good_filename == NULL)
  418.                 good_filename = get_filename(curr_ifile);
  419.         }
  420.         free(gfilelist);
  421.     }
  422.     /*
  423.      * Edit the first valid filename in the list.
  424.      */
  425.     if (good_filename == NULL)
  426.         return (1);
  427.     if (get_ifile(good_filename, curr_ifile) == curr_ifile)
  428.         /*
  429.          * Trying to edit the current file; don't reopen it.
  430.          */
  431.         return (0);
  432.     reedit_ifile(save_curr_ifile);
  433.     return (edit(good_filename));
  434. }
  435.  
  436. /*
  437.  * Edit the first file in the command line (ifile) list.
  438.  */
  439.     public int
  440. edit_first()
  441. {
  442.     curr_ifile = NULL_IFILE;
  443.     return (edit_next(1));
  444. }
  445.  
  446. /*
  447.  * Edit the last file in the command line (ifile) list.
  448.  */
  449.     public int
  450. edit_last()
  451. {
  452.     curr_ifile = NULL_IFILE;
  453.     return (edit_prev(1));
  454. }
  455.  
  456.  
  457. /*
  458.  * Edit the next file in the command line (ifile) list.
  459.  */
  460.     static int
  461. edit_inext(h, n)
  462.     IFILE h;
  463.     int n;
  464. {
  465.     IFILE next;
  466.  
  467.     /*
  468.      * Skip n filenames, then try to edit each filename.
  469.      */
  470.     for (;;)
  471.     {
  472.         next = next_ifile(h);
  473.         if (--n < 0)
  474.         {
  475.             if (edit_ifile(h) == 0)
  476.                 break;
  477.         }
  478.         if (next == NULL_IFILE)
  479.         {
  480.             /*
  481.              * Reached end of the ifile list.
  482.              */
  483.             return (1);
  484.         }
  485.         h = next;
  486.     } 
  487.     /*
  488.      * Found a file that we can edit.
  489.      */
  490.     return (0);
  491. }
  492.  
  493.     public int
  494. edit_next(n)
  495.     int n;
  496. {
  497.     return edit_inext(curr_ifile, n);
  498. }
  499.  
  500. /*
  501.  * Edit the previous file in the command line list.
  502.  */
  503.     static int
  504. edit_iprev(h, n)
  505.     IFILE h;
  506.     int n;
  507. {
  508.     IFILE next;
  509.  
  510.     /*
  511.      * Skip n filenames, then try to edit each filename.
  512.      */
  513.     for (;;)
  514.     {
  515.         next = prev_ifile(h);
  516.         if (--n < 0)
  517.         {
  518.             if (edit_ifile(h) == 0)
  519.                 break;
  520.         }
  521.         if (next == NULL_IFILE)
  522.         {
  523.             /*
  524.              * Reached beginning of the ifile list.
  525.              */
  526.             return (1);
  527.         }
  528.         h = next;
  529.     } 
  530.     /*
  531.      * Found a file that we can edit.
  532.      */
  533.     return (0);
  534. }
  535.  
  536.     public int
  537. edit_prev(n)
  538.     int n;
  539. {
  540.     return edit_iprev(curr_ifile, n);
  541. }
  542.  
  543. /*
  544.  * Edit a specific file in the command line (ifile) list.
  545.  */
  546.     public int
  547. edit_index(n)
  548.     int n;
  549. {
  550.     IFILE h;
  551.  
  552.     h = NULL_IFILE;
  553.     do
  554.     {
  555.         if ((h = next_ifile(h)) == NULL_IFILE)
  556.         {
  557.             /*
  558.              * Reached end of the list without finding it.
  559.              */
  560.             return (1);
  561.         }
  562.     } while (get_index(h) != n);
  563.  
  564.     return (edit_ifile(h));
  565. }
  566.  
  567. /*
  568.  * Reedit the ifile which was previously open.
  569.  */
  570.     public void
  571. reedit_ifile(save_ifile)
  572.     IFILE save_ifile;
  573. {
  574.     IFILE next;
  575.     IFILE prev;
  576.  
  577.     /*
  578.      * Try to reopen the ifile.
  579.      * Note that opening it may fail (maybe the file was removed),
  580.      * in which case the ifile will be deleted from the list.
  581.      * So save the next and prev ifiles first.
  582.      */
  583.     next = next_ifile(save_ifile);
  584.     prev = prev_ifile(save_ifile);
  585.     if (edit_ifile(save_ifile) == 0)
  586.         return;
  587.     /*
  588.      * If can't reopen it, open the next input file in the list.
  589.      */
  590.     if (next != NULL_IFILE && edit_inext(next, 0) == 0)
  591.         return;
  592.     /*
  593.      * If can't open THAT one, open the previous input file in the list.
  594.      */
  595.     if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
  596.         return;
  597.     /*
  598.      * If can't even open that, we're stuck.  Just quit.
  599.      */
  600.     quit(QUIT_ERROR);
  601. }
  602.  
  603. /*
  604.  * Edit standard input.
  605.  */
  606.     public int
  607. edit_stdin()
  608. {
  609.     if (isatty(fd0))
  610.     {
  611. #if SHELL_META_QUEST
  612.         error("Missing filename (\"less -\\?\" for help)", NULL_PARG);
  613. #else
  614.         error("Missing filename (\"less -?\" for help)", NULL_PARG);
  615. #endif
  616.         quit(QUIT_OK);
  617.     }
  618.     return (edit("-"));
  619. }
  620.  
  621. /*
  622.  * Copy a file directly to standard output.
  623.  * Used if standard output is not a tty.
  624.  */
  625.     public void
  626. cat_file()
  627. {
  628.     register int c;
  629.  
  630.     while ((c = ch_forw_get()) != EOI)
  631.         putchr(c);
  632.     flush();
  633. }
  634.  
  635. #if LOGFILE
  636.  
  637. /*
  638.  * If the user asked for a log file and our input file
  639.  * is standard input, create the log file.  
  640.  * We take care not to blindly overwrite an existing file.
  641.  */
  642.     public void
  643. use_logfile(filename)
  644.     char *filename;
  645. {
  646.     register int exists;
  647.     register int answer;
  648.     PARG parg;
  649.  
  650.     if (ch_getflags() & CH_CANSEEK)
  651.         /*
  652.          * Can't currently use a log file on a file that can seek.
  653.          */
  654.         return;
  655.  
  656.     /*
  657.      * {{ We could use access() here. }}
  658.      */
  659.     exists = open(filename, OPEN_READ);
  660.     close(exists);
  661.     exists = (exists >= 0);
  662.  
  663.     /*
  664.      * Decide whether to overwrite the log file or append to it.
  665.      * If it doesn't exist we "overwrite" it.
  666.      */
  667.     if (!exists || force_logfile)
  668.     {
  669.         /*
  670.          * Overwrite (or create) the log file.
  671.          */
  672.         answer = 'O';
  673.     } else
  674.     {
  675.         /*
  676.          * Ask user what to do.
  677.          */
  678.         parg.p_string = filename;
  679.         answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
  680.     }
  681.  
  682. loop:
  683.     switch (answer)
  684.     {
  685.     case 'O': case 'o':
  686.         /*
  687.          * Overwrite: create the file.
  688.          */
  689.         logfile = creat(filename, 0644);
  690.         break;
  691.     case 'A': case 'a':
  692.         /*
  693.          * Append: open the file and seek to the end.
  694.          */
  695.         logfile = open(filename, OPEN_APPEND);
  696.         if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
  697.         {
  698.             close(logfile);
  699.             logfile = -1;
  700.         }
  701.         break;
  702.     case 'D': case 'd':
  703.         /*
  704.          * Don't do anything.
  705.          */
  706.         return;
  707.     case 'q':
  708.         quit(QUIT_OK);
  709.         /*NOTREACHED*/
  710.     default:
  711.         /*
  712.          * Eh?
  713.          */
  714.         answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
  715.         goto loop;
  716.     }
  717.  
  718.     if (logfile < 0)
  719.     {
  720.         /*
  721.          * Error in opening logfile.
  722.          */
  723.         parg.p_string = filename;
  724.         error("Cannot write to \"%s\"", &parg);
  725.     }
  726. }
  727.  
  728. #endif
  729.