summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--usr.bin/more/Makefile30
-rw-r--r--usr.bin/more/command.c861
-rw-r--r--usr.bin/more/decode.c223
-rw-r--r--usr.bin/more/default.morerc134
-rw-r--r--usr.bin/more/input.c10
-rw-r--r--usr.bin/more/less.h110
-rw-r--r--usr.bin/more/less.morerc61
-rw-r--r--usr.bin/more/line.c13
-rw-r--r--usr.bin/more/macro.c292
-rw-r--r--usr.bin/more/main.c168
-rw-r--r--usr.bin/more/more.110
-rw-r--r--usr.bin/more/more.help6
-rw-r--r--usr.bin/more/most.morerc86
-rw-r--r--usr.bin/more/ncommand.c1452
-rw-r--r--usr.bin/more/option.c10
-rw-r--r--usr.bin/more/output.c88
-rw-r--r--usr.bin/more/pathnames.h6
-rw-r--r--usr.bin/more/prim.c26
-rw-r--r--usr.bin/more/screen.c3
-rw-r--r--usr.bin/more/signal.c4
-rw-r--r--usr.bin/more/ttyin.c2
21 files changed, 2777 insertions, 818 deletions
diff --git a/usr.bin/more/Makefile b/usr.bin/more/Makefile
index 7904843..a9f7d54 100644
--- a/usr.bin/more/Makefile
+++ b/usr.bin/more/Makefile
@@ -1,16 +1,34 @@
# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
# $FreeBSD$
+#
PROG= more
-CFLAGS+=-I${.CURDIR} -DTERMIOS
-SRCS= ch.c command.c decode.c help.c input.c line.c linenum.c main.c \
- option.c os.c output.c position.c prim.c screen.c signal.c tags.c \
- ttyin.c
+CFLAGS+=-I${.CURDIR} -I${.OBJDIR} -DTERMIOS
+SRCS= ch.c command.c defrc.h help.c input.c line.c linenum.c macro.c main.c \
+ ncommand.c option.c os.c output.c position.c prim.c screen.c signal.c \
+ tags.c ttyin.c
DPADD= ${LIBTERMCAP}
LDADD= -ltermcap
+CLEANFILES+= defrc.h
+
+EXAMPDIR= /usr/share/examples/more
+EXAMPLES= default.morerc less.morerc most.morerc
+
+defrc.h: default.morerc
+ @${ECHO} '/* ${.TARGET:T} auto-generated from ${.ALLSRC:T} */' \
+ > ${.TARGET}
+ @${ECHO} '#define DEFRC "\' >> ${.TARGET}
+ sed -e 's/\\/\\\\/g' -e 's/\"/\\\"/g' -e 's/$$/\\n\\/' \
+ < ${.ALLSRC} >> ${.TARGET}
+ @${ECHO} \" >> ${.TARGET}
+
beforeinstall:
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/more.help \
- ${DESTDIR}/usr/share/misc
+ ${INSTALL} ${COPY} -o ${SHAREOWN} -g ${SHAREGRP} -m ${SHAREMODE} \
+ ${.CURDIR}/more.help ${DESTDIR}/usr/share/misc
+.for xzamp in ${EXAMPLES}
+ ${INSTALL} ${COPY} -o ${SHAREOWN} -g ${SHAREGRP} -m ${SHAREMODE} \
+ ${.CURDIR}/${xzamp} ${DESTDIR}${EXAMPDIR}/${xzamp}
+.endfor
.include <bsd.prog.mk>
diff --git a/usr.bin/more/command.c b/usr.bin/more/command.c
index 0a400d8..92b860a 100644
--- a/usr.bin/more/command.c
+++ b/usr.bin/more/command.c
@@ -1,5 +1,6 @@
/*
* Copyright (c) 1988 Mark Nudleman
+ * Portions copyright (c) 1999 T. Michael Vanderhoek
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
*
@@ -41,114 +42,145 @@ static const char rcsid[] =
"$FreeBSD$";
#endif /* not lint */
+/*
+ * Functions for interacting with the user directly printing hello
+ * messages or reading from the terminal. All of these functions deal
+ * specifically with the prompt line, and only the prompt line.
+ */
+
#include <sys/param.h>
+#include <assert.h>
#include <ctype.h>
+#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "less.h"
#include "pathnames.h"
-#define NO_MCA 0
-#define MCA_DONE 1
-#define MCA_MORE 2
-
extern int erase_char, kill_char, werase_char;
-extern int ispipe;
extern int sigs;
extern int quit_at_eof;
extern int hit_eof;
-extern int sc_width;
-extern int sc_height;
-extern int sc_window;
extern int horiz_off;
+extern int sc_width;
+extern int bo_width;
+extern int be_width;
+extern int so_width;
+extern int se_width;
extern int curr_ac;
extern int ac;
extern char **av;
-extern int scroll;
extern int screen_trashed; /* The screen has been overwritten */
-static char cmdbuf[120]; /* Buffer for holding a multi-char command */
-static char *cp; /* Pointer into cmdbuf */
-static int cmd_col; /* Current column of the multi-char command */
-static int longprompt; /* if stat command instead of prompt */
-static int mca; /* The multicharacter command (action) */
-static int last_mca; /* The previous mca */
-static int number; /* The number typed by the user */
-static int wsearch; /* Search for matches (1) or non-matches (0) */
+static int cmd_col; /* Current screen column when accepting input */
-#define CMD_RESET cp = cmdbuf /* reset command buffer to empty */
-#define CMD_EXEC lower_left(); flush()
+static cmd_char(), cmd_erase(), getcc();
-/* backspace in command buffer. */
-static
-cmd_erase()
-{
- int c;
- /*
- * backspace past beginning of the string: this usually means
- * abort the command.
- */
- if (cp == cmdbuf)
- return(1);
- /* erase an extra character, for the carat. */
- c = *--cp;
- if (CONTROL_CHAR(c)) {
- backspace();
- --cmd_col;
- }
+/*****************************************************************************
+ *
+ * Functions for reading-in user input.
+ *
+ */
- backspace();
- --cmd_col;
- return(0);
-}
+static int biggetinputhack_f;
-/* set up the display to start a new multi-character command. */
-start_mca(action, prompt)
- int action;
- char *prompt;
+/* biggetinputhack()
+ *
+ * Performs as advertised.
+ */
+biggetinputhack()
{
- lower_left();
- clear_eol();
- putstr(prompt);
- cmd_col = strlen(prompt);
- mca = action;
+ biggetinputhack_f = 1;
}
/*
- * process a single character of a multi-character command, such as
- * a number, or the pattern of a search command.
+ * Read a line of input from the terminal. Reads at most bufsiz - 1 characters
+ * and places them in buffer buf. They are NUL-terminated. Prints the
+ * temporary prompt prompt.
*/
-static
-cmd_char(c)
+getinput(prompt, buf, bufsiz)
+ const char *prompt;
+ char *buf;
+ int bufsiz;
+{
+ extern bo_width, be_width;
+ char *bufcur;
int c;
+
+ prmpt(prompt);
+
+ bufcur = buf;
+ for (;;) {
+ c = getcc();
+ if (c == '\n') {
+ *bufcur = '\0';
+ return;
+ }
+ if (c == READ_INTR ||
+ cmd_char(c, buf, &bufcur, buf + bufsiz - 1)) {
+ /* input cancelled */
+ if (bufsiz) *buf = '\0';
+ return;
+ }
+ if (biggetinputhack_f) {
+ biggetinputhack_f = 0;
+ *bufcur = '\0';
+ return;
+ }
+ }
+}
+
+/*
+ * Process a single character of a multi-character input, such as
+ * a number, or the pattern of a search command. Returns true if the user
+ * has cancelled the multi-character input, false otherwise and attempts
+ * to add it to buf (not exceeding bufsize). Prints the character on the
+ * terminal output. The bufcur should initially equal bufbeg. After that
+ * it does not need to be touched or modified by the user, but may be expected
+ * to point at the future position of the next character.
+ */
+static int
+cmd_char(c, bufbeg, bufcur, bufend)
+ int c; /* The character to process */
+ char *bufbeg; /* The buffer to add the character to */
+ char **bufcur; /* The position at which to add the character */
+ char *bufend; /* The last spot available in the buffer --- remember
+ * to leave one after bufend for the '\0'! (You must
+ * add the '\0' yourself!!) */
{
if (c == erase_char)
- return(cmd_erase());
+ return(cmd_erase(bufbeg, bufcur));
/* in this order, in case werase == erase_char */
if (c == werase_char) {
- if (cp > cmdbuf) {
- while (isspace(cp[-1]) && !cmd_erase());
- while (!isspace(cp[-1]) && !cmd_erase());
- while (isspace(cp[-1]) && !cmd_erase());
+ if (*bufcur > bufbeg) {
+ while (isspace((*bufcur)[-1]) &&
+ !cmd_erase(bufbeg, bufcur)) ;
+ while (!isspace((*bufcur)[-1]) &&
+ !cmd_erase(bufbeg, bufcur)) ;
+ while (isspace((*bufcur)[-1]) &&
+ !cmd_erase(bufbeg, bufcur)) ;
}
- return(cp == cmdbuf);
+ return *bufcur == bufbeg;
}
if (c == kill_char) {
- while (!cmd_erase());
- return(1);
+ while (!cmd_erase(bufbeg, bufcur));
+ return 1;
}
+
/*
* No room in the command buffer, or no room on the screen;
- * {{ Could get fancy here; maybe shift the displayed line
- * and make room for more chars, like ksh. }}
+ * XXX If there is no room on the screen, we should just let the
+ * screen scroll down and set screen_trashed=1 appropriately, or
+ * alternatively, scroll the prompt line horizontally.
*/
- if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3)
+ assert (*bufcur <= bufend);
+ if (*bufcur == bufend || cmd_col >= sc_width - 3)
bell();
else {
- *cp++ = c;
+ *(*bufcur)++ = c;
if (CONTROL_CHAR(c)) {
putchr('^');
cmd_col++;
@@ -158,12 +190,123 @@ cmd_char(c)
putchr(c);
cmd_col++;
}
- return(0);
+ return 0;
}
+/*
+ * Helper function to cmd_char(). Backs-up one character from bufcur in the
+ * buffer passed, and prints a backspace on the screen. Returns true if the
+ * we backspaced past bufbegin (ie. the input is being aborted), and false
+ * otherwise. The bufcur is expected to point to the future location of the
+ * next character in the buffer, and is modified appropriately.
+ */
+static
+cmd_erase(bufbegin, bufcur)
+ char *bufbegin;
+ char **bufcur;
+{
+ int c;
+
+ /*
+ * XXX Could add code to detect a backspace that is backing us over
+ * the beginning of a line and onto the previous line. The backspace
+ * would not be printed for some terminals (eg. hardcopy) in that
+ * case.
+ */
+
+ /*
+ * backspace past beginning of the string: this usually means
+ * abort the input.
+ */
+ if (*bufcur == bufbegin)
+ return 1;
+
+ (*bufcur)--;
+
+ /* If erasing a control-char, erase an extra character for the carat. */
+ c = **bufcur;
+ if (CONTROL_CHAR(c)) {
+ backspace();
+ cmd_col--;
+ }
+
+ backspace();
+ cmd_col--;
+
+ return 0;
+}
+
+static int ungotcc;
+
+/*
+ * Get command character from the terminal.
+ */
+static
+getcc()
+{
+ int ch;
+ off_t position();
+
+ /* left over from error() routine. */
+ if (ungotcc) {
+ ch = ungotcc;
+ ungotcc = 0;
+ return(ch);
+ }
+
+ return(getchr());
+}
+
+/*
+ * Same as ungetc(), but works for people who don't like to use streams.
+ */
+ungetcc(c)
+ int c;
+{
+ ungotcc = c;
+}
+
+
+/*****************************************************************************
+ *
+ * prompts
+ *
+ */
+
+static int longprompt;
+
+/*
+ * Prints prmpt where the prompt would normally appear. This is different
+ * from changing the current prompt --- this is more like printing a
+ * unimportant notice or error. The prmpt line will be printed in bold (if
+ * possible). Will in the future print only the last sc_width - 1 - bo_width
+ * characters (to prevent newline).
+ */
+prmpt(prmpt)
+ const char *prmpt;
+{
+ lower_left();
+ clear_eol();
+ bo_enter();
+ putxstr(prmpt);
+ bo_exit();
+ flush();
+ cmd_col = strlen(prmpt) + bo_width + be_width;
+}
+
+/*
+ * Print the main prompt that signals we are ready for user commands. This
+ * also magically positions the current file where it should be (either by
+ * calling repaint() if screen_trashed or by searching for a search
+ * string that was specified through option.c on the more(1) command line).
+ * Additional magic will randomly call the quit() function.
+ *
+ * This is really intended to do a lot of the work of commands(). It has
+ * little purpose outside of commands().
+ */
prompt()
{
- extern int linenums, short_file;
+ extern int linenums, short_file, ispipe;
extern char *current_name, *firstsearch, *next_name;
off_t len, pos, ch_length(), position(), forw_line();
char pbuf[40];
@@ -173,8 +316,13 @@ prompt()
* if search string provided, go there instead.
*/
if (position(TOP) == NULL_POSITION) {
+#if 0
+/* This code causes "more zero-byte-file /etc/termcap" to skip straight
+ * to the /etc/termcap file ... that is undesireable. There are only a few
+ * instances where these two lines perform something useful. */
if (forw_line((off_t)0) == NULL_POSITION)
- return(0);
+ return 0 ;
+#endif
if (!firstsearch || !search(1, firstsearch, 1, 1))
jump_back(1);
}
@@ -188,38 +336,42 @@ prompt()
/* select the proper prompt and display it. */
lower_left();
clear_eol();
+ pbuf[sizeof(pbuf) - 1] = '\0';
if (longprompt) {
/*
* Get the current line/pos from the BOTTOM of the screen
* even though that's potentially confusing for the user
- * when switching between NO_HORIZ_OFF and a valid horiz_off.
- * In exchange, it is sometimes easier for the user to tell
- * when a file is relatively short vs. long.
+ * when switching between wraplines=true and a valid horiz_off
+ * (with wraplines=false). In exchange, it is sometimes
+ * easier for the user to tell when a file is relatively
+ * short vs. long.
*/
so_enter();
putstr(current_name);
putstr(":");
if (!ispipe) {
- (void)snprintf(pbuf, sizeof(pbuf),
+ (void)snprintf(pbuf, sizeof(pbuf) - 1,
" file %d/%d", curr_ac + 1, ac);
putstr(pbuf);
}
if (linenums) {
- (void)snprintf(pbuf, sizeof(pbuf),
+ (void)snprintf(pbuf, sizeof(pbuf) - 1,
" line %d", currline(BOTTOM));
putstr(pbuf);
}
+ (void)snprintf(pbuf, sizeof(pbuf) - 1, " col %d", horiz_off);
+ putstr(pbuf);
if ((pos = position(BOTTOM)) != NULL_POSITION) {
- (void)snprintf(pbuf, sizeof(pbuf), " byte %qd", pos);
+ (void)snprintf(pbuf, sizeof(pbuf) - 1,
+ " byte %qd", pos);
putstr(pbuf);
if (!ispipe && (len = ch_length())) {
- (void)snprintf(pbuf, sizeof(pbuf),
+ (void)snprintf(pbuf, sizeof(pbuf) - 1,
"/%qd pct %qd%%", len, ((100 * pos) / len));
putstr(pbuf);
}
}
so_exit();
- longprompt = 0;
}
else {
so_enter();
@@ -235,144 +387,173 @@ prompt()
else if (!ispipe &&
(pos = position(BOTTOM)) != NULL_POSITION &&
(len = ch_length())) {
- (void)snprintf(pbuf, sizeof(pbuf),
+ (void)snprintf(pbuf, sizeof(pbuf) - 1,
" (%qd%%)", ((100 * pos) / len));
putstr(pbuf);
}
so_exit();
}
- return(1);
+ return 1;
}
-/* get command character. */
-static
-getcc()
+/*
+ * Sets the current prompt. Currently it sets the current prompt to the
+ * long prompt.
+ */
+statprompt(nostatprompt)
+ int nostatprompt; /* Turn off the stat prompt? (off by default...) */
{
- extern int cmdstack;
- int ch;
- off_t position();
+ if (nostatprompt)
+ longprompt = 0;
+ else
+ longprompt = 1;
+}
- /* left over from error() routine. */
- if (cmdstack) {
- ch = cmdstack;
- cmdstack = NULL;
- return(ch);
- }
- if (cp > cmdbuf && position(TOP) == NULL_POSITION) {
- /*
- * Command is incomplete, so try to complete it.
- * There are only two cases:
- * 1. We have "/string" but no newline. Add the \n.
- * 2. We have a number but no command. Treat as #g.
- * (This is all pretty hokey.)
- */
- if (mca != A_DIGIT)
- /* Not a number; must be search string */
- return('\n');
- else
- /* A number; append a 'g' */
- return('g');
- }
- return(getchr());
+
+/*****************************************************************************
+ *
+ * Errors, next-of-kin to prompts.
+ *
+ */
+
+/*
+ * Shortcut function that may be used when setting the current erreur
+ * and erreur string at the same time. The function name is chosen to be
+ * symetric with the SETERR() macro in less.h. This could be written as
+ * macro, too, but we'd need to use a GNU C extension.
+ */
+SETERRSTR(enum error e, const char *s, ...)
+{
+ va_list args;
+
+ erreur = e;
+ if (errstr) free(errstr);
+ errstr = NULL;
+ va_start(args, s);
+ vasprintf(&errstr, s, args);
+ va_end(args);
}
-/* execute a multicharacter command. */
-static
-exec_mca()
+/*
+ * Prints an error message and clears the current error.
+ */
+handle_error()
{
- extern int file;
- extern char *tagfile;
- register char *p;
- char *glob();
-
- *cp = '\0';
- CMD_EXEC;
- switch (mca) {
- case A_F_SEARCH:
- (void)search(1, cmdbuf, number, wsearch);
- break;
- case A_B_SEARCH:
- (void)search(0, cmdbuf, number, wsearch);
- break;
- case A_EXAMINE:
- for (p = cmdbuf; isspace(*p); ++p);
- (void)edit(glob(p));
- break;
- case A_TAGFILE:
- for (p = cmdbuf; isspace(*p); ++p);
- findtag(p);
- if (tagfile == NULL)
- break;
- if (edit(tagfile))
- (void)tagsearch();
- break;
- }
+ if (erreur == E_OK)
+ return;
+
+ bell();
+ if (errstr)
+ error(errstr);
+ else
+ error(deferr[erreur]);
+ erreur = E_OK;
+ errstr = NULL;
}
-/* add a character to a multi-character command. */
-static
-mca_char(c)
- int c;
+/*
+ * Clears any error messages and pretends they never occurred.
+ */
+clear_error()
+{
+ erreur = E_OK;
+ if (errstr) free(errstr);
+ errstr = NULL;
+}
+
+int errmsgs;
+static char return_to_continue[] = "(press RETURN)";
+
+/*
+ * Output a message in the lower left corner of the screen
+ * and wait for carriage return.
+ */
+/* static */
+error(s)
+ char *s;
{
- switch (mca) {
- case 0: /* not in a multicharacter command. */
- case A_PREFIX: /* in the prefix of a command. */
- return(NO_MCA);
- case A_DIGIT:
+ extern int any_display;
+ int ch;
+
+ errmsgs++;
+ if (!any_display) {
/*
- * Entering digits of a number.
- * Terminated by a non-digit.
+ * Nothing has been displayed yet. Output this message on
+ * error output (file descriptor 2) and don't wait for a
+ * keystroke to continue.
+ *
+ * This has the desirable effect of producing all error
+ * messages on error output if standard output is directed
+ * to a file. It also does the same if we never produce
+ * any real output; for example, if the input file(s) cannot
+ * be opened. If we do eventually produce output, code in
+ * edit() makes sure these messages can be seen before they
+ * are overwritten or scrolled away.
*/
- if (!isascii(c) || !isdigit(c) &&
- c != erase_char && c != kill_char && c != werase_char) {
- /*
- * Not part of the number.
- * Treat as a normal command character.
- */
- *cp = '\0';
- number = atoi(cmdbuf);
- CMD_RESET;
- mca = 0;
- return(NO_MCA);
- }
- break;
+ (void)write(2, s, strlen(s));
+ (void)write(2, "\n", 1);
+ return;
}
- /*
- * Any other multicharacter command
- * is terminated by a newline.
- */
- if (c == '\n' || c == '\r') {
- exec_mca();
- return(MCA_DONE);
+ lower_left();
+ clear_eol();
+ so_enter();
+ if (s) {
+ putstr(s);
+ putstr(" ");
}
+ putstr(return_to_continue);
+ so_exit();
- /* append the char to the command buffer. */
- if (cmd_char(c))
- return(MCA_DONE);
+ if ((ch = getchr()) != '\n') {
+ /* XXX hardcoded */
+ if (ch == 'q')
+ quit();
+ ungotcc = ch;
+ }
+ lower_left();
- return(MCA_MORE);
+ if ((s==NULL)?0:(strlen(s)) + sizeof(return_to_continue) +
+ so_width + se_width + 1 > sc_width) {
+ /*
+ * Printing the message has probably scrolled the screen.
+ * {{ Unless the terminal doesn't have auto margins,
+ * in which case we just hammered on the right margin. }}
+ */
+ /* XXX Should probably just set screen_trashed=1, but I'm
+ * not going to touch that until all the places that call
+ * error() have been checked, or until error() is staticized. */
+ repaint();
+ }
+ flush();
}
+
+/****************************************************************************
+ *
+ * The main command processor.
+ *
+ * (Well, it deals with things on the prompt line, doesn't it?)
+ *
+ */
+
/*
* Main command processor.
+ *
* Accept and execute commands until a quit command, then return.
*/
commands()
{
- register int c;
- register int action;
- static int default_hscroll = 1;
- static int saved_horiz_off = NO_HORIZ_OFF;
- extern char *tagfile;
-
- last_mca = 0;
- scroll = (sc_height + 1) / 2;
+ enum runmacro runmacro();
+ enum runmacro rmret;
+ long numberN;
+ enum { NOTGOTTEN=0, GOTTEN=1, GETTING } Nstate; /* ie. numberNstate */
+ int c;
+ char inbuf[20], *incur = inbuf;
+ *inbuf = '\0';
+ Nstate = GETTING;
for (;;) {
- mca = 0;
- number = 0;
-
/*
* See if any signals need processing.
*/
@@ -380,287 +561,77 @@ commands()
psignals();
/*
- * Display prompt and accept a character.
+ * Display prompt and generally get setup. Don't display the
+ * prompt if we are already in the middle of accepting a
+ * set of characters.
*/
- CMD_RESET;
- if (!prompt()) {
+ if (!*inbuf && !prompt()) {
next_file(1);
continue;
}
- noprefix();
+
c = getcc();
-again: if (sigs)
- continue;
+ /* Check sigs here --- getcc() may have given us READ_INTR */
+ if (sigs) {
+ /* terminate any current macro */
+ *inbuf = '\0';
+ incur = inbuf;
- /*
- * If we are in a multicharacter command, call mca_char.
- * Otherwise we call cmd_decode to determine the
- * action to be performed.
- */
- if (mca)
- switch (mca_char(c)) {
- case MCA_MORE:
- /*
- * Need another character.
- */
- c = getcc();
- goto again;
- case MCA_DONE:
- /*
- * Command has been handled by mca_char.
- * Start clean with a prompt.
- */
- continue;
- case NO_MCA:
- /*
- * Not a multi-char command
- * (at least, not anymore).
- */
- break;
- }
+ continue; /* process the sigs */
+ }
+
+ if (Nstate == GETTING && !isdigit(c)) {
+ /* mark the end of an input number N, if any */
- /* decode the command character and decide what to do. */
- switch (action = cmd_decode(c)) {
- case A_DIGIT: /* first digit of a number */
- start_mca(A_DIGIT, ":");
- goto again;
- case A_F_SCREEN: /* forward one screen */
- CMD_EXEC;
- if (number <= 0 && (number = sc_window) <= 0)
- number = sc_height - 1;
- forward(number, 1);
- break;
- case A_B_SCREEN: /* backward one screen */
- CMD_EXEC;
- if (number <= 0 && (number = sc_window) <= 0)
- number = sc_height - 1;
- backward(number, 1);
- break;
- case A_F_LINE: /* forward N (default 1) line */
- CMD_EXEC;
- forward(number <= 0 ? 1 : number, 0);
- break;
- case A_B_LINE: /* backward N (default 1) line */
- CMD_EXEC;
- backward(number <= 0 ? 1 : number, 0);
- break;
- case A_R_COL: /* to the right N (default 1) cols */
- /* XXX Should beep here rather than silently truncating
- * lines in line.c when we are about to exceed the
- * line buffer. */
- if (number > 0)
- default_hscroll = number;
- horiz_off += default_hscroll;
- repaint();
- break;
- case A_L_COL: /* to the left N (default 1) cols */
- if (number > 0)
- default_hscroll = number;
- if (horiz_off != 0 && horiz_off != NO_HORIZ_OFF) {
- horiz_off -= default_hscroll;
- if (horiz_off < 0)
- horiz_off = 0;
- } else
- horiz_off = NO_HORIZ_OFF;
- repaint();
- break;
- case A_HOME:
- if (horiz_off != NO_HORIZ_OFF) {
- saved_horiz_off = horiz_off;
- horiz_off = NO_HORIZ_OFF;
- } else
- horiz_off = saved_horiz_off;
- repaint();
- break;
- case A_F_SCROLL: /* forward N lines */
- CMD_EXEC;
- if (number > 0)
- scroll = number;
- forward(scroll, 0);
- break;
- case A_B_SCROLL: /* backward N lines */
- CMD_EXEC;
- if (number > 0)
- scroll = number;
- backward(scroll, 0);
- break;
- case A_FREPAINT: /* flush buffers and repaint */
- if (!ispipe) {
- ch_init(0, 0);
- clr_linenum();
+ if (!*inbuf) {
+ /* We never actually got an input number */
+ Nstate = NOTGOTTEN;
+ } else {
+ numberN = atol(inbuf);
+ Nstate = GOTTEN;
}
- /* FALLTHROUGH */
- case A_REPAINT: /* repaint the screen */
- CMD_EXEC;
- repaint();
- break;
- case A_GOLINE: /* go to line N, default 1 */
- CMD_EXEC;
- if (number <= 0)
- number = 1;
- jump_back(number);
- break;
- case A_PERCENT: /* go to percent of file */
- CMD_EXEC;
- if (number < 0)
- number = 0;
- else if (number > 100)
- number = 100;
- jump_percent(number);
- break;
- case A_GOEND: /* go to line N, default end */
- CMD_EXEC;
- if (number <= 0)
- jump_forw();
- else
- jump_back(number);
- break;
- case A_STAT: /* print file name, etc. */
- longprompt = 1;
+ *inbuf = '\0';
+ incur = inbuf;
+ }
+ cmd_char(c, inbuf, &incur, inbuf + sizeof(inbuf) - 1);
+ *incur = '\0';
+ if (*inbuf) prmpt(inbuf);
+
+ if (Nstate == GETTING) {
+ /* Still reading in the number N ... don't want to
+ * try running the macro expander. */
continue;
- case A_QUIT: /* exit */
- quit();
- case A_F_SEARCH: /* search for a pattern */
- case A_B_SEARCH:
- if (number <= 0)
- number = 1;
- start_mca(action, (action==A_F_SEARCH) ? "/" : "?");
- last_mca = mca;
- wsearch = 1;
- c = getcc();
- if (c == '!') {
- /*
- * Invert the sense of the search; set wsearch
- * to 0 and get a new character for the start
- * of the pattern.
- */
- start_mca(action,
- (action == A_F_SEARCH) ? "!/" : "!?");
- wsearch = 0;
- c = getcc();
- }
- goto again;
- case A_AGAIN_SEARCH: /* repeat previous search */
- if (number <= 0)
- number = 1;
- if (wsearch)
- start_mca(last_mca,
- (last_mca == A_F_SEARCH) ? "/" : "?");
- else
- start_mca(last_mca,
- (last_mca == A_F_SEARCH) ? "!/" : "!?");
- CMD_EXEC;
- (void)search(mca == A_F_SEARCH, (char *)NULL,
- number, wsearch);
- break;
- case A_HELP: /* help */
- if (ac > 0 && !strcmp(_PATH_HELPFILE, av[curr_ac])) {
- error("Already viewing help.");
+ } else {
+ /* Try expanding the macro */
+ switch (runmacro(inbuf, numberN, Nstate)) {
+ case TOOMACRO:
break;
- }
- lower_left();
- clear_eol();
- putstr("help");
- CMD_EXEC;
- help();
- break;
- case A_TAGFILE: /* tag a new file */
- CMD_RESET;
- start_mca(A_TAGFILE, "Tag: ");
- c = getcc();
- goto again;
- case A_NEXTTAG:
- if (number <= 0)
- number = 1;
- nexttag(number);
- if (tagfile == NULL)
- break;
- if (edit(tagfile))
- (void)tagsearch();
- break;
- case A_PREVTAG:
- if (number <= 0)
- number = 1;
- prevtag(number);
- if (tagfile == NULL)
- break;
- if (edit(tagfile))
- (void)tagsearch();
- break;
- case A_FILE_LIST: /* show list of file names */
- CMD_EXEC;
- showlist();
- repaint();
- break;
- case A_EXAMINE: /* edit a new file */
- CMD_RESET;
- start_mca(A_EXAMINE, "Examine: ");
- c = getcc();
- goto again;
- case A_VISUAL: /* invoke the editor */
- if (ispipe) {
- error("Cannot edit standard input");
- break;
- }
- CMD_EXEC;
- editfile();
- ch_init(0, 0);
- clr_linenum();
- break;
- case A_NEXT_FILE: /* examine next file */
- if (number <= 0)
- number = 1;
- next_file(number);
- break;
- case A_PREV_FILE: /* examine previous file */
- if (number <= 0)
- number = 1;
- prev_file(number);
- break;
- case A_SETMARK: /* set a mark */
- lower_left();
- clear_eol();
- start_mca(A_SETMARK, "mark: ");
- c = getcc();
- if (c == erase_char || c == kill_char)
- break;
- setmark(c);
- break;
- case A_GOMARK: /* go to mark */
- lower_left();
- clear_eol();
- start_mca(A_GOMARK, "goto mark: ");
- c = getcc();
- if (c == erase_char || c == kill_char)
+ case BADMACRO: case NOMACRO: case BADCOMMAND:
+ handle_error();
+ /* fallthrough */
+ case OK:
+ /* recock */
+ *inbuf = '\0';
+ incur = inbuf;
+ Nstate = GETTING;
break;
- gomark(c);
- break;
- case A_PREFIX:
- /*
- * The command is incomplete (more chars are needed).
- * Display the current char so the user knows what's
- * going on and get another character.
- */
- if (mca != A_PREFIX)
- start_mca(A_PREFIX, "");
- if (CONTROL_CHAR(c)) {
- putchr('^');
- c &= ~0200;
- c = CARAT_CHAR(c);
}
- putchr(c);
- c = getcc();
- goto again;
- default:
- bell();
- break;
}
- }
+ } /* for (;;) */
}
+
+/*****************************************************************************
+ *
+ * Misc functions that belong in ncommand.c but are here for historical
+ * and for copyright reasons.
+ *
+ */
+
editfile()
{
+ off_t position();
extern char *current_file;
static int dolinenumber;
static char *editor;
diff --git a/usr.bin/more/decode.c b/usr.bin/more/decode.c
deleted file mode 100644
index 4a33152..0000000
--- a/usr.bin/more/decode.c
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (c) 1988 Mark Nudleman
- * Copyright (c) 1988, 1993
- * The Regents of the University of California. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by the University of
- * California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
- * may be used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-#ifndef lint
-static char sccsid[] = "@(#)decode.c 8.1 (Berkeley) 6/6/93";
-#endif /* not lint */
-
-#ifndef lint
-static const char rcsid[] =
- "$FreeBSD$";
-#endif /* not lint */
-
-/*
- * Routines to decode user commands.
- *
- * This is all table driven.
- * A command table is a sequence of command descriptors.
- * Each command descriptor is a sequence of bytes with the following format:
- * <c1><c2>...<cN><0><action>
- * The characters c1,c2,...,cN are the command string; that is,
- * the characters which the user must type.
- * It is terminated by a null <0> byte.
- * The byte after the null byte is the action code associated
- * with the command string.
- *
- * The default commands are described by cmdtable.
- */
-
-#include <sys/file.h>
-#include <sys/param.h>
-
-#include <stdio.h>
-
-#include "less.h"
-
-/*
- * Command table is ordered roughly according to expected
- * frequency of use, so the common commands are near the beginning.
- */
-#define CONTROL(c) ((c)&037)
-
-/*
- * Ideally the home and end keys would reset the horiz_scroll, too,
- * but this whole thing needs to be made dynamic along with some type
- * of macro commands.
- */
-static char cmdtable[] = {
- '\e','[','B',0, A_F_LINE,
- '\e','[','A',0, A_B_LINE,
- '\e','[','C',0, A_R_COL,
- '\e','[','D',0, A_L_COL,
- '\r',0, A_F_LINE,
- '\n',0, A_F_LINE,
- 'j',0, A_F_LINE,
- 'k',0, A_B_LINE,
- 'd',0, A_F_SCROLL,
- CONTROL('D'),0, A_F_SCROLL,
- 'u',0, A_B_SCROLL,
- CONTROL('U'),0, A_B_SCROLL,
- ' ',0, A_F_SCREEN,
- 'f',0, A_F_SCREEN,
- CONTROL('F'),0, A_F_SCREEN,
- '\e','[','G',0, A_F_SCREEN,
- 'b',0, A_B_SCREEN,
- CONTROL('B'),0, A_B_SCREEN,
- '\e','[','I',0, A_B_SCREEN,
- 'R',0, A_FREPAINT,
- 'r',0, A_REPAINT,
- CONTROL('L'),0, A_REPAINT,
- 'g',0, A_GOLINE,
- '\e','[','H',0, A_HOME,
- 'p',0, A_PERCENT,
- '%',0, A_PERCENT,
- 'G',0, A_GOEND,
- '\e','[','F',0, A_GOEND,
- '0',0, A_DIGIT,
- '1',0, A_DIGIT,
- '2',0, A_DIGIT,
- '3',0, A_DIGIT,
- '4',0, A_DIGIT,
- '5',0, A_DIGIT,
- '6',0, A_DIGIT,
- '7',0, A_DIGIT,
- '8',0, A_DIGIT,
- '9',0, A_DIGIT,
-
- '=',0, A_STAT,
- CONTROL('G'),0, A_STAT,
- '/',0, A_F_SEARCH,
- '?',0, A_B_SEARCH,
- 'n',0, A_AGAIN_SEARCH,
- 'm',0, A_SETMARK,
- '\'',0, A_GOMARK,
- 'E',0, A_EXAMINE,
- 'N',0, A_NEXT_FILE,
- ':','n',0, A_NEXT_FILE,
- 'P',0, A_PREV_FILE,
- ':','p',0, A_PREV_FILE,
- 'v',0, A_VISUAL,
-
- 'h',0, A_HELP,
- 'q',0, A_QUIT,
- ':','q',0, A_QUIT,
- ':','t',0, A_TAGFILE,
- 'T',0, A_PREVTAG,
- 't',0, A_NEXTTAG,
- ':', 'a', 0, A_FILE_LIST,
- 'Z','Z',0, A_QUIT,
-};
-
-char *cmdendtable = cmdtable + sizeof(cmdtable);
-
-#define MAX_CMDLEN 16
-
-static char kbuf[MAX_CMDLEN+1];
-static char *kp = kbuf;
-
-/*
- * Indicate that we're not in a prefix command
- * by resetting the command buffer pointer.
- */
-noprefix()
-{
- kp = kbuf;
-}
-
-/*
- * Decode a command character and return the associated action.
- */
-cmd_decode(c)
- int c;
-{
- register int action = A_INVALID;
-
- /*
- * Append the new command character to the command string in kbuf.
- */
- *kp++ = c;
- *kp = '\0';
-
- action = cmd_search(cmdtable, cmdendtable);
-
- /* This is not a prefix character. */
- if (action != A_PREFIX)
- noprefix();
- return(action);
-}
-
-/*
- * Search a command table for the current command string (in kbuf).
- */
-cmd_search(table, endtable)
- char *table;
- char *endtable;
-{
- register char *p, *q;
-
- for (p = table, q = kbuf; p < endtable; p++, q++) {
- if (*p == *q) {
- /*
- * Current characters match.
- * If we're at the end of the string, we've found it.
- * Return the action code, which is the character
- * after the null at the end of the string
- * in the command table.
- */
- if (*p == '\0')
- return(p[1]);
- }
- else if (*q == '\0') {
- /*
- * Hit the end of the user's command,
- * but not the end of the string in the command table.
- * The user's command is incomplete.
- */
- return(A_PREFIX);
- } else {
- /*
- * Not a match.
- * Skip ahead to the next command in the
- * command table, and reset the pointer
- * to the user's command.
- */
- while (*p++ != '\0');
- q = kbuf-1;
- }
- }
- /*
- * No match found in the entire command table.
- */
- return(A_INVALID);
-}
diff --git a/usr.bin/more/default.morerc b/usr.bin/more/default.morerc
new file mode 100644
index 0000000..7a2ccb4
--- /dev/null
+++ b/usr.bin/more/default.morerc
@@ -0,0 +1,134 @@
+#
+# This is the default initialization file for more(1). To avoid any need to
+# change the manpage or helpfile, almost all commands maintain their historical
+# keymappings. Some additional twoggles may be added that will be left for
+# the intrepid user to discover.
+#
+# This file is compiled directly into more; changing this file will not change
+# the actual defaults (unless it is changed in the source directory and more
+# is recompiled). The correct way to change the global defaults is by
+# adding a /etc/dot.morerc global initialization file.
+#
+# If you use an ~/.morerc that is dependent on specific features of this
+# default morerc, you should copy this default morerc to ~/.defmorerc so that
+# possible future changes in this file do not cause problems for you. The
+# ~/.defmorerc file will cause the compiled-in default morerc to be ignored.
+#
+# The default initialization file is compiled into more(1) so that more(1)
+# will work and be usable even if the filesystem (and the location
+# /usr/share/misc/default.morerc, where this would be stored if it was not
+# compiled into more(1)) is missing or away without leave (chroot directory,
+# fs crash, badly written rescue floppy, or any other reason).
+#
+# BUGS: a) There is no documentation.
+# b) There is no "map" command.
+#
+# $FreeBSD$
+#
+
+# required -- initialize more(1)
+deftog
+
+#
+# basic internal initialization
+#
+set lsthscr 1
+# Add "set hkey_scroll true" to ~/.morerc to enable all the hjkl keys (but
+# disabling h)elp).
+set hkey_scroll false
+set scr_scroll 0
+# We have no way of resetting this on SIGWINCH as the old more A_H_SCROLL did
+# It's probably just as well... (since resetting would lose the old value!)
+# (Actually, we could emulate it from here if we really wanted to).
+set half_scroll (${_sc_height} / 2)
+# magic number indicating the value is not initialized
+set savedhscroll 87382
+
+macro 1 j 'forw_scroll ${number}'
+macro 1 k 'back_scroll ${number}'
+macro 1 \e[B 'forw_scroll ${number}'
+macro 1 \e[A 'back_scroll ${number}'
+macro 1 \n 'forw_scroll ${number}'
+macro 1 \e[G 'forw (${_sc_height} * ${number})'
+macro 1 \e[I 'back (${_sc_height} * ${number})'
+set com_getscr 'condition (${number} != 0); \
+ set scr_scroll ${number}; \
+ condition (${number} == 0); \
+ set scr_scroll ${_sc_height}; \
+ condition true;'
+macro 0 " " 'eval ${com_getscr}; forw ${scr_scroll};'
+macro 0 f 'eval ${com_getscr}; forw ${scr_scroll};'
+macro 0 "" 'eval ${com_getscr}; forw ${scr_scroll};'
+macro 0 b 'eval ${com_getscr}; back ${scr_scroll};'
+macro 0 "" 'eval ${com_getscr}; back ${scr_scroll};'
+set com_sethalfscroll 'condition (${number} != 0); \
+ set half_scroll ${number}; \
+ condition true;'
+macro 0 d 'eval ${com_sethalfscroll}; forw_scroll ${half_scroll}'
+macro 0 "" 'eval ${com_sethalfscroll}; forw_scroll ${half_scroll}'
+macro 0 u 'eval ${com_sethalfscroll}; back_scroll ${half_scroll}'
+macro 0 "" 'eval ${com_sethalfscroll}; back_scroll ${half_scroll}'
+set com_rscroll 'condition (${number} != 0); \
+ set lsthscr ${number}; \
+ condition true; \
+ rscroll ${lsthscr};'
+set com_lscroll 'condition (${number} != 0); \
+ set lsthscr ${number}; \
+ condition true; \
+ lscroll ${lsthscr};'
+# this little trick lets the user simply set hkey_scroll=true in their own
+# ~/.morerc file to enable the 'l' and 'h' keys the way Bill meant them
+macro 0 h 'condition ${hkey_scroll}; eval ${com_lscroll}; \
+ condition_! ${hkey_scroll}; help; \
+ condition true;'
+macro 0 l 'condition ${hkey_scroll}; eval ${com_rscroll}; \
+ condition_! ${hkey_scroll}; error "key not enabled"; \
+ condition true;'
+macro 0 :help 'help'
+macro 0 \e[C 'eval ${com_rscroll}'
+macro 0 \e[D 'eval ${com_lscroll}'
+macro 0 \e[H 'condition (${_wraplines_n} && (${savedhscroll} != 87382)); \
+ rscroll 1; \
+ rscroll ${savedhscroll}; \
+ condition_toggle; \
+ set savedhscroll ${_curhscroll}; \
+ lscroll ${_curhscroll}; \
+ lscroll 1; \
+ condition true;'
+macro 1 n 'research ${_ls_direction_n} ${number}'
+macro 1 N 'research (${_ls_direction_n} + 1) ${number}'
+macro 1 / 'magicasksearch forw ${number}'
+macro 1 ? 'magicasksearch back ${number}'
+macro 0 G 'condition (${number} == 0); goend; \
+ condition (${number} != 0); goline ${number}; \
+ condition true;'
+macro 1 g 'goline ${number}'
+macro 0 p 'gopercent ${number}'
+macro 0 % 'gopercent ${number}'
+macro 0 \e[F 'goend'
+# Quote since it's technically an isspace() character
+macro 0 " " 'repaint'
+macro 0 r 'repaint'
+macro 0 R 'flush'
+macro 0 v 'edit'
+macro 0 :e 'askfile'
+macro 0 E 'askfile'
+# The old keymaping for 'N'
+#macro 1 N 'file next ${number}'
+macro 1 :n 'file next ${number}'
+macro 1 P 'file prev ${number}'
+macro 1 :p 'file prev ${number}'
+macro 0 :a 'file_list'
+macro 0 m 'setmark ?'
+macro 0 \' 'gomark ?'
+macro 0 :t 'asktag'
+macro 1 t 'nexttag ${number}'
+macro 1 T 'prevtag ${number}'
+macro 0 "" 'stat (${_stat_n} + 1)'
+macro 0 = 'stat (${_stat_n} + 1)'
+macro 0 q 'quit'
+macro 0 :q 'quit'
+macro 0 ZZ 'quit'
+# This command intentionally disabled by default. The command parser is
+# too baroque to expose hapless users to.
+#macro 0 :: 'usercom'
diff --git a/usr.bin/more/input.c b/usr.bin/more/input.c
index 50e2031..1ecd346 100644
--- a/usr.bin/more/input.c
+++ b/usr.bin/more/input.c
@@ -55,7 +55,9 @@ static const char rcsid[] =
#include "less.h"
-int horiz_off = NO_HORIZ_OFF; /* # characters scrolled off left of screen */
+/* NOTE!: if (wraplines) assert (horiz_off == 0) */
+int horiz_off = 0; /* # characters scrolled off left of screen */
+int wraplines = 1; /* wrap lines around screen, yes or no */
extern int squeeze;
extern int sigs;
@@ -103,7 +105,7 @@ forw_line(curr_pos)
/*
* Append the char to the line and get the next char.
* The pappend() will throw away any unimportant chars
- * (ie. not underlines or bolds) as per horiz_off.
+ * (ie. not underlines or bolds) as per wraplines.
*
* XXX line.c needs to be rewritten...
*/
@@ -114,7 +116,7 @@ forw_line(curr_pos)
* is too long to print in the screen width.
* End the line here.
*/
- if (horiz_off != NO_HORIZ_OFF) {
+ if (!wraplines) {
/* Throw away left-over characters on line */
c = ch_forw_get();
while (c != '\n' && c != EOI)
@@ -247,7 +249,7 @@ back_line(curr_pos)
break;
if (pappend(c))
{
- if (horiz_off == NO_HORIZ_OFF) {
+ if (wraplines) {
/*
* Got a full printable line, but we haven't
* reached our curr_pos yet. Discard the line
diff --git a/usr.bin/more/less.h b/usr.bin/more/less.h
index e17ec40..f30abd9 100644
--- a/usr.bin/more/less.h
+++ b/usr.bin/more/less.h
@@ -36,6 +36,8 @@
* $FreeBSD$
*/
+#define MAXVARLENGTH (20)
+
#define NULL_POSITION ((off_t)(-1))
#define EOI (0)
@@ -58,37 +60,77 @@
#define BOTTOM_PLUS_ONE (-2)
#define MIDDLE (-3)
-#define A_INVALID -1
-
-#define A_AGAIN_SEARCH 1
-#define A_B_LINE 2
-#define A_B_SCREEN 3
-#define A_B_SCROLL 4
-#define A_B_SEARCH 5
-#define A_DIGIT 6
-#define A_EXAMINE 7
-#define A_FREPAINT 8
-#define A_F_LINE 9
-#define A_F_SCREEN 10
-#define A_F_SCROLL 11
-#define A_F_SEARCH 12
-#define A_GOEND 13
-#define A_GOLINE 14
-#define A_GOMARK 15
-#define A_HELP 16
-#define A_NEXT_FILE 17
-#define A_PERCENT 18
-#define A_PREFIX 19
-#define A_PREV_FILE 20
-#define A_QUIT 21
-#define A_REPAINT 22
-#define A_SETMARK 23
-#define A_STAT 24
-#define A_VISUAL 25
-#define A_TAGFILE 26
-#define A_FILE_LIST 27
-#define A_L_COL 28
-#define A_R_COL 29
-#define A_HOME 30
-#define A_NEXTTAG 31
-#define A_PREVTAG 32
+/* The return type of runmacro() */
+enum runmacro { OK=0, TOOMACRO, BADMACRO, NOMACRO, BADCOMMAND };
+
+#ifdef DEFINEGLOBALS
+#define GLOBAL(var, val) var = val
+#else
+#define GLOBAL(var, val) extern var
+#endif
+
+
+/*
+ * This style of error-reporting (see also command.c) is only used by some
+ * code. Eventually most of the code should use it, since it is becoming
+ * inconvenient to have John Q. random function() calling error().
+ *
+ * This style of error-reporting still leaves somewhat to be desired....
+ *
+ * Note that more(1) needs to potentially work under low-memory conditions
+ * (such as may occur when all available memory has been sucked-up by
+ * the file buffer in ch.c).
+ */
+
+/* Be careful about ordering correctly!! (must match deferrinit_) */
+enum error { E_OK=0, E_AMBIG, E_BADMATH, E_BADVAR, E_BOGCOM, E_CANTPARSE,
+ E_CANTXPND, E_COMPLIM, E_EXTERN, E_NOMAC, E_MALLOC, E_NONUM,
+ E_NOSTR, E_NOTOG, E_NULL };
+
+/* Listed here for reference only. Be careful about ordering correctly!! */
+#define deferrinit_ { \
+ "", /* E_OK */ \
+ "ambigious macro", /* E_AMBIG */ \
+ "invalid arithmetic expression", /* E_BADMATH */ \
+ "bad variable", /* E_BADVAR */ \
+ "bogus command", /* E_BOGCOM */ \
+ "could not parse command string", /* E_CANTPARSE */ \
+ "could not expand macro", /* E_CANTXPND */ \
+ "compile time limit", /* E_COMPLIM */ \
+ "external dependency error", /* E_EXTERN */ \
+ "could not find match for macro", /* E_NOMAC */ \
+ "malloc() failed", /* E_MALLOC */ \
+ "missing numeric argument to command", /* E_NONUM */ \
+ "missing string argument to command", /* E_NOSTR */ \
+ "bad n-toggle argument to command", /* E_NOTOG */ \
+ "to the unknown error", /* E_NULL */ \
+}
+GLOBAL(const char *deferr[], deferrinit_ );
+
+/*
+ * It is possible for erreur to become dis-synchronized from errstr if
+ * its users aren't careful. Access through the macros is considered
+ * careful.
+ */
+GLOBAL(enum error erreur, NULL);
+GLOBAL(char *errstr, NULL); /* must point be null or free()'ble */
+
+#define SETERR(e) do { \
+ erreur = (e); \
+ if (errstr) free(errstr); \
+ errstr = NULL; \
+ } while (0)
+/* SETERRSTR() also exists. It is in command.c */
+
+/*
+ * An emalloc() traditionally never fails, but fmalloc() may fail, hence
+ * the non-standard name. The fmalloc() is just syntactic sugar that sets
+ * erreur for the user.
+ *
+ * fmalloc(size, pointer-to-new-memory);
+ *
+ * Don't compile this puppy with -Wall...
+ */
+
+#define FMALLOC(s,v) ((((v) = malloc(s)) ? 0 : \
+ ((errstr ? free(errstr), errstr=NULL : 0), erreur = E_MALLOC)), (v))
diff --git a/usr.bin/more/less.morerc b/usr.bin/more/less.morerc
new file mode 100644
index 0000000..95fb905
--- /dev/null
+++ b/usr.bin/more/less.morerc
@@ -0,0 +1,61 @@
+#
+# This sample .morerc causes more to emulate the default GNU less(1)
+# keys, in so far as more(1) is capable (which is not very far at the
+# moment).
+#
+# Some of this will/should/may be eventually merged into default.morerc.
+#
+# $FreeBSD$
+#
+
+# magic value indicating we should use ${sc_height}
+set window 2424989898
+
+macro 0 H 'help'
+set com_getscr 'condition (${number} == 0); \
+ condition (${window} == 2424989898); \
+ set scr_scroll ${sc_height}; \
+ condition_toggle; \
+ set scr_scroll ${window}; \
+ condition (${number} != 0); \
+ set scr_scroll ${number}; \
+ condition true;'
+macro 0 "" 'eval ${com_getscr}; forw ${scr_scroll};'
+macro 0 z 'condition (${number} == 0); \
+ eval ${com_getscr}; forw ${scr_scroll}; \
+ condition (${number} != 0); \
+ set window ${number}; forw ${number}; \
+ condition true;'
+macro 0 w 'condition (${number} == 0); \
+ eval ${com_getscr}; back ${scr_scroll}; \
+ condition (${number} != 0); \
+ set window ${number}; back ${number}; \
+ condition true;'
+macro 0 "\ev" 'eval ${com_getscr}; back ${scr_scroll};'
+macro 1 "" 'forw_scroll ${number};'
+macro 1 e 'forw_scroll ${number};'
+macro 1 "" 'forw_scroll ${number};'
+macro 1 y 'back_scroll ${number};'
+macro 1 "" 'back_scroll ${number};'
+macro 1 "" 'back_scroll ${number};'
+macro 1 " " 'back_scroll ${number};'
+macro 8 \e) 'rscroll ${number};'
+macro 8 \e[C 'rscroll ${number};'
+macro 8 \e( 'lscroll ${number};'
+macro 8 \e[D 'lscroll ${number};'
+macro 1 "<" 'goline ${number};'
+macro 1 "\e<" 'goline ${number};
+macro 0 ">" 'condition (${number} == 0); goend; \
+ condition (${number} != 0); goline ${number}; \
+ condition true;'
+macro 0 "\e>" 'condition (${number} == 0); goend; \
+ condition (${number} != 0); goline ${number}; \
+ condition true;'
+macro 0 "" 'gomark ?;'
+macro 0 "" 'askfile;'
+macro 0 :f 'stat (${_stat_n} + 1);'
+macro 0 V 'error "Less is not being run!"
+macro 0 Q 'quit;'
+macro 0 :Q 'quit;'
+# It might be possible to match the brace/bracket-pairing feature of
+# less using some complex regexps... Project for a rain weekend... :-)
diff --git a/usr.bin/more/line.c b/usr.bin/more/line.c
index 6b98073..f974e83 100644
--- a/usr.bin/more/line.c
+++ b/usr.bin/more/line.c
@@ -111,7 +111,7 @@ prewind()
{
line = curr = linebuf;
ln_state = LN_NORMAL;
- column = (horiz_off == NO_HORIZ_OFF) ? 0 : -horiz_off;
+ column = -horiz_off;
}
/*
@@ -379,14 +379,9 @@ ln_bo_xb_case:
/*
* Expand a tab into spaces.
*/
- if (horiz_off != NO_HORIZ_OFF)
- do {
- NEW_COLUMN(1);
- } while (((column + horiz_off) % tabstop) != 0);
- else
- do {
- NEW_COLUMN(1);
- } while ((column % tabstop) != 0);
+ do {
+ NEW_COLUMN(1);
+ } while (((column + horiz_off) % tabstop) != 0);
*curr++ = '\t';
return (0);
}
diff --git a/usr.bin/more/macro.c b/usr.bin/more/macro.c
new file mode 100644
index 0000000..1fdd56e
--- /dev/null
+++ b/usr.bin/more/macro.c
@@ -0,0 +1,292 @@
+/*-
+ * Copyright (c) 1999 Timmy M. Vanderhoek
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Expansion of macros.
+ */
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "less.h"
+
+
+/*
+ * Used to construct tables of macros. Each macro string expands to command.
+ * A number N is associated with each execution of a macro. The command
+ * "set number <N>" will be done before the expansion. The end of the table is
+ * specified by mactabsize. A NULL entry for command denotes a macro that
+ * has been marked deleted for some reason. As of this writing, there is no
+ * code that actually deletes a macro...
+ */
+struct macro {
+ char *string; /* characters typed to activate macro */
+ char *command; /* command resulting after the macro is activated */
+ long defnumber; /* default value of the N number */
+ int flags; /* only holds STICKYNUMB for now... */
+};
+/* (struct macro) ->flags */
+#define NOFLAGS 0
+#define STICKYNUMB 1 /* Set defnumber to current number, if current number */
+
+/*
+ * The macro table.
+ */
+struct macro *mactab = NULL;
+int mactabsize = 0;
+
+static enum runmacro runmacro_();
+static struct macro *matchmac();
+
+
+/*
+ * XXX Everything's really just a macro until resolved as a quantum wave
+ * probability distribution.
+ */
+
+
+/*
+ * Attempts to run the appropriate macro. Returns 0, or OK, if the macro
+ * was succesfully run. Returns BADMACRO and sets erreur if something is
+ * horribly wrong with the macro. Returns NOMACRO if the macro has no valid
+ * expansion. BADMACRO and NOMACRO are almost the same. Returns BADCOMMAND
+ * and leaves erreur set (hopefully it was set when runmacro() tried to execute
+ * the command associated with the macro) if the command associated with
+ * the macro was unsuccessful. Returns TOOMACRO if the macro appears to be
+ * incomplete (ie. the user has not finished typing it in yet). The erreur
+ * is not set in this case.
+ *
+ * XXX There's no good reason not to just disallow badmacros from within
+ * setmacro() ... It's not clear what the author was thinking at the time.
+ */
+enum runmacro
+runmacro(macro, number, anyN)
+ const char *macro; /* the macro string to try and expand */
+ long number; /* the number N associated with this execution */
+ int anyN; /* FALSE is we should use the default N associated with
+ * the macro. TRUE if we should use the number argument. */
+{
+ struct macro *cur, *matched;
+ int match, yetmatch;
+ int s;
+
+ if (!mactab) {
+ /* Should only happen with really sucky default rc files... */
+ SETERR (E_CANTXPND);
+ return NOMACRO;
+ }
+
+ match = yetmatch = 0;
+ for (cur = mactab, s = mactabsize; s; cur++, s--) {
+ if (!cur->command)
+ continue; /* deleted macro */
+ if (!strcmp(cur->string, macro))
+ matched = cur, match++;
+ else if (!strncmp(cur->string, macro, strlen(macro)))
+ yetmatch++;
+ }
+
+ if (match == 1) {
+ if (yetmatch) {
+ SETERR (E_AMBIG);
+ return BADMACRO;
+ }
+
+ /* XXX it's not clear how to handle error when setting
+ * the number N --- this is a deficiency in the style of error-
+ * reporting suggested in command.c and less.h. Could have
+ * setvar() guarantee success when setting "number". A failure
+ * must not become fatal or it becomes impossible to do
+ * any commands at all. */
+ if (anyN) {
+ if (matched->flags & STICKYNUMB)
+ matched->defnumber = number;
+ (void) setvari("number", number);
+ } else
+ (void) setvari("number", matched->defnumber);
+ clear_error();
+
+ if (command(matched->command))
+ return BADCOMMAND;
+ return OK;
+ }
+ if (match > 1) {
+ SETERR (E_AMBIG);
+ return BADMACRO;
+ }
+ if (!match && !yetmatch) {
+ SETERR (E_CANTXPND);
+ return NOMACRO;
+ }
+ assert(yetmatch);
+ return TOOMACRO;
+}
+
+/*
+ * Associates a macro with a given command. Returns -1 if it was unable to
+ * set the macro. Errors associated with setting a macro may be caught
+ * either in this function, setmacro(), or in runmacro(). Both macro and
+ * command are strcpy()'d into their own space.
+ */
+int
+setmacro(macro, command)
+ const char *macro;
+ const char *command;
+{
+ struct macro *cur, *new = NULL;
+ char *new_mac, *new_com;
+ int s;
+
+ assert (macro); assert (command);
+
+ /* First, check for any existing macro matches in the custom table */
+ s = mactabsize;
+ for (cur = mactab; s; cur++, s--) {
+ if (!cur->command) {
+ /* Hmm... A deleted macro in the table */
+ new = cur;
+ continue;
+ }
+ if (!strcmp(cur->string, macro)) {
+ /*
+ * An exact match to the new macro already exists.
+ * Calling realloc() on cur->string and cur->command
+ * without risking being left in bad state is tricky.
+ * Just do it the slow but sure way...
+ */
+ new = cur;
+ break;
+ }
+ }
+
+ /*
+ * Do the allocations here so that we can maintain consistent state
+ * even if realloc() fails when we try to expand the table (suppose
+ * the table gets expanded but the next malloc to get space for the
+ * macro fails).
+ */
+ if (!FMALLOC(strlen(macro) + 1, new_mac))
+ return -1;
+ if (!FMALLOC(strlen(command) + 1, new_com))
+ return -1;
+
+ if (!new) {
+ /* Extend the command table by one record */
+ struct macro *t = realloc(mactab, (mactabsize + 1) *
+ sizeof(struct macro));
+ if (!t) {
+ /* The old mactab is still valid. Just back out. */
+ free(new_mac), free(new_com);
+ SETERR (E_MALLOC);
+ return -1;
+ } else
+ mactab = t;
+ new = &mactab[mactabsize];
+ mactabsize++;
+ new->string = new->command = NULL;
+ }
+
+ if (new->string) free(new->string);
+ if (new->command) free(new->command);
+ new->string = new_mac;
+ new->command = new_com;
+ strcpy(new->string, macro);
+ strcpy(new->command, command);
+
+ return 0;
+}
+
+/*
+ * Set the sticky tag on a macro. Returns -1 on failure, 0 on success.
+ */
+int
+stickymac(macro, state)
+ const char *macro;
+ int state; /* set it to TRUE or set it to FALSE */
+{
+ struct macro *m = matchmac(macro);
+ if (!m)
+ return -1;
+
+ if (state)
+ m->flags |= STICKYNUMB;
+ else
+ m->flags &= ~STICKYNUMB;
+
+ return 0;
+}
+
+/*
+ * Set the default number of a macro. Returns -1 on failure, 0 on success.
+ */
+int
+setmacnumb(macro, N)
+ const char *macro;
+ long N; /* The default number */
+{
+ struct macro *m = matchmac(macro);
+ if (!m)
+ return -1;
+
+ m->defnumber = N;
+ return 0;
+}
+
+/*
+ * Tries to find a struct macro matching "macro". Returns NULL if an exact
+ * match could not be found (eg. ambiguous macro, no macro, etc).
+ */
+static struct macro *
+matchmac(macro)
+ const char *macro;
+{
+ struct macro *retr, *cur;
+ int s;
+
+ retr = NULL;
+ for (cur = mactab, s = mactabsize; s; cur++, s--) {
+ if (!cur->command)
+ continue;
+ if (!strcmp(cur->string, macro)) {
+ if (retr) {
+ SETERR (E_AMBIG);
+ return NULL; /* matched twice! */
+ } else
+ retr = cur;
+ } else if (!strncmp(cur->string, macro, strlen(macro))) {
+ SETERR (E_AMBIG);
+ return NULL; /* ambiguous macro! */
+ }
+ }
+ return retr;
+}
diff --git a/usr.bin/more/main.c b/usr.bin/more/main.c
index f7159ea..cbe853c 100644
--- a/usr.bin/more/main.c
+++ b/usr.bin/more/main.c
@@ -56,18 +56,21 @@ static const char rcsid[] =
#include <sys/param.h>
#include <sys/types.h>
+#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#define DEFINEGLOBALS
+#include "defrc.h"
#include "less.h"
+#include "pathnames.h"
int ispipe;
char *current_file, *previous_file, *current_name, *next_name;
int any_display;
-int scroll;
int ac;
char **av;
int curr_ac;
@@ -88,7 +91,6 @@ extern int tagoption;
edit(filename)
register char *filename;
{
- extern int errno;
register int f;
register char *m;
off_t initial_pos, prev_pos, position();
@@ -334,8 +336,15 @@ main(argc, argv)
} while (file < 0 && ++curr_ac < ac);
}
- if (file >= 0)
+ if (file >= 0) {
+ /*
+ * Don't call rcfiles() until now so that people who put
+ * wierd things (like "forw_scroll") in their rc file don't
+ * cause us to SEGV.
+ */
+ rcfiles();
commands();
+ }
quit();
/*NOTREACHED*/
}
@@ -375,3 +384,156 @@ quit()
raw_mode(0);
exit(0);
}
+
+/*
+ * Read in from each of the three rc files - default, system, user.
+ * Calls handle_error() directly to report errors.
+ */
+rcfiles()
+{
+ FILE *fd;
+ char fbuf[MAXPATHLEN + 1];
+ char *c;
+ int readrc();
+ int savederrno;
+ static int str_read();
+
+ /* The default builtin rc file */
+ if ((c = getenv("HOME")) &&
+ strlen(c) + strlen(_PATH_DEFRC) + 1 < MAXPATHLEN) {
+ sprintf(fbuf, "%s/%s", c, _PATH_DEFRC);
+ fd = fopen(fbuf, "r");
+ savederrno = errno;
+ if (!fd) {
+ if (!access(_PATH_SYSMORERC, F_OK)) {
+ SETERRSTR(E_EXTERN, "unable to read %s: %s",
+ _PATH_SYSMORERC, strerror(savederrno));
+ handle_error();
+ }
+ /* We'd better have some type of default keys!! */
+ goto use_builtin_defrc;
+ } else {
+ readrc(fd);
+ fclose(fd);
+ }
+ } else {
+use_builtin_defrc:
+ fd = fropen(DEFRC, str_read);
+ readrc(fd);
+ fclose(fd);
+ }
+
+ /* The system rc file */
+ fd = fopen(_PATH_SYSMORERC, "r");
+ savederrno = errno;
+ if (!fd) {
+ if (!access(_PATH_SYSMORERC, F_OK)) {
+ SETERRSTR(E_EXTERN, "unable to read %s: %s",
+ _PATH_SYSMORERC, strerror(savederrno));
+ handle_error();
+ } else
+ ; /* non-existant => non-error */
+ } else {
+ readrc(fd);
+ fclose(fd);
+ }
+
+ /* The user rc file */
+ if ((c = getenv("HOME")) &&
+ strlen(c) + strlen(_PATH_RC) + 1 < MAXPATHLEN) {
+ sprintf(fbuf, "%s/%s", c, _PATH_RC);
+ fd = fopen(fbuf, "r");
+ savederrno = errno;
+ if (!fd) {
+ if (!access(fbuf, F_OK)) {
+ SETERRSTR(E_EXTERN,
+ "unable to read %s: %s", fbuf,
+ strerror(savederrno));
+ handle_error();
+ } else
+ ; /* non-existant => non-error */
+ } else {
+ readrc(fd);
+ fclose(fd);
+ }
+ }
+}
+
+/*
+ * Read-in an rc file from a fd. Calls handle_error() directly to handle
+ * errors.
+ *
+ * This really belongs in ncommand.c, but that file is already 33292 bytes
+ * long.
+ */
+readrc(fd)
+ FILE *fd;
+{
+ char *bufptr, *buf;
+ size_t len;
+ int strlenbuf;
+
+ buf = NULL;
+ strlenbuf = 0;
+ while (bufptr = fgetln(fd, &len)) {
+ if (!len)
+ continue; /* ??? */
+ if (*bufptr == '#')
+ continue; /* skip comments */
+ if (!(buf = reallocf(buf, strlenbuf + len + 1))) {
+ SETERR(E_MALLOC);
+ handle_error();
+ if (strlenbuf + len < 1024)
+ return; /* major memory shortage... */
+ continue;
+ }
+ memcpy (buf + strlenbuf, bufptr, len);
+ buf[len + strlenbuf] = '\0';
+ if (len > 1 && buf[strlenbuf + len - 2] == '\\') {
+ /* line continuation */
+ buf[strlenbuf + len - 2] = '\0';
+ strlenbuf = strlen(buf);
+ continue;
+ }
+ if (buf[len + strlenbuf - 1] == '\n')
+ buf[len + strlenbuf - 1] = '\0';
+ if (command(buf))
+ handle_error();
+ free(buf);
+ buf = NULL;
+ strlenbuf = 0;
+ }
+}
+
+/*
+ * Read from the NUL-terminated cookie. Non-reentrant: keeps a static pointer
+ * to the current position in the cookie. Used for funopen().
+ */
+static int
+str_read(cookie, buf, len)
+ void *cookie;
+ char *buf;
+ size_t len;
+{
+ static char *curpos;
+ static int cooklen;
+ static char *lastcook;
+
+ if (lastcook != cookie) {
+ /* begin working on a new cookie */
+ curpos = cookie;
+ lastcook = cookie;
+ cooklen = strlen(cookie);
+ }
+
+ if (curpos + len > lastcook + cooklen) {
+ ssize_t r;
+ memcpy(buf, curpos, r = (cooklen - (curpos - lastcook)));
+ curpos = cookie + cooklen;
+ return (int) r;
+ } else {
+ memcpy(buf, curpos, len);
+ curpos += len;
+ return (int) len;
+ }
+}
diff --git a/usr.bin/more/more.1 b/usr.bin/more/more.1
index 09bd030..e0f9db7a0 100644
--- a/usr.bin/more/more.1
+++ b/usr.bin/more/more.1
@@ -225,8 +225,10 @@ which does NOT contain the pattern.
.It Ic \&?\&! Ns Ar pattern
Like ?, but the search is for the N-th line
which does NOT contain the pattern.
-.It Ic n
-Repeat previous search, for N-th line containing the last pattern
+.It Ic n No and Ic N
+Repeat previous search,
+in same or opposite direction respectively,
+for N-th line containing the last pattern
(or
.Tn NOT
containing the last pattern, if the previous search
@@ -237,11 +239,11 @@ If the filename is missing, the "current" file (see the N and P commands
below) from the list of files in the command line is re-examined.
If the filename is a pound sign (#), the previously examined file is
re-examined.
-.It Ic N No or Ic \&:n
+.It Ic \&:n
Examine the next file (from the list of files given in the command line).
If a number N is specified (not to be confused with the command N),
the N-th next file is examined.
-.It Ic P No or Ic \&:p
+.It Ic \&:p
Examine the previous file.
If a number N is specified, the N-th previous file is examined.
.It Ic \&:t
diff --git a/usr.bin/more/more.help b/usr.bin/more/more.help
index eb78cb4..1d5b1fb 100644
--- a/usr.bin/more/more.help
+++ b/usr.bin/more/more.help
@@ -25,11 +25,13 @@
?pattern * Search backward for N-th line containing the pattern.
?!pattern * Search backward for N-th line NOT containing the pattern.
n * Repeat previous search (for N-th occurence).
+ N * Repeat previous search (for N-th occurence), switching
+ the direction of the search.
:a Display the list of files.
E [file] Examine a new file.
- :n, N * Examine the next file.
- :p, P * Examine the previous file.
+ :n * Examine the next file.
+ :p * Examine the previous file.
:t [tag] Examine the tag.
t, T Move forward or backward N tags in the gtags queue.
v Run an editor on the current file.
diff --git a/usr.bin/more/most.morerc b/usr.bin/more/most.morerc
new file mode 100644
index 0000000..781f735
--- /dev/null
+++ b/usr.bin/more/most.morerc
@@ -0,0 +1,86 @@
+#
+# This sample .morerc causes more to emulate the default most(1)
+# keys, in so far as more(1) is capable (which is not very far at the
+# moment).
+#
+# $FreeBSD$
+#
+# BUGS: It's extremely unlikely that more(1) will ever support multiple
+# windows as most(1) does. Multiple windows is an editor function,
+# isn't it?
+#
+
+macro 1 "" 'forw (${_sc_height} * ${number})'
+macro 1 v 'forw ${number}'
+macro 1 V 'forw ${number}'
+macro 1 "" 'forw ${number}'
+macro 1 "" 'back ${number}'
+macro 1 ^ 'back ${number}'
+macro 0 T 'goline 1'
+macro 0 "\e<" 'goline 1'
+macro 0 B 'goend'
+macro 0 "\e>" 'goend'
+# Doesn't match most(1) documentation
+macro 1 "\e[C" 'rscroll ${number}'
+macro 1 "\t" 'rscroll (60 * ${number})'
+macro 1 > 'rscroll (60 * ${number})'
+# Doesn't match most(1) documentation
+macro 1 "\e[D" 'lscroll ${number}'
+# Doesn't really do much, being ^B and all...
+macro 1 "" 'lscroll (60 * ${number})'
+macro 1 < 'lscroll (60 * ${number})'
+macro 1 u 'back (${_sc_height} * ${number})'
+macro 1 U 'back (${_sc_height} * ${number})'
+macro 1 "" 'back (${_sc_height} * ${number})'
+macro 1 "" 'back (${_sc_height} * ${number})'
+macro 0 R 'repaint'
+macro 0 r 'repaint'
+macro 0 "" 'repaint'
+macro 0 j 'condition (${number} == 0); \
+ error "prompt for line-number not supported"; \
+ condition (${number} != 0); \
+ goline ${number}; \
+ condition true;'
+macro 0 J 'condition (${number} == 0); \
+ error "prompt for line-number not supported"; \
+ condition (${number} != 0); \
+ goline ${number}; \
+ condition true;'
+macro 0 g 'condition (${number} == 0); \
+ error "prompt for line-number not supported"; \
+ condition (${number} != 0); \
+ goline ${number}; \
+ condition true;'
+macro 0 G 'condition (${number} == 0); \
+ error "prompt for line-number not supported"; \
+ condition (${number} != 0); \
+ goline ${number}; \
+ condition true;'
+macro 0 % 'condition (${number} == 0); \
+ error "prompt for percent not supported"; \
+ condition_toggle; \
+ gopercent ${number}; \
+ condition true;'
+macro 0 q 'quit'
+macro 0 Q 'quit'
+macro 0 " E" 'quit'
+macro 0 h 'help'
+macro 0 H 'help'
+macro 0 "" 'help'
+macro 0 "\e[N" 'help'
+macro 1 f 'magicasksearch forw ${number}'
+macro 1 / 'magicasksearch forw ${number}'
+macro 1 "" 'magicasksearch forw ${number}'
+macro 1 ? 'magicasksearch back ${number}'
+macro 1 n 'research ${_ls_direction_n} ${number}'
+macro 1 N 'research ${_ls_direction_n} ${number}'
+# The mark stuff could be more effeciently done with a temp var, of course... :)
+# These are nice enough that I'm tempted to bring them into default.morerc
+macro 0 m 'setmark z'
+macro 0 " M" 'setmark z'
+macro 0 "." 'setmark z'
+macro 0 "" 'setmark y; gomark z; setmark x; gomark y; setmark z; gomark x;'
+macro 0 "," 'setmark y; gomark z; setmark x; gomark y; setmark z; gomark x;'
+macro 0 e 'edit'
+macro 0 E 'edit'
+macro 0 :n 'file next ${number}'
diff --git a/usr.bin/more/ncommand.c b/usr.bin/more/ncommand.c
new file mode 100644
index 0000000..8203293
--- /dev/null
+++ b/usr.bin/more/ncommand.c
@@ -0,0 +1,1452 @@
+/*-
+ * Copyright (c) 1999 Timmy M. Vanderhoek
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * These functions handle evaluation of primitive commands. In general,
+ * commands either come from macro.h as it expands user input, or
+ * directly from a .morerc file (in which case only a limited set of
+ * commands is valid.
+ *
+ * Commands are matched by command() against a command table. The rest
+ * of the command line string passed to command() is then passed to a
+ * function corresponding to the given command. The specific command
+ * function evaluates the remainder of the command string with the help
+ * of getstr() and getnumb(), both of which also handle variable expansion
+ * into a single word. It may in the future be desirable to add a special
+ * getsstring(), get-search-string, function. Specific command functions
+ * should not try grokking the command string by themselves.
+ *
+ * A command and its arguments are terminated by either a NUL or a ';'.
+ * This is recognized by both getstr() and getint(). Specific command
+ * functions return a pointer to the end of the command (and its arguments)
+ * thus allowing command() to accept commands that are chained together
+ * by semicolons. If a specific command fails it returns NULL preventing
+ * any proceeding commands (chained together with ';') from being parsed.
+ * This can be considered as a feature.
+ *
+ * All variable-access functions and variable state are internal to
+ * ncommand.c. The sole exceptions are setvar() and setvari().
+ */
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "less.h"
+#include "pathnames.h"
+
+static getint(), getstr_free();
+static void **getstr_raisectxt();
+
+/* The internal command table. */
+
+static const char *cscroll(), *cquit(), *cerror(), *ceval(), *cset(),
+ *cflush(), *cmacro(), *caskfile(), *cusercom(),
+ *ctags(), *chscroll(), *cgomisc(), *cgoend(), *csearch(),
+ *cstat(), *cdeftog(), *ccondition(), *chelp(), *cfile(),
+ *cfile_list(), *cedit(), *cmark(), *creadrc();
+
+/* An enum identifying each command */
+enum cident {
+ DEFTOG, /* Initialize toggle values */
+ EVAL, /* Evaluate a subexpression */
+ SET, /* Set a variable */
+ MACRO, /* Create a new macro */
+ ERROR, /* Print a notification message */
+ CONDITION, /* Condition evaluation of (almost) _all_ commands */
+ CONDITION_N, /* CONDITION with an inverse truth table */
+ CONDITION_TOGGLE,/* Switch to the reverse sense of the last condition */
+ USERCOM, /* Get the user to type in a direct command */
+ READRC, /* Read-in a named rc file */
+ QUIT, /* Quit */
+ HELP, /* Help */
+ FLUSH, /* Flush file buffer and friends */
+ REPAINT, /* Redraw the screen (useful if it got trashed) */
+ FORW_SCROLL, /* Scroll forward N lines */
+ BACK_SCROLL, /* Scroll backward N lines */
+ FORW, /* Jump or scroll forward N lines */
+ BACK, /* Jump or scroll backwards N lines */
+ LSCROLL, /* Scroll horizontally leftwards */
+ RSCROLL, /* Scroll horizontally to the right */
+ GOLINE, /* Goto line number N */
+ GOPERCENT, /* Goto percent N of the file */
+ GOEND, /* Goto the end of the file */
+ EDIT, /* Edit the current file, using getenv(EDITOR) */
+ ASKFILE, /* Ask for a different, new file */
+ CFILE, /* Page/view the N'th next or prev file */
+ FILE_LIST, /* List the files that CFILE moves around in */
+ STAT, /* List detailed file statistics in prompt */
+ MAGICASKSEARCH, /* Ask for a regexp search string */
+ SEARCH, /* Search for a regexp */
+ RESEARCH, /* Search for the next N'th occurrence */
+ SETMARK, /* Set a bookmark to the current position */
+ GOMARK, /* Goto a previously set bookmark */
+ ASKFTAG, /* Ask for a tag to goto */
+ NEXTFTAG, /* Move forward N in the tag queue */
+ PREVFTAG, /* Move backwards N in the tag queue */
+};
+
+static struct ctable {
+ const char *cname;
+ enum cident cident;
+ const char * (*cfunc)(enum cident, const char *args);
+} ctable[] = {
+ { "deftog", DEFTOG, cdeftog },
+ { "eval", EVAL, ceval },
+ { "set", SET, cset },
+ { "macro", MACRO, cmacro },
+ { "error", ERROR, cerror },
+ { "condition", CONDITION, ccondition },
+ { "condition_!", CONDITION_N, ccondition },
+ { "condition_toggle", CONDITION_TOGGLE, ccondition },
+ { "condition_else", CONDITION_TOGGLE, ccondition },
+ { "usercom", USERCOM, cusercom },
+ { "readrc", READRC, creadrc },
+ { "quit", QUIT, cquit },
+ { "help", HELP, chelp },
+ { "flush", FLUSH, cflush },
+ { "repaint", REPAINT, cflush },
+ { "forw_scroll", FORW_SCROLL, cscroll },
+ { "back_scroll", BACK_SCROLL, cscroll },
+ { "forw", FORW, cscroll },
+ { "back", BACK, cscroll },
+ { "rscroll", RSCROLL, chscroll },
+ { "lscroll", LSCROLL, chscroll },
+ { "goline", GOLINE, cgomisc },
+ { "gopercent", GOPERCENT, cgomisc },
+ { "goend", GOEND, cgoend },
+ { "edit", EDIT, cedit },
+ { "askfile", ASKFILE, caskfile },
+ { "file", CFILE, cfile },
+ { "file_list", FILE_LIST, cfile_list },
+ { "stat", STAT, cstat },
+ { "magicasksearch", MAGICASKSEARCH, csearch },
+ { "search", SEARCH, csearch },
+ { "research", RESEARCH, csearch },
+ { "setmark", SETMARK, cmark },
+ { "gomark", GOMARK, cmark },
+ { "asktag", ASKFTAG, ctags },
+ { "nexttag", NEXTFTAG, ctags },
+ { "prevtag", PREVFTAG, ctags },
+};
+
+
+/* I believe this is just for cosmetic purposes. */
+#define CMD_EXEC lower_left(); flush()
+
+
+/*
+ * Prototypes are for people who can't program.
+ */
+
+
+/*
+ * The main command string evaluator. Returns -1 if an error occurred
+ * in the command or in executing the command, returns 0 otherwise. If an
+ * error occurs while evaluating a command line containing multiple commands,
+ * commands after the error are not processed. Multiple commands may be
+ * separated by ';' or '\n'. (Multiple commands may also be separated by
+ * a ' ', but this is really a bug...)
+ */
+int
+command(line)
+ const char *line;
+{
+ struct ctable *i;
+
+donextcommand:
+
+ while (isspace(*line) || *line == ';' || *line == '\n') line++;
+ if (!*line)
+ return 0;
+
+ for (i = ctable; i != ctable + sizeof(ctable) / sizeof(struct ctable);
+ i++) {
+ if (!strncmp(i->cname, line, strlen(i->cname)) &&
+ (line[strlen(i->cname)] == ' ' ||
+ line[strlen(i->cname)] == ';' ||
+ line[strlen(i->cname)] == '\0')) {
+ /* Found a match! */
+ void **ctxt;
+ CMD_EXEC;
+ ctxt = getstr_raisectxt();
+ line = i->cfunc (i->cident, line + strlen(i->cname));
+ getstr_free(ctxt);
+ if (!line)
+ return -1; /* error evaluating command */
+ goto donextcommand;
+ }
+ }
+
+ SETERRSTR(E_BOGCOM, "invalid command: ``%s''", line);
+ (void) command("condition true");
+ return -1;
+}
+
+
+/*****************************************************************************
+ *
+ * Functions to help specific command functions to parse their arguments.
+ *
+ * The three functions here, getstr(), getint(), and gettog() could in theory
+ * have vastly different concepts of what a number is, and what a string is,
+ * etc., but in practice they don't.
+ */
+
+static char *readvar();
+
+#define NCTXTS 30
+void *getstr_ctxts[NCTXTS]; /* could easily be made dynamic... */
+void **getstr_curctxt = getstr_ctxts;
+
+/*
+ * Read a single argument string from a command string. This understands
+ * $variables, "double quotes", 'single quotes', and backslash escapes
+ * for \\, \$, \n, \e, \t, and \" (the latter only inside double quotes). A
+ * string may be delimited by double quotes or spaces, not both (duh). It
+ * may be worthwhile to add another quotation style in which arithmetic
+ * expressions are expanded. Currently an arithmetic expression is expanded
+ * iff it is the only component of the string.
+ *
+ * Returns a pointer to the beginning of the string or NULL if it was unable to
+ * read a string. The line is modified to point somewhere between the end of
+ * the command argument just read-in and the beginning of the next command
+ * argument (if any). The returned pointer will be free()'d by calling
+ * getstr_free().
+ */
+static char *
+getstr(line)
+ char **line; /* Where to look for the return string */
+{
+ int doquotes = 0; /* Doing a double-quote string */
+ char *retr;
+
+ if (getstr_curctxt - getstr_ctxts == NCTXTS) {
+ SETERRSTR(E_COMPLIM,
+ "compile-time limit exceeded: command contexts");
+ return NULL; /* wouldn't be able to register return pointer */
+ }
+
+ while (isspace(**line)) (*line)++;
+ if (**line == '\'') {
+ /* Read until closing quote or '\0'. */
+ char *nextw = retr = malloc(1);
+ char *c = ++(*line);
+ int l;
+ for (; *c; c++) {
+ if (*c == '\'') {
+ if (c[-1] == '\\') {
+ nextw[-1] = '\'';
+ continue;
+ } else {
+ *nextw = '\0';
+ *line = c + 1;
+ *getstr_curctxt = retr;
+ getstr_curctxt++;
+ return retr;
+ }
+ }
+ l = nextw - retr;
+ /* XXX How many realloc()'s can you make per second? */
+ if (!(retr = reallocf(retr, c - *line + 250))) {
+ SETERR (E_MALLOC);
+ return NULL;
+ }
+ nextw = retr + l;
+ *nextw = *c;
+ nextw++;
+ }
+ SETERR(E_CANTPARSE);
+ return NULL;
+ }
+ if (**line == '"') {
+ doquotes = 1;
+ (*line)++;
+ }
+ if (**line == '(') {
+ /* An arithmetic expression instead of a string... Well, I
+ * guess this is valid. See comment leading this function. */
+ int n;
+ if (getint(&n, line))
+ return NULL;
+ retr = NULL;
+ asprintf(&retr, "%d", n);
+ if (!retr)
+ SETERR (E_MALLOC);
+ *getstr_curctxt = retr;
+ getstr_curctxt++;
+ return retr;
+ }
+
+ if (!FMALLOC(1, retr))
+ return NULL;
+ *retr = '\0';
+ for (;;) {
+ char *c, hack[2];
+
+ switch (**line) {
+ case '\\':
+ switch (*(*line + 1)) {
+ case '\\': case '$': case '\'':
+ case 't': case ' ': case ';':
+ hack[0] = *(*line + 1);
+ hack[1] = '\0';
+ c = hack;
+ (*line) += 2;
+ break;
+ case 'n':
+ c = "\n";
+ (*line) += 2;
+ break;
+ case 'e':
+ c = "\e";
+ (*line) += 2;
+ break;
+ case '"':
+ if (doquotes) {
+ c = "\"";
+ (*line) += 2;
+ break;
+ } else
+ ; /* fallthrough */
+ default:
+ c = "\\";
+ (*line)++;
+ break;
+ }
+ break;
+ case '$':
+ (*line)++;
+ if (!(c = readvar(line))) {
+ free (retr);
+ return NULL;
+ }
+ break;
+ case ' ': case '\t': case ';':
+ if (!doquotes) {
+ doquotes = 1;
+ case '"':
+ if (doquotes) {
+ /* The end of the string */
+ (*line)++;
+ case '\0':
+ *getstr_curctxt = retr;
+ getstr_curctxt++;
+ return retr;
+ }
+ }
+ /* fallthrough */
+ default:
+ hack[0] = **line;
+ hack[1] = '\0';
+ c = hack;
+ (*line)++;
+ break;
+ }
+
+ retr = reallocf(retr, strlen(retr) + strlen(c) + 1);
+ if (!retr) {
+ SETERR (E_MALLOC);
+ return NULL;
+ }
+ strcat(retr, c);
+ }
+}
+
+/*
+ * Returns a new context that should be passed to getstr_free() so that
+ * getstr_free() only free()'s memory from that particular context.
+ */
+static void **
+getstr_raisectxt()
+{
+ return getstr_curctxt;
+}
+
+/*
+ * Calls free() on all memory from context or higher.
+ */
+static
+getstr_free(context)
+ void **context;
+{
+ while (getstr_curctxt != context) {
+ getstr_curctxt--;
+ free (*getstr_curctxt);
+ }
+}
+
+/*
+ * Reads an integer value from a command string. Typed numbers must be
+ * in base10. If a '(' is found as the first character of the integer value,
+ * then getint() will read until a closing ')' unless interupted by an
+ * end-of-command marker (error). The parentheses are expected to contain a
+ * simple arithmetic statement involving only one '*', '/', etc. operation. The
+ * rightmost digit or the closing parenthesis should be followed by either a
+ * space or an end-of-command marker.
+ *
+ * Returns 0 on success, -1 on failure. The line will be modified to just
+ * after the last piece of text parsed.
+ *
+ * XXX We may add support for negative numbers, someday...
+ */
+static int
+getint(numb, line)
+ long *numb; /* The read-in number is returned through this */
+ char **line; /* The command line from which to read numb */
+{
+ long n;
+ int j;
+ char *p, *t;
+
+ while (isspace(**line)) (*line)++;
+
+ switch (**line) {
+ case '(':
+ (*line)++;
+ if (getint(numb, line))
+ return -1;
+ while (isspace(**line)) (*line)++;
+ j = **line;
+ (*line)++;
+ if (j == ')')
+ return 0;
+ if (**line == '=' && (j == '!' || j == '=')
+ || j == '&' && **line == '&' || j == '|' && **line == '|')
+ j = (j << 8) + *((*line)++);
+ if (getint(&n, line))
+ return -1;
+ while (isspace(**line)) (*line)++;
+ if (**line != ')') {
+ SETERRSTR (E_BADMATH,
+ "missing arithmetic close parenthesis");
+ return -1;
+ } else
+ (*line)++;
+ switch (j) {
+ case ('!' << 8) + '=':
+ *numb = *numb != n;
+ return 0;
+ case ('=' << 8) + '=':
+ *numb = *numb == n;
+ return 0;
+ case ('&' << 8) + '&':
+ *numb = *numb && n;
+ return 0;
+ case ('|' << 8) + '|':
+ *numb = *numb || n;
+ return 0;
+ case '+':
+ *numb += n;
+ return 0;
+ case '-':
+ *numb -= n;
+ return 0;
+ case '*':
+ *numb *= n;
+ return 0;
+ case '/':
+ if (n == 0)
+ *numb = 1;
+ else
+ *numb /= n;
+ return 0;
+ default:
+ SETERRSTR (E_BADMATH,
+ "bad arithmetic operator: ``%c''", j);
+ return -1;
+ }
+ case '$':
+ t = (*line)++;
+ if (!(p = readvar(line)))
+ return -1;
+ if (!isdigit(*p)) {
+ SETERRSTR (E_BADMATH,
+ "non-number found (``%s'') "
+ "after expanding variable at ``%s''", p, t);
+ return -1;
+ }
+ *numb = atol(p);
+ return 0;
+ case '9': case '0': case '8': case '1': case '7': case '2': case '6':
+ case '3': case '5': case '4':
+ *numb = atol(*line);
+ while (isdigit(**line)) (*line)++;
+ return 0;
+ case '"': case '\'':
+ /* Uh-oh. It's really a string. We'll go through getstr()
+ * and hope for the best, but this isn't looking good. */
+ if (!(p = getstr(line)))
+ return -1;
+ *numb = atol(p);
+ return 0;
+ default:
+ SETERRSTR (E_BADMATH,
+ "non-number found, number expected, before parsing ``%s''",
+ *line);
+ return -1;
+ }
+}
+
+/*
+ * Read an argument from the command string and match that argument against
+ * a series of legitimate values. For example,
+ *
+ * command <<opt0|opt1|opt2>>
+ *
+ * This command by be given to the command() processor as a variant of either
+ * "command opt1" or "command 4", both of which will cause this function to
+ * return the value 1. This function returns -1 on failure.
+ *
+ * Note that an option (eg. "opt1") must _not_ start with a digit!!
+ */
+static int
+gettog(const char **line, int nopts, ...)
+{
+ char *str;
+ int n;
+ va_list opts;
+
+ if (!(str = getstr(line)))
+ return -1;
+
+ if (isdigit(*str)) {
+ n = atol(str) % nopts;
+ return n;
+ }
+
+ va_start(opts, nopts);
+ for (n=0; n < nopts; n++) {
+ if (!strcasecmp(str, va_arg(opts, const char *))) {
+ va_end(opts);
+ return n;
+ }
+ }
+ va_end(opts);
+ SETERR (E_NOTOG); /* XXX would be nice to list valid toggles... */
+ return -1;
+}
+
+/*
+ * A companion function for gettog(). Example,
+ *
+ * optnumb = gettog(&args, 3, "opt1", "opt2", "opt3");
+ * settog("_lastoptnumb", optnumb, "opt1", "opt2", "opt3");
+ *
+ * And the variable named _lastoptnumb_s will be set to one of "opt1", "opt2",
+ * or "opt3" as per the value of optnumb. The variable _lastoptnumb_n will
+ * also be set to a corresponding value. The optnumb argument had better
+ * be within the correct range (between 0 and 2 in the above example)!!
+ */
+settog(const char *varname, int optval, int nargs, ...)
+{
+ va_list opts;
+ char *s;
+ int optval_orig = optval;
+
+ assert(optval < nargs); assert(optval >= 0);
+ if (!(s = malloc(strlen(varname) + 3)))
+ return;
+ strcpy (s, varname);
+ va_start(opts, nargs);
+ for (; optval; optval--) va_arg(opts, const char *);
+ s[strlen(varname)] = '_';
+ s[strlen(varname) + 1] = 's';
+ s[strlen(varname) + 2] = '\0';
+ (void) setvar(s, va_arg(opts, const char *));
+ s[strlen(varname) + 1] = 'n';
+ (void) setvari(s, (long) optval_orig);
+ clear_error();
+ va_end(opts);
+ free(s);
+}
+
+/*
+ * Read {text} and return the string associated with the variable named
+ * <<text>>. Returns NULL on failure.
+ */
+static char *
+readvar(line)
+ char **line;
+{
+ int vlength;
+ char *vstart;
+ static char *getvar();
+
+ if (**line != '{') {
+ SETERR (E_BADVAR);
+ return NULL;
+ }
+ (*line)++;
+ for (vlength = 0, vstart = *line; **line &&
+ (isalpha(**line) || **line == '_'); (*line)++)
+ vlength++;
+ if (**line != '}' || vlength == 0) {
+ SETERRSTR (E_BADVAR,
+ "bad character ``%c'' in variable ``%.*s''", **line,
+ vlength, vstart);
+ return NULL;
+ }
+ (*line)++;
+ return getvar(vstart, vlength);
+}
+
+
+/*****************************************************************************
+ *
+ * Track variables.
+ *
+ */
+
+static struct vble {
+ struct vble *next;
+ char *name;
+ char *value;
+} *vble_l; /* linked-list of existing variables */
+
+/*
+ * Return a pointer to the string that variable var represents. Returns
+ * NULL if a match could not be found and sets erreur.
+ */
+static const char *
+getvar(var, len)
+ char *var;
+ int len; /* strncmp(var, varmatch, len); is used to match variables */
+{
+ struct vble *i;
+
+ for (i = vble_l; i; i = i->next) {
+ if (!strncasecmp (i->name, var, len))
+ return i->value;
+ }
+
+ SETERRSTR (E_BADVAR, "variable ``%.*s'' not set", len, var);
+ return NULL;
+}
+
+/*
+ * Set variable var to val. Returns -1 on failure, 0 on success.
+ */
+int
+setvar(var, val)
+ char *var; /* variable to set */
+ char *val; /* value to set variable to */
+{
+ struct vble *i, *last;
+ char *var_n, *val_n;
+ char *c;
+
+ for (c = var; *c && (isalpha(*c) || *c == '_'); c++) ;
+ if (*c) {
+ SETERRSTR (E_BADVAR,
+ "bad character ``%c'' in variable ``%s''", *c, var);
+ return -1;
+ }
+
+ for (i = vble_l; i; last = i, i = i->next) {
+ if (!strcasecmp (i->name, var)) {
+ if (!FMALLOC(strlen(val) + 1, val_n))
+ return -1;
+ free(i->value);
+ i->value = val_n;
+ strcpy(i->value, val);
+ return 0;
+ }
+ }
+
+ /* Need to add another variable to the list vble_l */
+ if (!FMALLOC(strlen(var) + 1, var_n))
+ return -1;
+ if (!FMALLOC(strlen(val) + 1, val_n))
+ return -1;
+ if (!vble_l) {
+ if (!FMALLOC(sizeof(struct vble), vble_l))
+ return -1;
+ i = vble_l;
+ } else {
+ if (!FMALLOC(sizeof(struct vble), last->next))
+ return -1;
+ i = last->next;
+ }
+ i->next = NULL;
+ i->name = var_n; strcpy(i->name, var);
+ i->value = val_n; strcpy(i->value, val);
+ return 0;
+}
+
+/*
+ * Set or reset, as appropriate, variable var to val.
+ */
+int
+setvari(var, val)
+ const char *var;
+ long val;
+{
+ char n[21]; /* XXX */
+ snprintf(n, sizeof(n), "%ld", val);
+ n[20] = '\0';
+ setvar(var, n);
+}
+
+
+/*****************************************************************************
+ *
+ * Specific command functions. These aren't actually individual functions,
+ * since using a gigantic switch statement is faster to type, but they
+ * pretend to be individual functions.
+ *
+ */
+
+int condition_eval = 1; /* false if we just parse commands, but do nothing */
+
+#define ARGSTR(v) do { \
+ if (!((v) = getstr(&args))) return NULL; \
+ } while (0)
+#define ARGNUM(v) do { \
+ if (getint(&(v), &args)) return NULL; \
+ } while (0)
+/* semi-gratuitous use of GNU cpp extension */
+#define ARGTOG(v, n, togs...) do { \
+ if (((v) = gettog(&args, n, togs)) == -1) \
+ return NULL; \
+ } while (0)
+#define ENDPARSE do { \
+ if (!condition_eval) return args; \
+ } while (0)
+
+/*
+ * deftog
+ *
+ * Set all toggle options to their default values, provided the toggle option
+ * is registered with this function. This command is meant to be used at the
+ * beginning of the startup command list.
+ */
+static const char *
+cdeftog(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ extern int horiz_off, wraplines;
+
+ ENDPARSE;
+ settog("_stat", 1, 2, "on", "off");
+ settog("_ls_direction", 0, 2, "forw", "back");
+ settog("_ls_sense", 0, 2, "noinvert", "invert");
+ setvari("_curhscroll", (long) horiz_off);
+ settog("_wraplines", wraplines, 2, "off", "on");
+ setvar("_ls_regexp", "");
+ /*
+ * not present: _file_direction
+ */
+ return args;
+}
+
+/*
+ * eval <<string>>
+ *
+ * Passes string back into the command evaluator.
+ */
+static const char *
+ceval(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ const char *com;
+
+ ARGSTR(com); /* The command line to evaluate */
+ ENDPARSE;
+
+ /* It's not clear what to do with the command() return code */
+ (void) command(com);
+
+ return args;
+}
+
+/*
+ * set <<variablename>> <<variablestring>>
+ *
+ * Sets variable variablename to string variablestring.
+ */
+static const char *
+cset(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ const char *str, *var;
+
+ ARGSTR(var); /* name of variable to set */
+ ARGSTR(str); /* value to set variable to */
+ ENDPARSE;
+
+ if (*var == '_') {
+ SETERRSTR (E_BADVAR,
+ "variables beginning with '_' are reserved");
+ return NULL;
+ }
+ if (setvar(var, str))
+ return NULL;
+
+ return args;
+}
+
+/*
+ * macro <<default_number>> <<keys>> <<command>>
+ *
+ * Associates the macro keys with command.
+ */
+static const char *
+cmacro(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ const char *keys, *com;
+ long num;
+
+ ARGNUM(num); /* the default number N for this macro */
+ ARGSTR(keys); /* string of keys representing a macro */
+ ARGSTR(com); /* command line to associate with macro */
+ ENDPARSE;
+
+ if (setmacro(keys, com))
+ return NULL;
+ if (setmacnumb(keys, num))
+ return NULL;
+
+ return args;
+}
+
+/*
+ * error <<string>>
+ *
+ * Prints a notification message.
+ */
+static const char *
+cerror(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ char *s;
+
+ ARGSTR(s); /* error message */
+ ENDPARSE;
+ error(s);
+ return args;
+}
+
+/*
+ * condition <<boolean>>
+ * condition_! <<boolean>>
+ *
+ * If boolean is false, causes all commands except for other condition
+ * commands to be ignored. The <<boolean>> may be specified as a number
+ * (in which case even numbers are true, odd numbers are false), or one
+ * of "on", "off", "true", and "false".
+ */
+static const char *
+ccondition(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ /* ENDPARSE; */
+
+ if (cident == CONDITION_TOGGLE) {
+ condition_eval = !condition_eval;
+ return args;
+ }
+
+ switch (gettog(&args, 4, "off", "on", "false", "true")) {
+ case 0: case 2:
+ condition_eval = 0;
+ break;
+ case 1: case 3:
+ condition_eval = 1;
+ break;
+ case -1:
+ return NULL;
+ }
+ if (cident == CONDITION_N)
+ condition_eval = !condition_eval;
+ return args;
+}
+
+/*
+ * usercom
+ *
+ * Accept a direct command from the user's terminal.
+ */
+static const char *
+cusercom(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ char buf[125]; /* XXX should avoid static buffer... */
+
+ ENDPARSE;
+ getinput("Command: ", buf, sizeof(buf));
+ if (command(buf))
+ return NULL;
+
+ return args;
+}
+
+/*
+ * readrc <<filename>>
+ *
+ * Read-in rc commands from the named file.
+ */
+static const char *
+creadrc(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ const char *file;
+ FILE *fd;
+
+ ARGSTR(file);
+ ENDPARSE;
+
+ if (!*file)
+ return args;
+ /*
+ * Should perhaps warn user if file perms or ownership look suspicious.
+ */
+ fd = fopen(file, "r");
+ if (!fd) {
+ SETERRSTR (E_NULL, "could not open file ``%s''", file);
+ return NULL;
+ }
+ readrc(fd);
+ fclose(fd);
+
+ return args;
+}
+
+/*
+ * quit
+ *
+ * Performs as advertised.
+ */
+static const char *
+cquit(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ ENDPARSE;
+ quit();
+ return NULL; /* oh boy... */
+}
+
+/*
+ * help
+ *
+ * Doesn't do much.
+ */
+static const char *
+chelp(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ extern int ac, curr_ac;
+ extern char **av;
+
+ ENDPARSE;
+ if (ac > 0 && !strcmp(_PATH_HELPFILE, av[curr_ac])) {
+ SETERRSTR(E_NULL, "already viewing help");
+ return NULL;
+ }
+ help();
+ return args;
+}
+
+/*
+ * flush
+ * repaint
+ *
+ * Flushes the file buffer, provided we are not reading from a pipe.
+ * Frees any other memory that I can get my hands on from here.
+ */
+static const char *
+cflush(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ extern int ispipe;
+
+ ENDPARSE;
+ if (cident == FLUSH && !ispipe) {
+ ch_init(0, 0); /* XXX should this be ch_init(ctags,0) */
+ clr_linenum();
+ }
+ repaint();
+
+ return args;
+}
+
+/*
+ * forw_scroll <<n>>
+ * back_scroll <<n>>
+ * forw <<n>>
+ * back <<n>>
+ *
+ * Move forward number n lines. The _scroll variants force a scroll, the
+ * others may scroll or may just redraw the screen at the appropriate location,
+ * whichever is faster.
+ */
+static const char *
+cscroll(cident, args)
+ enum cident cident;
+ char *args;
+{
+ long n;
+ char *retr;
+
+ ARGNUM(n); /* number of lines to move by */
+ ENDPARSE;
+
+ switch (cident) {
+ case FORW_SCROLL:
+ forward(n, 0);
+ break;
+ case BACK_SCROLL:
+ backward(n, 0);
+ break;
+ case FORW:
+ forward(n, 1);
+ break;
+ case BACK:
+ backward(n, 1);
+ break;
+ }
+
+ return args;
+}
+
+/*
+ * rscroll <<n>>
+ * lscroll <<n>>
+ *
+ * Scroll left or right by n lines.
+ */
+static const char *
+chscroll(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ long n;
+ char *retr;
+ extern int horiz_off, wraplines;
+
+ ARGNUM(n); /* Number of columns to scroll by */
+ ENDPARSE;
+
+ if (n == 0)
+ return args;
+
+ switch (cident) {
+ case RSCROLL:
+ if (wraplines) {
+ wraplines = 0;
+ assert (horiz_off == 0);
+ } else {
+ horiz_off += n;
+ if (horiz_off < 0)
+ horiz_off = INT_MAX; /* disaster control */
+ }
+ break;
+ case LSCROLL:
+ if (horiz_off != 0) {
+ horiz_off -= n;
+ if (horiz_off < 0)
+ horiz_off = 0;
+ } else
+ wraplines = 1;
+ break;
+ }
+ repaint(); /* screen_trashed = 1 */
+ setvari("_curhscroll", (long) horiz_off);
+ settog("_wraplines", wraplines, 2, "off", "on");
+
+ return args;
+}
+
+/*
+ * goline <<line>>
+ * gopercent <<percent>>
+ *
+ * Goto the line numbered <<line>>, if possible. Goto <<percent>> percent of
+ * the file. Whole-numbered percents only, of course.
+ */
+static const char *
+cgomisc(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ long n;
+
+ ARGNUM(n); /* number N */
+ ENDPARSE;
+
+ switch (cident) {
+ case GOLINE:
+ jump_back(n);
+ break;
+ case GOPERCENT:
+ if (n > 100)
+ n = 100;
+ jump_percent(n);
+ break;
+ }
+ return args;
+}
+
+/*
+ * goend
+ *
+ * Goto the end of the file. Future variation should include the GNU less(1)-
+ * style follow a-la tail(1).
+ */
+static const char *
+cgoend(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ ENDPARSE;
+ jump_forw();
+ return args;
+}
+
+/*
+ * edit
+ *
+ * Edits the current file with a word editor. This command is just begging
+ * to be extended to allow the user to specify an editor. Additionally, this
+ * would require some kind of getenv command or similar change.
+ */
+static const char *
+cedit(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ extern ispipe;
+
+ ENDPARSE;
+ if (ispipe) {
+ SETERRSTR(E_NULL, "cannot edit standard input");
+ return NULL;
+ }
+ editfile();
+ /*
+ * XXX less-than brilliant things happen if the user while editing
+ * deletes a large section at the end of the file where we think we
+ * are currently viewing...
+ */
+ ch_init(0, 0); /* Clear the internal file buffer */
+ clr_linenum();
+ return args;
+}
+
+/*
+ * askfile
+ *
+ * Loads a new file. Queries the user for the name of the new file.
+ */
+static const char *
+caskfile(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ char buf[MAXPATHLEN + 1];
+
+ ENDPARSE;
+ getinput("Examine: ", buf, sizeof(buf));
+ /* XXX should modify this() or edit() to handle lists of file, ie.
+ * the type of lists that I get if I try to glob("*") */
+ (void)edit(glob(buf));
+
+ return args;
+}
+
+/*
+ * file <<next|previous>> <<N>>
+ *
+ * Loads the N'th next or previous file, typically from the list of files
+ * given on the command line.
+ */
+static const char *
+cfile(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ enum { FORW=0, BACK=1 } direction;
+ long N;
+
+ ARGTOG(direction, 10, "next", "previous", "forward", "backward",
+ "forwards", "backwards", "next", "prev", "forw", "back");
+ ARGNUM(N);
+ ENDPARSE;
+ direction %= 2;
+
+ /* next_file() and prev_file() call error() directly (bad) */
+ switch (direction) {
+ case FORW:
+ next_file(N);
+ break;
+ case BACK:
+ prev_file(N);
+ break;
+ }
+ settog("_file_direction", direction, 2, "next", "previous");
+ return args;
+}
+
+/*
+ * file_list
+ *
+ * Lists the files the "file next" and "file prev" are moving around in.
+ */
+static const char *
+cfile_list(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ ENDPARSE;
+ showlist();
+ repaint(); /* screen_trashed = 1; */
+ return args;
+}
+
+/*
+ * stat <<on|off>>
+ *
+ * Display the detailed statistics as part of the prompt. The toggle option
+ * variable is called _stat (giving ${_stat_s} and ${_stat_n}).
+ */
+static const char *
+cstat(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ int onoff;
+
+ ARGTOG(onoff, 2, "on", "off");
+ ENDPARSE;
+ statprompt(onoff);
+ settog("_stat", onoff, 2, "on", "off");
+ return args;
+}
+
+/*
+ * magicasksearch <<forw|back>> <<n>>
+ * search <<forw|back>> <<n>> <<<noinvert|invert>> <searchstring>>
+ * research <<forw|back>> <<n>>
+ *
+ * Arguments specifying an option (ie. <<forw|back>> and <<noinvert|invert>>
+ * may be specified either as text (eg. "forw"), or as a number, in which case
+ * even numbers specify the former setting and odd numbers the latter setting.
+ *
+ * The magicasksearch will ask the user for a regexp and intuit whether they
+ * want to invert the sense of matching or not: if the first character of the
+ * regexp is a '!', it is removed and the sense is inverted.
+ *
+ * The toggle options are called _ls_direction and _ls_sense. In addition,
+ * ${_ls_regexp} is set to the regexp used. These variables are only set
+ * when the search and magicsearch commands are used.
+ */
+static const char *
+csearch(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ char buf[100], *str;
+ enum { FORW=0, BACK=1 } direction;
+ static enum { NOINVERT=0, INVERT=1 } sense;
+ long N;
+
+ ARGTOG(direction, 6, "forw", "back", "forward", "backward",
+ "forwards", "backwards");
+ ARGNUM(N);
+ if (cident == SEARCH) {
+ ARGTOG(sense, 2, "noinvert", "invert");
+ ARGSTR(str);
+ }
+ ENDPARSE;
+ direction %= 2;
+
+ /* Get the search string, one way or another */
+ switch (cident) {
+ case MAGICASKSEARCH:
+ biggetinputhack(); /* It's magic, boys */
+ if (direction == FORW)
+ getinput("Search: /", buf, 2);
+ else
+ getinput("Search: ?", buf, 2);
+ switch (*buf) {
+ case '\0':
+ /* Cancelled */
+ return args;
+ case '!':
+ /* Magic */
+ if (direction == FORW)
+ getinput("Search: !/", buf, sizeof(buf));
+ else
+ getinput("Search: !?", buf, sizeof(buf));
+ sense = INVERT;
+ break;
+ default:
+ /* No magic */
+ ungetcc(*buf);
+ if (direction == FORW)
+ getinput("Search: /", buf, sizeof(buf));
+ else
+ getinput("Search: ?", buf, sizeof(buf));
+ sense = NOINVERT;
+ break;
+ }
+ if (!*buf)
+ return args;
+ str = buf;
+ break;
+ case SEARCH:
+ break;
+ case RESEARCH:
+ str = NULL;
+ break;
+ }
+
+ if (cident == SEARCH || cident == MAGICASKSEARCH) {
+ settog("_ls_direction", direction, 2, "forw", "back");
+ settog("_ls_sense", sense, 2, "noinvert", "invert");
+ setvar("_ls_regexp", str);
+ }
+ search(!direction, str, N, !sense);
+ return args;
+}
+
+/*
+ * setmark <<character>>
+ * gomark <<character>>
+ *
+ * Set a marker at the current position, or goto a previously set marker.
+ * Character may be a-z, or '?' to ask the user to enter a character. The
+ * special mark '\'' may not be set, but may be the target of a goto.
+ */
+static const char *
+cmark(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ char smark[2];
+ const char *mark;
+
+ ARGSTR(mark);
+ ENDPARSE;
+
+ /* gomark() and setmark() will further check mark's validity */
+ if (!*mark || mark[1]) {
+ SETERRSTR(E_NULL, "bad mark character");
+ return NULL;
+ }
+
+ if (*mark == '?') {
+ biggetinputhack(); /* so getinput() returns after one char */
+ switch (cident) {
+ case GOMARK:
+ getinput("goto mark: ", smark, sizeof smark);
+ break;
+ case SETMARK:
+ getinput("set mark: ", smark, sizeof smark);
+ break;
+ }
+ if (!*smark)
+ return args;
+ mark = smark;
+ }
+
+ switch (cident) {
+ case GOMARK:
+ gomark(*mark);
+ break;
+ case SETMARK:
+ setmark(*mark);
+ break;
+ }
+ return args;
+}
+
+/*
+ * asktag
+ * nexttag <<number>>
+ * prevtag <<number>>
+ *
+ * Asks the user for a tag, or moves around the tag queue.
+ */
+static const char *
+ctags(cident, args)
+ enum cident cident;
+ const char *args;
+{
+ extern char *tagfile; /* XXX No reason for this to be a global... */
+ long n;
+
+ if (cident != ASKFTAG)
+ ARGNUM(n);
+ ENDPARSE;
+
+ if (cident == ASKFTAG) {
+ char buf[100]; /* XXX should do something else... */
+ getinput("Tag: ", buf, sizeof(buf));
+ if (!*buf)
+ return args;
+ findtag(buf);
+ } else {
+ switch (cident) {
+ case NEXTFTAG:
+ nexttag(n);
+ break;
+ case PREVFTAG:
+ prevtag(n);
+ break;
+ }
+ }
+
+ /* Load the tagfile and position ourselves. */
+ if (tagfile == NULL)
+ return NULL;
+ if (edit(tagfile))
+ tagsearch();
+ return args; /* tag stuff still calls error() on its own */
+}
diff --git a/usr.bin/more/option.c b/usr.bin/more/option.c
index 53f332b..1bb2202 100644
--- a/usr.bin/more/option.c
+++ b/usr.bin/more/option.c
@@ -77,6 +77,16 @@ option(argc, argv)
while ((ch = getopt(argc, argv, "/:ceinst:ux:f")) != -1)
switch((char)ch) {
case '/':
+ /*
+ * Might be interesting to make this option search
+ * through the whole list of files on the command line
+ * until a match is found. Prior to this commit adding
+ * the new comand interpreter, it would sort-of do
+ * this, provided all the files listed on the command
+ * line were of length zero bytes (well, with the
+ * exception of the file actually containing a match,
+ * I suppose).
+ */
firstsearch = optarg;
break;
case 'c':
diff --git a/usr.bin/more/output.c b/usr.bin/more/output.c
index 337a294..cf07f09 100644
--- a/usr.bin/more/output.c
+++ b/usr.bin/more/output.c
@@ -51,8 +51,6 @@ static const char rcsid[] =
#include "less.h"
-int errmsgs; /* Count of messages displayed by error() */
-
extern int bs_mode;
extern int sigs;
extern int sc_width, sc_height;
@@ -61,7 +59,6 @@ extern int so_width, se_width;
extern int bo_width, be_width;
extern int tabstop;
extern int screen_trashed;
-extern int any_display;
extern char *line;
extern int horiz_off;
extern int mode_flags;
@@ -121,7 +118,7 @@ markup(ent_ul, ent_bo)
* UL_CHAR, UE_CHAR, BO_CHAR, BE_CHAR markups.
*/
#define MAYPUTCHR(char) \
- if (column >= eff_horiz_off) { \
+ if (column >= horiz_off) { \
column += markup(&ent_ul, &ent_bo); \
putchr(char); \
}
@@ -133,7 +130,6 @@ put_line()
register int column;
extern int auto_wrap, ignaw;
int ent_ul, ent_bo; /* enter or exit ul|bo mode for next char */
- int eff_horiz_off;
if (sigs)
{
@@ -144,11 +140,6 @@ put_line()
return;
}
- if (horiz_off == NO_HORIZ_OFF)
- eff_horiz_off = 0;
- else
- eff_horiz_off = horiz_off;
-
if (line == NULL)
line = "";
@@ -198,11 +189,11 @@ put_line()
case '\b':
/*
* column must be at least one greater than
- * eff_horiz_off (ie. we must be in the second or
+ * horiz_off (ie. we must be in the second or
* beyond screen column) or we'll just end-up
* backspacing up to the previous line.
*/
- if (column > eff_horiz_off) {
+ if (column > horiz_off) {
column += markup(&ent_ul, &ent_bo);
putbs();
column--;
@@ -234,11 +225,11 @@ put_line()
column++;
}
}
- if (column == sc_width + eff_horiz_off && mode_flags)
+ if (column == sc_width + horiz_off && mode_flags)
last_pos_highlighted = 1;
}
column += markup(&ent_ul, &ent_bo);
- if (column < sc_width + eff_horiz_off || !auto_wrap || ignaw)
+ if (column < sc_width + horiz_off || !auto_wrap || ignaw)
putchr('\n');
}
@@ -290,65 +281,28 @@ putstr(s)
putchr(*s++);
}
-int cmdstack;
-static char return_to_continue[] = "(press RETURN)";
-
/*
- * Output a message in the lower left corner of the screen
- * and wait for carriage return.
+ * Output a string, expanding control characters into printable sequences.
+ * Returns the number of characters printed.
*/
-error(s)
+int
+putxstr(s)
char *s;
{
- int ch;
-
- ++errmsgs;
- if (!any_display) {
- /*
- * Nothing has been displayed yet. Output this message on
- * error output (file descriptor 2) and don't wait for a
- * keystroke to continue.
- *
- * This has the desirable effect of producing all error
- * messages on error output if standard output is directed
- * to a file. It also does the same if we never produce
- * any real output; for example, if the input file(s) cannot
- * be opened. If we do eventually produce output, code in
- * edit() makes sure these messages can be seen before they
- * are overwritten or scrolled away.
- */
- (void)write(2, s, strlen(s));
- (void)write(2, "\n", 1);
- return;
- }
-
- lower_left();
- clear_eol();
- so_enter();
- if (s) {
- putstr(s);
- putstr(" ");
- }
- putstr(return_to_continue);
- so_exit();
-
- if ((ch = getchr()) != '\n') {
- /* XXX hardcoded */
- if (ch == 'q')
- quit();
- cmdstack = ch;
+ int c;
+ int retr = 0;
+
+ for (; c = *s; s++) {
+ if (CONTROL_CHAR(c)) {
+ putchr('^');
+ retr++;
+ c &= ~0200;
+ c = CARAT_CHAR(c);
+ }
+ putchr(c);
}
- lower_left();
- if ((s==NULL)?0:(strlen(s)) + sizeof(return_to_continue) +
- so_width + se_width + 1 > sc_width)
- /*
- * Printing the message has probably scrolled the screen.
- * {{ Unless the terminal doesn't have auto margins,
- * in which case we just hammered on the right margin. }}
- */
- repaint();
- flush();
+ return(retr);
}
static char intr_to_abort[] = "... (interrupt to abort)";
diff --git a/usr.bin/more/pathnames.h b/usr.bin/more/pathnames.h
index c564360..647ade6 100644
--- a/usr.bin/more/pathnames.h
+++ b/usr.bin/more/pathnames.h
@@ -31,8 +31,14 @@
* SUCH DAMAGE.
*
* @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
*/
#include <paths.h>
#define _PATH_HELPFILE "/usr/share/misc/more.help"
+#define _PATH_RC ".morerc"
+#define _PATH_DEFRC ".defmorerc"
+#define _PATH_SYSMORERC "/etc/dot.morerc"
+/* Should have a /etc/dot.defmorerc, too... */
diff --git a/usr.bin/more/prim.c b/usr.bin/more/prim.c
index 03fcad8..eb76f95 100644
--- a/usr.bin/more/prim.c
+++ b/usr.bin/more/prim.c
@@ -313,7 +313,7 @@ prepaint(pos)
off_t pos;
{
hit_eof = 0;
- forw(sc_height-1, pos, 0);
+ forw(sc_height - 1, pos, 0);
screen_trashed = 0;
}
@@ -581,26 +581,18 @@ gomark(c)
new_horiz_off = marks[c-'a'].horiz_off;
}
- /* Try to be nice about changing the horizontal scroll */
- if (!(horiz_off == NO_HORIZ_OFF && new_horiz_off <= sc_width)) {
+ /* Try to be nice about changing the horizontal scroll and wrapping */
+ if (new_horiz_off > sc_width / 3 + horiz_off) {
/*
- * We're going to have to change the horiz_off, even if
- * it's currently set to NO_HORIZ_OFF: if we don't change
- * horiz_off the bookmarked location won't show on the screen.
+ * We should change horiz_off: if we don't change horiz_off
+ * the bookmarked location won't be readily visible.
*/
- if (horiz_off != new_horiz_off) {
- /* We'll need to repaint(), too... */
- horiz_off = new_horiz_off;
- prepaint(pos);
- } else {
- /* No need to repaint. */
- jump_loc(pos);
- }
+ horiz_off = new_horiz_off;
+ prepaint(pos);
} else {
/*
- * The user doesn't want horizontal scrolling, and we can
- * fortunately honour the bookmark request without doing
- * any horizontal scrolling.
+ * We can honour the bookmark request without doing any
+ * horizontal scrolling.
*/
jump_loc(pos);
}
diff --git a/usr.bin/more/screen.c b/usr.bin/more/screen.c
index 1d0f41e..970b46f 100644
--- a/usr.bin/more/screen.c
+++ b/usr.bin/more/screen.c
@@ -102,7 +102,6 @@ int ignaw; /* Terminal ignores \n immediately after wrap */
int retain_below; /* Terminal retains text below the screen */
int erase_char, kill_char, werase_char;
int sc_width, sc_height = -1; /* Height & width of screen */
-int sc_window = -1; /* window size for forward and backward */
int bo_width, be_width; /* Printing width of boldface sequences */
int ul_width, ue_width; /* Printing width of underline sequences */
int so_width, se_width; /* Printing width of standout sequences */
@@ -294,6 +293,8 @@ get_term()
sc_width = tgetnum("co");
if (sc_width < 0)
sc_width = 80;
+ (void) setvari("_sc_height", (long) sc_height - 1);
+ (void) setvari("_sc_width", (long) sc_width);
auto_wrap = tgetflag("am");
ignaw = tgetflag("xn");
diff --git a/usr.bin/more/signal.c b/usr.bin/more/signal.c
index b046d52..c8be3af 100644
--- a/usr.bin/more/signal.c
+++ b/usr.bin/more/signal.c
@@ -65,7 +65,6 @@ extern int sc_width, sc_height;
extern int screen_trashed;
extern int lnloop;
extern int linenums;
-extern int scroll;
extern volatile int reading;
#ifdef SIGTSTP
@@ -181,7 +180,8 @@ psignals()
get_term();
if (sc_width != old_width || sc_height != old_height)
{
- scroll = (sc_height + 1) / 2;
+ (void) setvari("_sc_width", (long) sc_width);
+ (void) setvari("_sc_height", (long) sc_height - 1);
screen_trashed = 1;
}
}
diff --git a/usr.bin/more/ttyin.c b/usr.bin/more/ttyin.c
index 51376cd..26f8f96 100644
--- a/usr.bin/more/ttyin.c
+++ b/usr.bin/more/ttyin.c
@@ -74,6 +74,6 @@ getchr()
*/
quit();
}
- } while (result != 1);
+ } while (result != 1 || c == 0);
return ((unsigned char)c);
}
OpenPOWER on IntegriCloud