Logo Search packages:      
Sourcecode: less version File versions

edit.c

/*
 * Copyright (C) 1984-2002  Mark Nudelman
 *
 * You may distribute under the terms of either the GNU General Public
 * License or the Less License, as specified in the README file.
 *
 * For more information about less, or for information on how to 
 * contact the author, see the README file.
 */


#include "less.h"

public int fd0 = 0;

extern int new_file;
extern int errmsgs;
extern int cbufs;
extern char *every_first_cmd;
extern int any_display;
extern int force_open;
extern int is_tty;
extern int sigs;
extern IFILE curr_ifile;
extern IFILE old_ifile;
extern struct scrpos initial_scrpos;
extern void constant *ml_examine;
#if SPACES_IN_FILENAMES
extern char openquote;
extern char closequote;
#endif

#if LOGFILE
extern int logfile;
extern int force_logfile;
extern char *namelogfile;
#endif

char *curr_altfilename = NULL;
static void *curr_altpipe;


/*
 * Textlist functions deal with a list of words separated by spaces.
 * init_textlist sets up a textlist structure.
 * forw_textlist uses that structure to iterate thru the list of
 * words, returning each one as a standard null-terminated string.
 * back_textlist does the same, but runs thru the list backwards.
 */
      public void
init_textlist(tlist, str)
      struct textlist *tlist;
      char *str;
{
      char *s;
#if SPACES_IN_FILENAMES
      int meta_quoted = 0;
      int delim_quoted = 0;
      char *esc = get_meta_escape();
      int esclen = strlen(esc);
#endif
      
      tlist->string = skipsp(str);
      tlist->endstring = tlist->string + strlen(tlist->string);
      for (s = str;  s < tlist->endstring;  s++)
      {
#if SPACES_IN_FILENAMES
            if (meta_quoted)
            {
                  meta_quoted = 0;
            } else if (esclen > 0 && s + esclen < tlist->endstring &&
                       strncmp(s, esc, esclen) == 0)
            {
                  meta_quoted = 1;
                  s += esclen - 1;
            } else if (delim_quoted)
            {
                  if (*s == closequote)
                        delim_quoted = 0;
            } else /* (!delim_quoted) */
            {
                  if (*s == openquote)
                        delim_quoted = 1;
                  else if (*s == ' ')
                        *s = '\0';
            }
#else
            if (*s == ' ')
                  *s = '\0';
#endif
      }
}

      public char *
forw_textlist(tlist, prev)
      struct textlist *tlist;
      char *prev;
{
      char *s;
      
      /*
       * prev == NULL means return the first word in the list.
       * Otherwise, return the word after "prev".
       */
      if (prev == NULL)
            s = tlist->string;
      else
            s = prev + strlen(prev);
      if (s >= tlist->endstring)
            return (NULL);
      while (*s == '\0')
            s++;
      if (s >= tlist->endstring)
            return (NULL);
      return (s);
}

      public char *
back_textlist(tlist, prev)
      struct textlist *tlist;
      char *prev;
{
      char *s;
      
      /*
       * prev == NULL means return the last word in the list.
       * Otherwise, return the word before "prev".
       */
      if (prev == NULL)
            s = tlist->endstring;
      else if (prev <= tlist->string)
            return (NULL);
      else
            s = prev - 1;
      while (*s == '\0')
            s--;
      if (s <= tlist->string)
            return (NULL);
      while (s[-1] != '\0' && s > tlist->string)
            s--;
      return (s);
}

/*
 * Close the current input file.
 */
      static void
close_file()
{
      struct scrpos scrpos;
      
      if (curr_ifile == NULL_IFILE)
            return;

      /*
       * Save the current position so that we can return to
       * the same position if we edit this file again.
       */
      get_scrpos(&scrpos);
      if (scrpos.pos != NULL_POSITION)
      {
            store_pos(curr_ifile, &scrpos);
            lastmark();
      }
      /*
       * Close the file descriptor, unless it is a pipe.
       */
      ch_close();
      /*
       * If we opened a file using an alternate name,
       * do special stuff to close it.
       */
      if (curr_altfilename != NULL)
      {
            close_altfile(curr_altfilename, get_filename(curr_ifile),
                        curr_altpipe);
            free(curr_altfilename);
            curr_altfilename = NULL;
      }
      curr_ifile = NULL_IFILE;
}

/*
 * Edit a new file (given its name).
 * Filename == "-" means standard input.
 * Filename == NULL means just close the current file.
 */
      public int
edit(filename)
      char *filename;
{
      if (filename == NULL)
            return (edit_ifile(NULL_IFILE));
      return (edit_ifile(get_ifile(filename, curr_ifile)));
}
      
/*
 * Edit a new file (given its IFILE).
 * ifile == NULL means just close the current file.
 */
      public int
edit_ifile(ifile)
      IFILE ifile;
{
      int f;
      int answer;
      int no_display;
      int chflags;
      char *filename;
      char *open_filename;
      char *qopen_filename;
      char *alt_filename;
      void *alt_pipe;
      IFILE was_curr_ifile;
      PARG parg;
            
      if (ifile == curr_ifile)
      {
            /*
             * Already have the correct file open.
             */
            return (0);
      }

      /*
       * We must close the currently open file now.
       * This is necessary to make the open_altfile/close_altfile pairs
       * nest properly (or rather to avoid nesting at all).
       * {{ Some stupid implementations of popen() mess up if you do:
       *    fA = popen("A"); fB = popen("B"); pclose(fA); pclose(fB); }}
       */
#if LOGFILE
      end_logfile();
#endif
      was_curr_ifile = save_curr_ifile();
      if (curr_ifile != NULL_IFILE)
      {
            chflags = ch_getflags();
            close_file();
            if ((chflags & CH_HELPFILE) && held_ifile(was_curr_ifile) <= 1)
            {
                  /*
                   * Don't keep the help file in the ifile list.
                   */
                  del_ifile(was_curr_ifile);
                  was_curr_ifile = old_ifile;
            }
      }

      if (ifile == NULL_IFILE)
      {
            /*
             * No new file to open.
             * (Don't set old_ifile, because if you call edit_ifile(NULL),
             *  you're supposed to have saved curr_ifile yourself,
             *  and you'll restore it if necessary.)
             */
            unsave_ifile(was_curr_ifile);
            return (0);
      }

      filename = save(get_filename(ifile));
      /*
       * See if LESSOPEN specifies an "alternate" file to open.
       */
      alt_pipe = NULL;
      alt_filename = open_altfile(filename, &f, &alt_pipe);
      open_filename = (alt_filename != NULL) ? alt_filename : filename;
      qopen_filename = shell_unquote(open_filename);

      chflags = 0;
      if (alt_pipe != NULL)
      {
            /*
             * The alternate "file" is actually a pipe.
             * f has already been set to the file descriptor of the pipe
             * in the call to open_altfile above.
             * Keep the file descriptor open because it was opened 
             * via popen(), and pclose() wants to close it.
             */
            chflags |= CH_POPENED;
      } else if (strcmp(open_filename, "-") == 0)
      {
            /* 
             * Use standard input.
             * Keep the file descriptor open because we can't reopen it.
             */
            f = fd0;
            chflags |= CH_KEEPOPEN;
            /*
             * Must switch stdin to BINARY mode.
             */
            SET_BINARY(f);
#if MSDOS_COMPILER==DJGPPC
            /*
             * Setting stdin to binary by default causes
             * Ctrl-C to not raise SIGINT.  We must undo
             * that side-effect.
             */
            __djgpp_set_ctrl_c(1);
#endif
      } else if (strcmp(open_filename, FAKE_HELPFILE) == 0)
      {
            f = -1;
            chflags |= CH_HELPFILE;
      } else if ((parg.p_string = bad_file(open_filename)) != NULL)
      {
            /*
             * It looks like a bad file.  Don't try to open it.
             */
            error("%s", &parg);
            free(parg.p_string);
          err1:
            if (alt_filename != NULL)
            {
                  close_altfile(alt_filename, filename, alt_pipe);
                  free(alt_filename);
            }
            del_ifile(ifile);
            free(qopen_filename);
            free(filename);
            /*
             * Re-open the current file.
             */
            reedit_ifile(was_curr_ifile);
            return (1);
      } else if ((f = open(qopen_filename, OPEN_READ)) < 0)
      {
            /*
             * Got an error trying to open it.
             */
            parg.p_string = errno_message(filename);
            error("%s", &parg);
            free(parg.p_string);
            goto err1;
      } else 
      {
            chflags |= CH_CANSEEK;
            if (!force_open && !opened(ifile) && bin_file(f))
            {
                  /*
                   * Looks like a binary file.  
                   * Ask user if we should proceed.
                   */
                  parg.p_string = filename;
                  answer = query("\"%s\" may be a binary file.  See it anyway? ",
                        &parg);
                  if (answer != 'y' && answer != 'Y')
                  {
                        close(f);
                        goto err1;
                  }
            }
      }
      free(qopen_filename);

      /*
       * Get the new ifile.
       * Get the saved position for the file.
       */
      if (was_curr_ifile != NULL_IFILE)
      {
            old_ifile = was_curr_ifile;
            unsave_ifile(was_curr_ifile);
      }
      curr_ifile = ifile;
      curr_altfilename = alt_filename;
      curr_altpipe = alt_pipe;
      set_open(curr_ifile); /* File has been opened */
      get_pos(curr_ifile, &initial_scrpos);
      new_file = TRUE;
      ch_init(f, chflags);

      if (!(chflags & CH_HELPFILE))
      {
#if LOGFILE
            if (namelogfile != NULL && is_tty)
                  use_logfile(namelogfile);
#endif
            if (every_first_cmd != NULL)
                  ungetsc(every_first_cmd);
      }

      no_display = !any_display;
      flush();
      any_display = TRUE;

      if (is_tty)
      {
            /*
             * Output is to a real tty.
             */

            /*
             * Indicate there is nothing displayed yet.
             */
            pos_clear();
            clr_linenum();
#if HILITE_SEARCH
            clr_hilite();
#endif
            cmd_addhist(ml_examine, filename);
            if (no_display && errmsgs > 0)
            {
                  /*
                   * We displayed some messages on error output
                   * (file descriptor 2; see error() function).
                   * Before erasing the screen contents,
                   * display the file name and wait for a keystroke.
                   */
                  parg.p_string = filename;
                  error("%s", &parg);
            }
      }
      free(filename);
      return (0);
}

/*
 * Edit a space-separated list of files.
 * For each filename in the list, enter it into the ifile list.
 * Then edit the first one.
 */
      public int
edit_list(filelist)
      char *filelist;
{
      IFILE save_ifile;
      char *good_filename;
      char *filename;
      char *gfilelist;
      char *gfilename;
      struct textlist tl_files;
      struct textlist tl_gfiles;

      save_ifile = save_curr_ifile();
      good_filename = NULL;
      
      /*
       * Run thru each filename in the list.
       * Try to glob the filename.  
       * If it doesn't expand, just try to open the filename.
       * If it does expand, try to open each name in that list.
       */
      init_textlist(&tl_files, filelist);
      filename = NULL;
      while ((filename = forw_textlist(&tl_files, filename)) != NULL)
      {
            gfilelist = lglob(filename);
            init_textlist(&tl_gfiles, gfilelist);
            gfilename = NULL;
            while ((gfilename = forw_textlist(&tl_gfiles, gfilename)) != NULL)
            {
                  if (edit(gfilename) == 0 && good_filename == NULL)
                        good_filename = get_filename(curr_ifile);
            }
            free(gfilelist);
      }
      /*
       * Edit the first valid filename in the list.
       */
      if (good_filename == NULL)
      {
            unsave_ifile(save_ifile);
            return (1);
      }
      if (get_ifile(good_filename, curr_ifile) == curr_ifile)
      {
            /*
             * Trying to edit the current file; don't reopen it.
             */
            unsave_ifile(save_ifile);
            return (0);
      }
      reedit_ifile(save_ifile);
      return (edit(good_filename));
}

/*
 * Edit the first file in the command line (ifile) list.
 */
      public int
edit_first()
{
      curr_ifile = NULL_IFILE;
      return (edit_next(1));
}

/*
 * Edit the last file in the command line (ifile) list.
 */
      public int
edit_last()
{
      curr_ifile = NULL_IFILE;
      return (edit_prev(1));
}


/*
 * Edit the next or previous file in the command line (ifile) list.
 */
      static int
edit_istep(h, n, dir)
      IFILE h;
      int n;
      int dir;
{
      IFILE next;

      /*
       * Skip n filenames, then try to edit each filename.
       */
      for (;;)
      {
            next = (dir > 0) ? next_ifile(h) : prev_ifile(h);
            if (--n < 0)
            {
                  if (edit_ifile(h) == 0)
                        break;
            }
            if (next == NULL_IFILE)
            {
                  /*
                   * Reached end of the ifile list.
                   */
                  return (1);
            }
            if (ABORT_SIGS())
            {
                  /*
                   * Interrupt breaks out, if we're in a long
                   * list of files that can't be opened.
                   */
                  return (1);
            }
            h = next;
      } 
      /*
       * Found a file that we can edit.
       */
      return (0);
}

      static int
edit_inext(h, n)
      IFILE h;
      int n;
{
      return (edit_istep(h, n, 1));
}

      public int
edit_next(n)
      int n;
{
      return edit_istep(curr_ifile, n, 1);
}

      static int
edit_iprev(h, n)
      IFILE h;
      int n;
{
      return (edit_istep(h, n, -1));
}

      public int
edit_prev(n)
      int n;
{
      return edit_istep(curr_ifile, n, -1);
}

/*
 * Edit a specific file in the command line (ifile) list.
 */
      public int
edit_index(n)
      int n;
{
      IFILE h;

      h = NULL_IFILE;
      do
      {
            if ((h = next_ifile(h)) == NULL_IFILE)
            {
                  /*
                   * Reached end of the list without finding it.
                   */
                  return (1);
            }
      } while (get_index(h) != n);

      return (edit_ifile(h));
}

      public IFILE
save_curr_ifile()
{
      if (curr_ifile != NULL_IFILE)
            hold_ifile(curr_ifile, 1);
      return (curr_ifile);
}

      public void
unsave_ifile(save_ifile)
      IFILE save_ifile;
{
      if (save_ifile != NULL_IFILE)
            hold_ifile(save_ifile, -1);
}

/*
 * Reedit the ifile which was previously open.
 */
      public void
reedit_ifile(save_ifile)
      IFILE save_ifile;
{
      IFILE next;
      IFILE prev;

      /*
       * Try to reopen the ifile.
       * Note that opening it may fail (maybe the file was removed),
       * in which case the ifile will be deleted from the list.
       * So save the next and prev ifiles first.
       */
      unsave_ifile(save_ifile);
      next = next_ifile(save_ifile);
      prev = prev_ifile(save_ifile);
      if (edit_ifile(save_ifile) == 0)
            return;
      /*
       * If can't reopen it, open the next input file in the list.
       */
      if (next != NULL_IFILE && edit_inext(next, 0) == 0)
            return;
      /*
       * If can't open THAT one, open the previous input file in the list.
       */
      if (prev != NULL_IFILE && edit_iprev(prev, 0) == 0)
            return;
      /*
       * If can't even open that, we're stuck.  Just quit.
       */
      quit(QUIT_ERROR);
}

/*
 * Edit standard input.
 */
      public int
edit_stdin()
{
      if (isatty(fd0))
      {
            error("Missing filename (\"less --help\" for help)", NULL_PARG);
            quit(QUIT_OK);
      }
      return (edit("-"));
}

/*
 * Copy a file directly to standard output.
 * Used if standard output is not a tty.
 */
      public void
cat_file()
{
      register int c;

      while ((c = ch_forw_get()) != EOI)
            putchr(c);
      flush();
}

#if LOGFILE

/*
 * If the user asked for a log file and our input file
 * is standard input, create the log file.  
 * We take care not to blindly overwrite an existing file.
 */
      public void
use_logfile(filename)
      char *filename;
{
      register int exists;
      register int answer;
      PARG parg;

      if (ch_getflags() & CH_CANSEEK)
            /*
             * Can't currently use a log file on a file that can seek.
             */
            return;

      /*
       * {{ We could use access() here. }}
       */
      filename = shell_unquote(filename);
      exists = open(filename, OPEN_READ);
      close(exists);
      exists = (exists >= 0);

      /*
       * Decide whether to overwrite the log file or append to it.
       * If it doesn't exist we "overwrite" it.
       */
      if (!exists || force_logfile)
      {
            /*
             * Overwrite (or create) the log file.
             */
            answer = 'O';
      } else
      {
            /*
             * Ask user what to do.
             */
            parg.p_string = filename;
            answer = query("Warning: \"%s\" exists; Overwrite, Append or Don't log? ", &parg);
      }

loop:
      switch (answer)
      {
      case 'O': case 'o':
            /*
             * Overwrite: create the file.
             */
            logfile = creat(filename, 0644);
            break;
      case 'A': case 'a':
            /*
             * Append: open the file and seek to the end.
             */
            logfile = open(filename, OPEN_APPEND);
            if (lseek(logfile, (off_t)0, 2) == BAD_LSEEK)
            {
                  close(logfile);
                  logfile = -1;
            }
            break;
      case 'D': case 'd':
            /*
             * Don't do anything.
             */
            free(filename);
            return;
      case 'q':
            quit(QUIT_OK);
            /*NOTREACHED*/
      default:
            /*
             * Eh?
             */
            answer = query("Overwrite, Append, or Don't log? (Type \"O\", \"A\", \"D\" or \"q\") ", NULL_PARG);
            goto loop;
      }

      if (logfile < 0)
      {
            /*
             * Error in opening logfile.
             */
            parg.p_string = filename;
            error("Cannot write to \"%s\"", &parg);
            free(filename);
            return;
      }
      free(filename);
      SET_BINARY(logfile);
}

#endif

Generated by  Doxygen 1.6.0   Back to index