head 1.18; access; symbols RELENG_4_0_0_RELEASE:1.17 RELENG_4:1.17.0.2 RELENG_4_BP:1.17 RELENG_3_4_0_RELEASE:1.10.2.3 RELENG_3_3_0_RELEASE:1.10.2.3 RELENG_3_2_PAO:1.10.0.4 RELENG_3_2_PAO_BP:1.10 RELENG_3_2_0_RELEASE:1.10 RELENG_3_1_0_RELEASE:1.10 RELENG_3:1.10.0.2 RELENG_3_BP:1.10 RELENG_2_2_8_RELEASE:1.6.2.2 RELENG_3_0_0_RELEASE:1.10 RELENG_2_2_7_RELEASE:1.6.2.2 RELENG_2_2_6_RELEASE:1.6.2.1 RELENG_2_2_5_RELEASE:1.6.2.1 bsd_44_lite_2:1.1.1.1 RELENG_2_2_2_RELEASE:1.6.2.1 RELENG_2_2_1_RELEASE:1.6.2.1 RELENG_2_2_0_RELEASE:1.6.2.1 RELENG_2_1_7_RELEASE:1.4 RELENG_2_1_6_1_RELEASE:1.4 RELENG_2_1_6_RELEASE:1.4 RELENG_2_2:1.6.0.2 RELENG_2_2_BP:1.6 RELENG_2_1_5_RELEASE:1.4 RELENG_2_1_0_RELEASE:1.4 RELENG_2_1_0:1.4.0.4 RELENG_2_1_0_BP:1.4 RELENG_2_0_5_RELEASE:1.4 RELENG_2_0_5:1.4.0.2 RELENG_2_0_5_BP:1.4 RELENG_2_0_5_ALPHA:1.3 RELEASE_2_0:1.3 BETA_2_0:1.3 ALPHA_2_0:1.3.0.2 bsd_44_lite:1.1.1.1 CSRG:1.1.1; locks; comment @ * @; 1.18 date 2000.05.12.03.53.38; author hoek; state Exp; branches; next 1.17; 1.17 date 2000.01.08.18.11.05; author hoek; state Exp; branches; next 1.16; 1.16 date 99.12.26.03.03.03; author hoek; state Exp; branches; next 1.15; 1.15 date 99.09.03.22.31.13; author hoek; state Exp; branches; next 1.14; 1.14 date 99.08.28.01.04.14; author peter; state Exp; branches; next 1.13; 1.13 date 99.06.05.20.27.08; author hoek; state Exp; branches; next 1.12; 1.12 date 99.06.01.20.02.31; author hoek; state Exp; branches; next 1.11; 1.11 date 99.05.30.18.06.52; author hoek; state Exp; branches; next 1.10; 1.10 date 98.07.04.01.22.07; author hoek; state Exp; branches 1.10.2.1; next 1.9; 1.9 date 98.07.03.17.12.10; author hoek; state Exp; branches; next 1.8; 1.8 date 98.06.14.16.03.39; author steve; state Exp; branches; next 1.7; 1.7 date 97.03.02.18.55.41; author joerg; state Exp; branches; next 1.6; 1.6 date 95.10.05.22.26.43; author mpp; state Exp; branches 1.6.2.1; next 1.5; 1.5 date 95.10.04.09.33.39; author peter; state Exp; branches; next 1.4; 1.4 date 95.05.30.06.32.29; author rgrimes; state Exp; branches; next 1.3; 1.3 date 94.10.09.15.40.03; author ache; state Exp; branches; next 1.2; 1.2 date 94.09.22.01.19.06; author ache; state Exp; branches; next 1.1; 1.1 date 94.05.27.12.30.44; author rgrimes; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 94.05.27.12.30.45; author rgrimes; state Exp; branches; next ; 1.6.2.1 date 97.03.07.09.03.59; author joerg; state Exp; branches; next 1.6.2.2; 1.6.2.2 date 98.07.17.04.19.40; author jkh; state Exp; branches; next ; 1.10.2.1 date 99.07.28.06.09.49; author hoek; state Exp; branches; next 1.10.2.2; 1.10.2.2 date 99.07.28.06.17.10; author hoek; state Exp; branches; next 1.10.2.3; 1.10.2.3 date 99.08.29.15.31.13; author peter; state Exp; branches; next ; desc @@ 1.18 log @Fix an uncommon bug that would cause us to stop accepting input if the user entered a command that filled exactly the remaining screen width. @ text @/* * 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. * * 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[] = "@@(#)command.c 8.1 (Berkeley) 6/6/93"; #endif /* not lint */ #ifndef lint static const char rcsid[] = "$FreeBSD: src/usr.bin/more/command.c,v 1.17 2000/01/08 18:11:05 hoek Exp $"; #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 #include #include #include #include #include #include "less.h" #include "pathnames.h" extern int erase_char, kill_char, werase_char; extern int sigs; extern int quit_at_eof; extern int hit_eof; 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 screen_trashed; /* The screen has been overwritten */ static int cmd_col; /* Current screen column when accepting input */ static cmd_char(), cmd_erase(), getcc(); /***************************************************************************** * * Functions for reading-in user input. * */ static int biggetinputhack_f; /* biggetinputhack() * * Performs as advertised. */ biggetinputhack() { biggetinputhack_f = 1; } /* * 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. Returns true if the user aborted the input and * returns false otherwise. */ int 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 0; } if (c == READ_INTR || cmd_char(c, buf, &bufcur, buf + bufsiz - 1)) { /* input cancelled */ if (bufsiz) *buf = '\0'; return 1; } if (biggetinputhack_f) { biggetinputhack_f = 0; *bufcur = '\0'; return 0; } } } /* * 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; /* One after the last address available in the buffer. * No character will be placed into *bufend. */ { if (c == erase_char) return(cmd_erase(bufbeg, bufcur)); /* in this order, in case werase == erase_char */ if (c == werase_char) { 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 *bufcur == bufbeg; } if (c == kill_char) { while (!cmd_erase(bufbeg, bufcur)); return 1; } /* * No room in the command buffer, or no room on the screen; * 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. */ assert (*bufcur <= bufend); if (*bufcur == bufend || cmd_col >= sc_width - 3) bell(); else { *(*bufcur)++ = c; if (CONTROL_CHAR(c)) { putchr('^'); cmd_col++; c &= ~0200; c = CARAT_CHAR(c); } putchr(c); cmd_col++; } 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, ispipe; extern char *current_name, *firstsearch, *next_name; off_t len, pos, ch_length(), position(), forw_line(); char pbuf[40]; /* * if nothing is displayed yet, display starting from line 1; * 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 ; #endif if (!firstsearch || !search(1, firstsearch, 1, 1)) jump_back(1); } else if (screen_trashed) repaint(); /* if no -e flag and we've hit EOF on the last file, quit. */ if (!quit_at_eof && hit_eof && curr_ac + 1 >= ac) quit(); /* 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 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) - 1, " file %d/%d", curr_ac + 1, ac); putstr(pbuf); } if (linenums) { (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) - 1, " byte %qd", pos); putstr(pbuf); if (!ispipe && (len = ch_length())) { (void)snprintf(pbuf, sizeof(pbuf) - 1, "/%qd pct %qd%%", len, ((100 * pos) / len)); putstr(pbuf); } } so_exit(); } else { so_enter(); putstr(current_name); if (hit_eof) if (next_name) { putstr(": END (next file: "); putstr(next_name); putstr(")"); } else putstr(": END"); else if (!ispipe && (pos = position(BOTTOM)) != NULL_POSITION && (len = ch_length())) { (void)snprintf(pbuf, sizeof(pbuf) - 1, " (%qd%%)", ((100 * pos) / len)); putstr(pbuf); } so_exit(); } /* * XXX This isn't correct, but until we get around to reworking * the whole prompt stuff the way we want it to be, this hack * is necessary to prevent input from being blocked if getinput() * is called and the user enters an input that fills the cmd * buffer (or reaches the far rightside end of the screen). */ cmd_col = 0; return 1; } /* * 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...) */ { if (nostatprompt) longprompt = 0; else longprompt = 1; } /***************************************************************************** * * 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); } /* * Prints an error message and clears the current error. */ handle_error() { if (erreur == E_OK) return; bell(); if (errstr) error(errstr); else error(deferr[erreur]); erreur = E_OK; errstr = NULL; } /* * 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; { extern int any_display; 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(); ungotcc = ch; } 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. }} */ /* 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() { 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 (;;) { /* * See if any signals need processing. */ if (sigs) psignals(); /* * Display prompt and generally get setup. Don't display the * prompt if we are already in the middle of accepting a * set of characters. */ if (!*inbuf && !prompt()) { next_file(1); continue; } c = getcc(); /* Check sigs here --- getcc() may have given us READ_INTR */ if (sigs) { /* terminate any current macro */ *inbuf = '\0'; incur = inbuf; continue; /* process the sigs */ } if (Nstate == GETTING && !isdigit(c) && c != erase_char && c != werase_char && c != kill_char) { /* * Mark the end of an input number N, if any. */ if (!*inbuf) { /* We never actually got an input number */ Nstate = NOTGOTTEN; } else { numberN = atol(inbuf); Nstate = GOTTEN; } *inbuf = '\0'; incur = inbuf; } (void) cmd_char(c, inbuf, &incur, inbuf + sizeof(inbuf) - 1); *incur = '\0'; if (*inbuf) prmpt(inbuf); else Nstate = GETTING; /* abort command */ if (Nstate == GETTING) { /* Still reading in the number N ... don't want to * try running the macro expander. */ continue; } else { /* Try expanding the macro */ switch (runmacro(inbuf, numberN, Nstate)) { case TOOMACRO: break; case BADMACRO: case NOMACRO: case BADCOMMAND: handle_error(); /* fallthrough */ case OK: /* recock */ *inbuf = '\0'; incur = inbuf; Nstate = GETTING; 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; char *base; int linenumber; char buf[MAXPATHLEN * 2 + 20], *getenv(); if (editor == NULL) { editor = getenv("EDITOR"); /* default editor is vi */ if (editor == NULL || *editor == '\0') editor = _PATH_VI; /* check last component in case of full path */ base = strrchr(editor, '/'); if (!base) base = editor; else base++; /* emacs also accepts vi-style +nnnn */ if (strncmp(base, "vi", 2) == 0 || strcmp(base, "emacs") == 0) dolinenumber = 1; else dolinenumber = 0; } /* * XXX Can't just use currline(MIDDLE) since that might be NULL_POSITION * if we are editting a short file or some kind of search positioned * us near the last line. It's not clear what currline() should do * in those circumstances, but as of this writing, it doesn't do * anything reasonable from our perspective. The currline(MIDDLE) * never had the desired results for an editfile() after a search() * anyways. Note, though, that when vi(1) starts its editting, it * positions the focus line in the middle of the screen, not the top. * * I think what is needed is some kind of setfocus() and getfocus() * function. This could put the focussed line in the middle, top, * or wherever as per the user's wishes, and allow things like us * to getfocus() the correct file-position/line-number. A search would * then search forward (or backward) from the current focus position, * etc. * * currline() doesn't belong. */ if (position(MIDDLE) == NULL_POSITION) linenumber = currline(TOP); else linenumber = currline(MIDDLE); if (dolinenumber && linenumber) (void)snprintf(buf, sizeof(buf), "%s +%d %s", editor, linenumber, current_file); else (void)snprintf(buf, sizeof(buf), "%s %s", editor, current_file); lsystem(buf); } showlist() { extern int sc_width; register int indx, width; int len; char *p; if (ac <= 0) { error("No files provided as arguments."); return; } for (width = indx = 0; indx < ac;) { p = strcmp(av[indx], "-") ? av[indx] : "stdin"; len = strlen(p) + 1; if (curr_ac == indx) len += 2; if (width + len + 1 >= sc_width) { if (!width) { if (curr_ac == indx) putchr('['); putstr(p); if (curr_ac == indx) putchr(']'); ++indx; } width = 0; putchr('\n'); continue; } if (width) putchr(' '); if (curr_ac == indx) putchr('['); putstr(p); if (curr_ac == indx) putchr(']'); width += len; ++indx; } putchr('\n'); error((char *)NULL); } @ 1.17 log @Correctly backspace over number N that preceeds macros. @ text @d42 1 a42 1 "$FreeBSD: src/usr.bin/more/command.c,v 1.16 1999/12/26 03:03:03 hoek Exp $"; d397 10 @ 1.16 log @Allow excessive backspacing to correctly abort an input (most significantly a search string input). @ text @d42 1 a42 1 "$FreeBSD: src/usr.bin/more/command.c,v 1.15 1999/09/03 22:31:13 hoek Exp $"; d152 2 a153 3 char *bufend; /* The last spot available in the buffer --- remember * to leave one after bufend for the '\0'! (You must * add the '\0' yourself!!) */ d585 5 a589 2 if (Nstate == GETTING && !isdigit(c)) { /* mark the end of an input number N, if any */ d601 1 a601 1 cmd_char(c, inbuf, &incur, inbuf + sizeof(inbuf) - 1); d603 4 a606 1 if (*inbuf) prmpt(inbuf); @ 1.15 log @Thorough revamp of how input commands are processed. This allows customization of user keys (documentation pending). The only key whose semantics have changed is the capital 'N' key, which now performs a repeat-search in the opposite direction (just like in vi). This commit is a little bulkier than what I had originally planned. I'm not completely happy with the direction it went, but it's definately an improvement, and the alternative is to continue becoming irrelevant compared to GNU less. (Does anyone even _use_ /usr/bin/more these days?) @ text @d42 1 a42 1 "$FreeBSD: src/usr.bin/more/command.c,v 1.14 1999/08/28 01:04:14 peter Exp $"; d102 2 a103 1 * temporary prompt prompt. d105 1 d122 1 a122 1 return; d128 1 a128 1 return; d133 1 a133 1 return; @ 1.14 log @$Id$ -> $FreeBSD$ @ text @d3 1 d42 1 a42 1 "$FreeBSD$"; d45 6 d53 1 d55 1 a61 4 #define NO_MCA 0 #define MCA_DONE 1 #define MCA_MORE 2 a62 1 extern int ispipe; d66 1 d68 4 a71 3 extern int sc_height; extern int sc_window; extern int horiz_off; a74 1 extern int scroll; d77 1 a77 8 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) */ d79 1 a79 2 #define CMD_RESET cp = cmdbuf /* reset command buffer to empty */ #define CMD_EXEC lower_left(); flush() a80 11 /* 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); d82 7 a88 6 /* erase an extra character, for the carat. */ c = *--cp; if (CONTROL_CHAR(c)) { backspace(); --cmd_col; } d90 7 a96 3 backspace(); --cmd_col; return(0); d99 9 a107 4 /* set up the display to start a new multi-character command. */ start_mca(action, prompt) int action; char *prompt; d109 25 a133 5 lower_left(); clear_eol(); putstr(prompt); cmd_col = strlen(prompt); mca = action; d137 7 a143 2 * process a single character of a multi-character command, such as * a number, or the pattern of a search command. d145 8 a152 3 static cmd_char(c) int c; d155 1 a155 1 return(cmd_erase()); d158 7 a164 4 if (cp > cmdbuf) { while (isspace(cp[-1]) && !cmd_erase()); while (!isspace(cp[-1]) && !cmd_erase()); while (isspace(cp[-1]) && !cmd_erase()); d166 1 a166 1 return(cp == cmdbuf); d169 2 a170 2 while (!cmd_erase()); return(1); d172 1 d175 3 a177 2 * {{ Could get fancy here; maybe shift the displayed line * and make room for more chars, like ksh. }} d179 2 a180 1 if (cp >= &cmdbuf[sizeof(cmdbuf)-1] || cmd_col >= sc_width-3) d183 1 a183 1 *cp++ = c; d193 102 a294 1 return(0); d297 10 d309 1 a309 1 extern int linenums, short_file; d319 4 d324 2 a325 1 return(0); d339 1 d344 4 a347 3 * 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. d353 1 a353 1 (void)snprintf(pbuf, sizeof(pbuf), d358 1 a358 1 (void)snprintf(pbuf, sizeof(pbuf), d362 2 d365 2 a366 1 (void)snprintf(pbuf, sizeof(pbuf), " byte %qd", pos); d369 1 a369 1 (void)snprintf(pbuf, sizeof(pbuf), a374 1 longprompt = 0; d390 1 a390 1 (void)snprintf(pbuf, sizeof(pbuf), d396 14 a409 1 return(1); d412 29 a440 3 /* get command character. */ static getcc() d442 2 a443 3 extern int cmdstack; int ch; off_t position(); d445 7 a451 22 /* 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()); d454 4 a457 3 /* execute a multicharacter command. */ static exec_mca() d459 3 a461 27 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; } d464 10 a473 4 /* add a character to a multi-character command. */ static mca_char(c) int c; d475 5 a479 5 switch (mca) { case 0: /* not in a multicharacter command. */ case A_PREFIX: /* in the prefix of a command. */ return(NO_MCA); case A_DIGIT: d481 11 a491 2 * Entering digits of a number. * Terminated by a non-digit. d493 20 a512 13 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; d514 1 d516 11 a526 7 /* * Any other multicharacter command * is terminated by a newline. */ if (c == '\n' || c == '\r') { exec_mca(); return(MCA_DONE); d528 2 a530 3 /* append the char to the command buffer. */ if (cmd_char(c)) return(MCA_DONE); d532 7 a538 2 return(MCA_MORE); } d542 1 d547 7 a553 8 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; d555 1 a556 3 mca = 0; number = 0; d564 3 a566 1 * Display prompt and accept a character. d568 1 a568 2 CMD_RESET; if (!prompt()) { d572 1 a572 1 noprefix(); d575 8 a582 2 again: if (sigs) continue; d584 2 a585 26 /* * 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; } d587 6 a592 69 /* 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(); d594 10 a603 28 /* 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; d605 4 a608 38 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."); d610 8 a617 17 } 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) a618 72 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) 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); a619 6 putchr(c); c = getcc(); goto again; default: bell(); break; d621 1 a621 1 } d624 8 d634 1 @ 1.13 log @- When trying to decide if $EDITOR == "vi", match against only the first two characters of $EDITOR. This allows things like "vim" and "vi -G" (although nvi would fail...oh well). - Avoid certain cases where the editor is passed an invalid line number. @ text @d41 1 a41 1 "$Id: command.c,v 1.12 1999/06/01 20:02:31 hoek Exp $"; @ 1.12 log @Grok gtags too. @ text @d41 1 a41 1 "$Id: command.c,v 1.11 1999/05/30 18:06:52 hoek Exp $"; d668 1 a668 1 int c; d686 1 a686 1 if (strcmp(base, "vi") == 0 || strcmp(base, "emacs") == 0) d691 24 a714 1 if (dolinenumber && (c = currline(MIDDLE))) d716 1 a716 1 "%s +%d %s", editor, c, current_file); @ 1.11 log @General code cleanup [incomplete]. Make the arrow keys work. @ text @d41 1 a41 1 "$Id$"; d367 1 d573 18 @ 1.10 log @Fix bug from last commit: don't SEGV when reading as part of a pipe. @ text @d39 5 d45 2 a47 1 #include d49 2 a50 1 #include d65 1 a68 1 extern int quitting; d192 7 d365 2 d378 1 a378 1 if (sigs) { d380 1 a380 3 if (quitting) quit(); } d446 28 @ 1.10.2.1 log @commit-notes @ text @a38 5 #ifndef lint static const char rcsid[] = "$Id: command.c,v 1.13 1999/06/05 20:27:08 hoek Exp $"; #endif /* not lint */ d40 1 a40 1 a41 1 #include d43 1 a43 2 #include "less.h" a57 1 extern int horiz_off; d61 1 a184 7 /* * 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. */ a350 3 static int default_hscroll = 1; static int saved_horiz_off = NO_HORIZ_OFF; extern char *tagfile; d362 1 a362 1 if (sigs) d364 3 a366 1 a432 28 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; a529 18 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; d607 1 a607 1 int linenumber; d625 1 a625 1 if (strncmp(base, "vi", 2) == 0 || strcmp(base, "emacs") == 0) d630 1 a630 24 /* * XXX Can't just use currline(MIDDLE) since that might be NULL_POSITION * if we are editting a short file or some kind of search positioned * us near the last line. It's not clear what currline() should do * in those circumstances, but as of this writing, it doesn't do * anything reasonable from our perspective. The currline(MIDDLE) * never had the desired results for an editfile() after a search() * anyways. Note, though, that when vi(1) starts its editting, it * positions the focus line in the middle of the screen, not the top. * * I think what is needed is some kind of setfocus() and getfocus() * function. This could put the focussed line in the middle, top, * or wherever as per the user's wishes, and allow things like us * to getfocus() the correct file-position/line-number. A search would * then search forward (or backward) from the current focus position, * etc. * * currline() doesn't belong. */ if (position(MIDDLE) == NULL_POSITION) linenumber = currline(TOP); else linenumber = currline(MIDDLE); if (dolinenumber && linenumber) d632 1 a632 1 "%s +%d %s", editor, linenumber, current_file); @ 1.10.2.2 log @[Whoops... used ci -m instead of -F on the last commit...] MFC: misc incidental cleanup, gtags support, better line-number passing to $EDITOR, working arrow keys, $Id$ to manpage @ text @d41 1 a41 1 "$Id: command.c,v 1.10.2.1 1999/07/28 06:09:49 hoek Exp $"; @ 1.10.2.3 log @$Id$ -> $FreeBSD$ @ text @d41 1 a41 1 "$FreeBSD$"; @ 1.9 log @Prevent recursive help)elp)elping. @ text @d515 1 a515 1 if (!strcmp(_PATH_HELPFILE, av[curr_ac])) { @ 1.8 log @sprintf -> snprintf to avoid potential buffer overflow. PR: 6907 Submitted by: Archie Cobbs @ text @d60 1 d515 4 a640 1 extern char **av; @ 1.7 log @Make more not immediately exit on a short file, if the -e option is given. This makes more -e basically usable at all when your termcap entry supports an alternate screen buffer (like xterm-r6). I wonder whether we should make more -e the default. Oure more seems to be the only one on the world with this funny behaviour. 2.2 candidate Submitted by: dawes@@rf900.physics.usyd.edu.au (David Dawes) @ text @d188 2 a189 1 (void)sprintf(pbuf, " file %d/%d", curr_ac + 1, ac); d193 2 a194 1 (void)sprintf(pbuf, " line %d", currline(BOTTOM)); d198 1 a198 1 (void)sprintf(pbuf, " byte %qd", pos); d201 2 a202 2 (void)sprintf(pbuf, "/%qd pct %qd%%", len, ((100 * pos) / len)); d223 2 a224 1 (void)sprintf(pbuf, " (%qd%%)", ((100 * pos) / len)); d626 2 a627 1 (void)sprintf(buf, "%s +%d %s", editor, c, current_file); d629 1 a629 1 (void)sprintf(buf, "%s %s", editor, current_file); @ 1.6 log @Fix a bug in a recent commit that broke more so that it would no longer properly detect when "vi" was being called, and thus would not call vi with the "+line#" argument. @ text @d177 1 a177 1 if ((!quit_at_eof || short_file) && hit_eof && curr_ac + 1 >= ac) @ 1.6.2.1 log @YAMFC (rev 1.7, and 1.4 resp: don't exit immediately on short file if -e is given, so alternate screen buffers can be used at all) @ text @d177 1 a177 1 if (!quit_at_eof && hit_eof && curr_ac + 1 >= ac) @ 1.6.2.2 log @MFC: doc fixes, enhancements to locate & more, new tail functionality. @ text @a59 1 extern char **av; d188 1 a188 2 (void)snprintf(pbuf, sizeof(pbuf), " file %d/%d", curr_ac + 1, ac); d192 1 a192 2 (void)snprintf(pbuf, sizeof(pbuf), " line %d", currline(BOTTOM)); d196 1 a196 1 (void)snprintf(pbuf, sizeof(pbuf), " byte %qd", pos); d199 2 a200 2 (void)snprintf(pbuf, sizeof(pbuf), "/%qd pct %qd%%", len, ((100 * pos) / len)); d221 1 a221 2 (void)snprintf(pbuf, sizeof(pbuf), " (%qd%%)", ((100 * pos) / len)); a510 4 if (ac > 0 && !strcmp(_PATH_HELPFILE, av[curr_ac])) { error("Already viewing help."); break; } d623 1 a623 2 (void)snprintf(buf, sizeof(buf), "%s +%d %s", editor, c, current_file); d625 1 a625 1 (void)snprintf(buf, sizeof(buf), "%s %s", editor, current_file); d632 1 @ 1.5 log @Make more pass +linenumber to vi (and emacs), even if $EDITOR is used. Closes PR#441, but with a more generic method. @ text @d613 2 @ 1.4 log @Remove trailing whitespace. @ text @d42 1 d598 1 d604 3 a606 2 /* pass the line number to vi */ if (editor == NULL || *editor == '\0') { d608 8 a616 1 } @ 1.3 log @Back out part of ctype fixes, unneded with new ctypoe @ text @d490 1 a490 1 start_mca(action, d500 1 a500 1 start_mca(last_mca, d503 1 a503 1 start_mca(last_mca, @ 1.2 log @1) Make ctype-aware expect chars in range 0200-0204 treated as controls in any case. 2) Fix bug with incorrect column position when standouts occurse 3) Fix bug when last standouted char cause clearing next line in standout @ text @d88 1 a88 1 c = *--cp & 0xff; d124 3 a126 3 while (isspace(cp[-1] & 0xff) && !cmd_erase()); while (!isspace(cp[-1] & 0xff) && !cmd_erase()); while (isspace(cp[-1] & 0xff) && !cmd_erase()); d279 1 a279 1 for (p = cmdbuf; isspace(*p & 0xff); ++p); d283 1 a283 1 for (p = cmdbuf; isspace(*p & 0xff); ++p); @ 1.1 log @Initial revision @ text @d79 1 d88 2 a89 1 if (CONTROL_CHAR(*--cp)) { d124 3 a126 3 while (isspace(cp[-1]) && !cmd_erase()); while (!isspace(cp[-1]) && !cmd_erase()); while (isspace(cp[-1]) && !cmd_erase()); d146 1 d279 1 a279 1 for (p = cmdbuf; isspace(*p); ++p); d283 1 a283 1 for (p = cmdbuf; isspace(*p); ++p); d579 1 @ 1.1.1.1 log @BSD 4.4 Lite Usr.bin Sources @ text @@