less beta release (part 4 of 5)
Mark Nudelman
mark at unix386.Convergent.COM
Fri Sep 15 08:24:53 AEST 1989
#! /bin/sh
# This is a shell archive.
# Remove anything before this line, then unpack it
# by saving it into a file and typing "sh file".
echo shar: Extracting \"linenum.c\"
sed "s/^X//" >'linenum.c' <<'END_OF_FILE'
X/*
X * Code to handle displaying line numbers.
X *
X * Finding the line number of a given file position is rather tricky.
X * We don't want to just start at the beginning of the file and
X * count newlines, because that is slow for large files (and also
X * wouldn't work if we couldn't get to the start of the file; e.g.
X * if input is a long pipe).
X *
X * So we use the function add_lnum to cache line numbers.
X * We try to be very clever and keep only the more interesting
X * line numbers when we run out of space in our table. A line
X * number is more interesting than another when it is far from
X * other line numbers. For example, we'd rather keep lines
X * 100,200,300 than 100,101,300. 200 is more interesting than
X * 101 because 101 can be derived very cheaply from 100, while
X * 200 is more expensive to derive from 100.
X *
X * The function currline() returns the line number of a given
X * position in the file. As a side effect, it calls add_lnum
X * to cache the line number. Therefore currline is occasionally
X * called to make sure we cache line numbers often enough.
X */
X
X#include "less.h"
X#include "position.h"
X
X/*
X * Structure to keep track of a line number and the associated file position.
X * A doubly-linked circular list of line numbers is kept ordered by line number.
X */
Xstruct linenum
X{
X struct linenum *next; /* Link to next in the list */
X struct linenum *prev; /* Line to previous in the list */
X POSITION pos; /* File position */
X POSITION gap; /* Gap between prev and next */
X int line; /* Line number */
X};
X/*
X * "gap" needs some explanation: the gap of any particular line number
X * is the distance between the previous one and the next one in the list.
X * ("Distance" means difference in file position.) In other words, the
X * gap of a line number is the gap which would be introduced if this
X * line number were deleted. It is used to decide which one to replace
X * when we have a new one to insert and the table is full.
X */
X
X#define NPOOL 50 /* Size of line number pool */
X
X#define LONGTIME (2) /* In seconds */
X
Xpublic int lnloop = 0; /* Are we in the line num loop? */
X
Xstatic struct linenum anchor; /* Anchor of the list */
Xstatic struct linenum *freelist; /* Anchor of the unused entries */
Xstatic struct linenum pool[NPOOL]; /* The pool itself */
Xstatic struct linenum *spare; /* We always keep one spare entry */
X
Xextern int linenums;
Xextern int sigs;
X
X/*
X * Initialize the line number structures.
X */
X public void
Xclr_linenum()
X{
X register struct linenum *p;
X
X /*
X * Put all the entries on the free list.
X * Leave one for the "spare".
X */
X for (p = pool; p < &pool[NPOOL-2]; p++)
X p->next = p+1;
X pool[NPOOL-2].next = NULL;
X freelist = pool;
X
X spare = &pool[NPOOL-1];
X
X /*
X * Initialize the anchor.
X */
X anchor.next = anchor.prev = &anchor;
X anchor.gap = 0;
X anchor.pos = (POSITION)0;
X anchor.line = 1;
X}
X
X/*
X * Calculate the gap for an entry.
X */
X static void
Xcalcgap(p)
X register struct linenum *p;
X{
X /*
X * Don't bother to compute a gap for the anchor.
X * Also don't compute a gap for the last one in the list.
X * The gap for that last one should be considered infinite,
X * but we never look at it anyway.
X */
X if (p == &anchor || p->next == &anchor)
X return;
X p->gap = p->next->pos - p->prev->pos;
X}
X
X/*
X * Add a new line number to the cache.
X * The specified position (pos) should be the file position of the
X * FIRST character in the specified line.
X */
X public void
Xadd_lnum(lno, pos)
X int lno;
X POSITION pos;
X{
X register struct linenum *p;
X register struct linenum *new;
X register struct linenum *nextp;
X register struct linenum *prevp;
X register POSITION mingap;
X
X /*
X * Find the proper place in the list for the new one.
X * The entries are sorted by position.
X */
X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
X if (p->line == lno)
X /* We already have this one. */
X return;
X nextp = p;
X prevp = p->prev;
X
X if (freelist != NULL)
X {
X /*
X * We still have free (unused) entries.
X * Use one of them.
X */
X new = freelist;
X freelist = freelist->next;
X } else
X {
X /*
X * No free entries.
X * Use the "spare" entry.
X */
X new = spare;
X spare = NULL;
X }
X
X /*
X * Fill in the fields of the new entry,
X * and insert it into the proper place in the list.
X */
X new->next = nextp;
X new->prev = prevp;
X new->pos = pos;
X new->line = lno;
X
X nextp->prev = new;
X prevp->next = new;
X
X /*
X * Recalculate gaps for the new entry and the neighboring entries.
X */
X calcgap(new);
X calcgap(nextp);
X calcgap(prevp);
X
X if (spare == NULL)
X {
X /*
X * We have used the spare entry.
X * Scan the list to find the one with the smallest
X * gap, take it out and make it the spare.
X * We should never remove the last one, so stop when
X * we get to p->next == &anchor. This also avoids
X * looking at the gap of the last one, which is
X * not computed by calcgap.
X */
X mingap = anchor.next->gap;
X for (p = anchor.next; p->next != &anchor; p = p->next)
X {
X if (p->gap <= mingap)
X {
X spare = p;
X mingap = p->gap;
X }
X }
X spare->next->prev = spare->prev;
X spare->prev->next = spare->next;
X }
X}
X
X/*
X * If we get stuck in a long loop trying to figure out the
X * line number, print a message to tell the user what we're doing.
X */
X static void
Xlongloopmessage()
X{
X ierror("Calculating line numbers");
X /*
X * Set the lnloop flag here, so if the user interrupts while
X * we are calculating line numbers, the signal handler will
X * turn off line numbers (linenums=0).
X */
X lnloop = 1;
X}
X
Xstatic int loopcount;
X#if GET_TIME
Xstatic long startime;
X#endif
X
X static void
Xlongish()
X{
X#if GET_TIME
X if (loopcount >= 0 && ++loopcount > 100)
X {
X loopcount = 0;
X if (get_time() >= startime + LONGTIME)
X {
X longloopmessage();
X loopcount = -1;
X }
X }
X#else
X if (loopcount >= 0 && ++loopcount > LONGLOOP)
X {
X longloopmessage();
X loopcount = -1;
X }
X#endif
X}
X
X/*
X * Find the line number associated with a given position.
X * Return 0 if we can't figure it out.
X */
X public int
Xfind_linenum(pos)
X POSITION pos;
X{
X register struct linenum *p;
X register int lno;
X POSITION cpos;
X
X if (!linenums)
X /*
X * We're not using line numbers.
X */
X return (0);
X if (pos == NULL_POSITION)
X /*
X * Caller doesn't know what he's talking about.
X */
X return (0);
X if (pos == (POSITION)0)
X /*
X * Beginning of file is always line number 1.
X */
X return (1);
X
X /*
X * Find the entry nearest to the position we want.
X */
X for (p = anchor.next; p != &anchor && p->pos < pos; p = p->next)
X continue;
X if (p->pos == pos)
X /* Found it exactly. */
X return (p->line);
X
X /*
X * This is the (possibly) time-consuming part.
X * We start at the line we just found and start
X * reading the file forward or backward till we
X * get to the place we want.
X *
X * First decide whether we should go forward from the
X * previous one or backwards from the next one.
X * The decision is based on which way involves
X * traversing fewer bytes in the file.
X */
X flush();
X#if GET_TIME
X startime = get_time();
X#endif
X if (p == &anchor || pos - p->prev->pos < p->pos - pos)
X {
X /*
X * Go forward.
X */
X p = p->prev;
X if (ch_seek(p->pos))
X return (0);
X loopcount = 0;
X for (lno = p->line, cpos = p->pos; cpos < pos; lno++)
X {
X /*
X * Allow a signal to abort this loop.
X */
X cpos = forw_raw_line(cpos, (char **)NULL);
X if (sigs || cpos == NULL_POSITION)
X return (0);
X longish();
X }
X lnloop = 0;
X /*
X * We might as well cache it.
X */
X add_lnum(lno, cpos);
X /*
X * If the given position is not at the start of a line,
X * make sure we return the correct line number.
X */
X if (cpos > pos)
X lno--;
X } else
X {
X /*
X * Go backward.
X */
X if (ch_seek(p->pos))
X return (0);
X loopcount = 0;
X for (lno = p->line, cpos = p->pos; cpos > pos; lno--)
X {
X /*
X * Allow a signal to abort this loop.
X */
X cpos = back_raw_line(cpos, (char **)NULL);
X if (sigs || cpos == NULL_POSITION)
X return (0);
X longish();
X }
X lnloop = 0;
X /*
X * We might as well cache it.
X */
X add_lnum(lno, cpos);
X }
X
X return (lno);
X}
X
X/*
X * Find the position of a given line number.
X * Return NULL_POSITION if we can't figure it out.
X */
X public POSITION
Xfind_pos(lno)
X int lno;
X{
X register struct linenum *p;
X POSITION cpos;
X int clno;
X
X if (lno <= 1)
X /*
X * Line number 1 is beginning of file.
X */
X return ((POSITION)0);
X
X /*
X * Find the entry nearest to the line number we want.
X */
X for (p = anchor.next; p != &anchor && p->line < lno; p = p->next)
X continue;
X if (p->line == lno)
X /* Found it exactly. */
X return (p->pos);
X
X flush();
X if (p == &anchor || lno - p->prev->line < p->line - lno)
X {
X /*
X * Go forward.
X */
X p = p->prev;
X if (ch_seek(p->pos))
X return (NULL_POSITION);
X for (clno = p->line, cpos = p->pos; clno < lno; clno++)
X {
X /*
X * Allow a signal to abort this loop.
X */
X cpos = forw_raw_line(cpos, (char **)NULL);
X if (sigs || cpos == NULL_POSITION)
X return (NULL_POSITION);
X }
X } else
X {
X /*
X * Go backward.
X */
X if (ch_seek(p->pos))
X return (NULL_POSITION);
X for (clno = p->line, cpos = p->pos; clno > lno; clno--)
X {
X /*
X * Allow a signal to abort this loop.
X */
X cpos = back_raw_line(cpos, (char **)NULL);
X if (sigs || cpos == NULL_POSITION)
X return (NULL_POSITION);
X }
X }
X /*
X * We might as well cache it.
X */
X add_lnum(clno, cpos);
X return (cpos);
X}
X
X/*
X * Return the line number of the "current" line.
X * The argument "where" tells which line is to be considered
X * the "current" line (e.g. TOP, BOTTOM, MIDDLE, etc).
X */
X public int
Xcurrline(where)
X int where;
X{
X POSITION pos;
X POSITION len;
X int lnum;
X
X pos = position(where);
X len = ch_length();
X if (pos == NULL_POSITION)
X pos = len;
X lnum = find_linenum(pos);
X if (pos == len)
X lnum--;
X return (lnum);
X}
END_OF_FILE
echo shar: Extracting \"main.c\"
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/*
X * Entry point, initialization, miscellaneous routines.
X */
X
X#include "less.h"
X#include "position.h"
X
Xpublic int ispipe;
Xpublic char * first_cmd = NULL;
Xpublic char * every_first_cmd = NULL;
Xpublic int new_file;
Xpublic int is_tty;
Xpublic char * current_file = NULL;
Xpublic char * previous_file = NULL;
Xpublic HANDLE curr_handle;
Xpublic POSITION initial_pos;
Xpublic int any_display;
Xpublic int scroll;
Xpublic int ac;
Xpublic char ** av;
Xpublic int curr_ac;
Xpublic int quitting;
X
Xextern int file;
Xextern int quit_at_eof;
Xextern int hit_eof;
Xextern int cbufs;
Xextern int errmsgs;
Xextern int screen_trashed;
X
X#if LOGFILE
Xpublic int logfile = -1;
Xpublic int force_logfile = 0;
Xpublic char * namelogfile = NULL;
X#endif
X
X#if EDITOR
Xpublic char * editor;
Xpublic char * editproto;
X#endif
X
X#if TAGS
Xextern char * tagfile;
Xextern char * tagpattern;
Xextern int tagoption;
X#endif
X
X
X/*
X * Edit a new file.
X * Filename "-" means standard input.
X * No filename means the "current" file, from the command line.
X */
X public void
Xedit(filename)
X register char *filename;
X{
X register int f;
X register char *m;
X static int didpipe;
X
X if (filename == NULL || *filename == '\0')
X {
X if (curr_ac >= ac)
X {
X error("No current file");
X return;
X }
X filename = av[curr_ac];
X }
X
X if (strcmp(filename, "-") == 0)
X {
X /*
X * Use standard input.
X */
X if (didpipe)
X {
X error("Can view standard input only once");
X return;
X }
X f = 0;
X } else if ((m = bad_file(filename)) != NULL)
X {
X error(m);
X free(m);
X return;
X } else if ((f = open(filename, 0)) < 0)
X {
X m = errno_message(filename);
X error(m);
X free(m);
X return;
X }
X
X if (isatty(f))
X {
X /*
X * Not really necessary to call this an error,
X * but if the control terminal (for commands)
X * and the input file (for data) are the same,
X * we get weird results at best.
X */
X error("Can't take input from a terminal (\"less -\\?\" for help)");
X if (f > 0)
X close(f);
X return;
X }
X
X#if LOGFILE
X if (f == 0 && namelogfile != NULL && is_tty)
X use_logfile();
X#endif
X
X /*
X * We are now committed to using the new file.
X * Close the current input file and set up to use the new one.
X */
X if (current_file != NULL)
X {
X store_pos(curr_handle, position(TOP));
X lastmark();
X }
X
X curr_handle = get_handle(filename);
X initial_pos = recall_pos(curr_handle);
X
X previous_file = current_file;
X current_file = get_filename(curr_handle);
X
X if (file > 0)
X close(file);
X new_file = 1;
X ispipe = (f == 0);
X /*
X * {{ Would this make more sense? }}
X * ispipe = (lseek(f, (offset_t)0, 0) == BAD_LSEEK);
X */
X if (ispipe)
X didpipe = 1;
X file = f;
X ch_init(cbufs, 0);
X
X if (every_first_cmd != NULL)
X first_cmd = every_first_cmd;
X
X if (is_tty)
X {
X int no_display = !any_display;
X any_display = 1;
X /*
X * Indicate there is nothing displayed yet.
X */
X pos_clear();
X clr_linenum();
X if (no_display && errmsgs > 0)
X {
X /*
X * We displayed some messages on error output
X * (file descriptor 2; see error() function).
X * Before erasing the screen contents,
X * display the file name and wait for a keystroke.
X */
X error(filename);
X }
X }
X}
X
X/*
X * Edit the next file in the command line list.
X */
X public void
Xnext_file(n)
X int n;
X{
X if (curr_ac + n >= ac)
X {
X if (quit_at_eof && hit_eof)
X quit();
X error("No (N-th) next file");
X } else
X edit(av[curr_ac += n]);
X}
X
X/*
X * Edit the previous file in the command line list.
X */
X public void
Xprev_file(n)
X int n;
X{
X if (curr_ac - n < 0)
X error("No (N-th) previous file");
X else
X edit(av[curr_ac -= n]);
X}
X
X/*
X * Copy a file directly to standard output.
X * Used if standard output is not a tty.
X */
X static void
Xcat_file()
X{
X register int c;
X
X while ((c = ch_forw_get()) != EOI)
X putchr(c);
X flush();
X}
X
X#if LOGFILE
X
X/*
X * If the user asked for a log file and our input file
X * is standard input, create the log file.
X * We take care not to blindly overwrite an existing file.
X */
Xuse_logfile()
X{
X register int exists;
X register int answer;
X register char *m;
X
X end_logfile();
X
X /*
X * {{ We could use access() here. }}
X */
X exists = open(namelogfile, 0);
X close(exists);
X exists = (exists >= 0);
X
X if (exists && !force_logfile)
X {
X static char w[] = "WARNING: log file exists: ";
X m = ecalloc(sizeof(w) + strlen(namelogfile), sizeof(char));
X strcpy(m, w);
X strcat(m, namelogfile);
X error(m);
X free(m);
X answer = 'X'; /* Ask the user what to do */
X } else
X answer = 'O'; /* Create the log file */
X
Xloop:
X switch (answer)
X {
X case 'O': case 'o':
X logfile = creat(namelogfile, 0644);
X break;
X case 'A': case 'a':
X logfile = open(namelogfile, 1);
X if (lseek(logfile, (offset_t)0, 2) == BAD_LSEEK)
X {
X close(logfile);
X logfile = -1;
X }
X break;
X case 'D': case 'd':
X answer = 0; /* Don't print an error message */
X break;
X case 'q':
X quit();
X default:
X putstr("\n Overwrite, Append, or Don't log? ");
X screen_trashed = 1;
X answer = getchr();
X putstr("\n");
X flush();
X goto loop;
X }
X
X if (logfile < 0 && answer != 0)
X {
X m = ecalloc(strlen(namelogfile) + 20, sizeof(char));
X sprintf(m, "Cannot write to \"%s\"", namelogfile);
X error(m);
X free(m);
X }
X}
X
X#endif
X
X/*
X * Entry point.
X */
Xmain(argc, argv)
X int argc;
X char *argv[];
X{
X register int i;
X extern char *getenv();
X
X /*
X * Process command line arguments and LESS environment arguments.
X * Command line arguments override environment arguments.
X */
X init_prompt();
X init_option();
X scan_option(getenv("LESS"));
X
X#define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0')
X argv++;
X while (--argc > 0 && (isoptstring(argv[0]) || isoptpending()))
X scan_option(*argv++);
X#undef isoptstring
X
X if (isoptpending())
X {
X /*
X * Last command line option was a flag requiring a
X * following string, but there was no following string.
X */
X nopendopt();
X exit(0);
X }
X
X#if USERFILE
X /*
X * Try to use the lesskey file "$HOME/.less".
X */
X add_hometable();
X#endif
X#if EDITOR
X editor = getenv("EDITOR");
X if (editor == NULL || *editor == '\0')
X editor = EDIT_PGM;
X editproto = getenv("EDITPROTO");
X if (editproto == NULL || *editproto == '\0')
X editproto = "%E ?lm+%lm. %f";
X#endif
X
X /*
X * Set up list of files to be examined.
X */
X ac = argc;
X av = argv;
X curr_ac = 0;
X
X /*
X * Set up terminal, etc.
X */
X is_tty = isatty(1);
X if (!is_tty)
X {
X /*
X * Output is not a tty.
X * Just copy the input file(s) to output.
X */
X if (ac < 1)
X {
X edit("-");
X cat_file();
X } else
X {
X do
X {
X edit((char *)NULL);
X if (file >= 0)
X cat_file();
X } while (++curr_ac < ac);
X }
X exit(0);
X }
X
X /*
X * Call save_handle with all the command line filenames,
X * just to avoid copying them to calloc'ed space later
X * when we call get_handle() from edit().
X */
X for (i = 0; i < ac; i++)
X save_handle(av[i]);
X
X init_mark();
X raw_mode(1);
X get_term();
X open_getchr();
X init();
X
X init_signals(1);
X
X /*
X * Select the first file to examine.
X */
X#if TAGS
X if (tagoption)
X {
X /*
X * A -t option was given.
X * Verify that no filenames were also given.
X * Edit the file selected by the "tags" search,
X * and search for the proper line in the file.
X */
X if (ac > 0)
X {
X error("No filenames allowed with -t option");
X quit();
X }
X if (tagfile == NULL)
X quit();
X edit(tagfile);
X if (file < 0)
X quit();
X if (tagsearch())
X quit();
X } else
X#endif
X if (ac < 1)
X edit("-"); /* Standard input */
X else
X {
X /*
X * Try all the files named as command arguments.
X * We are simply looking for one which can be
X * opened without error.
X */
X do
X {
X edit((char *)NULL);
X } while (file < 0 && ++curr_ac < ac);
X }
X
X if (file >= 0)
X commands();
X quit();
X /*NOTREACHED*/
X}
X
X/*
X * Copy a string, truncating to the specified length if necessary.
X * Unlike strncpy(), the resulting string is guaranteed to be null-terminated.
X */
X public void
Xstrtcpy(to, from, len)
X char *to;
X char *from;
X unsigned int len;
X{
X strncpy(to, from, len);
X to[len-1] = '\0';
X}
X
X/*
X * Copy a string to a "safe" place
X * (that is, to a buffer allocated by calloc).
X */
X public char *
Xsave(s)
X char *s;
X{
X register char *p;
X
X p = (char *) ecalloc(strlen(s)+1, sizeof(char));
X strcpy(p, s);
X return (p);
X}
X
X public char *
Xecalloc(count, size)
X int count;
X unsigned int size;
X{
X register char *p;
X
X p = calloc(count, size);
X if (p != NULL)
X return (p);
X error("Cannot allocate memory");
X quit();
X /*NOTREACHED*/
X}
X
X/*
X * Skip leading spaces in a string.
X */
X public char *
Xskipsp(s)
X register char *s;
X{
X while (*s == ' ' || *s == '\t')
X s++;
X return (s);
X}
X
X/*
X * Exit the program.
X */
X public void
Xquit()
X{
X /*
X * Put cursor at bottom left corner, clear the line,
X * reset the terminal modes, and exit.
X */
X quitting = 1;
X#if LOGFILE
X end_logfile();
X#endif
X lower_left();
X clear_eol();
X deinit();
X flush();
X raw_mode(0);
X exit(0);
X}
END_OF_FILE
echo shar: Extracting \"option.c\"
sed "s/^X//" >'option.c' <<'END_OF_FILE'
X/*
X * Process command line options.
X *
X * Each option is a single letter which controls a program variable.
X * The options have defaults which may be changed via
X * the command line option, toggled via the "-" command,
X * or queried via the "_" command.
X */
X
X#include "less.h"
X#include "option.h"
X
Xstatic struct option *pendopt;
Xpublic int plusoption;
X
Xstatic char *propt();
Xstatic char *optstring();
X
Xextern int screen_trashed;
Xextern char *first_cmd;
Xextern char *every_first_cmd;
X
X/*
X * Scan an argument (either from the command line or from the
X * LESS environment variable) and process it.
X */
X public void
Xscan_option(s)
X char *s;
X{
X register struct option *o;
X register int c;
X char *str;
X int set_default;
X char message[80];
X
X if (s == NULL)
X return;
X
X /*
X * If we have a pending string-valued option, handle it now.
X * This happens if the previous option was, for example, "-P"
X * without a following string. In that case, the current
X * option is simply the string for the previous option.
X */
X if (pendopt != NULL)
X {
X (*pendopt->ofunc)(INIT, s);
X pendopt = NULL;
X return;
X }
X
X set_default = 0;
X
X while (*s != '\0')
X {
X /*
X * Check some special cases first.
X */
X switch (c = *s++)
X {
X case ' ':
X case '\t':
X case END_OPTION_STRING:
X continue;
X case '-':
X /*
X * "-+" means set these options back to their defaults.
X * (They may have been set otherwise by previous
X * options.)
X */
X if (set_default = (*s == '+'))
X s++;
X continue;
X case '+':
X /*
X * An option prefixed by a "+" becomes the "first_cmd"
X * string, which is interpreted as less commands
X * processed at the start of the first input file.
X * "++" means process the commands at the start of
X * EVERY input file.
X */
X plusoption = 1;
X if (*s == '+')
X every_first_cmd = save(++s);
X first_cmd = s;
X s = optstring(s, c);
X continue;
X case '0': case '1': case '2': case '3': case '4':
X case '5': case '6': case '7': case '8': case '9':
X /*
X * Special "more" compatibility form "-<number>"
X * instead of -z<number> to set the scrolling
X * window size.
X */
X s--;
X c = 'z';
X break;
X }
X
X /*
X * Not a special case.
X * Look up the option letter in the option table.
X */
X o = findopt(c);
X if (o == NULL)
X {
X sprintf(message,
X "There is no %s flag (\"less -\\?\" for help)",
X propt(c));
X error(message);
X exit(1);
X }
X
X switch (o->otype & OTYPE)
X {
X case BOOL:
X if (set_default)
X *(o->ovar) = o->odefault;
X else
X *(o->ovar) = ! o->odefault;
X break;
X case TRIPLE:
X if (set_default)
X *(o->ovar) = o->odefault;
X else if (o->oletter == c)
X *(o->ovar) = (o->odefault == 1) ? 0 : 1;
X else
X *(o->ovar) = (o->odefault == 2) ? 0 : 2;
X break;
X case STRING:
X if (*s == '\0')
X {
X /*
X * Set pendopt and return.
X * We will get the string next time
X * scan_option is called.
X */
X pendopt = o;
X return;
X }
X /*
X * Don't do anything here.
X * All processing of STRING options is done by
X * the handling function.
X */
X str = s;
X s = optstring(s, c);
X break;
X case NUMBER:
X *(o->ovar) = getnum(&s, c);
X break;
X }
X /*
X * If the option has a handling function, call it.
X */
X if (o->ofunc != NULL)
X (*o->ofunc)(INIT, str);
X }
X}
X
X/*
X * Toggle command line flags from within the program.
X * Used by the "-" and "_" commands.
X * If do_toggle is zero, just report the current setting, without changing it.
X */
X public void
Xtoggle_option(c, s, do_toggle)
X int c;
X char *s;
X int do_toggle;
X{
X register struct option *o;
X int num;
X char message[100];
X
X /*
X * Look up the option letter in the option table.
X */
X o = findopt(c);
X if (o == NULL)
X {
X sprintf(message, "There is no %s flag", propt(c));
X error(message);
X return;
X }
X
X /*
X * Check for something which appears to be a do_toggle
X * (because the "-" command was used), but really is not.
X * This could be a string option with no string, or
X * a number option with no number.
X */
X switch (o->otype & OTYPE)
X {
X case STRING:
X if (*s == '\0')
X do_toggle = 0;
X break;
X case NUMBER:
X num = getnum(&s, '\0');
X if (num < 0)
X do_toggle = 0;
X break;
X }
X
X /*
X * Now actually toggle (change) the variable.
X */
X if (do_toggle)
X {
X if (o->otype & NO_TOGGLE)
X {
X sprintf(message, "Cannot change the %s flag", propt(c));
X error(message);
X return;
X }
X
X switch (o->otype & OTYPE)
X {
X case BOOL:
X /*
X * Boolean: just negate.
X */
X *(o->ovar) = ! *(o->ovar);
X break;
X case TRIPLE:
X /*
X * Triple:
X * If user gave the lower case letter, then switch
X * to 1 unless already 1, in which case make it 0.
X * If user gave the upper case letter, then switch
X * to 2 unless already 2, in which case make it 0.
X */
X if (o->oletter == c)
X *(o->ovar) = (*(o->ovar) == 1) ? 0 : 1;
X else
X *(o->ovar) = (*(o->ovar) == 2) ? 0 : 2;
X break;
X case STRING:
X /*
X * String: don't do anything here.
X * The handling function will do everything.
X */
X break;
X case NUMBER:
X /*
X * Number: set the variable to the given number.
X */
X *(o->ovar) = num;
X break;
X }
X }
X
X /*
X * Call the handling function for any special action
X * specific to this option.
X */
X if (o->ofunc != NULL)
X (*o->ofunc)(do_toggle ? TOGGLE : QUERY, s);
X
X /*
X * Print a message describing the new setting.
X */
X switch (o->otype & OTYPE)
X {
X case BOOL:
X case TRIPLE:
X /*
X * Print the odesc message.
X */
X error(o->odesc[*(o->ovar)]);
X break;
X case NUMBER:
X /*
X * The odesc message has a %d for the value of the variable.
X */
X sprintf(message, o->odesc[0], *(o->ovar));
X error(message);
X break;
X case STRING:
X /*
X * Message was already printed by the handling function.
X */
X break;
X }
X
X if (do_toggle && (o->otype & REPAINT))
X screen_trashed = 1;
X}
X
X/*
X * Return a string suitable for printing as the "name" of an option.
X * For example, if the option letter is 'x', just return "-x".
X */
X static char *
Xpropt(c)
X int c;
X{
X static char buf[4];
X
X if (control_char(c))
X sprintf(buf, "-^%c", carat_char(c));
X else
X sprintf(buf, "-%c", c);
X return (buf);
X}
X
X/*
X * Determine if an option is a single character option (BOOL or TRIPLE),
X * or if it a multi-character option (NUMBER).
X */
X public int
Xsingle_char_option(c)
X int c;
X{
X register struct option *o;
X
X o = findopt(c);
X if (o == NULL)
X return (1);
X return (o->otype & (BOOL|TRIPLE|NOVAR));
X}
X
X/*
X * Return the prompt to be used for a given option letter.
X * Only string valued options have prompts.
X */
X public char *
Xopt_prompt(c)
X int c;
X{
X register struct option *o;
X
X o = findopt(c);
X if (o == NULL || (o->otype & STRING) == 0)
X return (NULL);
X return (o->odesc[0]);
X}
X
X/*
X * Return whether or not there is a string option pending;
X * that is, if the previous option was a string-valued option letter
X * (like -P) without a following string.
X * In that case, the current option is taken to be the string for
X * the previous option.
X */
X public int
Xisoptpending()
X{
X return (pendopt != NULL);
X}
X
X/*
X * Print error message about missing string.
X */
X static void
Xnostring(c)
X int c;
X{
X char message[80];
X
X sprintf(message, "String is required after %s", propt(c));
X error(message);
X}
X
X/*
X * Print error message if a STRING type option is not followed by a string.
X */
X public void
Xnopendopt()
X{
X nostring(pendopt->oletter);
X}
X
X/*
X * Scan to end of string or to an END_OPTION_STRING character.
X * In the latter case, replace the char with a null char.
X * Return a pointer to the remainder of the string, if any.
X */
X static char *
Xoptstring(s, c)
X char *s;
X int c;
X{
X register char *p;
X
X if (*s == '\0')
X {
X nostring(c);
X exit(1);
X }
X for (p = s; *p != '\0'; p++)
X if (*p == END_OPTION_STRING)
X {
X *p = '\0';
X return (p+1);
X }
X return (p);
X}
X
X/*
X * Translate a string into a number.
X * Like atoi(), but takes a pointer to a char *, and updates
X * the char * to point after the translated number.
X */
X public int
Xgetnum(sp, c)
X char **sp;
X int c;
X{
X register char *s;
X register int n;
X char message[80];
X
X s = skipsp(*sp);
X if (*s < '0' || *s > '9')
X {
X if (c == '\0')
X return (-1);
X sprintf(message, "Number is required after %s", propt(c));
X error(message);
X exit(1);
X }
X
X n = 0;
X while (*s >= '0' && *s <= '9')
X n = 10 * n + *s++ - '0';
X *sp = s;
X return (n);
X}
END_OF_FILE
echo shar: Extracting \"optfunc.c\"
sed "s/^X//" >'optfunc.c' <<'END_OF_FILE'
X/*
X * Handling functions for command line options.
X *
X * Most options are handled by the generic code in option.c.
X * But all string options, and a few non-string options, require
X * special handling specific to the particular option.
X * This special processing is done by the "handling functions" in this file.
X *
X * Each handling function is passed a "type" and, if it is a string
X * option, the string which should be "assigned" to the option.
X * The type may be one of:
X * INIT The option is being initialized from the command line.
X * TOGGLE The option is being changed from within the program.
X * QUERY The setting of the option is merely being queried.
X */
X
X#include "less.h"
X#include "option.h"
X
Xextern int nbufs;
Xextern int ispipe;
Xextern int cbufs;
Xextern int pr_type;
Xextern int seven_bit;
Xextern int nohelp;
Xextern char *prproto[];
Xextern char *eqproto;
X#if LOGFILE
Xextern char *namelogfile;
Xextern int force_logfile;
Xextern int logfile;
X#endif
X#if TAGS
Xpublic int tagoption = 0;
Xextern char *tagfile;
Xextern char *tagpattern;
X#endif
X
X
X#if LOGFILE
X/*
X * Handler for -l option.
X */
X public void
Xopt_l(type, s)
X int type;
X char *s;
X{
X char *m;
X
X switch (type)
X {
X case INIT:
X namelogfile = s;
X break;
X case TOGGLE:
X if (!ispipe)
X {
X error("Input is not a pipe");
X return;
X }
X if (logfile >= 0)
X {
X error("Log file is already in use");
X return;
X }
X namelogfile = save(skipsp(s));
X use_logfile();
X sync_logfile();
X break;
X case QUERY:
X if (logfile < 0)
X error("No log file");
X else
X {
X static char lf[] = "log file \"";
X m = ecalloc(sizeof(lf) + strlen(namelogfile) + 2, sizeof(char));
X strcpy(m, lf);
X strcat(m, namelogfile);
X strcat(m, "\"");
X error(m);
X free(m);
X }
X break;
X }
X}
X
X/*
X * Handler for -L option.
X */
X public void
Xopt__L(type, s)
X int type;
X char *s;
X{
X force_logfile = 1;
X opt_l(type, s);
X}
X#endif
X
X#if USERFILE
X public void
Xopt_k(type, s)
X int type;
X char *s;
X{
X char *message;
X static char MSG[] = "Cannot use lesskey file: ";
X
X switch (type)
X {
X case INIT:
X if (add_cmdtable(s) == 0)
X return;
X message = (char *) ecalloc(strlen(s) + sizeof(MSG) + 1,
X sizeof(char));
X strcpy(message, MSG);
X strcat(message, s);
X error(message);
X free(message);
X break;
X case QUERY:
X case TOGGLE:
X error("Cannot query -k option");
X break;
X }
X}
X#endif
X
X#if TAGS
X/*
X * Handler for -t option.
X */
X public void
Xopt_t(type, s)
X int type;
X char *s;
X{
X switch (type)
X {
X case INIT:
X tagoption = 1;
X findtag(s);
X break;
X case TOGGLE:
X findtag(skipsp(s));
X if (tagfile != NULL)
X {
X edit(tagfile);
X (void) tagsearch();
X }
X break;
X case QUERY:
X error("Tag is required after -t");
X break;
X }
X}
X#endif
X
X/*
X * Handler for -P option.
X */
X public void
Xopt_P(type, s)
X int type;
X register char *s;
X{
X register char **proto;
X
X switch (type)
X {
X case INIT:
X case TOGGLE:
X /*
X * Figure out which prototype string should be changed.
X */
X switch (*s)
X {
X case 'm': proto = &prproto[PR_MEDIUM]; s++; break;
X case 'M': proto = &prproto[PR_LONG]; s++; break;
X case '=': proto = &eqproto; s++; break;
X default: proto = &prproto[pr_type]; break;
X }
X free(*proto);
X *proto = save(s);
X break;
X case QUERY:
X error(prproto[pr_type]);
X break;
X }
X}
X
X/*
X * Handler for the -b option.
X */
X public void
Xopt_b(type)
X int type;
X{
X switch (type)
X {
X case TOGGLE:
X /*
X * Allocate the new number of buffers.
X */
X ch_init(cbufs, 1);
X break;
X case QUERY:
X case INIT:
X break;
X }
X}
X
X/*
X * Handler for the -g option.
X */
X public void
Xopt_g(type)
X int type;
X{
X switch (type)
X {
X case TOGGLE:
X /*
X * Flush the buffers, since the buffered data may
X * be bad (if we are switching from 7 bits to 8 bits,
X * the eighth bit has been stripped from the buffered data).
X */
X if (ispipe)
X {
X error("Can't change -g on a pipe");
X seven_bit = !seven_bit;
X break;
X }
X ch_init(0, 0);
X break;
X case INIT:
X case QUERY:
X break;
X }
X}
X
X/*
X * "-?" means display a help message.
X * If from the command line, exit immediately.
X */
X public void
Xopt_query(type)
X int type;
X{
X if (nohelp)
X return;
X switch (type)
X {
X case QUERY:
X case TOGGLE:
X error("Use \"h\" for help");
X break;
X case INIT:
X raw_mode(1);
X init();
X help();
X quit();
X /*NOTREACHED*/
X }
X}
END_OF_FILE
echo shar: Extracting \"opttbl.c\"
sed "s/^X//" >'opttbl.c' <<'END_OF_FILE'
X/*
X * The option table.
X */
X
X#include "less.h"
X#include "option.h"
X
X#define toupper(c) ((c)-'a'+'A')
X
X/*
X * Variables controlled by command line options.
X */
Xpublic int seven_bit; /* Force seven-bit characters? */
Xpublic int quiet; /* Should we suppress the audible bell? */
Xpublic int how_search; /* Where should forward searches start? */
Xpublic int top_scroll; /* Repaint screen from top?
X (alternative is scroll from bottom) */
Xpublic int pr_type; /* Type of prompt (short, medium, long) */
Xpublic int bs_mode; /* How to process backspaces */
Xpublic int know_dumb; /* Don't complain about dumb terminals */
Xpublic int quit_at_eof; /* Quit after hitting end of file twice */
Xpublic int squeeze; /* Squeeze multiple blank lines into one */
Xpublic int tabstop; /* Tab settings */
Xpublic int back_scroll; /* Repaint screen on backwards movement */
Xpublic int twiddle; /* Display "~" for lines after EOF */
Xpublic int caseless; /* Do "caseless" searches */
Xpublic int linenums; /* Use line numbers */
Xpublic int cbufs; /* Current number of buffers */
Xpublic int autobuf; /* Automatically allocate buffers as needed */
Xpublic int nohelp; /* Disable the HELP command */
Xpublic int sendctl; /* Send control chars to screen untranslated */
Xpublic int force_open; /* Open the file even if not regular file */
Xpublic int swindow;
X
X/*
X * Table of all options and their semantics.
X */
Xstatic struct option option[] =
X{
X { 'a', TRIPLE, 0, &how_search, NULL,
X "Forward search starts at second REAL line displayed",
X "Forward search starts at bottom of screen",
X "Forward search starts at second SCREEN line displayed"
X },
X { 'b', NUMBER, 10, &cbufs, opt_b,
X "%d buffers",
X NULL, NULL
X },
X { 'B', BOOL, 1, &autobuf, NULL,
X "Don't automatically allocate buffers",
X "Automatically allocate buffers when needed",
X NULL
X },
X { 'c', TRIPLE, 0, &top_scroll, NULL,
X "Repaint by scrolling from bottom of screen",
X "Repaint by clearing each line",
X "Repaint by painting from top of screen"
X },
X { 'd', BOOL|NO_TOGGLE, 0, &know_dumb, NULL,
X "Assume intelligent terminal",
X "Assume dumb terminal",
X NULL
X },
X { 'e', TRIPLE, 0, &quit_at_eof, NULL,
X "Don't quit at end-of-file",
X "Quit at end-of-file",
X "Quit immediately at end-of-file"
X },
X { 'f', BOOL, 0, &force_open, NULL,
X "Open only regular files",
X "Open even non-regular files",
X NULL
X },
X { 'g', BOOL, 0, &seven_bit, opt_g,
X "Use eight bit characters",
X "Use seven bit characters",
X NULL
X },
X { 'h', NUMBER, -1, &back_scroll, NULL,
X "Backwards scroll limit is %d lines",
X NULL, NULL
X },
X { 'H', BOOL|NO_TOGGLE, 0, &nohelp, NULL,
X "Allow help command",
X "Don't allow help command",
X NULL
X },
X { 'i', BOOL, 0, &caseless, NULL,
X "Case is significant in searches",
X "Ignore case in searches",
X NULL
X },
X#if USERFILE
X { 'k', STRING|NO_TOGGLE, 0, NULL, opt_k,
X NULL, NULL, NULL
X },
X#endif
X#if LOGFILE
X { 'l', STRING, 0, NULL, opt_l,
X "log file: ", NULL, NULL
X },
X { 'L', STRING, 0, NULL, opt__L,
X "Log file: ", NULL, NULL
X },
X#endif
X { 'm', TRIPLE, 0, &pr_type, NULL,
X "Short prompt",
X "Medium prompt",
X "Long prompt"
X },
X { 'n', TRIPLE|REPAINT, 1, &linenums, NULL,
X "Don't use line numbers",
X "Use line numbers",
X "Constantly display line numbers"
X },
X { 'P', STRING, 0, NULL, opt_P,
X "prompt: ", NULL, NULL
X },
X { 'q', TRIPLE, 0, &quiet, NULL,
X "Ring the bell for errors AND at eof/bof",
X "Ring the bell for errors but not at eof/bof",
X "Never ring the bell"
X },
X { 'r', BOOL|REPAINT, 0, &sendctl, NULL,
X "Control characters are translated",
X "Control characters displayed directly",
X NULL
X },
X { 's', BOOL|REPAINT, 0, &squeeze, NULL,
X "Don't squeeze multiple blank lines",
X "Squeeze multiple blank lines",
X NULL
X },
X#if TAGS
X { 't', STRING, 0, NULL, opt_t,
X "tag: ", NULL, NULL
X },
X#endif
X { 'u', TRIPLE|REPAINT, 0, &bs_mode, NULL,
X "Underlined text displayed in underline mode",
X "Backspaces cause overstrike",
X "Backspaces print as ^H"
X },
X { 'w', BOOL|REPAINT, 1, &twiddle, NULL,
X "Display nothing for lines after end-of-file",
X "Display ~ for lines after end-of-file",
X NULL
X },
X { 'x', NUMBER|REPAINT, 8, &tabstop, NULL,
X "Tab stops every %d spaces",
X NULL, NULL
X
X },
X { 'z', NUMBER, -1, &swindow, NULL,
X "Scroll window size is %d lines",
X NULL, NULL
X },
X { '?', NOVAR, 0, NULL, opt_query,
X NULL, NULL, NULL
X },
X { '\0' }
X};
X
X
X/*
X * Initialize each option to its default value.
X */
X public void
Xinit_option()
X{
X register struct option *o;
X
X for (o = option; o->oletter != '\0'; o++)
X {
X /*
X * Set each variable to its default.
X */
X if (o->ovar != NULL)
X *(o->ovar) = o->odefault;
X }
X}
X
X/*
X * Find an option in the option table.
X */
X public struct option *
Xfindopt(c)
X int c;
X{
X register struct option *o;
X
X for (o = option; o->oletter != '\0'; o++)
X {
X if (o->oletter == c)
X return (o);
X if ((o->otype & TRIPLE) && toupper(o->oletter) == c)
X return (o);
X }
X return (NULL);
X}
END_OF_FILE
echo shar: Extracting \"os.c\"
sed "s/^X//" >'os.c' <<'END_OF_FILE'
X/*
X * Operating system dependent routines.
X *
X * Most of the stuff in here is based on Unix, but an attempt
X * has been made to make things work on other operating systems.
X * This will sometimes result in a loss of functionality, unless
X * someone rewrites code specifically for the new operating system.
X *
X * The makefile provides defines to decide whether various
X * Unix features are present.
X */
X
X#include <stdio.h>
X#include <signal.h>
X#include <setjmp.h>
X#include "less.h"
X
Xextern char *getenv();
X
Xpublic int reading;
X
Xextern int screen_trashed;
Xextern int force_open;
Xextern char *current_file;
Xextern char *previous_file;
X
Xstatic jmp_buf read_label;
X
X/*
X * Pass the specified command to a shell to be executed.
X * Like plain "system()", but handles resetting terminal modes, etc.
X */
X public void
Xlsystem(cmd)
X char *cmd;
X{
X register int inp;
X register char *shell;
X register char *p;
X
X /*
X * Print the command which is to be executed,
X * unless the command starts with a "-".
X */
X if (cmd[0] == '-')
X cmd++;
X else
X {
X lower_left();
X clear_eol();
X putstr("!");
X putstr(cmd);
X putstr("\n");
X }
X
X /*
X * De-initialize the terminal and take out of raw mode.
X */
X deinit();
X flush();
X raw_mode(0);
X
X /*
X * Restore signals to their defaults.
X */
X init_signals(0);
X
X /*
X * Force standard input to be the terminal, "/dev/tty",
X * even if less's standard input is coming from a pipe.
X */
X inp = dup(0);
X close(0);
X if (open("/dev/tty", 0) < 0)
X dup(inp);
X
X /*
X * Pass the command to the system to be executed.
X * If we have a SHELL environment variable, use
X * <$SHELL -c "command"> instead of just <command>.
X * If the command is empty, just invoke a shell.
X */
X p = NULL;
X if ((shell = getenv("SHELL")) != NULL && *shell != '\0')
X {
X if (*cmd == '\0')
X p = save(shell);
X else
X {
X p = ecalloc(strlen(shell) + strlen(cmd) + 7, sizeof(char));
X sprintf(p, "%s -c \"%s\"", shell, cmd);
X }
X }
X if (p == NULL)
X p = save("sh");
X
X system(p);
X free(p);
X
X /*
X * Restore standard input, reset signals, raw mode, etc.
X */
X close(0);
X dup(inp);
X close(inp);
X
X init_signals(1);
X raw_mode(1);
X init();
X screen_trashed = 1;
X#if defined(SIGWINCH) || defined(SIGWIND)
X /*
X * Since we were ignoring window change signals while we executed
X * the system command, we must assume the window changed.
X * Warning: this leaves a signal pending (in "sigs"),
X * so psignals() should be called soon after lsystem().
X */
X winch();
X#endif
X}
X
X/*
X * Like read() system call, but is deliberately interruptible.
X * A call to intread() from a signal handler will interrupt
X * any pending iread().
X */
X public int
Xiread(fd, buf, len)
X int fd;
X char *buf;
X int len;
X{
X register int n;
X
X if (setjmp(read_label))
X {
X /*
X * We jumped here from intread.
X */
X reading = 0;
X#if SIGSETMASK
X sigsetmask(0);
X#endif
X return (READ_INTR);
X }
X
X flush();
X reading = 1;
X n = read(fd, buf, len);
X reading = 0;
X if (n < 0)
X return (-1);
X return (n);
X}
X
X public void
Xintread()
X{
X longjmp(read_label, 1);
X}
X
X#if GET_TIME
X public long
Xget_time()
X{
X long t;
X
X time(&t);
X return (t);
X}
X#endif
X
X/*
X * Expand a string, substituting any "%" with the current filename,
X * and any "#" with the previous filename.
X */
X public char *
Xfexpand(s)
X char *s;
X{
X register char *fr, *to;
X register int n;
X register char *e;
X
X /*
X * Make one pass to see how big a buffer we
X * need to allocate for the expanded string.
X */
X n = 0;
X for (fr = s; *fr != '\0'; fr++)
X {
X if (*fr == '%')
X n += strlen(current_file);
X else if (*fr == '#')
X {
X if (previous_file == NULL)
X {
X error("No previous file");
X return (NULL);
X }
X n += strlen(previous_file);
X } else
X n++;
X }
X
X e = ecalloc(n+1, sizeof(char));
X
X /*
X * Now copy the string, expanding any "%" or "#".
X */
X to = e;
X for (fr = s; *fr != '\0'; fr++)
X {
X if (*fr == '%')
X {
X strcpy(to, current_file);
X to += strlen(to);
X } else if (*fr == '#')
X {
X strcpy(to, previous_file);
X to += strlen(to);
X } else
X *to++ = *fr;
X }
X *to = '\0';
X return (e);
X}
X
X static POSITION
Xseek_filesize(f)
X int f;
X{
X offset_t spos;
X
X spos = lseek(f, (offset_t)0, 2);
X if (spos == BAD_LSEEK)
X return (NULL_POSITION);
X return ((POSITION) spos);
X}
X
X/*
X * Expand a filename, substituting any environment variables, etc.
X * The implementation of this is necessarily very operating system
X * dependent. This implementation is unabashedly only for Unix systems.
X */
X#if GLOB
X
XFILE *popen();
X
X public char *
Xglob(filename)
X char *filename;
X{
X FILE *f;
X char *p;
X int ch;
X char *cmd;
X char *gfilename;
X
X filename = fexpand(filename);
X if (filename == NULL)
X return (NULL);
X
X /*
X * We get the shell to expand the filename for us by passing
X * an "echo" command to the shell and reading its output.
X */
X p = getenv("SHELL");
X if (p == NULL || *p == '\0')
X {
X /*
X * Read the output of <echo filename>.
X */
X cmd = ecalloc(strlen(filename)+6, sizeof(char));
X sprintf(cmd, "echo %s", filename);
X } else
X {
X /*
X * Read the output of <$SHELL -c "echo filename">.
X */
X cmd = ecalloc(strlen(p)+strlen(filename)+12, sizeof(char));
X sprintf(cmd, "%s -c \"echo %s\"", p, filename);
X }
X
X f = popen(cmd, "r");
X free(cmd);
X if (f == NULL)
X return (filename);
X free(filename);
X
X gfilename = ecalloc(FILENAME, sizeof(char));
X for (p = gfilename; p < &gfilename[FILENAME-1]; p++)
X {
X if ((ch = getc(f)) == '\n' || ch == EOF)
X break;
X *p = ch;
X }
X *p = '\0';
X pclose(f);
X return (gfilename);
X}
X
X#else
X
X public char *
Xglob(filename)
X char *filename;
X{
X return (fexpand(filename));
X}
X
X#endif
X
X
X/*
X * Returns NULL if the file can be opened and
X * is an ordinary file, otherwise an error message
X * (if it cannot be opened or is a directory, etc.)
X */
X
X#if STAT
X
X#include <sys/types.h>
X#include <sys/stat.h>
X
X public char *
Xbad_file(filename)
X char *filename;
X{
X register char *m;
X struct stat statbuf;
X
X if (stat(filename, &statbuf) < 0)
X return (errno_message(filename));
X
X if (force_open)
X return (NULL);
X
X if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
X {
X static char is_dir[] = " is a directory";
X m = ecalloc(strlen(filename) + sizeof(is_dir), sizeof(char));
X strcpy(m, filename);
X strcat(m, is_dir);
X return (m);
X }
X if ((statbuf.st_mode & S_IFMT) != S_IFREG)
X {
X static char not_reg[] = " is not a regular file";
X m = ecalloc(strlen(filename) + sizeof(not_reg), sizeof(char));
X strcpy(m, filename);
X strcat(m, not_reg);
X return (m);
X }
X return (NULL);
X}
X
X public POSITION
Xfilesize(f)
X int f;
X{
X struct stat statbuf;
X
X if (fstat(f, &statbuf) < 0)
X return (seek_filesize(f));
X return ((POSITION) statbuf.st_size);
X}
X
X#else
X
X public char *
Xbad_file(filename)
X char *filename;
X{
X return (NULL);
X}
X
X public POSITION
Xfilesize(f)
X int f;
X{
X return (seek_filesize(f));
X}
X
X#endif
X
X/*
X * errno_message: Return an error message based on the value of "errno".
X */
X
X#if PERROR
X
Xextern char *sys_errlist[];
Xextern int sys_nerr;
Xextern int errno;
X
X public char *
Xerrno_message(filename)
X char *filename;
X{
X register char *p;
X register char *m;
X char msg[16];
X
X if (errno < sys_nerr)
X p = sys_errlist[errno];
X else
X {
X sprintf(msg, "Error %d", errno);
X p = msg;
X }
X m = ecalloc(strlen(filename) + strlen(p) + 3, sizeof(char));
X sprintf(m, "%s: %s", filename, p);
X return (m);
X}
X
X#else
X
X public char *
Xerrno_message(filename)
X char *filename;
X{
X register char *m;
X static char msg[] = ": cannot open";
X
X m = ecalloc(strlen(filename) + sizeof(msg), sizeof(char));
X strcpy(m, filename);
X strcat(m, msg);
X return (m);
X}
X
X#endif
END_OF_FILE
echo shar: Extracting \"output.c\"
sed "s/^X//" >'output.c' <<'END_OF_FILE'
X/*
X * High level routines dealing with the output to the screen.
X */
X
X#include "less.h"
X
Xpublic int errmsgs; /* Count of messages displayed by error() */
Xstatic int need_clr;
X
Xextern int sigs;
Xextern int sc_width;
Xextern int so_width, se_width;
Xextern int screen_trashed;
Xextern int any_display;
Xextern char *first_cmd;
X
X
X/*
X * Display the line which is in the line buffer.
X */
X public void
Xput_line()
X{
X register int c;
X register int i;
X
X if (sigs)
X {
X /*
X * Don't output if a signal is pending.
X */
X screen_trashed = 1;
X return;
X }
X
X for (i = 0; (c = gline(i)) != '\0'; i++)
X {
X switch (c)
X {
X case UL_CHAR:
X ul_enter();
X break;
X case UE_CHAR:
X ul_exit();
X break;
X case BO_CHAR:
X bo_enter();
X break;
X case BE_CHAR:
X bo_exit();
X break;
X case '\b':
X putbs();
X break;
X default:
X if (c & CARATBIT)
X {
X /*
X * Control characters arrive here as the
X * normal character [carat_char(c)] with
X * the CARATBIT bit set. See pappend().
X */
X putchr('^');
X putchr(c &~ CARATBIT);
X } else
X {
X putchr(c);
X }
X }
X }
X}
X
X/*
X * Is a given character a "control" character?
X * {{ ASCII DEPENDENT }}
X */
X public int
Xcontrol_char(c)
X int c;
X{
X return (c < ' ' || c == '\177');
X}
X
X/*
X * Return the printable character used to identify a control character
X * (printed after a carat; e.g. '\3' => "^C").
X * {{ ASCII DEPENDENT }}
X */
X public int
Xcarat_char(c)
X int c;
X{
X return ((c == '\177') ? '?' : (c | 0100));
X}
X
X
Xstatic char obuf[1024];
Xstatic char *ob = obuf;
X
X/*
X * Flush buffered output.
X */
X public void
Xflush()
X{
X register int n;
X
X n = ob - obuf;
X if (n == 0)
X return;
X if (write(1, obuf, n) != n)
X screen_trashed = 1;
X ob = obuf;
X}
X
X/*
X * Discard buffered output.
X */
X public void
Xdropout()
X{
X ob = obuf;
X}
X
X/*
X * Output a character.
X */
X public void
Xputchr(c)
X int c;
X{
X if (ob >= &obuf[sizeof(obuf)])
X flush();
X if (need_clr)
X {
X need_clr = 0;
X lower_left();
X clear_eol();
X }
X *ob++ = c;
X}
X
X/*
X * Output a string.
X */
X public void
Xputstr(s)
X register char *s;
X{
X while (*s != '\0')
X putchr(*s++);
X}
X
X/*
X * Output a message in the lower left corner of the screen
X * and wait for carriage return.
X */
X
Xstatic char return_to_continue[] = " (press RETURN)";
X
X public void
Xerror(s)
X char *s;
X{
X register int c;
X static char buf[2] = { '\0', '\0' };
X
X errmsgs++;
X if (!any_display)
X {
X /*
X * Nothing has been displayed yet.
X * Output this message on error output (file
X * descriptor 2) and don't wait for a keystroke
X * to continue.
X *
X * This has the desirable effect of producing all
X * error messages on error output if standard output
X * is directed to a file. It also does the same if
X * we never produce any real output; for example, if
X * the input file(s) cannot be opened. If we do
X * eventually produce output, code in edit() makes
X * sure these messages can be seen before they are
X * overwritten or scrolled away.
X */
X write(2, s, strlen(s));
X write(2, "\n", 1);
X return;
X }
X
X lower_left();
X clear_eol();
X so_enter();
X putstr(s);
X putstr(return_to_continue);
X so_exit();
X
X#if ONLY_RETURN
X while ((c = getchr()) != '\n' && c != '\r')
X bell();
X#else
X c = getchr();
X if (c != '\n' && c != '\r' && c != ' ' && c != READ_INTR)
X {
X buf[0] = c;
X first_cmd = buf;
X }
X#endif
X lower_left();
X
X if (strlen(s) + sizeof(return_to_continue) +
X so_width + se_width + 1 > sc_width)
X /*
X * Printing the message has probably scrolled the screen.
X * {{ Unless the terminal doesn't have auto margins,
X * in which case we just hammered on the right margin. }}
X */
X screen_trashed = 1;
X
X flush();
X}
X
Xstatic char intr_to_abort[] = "... (interrupt to abort)";
X
X public void
Xierror(s)
X char *s;
X{
X
X lower_left();
X clear_eol();
X so_enter();
X putstr(s);
X putstr(intr_to_abort);
X so_exit();
X flush();
X need_clr = 1;
X}
END_OF_FILE
echo shar: Extracting \"position.c\"
sed "s/^X//" >'position.c' <<'END_OF_FILE'
X/*
X * Routines dealing with the "position" table.
X * This is a table which tells the position (in the input file) of the
X * first char on each currently displayed line.
X *
X * {{ The position table is scrolled by moving all the entries.
X * Would be better to have a circular table
X * and just change a couple of pointers. }}
X */
X
X#include "less.h"
X#include "position.h"
X
Xstatic POSITION *table = NULL; /* The position table */
Xstatic int table_size;
X
Xextern int sc_width, sc_height;
X
X/*
X * Return the starting file position of a line displayed on the screen.
X * The line may be specified as a line number relative to the top
X * of the screen, but is usually one of these special cases:
X * the top (first) line on the screen
X * the second line on the screen
X * the bottom line on the screen
X * the line after the bottom line on the screen
X */
X public POSITION
Xposition(where)
X int where;
X{
X switch (where)
X {
X case BOTTOM:
X where = sc_height - 2;
X break;
X case BOTTOM_PLUS_ONE:
X where = sc_height - 1;
X break;
X case MIDDLE:
X where = sc_height / 2;
X }
X return (table[where]);
X}
X
X/*
X * Add a new file position to the bottom of the position table.
X */
X public void
Xadd_forw_pos(pos)
X POSITION pos;
X{
X register int i;
X
X /*
X * Scroll the position table up.
X */
X for (i = 1; i < sc_height; i++)
X table[i-1] = table[i];
X table[sc_height - 1] = pos;
X}
X
X/*
X * Add a new file position to the top of the position table.
X */
X public void
Xadd_back_pos(pos)
X POSITION pos;
X{
X register int i;
X
X /*
X * Scroll the position table down.
X */
X for (i = sc_height - 1; i > 0; i--)
X table[i] = table[i-1];
X table[0] = pos;
X}
X
X/*
X * Initialize the position table, done whenever we clear the screen.
X */
X public void
Xpos_clear()
X{
X register int i;
X
X for (i = 0; i < sc_height; i++)
X table[i] = NULL_POSITION;
X}
X
X/*
X * Allocate the position table.
X */
X public void
Xpos_init()
X{
X if (sc_height <= table_size)
X return;
X if (table != NULL)
X free((char*)table);
X table = (POSITION *) ecalloc(sc_height, sizeof(POSITION));
X table_size = sc_height;
X}
X
X/*
X * See if the byte at a specified position is currently on the screen.
X * Check the position table to see if the position falls within its range.
X * Return the position table entry if found, -1 if not.
X */
X public int
Xonscreen(pos)
X POSITION pos;
X{
X register int i;
X
X if (pos < table[0])
X return (-1);
X for (i = 1; i < sc_height; i++)
X if (pos < table[i])
X return (i-1);
X return (-1);
X}
END_OF_FILE
echo shar: Extracting \"handle.c\"
sed "s/^X//" >'handle.c' <<'END_OF_FILE'
X/*
X * Stuff to manipulate file handles.
X * A file handle is a small integer that refers to a file name.
X * The point of all this is to avoid keeping big file names around
X * in many different places; instead we keep file HANDLES around
X * and all the names are stored away here in this module.
X * There are routines here to convert a handle to a name and vice versa.
X *
X * Also done in this module is keeping track of a file position
X * for every filename. This is used to restore the last position
X * when we re-examine a previously examined file.
X */
X
X#include "less.h"
X
Xstruct fhandle {
X struct fhandle *h_next;
X HANDLE h_handle;
X char *h_filename;
X POSITION h_pos;
X};
X
Xstatic HANDLE xhandle = (HANDLE)0;
Xstatic struct fhandle *anchor = NULL;
X
X/*
X * Allocate a new handle structure and stick a filename in it.
X */
X static struct fhandle *
Xnew_handle(filename)
X char *filename;
X{
X register struct fhandle *p;
X
X p = (struct fhandle *) ecalloc(1, sizeof(struct fhandle));
X p->h_handle = ++xhandle;
X p->h_filename = filename;
X p->h_next = anchor;
X p->h_pos = (POSITION)0;
X anchor = p;
X return (p);
X}
X
X/*
X * Find a handle structure, given a handle.
X */
X static struct fhandle *
Xh_to_fh(handle)
X HANDLE handle;
X{
X register struct fhandle *p;
X
X for (p = anchor; p != NULL; p = p->h_next)
X if (handle == p->h_handle)
X return (p);
X
X error("ERROR: h_to_fh");
X quit();
X /*NOTREACHED*/
X}
X
X/*
X * Find a handle structure, given a filename.
X */
X static struct fhandle *
Xfn_to_fh(filename)
X char *filename;
X{
X register struct fhandle *p;
X
X for (p = anchor; p != NULL; p = p->h_next)
X if (strcmp(filename, p->h_filename) == 0)
X return (p);
X return (NULL);
X}
X
X/*
X * Get the handle associated with a filename.
X */
X public HANDLE
Xget_handle(filename)
X char *filename;
X{
X register struct fhandle *p;
X
X if ((p = fn_to_fh(filename)) == NULL)
X p = new_handle(save(filename));
X return (p->h_handle);
X}
X
X/*
X * Stash away a filename.
X * The filename must be known to be STATIC;
X * this function, unlike get_handle(),
X * does not make a copy of the filename.
X */
X public void
Xsave_handle(filename)
X char *filename;
X{
X if (fn_to_fh(filename) == NULL)
X (void) new_handle(filename);
X}
X
X/*
X * Get the filename associated with a handle.
X */
X public char *
Xget_filename(handle)
X HANDLE handle;
X{
X register struct fhandle *p;
X
X p = h_to_fh(handle);
X return (p->h_filename);
X}
X
X/*
X * Save the current position in a given file.
X */
X public void
Xstore_pos(handle, pos)
X HANDLE handle;
X POSITION pos;
X{
X register struct fhandle *p;
X
X p = h_to_fh(handle);
X p->h_pos = pos;
X}
X
X/*
X * Recall the current position in a file which may have been seen before.
X * If it hasn't been seen before, return NULL_POSITION.
X */
X public POSITION
Xrecall_pos(handle)
X HANDLE handle;
X{
X register struct fhandle *p;
X
X p = h_to_fh(handle);
X return (p->h_pos);
X}
END_OF_FILE
More information about the Alt.sources
mailing list