Path: utzoo!utgpu!jarvis.csri.toronto.edu!mailrus!ames!amdahl!amdcad!pyramid!ctnews!unix386!mark From: mark@unix386.Convergent.COM (Mark Nudelman) Newsgroups: alt.sources Subject: less beta release (part 4 of 5) Message-ID: <532@unix386.Convergent.COM> Date: 14 Sep 89 22:24:53 GMT Distribution: usa Organization: Convergent Technologies, San Jose, CA Lines: 2813 #! /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 "-" X * instead of -z 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 X#include X#include 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 . 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 . 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 X#include 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