Index: head/contrib/less/command.c =================================================================== --- head/contrib/less/command.c (revision 89021) +++ head/contrib/less/command.c (revision 89022) @@ -1,1552 +1,1613 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * User-level command processor. */ #include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#endif #include "position.h" #include "option.h" #include "cmd.h" extern int erase_char, kill_char; extern int sigs; extern int quit_at_eof; extern int quit_if_one_screen; extern int squished; extern int hit_eof; extern int sc_width; extern int sc_height; extern int swindow; extern int jump_sline; extern int quitting; extern int wscroll; extern int top_scroll; extern int ignore_eoi; extern int secure; extern int hshift; extern int show_attn; extern int more_mode; extern char *every_first_cmd; extern char *curr_altfilename; extern char version[]; extern struct scrpos initial_scrpos; extern IFILE curr_ifile; extern void constant *ml_search; extern void constant *ml_examine; #if SHELL_ESCAPE || PIPEC extern void constant *ml_shell; #endif #if EDITOR extern char *editor; extern char *editproto; #endif extern int screen_trashed; /* The screen has been overwritten */ extern int shift_count; static char ungot[UNGOT_SIZE]; static char *ungotp = NULL; #if SHELL_ESCAPE static char *shellcmd = NULL; /* For holding last shell command for "!!" */ #endif static int mca; /* The multicharacter command (action) */ static int search_type; /* The previous type of search */ static int number; /* The number typed by the user */ static char optchar; static int optflag; static int optgetname; static POSITION bottompos; #if PIPEC static char pipec; #endif static void multi_search(); /* * Move the cursor to lower left before executing a command. * This looks nicer if the command takes a long time before * updating the screen. */ static void cmd_exec() { clear_attn(); lower_left(); flush(); } /* * Set up the display to start a new multi-character command. */ static void start_mca(action, prompt, mlist, cmdflags) int action; char *prompt; void *mlist; int cmdflags; { mca = action; clear_cmd(); cmd_putstr(prompt); set_mlist(mlist, cmdflags); } public int in_mca() { return (mca != 0 && mca != A_PREFIX); } /* * Set up the display to start a new search command. */ static void mca_search() { if (search_type & SRCH_FORW) mca = A_F_SEARCH; else mca = A_B_SEARCH; clear_cmd(); if (search_type & SRCH_NO_MATCH) cmd_putstr("Non-match "); if (search_type & SRCH_FIRST_FILE) cmd_putstr("First-file "); if (search_type & SRCH_PAST_EOF) cmd_putstr("EOF-ignore "); if (search_type & SRCH_NO_MOVE) cmd_putstr("Keep-pos "); if (search_type & SRCH_NO_REGEX) cmd_putstr("Regex-off "); if (search_type & SRCH_FORW) cmd_putstr("/"); else cmd_putstr("?"); set_mlist(ml_search, 0); } /* * Set up the display to start a new toggle-option command. */ static void mca_opt_toggle() { int no_prompt; int flag; char *dash; no_prompt = (optflag & OPT_NO_PROMPT); flag = (optflag & ~OPT_NO_PROMPT); dash = (flag == OPT_NO_TOGGLE) ? "_" : "-"; mca = A_OPT_TOGGLE; clear_cmd(); cmd_putstr(dash); if (optgetname) cmd_putstr(dash); if (no_prompt) cmd_putstr("(P)"); switch (flag) { case OPT_UNSET: cmd_putstr("+"); break; case OPT_SET: cmd_putstr("!"); break; } set_mlist(NULL, 0); } /* * Execute a multicharacter command. */ static void exec_mca() { register char *cbuf; cmd_exec(); cbuf = get_cmdbuf(); switch (mca) { case A_F_SEARCH: case A_B_SEARCH: multi_search(cbuf, number); break; case A_FIRSTCMD: /* * Skip leading spaces or + signs in the string. */ while (*cbuf == '+' || *cbuf == ' ') cbuf++; if (every_first_cmd != NULL) free(every_first_cmd); if (*cbuf == '\0') every_first_cmd = NULL; else every_first_cmd = save(cbuf); break; case A_OPT_TOGGLE: toggle_option(optchar, cbuf, optflag); optchar = '\0'; break; case A_F_BRACKET: match_brac(cbuf[0], cbuf[1], 1, number); break; case A_B_BRACKET: match_brac(cbuf[1], cbuf[0], 0, number); break; #if EXAMINE case A_EXAMINE: if (secure) break; edit_list(cbuf); + /* If tag structure is loaded then clean it up. */ + cleantags(); break; #endif #if SHELL_ESCAPE case A_SHELL: /* * !! just uses whatever is in shellcmd. * Otherwise, copy cmdbuf to shellcmd, * expanding any special characters ("%" or "#"). */ if (*cbuf != '!') { if (shellcmd != NULL) free(shellcmd); shellcmd = fexpand(cbuf); } if (secure) break; if (shellcmd == NULL) lsystem("", "!done"); else lsystem(shellcmd, "!done"); break; #endif #if PIPEC case A_PIPE: if (secure) break; (void) pipe_mark(pipec, cbuf); error("|done", NULL_PARG); break; #endif } } /* * Add a character to a multi-character command. */ static int mca_char(c) int c; { char *p; int flag; char buf[3]; PARG parg; switch (mca) { case 0: /* * Not in a multicharacter command. */ return (NO_MCA); case A_PREFIX: /* * In the prefix of a command. * This not considered a multichar command * (even tho it uses cmdbuf, etc.). * It is handled in the commands() switch. */ return (NO_MCA); case A_DIGIT: /* * Entering digits of a number. * Terminated by a non-digit. */ if ((c < '0' || c > '9') && - editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE) == A_INVALID) + editchar(c, EC_PEEK|EC_NOHISTORY|EC_NOCOMPLETE|EC_NORIGHTLEFT) == A_INVALID) { /* * Not part of the number. * Treat as a normal command character. */ number = cmd_int(); mca = 0; cmd_accept(); return (NO_MCA); } break; case A_OPT_TOGGLE: /* * Special case for the TOGGLE_OPTION command. * If the option letter which was entered is a * single-char option, execute the command immediately, * so user doesn't have to hit RETURN. * If the first char is + or -, this indicates * OPT_UNSET or OPT_SET respectively, instead of OPT_TOGGLE. * "--" begins inputting a long option name. */ if (optchar == '\0' && len_cmdbuf() == 0) { flag = (optflag & ~OPT_NO_PROMPT); if (flag == OPT_NO_TOGGLE) { switch (c) { case '_': /* "__" = long option name. */ optgetname = TRUE; mca_opt_toggle(); return (MCA_MORE); } } else { switch (c) { case '+': /* "-+" = UNSET. */ optflag = (flag == OPT_UNSET) ? OPT_TOGGLE : OPT_UNSET; mca_opt_toggle(); return (MCA_MORE); case '!': /* "-!" = SET */ optflag = (flag == OPT_SET) ? OPT_TOGGLE : OPT_SET; mca_opt_toggle(); return (MCA_MORE); case CONTROL('P'): optflag ^= OPT_NO_PROMPT; mca_opt_toggle(); return (MCA_MORE); case '-': /* "--" = long option name. */ optgetname = TRUE; mca_opt_toggle(); return (MCA_MORE); } } } if (optgetname) { /* * We're getting a long option name. * See if we've matched an option name yet. * If so, display the complete name and stop * accepting chars until user hits RETURN. */ struct option *o; char *oname; int lc; if (c == '\n' || c == '\r') { /* * When the user hits RETURN, make sure * we've matched an option name, then * pretend he just entered the equivalent * option letter. */ if (optchar == '\0') { parg.p_string = get_cmdbuf(); error("There is no --%s option", &parg); return (MCA_DONE); } optgetname = FALSE; cmd_reset(); c = optchar; } else { if (optchar != '\0') { /* * Already have a match for the name. * Don't accept anything but erase/kill. */ if (c == erase_char || c == kill_char) return (MCA_DONE); return (MCA_MORE); } /* * Add char to cmd buffer and try to match * the option name. */ if (cmd_char(c) == CC_QUIT) return (MCA_DONE); p = get_cmdbuf(); lc = islower(p[0]); o = findopt_name(&p, &oname, NULL); if (o != NULL) { /* * Got a match. * Remember the option letter and * display the full option name. */ optchar = o->oletter; if (!lc && islower(optchar)) optchar = toupper(optchar); cmd_reset(); mca_opt_toggle(); for (p = oname; *p != '\0'; p++) { c = *p; if (!lc && islower(c)) c = toupper(c); if (cmd_char(c) != CC_OK) return (MCA_DONE); } } return (MCA_MORE); } } else { if (c == erase_char || c == kill_char) break; if (optchar != '\0') /* We already have the option letter. */ break; } optchar = c; if ((optflag & ~OPT_NO_PROMPT) != OPT_TOGGLE || single_char_option(c)) { toggle_option(c, "", optflag); return (MCA_DONE); } /* * Display a prompt appropriate for the option letter. */ if ((p = opt_prompt(c)) == NULL) { buf[0] = '-'; buf[1] = c; buf[2] = '\0'; p = buf; } start_mca(A_OPT_TOGGLE, p, (void*)NULL, 0); return (MCA_MORE); case A_F_SEARCH: case A_B_SEARCH: /* * Special case for search commands. * Certain characters as the first char of * the pattern have special meaning: * ! Toggle the NO_MATCH flag * * Toggle the PAST_EOF flag * @ Toggle the FIRST_FILE flag */ if (len_cmdbuf() > 0) /* * Only works for the first char of the pattern. */ break; flag = 0; switch (c) { case '*': if (more_mode) break; case CONTROL('E'): /* ignore END of file */ flag = SRCH_PAST_EOF; break; case '@': if (more_mode) break; case CONTROL('F'): /* FIRST file */ flag = SRCH_FIRST_FILE; break; case CONTROL('K'): /* KEEP position */ flag = SRCH_NO_MOVE; break; case CONTROL('R'): /* Don't use REGULAR EXPRESSIONS */ flag = SRCH_NO_REGEX; break; case CONTROL('N'): /* NOT match */ case '!': flag = SRCH_NO_MATCH; break; } if (flag != 0) { search_type ^= flag; mca_search(); return (MCA_MORE); } break; } /* * Any other multicharacter command * is terminated by a newline. */ if (c == '\n' || c == '\r') { /* * Execute the command. */ exec_mca(); return (MCA_DONE); } /* * Append the char to the command buffer. */ if (cmd_char(c) == CC_QUIT) /* * Abort the multi-char command. */ return (MCA_DONE); if ((mca == A_F_BRACKET || mca == A_B_BRACKET) && len_cmdbuf() >= 2) { /* * Special case for the bracket-matching commands. * Execute the command after getting exactly two * characters from the user. */ exec_mca(); return (MCA_DONE); } /* * Need another character. */ return (MCA_MORE); } /* * Make sure the screen is displayed. */ static void make_display() { /* * If nothing is displayed yet, display starting from initial_scrpos. */ if (empty_screen()) { if (initial_scrpos.pos == NULL_POSITION) /* * {{ Maybe this should be: * jump_loc(ch_zero(), jump_sline); * but this behavior seems rather unexpected * on the first screen. }} */ jump_loc(ch_zero(), 1); else jump_loc(initial_scrpos.pos, initial_scrpos.ln); } else if (screen_trashed) { int save_top_scroll; save_top_scroll = top_scroll; top_scroll = 1; repaint(); top_scroll = save_top_scroll; } } /* * Display the appropriate prompt. */ static void prompt() { register char *p; if (ungotp != NULL && ungotp > ungot) { /* * No prompt necessary if commands are from * ungotten chars rather than from the user. */ return; } /* * Make sure the screen is displayed. */ make_display(); bottompos = position(BOTTOM_PLUS_ONE); /* * If the -E flag is set and we've hit EOF on the last file, quit. */ if ((quit_at_eof == OPT_ONPLUS || quit_if_one_screen) && hit_eof && !(ch_getflags() & CH_HELPFILE) && next_ifile(curr_ifile) == NULL_IFILE) quit(QUIT_OK); quit_if_one_screen = FALSE; #if 0 /* This doesn't work well because some "te"s clear the screen. */ /* * If the -e flag is set and we've hit EOF on the last file, * and the file is squished (shorter than the screen), quit. */ if (quit_at_eof && squished && next_ifile(curr_ifile) == NULL_IFILE) quit(QUIT_OK); #endif +#if MSDOS_COMPILER==WIN32C + /* + * In Win32, display the file name in the window title. + */ + if (!(ch_getflags() & CH_HELPFILE)) + SetConsoleTitle(pr_expand("Less?f - %f.", 0)); +#endif /* * Select the proper prompt and display it. */ clear_cmd(); p = pr_string(); if (p == NULL) putchr(':'); else { so_enter(); putstr(p); so_exit(); } } /* * Display the less version message. */ public void dispversion() { PARG parg; parg.p_string = version; error("less %s", &parg); } /* * Get command character. * The character normally comes from the keyboard, * but may come from ungotten characters * (characters previously given to ungetcc or ungetsc). */ public int getcc() { if (ungotp == NULL) /* * Normal case: no ungotten chars, so get one from the user. */ return (getchr()); if (ungotp > ungot) /* * Return the next ungotten char. */ return (*--ungotp); /* * We have just run out of ungotten chars. */ ungotp = NULL; if (len_cmdbuf() == 0 || !empty_screen()) return (getchr()); /* * Command is incomplete, so try to complete it. */ switch (mca) { case A_DIGIT: /* * We have a number but no command. Treat as #g. */ return ('g'); case A_F_SEARCH: case A_B_SEARCH: /* * We have "/string" but no newline. Add the \n. */ return ('\n'); default: /* * Some other incomplete command. Let user complete it. */ return (getchr()); } } /* * "Unget" a command character. * The next getcc() will return this character. */ public void ungetcc(c) int c; { if (ungotp == NULL) ungotp = ungot; if (ungotp >= ungot + sizeof(ungot)) { error("ungetcc overflow", NULL_PARG); quit(QUIT_ERROR); } *ungotp++ = c; } /* * Unget a whole string of command characters. * The next sequence of getcc()'s will return this string. */ public void ungetsc(s) char *s; { register char *p; for (p = s + strlen(s) - 1; p >= s; p--) ungetcc(*p); } /* * Search for a pattern, possibly in multiple files. * If SRCH_FIRST_FILE is set, begin searching at the first file. * If SRCH_PAST_EOF is set, continue the search thru multiple files. */ static void multi_search(pattern, n) char *pattern; int n; { register int nomore; IFILE save_ifile; int changed_file; changed_file = 0; save_ifile = save_curr_ifile(); if (search_type & SRCH_FIRST_FILE) { /* * Start at the first (or last) file * in the command line list. */ if (search_type & SRCH_FORW) nomore = edit_first(); else nomore = edit_last(); if (nomore) { unsave_ifile(save_ifile); return; } changed_file = 1; search_type &= ~SRCH_FIRST_FILE; } for (;;) { n = search(search_type, pattern, n); /* * The SRCH_NO_MOVE flag doesn't "stick": it gets cleared * after being used once. This allows "n" to work after * using a /@@ search. */ search_type &= ~SRCH_NO_MOVE; if (n == 0) { /* * Found it. */ unsave_ifile(save_ifile); return; } if (n < 0) /* * Some kind of error in the search. * Error message has been printed by search(). */ break; if ((search_type & SRCH_PAST_EOF) == 0) /* * We didn't find a match, but we're * supposed to search only one file. */ break; /* * Move on to the next file. */ if (search_type & SRCH_FORW) nomore = edit_next(1); else nomore = edit_prev(1); if (nomore) break; changed_file = 1; } /* * Didn't find it. * Print an error message if we haven't already. */ if (n > 0) error("Pattern not found", NULL_PARG); if (changed_file) { /* * Restore the file we were originally viewing. */ reedit_ifile(save_ifile); } } /* * Main command processor. * Accept and execute commands until a quit command. */ public void commands() { register int c; register int action; register char *cbuf; int newaction; int save_search_type; char *extra; char tbuf[2]; PARG parg; IFILE old_ifile; IFILE new_ifile; + char *tagfile; search_type = SRCH_FORW; wscroll = (sc_height + 1) / 2; newaction = A_NOACTION; for (;;) { mca = 0; cmd_accept(); number = 0; optchar = '\0'; /* * See if any signals need processing. */ if (sigs) { psignals(); if (quitting) quit(QUIT_SAVED_STATUS); } /* * See if window size changed, for systems that don't * generate SIGWINCH. */ check_winch(); /* * Display prompt and accept a character. */ cmd_reset(); prompt(); if (sigs) continue; if (newaction == A_NOACTION) c = getcc(); again: if (sigs) continue; if (newaction != A_NOACTION) { action = newaction; newaction = A_NOACTION; } else { /* * If we are in a multicharacter command, call mca_char. * Otherwise we call fcmd_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; } /* * Decode the command character and decide what to do. */ if (mca) { /* * We're in a multichar command. * Add the character to the command buffer * and display it on the screen. * If the user backspaces past the start * of the line, abort the command. */ if (cmd_char(c) == CC_QUIT || len_cmdbuf() == 0) continue; cbuf = get_cmdbuf(); } else { /* * Don't use cmd_char if we're starting fresh * at the beginning of a command, because we * don't want to echo the command until we know * it is a multichar command. We also don't * want erase_char/kill_char to be treated * as line editing characters. */ tbuf[0] = c; tbuf[1] = '\0'; cbuf = tbuf; } extra = NULL; action = fcmd_decode(cbuf, &extra); /* * If an "extra" string was returned, * process it as a string of command characters. */ if (extra != NULL) ungetsc(extra); } /* * Clear the cmdbuf string. * (But not if we're in the prefix of a command, * because the partial command string is kept there.) */ if (action != A_PREFIX) cmd_reset(); switch (action) { case A_DIGIT: /* * First digit of a number. */ start_mca(A_DIGIT, ":", (void*)NULL, CF_QUIT_ON_ERASE); goto again; case A_F_WINDOW: /* * Forward one window (and set the window size). */ if (number > 0) swindow = number; /* FALLTHRU */ case A_F_SCREEN: /* * Forward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); if (show_attn) set_attnpos(bottompos); forward(number, 0, 1); break; case A_B_WINDOW: /* * Backward one window (and set the window size). */ if (number > 0) swindow = number; /* FALLTHRU */ case A_B_SCREEN: /* * Backward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); backward(number, 0, 1); break; case A_F_LINE: /* * Forward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); if (show_attn == OPT_ONPLUS && number > 1) set_attnpos(bottompos); forward(number, 0, 0); break; case A_B_LINE: /* * Backward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); backward(number, 0, 0); break; case A_FF_LINE: /* * Force forward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); if (show_attn == OPT_ONPLUS && number > 1) set_attnpos(bottompos); forward(number, 1, 0); break; case A_BF_LINE: /* * Force backward N (default 1) line. */ if (number <= 0) number = 1; cmd_exec(); backward(number, 1, 0); break; case A_FF_SCREEN: /* * Force forward one screen. */ if (number <= 0) number = get_swindow(); cmd_exec(); if (show_attn == OPT_ONPLUS) set_attnpos(bottompos); forward(number, 1, 0); break; case A_F_FOREVER: /* * Forward forever, ignoring EOF. */ if (ch_getflags() & CH_HELPFILE) break; cmd_exec(); jump_forw(); ignore_eoi = 1; hit_eof = 0; while (!sigs) forward(1, 0, 0); ignore_eoi = 0; /* * This gets us back in "F mode" after processing * a non-abort signal (e.g. window-change). */ if (sigs && !ABORT_SIGS()) newaction = A_F_FOREVER; break; case A_F_SCROLL: /* * Forward N lines * (default same as last 'd' or 'u' command). */ if (number > 0) wscroll = number; cmd_exec(); if (show_attn == OPT_ONPLUS) set_attnpos(bottompos); forward(wscroll, 0, 0); break; case A_B_SCROLL: /* * Forward N lines * (default same as last 'd' or 'u' command). */ if (number > 0) wscroll = number; cmd_exec(); backward(wscroll, 0, 0); break; case A_FREPAINT: /* * Flush buffers, then repaint screen. * Don't flush the buffers on a pipe! */ if (ch_getflags() & CH_CANSEEK) { ch_flush(); clr_linenum(); #if HILITE_SEARCH clr_hilite(); #endif } /* FALLTHRU */ case A_REPAINT: /* * Repaint screen. */ cmd_exec(); repaint(); break; case A_GOLINE: /* * Go to line N, default beginning of file. */ if (number <= 0) number = 1; cmd_exec(); jump_back(number); break; case A_PERCENT: /* * Go to a specified percentage into the file. */ if (number < 0) number = 0; if (number > 100) number = 100; cmd_exec(); jump_percent(number); break; case A_GOEND: /* * Go to line N, default end of file. */ cmd_exec(); if (number <= 0) jump_forw(); else jump_back(number); break; case A_GOPOS: /* * Go to a specified byte position in the file. */ cmd_exec(); if (number < 0) number = 0; jump_line_loc((POSITION)number, jump_sline); break; case A_STAT: /* * Print file name, etc. */ if (ch_getflags() & CH_HELPFILE) break; cmd_exec(); parg.p_string = eq_message(); error("%s", &parg); break; case A_VERSION: /* * Print version number, without the "@(#)". */ cmd_exec(); dispversion(); break; case A_QUIT: /* * Exit. */ if (curr_ifile != NULL_IFILE && ch_getflags() & CH_HELPFILE) { /* * Quit while viewing the help file * just means return to viewing the * previous file. */ if (edit_prev(1) == 0) break; } if (extra != NULL) quit(*extra); quit(QUIT_OK); break; /* * Define abbreviation for a commonly used sequence below. */ #define DO_SEARCH() if (number <= 0) number = 1; \ mca_search(); \ cmd_exec(); \ multi_search((char *)NULL, number); case A_F_SEARCH: /* * Search forward for a pattern. * Get the first char of the pattern. */ search_type = SRCH_FORW; if (number <= 0) number = 1; mca_search(); c = getcc(); goto again; case A_B_SEARCH: /* * Search backward for a pattern. * Get the first char of the pattern. */ search_type = SRCH_BACK; if (number <= 0) number = 1; mca_search(); c = getcc(); goto again; case A_AGAIN_SEARCH: /* * Repeat previous search. */ DO_SEARCH(); break; case A_T_AGAIN_SEARCH: /* * Repeat previous search, multiple files. */ search_type |= SRCH_PAST_EOF; DO_SEARCH(); break; case A_REVERSE_SEARCH: /* * Repeat previous search, in reverse direction. */ save_search_type = search_type; search_type = SRCH_REVERSE(search_type); DO_SEARCH(); search_type = save_search_type; break; case A_T_REVERSE_SEARCH: /* * Repeat previous search, * multiple files in reverse direction. */ save_search_type = search_type; search_type = SRCH_REVERSE(search_type); search_type |= SRCH_PAST_EOF; DO_SEARCH(); search_type = save_search_type; break; case A_UNDO_SEARCH: undo_search(); break; case A_HELP: /* * Help. */ if (ch_getflags() & CH_HELPFILE) break; cmd_exec(); (void) edit(FAKE_HELPFILE); break; case A_EXAMINE: #if EXAMINE /* * Edit a new file. Get the filename. */ if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_EXAMINE, "Examine: ", ml_examine, 0); c = getcc(); goto again; #else error("Command not available", NULL_PARG); break; #endif case A_VISUAL: /* * Invoke an editor on the input file. */ #if EDITOR if (secure) { error("Command not available", NULL_PARG); break; } if (ch_getflags() & CH_HELPFILE) break; if (strcmp(get_filename(curr_ifile), "-") == 0) { error("Cannot edit standard input", NULL_PARG); break; } if (curr_altfilename != NULL) { error("Cannot edit file processed with LESSOPEN", NULL_PARG); break; } start_mca(A_SHELL, "!", ml_shell, 0); /* * Expand the editor prototype string * and pass it to the system to execute. * (Make sure the screen is displayed so the * expansion of "+%lm" works.) */ make_display(); cmd_exec(); lsystem(pr_expand(editproto, 0), (char*)NULL); break; #else error("Command not available", NULL_PARG); break; #endif case A_NEXT_FILE: /* * Examine next file. */ + if (ntags()) + { + error("No next file", NULL_PARG); + break; + } if (number <= 0) number = 1; if (edit_next(number)) { if (quit_at_eof && hit_eof && !(ch_getflags() & CH_HELPFILE)) quit(QUIT_OK); parg.p_string = (number > 1) ? "(N-th) " : ""; error("No %snext file", &parg); } break; case A_PREV_FILE: /* * Examine previous file. */ + if (ntags()) + { + error("No previous file", NULL_PARG); + break; + } if (number <= 0) number = 1; if (edit_prev(number)) { parg.p_string = (number > 1) ? "(N-th) " : ""; error("No %sprevious file", &parg); } break; + case A_NEXT_TAG: + if (number <= 0) + number = 1; + tagfile = nexttag(number); + if (tagfile == NULL) + { + error("No next tag", NULL_PARG); + break; + } + if (edit(tagfile) == 0) + { + POSITION pos = tagsearch(); + if (pos != NULL_POSITION) + jump_loc(pos, jump_sline); + } + break; + + case A_PREV_TAG: + if (number <= 0) + number = 1; + tagfile = prevtag(number); + if (tagfile == NULL) + { + error("No previous tag", NULL_PARG); + break; + } + if (edit(tagfile) == 0) + { + POSITION pos = tagsearch(); + if (pos != NULL_POSITION) + jump_loc(pos, jump_sline); + } + break; + case A_INDEX_FILE: /* * Examine a particular file. */ if (number <= 0) number = 1; if (edit_index(number)) error("No such file", NULL_PARG); break; case A_REMOVE_FILE: if (ch_getflags() & CH_HELPFILE) break; old_ifile = curr_ifile; new_ifile = getoff_ifile(curr_ifile); if (new_ifile == NULL_IFILE) { bell(); break; } if (edit_ifile(new_ifile) != 0) { reedit_ifile(old_ifile); break; } del_ifile(old_ifile); break; case A_OPT_TOGGLE: optflag = OPT_TOGGLE; optgetname = FALSE; mca_opt_toggle(); c = getcc(); goto again; case A_DISP_OPTION: /* * Report a flag setting. */ optflag = OPT_NO_TOGGLE; optgetname = FALSE; mca_opt_toggle(); c = getcc(); goto again; case A_FIRSTCMD: /* * Set an initial command for new files. */ start_mca(A_FIRSTCMD, "+", (void*)NULL, 0); c = getcc(); goto again; case A_SHELL: /* * Shell escape. */ #if SHELL_ESCAPE if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_SHELL, "!", ml_shell, 0); c = getcc(); goto again; #else error("Command not available", NULL_PARG); break; #endif case A_SETMARK: /* * Set a mark. */ if (ch_getflags() & CH_HELPFILE) break; start_mca(A_SETMARK, "mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == kill_char || c == '\n' || c == '\r') break; setmark(c); break; case A_GOMARK: /* * Go to a mark. */ start_mca(A_GOMARK, "goto mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == kill_char || c == '\n' || c == '\r') break; gomark(c); break; case A_PIPE: #if PIPEC if (secure) { error("Command not available", NULL_PARG); break; } start_mca(A_PIPE, "|mark: ", (void*)NULL, 0); c = getcc(); if (c == erase_char || c == kill_char) break; if (c == '\n' || c == '\r') c = '.'; if (badmark(c)) break; pipec = c; start_mca(A_PIPE, "!", ml_shell, 0); c = getcc(); goto again; #else error("Command not available", NULL_PARG); break; #endif case A_B_BRACKET: case A_F_BRACKET: start_mca(action, "Brackets: ", (void*)NULL, 0); c = getcc(); goto again; case A_LSHIFT: - if (number <= 0) + if (number > 0) + shift_count = number; + else number = (shift_count > 0) ? shift_count : sc_width / 2; if (number > hshift) number = hshift; hshift -= number; screen_trashed = 1; break; case A_RSHIFT: - if (number <= 0) + if (number > 0) + shift_count = number; + else number = (shift_count > 0) ? shift_count : sc_width / 2; hshift += number; screen_trashed = 1; 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) { cmd_reset(); start_mca(A_PREFIX, " ", (void*)NULL, CF_QUIT_ON_ERASE); (void) cmd_char(c); } c = getcc(); goto again; case A_NOACTION: break; default: bell(); break; } } } Index: head/contrib/less/less.h =================================================================== --- head/contrib/less/less.h (revision 89021) +++ head/contrib/less/less.h (revision 89022) @@ -1,397 +1,412 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * Standard include file for "less". */ /* * Defines for MSDOS_COMPILER. */ #define MSOFTC 1 /* Microsoft C */ #define BORLANDC 2 /* Borland C */ #define WIN32C 3 /* Windows (Borland C or Microsoft C) */ #define DJGPPC 4 /* DJGPP C */ /* * Include the file of compile-time options. * The <> make cc search for it in -I., not srcdir. */ #include #ifdef _SEQUENT_ /* * Kludge for Sequent Dynix systems that have sigsetmask, but * it's not compatible with the way less calls it. * {{ Do other systems need this? }} */ #undef HAVE_SIGSETMASK #endif /* * Language details. */ #if HAVE_VOID #define VOID_POINTER void * #else #define VOID_POINTER char * #define void int #endif #if HAVE_CONST #define constant const #else #define constant #endif #define public /* PUBLIC FUNCTION */ /* Library function declarations */ #if HAVE_SYS_TYPES_H #include #endif #if HAVE_STDIO_H #include #endif #if HAVE_FCNTL_H #include #endif #if HAVE_UNISTD_H #include #endif #if HAVE_CTYPE_H #include #endif +#if HAVE_LIMITS_H +#include +#endif #if HAVE_STDLIB_H #include #endif #if HAVE_STRING_H #include #endif #ifdef _OSK #include #include #endif -#if MSDOS_COMPILER==WIN32C +#if MSDOS_COMPILER==WIN32C || OS2 #include #endif #if MSDOS_COMPILER==DJGPPC #include #include #include #include #endif #if !HAVE_STDLIB_H char *getenv(); off_t lseek(); VOID_POINTER calloc(); void free(); #endif /* * Simple lowercase test which can be used during option processing * (before options are parsed which might tell us what charset to use). */ #define SIMPLE_IS_UPPER(c) ((c) >= 'A' && (c) <= 'Z') #define SIMPLE_IS_LOWER(c) ((c) >= 'a' && (c) <= 'z') #define SIMPLE_TO_UPPER(c) ((c) - 'a' + 'A') #define SIMPLE_TO_LOWER(c) ((c) - 'A' + 'a') #if !HAVE_UPPER_LOWER #define isupper(c) SIMPLE_IS_UPPER(c) #define islower(c) SIMPLE_IS_LOWER(c) #define toupper(c) SIMPLE_TO_UPPER(c) #define tolower(c) SIMPLE_TO_LOWER(c) #endif #ifndef NULL #define NULL 0 #endif #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define OPT_OFF 0 #define OPT_ON 1 #define OPT_ONPLUS 2 #if !HAVE_MEMCPY #ifndef memcpy #define memcpy(to,from,len) bcopy((from),(to),(len)) #endif #endif #define BAD_LSEEK ((off_t)-1) +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + /* + * Upper bound on the string length of an integer converted to string. + * 302 / 1000 is ceil (log10 (2.0)). Subtract 1 for the sign bit; + * add 1 for integer division truncation; add 1 more for a minus sign. + */ +#define INT_STRLEN_BOUND(t) ((sizeof(t) * CHAR_BIT - 1) * 302 / 1000 + 1 + 1) + +/* * Special types and constants. */ typedef off_t POSITION; -#define PR_POSITION "%lld" -#define MAX_PRINT_POSITION 20 -#define MAX_PRINT_INT 10 #define NULL_POSITION ((POSITION)(-1)) /* * Flags for open() */ #if MSDOS_COMPILER || OS2 #define OPEN_READ (O_RDONLY|O_BINARY) #else #ifdef _OSK #define OPEN_READ (S_IREAD) #else #ifdef O_RDONLY #define OPEN_READ (O_RDONLY) #else #define OPEN_READ (0) #endif #endif #endif #if defined(O_WRONLY) && defined(O_APPEND) #define OPEN_APPEND (O_APPEND|O_WRONLY) #else #ifdef _OSK #define OPEN_APPEND (S_IWRITE) #else #define OPEN_APPEND (1) #endif #endif /* * Set a file descriptor to binary mode. */ #if MSDOS_COMPILER==MSOFTC #define SET_BINARY(f) _setmode(f, _O_BINARY); #else -#if MSDOS_COMPILER +#if MSDOS_COMPILER || OS2 #define SET_BINARY(f) setmode(f, O_BINARY) #else #define SET_BINARY(f) #endif #endif /* * Does the shell treat "?" as a metacharacter? */ #if MSDOS_COMPILER || OS2 || _OSK #define SHELL_META_QUEST 0 #else #define SHELL_META_QUEST 1 #endif #define SPACES_IN_FILENAMES 1 /* * An IFILE represents an input file. */ #define IFILE VOID_POINTER #define NULL_IFILE ((IFILE)NULL) /* * The structure used to represent a "screen position". * This consists of a file position, and a screen line number. * The meaning is that the line starting at the given file * position is displayed on the ln-th line of the screen. * (Screen lines before ln are empty.) */ struct scrpos { POSITION pos; int ln; }; typedef union parg { char *p_string; int p_int; } PARG; #define NULL_PARG ((PARG *)NULL) struct textlist { char *string; char *endstring; }; #define EOI (-1) #define READ_INTR (-2) /* How quiet should we be? */ #define NOT_QUIET 0 /* Ring bell at eof and for errors */ #define LITTLE_QUIET 1 /* Ring bell only for errors */ #define VERY_QUIET 2 /* Never ring bell */ /* How should we prompt? */ #define PR_SHORT 0 /* Prompt with colon */ #define PR_MEDIUM 1 /* Prompt with message */ #define PR_LONG 2 /* Prompt with longer message */ /* How should we handle backspaces? */ #define BS_SPECIAL 0 /* Do special things for underlining and bold */ #define BS_NORMAL 1 /* \b treated as normal char; actually output */ #define BS_CONTROL 2 /* \b treated as control char; prints as ^H */ /* How should we search? */ #define SRCH_FORW 000001 /* Search forward from current position */ #define SRCH_BACK 000002 /* Search backward from current position */ #define SRCH_NO_MOVE 000004 /* Highlight, but don't move */ #define SRCH_FIND_ALL 000010 /* Find and highlight all matches */ #define SRCH_NO_MATCH 000100 /* Search for non-matching lines */ #define SRCH_PAST_EOF 000200 /* Search past end-of-file, into next file */ #define SRCH_FIRST_FILE 000400 /* Search starting at the first file */ #define SRCH_NO_REGEX 001000 /* Don't use regular expressions */ #define SRCH_REVERSE(t) (((t) & SRCH_FORW) ? \ (((t) & ~SRCH_FORW) | SRCH_BACK) : \ (((t) & ~SRCH_BACK) | SRCH_FORW)) /* */ #define NO_MCA 0 #define MCA_DONE 1 #define MCA_MORE 2 #define CC_OK 0 /* Char was accepted & processed */ #define CC_QUIT 1 /* Char was a request to abort current cmd */ #define CC_ERROR 2 /* Char could not be accepted due to error */ #define CC_PASS 3 /* Char was rejected (internal) */ #define CF_QUIT_ON_ERASE 0001 /* Abort cmd if its entirely erased */ /* Special chars used to tell put_line() to do something special */ #define AT_NORMAL (0) #define AT_UNDERLINE (1) #define AT_BOLD (2) #define AT_BLINK (3) #define AT_INVIS (4) #define AT_STANDOUT (5) + +#if '0' == 240 +#define IS_EBCDIC_HOST 1 +#endif #if IS_EBCDIC_HOST /* * Long definition for EBCDIC. * Since the argument is usually a constant, this macro normally compiles * into a constant. */ #define CONTROL(c) ( \ (c)=='[' ? '\047' : \ (c)=='a' ? '\001' : \ (c)=='b' ? '\002' : \ (c)=='c' ? '\003' : \ (c)=='d' ? '\067' : \ (c)=='e' ? '\055' : \ (c)=='f' ? '\056' : \ (c)=='g' ? '\057' : \ (c)=='h' ? '\026' : \ (c)=='i' ? '\005' : \ (c)=='j' ? '\025' : \ (c)=='k' ? '\013' : \ (c)=='l' ? '\014' : \ (c)=='m' ? '\015' : \ (c)=='n' ? '\016' : \ (c)=='o' ? '\017' : \ (c)=='p' ? '\020' : \ (c)=='q' ? '\021' : \ (c)=='r' ? '\022' : \ (c)=='s' ? '\023' : \ (c)=='t' ? '\074' : \ (c)=='u' ? '\075' : \ (c)=='v' ? '\062' : \ (c)=='w' ? '\046' : \ (c)=='x' ? '\030' : \ (c)=='y' ? '\031' : \ (c)=='z' ? '\077' : \ (c)=='A' ? '\001' : \ (c)=='B' ? '\002' : \ (c)=='C' ? '\003' : \ (c)=='D' ? '\067' : \ (c)=='E' ? '\055' : \ (c)=='F' ? '\056' : \ (c)=='G' ? '\057' : \ (c)=='H' ? '\026' : \ (c)=='I' ? '\005' : \ (c)=='J' ? '\025' : \ (c)=='K' ? '\013' : \ (c)=='L' ? '\014' : \ (c)=='M' ? '\015' : \ (c)=='N' ? '\016' : \ (c)=='O' ? '\017' : \ (c)=='P' ? '\020' : \ (c)=='Q' ? '\021' : \ (c)=='R' ? '\022' : \ (c)=='S' ? '\023' : \ (c)=='T' ? '\074' : \ (c)=='U' ? '\075' : \ (c)=='V' ? '\062' : \ (c)=='W' ? '\046' : \ (c)=='X' ? '\030' : \ (c)=='Y' ? '\031' : \ (c)=='Z' ? '\077' : \ (c)=='|' ? '\031' : \ (c)=='\\' ? '\034' : \ (c)=='^' ? '\036' : \ (c)&077) #else #define CONTROL(c) ((c)&037) #endif /* IS_EBCDIC_HOST */ #define ESC CONTROL('[') #if _OSK_MWC32 #define LSIGNAL(sig,func) os9_signal(sig,func) #else #define LSIGNAL(sig,func) signal(sig,func) #endif #if HAVE_SIGPROCMASK #if HAVE_SIGSET_T #else #undef HAVE_SIGPROCMASK #endif #endif #if HAVE_SIGPROCMASK #if HAVE_SIGEMPTYSET #else #undef sigemptyset #define sigemptyset(mp) *(mp) = 0 #endif #endif #define S_INTERRUPT 01 #define S_STOP 02 #define S_WINCH 04 #define ABORT_SIGS() (sigs & (S_INTERRUPT|S_STOP)) #define QUIT_OK 0 #define QUIT_ERROR 1 #define QUIT_SAVED_STATUS (-1) /* filestate flags */ #define CH_CANSEEK 001 #define CH_KEEPOPEN 002 #define CH_POPENED 004 #define CH_HELPFILE 010 #define ch_zero() ((POSITION)0) #define FAKE_HELPFILE "@/\\less/\\help/\\file/\\@" #include "funcs.h" Index: head/contrib/less/main.c =================================================================== --- head/contrib/less/main.c (revision 89021) +++ head/contrib/less/main.c (revision 89022) @@ -1,385 +1,397 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * Entry point, initialization, miscellaneous routines. */ #include "less.h" +#if MSDOS_COMPILER==WIN32C +#include +#endif public char * every_first_cmd = NULL; public int new_file; public int is_tty; public IFILE curr_ifile = NULL_IFILE; public IFILE old_ifile = NULL_IFILE; public struct scrpos initial_scrpos; public int any_display = FALSE; public POSITION start_attnpos = NULL_POSITION; public POSITION end_attnpos = NULL_POSITION; public int wscroll; public char * progname; public int quitting; public int secure; public int dohelp; public int more_mode = 0; #if LOGFILE public int logfile = -1; public int force_logfile = FALSE; public char * namelogfile = NULL; #endif #if EDITOR public char * editor; public char * editproto; #endif #if TAGS +extern char * tags; extern char * tagoption; extern int jump_sline; #endif +#ifdef WIN32 +static char consoleTitle[256]; +#endif + extern int missing_cap; extern int know_dumb; /* * Entry point. */ int main(argc, argv) int argc; char *argv[]; { IFILE ifile; char *s; extern char *__progname; #ifdef __EMX__ _response(&argc, &argv); _wildcard(&argc, &argv); #endif progname = *argv++; argc--; secure = 0; s = lgetenv("LESSSECURE"); if (s != NULL && *s != '\0') secure = 1; #ifdef WIN32 if (getenv("HOME") == NULL) { /* * If there is no HOME environment variable, * try the concatenation of HOMEDRIVE + HOMEPATH. */ char *drive = getenv("HOMEDRIVE"); char *path = getenv("HOMEPATH"); if (drive != NULL && path != NULL) { char *env = (char *) ecalloc(strlen(drive) + strlen(path) + 6, sizeof(char)); strcpy(env, "HOME="); strcat(env, drive); strcat(env, path); putenv(env); } } + GetConsoleTitle(consoleTitle, sizeof(consoleTitle)/sizeof(char)); #endif /* WIN32 */ /* * Process command line arguments and LESS environment arguments. * Command line arguments override environment arguments. */ if (strcmp(__progname, "more") == 0) more_mode = 1; is_tty = isatty(1); get_term(); init_cmds(); init_prompt(); init_charset(); init_line(); init_option(); if (more_mode) { scan_option("-E"); scan_option("-m"); scan_option("-G"); scan_option("-f"); s = lgetenv("MORE"); } else { s = lgetenv("LESS"); } if (s != NULL) scan_option(save(s)); #define isoptstring(s) (((s)[0] == '-' || (s)[0] == '+') && (s)[1] != '\0') while (argc > 0 && (isoptstring(*argv) || isoptpending())) { s = *argv++; argc--; if (strcmp(s, "--") == 0) break; scan_option(s); } #undef isoptstring if (isoptpending()) { /* * Last command line option was a flag requiring a * following string, but there was no following string. */ nopendopt(); quit(QUIT_OK); } #if EDITOR editor = lgetenv("VISUAL"); if (editor == NULL || *editor == '\0') { editor = lgetenv("EDITOR"); if (editor == NULL || *editor == '\0') editor = EDIT_PGM; } editproto = lgetenv("LESSEDIT"); if (editproto == NULL || *editproto == '\0') editproto = "%E ?lm+%lm. %f"; #endif /* * Call get_ifile with all the command line filenames * to "register" them with the ifile system. */ ifile = NULL_IFILE; if (dohelp) ifile = get_ifile(FAKE_HELPFILE, ifile); while (argc-- > 0) { -#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) || OS2 +#if (MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC) /* * Because the "shell" doesn't expand filename patterns, * treat each argument as a filename pattern rather than * a single filename. * Expand the pattern and iterate over the expanded list. */ struct textlist tlist; char *gfilename; char *filename; gfilename = lglob(*argv++); init_textlist(&tlist, gfilename); filename = NULL; while ((filename = forw_textlist(&tlist, filename)) != NULL) ifile = get_ifile(filename, ifile); free(gfilename); #else ifile = get_ifile(*argv++, ifile); #endif } /* * Set up terminal, etc. */ if (!is_tty) { /* * Output is not a tty. * Just copy the input file(s) to output. */ SET_BINARY(1); if (nifile() == 0) { if (edit_stdin() == 0) cat_file(); } else if (edit_first() == 0) { do { cat_file(); } while (edit_next(1) == 0); } quit(QUIT_OK); } if (missing_cap && !know_dumb && !more_mode) error("WARNING: terminal is not fully functional", NULL_PARG); init_mark(); raw_mode(1); open_getchr(); init_signals(1); /* * Select the first file to examine. */ #if TAGS - if (tagoption != NULL) + if (tagoption != NULL || strcmp(tags, "-") == 0) { /* * A -t option was given. * Verify that no filenames were also given. * Edit the file selected by the "tags" search, * and search for the proper line in the file. */ if (nifile() > 0) { error("No filenames allowed with -t option", NULL_PARG); quit(QUIT_ERROR); } findtag(tagoption); if (edit_tagfile()) /* Edit file which contains the tag */ quit(QUIT_ERROR); /* * Search for the line which contains the tag. * Set up initial_scrpos so we display that line. */ initial_scrpos.pos = tagsearch(); if (initial_scrpos.pos == NULL_POSITION) quit(QUIT_ERROR); initial_scrpos.ln = jump_sline; } else #endif if (nifile() == 0) { if (edit_stdin()) /* Edit standard input */ quit(QUIT_ERROR); } else { if (edit_first()) /* Edit first valid file in cmd line */ quit(QUIT_ERROR); } init(); commands(); quit(QUIT_OK); /*NOTREACHED*/ } /* * Copy a string to a "safe" place * (that is, to a buffer allocated by calloc). */ public char * save(s) char *s; { register char *p; p = (char *) ecalloc(strlen(s)+1, sizeof(char)); strcpy(p, s); return (p); } /* * Allocate memory. * Like calloc(), but never returns an error (NULL). */ public VOID_POINTER ecalloc(count, size) int count; unsigned int size; { register VOID_POINTER p; p = (VOID_POINTER) calloc(count, size); if (p != NULL) return (p); error("Cannot allocate memory", NULL_PARG); quit(QUIT_ERROR); /*NOTREACHED*/ } /* * Skip leading spaces in a string. */ public char * skipsp(s) register char *s; { while (*s == ' ' || *s == '\t') s++; return (s); } /* * See how many characters of two strings are identical. * If uppercase is true, the first string must begin with an uppercase * character; the remainder of the first string may be either case. */ public int sprefix(ps, s, uppercase) char *ps; char *s; int uppercase; { register int c; register int sc; register int len = 0; for ( ; *s != '\0'; s++, ps++) { c = *ps; if (uppercase) { if (len == 0 && SIMPLE_IS_LOWER(c)) return (-1); if (SIMPLE_IS_UPPER(c)) c = SIMPLE_TO_LOWER(c); } sc = *s; if (len > 0 && SIMPLE_IS_UPPER(sc)) sc = SIMPLE_TO_LOWER(sc); if (c != sc) break; len++; } return (len); } /* * Exit the program. */ public void quit(status) int status; { static int save_status; /* * Put cursor at bottom left corner, clear the line, * reset the terminal modes, and exit. */ if (status < 0) status = save_status; else save_status = status; quitting = 1; edit((char*)NULL); if (any_display && is_tty) clear_bot(); deinit(); flush(); raw_mode(0); #if MSDOS_COMPILER && MSDOS_COMPILER != DJGPPC /* * If we don't close 2, we get some garbage from * 2's buffer when it flushes automatically. * I cannot track this one down RB * The same bug shows up if we use ^C^C to abort. */ close(2); +#endif +#if WIN32 + SetConsoleTitle(consoleTitle); #endif close_getchr(); exit(status); } Index: head/contrib/less/prompt.c =================================================================== --- head/contrib/less/prompt.c (revision 89021) +++ head/contrib/less/prompt.c (revision 89022) @@ -1,522 +1,562 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * Prompting and other messages. * There are three flavors of prompts, SHORT, MEDIUM and LONG, * selected by the -m/-M options. * There is also the "equals message", printed by the = command. * A prompt is a message composed of various pieces, such as the * name of the file being viewed, the percentage into the file, etc. */ #include "less.h" #include "position.h" extern int pr_type; extern int hit_eof; extern int new_file; extern int sc_width; extern int so_s_width, so_e_width; extern int linenums; extern int hshift; extern int sc_height; extern int jump_sline; extern IFILE curr_ifile; #if EDITOR extern char *editor; extern char *editproto; #endif /* * Prototypes for the three flavors of prompts. * These strings are expanded by pr_expand(). */ static constant char s_proto[] = - "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x..%t"; + "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x..%t"; static constant char m_proto[] = - "?n?f%f .?m(file %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; + "?n?f%f .?m(%T %i of %m) ..?e(END) ?x- Next\\: %x.:?pB%pB\\%:byte %bB?s/%s...%t"; static constant char M_proto[] = - "?f%f .?n?m(file %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; + "?f%f .?n?m(%T %i of %m) ..?ltlines %lt-%lb?L/%L. :byte %bB?s/%s. .?e(END) ?x- Next\\: %x.:?pB%pB\\%..%t"; static constant char e_proto[] = - "?f%f .?m(file %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; + "?f%f .?m(%T %i of %m) .?ltlines %lt-%lb?L/%L. .byte %bB?s/%s. ?e(END) :?pB%pB\\%..%t"; static constant char h_proto[] = "HELP -- ?eEND -- Press g to see it again:Press RETURN for more., or q when done"; +static constant char w_proto[] = + "Waiting for data"; public char *prproto[3]; public char constant *eqproto = e_proto; public char constant *hproto = h_proto; +public char constant *wproto = w_proto; static char message[PROMPT_SIZE]; static char *mp; /* * Initialize the prompt prototype strings. */ public void init_prompt() { prproto[0] = save(s_proto); prproto[1] = save(m_proto); prproto[2] = save(M_proto); eqproto = save(e_proto); hproto = save(h_proto); + wproto = save(w_proto); } /* * Append a string to the end of the message. */ static void ap_str(s) char *s; { int len; len = strlen(s); if (mp + len >= message + PROMPT_SIZE) len = message + PROMPT_SIZE - mp - 1; strncpy(mp, s, len); mp += len; *mp = '\0'; } /* * Append a character to the end of the message. */ static void ap_char(c) char c; { char buf[2]; buf[0] = c; buf[1] = '\0'; ap_str(buf); } /* * Append a POSITION (as a decimal integer) to the end of the message. */ static void ap_pos(pos) POSITION pos; { - char buf[MAX_PRINT_POSITION]; - - sprintf(buf, PR_POSITION, (long long)pos); - ap_str(buf); + char buf[INT_STRLEN_BOUND(pos) + 1]; + char *p = buf + sizeof(buf) - 1; + int neg = (pos < 0); + + if (neg) + pos = -pos; + *p = '\0'; + do + *--p = '0' + (pos % 10); + while ((pos /= 10) != 0); + if (neg) + *--p = '-'; + ap_str(p); } /* * Append an integer to the end of the message. */ static void ap_int(n) int n; { - char buf[MAX_PRINT_INT]; + char buf[INT_STRLEN_BOUND(n) + 1]; sprintf(buf, "%d", n); ap_str(buf); } /* * Append a question mark to the end of the message. */ static void ap_quest() { ap_str("?"); } /* * Return the "current" byte offset in the file. */ static POSITION curr_byte(where) int where; { POSITION pos; pos = position(where); while (pos == NULL_POSITION && where >= 0 && where < sc_height) pos = position(++where); if (pos == NULL_POSITION) pos = ch_length(); return (pos); } /* * Return the value of a prototype conditional. * A prototype string may include conditionals which consist of a * question mark followed by a single letter. * Here we decode that letter and return the appropriate boolean value. */ static int cond(c, where) char c; int where; { POSITION len; switch (c) { case 'a': /* Anything in the message yet? */ return (mp > message); case 'b': /* Current byte offset known? */ return (curr_byte(where) != NULL_POSITION); case 'c': return (hshift != 0); case 'e': /* At end of file? */ return (hit_eof); case 'f': /* Filename known? */ return (strcmp(get_filename(curr_ifile), "-") != 0); case 'l': /* Line number known? */ case 'd': /* Same as l */ return (linenums); case 'L': /* Final line number known? */ case 'D': /* Same as L */ return (linenums && ch_length() != NULL_POSITION); case 'm': /* More than one file? */ - return (nifile() > 1); + return (ntags() ? (ntags() > 1) : (nifile() > 1)); case 'n': /* First prompt in a new file? */ - return (new_file); + return (ntags() ? 1 : new_file); case 'p': /* Percent into file (bytes) known? */ return (curr_byte(where) != NULL_POSITION && ch_length() > 0); case 'P': /* Percent into file (lines) known? */ return (currline(where) != 0 && (len = ch_length()) > 0 && find_linenum(len) != 0); case 's': /* Size of file known? */ case 'B': return (ch_length() != NULL_POSITION); case 'x': /* Is there a "next" file? */ + if (ntags()) + return (0); return (next_ifile(curr_ifile) != NULL_IFILE); } return (0); } /* * Decode a "percent" prototype character. * A prototype string may include various "percent" escapes; * that is, a percent sign followed by a single letter. * Here we decode that letter and take the appropriate action, * usually by appending something to the message being built. */ static void protochar(c, where, iseditproto) int c; int where; int iseditproto; { POSITION pos; POSITION len; int n; IFILE h; char *s; char *escs; switch (c) { case 'b': /* Current byte offset */ pos = curr_byte(where); if (pos != NULL_POSITION) ap_pos(pos); else ap_quest(); break; case 'c': ap_int(hshift); break; case 'd': /* Current page number */ n = currline(where); if (n > 0 && sc_height > 1) ap_int(((n - 1) / (sc_height - 1)) + 1); else ap_quest(); break; case 'D': /* Last page number */ len = ch_length(); if (len == NULL_POSITION || len == ch_zero() || (n = find_linenum(len)) <= 0) ap_quest(); else ap_int(((n - 1) / (sc_height - 1)) + 1); break; #if EDITOR case 'E': /* Editor name */ ap_str(editor); break; #endif case 'f': /* File name */ s = unquote_file(get_filename(curr_ifile)); /* * If we are expanding editproto then we escape metachars. * This allows us to run the editor on files with funny names. */ if (iseditproto && (escs = esc_metachars(s)) != NULL) { free(s); s = escs; } ap_str(s); free(s); break; case 'i': /* Index into list of files */ - ap_int(get_index(curr_ifile)); + if (ntags()) + ap_int(curr_tag()); + else + ap_int(get_index(curr_ifile)); break; case 'l': /* Current line number */ n = currline(where); if (n != 0) ap_int(n); else ap_quest(); break; case 'L': /* Final line number */ len = ch_length(); if (len == NULL_POSITION || len == ch_zero() || (n = find_linenum(len)) <= 0) ap_quest(); else ap_int(n-1); break; case 'm': /* Number of files */ - ap_int(nifile()); + n = ntags(); + if (n) + ap_int(n); + else + ap_int(nifile()); break; case 'p': /* Percent into file (bytes) */ pos = curr_byte(where); len = ch_length(); if (pos != NULL_POSITION && len > 0) ap_int(percentage(pos,len)); else ap_quest(); break; case 'P': /* Percent into file (lines) */ pos = (POSITION) currline(where); if (pos == 0 || (len = ch_length()) == NULL_POSITION || len == ch_zero() || (n = find_linenum(len)) <= 0) ap_quest(); else ap_int(percentage(pos, (POSITION)n)); break; case 's': /* Size of file */ case 'B': len = ch_length(); if (len != NULL_POSITION) ap_pos(len); else ap_quest(); break; case 't': /* Truncate trailing spaces in the message */ while (mp > message && mp[-1] == ' ') mp--; break; + case 'T': /* Type of list */ + if (ntags()) + ap_str("tag"); + else + ap_str("file"); + break; case 'x': /* Name of next file */ h = next_ifile(curr_ifile); if (h != NULL_IFILE) { s = unquote_file(get_filename(h)); ap_str(s); free(s); } else ap_quest(); break; } } /* * Skip a false conditional. * When a false condition is found (either a false IF or the ELSE part * of a true IF), this routine scans the prototype string to decide * where to resume parsing the string. * We must keep track of nested IFs and skip them properly. */ static char * skipcond(p) register char *p; { register int iflevel; /* * We came in here after processing a ? or :, * so we start nested one level deep. */ iflevel = 1; for (;;) switch (*++p) { case '?': /* * Start of a nested IF. */ iflevel++; break; case ':': /* * Else. * If this matches the IF we came in here with, * then we're done. */ if (iflevel == 1) return (p); break; case '.': /* * Endif. * If this matches the IF we came in here with, * then we're done. */ if (--iflevel == 0) return (p); break; case '\\': /* * Backslash escapes the next character. */ ++p; break; case '\0': /* * Whoops. Hit end of string. * This is a malformed conditional, but just treat it * as if all active conditionals ends here. */ return (p-1); } /*NOTREACHED*/ } /* * Decode a char that represents a position on the screen. */ static char * wherechar(p, wp) char *p; int *wp; { switch (*p) { case 'b': case 'd': case 'l': case 'p': case 'P': switch (*++p) { case 't': *wp = TOP; break; case 'm': *wp = MIDDLE; break; case 'b': *wp = BOTTOM; break; case 'B': *wp = BOTTOM_PLUS_ONE; break; case 'j': *wp = adjsline(jump_sline); break; default: *wp = TOP; p--; break; } } return (p); } /* * Construct a message based on a prototype string. */ public char * pr_expand(proto, maxwidth) char *proto; int maxwidth; { register char *p; register int c; int where; mp = message; if (*proto == '\0') return (""); for (p = proto; *p != '\0'; p++) { switch (*p) { default: /* Just put the character in the message */ ap_char(*p); break; case '\\': /* Backslash escapes the next character */ p++; ap_char(*p); break; case '?': /* Conditional (IF) */ if ((c = *++p) == '\0') --p; else { where = 0; p = wherechar(p, &where); if (!cond(c, where)) p = skipcond(p); } break; case ':': /* ELSE */ p = skipcond(p); break; case '.': /* ENDIF */ break; case '%': /* Percent escape */ if ((c = *++p) == '\0') --p; else { where = 0; p = wherechar(p, &where); protochar(c, where, #if EDITOR (proto == editproto)); #else 0); #endif } break; } } - new_file = 0; if (mp == message) return (NULL); if (maxwidth > 0 && mp >= message + maxwidth) { /* * Message is too long. * Return just the final portion of it. */ return (mp - maxwidth); } return (message); } /* * Return a message suitable for printing by the "=" command. */ public char * eq_message() { return (pr_expand(eqproto, 0)); } /* * Return a prompt. * This depends on the prompt type (SHORT, MEDIUM, LONG), etc. * If we can't come up with an appropriate prompt, return NULL * and the caller will prompt with a colon. */ public char * pr_string() { - if (ch_getflags() & CH_HELPFILE) - return (pr_expand(hproto, sc_width-so_s_width-so_e_width-2)); - return (pr_expand(prproto[pr_type], sc_width-so_s_width-so_e_width-2)); + char *prompt; + + prompt = pr_expand((ch_getflags() & CH_HELPFILE) ? + hproto : prproto[pr_type], + sc_width-so_s_width-so_e_width-2); + new_file = 0; + return (prompt); +} + +/* + * Return a message suitable for printing while waiting in the F command. + */ + public char * +wait_message() +{ + return (pr_expand(wproto, sc_width-so_s_width-so_e_width-2)); } Index: head/contrib/less/screen.c =================================================================== --- head/contrib/less/screen.c (revision 89021) +++ head/contrib/less/screen.c (revision 89022) @@ -1,2297 +1,2439 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * Routines which deal with the characteristics of the terminal. * Uses termcap to be as terminal-independent as possible. */ #include "less.h" #include "cmd.h" #if MSDOS_COMPILER #include "pckeys.h" #if MSDOS_COMPILER==MSOFTC #include #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC #include #if MSDOS_COMPILER==DJGPPC #include extern int fd0; #endif #else #if MSDOS_COMPILER==WIN32C #include #endif #endif #endif #include #else #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS #include #if HAVE_SYS_IOCTL_H && !defined(TIOCGWINSZ) #include #endif #else #if HAVE_TERMIO_H #include #else #if HAVE_SGSTAT_H #include #else #include #endif #if HAVE_SYS_IOCTL_H && (defined(TIOCGWINSZ) || defined(TCGETA) || defined(TIOCGETP) || defined(WIOCGETD)) #include #endif #endif #endif #if HAVE_TERMCAP_H #include #endif #ifdef _OSK #include #endif #if OS2 #include +#include "pckeys.h" #endif #if HAVE_SYS_STREAM_H #include #endif #if HAVE_SYS_PTEM_H #include #endif #endif /* MSDOS_COMPILER */ /* * Check for broken termios package that forces you to manually * set the line discipline. */ #ifdef __ultrix__ #define MUST_SET_LINE_DISCIPLINE 1 #else #define MUST_SET_LINE_DISCIPLINE 0 #endif #if OS2 #define DEFAULT_TERM "ansi" +static char *windowid; #else #define DEFAULT_TERM "unknown" #endif #if MSDOS_COMPILER==MSOFTC static int videopages; static long msec_loops; static int flash_created = 0; #define SETCOLORS(fg,bg) { _settextcolor(fg); _setbkcolor(bg); } #endif #if MSDOS_COMPILER==BORLANDC static unsigned short *whitescreen; static int flash_created = 0; #endif #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC #define _settextposition(y,x) gotoxy(x,y) #define _clearscreen(m) clrscr() #define _outtext(s) cputs(s) #define SETCOLORS(fg,bg) { textcolor(fg); textbackground(bg); } extern int sc_height; #endif #if MSDOS_COMPILER==WIN32C struct keyRecord { int ascii; int scan; } currentKey; static int keyCount = 0; static WORD curr_attr; static int pending_scancode = 0; static WORD *whitescreen; static HANDLE con_out_save = INVALID_HANDLE_VALUE; /* previous console */ static HANDLE con_out_ours = INVALID_HANDLE_VALUE; /* our own */ HANDLE con_out = INVALID_HANDLE_VALUE; /* current console */ extern int quitting; static void win32_init_term(); static void win32_deinit_term(); #define FG_COLORS (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY) #define BG_COLORS (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_INTENSITY) #define MAKEATTR(fg,bg) ((WORD)((fg)|((bg)<<4))) #define SETCOLORS(fg,bg) { curr_attr = MAKEATTR(fg,bg); \ if (SetConsoleTextAttribute(con_out, curr_attr) == 0) \ error("SETCOLORS failed"); } #endif #if MSDOS_COMPILER public int nm_fg_color; /* Color of normal text */ public int nm_bg_color; public int bo_fg_color; /* Color of bold text */ public int bo_bg_color; public int ul_fg_color; /* Color of underlined text */ public int ul_bg_color; public int so_fg_color; /* Color of standout text */ public int so_bg_color; public int bl_fg_color; /* Color of blinking text */ public int bl_bg_color; static int sy_fg_color; /* Color of system text (before less) */ static int sy_bg_color; #else /* * Strings passed to tputs() to do various terminal functions. */ static char *sc_pad, /* Pad string */ *sc_home, /* Cursor home */ *sc_addline, /* Add line, scroll down following lines */ *sc_lower_left, /* Cursor to last line, first column */ *sc_move, /* General cursor positioning */ *sc_clear, /* Clear screen */ *sc_eol_clear, /* Clear to end of line */ *sc_eos_clear, /* Clear to end of screen */ *sc_s_in, /* Enter standout (highlighted) mode */ *sc_s_out, /* Exit standout mode */ *sc_u_in, /* Enter underline mode */ *sc_u_out, /* Exit underline mode */ *sc_b_in, /* Enter bold mode */ *sc_b_out, /* Exit bold mode */ *sc_bl_in, /* Enter blink mode */ *sc_bl_out, /* Exit blink mode */ *sc_visual_bell, /* Visual bell (flash screen) sequence */ *sc_backspace, /* Backspace cursor */ *sc_s_keypad, /* Start keypad mode */ *sc_e_keypad, /* End keypad mode */ *sc_init, /* Startup terminal initialization */ *sc_deinit; /* Exit terminal de-initialization */ #endif static int init_done = 0; public int auto_wrap; /* Terminal does \r\n when write past margin */ public int ignaw; /* Terminal ignores \n immediately after wrap */ public int erase_char, kill_char; /* The user's erase and line-kill chars */ public int werase_char; /* The user's word-erase char */ public int sc_width, sc_height; /* Height & width of screen */ public int bo_s_width, bo_e_width; /* Printing width of boldface seq */ public int ul_s_width, ul_e_width; /* Printing width of underline seq */ public int so_s_width, so_e_width; /* Printing width of standout seq */ public int bl_s_width, bl_e_width; /* Printing width of blink seq */ public int above_mem, below_mem; /* Memory retained above/below screen */ public int can_goto_line; /* Can move cursor to any line */ public int clear_bg; /* Clear fills with background color */ public int missing_cap = 0; /* Some capability is missing */ static int attrmode = AT_NORMAL; #if !MSDOS_COMPILER static char *cheaper(); static void tmodes(); #endif /* * These two variables are sometimes defined in, * and needed by, the termcap library. */ #if MUST_DEFINE_OSPEED extern short ospeed; /* Terminal output baud rate */ extern char PC; /* Pad character */ #endif #ifdef _OSK short ospeed; char PC_, *UP, *BC; #endif extern int quiet; /* If VERY_QUIET, use visual bell for bell */ extern int no_back_scroll; extern int swindow; extern int no_init; extern int quit_at_eof; extern int more_mode; +extern int no_keypad; extern int sigs; extern int wscroll; extern int screen_trashed; #if HILITE_SEARCH extern int hilite_search; #endif extern char *tgetstr(); extern char *tgoto(); /* * Change terminal to "raw mode", or restore to "normal" mode. * "Raw mode" means * 1. An outstanding read will complete on receipt of a single keystroke. * 2. Input is not echoed. * 3. On output, \n is mapped to \r\n. * 4. \t is NOT expanded into spaces. * 5. Signal-causing characters such as ctrl-C (interrupt), * etc. are NOT disabled. * It doesn't matter whether an input \n is mapped to \r, or vice versa. */ public void raw_mode(on) int on; { static int curr_on = 0; if (on == curr_on) return; #if HAVE_TERMIOS_H && HAVE_TERMIOS_FUNCS { struct termios s; static struct termios save_term; static int saved_term = 0; if (on) { /* * Get terminal modes. */ tcgetattr(2, &s); /* * Save modes and set certain variables dependent on modes. */ if (!saved_term) { save_term = s; saved_term = 1; } #if HAVE_OSPEED switch (cfgetospeed(&s)) { #ifdef B0 case B0: ospeed = 0; break; #endif #ifdef B50 case B50: ospeed = 1; break; #endif #ifdef B75 case B75: ospeed = 2; break; #endif #ifdef B110 case B110: ospeed = 3; break; #endif #ifdef B134 case B134: ospeed = 4; break; #endif #ifdef B150 case B150: ospeed = 5; break; #endif #ifdef B200 case B200: ospeed = 6; break; #endif #ifdef B300 case B300: ospeed = 7; break; #endif #ifdef B600 case B600: ospeed = 8; break; #endif #ifdef B1200 case B1200: ospeed = 9; break; #endif #ifdef B1800 case B1800: ospeed = 10; break; #endif #ifdef B2400 case B2400: ospeed = 11; break; #endif #ifdef B4800 case B4800: ospeed = 12; break; #endif #ifdef B9600 case B9600: ospeed = 13; break; #endif #ifdef EXTA case EXTA: ospeed = 14; break; #endif #ifdef EXTB case EXTB: ospeed = 15; break; #endif #ifdef B57600 case B57600: ospeed = 16; break; #endif #ifdef B115200 case B115200: ospeed = 17; break; #endif default: ; } #endif erase_char = s.c_cc[VERASE]; kill_char = s.c_cc[VKILL]; #ifdef VWERASE werase_char = s.c_cc[VWERASE]; #else werase_char = CONTROL('W'); #endif /* * Set the modes to the way we want them. */ s.c_lflag &= ~(0 #ifdef ICANON | ICANON #endif #ifdef ECHO | ECHO #endif #ifdef ECHOE | ECHOE #endif #ifdef ECHOK | ECHOK #endif #if ECHONL | ECHONL #endif ); s.c_oflag |= (0 #ifdef OXTABS | OXTABS #else #ifdef TAB3 | TAB3 #else #ifdef XTABS | XTABS #endif #endif #endif #ifdef OPOST | OPOST #endif #ifdef ONLCR | ONLCR #endif ); s.c_oflag &= ~(0 #ifdef ONOEOT | ONOEOT #endif #ifdef OCRNL | OCRNL #endif #ifdef ONOCR | ONOCR #endif #ifdef ONLRET | ONLRET #endif ); s.c_cc[VMIN] = 1; s.c_cc[VTIME] = 0; #ifdef VLNEXT s.c_cc[VLNEXT] = 0; #endif #ifdef VDSUSP s.c_cc[VDSUSP] = 0; #endif #if MUST_SET_LINE_DISCIPLINE /* * System's termios is broken; need to explicitly * request TERMIODISC line discipline. */ s.c_line = TERMIODISC; #endif } else { /* * Restore saved modes. */ s = save_term; } +#if HAVE_FSYNC + fsync(2); +#endif tcsetattr(2, TCSADRAIN, &s); #if MUST_SET_LINE_DISCIPLINE if (!on) { /* * Broken termios *ignores* any line discipline * except TERMIODISC. A different old line discipline * is therefore not restored, yet. Restore the old * line discipline by hand. */ ioctl(2, TIOCSETD, &save_term.c_line); } #endif } #else #ifdef TCGETA { struct termio s; static struct termio save_term; static int saved_term = 0; if (on) { /* * Get terminal modes. */ ioctl(2, TCGETA, &s); /* * Save modes and set certain variables dependent on modes. */ if (!saved_term) { save_term = s; saved_term = 1; } #if HAVE_OSPEED ospeed = s.c_cflag & CBAUD; #endif erase_char = s.c_cc[VERASE]; kill_char = s.c_cc[VKILL]; #ifdef VWERASE werase_char = s.c_cc[VWERASE]; #else werase_char = CONTROL('W'); #endif /* * Set the modes to the way we want them. */ s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL); s.c_oflag |= (OPOST|ONLCR|TAB3); s.c_oflag &= ~(OCRNL|ONOCR|ONLRET); s.c_cc[VMIN] = 1; s.c_cc[VTIME] = 0; } else { /* * Restore saved modes. */ s = save_term; } ioctl(2, TCSETAW, &s); } #else #ifdef TIOCGETP { struct sgttyb s; static struct sgttyb save_term; static int saved_term = 0; if (on) { /* * Get terminal modes. */ ioctl(2, TIOCGETP, &s); /* * Save modes and set certain variables dependent on modes. */ if (!saved_term) { save_term = s; saved_term = 1; } #if HAVE_OSPEED ospeed = s.sg_ospeed; #endif erase_char = s.sg_erase; kill_char = s.sg_kill; werase_char = CONTROL('W'); /* * Set the modes to the way we want them. */ s.sg_flags |= CBREAK; s.sg_flags &= ~(ECHO|XTABS); } else { /* * Restore saved modes. */ s = save_term; } ioctl(2, TIOCSETN, &s); } #else #ifdef _OSK { struct sgbuf s; static struct sgbuf save_term; static int saved_term = 0; if (on) { /* * Get terminal modes. */ _gs_opt(2, &s); /* * Save modes and set certain variables dependent on modes. */ if (!saved_term) { save_term = s; saved_term = 1; } erase_char = s.sg_bspch; kill_char = s.sg_dlnch; werase_char = CONTROL('W'); /* * Set the modes to the way we want them. */ s.sg_echo = 0; s.sg_eofch = 0; s.sg_pause = 0; s.sg_psch = 0; } else { /* * Restore saved modes. */ s = save_term; } _ss_opt(2, &s); } #else /* MS-DOS, Windows, or OS2 */ #if OS2 /* OS2 */ LSIGNAL(SIGINT, SIG_IGN); #endif erase_char = '\b'; #if MSDOS_COMPILER==DJGPPC kill_char = CONTROL('U'); /* * So that when we shell out or run another program, its * stdin is in cooked mode. We do not switch stdin to binary * mode if fd0 is zero, since that means we were called before * tty was reopened in open_getchr, in which case we would be * changing the original stdin device outside less. */ if (fd0 != 0) setmode(0, on ? O_BINARY : O_TEXT); #else kill_char = ESC; #endif werase_char = CONTROL('W'); #endif #endif #endif #endif curr_on = on; } #if !MSDOS_COMPILER /* * Some glue to prevent calling termcap functions if tgetent() failed. */ static int hardcopy; static char * ltget_env(capname) char *capname; { char name[16]; strcpy(name, "LESS_TERMCAP_"); strcat(name, capname); return (lgetenv(name)); } static int ltgetflag(capname) char *capname; { char *s; if ((s = ltget_env(capname)) != NULL) return (*s != '\0' && *s != '0'); if (hardcopy) return (0); return (tgetflag(capname)); } static int ltgetnum(capname) char *capname; { char *s; if ((s = ltget_env(capname)) != NULL) return (atoi(s)); if (hardcopy) return (-1); return (tgetnum(capname)); } static char * ltgetstr(capname, pp) char *capname; char **pp; { char *s; if ((s = ltget_env(capname)) != NULL) return (s); if (hardcopy) return (NULL); return (tgetstr(capname, pp)); } #endif /* MSDOS_COMPILER */ /* * Get size of the output screen. */ public void scrsize() { register char *s; int sys_height; int sys_width; #if !MSDOS_COMPILER int n; #endif #define DEF_SC_WIDTH 80 #if MSDOS_COMPILER #define DEF_SC_HEIGHT 25 #else #define DEF_SC_HEIGHT 24 #endif sys_width = sys_height = 0; #if MSDOS_COMPILER==MSOFTC { struct videoconfig w; _getvideoconfig(&w); sys_height = w.numtextrows; sys_width = w.numtextcols; } #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC { struct text_info w; gettextinfo(&w); sys_height = w.screenheight; sys_width = w.screenwidth; } #else #if MSDOS_COMPILER==WIN32C { CONSOLE_SCREEN_BUFFER_INFO scr; GetConsoleScreenBufferInfo(con_out, &scr); sys_height = scr.srWindow.Bottom - scr.srWindow.Top + 1; sys_width = scr.srWindow.Right - scr.srWindow.Left + 1; } #else #if OS2 { int s[2]; _scrsize(s); sys_width = s[0]; sys_height = s[1]; + /* + * When using terminal emulators for XFree86/OS2, the + * _scrsize function does not work well. + * Call the scrsize.exe program to get the window size. + */ + windowid = getenv("WINDOWID"); + if (windowid != NULL) + { + FILE *fd = popen("scrsize", "rt"); + if (fd != NULL) + { + int w, h; + fscanf(fd, "%i %i", &w, &h); + if (w > 0 && h > 0) + { + sys_width = w; + sys_height = h; + } + pclose(fd); + } + } } #else #ifdef TIOCGWINSZ { struct winsize w; if (ioctl(2, TIOCGWINSZ, &w) == 0) { if (w.ws_row > 0) sys_height = w.ws_row; if (w.ws_col > 0) sys_width = w.ws_col; } } #else #ifdef WIOCGETD { struct uwdata w; if (ioctl(2, WIOCGETD, &w) == 0) { if (w.uw_height > 0) sys_height = w.uw_height / w.uw_vs; if (w.uw_width > 0) sys_width = w.uw_width / w.uw_hs; } } #endif #endif #endif #endif #endif #endif if (sys_height > 0) sc_height = sys_height; else if ((s = lgetenv("LINES")) != NULL) sc_height = atoi(s); #if !MSDOS_COMPILER else if ((n = ltgetnum("li")) > 0) sc_height = n; #endif else sc_height = DEF_SC_HEIGHT; if (sys_width > 0) sc_width = sys_width; else if ((s = lgetenv("COLUMNS")) != NULL) sc_width = atoi(s); #if !MSDOS_COMPILER else if ((n = ltgetnum("co")) > 0) sc_width = n; #endif else sc_width = DEF_SC_WIDTH; } #if MSDOS_COMPILER==MSOFTC /* * Figure out how many empty loops it takes to delay a millisecond. */ static void get_clock() { clock_t start; /* * Get synchronized at the start of a tick. */ start = clock(); while (clock() == start) ; /* * Now count loops till the next tick. */ start = clock(); msec_loops = 0; while (clock() == start) msec_loops++; /* * Convert from (loops per clock) to (loops per millisecond). */ msec_loops *= CLOCKS_PER_SEC; msec_loops /= 1000; } /* * Delay for a specified number of milliseconds. */ static void dummy_func() { static long delay_dummy = 0; delay_dummy++; } static void delay(msec) int msec; { long i; while (msec-- > 0) { for (i = 0; i < msec_loops; i++) { /* * Make it look like we're doing something here, * so the optimizer doesn't remove the whole loop. */ dummy_func(); } } } #endif /* * Return the characters actually input by a "special" key. */ public char * special_key_str(key) int key; { static char tbuf[40]; char *s; -#if MSDOS_COMPILER +#if MSDOS_COMPILER || OS2 static char k_right[] = { '\340', PCK_RIGHT, 0 }; static char k_left[] = { '\340', PCK_LEFT, 0 }; static char k_ctl_right[] = { '\340', PCK_CTL_RIGHT, 0 }; static char k_ctl_left[] = { '\340', PCK_CTL_LEFT, 0 }; static char k_insert[] = { '\340', PCK_INSERT, 0 }; static char k_delete[] = { '\340', PCK_DELETE, 0 }; static char k_ctl_delete[] = { '\340', PCK_CTL_DELETE, 0 }; static char k_ctl_backspace[] = { '\177', 0 }; static char k_home[] = { '\340', PCK_HOME, 0 }; static char k_end[] = { '\340', PCK_END, 0 }; static char k_up[] = { '\340', PCK_UP, 0 }; static char k_down[] = { '\340', PCK_DOWN, 0 }; static char k_backtab[] = { '\340', PCK_SHIFT_TAB, 0 }; static char k_pagedown[] = { '\340', PCK_PAGEDOWN, 0 }; static char k_pageup[] = { '\340', PCK_PAGEUP, 0 }; static char k_f1[] = { '\340', PCK_F1, 0 }; -#else +#endif +#if !MSDOS_COMPILER char *sp = tbuf; #endif switch (key) { +#if OS2 + /* + * If windowid is not NULL, assume less is executed in + * the XFree86 environment. + */ + case SK_RIGHT_ARROW: + s = windowid ? ltgetstr("kr", &sp) : k_right; + break; + case SK_LEFT_ARROW: + s = windowid ? ltgetstr("kl", &sp) : k_left; + break; + case SK_UP_ARROW: + s = windowid ? ltgetstr("ku", &sp) : k_up; + break; + case SK_DOWN_ARROW: + s = windowid ? ltgetstr("kd", &sp) : k_down; + break; + case SK_PAGE_UP: + s = windowid ? ltgetstr("kP", &sp) : k_pageup; + break; + case SK_PAGE_DOWN: + s = windowid ? ltgetstr("kN", &sp) : k_pagedown; + break; + case SK_HOME: + s = windowid ? ltgetstr("kh", &sp) : k_home; + break; + case SK_END: + s = windowid ? ltgetstr("@7", &sp) : k_end; + break; + case SK_DELETE: + if (windowid) + { + s = ltgetstr("kD", &sp); + if (s == NULL) + { + tbuf[0] = '\177'; + tbuf[1] = '\0'; + s = tbuf; + } + } else + s = k_delete; + break; +#endif #if MSDOS_COMPILER case SK_RIGHT_ARROW: s = k_right; break; case SK_LEFT_ARROW: s = k_left; break; case SK_UP_ARROW: s = k_up; break; case SK_DOWN_ARROW: s = k_down; break; case SK_PAGE_UP: s = k_pageup; break; case SK_PAGE_DOWN: s = k_pagedown; break; case SK_HOME: s = k_home; break; case SK_END: s = k_end; break; case SK_DELETE: s = k_delete; break; +#endif +#if MSDOS_COMPILER || OS2 case SK_INSERT: s = k_insert; break; case SK_CTL_LEFT_ARROW: s = k_ctl_left; break; case SK_CTL_RIGHT_ARROW: s = k_ctl_right; break; case SK_CTL_BACKSPACE: s = k_ctl_backspace; break; case SK_CTL_DELETE: s = k_ctl_delete; break; case SK_F1: s = k_f1; break; case SK_BACKTAB: s = k_backtab; break; #else case SK_RIGHT_ARROW: s = ltgetstr("kr", &sp); break; case SK_LEFT_ARROW: s = ltgetstr("kl", &sp); break; case SK_UP_ARROW: s = ltgetstr("ku", &sp); break; case SK_DOWN_ARROW: s = ltgetstr("kd", &sp); break; case SK_PAGE_UP: s = ltgetstr("kP", &sp); break; case SK_PAGE_DOWN: s = ltgetstr("kN", &sp); break; case SK_HOME: s = ltgetstr("kh", &sp); break; case SK_END: s = ltgetstr("@7", &sp); break; case SK_DELETE: s = ltgetstr("kD", &sp); if (s == NULL) { tbuf[0] = '\177'; tbuf[1] = '\0'; s = tbuf; } break; #endif case SK_CONTROL_K: tbuf[0] = CONTROL('K'); tbuf[1] = '\0'; s = tbuf; break; default: return (NULL); } return (s); } /* * Get terminal capabilities via termcap. */ public void get_term() { #if MSDOS_COMPILER auto_wrap = 1; ignaw = 0; can_goto_line = 1; clear_bg = 1; /* * Set up default colors. * The xx_s_width and xx_e_width vars are already initialized to 0. */ #if MSDOS_COMPILER==MSOFTC sy_bg_color = _getbkcolor(); sy_fg_color = _gettextcolor(); get_clock(); #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC { struct text_info w; gettextinfo(&w); sy_bg_color = (w.attribute >> 4) & 0x0F; sy_fg_color = (w.attribute >> 0) & 0x0F; } #else #if MSDOS_COMPILER==WIN32C { DWORD nread; CONSOLE_SCREEN_BUFFER_INFO scr; con_out_save = con_out = GetStdHandle(STD_OUTPUT_HANDLE); /* * Always open stdin in binary. Note this *must* be done * before any file operations have been done on fd0. */ SET_BINARY(0); GetConsoleScreenBufferInfo(con_out, &scr); ReadConsoleOutputAttribute(con_out, &curr_attr, 1, scr.dwCursorPosition, &nread); sy_bg_color = (curr_attr & BG_COLORS) >> 4; /* normalize */ sy_fg_color = curr_attr & FG_COLORS; } #endif #endif #endif nm_fg_color = sy_fg_color; nm_bg_color = sy_bg_color; bo_fg_color = 11; bo_bg_color = 0; ul_fg_color = 9; ul_bg_color = 0; so_fg_color = 15; so_bg_color = 9; bl_fg_color = 15; bl_bg_color = 0; /* * Get size of the screen. */ scrsize(); pos_init(); #else /* !MSDOS_COMPILER */ char *sp; register char *t1, *t2; char *term; char termbuf[TERMBUF_SIZE]; static char sbuf[TERMSBUF_SIZE]; #if OS2 /* * Make sure the termcap database is available. */ sp = lgetenv("TERMCAP"); if (sp == NULL || *sp == '\0') { char *termcap; if ((sp = homefile("termcap.dat")) != NULL) { termcap = (char *) ecalloc(strlen(sp)+9, sizeof(char)); sprintf(termcap, "TERMCAP=%s", sp); free(sp); putenv(termcap); } } #endif /* * Find out what kind of terminal this is. */ if ((term = lgetenv("TERM")) == NULL) term = DEFAULT_TERM; hardcopy = 0; if (tgetent(termbuf, term) <= 0) hardcopy = 1; if (ltgetflag("hc")) hardcopy = 1; /* * Get size of the screen. */ scrsize(); pos_init(); auto_wrap = ltgetflag("am"); ignaw = ltgetflag("xn"); above_mem = ltgetflag("da"); below_mem = ltgetflag("db"); clear_bg = ltgetflag("ut"); /* * Assumes termcap variable "sg" is the printing width of: * the standout sequence, the end standout sequence, * the underline sequence, the end underline sequence, * the boldface sequence, and the end boldface sequence. */ if ((so_s_width = ltgetnum("sg")) < 0) so_s_width = 0; so_e_width = so_s_width; bo_s_width = bo_e_width = so_s_width; ul_s_width = ul_e_width = so_s_width; bl_s_width = bl_e_width = so_s_width; #if HILITE_SEARCH if (so_s_width > 0 || so_e_width > 0) /* * Disable highlighting by default on magic cookie terminals. * Turning on highlighting might change the displayed width * of a line, causing the display to get messed up. * The user can turn it back on with -g, * but she won't like the results. */ hilite_search = 0; #endif /* * Get various string-valued capabilities. */ sp = sbuf; #if HAVE_OSPEED sc_pad = ltgetstr("pc", &sp); if (sc_pad != NULL) PC = *sc_pad; #endif sc_s_keypad = ltgetstr("ks", &sp); if (sc_s_keypad == NULL) sc_s_keypad = ""; sc_e_keypad = ltgetstr("ke", &sp); if (sc_e_keypad == NULL) sc_e_keypad = ""; /* * This loses for terminals with termcap entries with ti/te strings * that switch to/from an alternate screen, and we're in quit_at_eof * (eg, more(1)). */ if (!quit_at_eof && !more_mode) { sc_init = ltgetstr("ti", &sp); sc_deinit = ltgetstr("te", &sp); } if (sc_init == NULL) sc_init = ""; if (sc_deinit == NULL) sc_deinit = ""; sc_eol_clear = ltgetstr("ce", &sp); if (sc_eol_clear == NULL || *sc_eol_clear == '\0') { missing_cap = 1; sc_eol_clear = ""; } sc_eos_clear = ltgetstr("cd", &sp); if (below_mem && (sc_eos_clear == NULL || *sc_eos_clear == '\0')) { missing_cap = 1; sc_eol_clear = ""; } sc_clear = ltgetstr("cl", &sp); if (sc_clear == NULL || *sc_clear == '\0') { missing_cap = 1; sc_clear = "\n\n"; } sc_move = ltgetstr("cm", &sp); if (sc_move == NULL || *sc_move == '\0') { /* * This is not an error here, because we don't * always need sc_move. * We need it only if we don't have home or lower-left. */ sc_move = ""; can_goto_line = 0; } else can_goto_line = 1; tmodes("so", "se", &sc_s_in, &sc_s_out, "", "", &sp); tmodes("us", "ue", &sc_u_in, &sc_u_out, sc_s_in, sc_s_out, &sp); tmodes("md", "me", &sc_b_in, &sc_b_out, sc_s_in, sc_s_out, &sp); tmodes("mb", "me", &sc_bl_in, &sc_bl_out, sc_s_in, sc_s_out, &sp); sc_visual_bell = ltgetstr("vb", &sp); if (sc_visual_bell == NULL) sc_visual_bell = ""; if (ltgetflag("bs")) sc_backspace = "\b"; else { sc_backspace = ltgetstr("bc", &sp); if (sc_backspace == NULL || *sc_backspace == '\0') sc_backspace = "\b"; } /* * Choose between using "ho" and "cm" ("home" and "cursor move") * to move the cursor to the upper left corner of the screen. */ t1 = ltgetstr("ho", &sp); if (t1 == NULL) t1 = ""; if (*sc_move == '\0') t2 = ""; else { strcpy(sp, tgoto(sc_move, 0, 0)); t2 = sp; sp += strlen(sp) + 1; } sc_home = cheaper(t1, t2, "|\b^"); /* * Choose between using "ll" and "cm" ("lower left" and "cursor move") * to move the cursor to the lower left corner of the screen. */ t1 = ltgetstr("ll", &sp); if (t1 == NULL) t1 = ""; if (*sc_move == '\0') t2 = ""; else { strcpy(sp, tgoto(sc_move, 0, sc_height-1)); t2 = sp; sp += strlen(sp) + 1; } sc_lower_left = cheaper(t1, t2, "\r"); /* * Choose between using "al" or "sr" ("add line" or "scroll reverse") * to add a line at the top of the screen. */ t1 = ltgetstr("al", &sp); if (t1 == NULL) t1 = ""; t2 = ltgetstr("sr", &sp); if (t2 == NULL) t2 = ""; #if OS2 if (*t1 == '\0' && *t2 == '\0') sc_addline = ""; else #endif if (above_mem) sc_addline = t1; else sc_addline = cheaper(t1, t2, ""); if (*sc_addline == '\0') { /* * Force repaint on any backward movement. */ no_back_scroll = 1; } #endif /* MSDOS_COMPILER */ } #if !MSDOS_COMPILER /* * Return the cost of displaying a termcap string. * We use the trick of calling tputs, but as a char printing function * we give it inc_costcount, which just increments "costcount". * This tells us how many chars would be printed by using this string. * {{ Couldn't we just use strlen? }} */ static int costcount; /*ARGSUSED*/ static int inc_costcount(c) int c; { costcount++; return (c); } static int cost(t) char *t; { costcount = 0; tputs(t, sc_height, inc_costcount); return (costcount); } /* * Return the "best" of the two given termcap strings. * The best, if both exist, is the one with the lower * cost (see cost() function). */ static char * cheaper(t1, t2, def) char *t1, *t2; char *def; { if (*t1 == '\0' && *t2 == '\0') { missing_cap = 1; return (def); } if (*t1 == '\0') return (t2); if (*t2 == '\0') return (t1); if (cost(t1) < cost(t2)) return (t1); return (t2); } static void tmodes(incap, outcap, instr, outstr, def_instr, def_outstr, spp) char *incap; char *outcap; char **instr; char **outstr; char *def_instr; char *def_outstr; char **spp; { *instr = ltgetstr(incap, spp); if (*instr == NULL) { /* Use defaults. */ *instr = def_instr; *outstr = def_outstr; return; } *outstr = ltgetstr(outcap, spp); if (*outstr == NULL) /* No specific out capability; use "me". */ *outstr = ltgetstr("me", spp); if (*outstr == NULL) /* Don't even have "me"; use a null string. */ *outstr = ""; } #endif /* MSDOS_COMPILER */ /* * Below are the functions which perform all the * terminal-specific screen manipulation. */ #if MSDOS_COMPILER #if MSDOS_COMPILER==WIN32C static void _settextposition(int row, int col) { COORD cpos; CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(con_out, &csbi); cpos.X = csbi.srWindow.Left + (col - 1); cpos.Y = csbi.srWindow.Top + (row - 1); SetConsoleCursorPosition(con_out, cpos); } #endif /* * Initialize the screen to the correct color at startup. */ static void initcolor() { SETCOLORS(nm_fg_color, nm_bg_color); #if 0 /* * This clears the screen at startup. This is different from * the behavior of other versions of less. Disable it for now. */ char *blanks; int row; int col; /* * Create a complete, blank screen using "normal" colors. */ SETCOLORS(nm_fg_color, nm_bg_color); blanks = (char *) ecalloc(width+1, sizeof(char)); for (col = 0; col < sc_width; col++) blanks[col] = ' '; blanks[sc_width] = '\0'; for (row = 0; row < sc_height; row++) _outtext(blanks); free(blanks); #endif } #endif #if MSDOS_COMPILER==WIN32C /* * Termcap-like init with a private win32 console. */ static void win32_init_term() { CONSOLE_SCREEN_BUFFER_INFO scr; COORD size; if (con_out_save == INVALID_HANDLE_VALUE) return; GetConsoleScreenBufferInfo(con_out_save, &scr); if (con_out_ours == INVALID_HANDLE_VALUE) { /* * Create our own screen buffer, so that we * may restore the original when done. */ con_out_ours = CreateConsoleScreenBuffer( GENERIC_WRITE | GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES) NULL, CONSOLE_TEXTMODE_BUFFER, (LPVOID) NULL); } size.X = scr.srWindow.Right - scr.srWindow.Left + 1; size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; SetConsoleScreenBufferSize(con_out_ours, size); SetConsoleActiveScreenBuffer(con_out_ours); con_out = con_out_ours; } /* * Restore the startup console. */ static void win32_deinit_term() { if (con_out_save == INVALID_HANDLE_VALUE) return; if (quitting) (void) CloseHandle(con_out_ours); SetConsoleActiveScreenBuffer(con_out_save); con_out = con_out_save; } #endif /* * Initialize terminal */ public void init() { - if (no_init) - { -#if MSDOS_COMPILER==WIN32C - /* no_init or not, never trash win32 console colors. */ - initcolor(); - flush(); -#endif - return; - } #if !MSDOS_COMPILER - tputs(sc_init, sc_height, putchr); - tputs(sc_s_keypad, sc_height, putchr); + if (!no_init) + tputs(sc_init, sc_height, putchr); + if (!no_keypad) + tputs(sc_s_keypad, sc_height, putchr); #else #if MSDOS_COMPILER==WIN32C - win32_init_term(); + if (!no_init) + win32_init_term(); #endif initcolor(); flush(); #endif init_done = 1; } /* * Deinitialize terminal */ public void deinit() { - if (no_init) - { -#if MSDOS_COMPILER==WIN32C - /* no_init or not, never trash win32 console colors. */ - SETCOLORS(sy_fg_color, sy_bg_color); -#endif - return; - } - if (!init_done) return; #if !MSDOS_COMPILER - tputs(sc_e_keypad, sc_height, putchr); - tputs(sc_deinit, sc_height, putchr); + if (!no_keypad) + tputs(sc_e_keypad, sc_height, putchr); + if (!no_init) + tputs(sc_deinit, sc_height, putchr); #else + /* Restore system colors. */ SETCOLORS(sy_fg_color, sy_bg_color); #if MSDOS_COMPILER==WIN32C - win32_deinit_term(); + if (!no_init) + win32_deinit_term(); +#else + /* Need clreol to make SETCOLORS take effect. */ + clreol(); #endif #endif init_done = 0; } /* * Home cursor (move to upper left corner of screen). */ public void home() { #if !MSDOS_COMPILER tputs(sc_home, 1, putchr); #else flush(); _settextposition(1,1); #endif } /* * Add a blank line (called with cursor at home). * Should scroll the display down. */ public void add_line() { #if !MSDOS_COMPILER tputs(sc_addline, sc_height, putchr); #else flush(); #if MSDOS_COMPILER==MSOFTC _scrolltextwindow(_GSCROLLDOWN); _settextposition(1,1); #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC movetext(1,1, sc_width,sc_height-1, 1,2); gotoxy(1,1); clreol(); #else #if MSDOS_COMPILER==WIN32C { CHAR_INFO fillchar; SMALL_RECT rcSrc, rcClip; COORD new_org; CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(con_out,&csbi); /* The clip rectangle is the entire visible screen. */ rcClip.Left = csbi.srWindow.Left; rcClip.Top = csbi.srWindow.Top; rcClip.Right = csbi.srWindow.Right; rcClip.Bottom = csbi.srWindow.Bottom; /* The source rectangle is the visible screen minus the last line. */ rcSrc = rcClip; rcSrc.Bottom--; /* Move the top left corner of the source window down one row. */ new_org.X = rcSrc.Left; new_org.Y = rcSrc.Top + 1; /* Fill the right character and attributes. */ fillchar.Char.AsciiChar = ' '; curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); fillchar.Attributes = curr_attr; ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); _settextposition(1,1); } #endif #endif #endif #endif } +#if 0 /* * Remove the n topmost lines and scroll everything below it in the * window upward. This is needed to stop leaking the topmost line * into the scrollback buffer when we go down-one-line (in WIN32). */ public void remove_top(n) int n; { #if MSDOS_COMPILER==WIN32C SMALL_RECT rcSrc, rcClip; CHAR_INFO fillchar; COORD new_org; CONSOLE_SCREEN_BUFFER_INFO csbi; /* to get buffer info */ if (n >= sc_height - 1) { clear(); home(); return; } flush(); GetConsoleScreenBufferInfo(con_out, &csbi); /* Get the extent of all-visible-rows-but-the-last. */ rcSrc.Left = csbi.srWindow.Left; rcSrc.Top = csbi.srWindow.Top + n; rcSrc.Right = csbi.srWindow.Right; rcSrc.Bottom = csbi.srWindow.Bottom; /* Get the clip rectangle. */ rcClip.Left = rcSrc.Left; rcClip.Top = csbi.srWindow.Top; rcClip.Right = rcSrc.Right; rcClip.Bottom = rcSrc.Bottom ; /* Move the source window up n rows. */ new_org.X = rcSrc.Left; new_org.Y = rcSrc.Top - n; /* Fill the right character and attributes. */ fillchar.Char.AsciiChar = ' '; curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); fillchar.Attributes = curr_attr; ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); /* Position cursor on first blank line. */ goto_line(sc_height - n - 1); #endif } +#endif +#if MSDOS_COMPILER==WIN32C /* + * Clear the screen. + */ + static void +win32_clear() +{ + /* + * This will clear only the currently visible rows of the NT + * console buffer, which means none of the precious scrollback + * rows are touched making for faster scrolling. Note that, if + * the window has fewer columns than the console buffer (i.e. + * there is a horizontal scrollbar as well), the entire width + * of the visible rows will be cleared. + */ + COORD topleft; + DWORD nchars; + DWORD winsz; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + /* get the number of cells in the current buffer */ + GetConsoleScreenBufferInfo(con_out, &csbi); + winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); + topleft.X = 0; + topleft.Y = csbi.srWindow.Top; + + curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); + FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); + FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); +} + +/* + * Remove the n topmost lines and scroll everything below it in the + * window upward. + */ + public void +win32_scroll_up(n) + int n; +{ + SMALL_RECT rcSrc, rcClip; + CHAR_INFO fillchar; + COORD topleft; + COORD new_org; + DWORD nchars; + DWORD size; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + if (n <= 0) + return; + + if (n >= sc_height - 1) + { + win32_clear(); + _settextposition(1,1); + return; + } + + /* Get the extent of what will remain visible after scrolling. */ + GetConsoleScreenBufferInfo(con_out, &csbi); + rcSrc.Left = csbi.srWindow.Left; + rcSrc.Top = csbi.srWindow.Top + n; + rcSrc.Right = csbi.srWindow.Right; + rcSrc.Bottom = csbi.srWindow.Bottom; + + /* Get the clip rectangle. */ + rcClip.Left = rcSrc.Left; + rcClip.Top = csbi.srWindow.Top; + rcClip.Right = rcSrc.Right; + rcClip.Bottom = rcSrc.Bottom ; + + /* Move the source text to the top of the screen. */ + new_org.X = rcSrc.Left; + new_org.Y = 0; + + /* Fill the right character and attributes. */ + fillchar.Char.AsciiChar = ' '; + fillchar.Attributes = MAKEATTR(nm_fg_color, nm_bg_color); + + /* Scroll the window. */ + SetConsoleTextAttribute(con_out, fillchar.Attributes); + ScrollConsoleScreenBuffer(con_out, &rcSrc, &rcClip, new_org, &fillchar); + + /* Clear remaining lines at bottom. */ + topleft.X = csbi.dwCursorPosition.X; + topleft.Y = rcSrc.Bottom - n; + size = (n * csbi.dwSize.X) + (rcSrc.Right - topleft.X); + FillConsoleOutputCharacter(con_out, ' ', size, topleft, + &nchars); + FillConsoleOutputAttribute(con_out, fillchar.Attributes, size, topleft, + &nchars); + SetConsoleTextAttribute(con_out, curr_attr); + + /* Move cursor n lines up from where it was. */ + csbi.dwCursorPosition.Y -= n; + SetConsoleCursorPosition(con_out, csbi.dwCursorPosition); +} +#endif + +/* * Move cursor to lower left corner of screen. */ public void lower_left() { #if !MSDOS_COMPILER tputs(sc_lower_left, 1, putchr); #else flush(); _settextposition(sc_height, 1); #endif } /* * Check if the console size has changed and reset internals * (in lieu of SIGWINCH for WIN32). */ public void check_winch() { #if MSDOS_COMPILER==WIN32C CONSOLE_SCREEN_BUFFER_INFO scr; COORD size; if (con_out == INVALID_HANDLE_VALUE) return; flush(); GetConsoleScreenBufferInfo(con_out, &scr); size.Y = scr.srWindow.Bottom - scr.srWindow.Top + 1; size.X = scr.srWindow.Right - scr.srWindow.Left + 1; if (size.Y != sc_height || size.X != sc_width) { sc_height = size.Y; sc_width = size.X; if (!no_init && con_out_ours == con_out) SetConsoleScreenBufferSize(con_out, size); pos_init(); wscroll = (sc_height + 1) / 2; screen_trashed = 1; } #endif } /* * Goto a specific line on the screen. */ public void goto_line(slinenum) int slinenum; { #if !MSDOS_COMPILER tputs(tgoto(sc_move, 0, slinenum), 1, putchr); #else flush(); _settextposition(slinenum+1, 1); #endif } #if MSDOS_COMPILER==MSOFTC || MSDOS_COMPILER==BORLANDC /* * Create an alternate screen which is all white. * This screen is used to create a "flash" effect, by displaying it * briefly and then switching back to the normal screen. * {{ Yuck! There must be a better way to get a visual bell. }} */ static void create_flash() { #if MSDOS_COMPILER==MSOFTC struct videoconfig w; char *blanks; int row, col; _getvideoconfig(&w); videopages = w.numvideopages; if (videopages < 2) { so_enter(); so_exit(); } else { _setactivepage(1); so_enter(); blanks = (char *) ecalloc(w.numtextcols, sizeof(char)); for (col = 0; col < w.numtextcols; col++) blanks[col] = ' '; for (row = w.numtextrows; row > 0; row--) _outmem(blanks, w.numtextcols); _setactivepage(0); _setvisualpage(0); free(blanks); so_exit(); } #else #if MSDOS_COMPILER==BORLANDC register int n; whitescreen = (unsigned short *) malloc(sc_width * sc_height * sizeof(short)); if (whitescreen == NULL) return; for (n = 0; n < sc_width * sc_height; n++) whitescreen[n] = 0x7020; #else #if MSDOS_COMPILER==WIN32C register int n; whitescreen = (WORD *) malloc(sc_height * sc_width * sizeof(WORD)); if (whitescreen == NULL) return; /* Invert the standard colors. */ for (n = 0; n < sc_width * sc_height; n++) whitescreen[n] = (WORD)((nm_fg_color << 4) | nm_bg_color); #endif #endif #endif flash_created = 1; } #endif /* MSDOS_COMPILER */ /* * Output the "visual bell", if there is one. */ public void vbell() { #if !MSDOS_COMPILER if (*sc_visual_bell == '\0') return; tputs(sc_visual_bell, sc_height, putchr); #else #if MSDOS_COMPILER==DJGPPC ScreenVisualBell(); #else #if MSDOS_COMPILER==MSOFTC /* * Create a flash screen on the second video page. * Switch to that page, then switch back. */ if (!flash_created) create_flash(); if (videopages < 2) return; _setvisualpage(1); delay(100); _setvisualpage(0); #else #if MSDOS_COMPILER==BORLANDC unsigned short *currscreen; /* * Get a copy of the current screen. * Display the flash screen. * Then restore the old screen. */ if (!flash_created) create_flash(); if (whitescreen == NULL) return; currscreen = (unsigned short *) malloc(sc_width * sc_height * sizeof(short)); if (currscreen == NULL) return; gettext(1, 1, sc_width, sc_height, currscreen); puttext(1, 1, sc_width, sc_height, whitescreen); delay(100); puttext(1, 1, sc_width, sc_height, currscreen); free(currscreen); #else #if MSDOS_COMPILER==WIN32C /* paint screen with an inverse color */ clear(); /* leave it displayed for 100 msec. */ Sleep(100); /* restore with a redraw */ repaint(); #endif #endif #endif #endif #endif } /* * Make a noise. */ static void beep() { #if !MSDOS_COMPILER - putchr('\7'); + putchr(CONTROL('G')); #else #if MSDOS_COMPILER==WIN32C MessageBeep(0); #else write(1, "\7", 1); #endif #endif } /* * Ring the terminal bell. */ public void bell() { if (quiet == VERY_QUIET) vbell(); else beep(); } /* * Clear the screen. */ public void clear() { #if !MSDOS_COMPILER tputs(sc_clear, sc_height, putchr); #else flush(); #if MSDOS_COMPILER==WIN32C - /* - * This will clear only the currently visible rows of the NT - * console buffer, which means none of the precious scrollback - * rows are touched making for faster scrolling. Note that, if - * the window has fewer columns than the console buffer (i.e. - * there is a horizontal scrollbar as well), the entire width - * of the visible rows will be cleared. - */ - { - COORD topleft; - DWORD nchars; - DWORD winsz; - CONSOLE_SCREEN_BUFFER_INFO csbi; - - /* get the number of cells in the current buffer */ - GetConsoleScreenBufferInfo(con_out, &csbi); - winsz = csbi.dwSize.X * (csbi.srWindow.Bottom - csbi.srWindow.Top + 1); - topleft.X = 0; - topleft.Y = csbi.srWindow.Top; - - curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); - FillConsoleOutputCharacter(con_out, ' ', winsz, topleft, &nchars); - FillConsoleOutputAttribute(con_out, curr_attr, winsz, topleft, &nchars); - } + win32_clear(); #else _clearscreen(_GCLEARSCREEN); #endif #endif } /* * Clear from the cursor to the end of the cursor's line. * {{ This must not move the cursor. }} */ public void clear_eol() { #if !MSDOS_COMPILER tputs(sc_eol_clear, 1, putchr); #else #if MSDOS_COMPILER==MSOFTC short top, left; short bot, right; struct rccoord tpos; flush(); /* * Save current state. */ tpos = _gettextposition(); _gettextwindow(&top, &left, &bot, &right); /* * Set a temporary window to the current line, * from the cursor's position to the right edge of the screen. * Then clear that window. */ _settextwindow(tpos.row, tpos.col, tpos.row, sc_width); _clearscreen(_GWINDOW); /* * Restore state. */ _settextwindow(top, left, bot, right); _settextposition(tpos.row, tpos.col); #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC flush(); clreol(); #else #if MSDOS_COMPILER==WIN32C DWORD nchars; COORD cpos; CONSOLE_SCREEN_BUFFER_INFO scr; flush(); memset(&scr, 0, sizeof(scr)); GetConsoleScreenBufferInfo(con_out, &scr); cpos.X = scr.dwCursorPosition.X; cpos.Y = scr.dwCursorPosition.Y; curr_attr = MAKEATTR(nm_fg_color, nm_bg_color); FillConsoleOutputAttribute(con_out, curr_attr, scr.dwSize.X - cpos.X, cpos, &nchars); FillConsoleOutputCharacter(con_out, ' ', scr.dwSize.X - cpos.X, cpos, &nchars); #endif #endif #endif #endif } /* * Clear the current line. * Clear the screen if there's off-screen memory below the display. */ static void clear_eol_bot() { #if MSDOS_COMPILER clear_eol(); #else if (below_mem) tputs(sc_eos_clear, 1, putchr); else tputs(sc_eol_clear, 1, putchr); #endif } /* * Clear the bottom line of the display. * Leave the cursor at the beginning of the bottom line. */ public void clear_bot() { /* * If we're in a non-normal attribute mode, temporarily exit * the mode while we do the clear. Some terminals fill the * cleared area with the current attribute. */ lower_left(); switch (attrmode) { case AT_STANDOUT: so_exit(); clear_eol_bot(); so_enter(); break; case AT_UNDERLINE: ul_exit(); clear_eol_bot(); ul_enter(); break; case AT_BOLD: bo_exit(); clear_eol_bot(); bo_enter(); break; case AT_BLINK: bl_exit(); clear_eol_bot(); bl_enter(); break; default: clear_eol_bot(); break; } } /* * Begin "standout" (bold, underline, or whatever). */ public void so_enter() { #if !MSDOS_COMPILER tputs(sc_s_in, 1, putchr); #else flush(); SETCOLORS(so_fg_color, so_bg_color); #endif attrmode = AT_STANDOUT; } /* * End "standout". */ public void so_exit() { #if !MSDOS_COMPILER tputs(sc_s_out, 1, putchr); #else flush(); SETCOLORS(nm_fg_color, nm_bg_color); #endif attrmode = AT_NORMAL; } /* * Begin "underline" (hopefully real underlining, * otherwise whatever the terminal provides). */ public void ul_enter() { #if !MSDOS_COMPILER tputs(sc_u_in, 1, putchr); #else flush(); SETCOLORS(ul_fg_color, ul_bg_color); #endif attrmode = AT_UNDERLINE; } /* * End "underline". */ public void ul_exit() { #if !MSDOS_COMPILER tputs(sc_u_out, 1, putchr); #else flush(); SETCOLORS(nm_fg_color, nm_bg_color); #endif attrmode = AT_NORMAL; } /* * Begin "bold" */ public void bo_enter() { #if !MSDOS_COMPILER tputs(sc_b_in, 1, putchr); #else flush(); SETCOLORS(bo_fg_color, bo_bg_color); #endif attrmode = AT_BOLD; } /* * End "bold". */ public void bo_exit() { #if !MSDOS_COMPILER tputs(sc_b_out, 1, putchr); #else flush(); SETCOLORS(nm_fg_color, nm_bg_color); #endif attrmode = AT_NORMAL; } /* * Begin "blink" */ public void bl_enter() { #if !MSDOS_COMPILER tputs(sc_bl_in, 1, putchr); #else flush(); SETCOLORS(bl_fg_color, bl_bg_color); #endif attrmode = AT_BLINK; } /* * End "blink". */ public void bl_exit() { #if !MSDOS_COMPILER tputs(sc_bl_out, 1, putchr); #else flush(); SETCOLORS(nm_fg_color, nm_bg_color); #endif attrmode = AT_NORMAL; } #if 0 /* No longer used */ /* * Erase the character to the left of the cursor * and move the cursor left. */ public void backspace() { #if !MSDOS_COMPILER /* * Erase the previous character by overstriking with a space. */ tputs(sc_backspace, 1, putchr); putchr(' '); tputs(sc_backspace, 1, putchr); #else #if MSDOS_COMPILER==MSOFTC struct rccoord tpos; flush(); tpos = _gettextposition(); if (tpos.col <= 1) return; _settextposition(tpos.row, tpos.col-1); _outtext(" "); _settextposition(tpos.row, tpos.col-1); #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC cputs("\b"); #else #if MSDOS_COMPILER==WIN32C COORD cpos; DWORD cChars; CONSOLE_SCREEN_BUFFER_INFO scr; flush(); GetConsoleScreenBufferInfo(con_out, &scr); cpos = scr.dwCursorPosition; if (cpos.X <= 0) return; cpos.X--; SetConsoleCursorPosition(con_out, cpos); FillConsoleOutputCharacter(con_out, (TCHAR)' ', 1, cpos, &cChars); SetConsoleCursorPosition(con_out, cpos); #endif #endif #endif #endif } #endif /* 0 */ /* * Output a plain backspace, without erasing the previous char. */ public void putbs() { #if !MSDOS_COMPILER tputs(sc_backspace, 1, putchr); #else int row, col; flush(); { #if MSDOS_COMPILER==MSOFTC struct rccoord tpos; tpos = _gettextposition(); row = tpos.row; col = tpos.col; #else #if MSDOS_COMPILER==BORLANDC || MSDOS_COMPILER==DJGPPC row = wherey(); col = wherex(); #else #if MSDOS_COMPILER==WIN32C CONSOLE_SCREEN_BUFFER_INFO scr; GetConsoleScreenBufferInfo(con_out, &scr); row = scr.dwCursorPosition.Y - scr.srWindow.Top + 1; col = scr.dwCursorPosition.X - scr.srWindow.Left + 1; #endif #endif #endif } if (col <= 1) return; _settextposition(row, col-1); #endif /* MSDOS_COMPILER */ } #if MSDOS_COMPILER==WIN32C /* * Determine whether an input character is waiting to be read. */ static int win32_kbhit(tty) HANDLE tty; { INPUT_RECORD ip; DWORD read; if (keyCount > 0) return (TRUE); currentKey.ascii = 0; currentKey.scan = 0; /* * Wait for a real key-down event, but * ignore SHIFT and CONTROL key events. */ do { PeekConsoleInput(tty, &ip, 1, &read); if (read == 0) return (FALSE); ReadConsoleInput(tty, &ip, 1, &read); } while (ip.EventType != KEY_EVENT || ip.Event.KeyEvent.bKeyDown != TRUE || ip.Event.KeyEvent.wVirtualScanCode == 0 || ip.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT || ip.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL || ip.Event.KeyEvent.wVirtualKeyCode == VK_MENU); currentKey.ascii = ip.Event.KeyEvent.uChar.AsciiChar; currentKey.scan = ip.Event.KeyEvent.wVirtualScanCode; keyCount = ip.Event.KeyEvent.wRepeatCount; if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) { switch (currentKey.scan) { case PCK_ALT_E: /* letter 'E' */ currentKey.ascii = 0; break; } } else if (ip.Event.KeyEvent.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) { switch (currentKey.scan) { case PCK_RIGHT: /* right arrow */ currentKey.scan = PCK_CTL_RIGHT; break; case PCK_LEFT: /* left arrow */ currentKey.scan = PCK_CTL_LEFT; break; case PCK_DELETE: /* delete */ currentKey.scan = PCK_CTL_DELETE; break; } } return (TRUE); } /* * Read a character from the keyboard. */ public char WIN32getch(tty) int tty; { int ascii; if (pending_scancode) { pending_scancode = 0; return ((char)(currentKey.scan & 0x00FF)); } while (win32_kbhit((HANDLE)tty) == FALSE) { Sleep(20); if (ABORT_SIGS()) return ('\003'); continue; } keyCount --; ascii = currentKey.ascii; /* * On PC's, the extended keys return a 2 byte sequence beginning * with '00', so if the ascii code is 00, the next byte will be * the lsb of the scan code. */ pending_scancode = (ascii == 0x00); return ((char)ascii); } #endif Index: head/contrib/less/search.c =================================================================== --- head/contrib/less/search.c (revision 89021) +++ head/contrib/less/search.c (revision 89022) @@ -1,1363 +1,1345 @@ /* $FreeBSD$ */ /* * Copyright (C) 1984-2000 Mark Nudelman * * You may distribute under the terms of either the GNU General Public * License or the Less License, as specified in the README file. * * For more information about less, or for information on how to * contact the author, see the README file. */ /* * Routines to search a file for a pattern. */ #include "less.h" #include "position.h" #define MINPOS(a,b) (((a) < (b)) ? (a) : (b)) #define MAXPOS(a,b) (((a) > (b)) ? (a) : (b)) #if HAVE_POSIX_REGCOMP #include #ifdef REG_EXTENDED #define REGCOMP_FLAG (more_mode ? 0 : REG_EXTENDED) #else #define REGCOMP_FLAG 0 #endif #endif #if HAVE_PCRE #include #endif #if HAVE_RE_COMP char *re_comp(); int re_exec(); #endif #if HAVE_REGCMP char *regcmp(); char *regex(); extern char *__loc1; #endif #if HAVE_V8_REGCOMP #include "regexp.h" #endif static int match(); extern int sigs; extern int how_search; extern int caseless; extern int linenums; extern int sc_height; extern int jump_sline; extern int bs_mode; extern int more_mode; extern int status_col; extern POSITION start_attnpos; extern POSITION end_attnpos; #if HILITE_SEARCH extern int hilite_search; extern int screen_trashed; extern int size_linebuf; extern int squished; extern int can_goto_line; static int hide_hilite; static POSITION prep_startpos; static POSITION prep_endpos; struct hilite { struct hilite *hl_next; POSITION hl_startpos; POSITION hl_endpos; }; static struct hilite hilite_anchor = { NULL, NULL_POSITION, NULL_POSITION }; #define hl_first hl_next #endif /* * These are the static variables that represent the "remembered" * search pattern. */ #if HAVE_POSIX_REGCOMP static regex_t *regpattern = NULL; #endif #if HAVE_PCRE pcre *regpattern = NULL; #endif #if HAVE_RE_COMP int re_pattern = 0; #endif #if HAVE_REGCMP static char *cpattern = NULL; #endif #if HAVE_V8_REGCOMP static struct regexp *regpattern = NULL; #endif static int is_caseless; static int is_ucase_pattern; static int last_search_type; static char *last_pattern = NULL; /* * Convert text. Perform one or more of these transformations: */ #define CVT_TO_LC 01 /* Convert upper-case to lower-case */ #define CVT_BS 02 /* Do backspace processing */ #define CVT_CRLF 04 /* Remove CR after LF */ static void cvt_text(odst, osrc, ops) char *odst; char *osrc; int ops; { register char *dst; register char *src; for (src = osrc, dst = odst; *src != '\0'; src++, dst++) { if ((ops & CVT_TO_LC) && isupper((unsigned char) *src)) /* Convert uppercase to lowercase. */ *dst = tolower((unsigned char) *src); else if ((ops & CVT_BS) && *src == '\b' && dst > odst) /* Delete BS and preceding char. */ dst -= 2; else /* Just copy. */ *dst = *src; } if ((ops & CVT_CRLF) && dst > odst && dst[-1] == '\r') dst--; *dst = '\0'; } /* * Are there any uppercase letters in this string? */ static int is_ucase(s) char *s; { register char *p; for (p = s; *p != '\0'; p++) if (isupper((unsigned char) *p)) return (1); return (0); } /* * Is there a previous (remembered) search pattern? */ static int prev_pattern() { if (last_search_type & SRCH_NO_REGEX) return (last_pattern != NULL); #if HAVE_POSIX_REGCOMP return (regpattern != NULL); #endif #if HAVE_PCRE return (regpattern != NULL); #endif #if HAVE_RE_COMP return (re_pattern != 0); #endif #if HAVE_REGCMP return (cpattern != NULL); #endif #if HAVE_V8_REGCOMP return (regpattern != NULL); #endif #if NO_REGEX return (last_pattern != NULL); #endif } #if HILITE_SEARCH /* * Repaint the hilites currently displayed on the screen. * Repaint each line which contains highlighted text. * If on==0, force all hilites off. */ public void repaint_hilite(on) int on; { int slinenum; POSITION pos; POSITION epos; int save_hide_hilite; if (squished) repaint(); save_hide_hilite = hide_hilite; if (!on) { if (hide_hilite) return; hide_hilite = 1; } if (!can_goto_line) { repaint(); hide_hilite = save_hide_hilite; return; } for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) { pos = position(slinenum); if (pos == NULL_POSITION) continue; epos = position(slinenum+1); /* * If any character in the line is highlighted, * repaint the line. */ if (is_hilited(pos, epos, 1)) { (void) forw_line(pos); goto_line(slinenum); put_line(); } } hide_hilite = save_hide_hilite; } /* * Clear the attn hilite. */ public void clear_attn() { int slinenum; POSITION old_start_attnpos; POSITION old_end_attnpos; POSITION pos; POSITION epos; if (start_attnpos == NULL_POSITION) return; old_start_attnpos = start_attnpos; old_end_attnpos = end_attnpos; start_attnpos = end_attnpos = NULL_POSITION; if (!can_goto_line) { repaint(); return; } if (squished) repaint(); for (slinenum = TOP; slinenum < TOP + sc_height-1; slinenum++) { pos = position(slinenum); if (pos == NULL_POSITION) continue; epos = position(slinenum+1); if (pos < old_end_attnpos && (epos == NULL_POSITION || epos > old_start_attnpos)) { (void) forw_line(pos); goto_line(slinenum); put_line(); } } } #endif /* * Hide search string highlighting. */ public void undo_search() { if (!prev_pattern()) { error("No previous regular expression", NULL_PARG); return; } #if HILITE_SEARCH hide_hilite = !hide_hilite; repaint_hilite(1); #endif } /* * Compile a search pattern, for future use by match_pattern. */ static int compile_pattern(pattern, search_type) char *pattern; int search_type; { if ((search_type & SRCH_NO_REGEX) == 0) { #if HAVE_POSIX_REGCOMP regex_t *s = (regex_t *) ecalloc(1, sizeof(regex_t)); if (regcomp(s, pattern, REGCOMP_FLAG)) { free(s); error("Invalid pattern", NULL_PARG); return (-1); } if (regpattern != NULL) regfree(regpattern); regpattern = s; #endif #if HAVE_PCRE pcre *comp; const char *errstring; int erroffset; PARG parg; comp = pcre_compile(pattern, 0, &errstring, &erroffset, NULL); if (comp == NULL) { parg.p_string = (char *) errstring; error("%s", &parg); return (-1); } regpattern = comp; #endif #if HAVE_RE_COMP PARG parg; if ((parg.p_string = re_comp(pattern)) != NULL) { error("%s", &parg); return (-1); } re_pattern = 1; #endif #if HAVE_REGCMP char *s; if ((s = regcmp(pattern, 0)) == NULL) { error("Invalid pattern", NULL_PARG); return (-1); } if (cpattern != NULL) free(cpattern); cpattern = s; #endif #if HAVE_V8_REGCOMP struct regexp *s; if ((s = regcomp(pattern)) == NULL) { /* * regcomp has already printed an error message * via regerror(). */ return (-1); } if (regpattern != NULL) free(regpattern); regpattern = s; #endif } if (last_pattern != NULL) free(last_pattern); last_pattern = (char *) calloc(1, strlen(pattern)+1); if (last_pattern != NULL) strcpy(last_pattern, pattern); last_search_type = search_type; return (0); } /* * Forget that we have a compiled pattern. */ static void uncompile_pattern() { #if HAVE_POSIX_REGCOMP if (regpattern != NULL) regfree(regpattern); regpattern = NULL; #endif #if HAVE_PCRE if (regpattern != NULL) pcre_free(regpattern); regpattern = NULL; #endif #if HAVE_RE_COMP re_pattern = 0; #endif #if HAVE_REGCMP if (cpattern != NULL) free(cpattern); cpattern = NULL; #endif #if HAVE_V8_REGCOMP if (regpattern != NULL) free(regpattern); regpattern = NULL; #endif last_pattern = NULL; } /* * Perform a pattern match with the previously compiled pattern. * Set sp and ep to the start and end of the matched string. */ static int match_pattern(line, sp, ep, notbol) char *line; char **sp; char **ep; int notbol; { int matched; if (last_search_type & SRCH_NO_REGEX) return (match(last_pattern, line, sp, ep)); #if HAVE_POSIX_REGCOMP { regmatch_t rm; int flags = (notbol) ? REG_NOTBOL : 0; matched = !regexec(regpattern, line, 1, &rm, flags); if (!matched) return (0); #ifndef __WATCOMC__ *sp = line + rm.rm_so; *ep = line + rm.rm_eo; #else *sp = rm.rm_sp; *ep = rm.rm_ep; #endif } #endif #if HAVE_PCRE { int flags = (notbol) ? PCRE_NOTBOL : 0; int ovector[3]; matched = pcre_exec(regpattern, NULL, line, strlen(line), 0, flags, ovector, 3) >= 0; if (!matched) return (0); *sp = line + ovector[0]; *ep = line + ovector[1]; } #endif #if HAVE_RE_COMP matched = (re_exec(line) == 1); /* * re_exec doesn't seem to provide a way to get the matched string. */ *sp = *ep = NULL; #endif #if HAVE_REGCMP *ep = regex(cpattern, line); matched = (*ep != NULL); if (!matched) return (0); *sp = __loc1; #endif #if HAVE_V8_REGCOMP #if HAVE_REGEXEC2 matched = regexec2(regpattern, line, notbol); #else matched = regexec(regpattern, line); #endif if (!matched) return (0); *sp = regpattern->startp[0]; *ep = regpattern->endp[0]; #endif #if NO_REGEX matched = match(last_pattern, line, sp, ep); #endif return (matched); } #if HILITE_SEARCH /* * Clear the hilite list. */ public void clr_hilite() { struct hilite *hl; struct hilite *nexthl; for (hl = hilite_anchor.hl_first; hl != NULL; hl = nexthl) { nexthl = hl->hl_next; free((void*)hl); } hilite_anchor.hl_first = NULL; prep_startpos = prep_endpos = NULL_POSITION; } /* * Should any characters in a specified range be highlighted? * If nohide is nonzero, don't consider hide_hilite. */ public int is_hilited(pos, epos, nohide) POSITION pos; POSITION epos; int nohide; { struct hilite *hl; if (!status_col && start_attnpos != NULL_POSITION && pos < end_attnpos && (epos == NULL_POSITION || epos > start_attnpos)) /* * The attn line overlaps this range. */ return (1); if (hilite_search == 0) /* * Not doing highlighting. */ return (0); if (!nohide && hide_hilite) /* * Highlighting is hidden. */ return (0); /* * Look at each highlight and see if any part of it falls in the range. */ for (hl = hilite_anchor.hl_first; hl != NULL; hl = hl->hl_next) { if (hl->hl_endpos > pos && (epos == NULL_POSITION || epos > hl->hl_startpos)) return (1); } return (0); } /* * Add a new hilite to a hilite list. */ static void add_hilite(anchor, hl) struct hilite *anchor; struct hilite *hl; { struct hilite *ihl; /* * Hilites are sorted in the list; find where new one belongs. * Insert new one after ihl. */ for (ihl = anchor; ihl->hl_next != NULL; ihl = ihl->hl_next) { if (ihl->hl_next->hl_startpos > hl->hl_startpos) break; } /* * Truncate hilite so it doesn't overlap any existing ones * above and below it. */ if (ihl != anchor) hl->hl_startpos = MAXPOS(hl->hl_startpos, ihl->hl_endpos); if (ihl->hl_next != NULL) hl->hl_endpos = MINPOS(hl->hl_endpos, ihl->hl_next->hl_startpos); if (hl->hl_startpos >= hl->hl_endpos) { /* * Hilite was truncated out of existence. */ free(hl); return; } hl->hl_next = ihl->hl_next; ihl->hl_next = hl; } /* * Adjust hl_startpos & hl_endpos to account for backspace processing. */ static void adj_hilite(anchor, linepos) struct hilite *anchor; POSITION linepos; { char *line; struct hilite *hl; int checkstart; POSITION opos; POSITION npos; /* * The line was already scanned and hilites were added (in hilite_line). * But it was assumed that each char position in the line * correponds to one char position in the file. * This may not be true if there are backspaces in the line. * Get the raw line again. Look at each character. */ (void) forw_raw_line(linepos, &line); opos = npos = linepos; hl = anchor->hl_first; checkstart = TRUE; while (hl != NULL) { /* * See if we need to adjust the current hl_startpos or * hl_endpos. After adjusting startpos[i], move to endpos[i]. * After adjusting endpos[i], move to startpos[i+1]. * The hilite list must be sorted thus: * startpos[0] < endpos[0] <= startpos[1] < endpos[1] <= etc. */ if (checkstart && hl->hl_startpos == opos) { hl->hl_startpos = npos; checkstart = FALSE; continue; /* {{ not really necessary }} */ } else if (!checkstart && hl->hl_endpos == opos) { hl->hl_endpos = npos; checkstart = TRUE; hl = hl->hl_next; continue; /* {{ necessary }} */ } if (*line == '\0') break; opos++; npos++; line++; while (line[0] == '\b' && line[1] != '\0') { /* * Found a backspace. The file position moves * forward by 2 relative to the processed line * which was searched in hilite_line. */ npos += 2; line += 2; } } } /* * Make a hilite for each string in a physical line which matches * the current pattern. * sp,ep delimit the first match already found. */ static void hilite_line(linepos, line, sp, ep) POSITION linepos; char *line; char *sp; char *ep; { char *searchp; struct hilite *hl; struct hilite hilites; if (sp == NULL || ep == NULL) return; /* * sp and ep delimit the first match in the line. * Mark the corresponding file positions, then * look for further matches and mark them. * {{ This technique, of calling match_pattern on subsequent * substrings of the line, may mark more than is correct * if the pattern starts with "^". This bug is fixed * for those regex functions that accept a notbol parameter * (currently POSIX and V8-with-regexec2). }} */ searchp = line; /* * Put the hilites into a temporary list until they're adjusted. */ hilites.hl_first = NULL; do { if (ep > sp) { /* * Assume that each char position in the "line" * buffer corresponds to one char position in the file. * This is not quite true; we need to adjust later. */ hl = (struct hilite *) ecalloc(1, sizeof(struct hilite)); hl->hl_startpos = linepos + (sp-line); hl->hl_endpos = linepos + (ep-line); add_hilite(&hilites, hl); } /* * If we matched more than zero characters, * move to the first char after the string we matched. * If we matched zero, just move to the next char. */ if (ep > searchp) searchp = ep; else if (*searchp != '\0') searchp++; else /* end of line */ break; } while (match_pattern(searchp, &sp, &ep, 1)); if (bs_mode == BS_SPECIAL) { /* * If there were backspaces in the original line, they * were removed, and hl_startpos/hl_endpos are not correct. * {{ This is very ugly. }} */ adj_hilite(&hilites, linepos); } /* * Now put the hilites into the real list. */ while ((hl = hilites.hl_next) != NULL) { hilites.hl_next = hl->hl_next; add_hilite(&hilite_anchor, hl); } } #endif /* * Change the caseless-ness of searches. * Updates the internal search state to reflect a change in the -i flag. */ public void chg_caseless() { if (!is_ucase_pattern) /* * Pattern did not have uppercase. * Just set the search caselessness to the global caselessness. */ is_caseless = caseless; else /* * Pattern did have uppercase. * Discard the pattern; we can't change search caselessness now. */ uncompile_pattern(); } #if HILITE_SEARCH /* * Find matching text which is currently on screen and highlight it. */ static void hilite_screen() { struct scrpos scrpos; get_scrpos(&scrpos); if (scrpos.pos == NULL_POSITION) return; prep_hilite(scrpos.pos, position(BOTTOM_PLUS_ONE), -1); repaint_hilite(1); } /* * Change highlighting parameters. */ public void chg_hilite() { /* * Erase any highlights currently on screen. */ clr_hilite(); hide_hilite = 0; if (hilite_search == OPT_ONPLUS) /* * Display highlights. */ hilite_screen(); } #endif /* * Figure out where to start a search. */ static POSITION search_pos(search_type) int search_type; { POSITION pos; int linenum; if (empty_screen()) { /* * Start at the beginning (or end) of the file. * The empty_screen() case is mainly for * command line initiated searches; * for example, "+/xyz" on the command line. * Also for multi-file (SRCH_PAST_EOF) searches. */ if (search_type & SRCH_FORW) { return (ch_zero()); } else { pos = ch_length(); if (pos == NULL_POSITION) { (void) ch_end_seek(); pos = ch_length(); } return (pos); } } if (how_search) { /* * Search does not include current screen. */ if (search_type & SRCH_FORW) linenum = BOTTOM_PLUS_ONE; else linenum = TOP; pos = position(linenum); } else { /* * Search includes current screen. * It starts at the jump target (if searching backwards), * or at the jump target plus one (if forwards). */ linenum = adjsline(jump_sline); pos = position(linenum); if (search_type & SRCH_FORW) { pos = forw_raw_line(pos, (char **)NULL); while (pos == NULL_POSITION) { if (++linenum >= sc_height) break; pos = position(linenum); } } else { while (pos == NULL_POSITION) { if (--linenum < 0) break; pos = position(linenum); } } } return (pos); } /* * Search a subset of the file, specified by start/end position. */ static int search_range(pos, endpos, search_type, matches, maxlines, plinepos, pendpos) POSITION pos; POSITION endpos; int search_type; int matches; int maxlines; POSITION *plinepos; POSITION *pendpos; { char *line; int linenum; char *sp, *ep; int line_match; POSITION linepos, oldpos; linenum = find_linenum(pos); oldpos = pos; for (;;) { /* * Get lines until we find a matching one or until * we hit end-of-file (or beginning-of-file if we're * going backwards), or until we hit the end position. */ if (ABORT_SIGS()) { /* * A signal aborts the search. */ return (-1); } if ((endpos != NULL_POSITION && pos >= endpos) || maxlines == 0) { /* * Reached end position without a match. */ if (pendpos != NULL) *pendpos = pos; return (matches); } if (maxlines > 0) maxlines--; if (search_type & SRCH_FORW) { /* * Read the next line, and save the * starting position of that line in linepos. */ linepos = pos; pos = forw_raw_line(pos, &line); if (linenum != 0) linenum++; } else { /* * Read the previous line and save the * starting position of that line in linepos. */ pos = back_raw_line(pos, &line); linepos = pos; if (linenum != 0) linenum--; } if (pos == NULL_POSITION) { /* * Reached EOF/BOF without a match. */ if (pendpos != NULL) *pendpos = oldpos; return (matches); } /* * If we're using line numbers, we might as well * remember the information we have now (the position * and line number of the current line). * Don't do it for every line because it slows down * the search. Remember the line number only if * we're "far" from the last place we remembered it. */ if (linenums && abs((int)(pos - oldpos)) > 1024) add_lnum(linenum, pos); oldpos = pos; /* * If it's a caseless search, convert the line to lowercase. * If we're doing backspace processing, delete backspaces. */ if (is_caseless || bs_mode == BS_SPECIAL) { int ops = 0; if (is_caseless) ops |= CVT_TO_LC; if (bs_mode == BS_SPECIAL) ops |= CVT_BS; if (bs_mode != BS_CONTROL) ops |= CVT_CRLF; cvt_text(line, line, ops); } else if (bs_mode != BS_CONTROL) { cvt_text(line, line, CVT_CRLF); } /* * Test the next line to see if we have a match. * We are successful if we either want a match and got one, * or if we want a non-match and got one. */ line_match = match_pattern(line, &sp, &ep, 0); line_match = (!(search_type & SRCH_NO_MATCH) && line_match) || ((search_type & SRCH_NO_MATCH) && !line_match); if (!line_match) continue; /* * Got a match. */ if (search_type & SRCH_FIND_ALL) { #if HILITE_SEARCH /* * We are supposed to find all matches in the range. * Just add the matches in this line to the * hilite list and keep searching. */ if (line_match) hilite_line(linepos, line, sp, ep); #endif } else if (--matches <= 0) { /* * Found the one match we're looking for. * Return it. */ #if HILITE_SEARCH if (hilite_search == 1) { /* * Clear the hilite list and add only * the matches in this one line. */ clr_hilite(); if (line_match) hilite_line(linepos, line, sp, ep); } #endif if (plinepos != NULL) *plinepos = linepos; return (0); } } } /* * Search for the n-th occurrence of a specified pattern, * either forward or backward. * Return the number of matches not yet found in this file * (that is, n minus the number of matches found). * Return -1 if the search should be aborted. * Caller may continue the search in another file * if less than n matches are found in this file. */ public int search(search_type, pattern, n) int search_type; char *pattern; int n; { POSITION pos; int ucase; if (pattern == NULL || *pattern == '\0') { /* * A null pattern means use the previously compiled pattern. */ if (!prev_pattern()) { error("No previous regular expression", NULL_PARG); return (-1); } if ((search_type & SRCH_NO_REGEX) != (last_search_type & SRCH_NO_REGEX)) { error("Please re-enter search pattern", NULL_PARG); return -1; } #if HILITE_SEARCH if (hilite_search == OPT_ON) { /* * Erase the highlights currently on screen. * If the search fails, we'll redisplay them later. */ repaint_hilite(0); } if (hilite_search == OPT_ONPLUS && hide_hilite) { /* * Highlight any matches currently on screen, * before we actually start the search. */ hide_hilite = 0; hilite_screen(); } hide_hilite = 0; #endif } else { /* * Compile the pattern. */ ucase = is_ucase(pattern); if (caseless == OPT_ONPLUS) cvt_text(pattern, pattern, CVT_TO_LC); if (compile_pattern(pattern, search_type) < 0) return (-1); /* * Ignore case if -I is set OR * -i is set AND the pattern is all lowercase. */ is_ucase_pattern = ucase; if (is_ucase_pattern && caseless != OPT_ONPLUS) is_caseless = 0; else is_caseless = caseless; #if HILITE_SEARCH if (hilite_search) { /* * Erase the highlights currently on screen. * Also permanently delete them from the hilite list. */ repaint_hilite(0); hide_hilite = 0; clr_hilite(); } if (hilite_search == OPT_ONPLUS) { /* * Highlight any matches currently on screen, * before we actually start the search. */ hilite_screen(); } #endif } /* * Figure out where to start the search. */ pos = search_pos(search_type); if (pos == NULL_POSITION) { /* * Can't find anyplace to start searching from. */ if (search_type & SRCH_PAST_EOF) return (n); /* repaint(); -- why was this here? */ error("Nothing to search", NULL_PARG); return (-1); } n = search_range(pos, NULL_POSITION, search_type, n, -1, &pos, (POSITION*)NULL); if (n != 0) { /* * Search was unsuccessful. */ #if HILITE_SEARCH if (hilite_search == OPT_ON && n > 0) /* * Redisplay old hilites. */ repaint_hilite(1); #endif return (n); } if (!(search_type & SRCH_NO_MOVE)) { /* * Go to the matching line. */ jump_loc(pos, jump_sline); } #if HILITE_SEARCH if (hilite_search == OPT_ON) /* * Display new hilites in the matching line. */ repaint_hilite(1); #endif return (0); } #if HILITE_SEARCH /* * Prepare hilites in a given range of the file. * * The pair (prep_startpos,prep_endpos) delimits a contiguous region * of the file that has been "prepared"; that is, scanned for matches for * the current search pattern, and hilites have been created for such matches. * If prep_startpos == NULL_POSITION, the prep region is empty. * If prep_endpos == NULL_POSITION, the prep region extends to EOF. * prep_hilite asks that the range (spos,epos) be covered by the prep region. */ public void prep_hilite(spos, epos, maxlines) POSITION spos; POSITION epos; int maxlines; { POSITION nprep_startpos = prep_startpos; POSITION nprep_endpos = prep_endpos; POSITION new_epos; POSITION max_epos; int result; int i; /* * Search beyond where we're asked to search, so the prep region covers * more than we need. Do one big search instead of a bunch of small ones. */ #define SEARCH_MORE (3*size_linebuf) if (!prev_pattern()) return; /* * If we're limited to a max number of lines, figure out the * file position we should stop at. */ if (maxlines < 0) max_epos = NULL_POSITION; else { max_epos = spos; for (i = 0; i < maxlines; i++) max_epos = forw_raw_line(max_epos, (char **)NULL); } /* * Find two ranges: * The range that we need to search (spos,epos); and the range that * the "prep" region will then cover (nprep_startpos,nprep_endpos). */ if (prep_startpos == NULL_POSITION || (epos != NULL_POSITION && epos < prep_startpos) || spos > prep_endpos) { /* * New range is not contiguous with old prep region. * Discard the old prep region and start a new one. */ clr_hilite(); if (epos != NULL_POSITION) epos += SEARCH_MORE; nprep_startpos = spos; } else { /* * New range partially or completely overlaps old prep region. */ if (epos == NULL_POSITION) { /* * New range goes to end of file. */ ; } else if (epos > prep_endpos) { /* * New range ends after old prep region. * Extend prep region to end at end of new range. */ epos += SEARCH_MORE; } else /* (epos <= prep_endpos) */ { /* * New range ends within old prep region. * Truncate search to end at start of old prep region. */ epos = prep_startpos; } if (spos < prep_startpos) { /* * New range starts before old prep region. * Extend old prep region backwards to start at * start of new range. */ if (spos < SEARCH_MORE) spos = 0; else spos -= SEARCH_MORE; nprep_startpos = spos; } else /* (spos >= prep_startpos) */ { /* * New range starts within or after old prep region. * Trim search to start at end of old prep region. */ spos = prep_endpos; } } if (epos != NULL_POSITION && max_epos != NULL_POSITION && epos > max_epos) /* * Don't go past the max position we're allowed. */ epos = max_epos; if (epos == NULL_POSITION || epos > spos) { result = search_range(spos, epos, SRCH_FORW|SRCH_FIND_ALL, 0, maxlines, (POSITION*)NULL, &new_epos); if (result < 0) return; if (prep_endpos == NULL_POSITION || new_epos > prep_endpos) nprep_endpos = new_epos; } prep_startpos = nprep_startpos; prep_endpos = nprep_endpos; } #endif /* * Simple pattern matching function. * It supports no metacharacters like *, etc. */ static int match(pattern, buf, pfound, pend) char *pattern, *buf; char **pfound, **pend; { register char *pp, *lp; for ( ; *buf != '\0'; buf++) { for (pp = pattern, lp = buf; *pp == *lp; pp++, lp++) if (*pp == '\0' || *lp == '\0') break; if (*pp == '\0') { if (pfound != NULL) *pfound = buf; if (pend != NULL) *pend = lp; return (1); } } return (0); } #if HAVE_V8_REGCOMP /* * This function is called by the V8 regcomp to report * errors in regular expressions. */ void regerror(s) char *s; { PARG parg; parg.p_string = s; error("%s", &parg); } #endif -#if !HAVE_STRCHR -/* - * strchr is used by regexp.c. - */ - char * -strchr(s, c) - char *s; - int c; -{ - for ( ; *s != '\0'; s++) - if (*s == c) - return (s); - if (c == '\0') - return (s); - return (NULL); -} -#endif -