home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 6 File / 06-File.zip / less373.zip / edit.c < prev    next >
C/C++ Source or Header  |  2002-01-14  |  15KB  |  780 lines

  1. /*
  2.  * Copyright (C) 1984-2000  Mark Nudelman
  3.  *
  4.  * You may distribute under the terms of either the GNU General Public
  5.  * License or the Less License, as specified in the README file.
  6.  *
  7.  * For more information about less, or for information on how to 
  8.  * contact the author, see the README file.
  9.  */
  10.  
  11.  
  12. #include "less.h"
  13.  
  14. public int fd0 = 0;
  15.  
  16. extern int new_file;
  17. extern int errmsgs;
  18. extern int cbufs;
  19. extern char *every_first_cmd;
  20. extern int any_display;
  21. extern int force_open;
  22. extern int is_tty;
  23. extern int sigs;
  24. extern IFILE curr_ifile;
  25. extern IFILE old_ifile;
  26. extern struct scrpos initial_scrpos;
  27. extern void constant *ml_examine;
  28. #if SPACES_IN_FILENAMES
  29. extern char openquote;
  30. extern char closequote;
  31. #endif
  32.  
  33. #if LOGFILE
  34. extern int logfile;
  35. extern int force_logfile;
  36. extern char *namelogfile;
  37. #endif
  38.  
  39. char *curr_altfilename = NULL;
  40. static void *curr_altpipe;
  41.  
  42.  
  43. /*
  44.  * Textlist functions deal with a list of words separated by spaces.
  45.  * init_textlist sets up a textlist structure.
  46.  * forw_textlist uses that structure to iterate thru the list of
  47.  * words, returning each one as a standard null-terminated string.
  48.  * back_textlist does the same, but runs thru the list backwards.
  49.  */
  50.     public void
  51. init_textlist(tlist, str)
  52.     struct textlist *tlist;
  53.     char *str;
  54. {
  55.     char *s;
  56. #if SPACES_IN_FILENAMES
  57.     int meta_quoted = 0;
  58.     int delim_quoted = 0;
  59.     char *esc = get_meta_escape();
  60.     int esclen = strlen(esc);
  61. #endif
  62.     
  63.     tlist->string = skipsp(str);
  64.     tlist->endstring = tlist->string + strlen(tlist->string);
  65.     for (s = str;  s < tlist->endstring;  s++)
  66.     {
  67. #if SPACES_IN_FILENAMES
  68.         if (meta_quoted)
  69.         {
  70.             meta_quoted = 0;
  71.         } else if (esclen > 0 && s + esclen < tlist->endstring &&
  72.                    strncmp(s, esc, esclen) == 0)
  73.         {
  74.             meta_quoted = 1;
  75.             s += esclen - 1;
  76.         } else if (delim_quoted)
  77.         {
  78.             if (*s == closequote)
  79.                 delim_quoted = 0;
  80.         } else /* (!delim_quoted) */
  81.         {
  82.             if (*s == openquote)
  83.                 delim_quoted = 1;
  84.             else if (*s == ' ')
  85.                 *s = '\0';
  86.         }
  87. #else
  88.         if (*s == ' ')
  89.             *s = '\0';
  90. #endif
  91.     }
  92. }
  93.  
  94.     public char *
  95. forw_textlist(tlist, prev)
  96.     struct textlist *tlist;
  97.     char *prev;
  98. {
  99.     char *s;
  100.     
  101.     /*
  102.      * prev == NULL means return the first word in the list.
  103.      * Otherwise, return the word after "prev".
  104.      */
  105.     if (prev == NULL)
  106.         s = tlist->string;
  107.     else
  108.         s = prev + strlen(prev);
  109.     if (s >= tlist->endstring)
  110.         return (NULL);
  111.     while (*s == '\0')
  112.         s++;
  113.     if (s >= tlist->endstring)
  114.         return (NULL);
  115.     return (s);
  116. }
  117.  
  118.     public char *
  119. back_textlist(tlist, prev)
  120.     struct textlist *tlist;
  121.     char *prev;
  122. {
  123.     char *s;
  124.     
  125.     /*
  126.      * prev == NULL means return the last word in the list.
  127.      * Otherwise, return the word before "prev".
  128.      */
  129.     if (prev == NULL)
  130.         s = tlist->endstring;
  131.     else if (prev <= tlist->string)
  132.         return (NULL);
  133.     else
  134.         s = prev - 1;
  135.     while (*s == '\0')
  136.         s--;
  137.     if (s <= tlist->string)
  138.         return (NULL);
  139.     while (s[-1] != '\0' && s > tlist->string)
  140.         s--;
  141.     return (s);
  142. }
  143.  
  144. /*
  145.  * Close the current input file.
  146.  */
  147.     static void
  148. close_file()
  149. {
  150.     struct scrpos scrpos;
  151.     
  152.     if (curr_ifile == NULL_IFILE)
  153.         return;
  154.  
  155.     /*
  156.      * Save the current position so that we can return to
  157.      * the same position if we edit this file again.
  158.      */
  159.     get_scrpos(&scrpos);
  160.     if (scrpos.pos != NULL_POSITION)
  161.     {
  162.         store_pos(curr_ifile, &scrpos);
  163.         lastmark();
  164.     }
  165.     /*
  166.      * Close the file descriptor, unless it is a pipe.
  167.      */
  168.     ch_close();
  169.     /*
  170.      * If we opened a file using an alternate name,
  171.      * do special stuff to close it.
  172.      */
  173.     if (curr_altfilename != NULL)
  174.     {
  175.         close_altfile(curr_altfilename, get_filename(curr_ifile),
  176.                 curr_altpipe);
  177.         free(curr_altfilename);
  178.         curr_altfilename = NULL;
  179.     }
  180.     curr_ifile = NULL_IFILE;
  181. }
  182.  
  183. /*
  184.  * Edit a new file (given its name).
  185.  * Filename == "-" means standard input.
  186.  * Filename == NULL means just close the current file.
  187.  */
  188.     public int
  189. edit(filename)
  190.     char *filename;
  191. {
  192.     if (filename == NULL)
  193.         return (edit_ifile(NULL_IFILE));
  194.     return (edit_ifile(get_ifile(filename, curr_ifile)));
  195. }
  196.     
  197. /*
  198.  * Edit a new file (given its IFILE).
  199.  * ifile == NULL means just close the current file.
  200.  */
  201.     public int
  202. edit_ifile(ifile)
  203.     IFILE ifile;
  204. {
  205.     int f;
  206.     int answer;
  207.     int no_display;
  208.     int chflags;
  209.     char *filename;
  210.     char *open_filename;
  211.     char *qopen_filename;
  212.     char *alt_filename;
  213.     void *alt_pipe;
  214.     IFILE was_curr_ifile;
  215.     PARG parg;
  216.         
  217.     if (ifile == curr_ifile)
  218.     {
  219.         /*
  220.          * Already have the correct file open.
  221.          */
  222.         return (0);
  223.     }
  224.  
  225.     /*
  226.      * We must close the currently open file now.
  227.      * This is necessary to make the open_altfile/close_altfile pairs
  228.      * nest properly (or rather to avoid nesting at all).
  229.      * {{ Some stupid implementations of popen() mess up if you do:
  230.      *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
  231.      */
  232. #if LOGFILE
  233.     end_logfile();
  234. #endif
  235.     was_curr_ifile = save_curr_ifile();
  236.     if (curr_ifile != NULL_IFILE)
  237.     {
  238.         chflags = ch_getflags();
  239.         close_file();
  240.         if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
  241.         {
  242.             /*
  243.              * Don't keep the help file in the ifile list.
  244.              */
  245.             del_ifile(was_curr_ifile);
  246.             was_curr_ifile = old_ifile;
  247.         }
  248.     }
  249.  
  250.     if (ifile == NULL_IFILE)
  251.     {
  252.         /*
  253.          * No new file to open.
  254.          * (Don't set old_ifile, because if you call edit_ifile(NULL),
  255.          *  you're supposed to have saved curr_ifile yourself,
  256.          *  and you'll restore it if necessary.)
  257.          */
  258.         unsave_ifile(was_curr_ifile);
  259.         return (0);
  260.     }
  261.  
  262.     filename = save(get_filename(ifile));
  263.     /*
  264.      * See if LESSOPEN specifies an "alternate" file to open.
  265.      */
  266.     alt_pipe = NULL;
  267.     alt_filename = open_altfile(filename, &f, &alt_pipe);
  268.     open_filename = (alt_filename != NULL) ? alt_filename : filename;
  269.     qopen_filename = shell_unquote(open_filename);
  270.  
  271.     chflags = 0;
  272.     if (alt_pipe != NULL)
  273.     {
  274.         /*
  275.          * The alternate "file" is actually a pipe.
  276.          * f has already been set to the file descriptor of the pipe
  277.          * in the call to open_altfile above.
  278.          * Keep the file descriptor open because it was opened 
  279.          * via popen(), and pclose() wants to close it.
  280.          */
  281.         chflags |= CH_POPENED;
  282.     } else if (strcmp(open_filename, "-") == 0)
  283.     {
  284.         /* 
  285.          * Use standard input.
  286.          * Keep the file descriptor open because we can't reopen it.
  287.          */
  288.         f = fd0;
  289.         chflags |= CH_KEEPOPEN;
  290.         /*
  291.          * Must switch stdin to BINARY mode.
  292.          */
  293.         SET_BINARY(f);
  294. #if MSDOS_COMPILER==DJGPPC
  295.         /*
  296.          * Setting stdin to binary by default causes
  297.          * Ctrl-C to not raise SIGINT.  We must undo
  298.          * that side-effect.
  299.          */
  300.         __djgpp_set_ctrl_c(1);
  301. #endif
  302.     } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
  303.     {
  304.         f = -1;
  305.         chflags |= CH_HELPFILE;
  306.     } else if ((parg.p_string = bad_file(open_filename)) != NULL)
  307.     {
  308.         /*
  309.          * It looks like a bad file.  Don't try to open it.
  310.          */
  311.         error("%s", &parg);
  312.         free(parg.p_string);
  313.         err1:
  314.         if (alt_filename != NULL)
  315.         {
  316.             close_altfile(alt_filename, filename, alt_pipe);
  317.             free(alt_filename);
  318.         }
  319.         del_ifile(ifile);
  320.         free(qopen_filename);
  321.         free(filename);
  322.         /*
  323.          * Re-open the current file.
  324.          */
  325.         reedit_ifile(was_curr_ifile);
  326.         return (1);
  327.     } else if ((f = open(qopen_filename, OPEN_READ)) < 0)
  328.     {
  329.         /*
  330.          * Got an error trying to open it.
  331.          */
  332.         parg.p_string = errno_message(filename);
  333.         error("%s", &parg);
  334.         free(parg.p_string);
  335.             goto err1;
  336.     } else 
  337.     {
  338.         chflags |= CH_CANSEEK;
  339.         if (!force_open && !opened(ifile) && bin_file(f))
  340.         {
  341.             /*
  342.              * Looks like a binary file.  
  343.              * Ask user if we should proceed.
  344.              */
  345.             parg.p_string = filename;
  346.             answer = query("\"%s\" may be a binary file.  See it anyway? ",
  347.                 &parg);
  348.             if (answer != 'y' && answer != 'Y')
  349.             {
  350.                 close(f);
  351.                 goto err1;
  352.             }
  353.         }
  354.     }
  355.     free(qopen_filename);
  356.  
  357.     /*
  358.      * Get the new ifile.
  359.      * Get the saved position for the file.
  360.      */
  361.     if (was_curr_ifile != NULL_IFILE)
  362.     {
  363.         old_ifile = was_curr_ifile;
  364.         unsave_ifile(was_curr_ifile);
  365.     }
  366.     curr_ifile = ifile;
  367.     curr_altfilename = alt_filename;
  368.     curr_altpipe = alt_pipe;
  369.     set_open(curr_ifile); /* File has been opened */
  370.     get_pos(curr_ifile, &initial_scrpos);
  371.     new_file = TRUE;
  372.     ch_init(f, chflags);
  373.  
  374.     if (!(chflags & CH_HELPFILE))
  375.     {
  376. #if LOGFILE
  377.         if (namelogfile != NULL && is_tty)
  378.             use_logfile(namelogfile);
  379. #endif
  380.         if (every_first_cmd != NULL)
  381.             ungetsc(every_first_cmd);
  382.     }
  383.  
  384.     no_display = !any_display;
  385.     flush();
  386.     any_display = TRUE;
  387.  
  388.     if (is_tty)
  389.     {
  390.         /*
  391.          * Output is to a real tty.
  392.          */
  393.  
  394.         /*
  395.          * Indicate there is nothing displayed yet.
  396.          */
  397.         pos_clear();
  398.         clr_linenum();
  399. #if HILITE_SEARCH
  400.         clr_hilite();
  401. #endif
  402.         cmd_addhist(ml_examine, filename);
  403.         if (no_display && errmsgs > 0)
  404.         {
  405.             /*
  406.              * We displayed some messages on error output
  407.              * (file descriptor 2; see error() function).
  408.              * Before erasing the screen contents,
  409.              * display the file name and wait for a keystroke.
  410.              */
  411.             parg.p_string = filename;
  412.             error("%s", &parg);
  413.         }
  414.     }
  415.     free(filename);
  416.     return (0);
  417. }
  418.  
  419. /*
  420.  * Edit a space-separated list of files.
  421.  * For each filename in the list, enter it into the ifile list.
  422.  * Then edit the first one.
  423.  */
  424.     public int
  425. edit_list(filelist)
  426.     char *filelist;
  427. {
  428.     IFILE save_ifile;
  429.     char *good_filename;
  430.     char *filename;
  431.     char *gfilelist;
  432.     char *gfilename;
  433.     struct textlist tl_files;
  434.     struct textlist tl_gfiles;
  435.  
  436.     save_ifile = save_curr_ifile();
  437.     good_filename = NULL;
  438.     
  439.     /*
  440.      * Run thru each filename in the list.
  441.      * Try to glob the filename.  
  442.      * If it doesn't expand, just try to open the filename.
  443.      * If it does expand, try to open each name in that list.
  444.      */
  445.     init_textlist(&tl_files, filelist);
  446.     filename = NULL;
  447.     while ((filename = forw_textlist(&tl_files, filename)) != NULL)
  448.     {
  449.         gfilelist = lglob(filename);
  450.         init_textlist(&tl_gfiles, gfilelist);
  451.         gfilename = NULL;
  452.         while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
  453.         {
  454.             if (edit(gfilename) == 0 && good_filename == NULL)
  455.                 good_filename = get_filename(curr_ifile);
  456.         }
  457.         free(gfilelist);
  458.     }
  459.     /*
  460.      * Edit the first valid filename in the list.
  461.      */
  462.     if (good_filename == NULL)
  463.     {
  464.         unsave_ifile(save_ifile);
  465.         return (1);
  466.     }
  467.     if (get_ifile(good_filename, curr_ifile) == curr_ifile)
  468.     {
  469.         /*
  470.          * Trying to edit the current file; don't reopen it.
  471.          */
  472.         unsave_ifile(save_ifile);
  473.         return (0);
  474.     }
  475.     reedit_ifile(save_ifile);
  476.     return (edit(good_filename));
  477. }
  478.  
  479. /*
  480.  * Edit the first file in the command line (ifile) list.
  481.  */
  482.     public int
  483. edit_first()
  484. {
  485.     curr_ifile = NULL_IFILE;
  486.     return (edit_next(1));
  487. }
  488.  
  489. /*
  490.  * Edit the last file in the command line (ifile) list.
  491.  */
  492.     public int
  493. edit_last()
  494. {
  495.     curr_ifile = NULL_IFILE;
  496.     return (edit_prev(1));
  497. }
  498.  
  499.  
  500. /*
  501.  * Edit the next or previous file in the command line (ifile) list.
  502.  */
  503.     static int
  504. edit_istep(h, n, dir)
  505.     IFILE h;
  506.     int n;
  507.     int dir;
  508. {
  509.     IFILE next;
  510.  
  511.     /*
  512.      * Skip n filenames, then try to edit each filename.
  513.      */
  514.     for (;;)
  515.     {
  516.         next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
  517.         if (--n < 0)
  518.         {
  519.             if (edit_ifile(h) == 0)
  520.                 break;
  521.         }
  522.         if (next == NULL_IFILE)
  523.         {
  524.             /*
  525.              * Reached end of the ifile list.
  526.              */
  527.             return (1);
  528.         }
  529.         if (ABORT_SIGS())
  530.         {
  531.             /*
  532.              * Interrupt breaks out, if we're in a long
  533.              * list of files that can't be opened.
  534.              */
  535.             return (1);
  536.         }
  537.         h = next;
  538.     } 
  539.     /*
  540.      * Found a file that we can edit.
  541.      */
  542.     return (0);
  543. }
  544.  
  545.     static int
  546. edit_inext(h, n)
  547.     IFILE h;
  548.     int n;
  549. {
  550.     return (edit_istep(h, n, 1));
  551. }
  552.  
  553.     public int
  554. edit_next(n)
  555.     int n;
  556. {
  557.     return edit_istep(curr_ifile, n, 1);
  558. }
  559.  
  560.     static int
  561. edit_iprev(h, n)
  562.     IFILE h;
  563.     int n;
  564. {
  565.     return (edit_istep(h, n, -1));
  566. }
  567.  
  568.     public int
  569. edit_prev(n)
  570.     int n;
  571. {
  572.     return edit_istep(curr_ifile, n, -1);
  573. }
  574.  
  575. /*
  576.  * Edit a specific file in the command line (ifile) list.
  577.  */
  578.     public int
  579. edit_index(n)
  580.     int n;
  581. {
  582.     IFILE h;
  583.  
  584.     h = NULL_IFILE;
  585.     do
  586.     {
  587.         if ((h = next_ifile(h)) == NULL_IFILE)
  588.         {
  589.             /*
  590.              * Reached end of the list without finding it.
  591.              */
  592.             return (1);
  593.         }
  594.     } while (get_index(h) != n);
  595.  
  596.     return (edit_ifile(h));
  597. }
  598.  
  599.     public IFILE
  600. save_curr_ifile()
  601. {
  602.     if (curr_ifile != NULL_IFILE)
  603.         hold_ifile(curr_ifile, 1);
  604.     return (curr_ifile);
  605. }
  606.  
  607.     public void
  608. unsave_ifile(save_ifile)
  609.     IFILE save_ifile;
  610. {
  611.     if (save_ifile != NULL_IFILE)
  612.         hold_ifile(save_ifile, -1);
  613. }
  614.  
  615. /*
  616.  * Reedit the ifile which was previously open.
  617.  */
  618.     public void
  619. reedit_ifile(save_ifile)
  620.     IFILE save_ifile;
  621. {
  622.     IFILE next;
  623.     IFILE prev;
  624.  
  625.     /*
  626.      * Try to reopen the ifile.
  627.      * Note that opening it may fail (maybe the file was removed),
  628.      * in which case the ifile will be deleted from the list.
  629.      * So save the next and prev ifiles first.
  630.      */
  631.     unsave_ifile(save_ifile);
  632.     next = next_ifile(save_ifile);
  633.     prev = prev_ifile(save_ifile);
  634.     if (edit_ifile(save_ifile) == 0)
  635.         return;
  636.     /*
  637.      * If can't reopen it, open the next input file in the list.
  638.      */
  639.     if (next != NULL_IFILE && edit_inext(next, 0) == 0)
  640.         return;
  641.     /*
  642.      * If can't open THAT one, open the previous input file in the list.
  643.      */
  644.     if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
  645.         return;
  646.     /*
  647.      * If can't even open that, we're stuck.  Just quit.
  648.      */
  649.     quit(QUIT_ERROR);
  650. }
  651.  
  652. /*
  653.  * Edit standard input.
  654.  */
  655.     public int
  656. edit_stdin()
  657. {
  658.     if (isatty(fd0))
  659.     {
  660.         error("Missing filename (\"less --help\" for help)", NULL_PARG);
  661.         quit(QUIT_OK);
  662.     }
  663.     return (edit("-"));
  664. }
  665.  
  666. /*
  667.  * Copy a file directly to standard output.
  668.  * Used if standard output is not a tty.
  669.  */
  670.     public void
  671. cat_file()
  672. {
  673.     register int c;
  674.  
  675.     while ((c = ch_forw_get()) != EOI)
  676.         putchr(c);
  677.     flush();
  678. }
  679.  
  680. #if LOGFILE
  681.  
  682. /*
  683.  * If the user asked for a log file and our input file
  684.  * is standard input, create the log file.  
  685.  * We take care not to blindly overwrite an existing file.
  686.  */
  687.     public void
  688. use_logfile(filename)
  689.     char *filename;
  690. {
  691.     register int exists;
  692.     register int answer;
  693.     PARG parg;
  694.  
  695.     if (ch_getflags() & CH_CANSEEK)
  696.         /*
  697.          * Can't currently use a log file on a file that can seek.
  698.          */
  699.         return;
  700.  
  701.     /*
  702.      * {{ We could use access() here. }}
  703.      */
  704.     filename = shell_unquote(filename);
  705.     exists = open(filename, OPEN_READ);
  706.     close(exists);
  707.     exists = (exists >= 0);
  708.  
  709.     /*
  710.      * Decide whether to overwrite the log file or append to it.
  711.      * If it doesn't exist we "overwrite" it.
  712.      */
  713.     if (!exists || force_logfile)
  714.     {
  715.         /*
  716.          * Overwrite (or create) the log file.
  717.          */
  718.         answer = 'O';
  719.     } else
  720.     {
  721.         /*
  722.          * Ask user what to do.
  723.          */
  724.         parg.p_string = filename;
  725.         answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
  726.     }
  727.  
  728. loop:
  729.     switch (answer)
  730.     {
  731.     case 'O': case 'o':
  732.         /*
  733.          * Overwrite: create the file.
  734.          */
  735.         logfile = creat(filename, 0644);
  736.         break;
  737.     case 'A': case 'a':
  738.         /*
  739.          * Append: open the file and seek to the end.
  740.          */
  741.         logfile = open(filename, OPEN_APPEND);
  742.         if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
  743.         {
  744.             close(logfile);
  745.             logfile = -1;
  746.         }
  747.         break;
  748.     case 'D': case 'd':
  749.         /*
  750.          * Don't do anything.
  751.          */
  752.         free(filename);
  753.         return;
  754.     case 'q':
  755.         quit(QUIT_OK);
  756.         /*NOTREACHED*/
  757.     default:
  758.         /*
  759.          * Eh?
  760.          */
  761.         answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
  762.         goto loop;
  763.     }
  764.  
  765.     if (logfile < 0)
  766.     {
  767.         /*
  768.          * Error in opening logfile.
  769.          */
  770.         parg.p_string = filename;
  771.         error("Cannot write to \"%s\"", &parg);
  772.         free(filename);
  773.         return;
  774.     }
  775.     free(filename);
  776.     SET_BINARY(logfile);
  777. }
  778.  
  779. #endif
  780.