Index: contrib/mg/NOTES.md =================================================================== --- /dev/null +++ contrib/mg/NOTES.md @@ -0,0 +1,5 @@ +NOTES + +* On some Linux distributions, the backspace key doesn't work + correctly. This can be fixed with `bsmap-mode`; this should probably + be placed in the user's `$HOME/.mg` file. Index: contrib/mg/README-Mg =================================================================== --- /dev/null +++ contrib/mg/README-Mg @@ -0,0 +1,93 @@ +[This is an edited version of the original mg README, updated slightly to +reflect changes in the last 20 years.] + + +Mg (mg) is a Public Domain EMACS style editor. It is "broadly" +compatible with GNU Emacs, the latest creation of Richard M. +Stallman, Chief GNUisance and inventor of Emacs. GNU Emacs (and other +portions of GNU as they are released) are essentially free, (there are +handling charges for obtaining it) and so is Mg. You may never have +to learn another editor. (But probably will, at least long enough to +port Mg...) Mg was formerly named MicroGnuEmacs, the name change was +done at the request of Richard Stallman. + +Mg is not associated with the GNU project, and it does not have the +copyright restrictions present in GNU Emacs. (However, some modules +do have copyright notices.) The Mg authors individually may or may +not agree with the opinions expressed by Richard Stallman in "The GNU +Manifesto". + +This program is intended to be a small, fast, and portable editor for +people who can't (or don't want to) run real Emacs for one reason +or another. It is compatible with GNU because there shouldn't be +any reason to learn more than one Emacs flavor. + + +Beyond the work of Dave Conroy, author of the original public domain +v30, the current version contains the work of: + + blarson@ecla.usc.edu Bob Larson + mic@emx.utexas.edu Mic Kaczmarczik + mwm@violet.berkeley.edu Mike Meyer + sandra@cs.utah.edu Sandra Loosemore + mp1u+@andrew.cmu.edu Michael Portuesi + RCKG01M@CALSTATE.BITNET Stephen Walton + hakanson@mist.cs.orst.edu Marion Hakanson + +People who have worked on previous versions of Mg: + + rtech!daveb@sun.com Dave Brower + +Early release history: + +* Nov 16, 1986: First release to mod.sources +* Mar 3, 1987: First Release (mg1a) via comp.sources.unix +* May 26, 1988: Second release: (mg2a) via comp.sources.misc +* Jan 26, 1992: Linux port released by Charles Hedrick. This version + later makes its way onto tsx-11, Infomagic, and various other Linux + repositories. +* Feb 25, 2000: First import into the OpenBSD tree, where it is + currently maintained with contributions from many others. + +---------------------------------------------------------------------- + +Known limitations: + +Recursive bindings may cause help and key rebinding code to go into +an infinite loop, aborting with a stack overflow. + +Overwrite mode does not work in macros. (Characters are inserted +rather than overwriting.) + +Dired mode has some problems: Rename does not update the buffer. +Doing a dired again will update the buffer (whether it needs it or +not) and will lose any marks for deletion. .. and . are not +recognized as special cases. + +On systems with 16 bit integers, the kill buffer cannot exceed 32767 +bytes. + +Unlike GNU Emacs, Mg's minibuffer isn't multi-line aware and hence +some commands like "shell-command-on-region" always pop up a buffer to +display output irrespective of output's size. + +While navigating source code using Mg's cscope commands, the cursor +is always at the match location rather than in *cscope* buffer. Mg uses +the same keybindings of GNU Emacs's xcscope package for it's cscope commands. +As Mg's keybindings are case-insensitive some of the commands don't have a +default keybinding. + +New implementation oddities: + +insert and define-key are new commands corresponding to the mocklisp +functions in GNU Emacs. (Mg does not have non-command functions.) +(Mg's insert will only insert one string.) + +The display wrap code does not work at all like that of GNU emacs. + +Some commands that do not mimic emacs exactly don't have a "standard" +emacs name. For example 'backup-to-home-directory' is only a partial +implementation of emacs' range of commands that allow a user to +customise the backup file location. If a more complete implementation +were coded of these commands the non standard commands would probably +be removed. Index: contrib/mg/README.md =================================================================== --- /dev/null +++ contrib/mg/README.md @@ -0,0 +1,51 @@ +mg +== +This is a portable version of the Mg editor from OpenBSD. + +Mg is intended to be a small, fast, and portable editor for people who +can't (or don't want to) run emacs for one reason or another, or are not +familiar with the vi editor. It is compatible with emacs because there +shouldn't be any reason to learn more editor types than emacs or vi. + +This repository aggressively tracks upstream. + +Compiling +--------- +`mg` has a simple configure script that generates a `POSIX` `Makefile`. +``` +$ ./configure +$ make +$ sudo make install +``` + +Dependencies +------------ +By default, you need the ncurses library. + +NetBSD users will use the in-base NetBSD curses library. + +If you do not have the ncurses library, you can call `configure` with the +`--with-builtin-curses` flag to compile with a simplified version of the +NetBSD curses library. In this setup, there are no dependencies other than +the system's libc. + +Testing +------- +Tested on recent versions of Arch, Alpine, Cygwin, Debian, DragonFly BSD, +FreeBSD, Mac OS X (10.4 or later), NetBSD, Slackware, and Ubuntu. + +Licensing +--------- +Files originating from `mg` are Public Domain. Files needed for portability +have their own individual license headers. +All licenses are ISC or BSD. + +Commonly asked questions +------------------------ +`mg` does not yet support UTF-8. If you would like to work on this, please +reach out to the tech@ mailing list on OpenBSD. + +Get a tarball +------------- +See the Releases tab on GitHub. +The latest version is mg-7.0. Index: contrib/mg/autoexec.c =================================================================== --- /dev/null +++ contrib/mg/autoexec.c @@ -0,0 +1,116 @@ +/* $OpenBSD: autoexec.c,v 1.18 2021/04/21 14:45:28 lum Exp $ */ +/* this file is in the public domain */ +/* Author: Vincent Labrecque April 2002 */ + +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" + +struct autoexec { + SLIST_ENTRY(autoexec) next; /* link in the linked list */ + const char *pattern; /* Pattern to match to filenames */ + PF fp; +}; + +static SLIST_HEAD(, autoexec) autos; +static int ready; + + +#define AUTO_GROW 8 +/* + * Return a NULL terminated array of function pointers to be called + * when we open a file that matches . The list must be free(ed) + * after use. + */ +PF * +find_autoexec(const char *fname) +{ + PF *pfl, *npfl; + int have, used; + struct autoexec *ae; + + if (!ready) + return (NULL); + + pfl = NULL; + have = 0; + used = 0; + SLIST_FOREACH(ae, &autos, next) { + if (fnmatch(ae->pattern, fname, 0) == 0) { + if (used >= have) { + npfl = reallocarray(pfl, have + AUTO_GROW + 1, + sizeof(PF)); + if (npfl == NULL) + panic("out of memory"); + pfl = npfl; + have += AUTO_GROW; + } + pfl[used++] = ae->fp; + } + } + if (used) + pfl[used] = NULL; + + return (pfl); +} + +int +add_autoexec(const char *pattern, const char *func) +{ + PF fp; + struct autoexec *ae; + + if (!ready) { + SLIST_INIT(&autos); + ready = 1; + } + fp = name_function(func); + if (fp == NULL) + return (FALSE); + ae = malloc(sizeof(*ae)); + if (ae == NULL) + return (FALSE); + ae->fp = fp; + ae->pattern = strdup(pattern); + if (ae->pattern == NULL) { + free(ae); + return (FALSE); + } + SLIST_INSERT_HEAD(&autos, ae, next); + + return (TRUE); +} + +/* + * Register an auto-execute hook; that is, specify a filename pattern + * (conforming to the shell's filename globbing rules) and an associated + * function to execute when a file matching the specified pattern + * is read into a buffer. +*/ +/* ARGSUSED */ +int +auto_execute(int f, int n) +{ + char patbuf[BUFSIZE], funcbuf[BUFSIZE], *patp, *funcp; + int s; + + if ((patp = eread("Filename pattern: ", patbuf, sizeof(patbuf), + EFNEW | EFCR)) == NULL) + return (ABORT); + else if (patp[0] == '\0') + return (FALSE); + if ((funcp = eread("Execute: ", funcbuf, sizeof(funcbuf), + EFNEW | EFCR | EFFUNC)) == NULL) + return (ABORT); + else if (funcp[0] == '\0') + return (FALSE); + if ((s = add_autoexec(patp, funcp)) != TRUE) + return (s); + return (TRUE); +} Index: contrib/mg/basic.c =================================================================== --- /dev/null +++ contrib/mg/basic.c @@ -0,0 +1,580 @@ +/* $OpenBSD: basic.c,v 1.50 2021/02/27 13:24:52 lum Exp $ */ + +/* This file is in the public domain */ + +/* + * Basic cursor motion commands. + * + * The routines in this file are the basic + * command functions for moving the cursor around on + * the screen, setting mark, and swapping dot with + * mark. Only moves between lines, which might make the + * current buffer framing bad, are hard. + */ + +#include +#include +#include +#include +#include +#include + +#include "def.h" + +#define percint(n1, n2) ((n1 * (int) n2) * 0.1) + +/* + * Go to beginning of line. + */ +/* ARGSUSED */ +int +gotobol(int f, int n) +{ + if (n == 0) + return (TRUE); + + curwp->w_doto = 0; + return (TRUE); +} + +/* + * Move cursor backwards. Do the + * right thing if the count is less than + * 0. Error if you try to move back from + * the beginning of the buffer. + */ +/* ARGSUSED */ +int +backchar(int f, int n) +{ + struct line *lp; + + if (n < 0) + return (forwchar(f, -n)); + while (n--) { + if (curwp->w_doto == 0) { + if ((lp = lback(curwp->w_dotp)) == curbp->b_headp) { + if (!(f & FFRAND)) + (void)dobeep_msg("Beginning " + "of buffer"); + return (FALSE); + } + curwp->w_dotp = lp; + curwp->w_doto = llength(lp); + curwp->w_rflag |= WFMOVE; + curwp->w_dotline--; + } else + curwp->w_doto--; + } + return (TRUE); +} + +/* + * Go to end of line. + */ +/* ARGSUSED */ +int +gotoeol(int f, int n) +{ + if (n == 0) + return (TRUE); + + curwp->w_doto = llength(curwp->w_dotp); + return (TRUE); +} + +/* + * Move cursor forwards. Do the + * right thing if the count is less than + * 0. Error if you try to move forward + * from the end of the buffer. + */ +/* ARGSUSED */ +int +forwchar(int f, int n) +{ + if (n < 0) + return (backchar(f, -n)); + while (n--) { + if (curwp->w_doto == llength(curwp->w_dotp)) { + curwp->w_dotp = lforw(curwp->w_dotp); + if (curwp->w_dotp == curbp->b_headp) { + curwp->w_dotp = lback(curwp->w_dotp); + if (!(f & FFRAND)) + (void)dobeep_msg("End of buffer"); + return (FALSE); + } + curwp->w_doto = 0; + curwp->w_dotline++; + curwp->w_rflag |= WFMOVE; + } else + curwp->w_doto++; + } + return (TRUE); +} + +/* + * Go to the beginning of the buffer. Setting WFFULL is conservative, + * but almost always the case. A universal argument of higher than 9 + * puts the cursor back to the end of buffer. + */ +int +gotobob(int f, int n) +{ + if (!curwp->w_markp) + (void) setmark(f, n); + curwp->w_dotp = bfirstlp(curbp); + curwp->w_doto = 0; + curwp->w_rflag |= WFFULL; + curwp->w_dotline = 1; + if (f & FFOTHARG && n > 0) { + if (n > 9) + gotoeob(0, 0); + else + forwline(f, percint(curwp->w_bufp->b_lines, n) - 1); + } + return (TRUE); +} + +/* + * Go to the end of the buffer. Leave dot 3 lines from the bottom of the + * window if buffer length is longer than window length; same as emacs. + * Setting WFFULL is conservative, but almost always the case. A universal + * argument of higher than 9 puts the cursor back to the start of buffer. + */ +int +gotoeob(int f, int n) +{ + int ln; + struct line *lp; + + if (!curwp->w_markp) + (void) setmark(f, n); + curwp->w_dotp = blastlp(curbp); + curwp->w_doto = llength(curwp->w_dotp); + curwp->w_dotline = curwp->w_bufp->b_lines; + + lp = curwp->w_dotp; + ln = curwp->w_ntrows - 3; + + if (ln < curwp->w_bufp->b_lines && ln >= 3) { + while (ln--) + curwp->w_dotp = lback(curwp->w_dotp); + + curwp->w_linep = curwp->w_dotp; + curwp->w_dotp = lp; + } + if (f & FFOTHARG && n > 0) { + if (n > 9) + gotobob(0, 0); + else + backline(f, percint(curwp->w_bufp->b_lines, n)); + } + + curwp->w_rflag |= WFFULL; + return (TRUE); +} + +/* + * Move forward by full lines. + * If the number of lines to move is less + * than zero, call the backward line function to + * actually do it. The last command controls how + * the goal column is set. + */ +/* ARGSUSED */ +int +forwline(int f, int n) +{ + struct line *dlp; + + if (n < 0) + return (backline(f | FFRAND, -n)); + if ((dlp = curwp->w_dotp) == curbp->b_headp) { + if (!(f & FFRAND)) + (void)dobeep_msg("End of buffer"); + return(TRUE); + } + if ((lastflag & CFCPCN) == 0) /* Fix goal. */ + setgoal(); + thisflag |= CFCPCN; + if (n == 0) + return (TRUE); + while (n--) { + dlp = lforw(dlp); + if (dlp == curbp->b_headp) { + curwp->w_dotp = lback(dlp); + curwp->w_doto = llength(curwp->w_dotp); + curwp->w_rflag |= WFMOVE; + if (!(f & FFRAND)) + (void)dobeep_msg("End of buffer"); + return (TRUE); + } + curwp->w_dotline++; + } + curwp->w_rflag |= WFMOVE; + curwp->w_dotp = dlp; + curwp->w_doto = getgoal(dlp); + + return (TRUE); +} + +/* + * This function is like "forwline", but + * goes backwards. The scheme is exactly the same. + * Check for arguments that are less than zero and + * call your alternate. Figure out the new line and + * call "movedot" to perform the motion. + */ +/* ARGSUSED */ +int +backline(int f, int n) +{ + struct line *dlp; + + if (n < 0) + return (forwline(f | FFRAND, -n)); + if ((lastflag & CFCPCN) == 0) /* Fix goal. */ + setgoal(); + thisflag |= CFCPCN; + dlp = curwp->w_dotp; + if (lback(dlp) == curbp->b_headp) { + if (!(f & FFRAND)) + (void)dobeep_msg("Beginning of buffer"); + return(TRUE); + } + while (n-- && lback(dlp) != curbp->b_headp) { + dlp = lback(dlp); + curwp->w_dotline--; + } + if (n > 0 && !(f & FFRAND)) + (void)dobeep_msg("Beginning of buffer"); + curwp->w_dotp = dlp; + curwp->w_doto = getgoal(dlp); + curwp->w_rflag |= WFMOVE; + return (TRUE); +} + +/* + * Set the current goal column, which is saved in the external variable + * "curgoal", to the current cursor column. The column is never off + * the edge of the screen; it's more like display then show position. + */ +void +setgoal(void) +{ + curgoal = getcolpos(curwp); /* Get the position. */ + /* we can now display past end of display, don't chop! */ +} + +/* + * This routine looks at a line (pointed + * to by the LINE pointer "dlp") and the current + * vertical motion goal column (set by the "setgoal" + * routine above) and returns the best offset to use + * when a vertical motion is made into the line. + */ +int +getgoal(struct line *dlp) +{ + int c, i, col = 0; + char tmp[5]; + + + for (i = 0; i < llength(dlp); i++) { + c = lgetc(dlp, i); + if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif + ) { + col |= 0x07; + col++; + } else if (ISCTRL(c) != FALSE) { + col += 2; + } else if (isprint(c)) + col++; + else { + col += snprintf(tmp, sizeof(tmp), "\\%o", c); + } + if (col > curgoal) + break; + } + return (i); +} + +/* + * Scroll forward by a specified number + * of lines, or by a full page if no argument. + * The "2" is the window overlap (this is the default + * value from ITS EMACS). Because the top line in + * the window is zapped, we have to do a hard + * update and get it back. + */ +/* ARGSUSED */ +int +forwpage(int f, int n) +{ + struct line *lp; + + if (!(f & FFARG)) { + n = curwp->w_ntrows - 2; /* Default scroll. */ + if (n <= 0) /* Forget the overlap */ + n = 1; /* if tiny window. */ + } else if (n < 0) + return (backpage(f | FFRAND, -n)); + + lp = curwp->w_linep; + while (n--) + if ((lp = lforw(lp)) == curbp->b_headp) { + (void)dobeep_msg("End of buffer"); + return(TRUE); + } + curwp->w_linep = lp; + curwp->w_rflag |= WFFULL; + + /* if in current window, don't move dot */ + for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) + if (lp == curwp->w_dotp) + return (TRUE); + + /* Advance the dot the slow way, for line nos */ + while (curwp->w_dotp != curwp->w_linep) { + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_dotline++; + } + curwp->w_doto = 0; + return (TRUE); +} + +/* + * This command is like "forwpage", + * but it goes backwards. The "2", like above, + * is the overlap between the two windows. The + * value is from the ITS EMACS manual. The + * hard update is done because the top line in + * the window is zapped. + */ +/* ARGSUSED */ +int +backpage(int f, int n) +{ + struct line *lp, *lp2; + + if (!(f & FFARG)) { + n = curwp->w_ntrows - 2; /* Default scroll. */ + if (n <= 0) /* Don't blow up if the */ + return (backline(f, 1));/* window is tiny. */ + } else if (n < 0) + return (forwpage(f | FFRAND, -n)); + + lp = lp2 = curwp->w_linep; + + while (n-- && lback(lp) != curbp->b_headp) { + lp = lback(lp); + } + if (lp == curwp->w_linep) + (void)dobeep_msg("Beginning of buffer"); + + curwp->w_linep = lp; + curwp->w_rflag |= WFFULL; + + /* if in current window, don't move dot */ + for (n = curwp->w_ntrows; n-- && lp != curbp->b_headp; lp = lforw(lp)) + if (lp == curwp->w_dotp) + return (TRUE); + + lp2 = lforw(lp2); + + /* Move the dot the slow way, for line nos */ + while (curwp->w_dotp != lp2) { + if (curwp->w_dotline <= curwp->w_ntrows) + goto out; + curwp->w_dotp = lback(curwp->w_dotp); + curwp->w_dotline--; + } +out: + curwp->w_doto = 0; + return (TRUE); +} + +/* + * These functions are provided for compatibility with Gosling's Emacs. They + * are used to scroll the display up (or down) one line at a time. + */ +int +forw1page(int f, int n) +{ + if (!(f & FFARG)) { + n = 1; + f = FFUNIV; + } + forwpage(f | FFRAND, n); + return (TRUE); +} + +int +back1page(int f, int n) +{ + if (!(f & FFARG)) { + n = 1; + f = FFUNIV; + } + backpage(f | FFRAND, n); + return (TRUE); +} + +/* + * Page the other window. Check to make sure it exists, then + * nextwind, forwpage and restore window pointers. + */ +int +pagenext(int f, int n) +{ + struct mgwin *wp; + + if (wheadp->w_wndp == NULL) + return(dobeep_msg("No other window")); + + wp = curwp; + (void) nextwind(f, n); + (void) forwpage(f, n); + curwp = wp; + curbp = wp->w_bufp; + return (TRUE); +} + +/* + * Internal set mark routine, used by other functions (daveb). + */ +void +isetmark(void) +{ + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + curwp->w_markline = curwp->w_dotline; +} + +/* + * Set the mark in the current window + * to the value of dot. A message is written to + * the echo line. (ewprintf knows about macros) + */ +/* ARGSUSED */ +int +setmark(int f, int n) +{ + isetmark(); + ewprintf("Mark set"); + return (TRUE); +} + +/* Clear the mark, if set. */ +/* ARGSUSED */ +int +clearmark(int f, int n) +{ + if (!curwp->w_markp) + return (FALSE); + + curwp->w_markp = NULL; + curwp->w_marko = 0; + curwp->w_markline = 0; + + return (TRUE); +} + +/* + * Swap the values of "dot" and "mark" in + * the current window. This is pretty easy, because + * all of the hard work gets done by the standard routine + * that moves the mark about. The only possible + * error is "no mark". + */ +/* ARGSUSED */ +int +swapmark(int f, int n) +{ + struct line *odotp; + int odoto, odotline; + + if (curwp->w_markp == NULL) + return(dobeep_msg("No mark in this window")); + + odotp = curwp->w_dotp; + odoto = curwp->w_doto; + odotline = curwp->w_dotline; + curwp->w_dotp = curwp->w_markp; + curwp->w_doto = curwp->w_marko; + curwp->w_dotline = curwp->w_markline; + curwp->w_markp = odotp; + curwp->w_marko = odoto; + curwp->w_markline = odotline; + curwp->w_rflag |= WFMOVE; + return (TRUE); +} + +/* + * Go to a specific line, mostly for + * looking up errors in C programs, which give the + * error a line number. If an argument is present, then + * it is the line number, else prompt for a line number + * to use. + */ +/* ARGSUSED */ +int +gotoline(int f, int n) +{ + char buf[32], *bufp; + const char *err; + + if (!(f & FFARG)) { + if ((bufp = eread("Goto line: ", buf, sizeof(buf), + EFNUL | EFNEW | EFCR)) == NULL) + return (ABORT); + if (bufp[0] == '\0') + return (ABORT); + n = (int)strtonum(buf, INT_MIN, INT_MAX, &err); + if (err) + return(dobeep_msgs("Line number %s", err)); + } + return(setlineno(n)); +} + +/* + * Set the line number and switch to it. + */ +int +setlineno(int n) +{ + struct line *clp; + + if (n >= 0) { + if (n == 0) + n++; + curwp->w_dotline = n; + clp = lforw(curbp->b_headp); /* "clp" is first line */ + while (--n > 0) { + if (lforw(clp) == curbp->b_headp) { + curwp->w_dotline = curwp->w_bufp->b_lines; + break; + } + clp = lforw(clp); + } + } else { + curwp->w_dotline = curwp->w_bufp->b_lines + n; + clp = lback(curbp->b_headp); /* "clp" is last line */ + while (n < 0) { + if (lback(clp) == curbp->b_headp) { + curwp->w_dotline = 1; + break; + } + clp = lback(clp); + n++; + } + } + curwp->w_dotp = clp; + curwp->w_doto = 0; + curwp->w_rflag |= WFMOVE; + return (TRUE); +} Index: contrib/mg/bell.c =================================================================== --- /dev/null +++ contrib/mg/bell.c @@ -0,0 +1,90 @@ +/* $OpenBSD: bell.c,v 1.6 2021/05/06 12:44:21 lum Exp $ */ + +/* + * This file is in the public domain. + * + * Author: Mark Lumsden + * + */ + +/* + * Control how mg communicates with the user. + */ + +#include +#include +#include +#include + +#include "def.h" +#include "macro.h" + +void +bellinit(void) +{ + doaudiblebell = 1; + dovisiblebell = 0; +} + +int +dobeep_num(const char *msg, int n) +{ + ewprintf("%s %d", msg, n); + dobeep(); + return (FALSE); +} + +int +dobeep_msgs(const char *msg, const char *s) +{ + ewprintf("%s %s", msg, s); + dobeep(); + return (FALSE); +} + +int +dobeep_msg(const char *msg) +{ + ewprintf("%s", msg); + dobeep(); + return (FALSE); +} + +void +dobeep(void) +{ + if (doaudiblebell) { + ttbeep(); + } + if (dovisiblebell) { + sgarbf = TRUE; + update(CNONE); + if (inmacro) /* avoid delaying macro execution. */ + return; + usleep(50000); + } +} + +/* ARGSUSED */ +int +toggleaudiblebell(int f, int n) +{ + if (f & FFARG) + doaudiblebell = n > 0; + else + doaudiblebell = !doaudiblebell; + + return (TRUE); +} + +/* ARGSUSED */ +int +togglevisiblebell(int f, int n) +{ + if (f & FFARG) + dovisiblebell = n > 0; + else + dovisiblebell = !dovisiblebell; + + return (TRUE); +} Index: contrib/mg/buffer.c =================================================================== --- /dev/null +++ contrib/mg/buffer.c @@ -0,0 +1,1070 @@ +/* $OpenBSD: buffer.c,v 1.112 2021/03/26 15:02:10 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Buffer handling. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" /* needed for modes */ + +#define DIFFTOOL "/usr/bin/diff" + +static struct buffer *makelist(void); +static struct buffer *bnew(const char *); + +static int usebufname(const char *); + +/* Flag for global working dir */ +extern int globalwd; + +/* ARGSUSED */ +int +togglereadonlyall(int f, int n) +{ + struct buffer *bp = NULL; + int len = 0; + + allbro = !allbro; + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + len = strlen(bp->b_bname); + if (bp->b_bname[0] != '*' && bp->b_bname[len - 1] != '*') { + if (allbro) + bp->b_flag |= BFREADONLY; + else + bp->b_flag &= ~BFREADONLY; + } + } + curwp->w_rflag |= WFMODE; + + return (TRUE); +} + +/* ARGSUSED */ +int +togglereadonly(int f, int n) +{ + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (!(curbp->b_flag & BFREADONLY)) + curbp->b_flag |= BFREADONLY; + else { + curbp->b_flag &= ~BFREADONLY; + if (curbp->b_flag & BFCHG) + ewprintf("Warning: Buffer was modified"); + } + curwp->w_rflag |= WFMODE; + + return (TRUE); +} + +/* Switch to the named buffer. + * If no name supplied, switch to the default (alternate) buffer. + */ +int +usebufname(const char *bufp) +{ + struct buffer *bp = NULL; + int ret; + + if (bufp == NULL) { + if ((bp = bfind("*scratch*", TRUE)) == NULL) + return(FALSE); + } else if (bufp[0] == '\0' && curbp->b_altb != NULL) + bp = curbp->b_altb; + else if ((bp = bfind(bufp, TRUE)) == NULL) + return (FALSE); + + /* and put it in current window */ + curbp = bp; + ret = showbuffer(bp, curwp, WFFRAME | WFFULL); + eerase(); + + return (ret); +} + +/* + * Attach a buffer to a window. The values of dot and mark come + * from the buffer if the use count is 0. Otherwise, they come + * from some other window. *scratch* is the default alternate + * buffer. + */ +/* ARGSUSED */ +int +usebuffer(int f, int n) +{ + char bufn[NBUFN], *bufp; + + /* Get buffer to use from user */ + if (curbp->b_altb == NULL) + bufp = eread("Switch to buffer: ", bufn, NBUFN, EFNEW | EFBUF); + else + bufp = eread("Switch to buffer (default %s): ", bufn, NBUFN, + EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); + + if (bufp == NULL) + return (ABORT); + + return (usebufname(bufp)); +} + +/* + * pop to buffer asked for by the user. + */ +/* ARGSUSED */ +int +poptobuffer(int f, int n) +{ + struct buffer *bp; + struct mgwin *wp; + char bufn[NBUFN], *bufp; + + /* Get buffer to use from user */ + if ((curbp->b_altb == NULL) && + ((curbp->b_altb = bfind("*scratch*", TRUE)) == NULL)) + bufp = eread("Switch to buffer in other window: ", bufn, NBUFN, + EFNEW | EFBUF); + else + bufp = eread("Switch to buffer in other window (default %s): ", + bufn, NBUFN, EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); + if (bufp == NULL) + return (ABORT); + if (bufp[0] == '\0' && curbp->b_altb != NULL) + bp = curbp->b_altb; + else if ((bp = bfind(bufp, TRUE)) == NULL) + return (FALSE); + if (bp == curbp) + return (splitwind(f, n)); + /* and put it in a new, non-ephemeral window */ + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + curwp = wp; + return (TRUE); +} + +/* + * Dispose of a buffer, by name. + * Ask for the name (unless called by dired mode). Look it up (don't + * get too upset if it isn't there at all!). Clear the buffer (ask + * if the buffer has been changed). Then free the header + * line and the buffer header. Bound to "C-x k". + */ +/* ARGSUSED */ +int +killbuffer_cmd(int f, int n) +{ + struct buffer *bp; + char bufn[NBUFN], *bufp; + int ret; + + if (f & FFRAND) /* dired mode 'q' */ + bp = curbp; + else if ((bufp = eread("Kill buffer (default %s): ", bufn, NBUFN, + EFNUL | EFNEW | EFBUF, curbp->b_bname)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + bp = curbp; + else if ((bp = bfind(bufp, FALSE)) == NULL) + return (FALSE); + ret = killbuffer(bp); + eerase(); + + return (ret); +} + +int +killbuffer(struct buffer *bp) +{ + struct buffer *bp1; + struct buffer *bp2; + struct mgwin *wp; + int s; + struct undo_rec *rec; + + /* + * Find some other buffer to display. Try the alternate buffer, + * then the first different buffer in the buffer list. If there's + * only one buffer, create buffer *scratch* and make it the alternate + * buffer. Return if *scratch* is only buffer... + */ + if ((bp1 = bp->b_altb) == NULL) { + /* only one buffer. see if it's *scratch* */ + if (bp == bfind("*scratch*", FALSE)) + return (TRUE); + /* create *scratch* for alternate buffer */ + if ((bp1 = bfind("*scratch*", TRUE)) == NULL) + return (FALSE); + } + if ((s = bclear(bp)) != TRUE) + return (s); + for (wp = wheadp; bp->b_nwnd > 0; wp = wp->w_wndp) { + if (wp->w_bufp == bp) { + bp2 = bp1->b_altb; /* save alternate buffer */ + if (showbuffer(bp1, wp, WFMODE | WFFRAME | WFFULL)) + bp1->b_altb = bp2; + else + bp1 = bp2; + } + } + if (bp == curbp) + curbp = bp1; + free(bp->b_headp); /* Release header line. */ + bp2 = NULL; /* Find the header. */ + bp1 = bheadp; + while (bp1 != bp) { + if (bp1->b_altb == bp) + bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; + bp2 = bp1; + bp1 = bp1->b_bufp; + } + bp1 = bp1->b_bufp; /* Next one in chain. */ + if (bp2 == NULL) /* Unlink it. */ + bheadp = bp1; + else + bp2->b_bufp = bp1; + while (bp1 != NULL) { /* Finish with altb's */ + if (bp1->b_altb == bp) + bp1->b_altb = (bp->b_altb == bp1) ? NULL : bp->b_altb; + bp1 = bp1->b_bufp; + } + + while ((rec = TAILQ_FIRST(&bp->b_undo))) { + TAILQ_REMOVE(&bp->b_undo, rec, next); + free_undo_record(rec); + } + + free(bp->b_bname); /* Release name block */ + free(bp); /* Release buffer block */ + return (TRUE); +} + +/* + * Save some buffers - just call anycb with the arg flag. + */ +/* ARGSUSED */ +int +savebuffers(int f, int n) +{ + if (anycb(f) == ABORT) + return (ABORT); + return (TRUE); +} + +/* + * Listing buffers. + */ +static int listbuf_ncol; + +static int listbuf_goto_buffer(int f, int n); +static int listbuf_goto_buffer_one(int f, int n); +static int listbuf_goto_buffer_helper(int f, int n, int only); + +static PF listbuf_pf[] = { + listbuf_goto_buffer +}; +static PF listbuf_one[] = { + listbuf_goto_buffer_one +}; + + +static struct KEYMAPE (2) listbufmap = { + 2, + 2, + rescan, + { + { + CCHR('M'), CCHR('M'), listbuf_pf, NULL + }, + { + '1', '1', listbuf_one, NULL + } + } +}; + +/* + * Display the buffer list. This is done + * in two parts. The "makelist" routine figures out + * the text, and puts it in a buffer. "popbuf" + * then pops the data onto the screen. Bound to + * "C-x C-b". + */ +/* ARGSUSED */ +int +listbuffers(int f, int n) +{ + static int initialized = 0; + struct buffer *bp; + struct mgwin *wp; + + if (!initialized) { + maps_add((KEYMAP *)&listbufmap, "listbufmap"); + initialized = 1; + } + + if ((bp = makelist()) == NULL || (wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + wp->w_dotp = bp->b_dotp; /* fix up if window already on screen */ + wp->w_doto = bp->b_doto; + bp->b_modes[0] = name_mode("fundamental"); + bp->b_modes[1] = name_mode("listbufmap"); + bp->b_nmodes = 1; + + return (TRUE); +} + +/* + * This routine rebuilds the text for the + * list buffers command. Return pointer + * to new list if everything works. + * Return NULL if there is an error (if + * there is no memory). + */ +static struct buffer * +makelist(void) +{ + int w = ncol / 2; + struct buffer *bp, *blp; + struct line *lp; + + if ((blp = bfind("*Buffer List*", TRUE)) == NULL) + return (NULL); + if (bclear(blp) != TRUE) + return (NULL); + blp->b_flag &= ~BFCHG; /* Blow away old. */ + blp->b_flag |= BFREADONLY; + + listbuf_ncol = ncol; /* cache ncol for listbuf_goto_buffer */ + + if (addlinef(blp, "%-*s%s", w, " MR Buffer", "Size File") == FALSE || + addlinef(blp, "%-*s%s", w, " -- ------", "---- ----") == FALSE) + return (NULL); + + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + RSIZE nbytes; + + nbytes = 0; /* Count bytes in buf. */ + if (bp != blp) { + lp = bfirstlp(bp); + while (lp != bp->b_headp) { + nbytes += llength(lp) + 1; + lp = lforw(lp); + } + if (nbytes) + nbytes--; /* no bonus newline */ + } + + if (addlinef(blp, "%c%c%c %-*.*s%c%-6d %-*s", + (bp == curbp) ? '>' : ' ', /* current buffer ? */ + ((bp->b_flag & BFCHG) != 0) ? '*' : ' ', /* changed ? */ + ((bp->b_flag & BFREADONLY) != 0) ? '*' : ' ', + w - 5, /* four chars already written */ + w - 5, /* four chars already written */ + bp->b_bname, /* buffer name */ + strlen(bp->b_bname) < w - 5 ? ' ' : '$', /* truncated? */ + nbytes, /* buffer size */ + w - 7, /* seven chars already written */ + bp->b_fname) == FALSE) + return (NULL); + } + blp->b_dotp = bfirstlp(blp); /* put dot at beginning of + * buffer */ + blp->b_doto = 0; + return (blp); /* All done */ +} + +static int +listbuf_goto_buffer(int f, int n) +{ + return (listbuf_goto_buffer_helper(f, n, 0)); +} + +static int +listbuf_goto_buffer_one(int f, int n) +{ + return (listbuf_goto_buffer_helper(f, n, 1)); +} + +static int +listbuf_goto_buffer_helper(int f, int n, int only) +{ + struct buffer *bp; + struct mgwin *wp; + char *line = NULL; + int i, ret = FALSE; + + if (curwp->w_dotp->l_text[listbuf_ncol/2 - 1] == '$') + return(dobeep_msg("buffer name truncated")); + + if ((line = malloc(listbuf_ncol/2)) == NULL) + return (FALSE); + + memcpy(line, curwp->w_dotp->l_text + 4, listbuf_ncol/2 - 5); + for (i = listbuf_ncol/2 - 6; i > 0; i--) { + if (line[i] != ' ') { + line[i + 1] = '\0'; + break; + } + } + if (i == 0) + goto cleanup; + + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + if (strcmp(bp->b_bname, line) == 0) + break; + } + if (bp == NULL) + goto cleanup; + + if ((wp = popbuf(bp, WNONE)) == NULL) + goto cleanup; + curbp = bp; + curwp = wp; + + if (only) + ret = (onlywind(FFRAND, 1)); + else + ret = TRUE; + +cleanup: + free(line); + + return (ret); +} + +/* + * The argument "fmt" points to a format string. Append this line to the + * buffer. Handcraft the EOL on the end. Return TRUE if it worked and + * FALSE if you ran out of room. + */ +int +addlinef(struct buffer *bp, char *fmt, ...) +{ + va_list ap; + struct line *lp; + + if ((lp = lalloc(0)) == NULL) + return (FALSE); + va_start(ap, fmt); + if (vasprintf(&lp->l_text, fmt, ap) == -1) { + lfree(lp); + va_end(ap); + return (FALSE); + } + lp->l_used = strlen(lp->l_text); + va_end(ap); + + bp->b_headp->l_bp->l_fp = lp; /* Hook onto the end */ + lp->l_bp = bp->b_headp->l_bp; + bp->b_headp->l_bp = lp; + lp->l_fp = bp->b_headp; + bp->b_lines++; + + return (TRUE); +} + +/* + * Look through the list of buffers, giving the user a chance to save them. + * Return TRUE if there are any changed buffers afterwards. Buffers that don't + * have an associated file don't count. Return FALSE if there are no changed + * buffers. Return ABORT if an error occurs or if the user presses c-g. + */ +int +anycb(int f) +{ + struct buffer *bp; + int s = FALSE, save = FALSE, save2 = FALSE, ret; + char pbuf[NFILEN + 11]; + + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + if (*(bp->b_fname) != '\0' && (bp->b_flag & BFCHG) != 0) { + ret = snprintf(pbuf, sizeof(pbuf), "Save file %s", + bp->b_fname); + if (ret < 0 || ret >= sizeof(pbuf)) { + (void)dobeep_msg("Error: filename too long!"); + return (UERROR); + } + if ((f == TRUE || (save = eyorn(pbuf)) == TRUE) && + (save2 = buffsave(bp)) == TRUE) { + bp->b_flag &= ~BFCHG; + upmodes(bp); + } else { + if (save2 == FIOERR) + return (save2); + s = TRUE; + } + if (save == ABORT) + return (save); + save = TRUE; + } + } + if (save == FALSE /* && kbdmop == NULL */ ) /* experimental */ + ewprintf("(No files need saving)"); + return (s); +} + +/* + * Search for a buffer, by name. + * If not found, and the "cflag" is TRUE, + * create a new buffer. Return pointer to the found + * (or new) buffer. + */ +struct buffer * +bfind(const char *bname, int cflag) +{ + struct buffer *bp; + + bp = bheadp; + while (bp != NULL) { + if (strcmp(bname, bp->b_bname) == 0) + return (bp); + bp = bp->b_bufp; + } + if (cflag != TRUE) + return (NULL); + + bp = bnew(bname); + + return (bp); +} + +/* + * Create a new buffer and put it in the list of + * all buffers. + */ +static struct buffer * +bnew(const char *bname) +{ + struct buffer *bp; + struct line *lp; + int i; + size_t len; + + bp = calloc(1, sizeof(struct buffer)); + if (bp == NULL) { + dobeep(); + ewprintf("Can't get %d bytes", sizeof(struct buffer)); + return (NULL); + } + if ((lp = lalloc(0)) == NULL) { + free(bp); + return (NULL); + } + bp->b_altb = bp->b_bufp = NULL; + bp->b_dotp = lp; + bp->b_doto = 0; + bp->b_markp = NULL; + bp->b_marko = 0; + bp->b_flag = defb_flag; + /* if buffer name starts and ends with '*', we ignore changes */ + len = strlen(bname); + if (len) { + if (bname[0] == '*' && bname[len - 1] == '*') + bp->b_flag |= BFIGNDIRTY; + } + bp->b_nwnd = 0; + bp->b_headp = lp; + bp->b_nmodes = defb_nmodes; + TAILQ_INIT(&bp->b_undo); + bp->b_undoptr = NULL; + i = 0; + do { + bp->b_modes[i] = defb_modes[i]; + } while (i++ < defb_nmodes); + bp->b_fname[0] = '\0'; + bp->b_cwd[0] = '\0'; + bzero(&bp->b_fi, sizeof(bp->b_fi)); + lp->l_fp = lp; + lp->l_bp = lp; + bp->b_bufp = bheadp; + bheadp = bp; + bp->b_dotline = bp->b_markline = 1; + bp->b_lines = 1; + bp->b_nlseq = "\n"; /* use unix default */ + bp->b_nlchr = bp->b_nlseq; + if ((bp->b_bname = strdup(bname)) == NULL) { + dobeep(); + ewprintf("Can't get %d bytes", strlen(bname) + 1); + return (NULL); + } + + return (bp); +} + +/* + * This routine blows away all of the text + * in a buffer. If the buffer is marked as changed + * then we ask if it is ok to blow it away; this is + * to save the user the grief of losing text. The + * window chain is nearly always wrong if this gets + * called; the caller must arrange for the updates + * that are required. Return TRUE if everything + * looks good. + */ +int +bclear(struct buffer *bp) +{ + struct line *lp; + int s; + + /* Has buffer changed, and do we care? */ + if (!(bp->b_flag & BFIGNDIRTY) && (bp->b_flag & BFCHG) != 0 && + (s = eyesno("Buffer modified; kill anyway")) != TRUE) + return (s); + bp->b_flag &= ~BFCHG; /* Not changed */ + while ((lp = lforw(bp->b_headp)) != bp->b_headp) + lfree(lp); + bp->b_dotp = bp->b_headp; /* Fix dot */ + bp->b_doto = 0; + bp->b_markp = NULL; /* Invalidate "mark" */ + bp->b_marko = 0; + bp->b_dotline = bp->b_markline = 1; + bp->b_lines = 1; + + return (TRUE); +} + +/* + * Display the given buffer in the given window. Flags indicated + * action on redisplay. Update modified flag so insert loop can check it. + */ +int +showbuffer(struct buffer *bp, struct mgwin *wp, int flags) +{ + struct buffer *obp; + struct mgwin *owp; + + /* Ensure file has not been modified elsewhere */ + if (fchecktime(bp) != TRUE) + bp->b_flag |= BFDIRTY; + + if (wp->w_bufp == bp) { /* Easy case! */ + wp->w_rflag |= flags; + return (TRUE); + } + /* First, detach the old buffer from the window */ + if ((bp->b_altb = obp = wp->w_bufp) != NULL) { + if (--obp->b_nwnd == 0) { + obp->b_dotp = wp->w_dotp; + obp->b_doto = wp->w_doto; + obp->b_markp = wp->w_markp; + obp->b_marko = wp->w_marko; + obp->b_dotline = wp->w_dotline; + obp->b_markline = wp->w_markline; + } + } + /* Now, attach the new buffer to the window */ + wp->w_bufp = bp; + + if (bp->b_nwnd++ == 0) { /* First use. */ + wp->w_dotp = bp->b_dotp; + wp->w_doto = bp->b_doto; + wp->w_markp = bp->b_markp; + wp->w_marko = bp->b_marko; + wp->w_dotline = bp->b_dotline; + wp->w_markline = bp->b_markline; + } else + /* already on screen, steal values from other window */ + for (owp = wheadp; owp != NULL; owp = wp->w_wndp) + if (wp->w_bufp == bp && owp != wp) { + wp->w_dotp = owp->w_dotp; + wp->w_doto = owp->w_doto; + wp->w_markp = owp->w_markp; + wp->w_marko = owp->w_marko; + wp->w_dotline = owp->w_dotline; + wp->w_markline = owp->w_markline; + break; + } + wp->w_rflag |= WFMODE | flags; + return (TRUE); +} + +/* + * Augment a buffer name with a number, if necessary + * + * If more than one file of the same basename() is open, + * the additional buffers are named "file<2>", "file<3>", and + * so forth. This function adjusts a buffer name to + * include the number, if necessary. + */ +int +augbname(char *bn, const char *fn, size_t bs) +{ + int count; + size_t remain, len; + + if ((len = xbasename(bn, fn, bs)) >= bs) + return (FALSE); + + remain = bs - len; + for (count = 2; bfind(bn, FALSE) != NULL; count++) + snprintf(bn + len, remain, "<%d>", count); + + return (TRUE); +} + +/* + * Pop the buffer we got passed onto the screen. + * Returns a status. + */ +struct mgwin * +popbuf(struct buffer *bp, int flags) +{ + struct mgwin *wp; + + if (bp->b_nwnd == 0) { /* Not on screen yet. */ + /* + * Pick a window for a pop-up. + * If only one window, split the screen. + * Flag the new window as ephemeral + */ + if (wheadp->w_wndp == NULL && + splitwind(FFOTHARG, flags) == FALSE) + return (NULL); + + /* + * Pick the uppermost window that isn't + * the current window. An LRU algorithm + * might be better. Return a pointer, or NULL on error. + */ + wp = wheadp; + + while (wp != NULL && wp == curwp) + wp = wp->w_wndp; + } else { + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == bp) { + wp->w_rflag |= WFFULL | WFFRAME; + return (wp); + } + } + } + if (!wp) + return (NULL); + + if (showbuffer(bp, wp, WFFULL) != TRUE) + return (NULL); + return (wp); +} + +/* + * Insert another buffer at dot. Very useful. + */ +/* ARGSUSED */ +int +bufferinsert(int f, int n) +{ + struct buffer *bp; + struct line *clp; + int clo, nline; + char bufn[NBUFN], *bufp; + + /* Get buffer to use from user */ + if (curbp->b_altb != NULL) + bufp = eread("Insert buffer (default %s): ", bufn, NBUFN, + EFNUL | EFNEW | EFBUF, curbp->b_altb->b_bname); + else + bufp = eread("Insert buffer: ", bufn, NBUFN, EFNEW | EFBUF); + if (bufp == NULL) + return (ABORT); + if (bufp[0] == '\0' && curbp->b_altb != NULL) + bp = curbp->b_altb; + else if ((bp = bfind(bufp, FALSE)) == NULL) + return (FALSE); + + if (bp == curbp) + return(dobeep_msg("Cannot insert buffer into self")); + + /* insert the buffer */ + nline = 0; + clp = bfirstlp(bp); + for (;;) { + for (clo = 0; clo < llength(clp); clo++) + if (linsert(1, lgetc(clp, clo)) == FALSE) + return (FALSE); + if ((clp = lforw(clp)) == bp->b_headp) + break; + if (enewline(FFRAND, 1) == FALSE) /* fake newline */ + return (FALSE); + nline++; + } + if (nline == 1) + ewprintf("[Inserted 1 line]"); + else + ewprintf("[Inserted %d lines]", nline); + + clp = curwp->w_linep; /* cosmetic adjustment */ + if (curwp->w_dotp == clp) { /* for offscreen insert */ + while (nline-- && lback(clp) != curbp->b_headp) + clp = lback(clp); + curwp->w_linep = clp; /* adjust framing. */ + curwp->w_rflag |= WFFULL; + } + return (TRUE); +} + +/* + * Turn off the dirty bit on this buffer. + */ +/* ARGSUSED */ +int +notmodified(int f, int n) +{ + struct mgwin *wp; + + curbp->b_flag &= ~BFCHG; + wp = wheadp; /* Update mode lines. */ + while (wp != NULL) { + if (wp->w_bufp == curbp) + wp->w_rflag |= WFMODE; + wp = wp->w_wndp; + } + ewprintf("Modification-flag cleared"); + return (TRUE); +} + +/* + * Popbuf and set all windows to top of buffer. + */ +int +popbuftop(struct buffer *bp, int flags) +{ + struct mgwin *wp; + + bp->b_dotp = bfirstlp(bp); + bp->b_doto = 0; + if (bp->b_nwnd != 0) { + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) + if (wp->w_bufp == bp) { + wp->w_dotp = bp->b_dotp; + wp->w_doto = 0; + wp->w_rflag |= WFFULL; + } + } + return (popbuf(bp, flags) != NULL); +} + +/* + * Return the working directory for the current buffer, terminated + * with a '/'. First, try to extract it from the current buffer's + * filename. If that fails, use global cwd. + */ +int +getbufcwd(char *path, size_t plen) +{ + char cwd[NFILEN]; + + if (plen == 0) + return (FALSE); + + if (globalwd == FALSE && curbp->b_cwd[0] != '\0') { + (void)strlcpy(path, curbp->b_cwd, plen); + } else { + if (getcwdir(cwd, sizeof(cwd)) == FALSE) + goto error; + (void)strlcpy(path, cwd, plen); + } + return (TRUE); +error: + path[0] = '\0'; + return (FALSE); +} + +/* + * Ensures a buffer has not been modified elsewhere; e.g. on disk. + * Prompt the user if it has. + * Returns TRUE if it has NOT (i.e. buffer is ok to edit). + * FALSE or ABORT otherwise + */ +int +checkdirty(struct buffer *bp) +{ + int s; + + if ((bp->b_flag & (BFCHG | BFDIRTY)) == 0) + if (fchecktime(bp) != TRUE) + bp->b_flag |= BFDIRTY; + + if ((bp->b_flag & (BFDIRTY | BFIGNDIRTY)) == BFDIRTY) { + s = eynorr("File changed on disk; really edit the buffer"); + switch (s) { + case TRUE: + bp->b_flag &= ~BFDIRTY; + bp->b_flag |= BFIGNDIRTY; + return (TRUE); + case REVERT: + dorevert(); + return (FALSE); + default: + return (s); + } + } + + return (TRUE); +} + +/* + * Revert the current buffer to whatever is on disk. + */ +/* ARGSUSED */ +int +revertbuffer(int f, int n) +{ + char fbuf[NFILEN + 32]; + + if (curbp->b_fname[0] == 0) + return(dobeep_msg("Cannot revert buffer not associated " + "with any files.")); + + snprintf(fbuf, sizeof(fbuf), "Revert buffer from file %s", + curbp->b_fname); + + if (eyorn(fbuf) == TRUE) + return dorevert(); + + return (FALSE); +} + +int +dorevert(void) +{ + int lineno; + struct undo_rec *rec; + + if (access(curbp->b_fname, F_OK|R_OK) != 0) { + dobeep(); + if (errno == ENOENT) + ewprintf("File %s no longer exists!", + curbp->b_fname); + else + ewprintf("File %s is no longer readable!", + curbp->b_fname); + return (FALSE); + } + + /* Save our current line, so we can go back after reloading. */ + lineno = curwp->w_dotline; + + /* Prevent readin from asking if we want to kill the buffer. */ + curbp->b_flag &= ~BFCHG; + + /* Clean up undo memory */ + while ((rec = TAILQ_FIRST(&curbp->b_undo))) { + TAILQ_REMOVE(&curbp->b_undo, rec, next); + free_undo_record(rec); + } + + if (readin(curbp->b_fname)) + return(setlineno(lineno)); + return (FALSE); +} + +/* + * Diff the current buffer to what is on disk. + */ +/*ARGSUSED */ +int +diffbuffer(int f, int n) +{ + struct buffer *bp; + struct line *lp, *lpend; + size_t len; + int ret; + char *text, *ttext; + char * const argv[] = + {DIFFTOOL, "-u", "-p", curbp->b_fname, "-", (char *)NULL}; + + len = 0; + + /* C-u is not supported */ + if (n > 1) + return (ABORT); + + if (access(DIFFTOOL, X_OK) != 0) { + dobeep(); + ewprintf("%s not found or not executable.", DIFFTOOL); + return (FALSE); + } + + if (curbp->b_fname[0] == 0) + return(dobeep_msg("Cannot diff buffer not associated with " + "any files.")); + + lpend = curbp->b_headp; + for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { + len+=llength(lp); + if (lforw(lp) != lpend) /* no implied \n on last line */ + len++; + } + if ((text = calloc(len + 1, sizeof(char))) == NULL) + return(dobeep_msg("Cannot allocate memory.")); + + ttext = text; + + for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { + if (llength(lp) != 0) { + memcpy(ttext, ltext(lp), llength(lp)); + ttext += llength(lp); + } + if (lforw(lp) != lpend) /* no implied \n on last line */ + *ttext++ = *curbp->b_nlchr; + } + + bp = bfind("*Diff*", TRUE); + bp->b_flag |= BFREADONLY; + if (bclear(bp) != TRUE) { + free(text); + return (FALSE); + } + + ret = pipeio(DIFFTOOL, argv, text, len, bp); + + if (ret == TRUE) { + eerase(); + if (lforw(bp->b_headp) == bp->b_headp) + addline(bp, "Diff finished (no differences)."); + } + + free(text); + return (ret); +} + +/* + * Given a file name, either find the buffer it uses, or create a new + * empty buffer to put it in. + */ +struct buffer * +findbuffer(char *fn) +{ + struct buffer *bp; + char bname[NBUFN], fname[NBUFN]; + + if (strlcpy(fname, fn, sizeof(fname)) >= sizeof(fname)) { + (void)dobeep_msg("filename too long"); + return (NULL); + } + + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + if (strcmp(bp->b_fname, fname) == 0) + return (bp); + } + /* Not found. Create a new one, adjusting name first */ + if (augbname(bname, fname, sizeof(bname)) == FALSE) + return (NULL); + + bp = bfind(bname, TRUE); + return (bp); +} Index: contrib/mg/cdbr.h =================================================================== --- /dev/null +++ contrib/mg/cdbr.h @@ -0,0 +1,64 @@ +/* $NetBSD: cdbr.h,v 1.1 2013/12/11 01:24:08 joerg Exp $ */ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CDBR_H +#define _CDBR_H + +#include +#if defined(_KERNEL) || defined(_STANDALONE) +#include +#else +#include +#include +#endif + +#define CDBR_DEFAULT 0 + +struct cdbr; + +__BEGIN_DECLS + +#if !defined(_KERNEL) && !defined(_STANDALONE) +struct cdbr *cdbr_open(const char *, int); +#endif +struct cdbr *cdbr_open_mem(void *, size_t, int, + void (*)(void *, void *, size_t), void *); +uint32_t cdbr_entries(struct cdbr *); +int cdbr_get(struct cdbr *, uint32_t, const void **, size_t *); +int cdbr_find(struct cdbr *, const void *, size_t, + const void **, size_t *); +void cdbr_close(struct cdbr *); + +__END_DECLS + +#endif /* _CDBR_H */ Index: contrib/mg/cdbr.c =================================================================== --- /dev/null +++ contrib/mg/cdbr.c @@ -0,0 +1,294 @@ +/* $NetBSD: cdbr.c,v 1.2 2017/01/10 23:06:06 christos Exp $ */ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "cdbr.h" + +#define SET_ERRNO(val) errno = (val) + +#ifndef fast_divide32_prepare +#define fast_divide32_prepare(d,m,s1,s2) (void)0 +#endif + +#ifndef fast_remainder32 +#define fast_remainder32(v,d,m,s1,s2) (v%d) +#endif + +extern void mi_vector_hash(const void * __restrict, size_t, uint32_t, + uint32_t[3]); + +struct cdbr { + void (*unmap)(void *, void *, size_t); + void *cookie; + uint8_t *mmap_base; + size_t mmap_size; + + uint8_t *hash_base; + uint8_t *offset_base; + uint8_t *data_base; + + uint32_t data_size; + uint32_t entries; + uint32_t entries_index; + uint32_t seed; + + uint8_t offset_size; + uint8_t index_size; + + uint32_t entries_m; + uint32_t entries_index_m; + uint8_t entries_s1, entries_s2; + uint8_t entries_index_s1, entries_index_s2; +}; + +/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */ +static uint32_t +le32dec(const void *buf) +{ + uint8_t const *p = (uint8_t const *) buf; + + return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +#if !defined(_KERNEL) && !defined(_STANDALONE) +static void +cdbr_unmap(void *cookie __unused, void *base, size_t size) +{ + munmap(base, size); +} + +/* ARGSUSED */ +struct cdbr * +cdbr_open(const char *path, int flags) +{ + void *base; + size_t size; + int fd; + struct cdbr *cdbr; + struct stat sb; + + if ((fd = open(path, O_RDONLY)) == -1) + return NULL; + if (fstat(fd, &sb) == -1) { + close(fd); + return NULL; + } + + if (sb.st_size >= SSIZE_MAX) { + close(fd); + SET_ERRNO(EINVAL); + return NULL; + } + + + size = (size_t)sb.st_size; + base = mmap(NULL, size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); + close(fd); + + if (base == MAP_FAILED) + return NULL; + + cdbr = cdbr_open_mem(base, size, flags, cdbr_unmap, NULL); + if (cdbr == NULL) + munmap(base, size); + return cdbr; +} +#endif + +struct cdbr * +cdbr_open_mem(void *base, size_t size, int flags __unused, + void (*unmap)(void *, void *, size_t), void *cookie) +{ + struct cdbr *cdbr; + uint8_t *buf = base; + if (size < 40 || memcmp(buf, "NBCDB\n\0\001", 8)) { + SET_ERRNO(EINVAL); + return NULL; + } + + cdbr = malloc(sizeof(*cdbr)); + cdbr->unmap = unmap; + cdbr->cookie = cookie; + + cdbr->data_size = le32dec(buf + 24); + cdbr->entries = le32dec(buf + 28); + cdbr->entries_index = le32dec(buf + 32); + cdbr->seed = le32dec(buf + 36); + + if (cdbr->data_size < 0x100) + cdbr->offset_size = 1; + else if (cdbr->data_size < 0x10000) + cdbr->offset_size = 2; + else + cdbr->offset_size = 4; + + if (cdbr->entries_index < 0x100) + cdbr->index_size = 1; + else if (cdbr->entries_index < 0x10000) + cdbr->index_size = 2; + else + cdbr->index_size = 4; + + cdbr->mmap_base = base; + cdbr->mmap_size = size; + + cdbr->hash_base = cdbr->mmap_base + 40; + cdbr->offset_base = cdbr->hash_base + cdbr->entries_index * cdbr->index_size; + if (cdbr->entries_index * cdbr->index_size % cdbr->offset_size) + cdbr->offset_base += cdbr->offset_size - + cdbr->entries_index * cdbr->index_size % cdbr->offset_size; + cdbr->data_base = cdbr->offset_base + (cdbr->entries + 1) * cdbr->offset_size; + + if (cdbr->hash_base < cdbr->mmap_base || + cdbr->offset_base < cdbr->mmap_base || + cdbr->data_base < cdbr->mmap_base || + cdbr->data_base + cdbr->data_size < cdbr->mmap_base || + cdbr->data_base + cdbr->data_size > + cdbr->mmap_base + cdbr->mmap_size) { + SET_ERRNO(EINVAL); + free(cdbr); + return NULL; + } + + if (cdbr->entries) { + fast_divide32_prepare(cdbr->entries, &cdbr->entries_m, + &cdbr->entries_s1, &cdbr->entries_s2); + } + if (cdbr->entries_index) { + fast_divide32_prepare(cdbr->entries_index, + &cdbr->entries_index_m, + &cdbr->entries_index_s1, &cdbr->entries_index_s2); + } + + return cdbr; +} + +static uint32_t +get_uintX(const uint8_t *addr, uint32_t idx, int size) +{ + addr += idx * size; + + if (size == 4) + return /* LINTED */le32toh(*(const uint32_t *)addr); + else if (size == 2) + return /* LINTED */le16toh(*(const uint16_t *)addr); + else + return *addr; +} + +uint32_t +cdbr_entries(struct cdbr *cdbr) +{ + + return cdbr->entries; +} + +int +cdbr_get(struct cdbr *cdbr, uint32_t idx, const void **data, size_t *data_len) +{ + uint32_t start, end; + + if (idx >= cdbr->entries) { + SET_ERRNO(EINVAL); + return -1; + } + + start = get_uintX(cdbr->offset_base, idx, cdbr->offset_size); + end = get_uintX(cdbr->offset_base, idx + 1, cdbr->offset_size); + + if (start > end) { + SET_ERRNO(EIO); + return -1; + } + + if (end > cdbr->data_size) { + SET_ERRNO(EIO); + return -1; + } + + *data = cdbr->data_base + start; + *data_len = end - start; + + return 0; +} + +int +cdbr_find(struct cdbr *cdbr, const void *key, size_t key_len, + const void **data, size_t *data_len) +{ + uint32_t hashes[3], idx; + + if (cdbr->entries_index == 0) { + SET_ERRNO(EINVAL); + return -1; + } + + mi_vector_hash(key, key_len, cdbr->seed, hashes); + + hashes[0] = fast_remainder32(hashes[0], cdbr->entries_index, + cdbr->entries_index_m, cdbr->entries_index_s1, + cdbr->entries_index_s2); + hashes[1] = fast_remainder32(hashes[1], cdbr->entries_index, + cdbr->entries_index_m, cdbr->entries_index_s1, + cdbr->entries_index_s2); + hashes[2] = fast_remainder32(hashes[2], cdbr->entries_index, + cdbr->entries_index_m, cdbr->entries_index_s1, + cdbr->entries_index_s2); + + idx = get_uintX(cdbr->hash_base, hashes[0], cdbr->index_size); + idx += get_uintX(cdbr->hash_base, hashes[1], cdbr->index_size); + idx += get_uintX(cdbr->hash_base, hashes[2], cdbr->index_size); + + return cdbr_get(cdbr, fast_remainder32(idx, cdbr->entries, + cdbr->entries_m, cdbr->entries_s1, cdbr->entries_s2), data, + data_len); +} + +void +cdbr_close(struct cdbr *cdbr) +{ + if (cdbr->unmap) + (*cdbr->unmap)(cdbr->cookie, cdbr->mmap_base, cdbr->mmap_size); + free(cdbr); +} Index: contrib/mg/cdbw.h =================================================================== --- /dev/null +++ contrib/mg/cdbw.h @@ -0,0 +1,55 @@ +/* $NetBSD: cdbw.h,v 1.2 2012/06/03 21:21:45 joerg Exp $ */ +/*- + * Copyright (c) 2010 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _CDBW_H +#define _CDBW_H + +#include +#include +#include + +struct cdbw; + +struct cdbw *cdbw_open(void); +int cdbw_put(struct cdbw *, const void *, size_t, + const void *, size_t); +int cdbw_put_data(struct cdbw *, const void *, size_t, + uint32_t *); +int cdbw_put_key(struct cdbw *, const void *, size_t, + uint32_t); +uint32_t cdbw_stable_seeder(void); +int cdbw_output(struct cdbw *, int, const char[16], + uint32_t (*)(void)); +void cdbw_close(struct cdbw *); + +#endif /* _CDBW_H */ Index: contrib/mg/cdbw.c =================================================================== --- /dev/null +++ contrib/mg/cdbw.c @@ -0,0 +1,578 @@ +/* $NetBSD: cdbw.c,v 1.6 2017/11/11 18:05:31 alnsn Exp $ */ +/*- + * Copyright (c) 2009, 2010, 2015 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger and Alexander Nasonov. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include + +#include "cdbw.h" + +void le32enc(void *, uint32_t); +void mi_vector_hash(const void * __restrict, size_t, uint32_t, + uint32_t[3]); + +struct key_hash { + SLIST_ENTRY(key_hash) link; + uint32_t hashes[3]; + uint32_t idx; + void *key; + size_t keylen; +}; + +SLIST_HEAD(key_hash_head, key_hash); + +struct cdbw { + size_t data_counter; + size_t data_allocated; + size_t data_size; + size_t *data_len; + void **data_ptr; + + size_t hash_size; + struct key_hash_head *hash; + size_t key_counter; +}; + + /* Max. data counter that allows the index size to be 32bit. */ +static const uint32_t max_data_counter = 0xccccccccU; + +struct cdbw * +cdbw_open(void) +{ + struct cdbw *cdbw; + size_t i; + + cdbw = calloc(sizeof(*cdbw), 1); + if (cdbw == NULL) + return NULL; + + cdbw->hash_size = 1024; + cdbw->hash = calloc(cdbw->hash_size, sizeof(*cdbw->hash)); + if (cdbw->hash == NULL) { + free(cdbw); + return NULL; + } + + for (i = 0; i < cdbw->hash_size; ++i) + SLIST_INIT(cdbw->hash + i); + + return cdbw; +} + +int +cdbw_put(struct cdbw *cdbw, const void *key, size_t keylen, + const void *data, size_t datalen) +{ + uint32_t idx; + int rv; + + rv = cdbw_put_data(cdbw, data, datalen, &idx); + if (rv) + return rv; + rv = cdbw_put_key(cdbw, key, keylen, idx); + if (rv) { + --cdbw->data_counter; + free(cdbw->data_ptr[cdbw->data_counter]); + cdbw->data_size -= datalen; + return rv; + } + return 0; +} + +int +cdbw_put_data(struct cdbw *cdbw, const void *data, size_t datalen, + uint32_t *idx) +{ + + if (cdbw->data_counter == max_data_counter) + return -1; + + if (cdbw->data_size + datalen < cdbw->data_size || + cdbw->data_size + datalen > 0xffffffffU) + return -1; /* Overflow */ + + if (cdbw->data_allocated == cdbw->data_counter) { + void **new_data_ptr; + size_t *new_data_len; + size_t new_allocated; + + if (cdbw->data_allocated == 0) + new_allocated = 256; + else + new_allocated = cdbw->data_allocated * 2; + + new_data_ptr = realloc(cdbw->data_ptr, + sizeof(*cdbw->data_ptr) * new_allocated); + if (new_data_ptr == NULL) + return -1; + cdbw->data_ptr = new_data_ptr; + + new_data_len = realloc(cdbw->data_len, + sizeof(*cdbw->data_len) * new_allocated); + if (new_data_len == NULL) + return -1; + cdbw->data_len = new_data_len; + + cdbw->data_allocated = new_allocated; + } + + cdbw->data_ptr[cdbw->data_counter] = malloc(datalen); + if (cdbw->data_ptr[cdbw->data_counter] == NULL) + return -1; + memcpy(cdbw->data_ptr[cdbw->data_counter], data, datalen); + cdbw->data_len[cdbw->data_counter] = datalen; + cdbw->data_size += datalen; + *idx = cdbw->data_counter++; + return 0; +} + +int +cdbw_put_key(struct cdbw *cdbw, const void *key, size_t keylen, uint32_t idx) +{ + uint32_t hashes[3]; + struct key_hash_head *head, *head2, *new_head; + struct key_hash *key_hash; + size_t new_hash_size, i; + + if (idx >= cdbw->data_counter || + cdbw->key_counter == max_data_counter) + return -1; + + mi_vector_hash(key, keylen, 0, hashes); + + head = cdbw->hash + (hashes[0] & (cdbw->hash_size - 1)); + SLIST_FOREACH(key_hash, head, link) { + if (key_hash->keylen != keylen) + continue; + if (key_hash->hashes[0] != hashes[0]) + continue; + if (key_hash->hashes[1] != hashes[1]) + continue; + if (key_hash->hashes[2] != hashes[2]) + continue; + if (memcmp(key, key_hash->key, keylen)) + continue; + return -1; + } + key_hash = malloc(sizeof(*key_hash)); + if (key_hash == NULL) + return -1; + key_hash->key = malloc(keylen); + if (key_hash->key == NULL) { + free(key_hash); + return -1; + } + memcpy(key_hash->key, key, keylen); + key_hash->hashes[0] = hashes[0]; + key_hash->hashes[1] = hashes[1]; + key_hash->hashes[2] = hashes[2]; + key_hash->keylen = keylen; + key_hash->idx = idx; + SLIST_INSERT_HEAD(head, key_hash, link); + ++cdbw->key_counter; + + if (cdbw->key_counter <= cdbw->hash_size) + return 0; + + /* Try to resize the hash table, but ignore errors. */ + new_hash_size = cdbw->hash_size * 2; + new_head = calloc(sizeof(*new_head), new_hash_size); + if (new_head == NULL) + return 0; + + head = &cdbw->hash[hashes[0] & (cdbw->hash_size - 1)]; + for (i = 0; i < new_hash_size; ++i) + SLIST_INIT(new_head + i); + + for (i = 0; i < cdbw->hash_size; ++i) { + head = cdbw->hash + i; + + while ((key_hash = SLIST_FIRST(head)) != NULL) { + SLIST_REMOVE_HEAD(head, link); + head2 = new_head + + (key_hash->hashes[0] & (new_hash_size - 1)); + SLIST_INSERT_HEAD(head2, key_hash, link); + } + } + free(cdbw->hash); + cdbw->hash_size = new_hash_size; + cdbw->hash = new_head; + + return 0; +} + +void +cdbw_close(struct cdbw *cdbw) +{ + struct key_hash_head *head; + struct key_hash *key_hash; + size_t i; + + for (i = 0; i < cdbw->hash_size; ++i) { + head = cdbw->hash + i; + while ((key_hash = SLIST_FIRST(head)) != NULL) { + SLIST_REMOVE_HEAD(head, link); + free(key_hash->key); + free(key_hash); + } + } + + for (i = 0; i < cdbw->data_counter; ++i) + free(cdbw->data_ptr[i]); + free(cdbw->data_ptr); + free(cdbw->data_len); + free(cdbw->hash); + free(cdbw); +} + +uint32_t +cdbw_stable_seeder(void) +{ + return 0; +} + +/* + * The algorithm below is based on paper + * Cache-Oblivious Peeling of Random Hypergraphs by Djamal Belazzougui, + * Paolo Boldi, Giuseppe Ottaviano, Rossano Venturini, and Sebastiano + * Vigna. + * http://zola.di.unipi.it/rossano/wp-content/papercite-data/pdf/dcc14.pdf + */ + +/* + * Data type for a valid oriented edge (v0, v1, v2), v1 < v2. + * The first vertex v0 is implicit and is determined by an index + * of the corresponding element in the state->oedges array. + * If the degree of v0 is greater than 1, other members don't + * make sense because they're a result of XORing multiple values. + */ +struct oedge { + uint32_t degree; /* Degree of v0. */ + uint32_t verts[2]; /* v1 and v2 */ + uint32_t edge; +}; + +struct edge { + uint32_t idx; + + uint32_t left, middle, right; +}; + +struct state { + uint32_t data_entries; + uint32_t entries; + uint32_t keys; + uint32_t seed; + + uint32_t *g; + char *visited; + + struct oedge *oedges; + struct edge *edges; + uint32_t output_index; + uint32_t *output_order; +}; + +/* + * Add (delta == 1) or remove (delta == -1) the edge e from vertex v0. + */ +static void +add_remove_edge(struct oedge *o, int delta, uint32_t e, + uint32_t v0, uint32_t v1, uint32_t v2) +{ + + o[v0].verts[v1 < v2 ? 0 : 1] ^= v1; + o[v0].verts[v1 < v2 ? 1 : 0] ^= v2; + o[v0].degree += delta; + o[v0].edge ^= e; +} + +static void +add_edge(struct oedge *o, uint32_t e, + uint32_t v0, uint32_t v1, uint32_t v2) +{ + + add_remove_edge(o, 1, e, v0, v1, v2); +} + +static void +remove_vertex(struct state *state, uint32_t v0) +{ + uint32_t e, v1, v2; + struct oedge *o = state->oedges; + + if (o[v0].degree == 1) { + e = o[v0].edge; + v1 = o[v0].verts[0]; + v2 = o[v0].verts[1]; + o[v0].degree = 0; + add_remove_edge(o, -1, e, v1, v0, v2); + add_remove_edge(o, -1, e, v2, v0, v1); + state->output_order[--state->output_index] = e; + } +} + +static int +build_graph(struct cdbw *cdbw, struct state *state) +{ + struct key_hash_head *head; + struct key_hash *key_hash; + struct edge *e; + uint32_t hashes[3]; + size_t i; + + memset(state->oedges, 0, sizeof(struct oedge) * state->entries); + + e = state->edges; + for (i = 0; i < cdbw->hash_size; ++i) { + head = &cdbw->hash[i]; + SLIST_FOREACH(key_hash, head, link) { + e->idx = key_hash->idx; + mi_vector_hash(key_hash->key, key_hash->keylen, + state->seed, hashes); + e->left = hashes[0] % state->entries; + e->middle = hashes[1] % state->entries; + e->right = hashes[2] % state->entries; + + if (e->left == e->middle) + return -1; + add_edge(state->oedges, e - state->edges, + e->right, e->left, e->middle); + if (e->left == e->right) + return -1; + add_edge(state->oedges, e - state->edges, + e->middle, e->left, e->right); + if (e->middle == e->right) + return -1; + add_edge(state->oedges, e - state->edges, + e->left, e->middle, e->right); + + ++e; + } + } + + state->output_index = state->keys; + for (i = 0; i < state->entries; ++i) + remove_vertex(state, i); + + i = state->keys; + while (i > 0 && i > state->output_index) { + --i; + e = state->edges + state->output_order[i]; + remove_vertex(state, e->left); + remove_vertex(state, e->middle); + remove_vertex(state, e->right); + } + + return state->output_index == 0 ? 0 : -1; +} + +static void +assign_nodes(struct state *state) +{ + struct edge *e; + size_t i; + + for (i = 0; i < state->keys; ++i) { + e = state->edges + state->output_order[i]; + + if (!state->visited[e->left]) { + state->g[e->left] = + (2 * state->data_entries + e->idx + - state->g[e->middle] - state->g[e->right]) + % state->data_entries; + } else if (!state->visited[e->middle]) { + state->g[e->middle] = + (2 * state->data_entries + e->idx + - state->g[e->left] - state->g[e->right]) + % state->data_entries; + } else { + state->g[e->right] = + (2 * state->data_entries + e->idx + - state->g[e->left] - state->g[e->middle]) + % state->data_entries; + } + state->visited[e->left] = 1; + state->visited[e->middle] = 1; + state->visited[e->right] = 1; + } +} + +static size_t +compute_size(uint32_t size) +{ + if (size < 0x100) + return 1; + else if (size < 0x10000) + return 2; + else + return 4; +} + +#define COND_FLUSH_BUFFER(n) do { \ + if (__predict_false(cur_pos + (n) >= sizeof(buf))) { \ + ret = write(fd, buf, cur_pos); \ + if (ret == -1 || (size_t)ret != cur_pos) \ + return -1; \ + cur_pos = 0; \ + } \ +} while (/* CONSTCOND */ 0) + +static int +print_hash(struct cdbw *cdbw, struct state *state, int fd, const char *descr) +{ + uint32_t data_size; + uint8_t buf[90000]; + size_t i, size, size2, cur_pos; + ssize_t ret; + + memcpy(buf, "NBCDB\n\0", 7); + buf[7] = 1; + strncpy((char *)buf + 8, descr, 16); + le32enc(buf + 24, cdbw->data_size); + le32enc(buf + 28, cdbw->data_counter); + le32enc(buf + 32, state->entries); + le32enc(buf + 36, state->seed); + cur_pos = 40; + + size = compute_size(state->entries); + for (i = 0; i < state->entries; ++i) { + COND_FLUSH_BUFFER(4); + le32enc(buf + cur_pos, state->g[i]); + cur_pos += size; + } + size2 = compute_size(cdbw->data_size); + size = size * state->entries % size2; + if (size != 0) { + size = size2 - size; + COND_FLUSH_BUFFER(4); + le32enc(buf + cur_pos, 0); + cur_pos += size; + } + for (data_size = 0, i = 0; i < cdbw->data_counter; ++i) { + COND_FLUSH_BUFFER(4); + le32enc(buf + cur_pos, data_size); + cur_pos += size2; + data_size += cdbw->data_len[i]; + } + COND_FLUSH_BUFFER(4); + le32enc(buf + cur_pos, data_size); + cur_pos += size2; + + for (i = 0; i < cdbw->data_counter; ++i) { + COND_FLUSH_BUFFER(cdbw->data_len[i]); + if (cdbw->data_len[i] < sizeof(buf)) { + memcpy(buf + cur_pos, cdbw->data_ptr[i], + cdbw->data_len[i]); + cur_pos += cdbw->data_len[i]; + } else { + ret = write(fd, cdbw->data_ptr[i], cdbw->data_len[i]); + if (ret == -1 || (size_t)ret != cdbw->data_len[i]) + return -1; + } + } + if (cur_pos != 0) { + ret = write(fd, buf, cur_pos); + if (ret == -1 || (size_t)ret != cur_pos) + return -1; + } + return 0; +} + +int +cdbw_output(struct cdbw *cdbw, int fd, const char descr[16], + uint32_t (*seedgen)(void)) +{ + struct state state; + int rv; + + if (cdbw->data_counter == 0 || cdbw->key_counter == 0) { + state.entries = 0; + state.seed = 0; + print_hash(cdbw, &state, fd, descr); + return 0; + } + +#if HAVE_NBTOOL_CONFIG_H + if (seedgen == NULL) + seedgen = cdbw_stable_seeder; +#else + if (seedgen == NULL) + seedgen = arc4random; +#endif + + rv = 0; + + state.keys = cdbw->key_counter; + state.data_entries = cdbw->data_counter; + state.entries = state.keys + (state.keys + 3) / 4; + if (state.entries < 10) + state.entries = 10; + +#define NALLOC(var, n) var = calloc(sizeof(*var), n) + NALLOC(state.g, state.entries); + NALLOC(state.visited, state.entries); + NALLOC(state.oedges, state.entries); + NALLOC(state.edges, state.keys); + NALLOC(state.output_order, state.keys); +#undef NALLOC + + if (state.g == NULL || state.visited == NULL || state.oedges == NULL || + state.edges == NULL || state.output_order == NULL) { + rv = -1; + goto release; + } + + state.seed = 0; + do { + if (seedgen == cdbw_stable_seeder) + ++state.seed; + else + state.seed = (*seedgen)(); + } while (build_graph(cdbw, &state)); + + assign_nodes(&state); + rv = print_hash(cdbw, &state, fd, descr); + +release: + free(state.g); + free(state.visited); + free(state.oedges); + free(state.edges); + free(state.output_order); + + return rv; +} Index: contrib/mg/chrdef.h =================================================================== --- /dev/null +++ contrib/mg/chrdef.h @@ -0,0 +1,74 @@ +/* $OpenBSD: chrdef.h,v 1.10 2015/03/25 12:29:03 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * sys/default/chardef.h: character set specific #defines for Mg 2a + * Warning: System specific ones exist + */ + +/* + * Casting should be at least as efficient as anding with 0xff, + * and won't have the size problems. + */ +#define CHARMASK(c) ((unsigned char) (c)) + +/* + * These flags, and the macros below them, + * make up a do-it-yourself set of "ctype" macros that + * understand the DEC multinational set, and let me ask + * a slightly different set of questions. + */ +#define _MG_W 0x01 /* Word. */ +#define _MG_U 0x02 /* Upper case letter. */ +#define _MG_L 0x04 /* Lower case letter. */ +#define _MG_C 0x08 /* Control. */ +#define _MG_P 0x10 /* end of sentence punctuation */ +#define _MG_D 0x20 /* is decimal digit */ + +#define ISWORD(c) ((cinfo[CHARMASK(c)]&_MG_W)!=0) +#define ISCTRL(c) ((cinfo[CHARMASK(c)]&_MG_C)!=0) +#define ISUPPER(c) ((cinfo[CHARMASK(c)]&_MG_U)!=0) +#define ISLOWER(c) ((cinfo[CHARMASK(c)]&_MG_L)!=0) +#define ISEOSP(c) ((cinfo[CHARMASK(c)]&_MG_P)!=0) +#define ISDIGIT(c) ((cinfo[CHARMASK(c)]&_MG_D)!=0) +#define TOUPPER(c) ((c)-0x20) +#define TOLOWER(c) ((c)+0x20) + +/* + * Generally useful thing for chars + */ +#define CCHR(x) ((x) ^ 0x40) /* CCHR('?') == DEL */ + +#define K00 256 +#define K01 257 +#define K02 258 +#define K03 259 +#define K04 260 +#define K05 261 +#define K06 262 +#define K07 263 +#define K08 264 +#define K09 265 +#define K0A 266 +#define K0B 267 +#define K0C 268 +#define K0D 269 +#define K0E 270 +#define K0F 271 +#define K10 272 +#define K11 273 +#define K12 274 +#define K13 275 +#define K14 276 +#define K15 277 +#define K16 278 +#define K17 279 +#define K18 280 +#define K19 281 +#define K1A 282 +#define K1B 283 +#define K1C 284 +#define K1D 285 +#define K1E 286 +#define K1F 287 Index: contrib/mg/cinfo.c =================================================================== --- /dev/null +++ contrib/mg/cinfo.c @@ -0,0 +1,158 @@ +/* $OpenBSD: cinfo.c,v 1.18 2015/03/19 21:22:15 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * Character class tables. + * Do it yourself character classification + * macros, that understand the multinational character set, + * and let me ask some questions the standard macros (in + * ctype.h) don't let you ask. + */ + +#include +#include +#include +#include + +#include "def.h" + +/* + * This table, indexed by a character drawn + * from the 256 member character set, is used by my + * own character type macros to answer questions about the + * type of a character. It handles the full multinational + * character set, and lets me ask some questions that the + * standard "ctype" macros cannot ask. + */ +/* + * Due to incompatible behaviour between "standard" emacs and + * ctags word traversing, '_' character's value is changed on + * the fly in ctags mode, hence non-const. + */ +char cinfo[256] = { + _MG_C, _MG_C, _MG_C, _MG_C, /* 0x0X */ + _MG_C, _MG_C, _MG_C, _MG_C, + _MG_C, _MG_C, _MG_C, _MG_C, + _MG_C, _MG_C, _MG_C, _MG_C, + _MG_C, _MG_C, _MG_C, _MG_C, /* 0x1X */ + _MG_C, _MG_C, _MG_C, _MG_C, + _MG_C, _MG_C, _MG_C, _MG_C, + _MG_C, _MG_C, _MG_C, _MG_C, + 0, _MG_P, 0, 0, /* 0x2X */ + _MG_W, _MG_W, 0, _MG_W, + 0, 0, 0, 0, + 0, 0, _MG_P, 0, + _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, /* 0x3X */ + _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, _MG_D | _MG_W, + _MG_D | _MG_W, _MG_D | _MG_W, 0, 0, + 0, 0, 0, _MG_P, + 0, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0x4X */ + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0x5X */ + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, 0, + 0, 0, 0, 0, + 0, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0x6X */ + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0x7X */ + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, 0, + 0, 0, 0, _MG_C, + 0, 0, 0, 0, /* 0x8X */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, /* 0x9X */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, /* 0xAX */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, /* 0xBX */ + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0xCX */ + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + 0, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, /* 0xDX */ + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, _MG_U | _MG_W, + _MG_U | _MG_W, _MG_U | _MG_W, 0, _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0xEX */ + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + 0, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, /* 0xFX */ + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, _MG_L | _MG_W, + _MG_L | _MG_W, _MG_L | _MG_W, 0, 0 +}; + +/* + * Find the name of a keystroke. Needs to be changed to handle 8-bit printing + * characters and function keys better. Returns a pointer to the terminating + * '\0'. Returns NULL on failure. + */ +char * +getkeyname(char *cp, size_t len, int k) +{ + const char *np; + size_t copied; + + if (k < 0) + k = CHARMASK(k); /* sign extended char */ + switch (k) { + case CCHR('@'): + np = "C-SPC"; + break; + case CCHR('I'): + np = "TAB"; + break; + case CCHR('M'): + np = "RET"; + break; + case CCHR('['): + np = "ESC"; + break; + case ' ': + np = "SPC"; + break; /* yuck again */ + case CCHR('?'): + np = "DEL"; + break; + default: + if (k >= KFIRST && k <= KLAST && + (np = keystrings[k - KFIRST]) != NULL) + break; + if (k > CCHR('?')) { + *cp++ = '0'; + *cp++ = ((k >> 6) & 7) + '0'; + *cp++ = ((k >> 3) & 7) + '0'; + *cp++ = (k & 7) + '0'; + *cp = '\0'; + return (cp); + } else if (k < ' ') { + *cp++ = 'C'; + *cp++ = '-'; + k = CCHR(k); + if (ISUPPER(k)) + k = TOLOWER(k); + } + *cp++ = k; + *cp = '\0'; + return (cp); + } + copied = strlcpy(cp, np, len); + if (copied >= len) + copied = len - 1; + return (cp + copied); +} Index: contrib/mg/cmode.c =================================================================== --- /dev/null +++ contrib/mg/cmode.c @@ -0,0 +1,522 @@ +/* $OpenBSD: cmode.c,v 1.17 2019/07/11 18:20:18 lum Exp $ */ +/* + * This file is in the public domain. + * + * Author: Kjell Wooding + */ + +/* + * Implement an non-irritating KNF-compliant mode for editing + * C code. + */ + +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" +#include "kbd.h" + +/* Pull in from modes.c */ +extern int changemode(int, int, char *); + +static int cc_strip_trailp = TRUE; /* Delete Trailing space? */ +static int cc_basic_indent = 8; /* Basic Indent multiple */ +static int cc_cont_indent = 4; /* Continued line indent */ +static int cc_colon_indent = -8; /* Label / case indent */ + +static int getmatch(int, int); +static int getindent(const struct line *, int *); +static int in_whitespace(struct line *, int); +static int findcolpos(const struct buffer *, const struct line *, int); +static struct line *findnonblank(struct line *); +static int isnonblank(const struct line *, int); + +void cmode_init(void); +int cc_comment(int, int); + +/* Keymaps */ + +static PF cmode_brace[] = { + cc_brace, /* } */ +}; + +static PF cmode_cCP[] = { + compile, /* C-c P */ +}; + + +static PF cmode_cc[] = { + NULL, /* ^C */ + rescan, /* ^D */ + rescan, /* ^E */ + rescan, /* ^F */ + rescan, /* ^G */ + rescan, /* ^H */ + cc_tab, /* ^I */ + rescan, /* ^J */ + rescan, /* ^K */ + rescan, /* ^L */ + cc_lfindent, /* ^M */ +}; + +static PF cmode_spec[] = { + cc_char, /* : */ +}; + +static struct KEYMAPE (1) cmode_cmap = { + 1, + 1, + rescan, + { + { 'P', 'P', cmode_cCP, NULL } + } +}; + +static struct KEYMAPE (3) cmodemap = { + 3, + 3, + rescan, + { + { CCHR('C'), CCHR('M'), cmode_cc, (KEYMAP *) &cmode_cmap }, + { ':', ':', cmode_spec, NULL }, + { '}', '}', cmode_brace, NULL } + } +}; + +/* Funtion, Mode hooks */ + +void +cmode_init(void) +{ + funmap_add(cmode, "c-mode", 0); + funmap_add(cc_char, "c-handle-special-char", 0); + funmap_add(cc_brace, "c-handle-special-brace", 0); + funmap_add(cc_tab, "c-tab-or-indent", 0); + funmap_add(cc_indent, "c-indent", 0); + funmap_add(cc_lfindent, "c-indent-and-newline", 0); + maps_add((KEYMAP *)&cmodemap, "c"); +} + +/* + * Enable/toggle c-mode + */ +int +cmode(int f, int n) +{ + return(changemode(f, n, "c")); +} + +/* + * Handle special C character - selfinsert then indent. + */ +int +cc_char(int f, int n) +{ + if (n < 0) + return (FALSE); + if (selfinsert(FFRAND, n) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + +/* + * Handle special C character - selfinsert then indent. + */ +int +cc_brace(int f, int n) +{ + if (n < 0) + return (FALSE); + if (showmatch(FFRAND, 1) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + + +/* + * If we are in the whitespace at the beginning of the line, + * simply act as a regular tab. If we are not, indent + * current line according to whitespace rules. + */ +int +cc_tab(int f, int n) +{ + int inwhitep = FALSE; /* In leading whitespace? */ + + inwhitep = in_whitespace(curwp->w_dotp, llength(curwp->w_dotp)); + + /* If empty line, or in whitespace */ + if (llength(curwp->w_dotp) == 0 || inwhitep) + return (selfinsert(f, n)); + + return (cc_indent(FFRAND, 1)); +} + +/* + * Attempt to indent current line according to KNF rules. + */ +int +cc_indent(int f, int n) +{ + int pi, mi; /* Previous indents (mi is ignored) */ + int ci; /* current indent */ + struct line *lp; + int ret; + + if (n < 0) + return (FALSE); + + undo_boundary_enable(FFRAND, 0); + if (cc_strip_trailp) + deltrailwhite(FFRAND, 1); + + /* + * Search backwards for a non-blank, non-preprocessor, + * non-comment line + */ + + lp = findnonblank(curwp->w_dotp); + + pi = getindent(lp, &mi); + + /* Strip leading space on current line */ + delleadwhite(FFRAND, 1); + /* current indent is computed only to current position */ + (void)getindent(curwp->w_dotp, &ci); + + if (pi + ci < 0) + ret = indent(FFOTHARG, 0); + else + ret = indent(FFOTHARG, pi + ci); + + undo_boundary_enable(FFRAND, 1); + + return (ret); +} + +/* + * Indent-and-newline (technically, newline then indent) + */ +int +cc_lfindent(int f, int n) +{ + if (n < 0) + return (FALSE); + if (enewline(FFRAND, 1) == FALSE) + return (FALSE); + return (cc_indent(FFRAND, n)); +} + +/* + * Get the level of indention after line lp is processed + * Note getindent has two returns: + * curi = value if indenting current line. + * return value = value affecting subsequent lines. + */ +static int +getindent(const struct line *lp, int *curi) +{ + int lo, co; /* leading space, current offset*/ + int nicol = 0; /* position count */ + int c = '\0'; /* current char */ + int newind = 0; /* new index value */ + int stringp = FALSE; /* in string? */ + int escp = FALSE; /* Escape char? */ + int lastc = '\0'; /* Last matched string delimeter */ + int nparen = 0; /* paren count */ + int obrace = 0; /* open brace count */ + int cbrace = 0; /* close brace count */ + int firstnwsp = FALSE; /* First nonspace encountered? */ + int colonp = FALSE; /* Did we see a colon? */ + int questionp = FALSE; /* Did we see a question mark? */ + int slashp = FALSE; /* Slash? */ + int astp = FALSE; /* Asterisk? */ + int cpos = -1; /* comment position */ + int cppp = FALSE; /* Preprocessor command? */ + + *curi = 0; + + /* Compute leading space */ + for (lo = 0; lo < llength(lp); lo++) { + if (!isspace(c = lgetc(lp, lo))) + break; + if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif /* NOTAB */ + ) { + nicol |= 0x07; + } + nicol++; + } + + /* If last line was blank, choose 0 */ + if (lo == llength(lp)) + nicol = 0; + + newind = 0; + /* Compute modifiers */ + for (co = lo; co < llength(lp); co++) { + c = lgetc(lp, co); + /* We have a non-whitespace char */ + if (!firstnwsp && !isspace(c)) { + if (c == '#') + cppp = TRUE; + firstnwsp = TRUE; + } + if (c == '\\') + escp = !escp; + else if (stringp) { + if (!escp && (c == '"' || c == '\'')) { + /* unescaped string char */ + if (getmatch(c, lastc)) + stringp = FALSE; + } + } else if (c == '"' || c == '\'') { + stringp = TRUE; + lastc = c; + } else if (c == '(') { + nparen++; + } else if (c == ')') { + nparen--; + } else if (c == '{') { + obrace++; + firstnwsp = FALSE; + } else if (c == '}') { + cbrace++; + } else if (c == '?') { + questionp = TRUE; + } else if (c == ':') { + /* ignore (foo ? bar : baz) construct */ + if (!questionp) + colonp = TRUE; + } else if (c == '/') { + /* first nonwhitespace? -> indent */ + if (firstnwsp) { + /* If previous char asterisk -> close */ + if (astp) + cpos = -1; + else + slashp = TRUE; + } + } else if (c == '*') { + /* If previous char slash -> open */ + if (slashp) + cpos = co; + else + astp = TRUE; + } else if (firstnwsp) { + firstnwsp = FALSE; + } + + /* Reset matches that apply to next character only */ + if (c != '\\') + escp = FALSE; + if (c != '*') + astp = FALSE; + if (c != '/') + slashp = FALSE; + } + /* + * If not terminated with a semicolon, and brace or paren open. + * we continue + */ + if (colonp) { + *curi += cc_colon_indent; + newind -= cc_colon_indent; + } + + *curi -= (cbrace) * cc_basic_indent; + newind += obrace * cc_basic_indent; + + if (nparen < 0) + newind -= cc_cont_indent; + else if (nparen > 0) + newind += cc_cont_indent; + + *curi += nicol; + + /* Ignore preprocessor. Otherwise, add current column */ + if (cppp) { + newind = nicol; + *curi = 0; + } else { + newind += nicol; + } + + if (cpos != -1) + newind = findcolpos(curbp, lp, cpos); + + return (newind); +} + +/* + * Given a delimeter and its purported mate, tell us if they + * match. + */ +static int +getmatch(int c, int mc) +{ + int match = FALSE; + + switch (c) { + case '"': + match = (mc == '"'); + break; + case '\'': + match = (mc == '\''); + break; + case '(': + match = (mc == ')'); + break; + case '[': + match = (mc == ']'); + break; + case '{': + match = (mc == '}'); + break; + } + + return (match); +} + +static int +in_whitespace(struct line *lp, int len) +{ + int lo; + int inwhitep = FALSE; + + for (lo = 0; lo < len; lo++) { + if (!isspace(lgetc(lp, lo))) + break; + if (lo == len - 1) + inwhitep = TRUE; + } + + return (inwhitep); +} + + +/* convert a line/offset pair to a column position (for indenting) */ +static int +findcolpos(const struct buffer *bp, const struct line *lp, int lo) +{ + int col, i, c; + char tmp[5]; + + /* determine column */ + col = 0; + + for (i = 0; i < lo; ++i) { + c = lgetc(lp, i); + if (c == '\t' +#ifdef NOTAB + && !(bp->b_flag & BFNOTAB) +#endif /* NOTAB */ + ) { + col |= 0x07; + col++; + } else if (ISCTRL(c) != FALSE) + col += 2; + else if (isprint(c)) { + col++; + } else { + col += snprintf(tmp, sizeof(tmp), "\\%o", c); + } + + } + return (col); +} + +/* + * Find a non-blank line, searching backwards from the supplied line pointer. + * For C, nonblank is non-preprocessor, non C++, and accounts + * for complete C-style comments. + */ +static struct line * +findnonblank(struct line *lp) +{ + int lo; + int nonblankp = FALSE; + int commentp = FALSE; + int slashp; + int astp; + int c; + + while (lback(lp) != curbp->b_headp && (commentp || !nonblankp)) { + lp = lback(lp); + slashp = FALSE; + astp = FALSE; + + /* Potential nonblank? */ + nonblankp = isnonblank(lp, llength(lp)); + + /* + * Search from end, removing complete C-style + * comments. If one is found, ignore it and + * test for nonblankness from where it starts. + */ + for (lo = llength(lp) - 1; lo >= 0; lo--) { + if (!isspace(c = lgetc(lp, lo))) { + if (commentp) { /* find comment "open" */ + if (c == '*') + astp = TRUE; + else if (astp && c == '/') { + commentp = FALSE; + /* whitespace to here? */ + nonblankp = isnonblank(lp, lo); + } + } else { /* find comment "close" */ + if (c == '/') + slashp = TRUE; + else if (slashp && c == '*') + /* found a comment */ + commentp = TRUE; + } + } + } + } + + /* Rewound to start of file? */ + if (lback(lp) == curbp->b_headp && !nonblankp) + return (curbp->b_headp); + + return (lp); +} + +/* + * Given a line, scan forward to 'omax' and determine if we + * are all C whitespace. + * Note that preprocessor directives and C++-style comments + * count as whitespace. C-style comments do not, and must + * be handled elsewhere. + */ +static int +isnonblank(const struct line *lp, int omax) +{ + int nonblankp = FALSE; /* Return value */ + int slashp = FALSE; /* Encountered slash */ + int lo; /* Loop index */ + int c; /* char being read */ + + /* Scan from front for preprocessor, C++ comments */ + for (lo = 0; lo < omax; lo++) { + if (!isspace(c = lgetc(lp, lo))) { + /* Possible nonblank line */ + nonblankp = TRUE; + /* skip // and # starts */ + if (c == '#' || (slashp && c == '/')) { + nonblankp = FALSE; + break; + } else if (!slashp && c == '/') { + slashp = TRUE; + continue; + } + } + slashp = FALSE; + } + return (nonblankp); +} Index: contrib/mg/common.h =================================================================== --- /dev/null +++ contrib/mg/common.h @@ -0,0 +1,35 @@ +/* + * Support for non-OpenBSD systems. + */ + +/* From OpenBSD sys/stat.h; glibc io/sys/stat.h agrees */ +#ifndef DEFFILEMODE +#define DEFFILEMODE 0000666 +#endif + +/* From OpenBSD regex.h */ +#ifndef REG_STARTEND +#define REG_STARTEND 00004 +#endif + +/* From OpenBSD sys/queue.h */ +#ifndef SLIST_FOREACH_SAFE +#define SLIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = SLIST_FIRST(head); \ + (var) && ((tvar) = SLIST_NEXT(var, field), 1); \ + (var) = (tvar)) +#endif + +/* From OpenBSD sys/queue.h */ +#ifndef TAILQ_END +#define TAILQ_END(head) NULL +#endif + +/* From OpenBSD sys/queue.h */ +#ifndef TAILQ_FOREACH_SAFE +#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = TAILQ_FIRST(head); \ + (var) != TAILQ_END(head) && \ + ((tvar) = TAILQ_NEXT(var, field), 1); \ + (var) = (tvar)) +#endif Index: contrib/mg/compile.c =================================================================== --- /dev/null +++ contrib/mg/compile.c @@ -0,0 +1,670 @@ +/* $NetBSD: compile.c,v 1.12 2017/05/04 09:46:30 roy Exp $ */ + +/* + * Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */ +static uint16_t +le16dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[1] << 8) | p[0]); +} + +static void +le16enc(void *buf, uint32_t u) +{ + uint8_t *p = (uint8_t *) buf; + + p[0] = u & 0xff; + p[1] = ((unsigned)u >> 8) & 0xff; +} + +static void +dowarn(int flags, const char *fmt, ...) +{ + va_list va; + + errno = EINVAL; + if (flags & TIC_WARNING) { + va_start(va, fmt); + vwarnx(fmt, va); + va_end(va); + } +} + +char * +_ti_grow_tbuf(TBUF *tbuf, size_t len) +{ + char *buf; + size_t l; + + l = tbuf->bufpos + len; + if (l > tbuf->buflen) { + if (tbuf->buflen == 0) + buf = malloc(l); + else + buf = realloc(tbuf->buf, l); + if (buf == NULL) + return NULL; + tbuf->buf = buf; + tbuf->buflen = l; + } + return tbuf->buf; +} + +char * +_ti_find_cap(TBUF *tbuf, char type, short ind) +{ + size_t n; + uint16_t num; + char *cap; + + cap = tbuf->buf; + for (n = tbuf->entries; n > 0; n--) { + num = le16dec(cap); + cap += sizeof(uint16_t); + if ((short)num == ind) + return cap; + switch (type) { + case 'f': + cap++; + break; + case 'n': + cap += sizeof(uint16_t); + break; + case 's': + num = le16dec(cap); + cap += sizeof(uint16_t); + cap += num; + break; + } + } + + errno = ESRCH; + return NULL; +} + +char * +_ti_find_extra(TBUF *tbuf, const char *code) +{ + size_t n; + uint16_t num; + char *cap; + + cap = tbuf->buf; + for (n = tbuf->entries; n > 0; n--) { + num = le16dec(cap); + cap += sizeof(uint16_t); + if (strcmp(cap, code) == 0) + return cap + num; + cap += num; + switch (*cap++) { + case 'f': + cap++; + break; + case 'n': + cap += sizeof(uint16_t); + break; + case 's': + num = le16dec(cap); + cap += sizeof(uint16_t); + cap += num; + break; + } + } + + errno = ESRCH; + return NULL; +} + +size_t +_ti_store_extra(TIC *tic, int wrn, char *id, char type, char flag, short num, + char *str, size_t strl, int flags) +{ + size_t l; + + if (strcmp(id, "use") != 0) { + if (_ti_find_extra(&tic->extras, id) != NULL) + return 0; + if (!(flags & TIC_EXTRA)) { + if (wrn != 0) + dowarn(flags, "%s: %s: unknown capability", + tic->name, id); + return 0; + } + } + + l = strlen(id) + 1; + if (l > UINT16_T_MAX) { + dowarn(flags, "%s: %s: cap name is too long", tic->name, id); + return 0; + } + + if (!_ti_grow_tbuf(&tic->extras, + l + strl + (sizeof(uint16_t) * 2) + 1)) + return 0; + le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)l); + tic->extras.bufpos += sizeof(uint16_t); + memcpy(tic->extras.buf + tic->extras.bufpos, id, l); + tic->extras.bufpos += l; + tic->extras.buf[tic->extras.bufpos++] = type; + switch (type) { + case 'f': + tic->extras.buf[tic->extras.bufpos++] = flag; + break; + case 'n': + le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)num); + tic->extras.bufpos += sizeof(uint16_t); + break; + case 's': + le16enc(tic->extras.buf + tic->extras.bufpos, (uint16_t)strl); + tic->extras.bufpos += sizeof(uint16_t); + memcpy(tic->extras.buf + tic->extras.bufpos, str, strl); + tic->extras.bufpos += strl; + break; + } + tic->extras.entries++; + return 1; +} + +ssize_t +_ti_flatten(uint8_t **buf, const TIC *tic) +{ + size_t buflen, len, alen, dlen; + uint8_t *cap; + + len = strlen(tic->name) + 1; + if (tic->alias == NULL) + alen = 0; + else + alen = strlen(tic->alias) + 1; + if (tic->desc == NULL) + dlen = 0; + else + dlen = strlen(tic->desc) + 1; + buflen = sizeof(char) + + sizeof(uint16_t) + len + + sizeof(uint16_t) + alen + + sizeof(uint16_t) + dlen + + (sizeof(uint16_t) * 2) + tic->flags.bufpos + + (sizeof(uint16_t) * 2) + tic->nums.bufpos + + (sizeof(uint16_t) * 2) + tic->strs.bufpos + + (sizeof(uint16_t) * 2) + tic->extras.bufpos; + *buf = malloc(buflen); + if (*buf == NULL) + return -1; + + cap = *buf; + *cap++ = 1; + le16enc(cap, (uint16_t)len); + cap += sizeof(uint16_t); + memcpy(cap, tic->name, len); + cap += len; + + le16enc(cap, (uint16_t)alen); + cap += sizeof(uint16_t); + if (tic->alias != NULL) { + memcpy(cap, tic->alias, alen); + cap += alen; + } + le16enc(cap, (uint16_t)dlen); + cap += sizeof(uint16_t); + if (tic->desc != NULL) { + memcpy(cap, tic->desc, dlen); + cap += dlen; + } + + if (tic->flags.entries == 0) { + le16enc(cap, 0); + cap += sizeof(uint16_t); + } else { + le16enc(cap, (uint16_t)(tic->flags.bufpos + sizeof(uint16_t))); + cap += sizeof(uint16_t); + le16enc(cap, (uint16_t)tic->flags.entries); + cap += sizeof(uint16_t); + memcpy(cap, tic->flags.buf, tic->flags.bufpos); + cap += tic->flags.bufpos; + } + + if (tic->nums.entries == 0) { + le16enc(cap, 0); + cap += sizeof(uint16_t); + } else { + le16enc(cap, (uint16_t)(tic->nums.bufpos + sizeof(uint16_t))); + cap += sizeof(uint16_t); + le16enc(cap, (uint16_t)tic->nums.entries); + cap += sizeof(uint16_t); + memcpy(cap, tic->nums.buf, tic->nums.bufpos); + cap += tic->nums.bufpos; + } + + if (tic->strs.entries == 0) { + le16enc(cap, 0); + cap += sizeof(uint16_t); + } else { + le16enc(cap, (uint16_t)(tic->strs.bufpos + sizeof(uint16_t))); + cap += sizeof(uint16_t); + le16enc(cap, (uint16_t)tic->strs.entries); + cap += sizeof(uint16_t); + memcpy(cap, tic->strs.buf, tic->strs.bufpos); + cap += tic->strs.bufpos; + } + + if (tic->extras.entries == 0) { + le16enc(cap, 0); + cap += sizeof(uint16_t); + } else { + le16enc(cap, (uint16_t)(tic->extras.bufpos + sizeof(uint16_t))); + cap += sizeof(uint16_t); + le16enc(cap, (uint16_t)tic->extras.entries); + cap += sizeof(uint16_t); + memcpy(cap, tic->extras.buf, tic->extras.bufpos); + cap += tic->extras.bufpos; + } + + return cap - *buf; +} + +static int +encode_string(const char *term, const char *cap, TBUF *tbuf, const char *str, + int flags) +{ + int slash, i, num; + char ch, *p, *s, last; + + if (_ti_grow_tbuf(tbuf, strlen(str) + 1) == NULL) + return -1; + p = s = tbuf->buf + tbuf->bufpos; + slash = 0; + last = '\0'; + /* Convert escape codes */ + while ((ch = *str++) != '\0') { + if (ch == '\n') { + /* Following a newline, strip leading whitespace from + * capability strings. */ + while (isspace((unsigned char)*str)) + str++; + continue; + } + if (slash == 0 && ch == '\\') { + slash = 1; + continue; + } + if (slash == 0) { + if (last != '%' && ch == '^') { + ch = *str++; + if (((unsigned char)ch) >= 128) + dowarn(flags, + "%s: %s: illegal ^ character", + term, cap); + if (ch == '\0') + break; + if (ch == '?') + ch = '\177'; + else if ((ch &= 037) == 0) + ch = (char)128; + } else if (!isprint((unsigned char)ch)) + dowarn(flags, + "%s: %s: unprintable character", + term, cap); + *p++ = ch; + last = ch; + continue; + } + slash = 0; + if (ch >= '0' && ch <= '7') { + num = ch - '0'; + for (i = 0; i < 2; i++) { + if (*str < '0' || *str > '7') { + if (isdigit((unsigned char)*str)) + dowarn(flags, + "%s: %s: non octal" + " digit", term, cap); + else + break; + } + num = num * 8 + *str++ - '0'; + } + if (num == 0) + num = 0200; + *p++ = (char)num; + continue; + } + switch (ch) { + case 'a': + *p++ = '\a'; + break; + case 'b': + *p++ = '\b'; + break; + case 'e': /* FALLTHROUGH */ + case 'E': + *p++ = '\033'; + break; + case 'f': + *p++ = '\014'; + break; + case 'l': /* FALLTHROUGH */ + case 'n': + *p++ = '\n'; + break; + case 'r': + *p++ = '\r'; + break; + case 's': + *p++ = ' '; + break; + case 't': + *p++ = '\t'; + break; + default: + /* We should warn here */ + case '^': + case ',': + case ':': + case '|': + *p++ = ch; + break; + } + last = ch; + } + *p++ = '\0'; + tbuf->bufpos += (size_t)(p - s); + return 0; +} + +char * +_ti_get_token(char **cap, char sep) +{ + char esc, *token; + + while (isspace((unsigned char)**cap)) + (*cap)++; + if (**cap == '\0') + return NULL; + + /* We can't use stresep(3) as ^ we need two escape chars */ + esc = '\0'; + for (token = *cap; + **cap != '\0' && (esc != '\0' || **cap != sep); + (*cap)++) + { + if (esc == '\0') { + if (**cap == '\\' || **cap == '^') + esc = **cap; + } else { + /* termcap /E/ is valid */ + if (sep == ':' && esc == '\\' && **cap == 'E') + esc = 'x'; + else + esc = '\0'; + } + } + + if (**cap != '\0') + *(*cap)++ = '\0'; + + return token; +} + +TIC * +_ti_compile(char *cap, int flags) +{ + char *token, *p, *e, *name, *desc, *alias; + signed char flag; + long cnum; + short ind, num; + size_t len; + TBUF buf; + TIC *tic; + + name = _ti_get_token(&cap, ','); + if (name == NULL) { + dowarn(flags, "no seperator found: %s", cap); + return NULL; + } + desc = strrchr(name, '|'); + if (desc != NULL) + *desc++ = '\0'; + alias = strchr(name, '|'); + if (alias != NULL) + *alias++ = '\0'; + + tic = calloc(sizeof(*tic), 1); + if (tic == NULL) + return NULL; + + buf.buf = NULL; + buf.buflen = 0; + + tic->name = strdup(name); + if (tic->name == NULL) + goto error; + if (alias != NULL && flags & TIC_ALIAS) { + tic->alias = strdup(alias); + if (tic->alias == NULL) + goto error; + } + if (desc != NULL && flags & TIC_DESCRIPTION) { + tic->desc = strdup(desc); + if (tic->desc == NULL) + goto error; + } + + for (token = _ti_get_token(&cap, ','); + token != NULL && *token != '\0'; + token = _ti_get_token(&cap, ',')) + { + /* Skip commented caps */ + if (!(flags & TIC_COMMENT) && token[0] == '.') + continue; + + /* Obsolete entries */ + if (token[0] == 'O' && token[1] == 'T') { + if (!(flags & TIC_EXTRA)) + continue; + token += 2; + } + + /* str cap */ + p = strchr(token, '='); + if (p != NULL) { + *p++ = '\0'; + /* Don't use the string if we already have it */ + ind = (short)_ti_strindex(token); + if (ind != -1 && + _ti_find_cap(&tic->strs, 's', ind) != NULL) + continue; + + /* Encode the string to our scratch buffer */ + buf.bufpos = 0; + if (encode_string(tic->name, token, + &buf, p, flags) == -1) + goto error; + if (buf.bufpos > UINT16_T_MAX) { + dowarn(flags, "%s: %s: string is too long", + tic->name, token); + continue; + } + if (!VALID_STRING(buf.buf)) { + dowarn(flags, "%s: %s: invalid string", + tic->name, token); + continue; + } + + if (ind == -1) + _ti_store_extra(tic, 1, token, 's', -1, -2, + buf.buf, buf.bufpos, flags); + else { + if (!_ti_grow_tbuf(&tic->strs, + (sizeof(uint16_t) * 2) + buf.bufpos)) + goto error; + le16enc(tic->strs.buf + tic->strs.bufpos, (uint16_t)ind); + tic->strs.bufpos += sizeof(uint16_t); + le16enc(tic->strs.buf + tic->strs.bufpos, + (uint16_t)buf.bufpos); + tic->strs.bufpos += sizeof(uint16_t); + memcpy(tic->strs.buf + tic->strs.bufpos, + buf.buf, buf.bufpos); + tic->strs.bufpos += buf.bufpos; + tic->strs.entries++; + } + continue; + } + + /* num cap */ + p = strchr(token, '#'); + if (p != NULL) { + *p++ = '\0'; + /* Don't use the number if we already have it */ + ind = (short)_ti_numindex(token); + if (ind != -1 && + _ti_find_cap(&tic->nums, 'n', ind) != NULL) + continue; + + cnum = strtol(p, &e, 0); + if (*e != '\0') { + dowarn(flags, "%s: %s: not a number", + tic->name, token); + continue; + } + if (!VALID_NUMERIC(cnum)) { + dowarn(flags, "%s: %s: number out of range", + tic->name, token); + continue; + } + num = (short)cnum; + if (ind == -1) + _ti_store_extra(tic, 1, token, 'n', -1, + num, NULL, 0, flags); + else { + if (_ti_grow_tbuf(&tic->nums, + sizeof(uint16_t) * 2) == NULL) + goto error; + le16enc(tic->nums.buf + tic->nums.bufpos, + (uint16_t)ind); + tic->nums.bufpos += sizeof(uint16_t); + le16enc(tic->nums.buf + tic->nums.bufpos, + (uint16_t)num); + tic->nums.bufpos += sizeof(uint16_t); + tic->nums.entries++; + } + continue; + } + + flag = 1; + len = strlen(token) - 1; + if (token[len] == '@') { + flag = CANCELLED_BOOLEAN; + token[len] = '\0'; + } + ind = (short)_ti_flagindex(token); + if (ind == -1 && flag == CANCELLED_BOOLEAN) { + if ((ind = (short)_ti_numindex(token)) != -1) { + if (_ti_find_cap(&tic->nums, 'n', ind) != NULL) + continue; + if (_ti_grow_tbuf(&tic->nums, + sizeof(uint16_t) * 2) == NULL) + goto error; + le16enc(tic->nums.buf + tic->nums.bufpos, + (uint16_t)ind); + tic->nums.bufpos += sizeof(uint16_t); + le16enc(tic->nums.buf + tic->nums.bufpos, + (uint16_t)CANCELLED_NUMERIC); + tic->nums.bufpos += sizeof(uint16_t); + tic->nums.entries++; + continue; + } else if ((ind = (short)_ti_strindex(token)) != -1) { + if (_ti_find_cap(&tic->strs, 's', ind) != NULL) + continue; + if (_ti_grow_tbuf(&tic->strs, + (sizeof(uint16_t) * 2) + 1) == NULL) + goto error; + le16enc(tic->strs.buf + tic->strs.bufpos, (uint16_t)ind); + tic->strs.bufpos += sizeof(uint16_t); + le16enc(tic->strs.buf + tic->strs.bufpos, 0); + tic->strs.bufpos += sizeof(uint16_t); + tic->strs.entries++; + continue; + } + } + if (ind == -1) + _ti_store_extra(tic, 1, token, 'f', flag, 0, NULL, 0, + flags); + else if (_ti_find_cap(&tic->flags, 'f', ind) == NULL) { + if (_ti_grow_tbuf(&tic->flags, sizeof(uint16_t) + 1) + == NULL) + goto error; + le16enc(tic->flags.buf + tic->flags.bufpos, + (uint16_t)ind); + tic->flags.bufpos += sizeof(uint16_t); + tic->flags.buf[tic->flags.bufpos++] = flag; + tic->flags.entries++; + } + } + + free(buf.buf); + return tic; + +error: + free(buf.buf); + _ti_freetic(tic); + return NULL; +} + +void +_ti_freetic(TIC *tic) +{ + + if (tic != NULL) { + free(tic->name); + free(tic->alias); + free(tic->desc); + free(tic->extras.buf); + free(tic->flags.buf); + free(tic->nums.buf); + free(tic->strs.buf); + free(tic); + } +} Index: contrib/mg/compiled_terms.c =================================================================== --- /dev/null +++ contrib/mg/compiled_terms.c @@ -0,0 +1,1650 @@ +/* DO NOT EDIT + * Automatically generated from terminfo */ + +struct compiled_term { + const char *name; + const char *cap; + size_t caplen; +}; + +const struct compiled_term compiled_terms[] = { + { + "ansi", + "\001\005\000\141\156\163\151\000\000\000\000\000\016\000\004" + "\000\036\000\001\001\000\001\026\000\001\027\000\001\032\000" + "\006\000\016\000\010\000\025\000\003\000\021\000\100\000\004" + "\000\120\000\007\000\010\000\012\000\030\000\142\003\107\000" + "\200\001\013\000\033\133\045\151\045\144\073\045\144\122\000" + "\201\001\005\000\033\133\066\156\000\202\001\023\000\033\133" + "\077\045\133\073\060\061\062\063\064\065\066\067\070\071\135" + "\143\000\203\001\004\000\033\133\143\000\052\001\011\000\033" + "\133\063\071\073\064\071\155\000\132\001\012\000\033\133\064" + "\045\160\061\045\144\155\000\133\001\012\000\033\133\063\045" + "\160\061\045\144\155\000\110\000\005\000\033\133\070\155\000" + "\134\001\126\000\033\133\060\073\061\060\045\077\045\160\061" + "\045\164\073\067\045\073\045\077\045\160\062\045\164\073\064" + "\045\073\045\077\045\160\063\045\164\073\067\045\073\045\077" + "\045\160\064\045\164\073\065\045\073\045\077\045\160\066\045" + "\164\073\061\045\073\045\077\045\160\067\045\164\073\070\045" + "\073\045\077\045\160\071\045\164\073\061\061\045\073\155\000" + "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133" + "\061\155\000\105\000\005\000\033\133\067\155\000\135\000\006" + "\000\033\133\061\060\155\000\140\000\004\000\033\133\155\000" + "\143\000\004\000\033\133\155\000\125\000\010\000\033\133\060" + "\073\061\060\155\000\103\000\006\000\033\133\061\061\155\000" + "\112\000\005\000\033\133\067\155\000\116\000\005\000\033\133" + "\064\155\000\000\000\077\000\053\020\054\021\055\030\056\031" + "\060\333\140\004\141\261\146\370\147\361\150\260\152\331\153" + "\277\154\332\155\300\156\305\157\176\160\304\161\304\162\304" + "\163\137\164\303\165\264\166\301\167\302\170\263\171\363\172" + "\362\173\343\174\330\175\234\176\376\000\123\000\006\000\033" + "\133\061\060\155\000\061\000\006\000\033\133\061\061\155\000" + "\063\001\011\000\033\133\045\160\061\045\144\104\000\056\001" + "\011\000\033\133\045\160\061\045\144\102\000\065\001\011\000" + "\033\133\045\160\061\045\144\103\000\070\001\011\000\033\133" + "\045\160\061\045\144\101\000\054\001\011\000\033\133\045\160" + "\061\045\144\120\000\055\001\011\000\033\133\045\160\061\045" + "\144\115\000\122\000\011\000\033\133\045\160\061\045\144\130" + "\000\022\000\005\000\033\133\061\113\000\027\000\013\000\033" + "\133\045\151\045\160\061\045\144\107\000\166\001\004\000\033" + "\133\111\000\060\001\011\000\033\133\045\160\061\045\144\100" + "\000\062\001\011\000\033\133\045\160\061\045\144\114\000\061" + "\001\011\000\033\133\045\160\061\045\144\123\000\173\000\002" + "\000\010\000\175\000\004\000\033\133\132\000\326\000\004\000" + "\033\133\104\000\212\000\004\000\033\133\102\000\351\000\004" + "\000\033\133\103\000\015\001\004\000\033\133\101\000\324\000" + "\004\000\033\133\114\000\102\001\005\000\033\133\064\151\000" + "\103\001\005\000\033\133\065\151\000\047\001\005\000\015\033" + "\133\123\000\107\001\024\000\045\160\061\045\143\033\133\045" + "\160\062\045\173\061\175\045\055\045\144\142\000\067\001\011" + "\000\033\133\045\160\061\045\144\124\000\125\001\004\000\033" + "\050\102\000\126\001\004\000\033\051\102\000\127\001\004\000" + "\033\052\102\000\130\001\004\000\033\053\102\000\017\000\005" + "\000\033\133\063\147\000\117\001\013\000\033\133\045\151\045" + "\160\061\045\144\144\000\003\000\002\000\007\000\002\000\004" + "\000\033\133\132\000\021\000\007\000\033\133\110\033\133\112" + "\000\007\000\002\000\015\000\036\000\004\000\033\133\104\000" + "\033\000\004\000\033\133\102\000\041\000\004\000\033\133\103" + "\000\032\000\021\000\033\133\045\151\045\160\061\045\144\073" + "\045\160\062\045\144\110\000\043\000\004\000\033\133\101\000" + "\047\000\004\000\033\133\120\000\050\000\004\000\033\133\115" + "\000\024\000\004\000\033\133\112\000\023\000\004\000\033\133" + "\113\000\034\000\004\000\033\133\110\000\153\001\003\000\033" + "\110\000\166\000\004\000\033\133\114\000\122\001\002\000\012" + "\000\323\000\004\000\033\133\110\000\020\000\002\000\003\000" + "\101\130\000\146\001\003\000\142\163\000\146\001", + 942 + }, + { + "dumb", + "\001\005\000\144\165\155\142\000\000\000\000\000\005\000\001" + "\000\001\000\001\006\000\001\000\004\000\120\000\032\000\004" + "\000\003\000\002\000\007\000\007\000\002\000\015\000\033\000" + "\002\000\012\000\122\001\002\000\012\000\000\000", + 57 + }, + { + "rxvt-unicode-256color", + "\001\026\000\162\170\166\164\055\165\156\151\143\157\144\145" + "\055\062\065\066\143\157\154\157\162\000\000\000\000\000\032" + "\000\010\000\003\000\001\001\000\001\002\000\001\012\000\001" + "\026\000\001\027\000\001\011\000\001\044\000\001\026\000\005" + "\000\016\000\000\001\021\000\377\177\004\000\120\000\007\000" + "\010\000\012\000\030\000\053\007\222\000\163\000\136\000\033" + "\135\064\073\045\160\061\045\144\073\162\147\142\072\045\160" + "\062\045\173\062\065\065\175\045\052\045\173\061\060\060\060" + "\175\045\057\045\062\056\062\130\057\045\160\063\045\173\062" + "\065\065\175\045\052\045\173\061\060\060\060\175\045\057\045" + "\062\056\062\130\057\045\160\064\045\173\062\065\065\175\045" + "\052\045\173\061\060\060\060\175\045\057\045\062\056\062\130" + "\033\134\000\051\001\007\000\033\135\061\060\064\007\000\132" + "\001\100\000\033\133\045\077\045\160\061\045\173\070\175\045" + "\074\045\164\064\045\160\061\045\144\045\145\045\160\061\045" + "\173\061\066\175\045\074\045\164\061\060\045\160\061\045\173" + "\070\175\045\055\045\144\045\145\064\070\073\065\073\045\160" + "\061\045\144\045\073\155\000\133\001\077\000\033\133\045\077" + "\045\160\061\045\173\070\175\045\074\045\164\063\045\160\061" + "\045\144\045\145\045\160\061\045\173\061\066\175\045\074\045" + "\164\071\045\160\061\045\173\070\175\045\055\045\144\045\145" + "\063\070\073\065\073\045\160\061\045\144\045\073\155\000\027" + "\000\013\000\033\133\045\151\045\160\061\045\144\107\000\221" + "\000\006\000\033\133\062\061\176\000\125\000\005\000\033\133" + "\155\017\000\117\001\013\000\033\133\045\151\045\160\061\045" + "\144\144\000\000\000\063\000\140\140\141\141\146\146\147\147" + "\152\152\153\153\154\154\155\155\156\156\157\157\160\160\161" + "\161\162\162\163\163\164\164\165\165\166\166\167\167\170\170" + "\171\171\172\172\173\173\174\174\175\175\176\176\000\003\000" + "\002\000\007\000\063\000\005\000\033\133\065\155\000\064\000" + "\005\000\033\133\061\155\000\035\000\007\000\033\133\077\062" + "\065\154\000\021\000\010\000\033\133\110\033\133\062\112\000" + "\040\000\007\000\033\133\077\062\065\150\000\007\000\002\000" + "\015\000\014\000\021\000\033\133\045\151\045\160\061\045\144" + "\073\045\160\062\045\144\162\000\063\001\011\000\033\133\045" + "\160\061\045\144\104\000\036\000\002\000\010\000\056\001\011" + "\000\033\133\045\160\061\045\144\102\000\033\000\002\000\012" + "\000\065\001\011\000\033\133\045\160\061\045\144\103\000\041" + "\000\004\000\033\133\103\000\032\000\021\000\033\133\045\151" + "\045\160\061\045\144\073\045\160\062\045\144\110\000\070\001" + "\011\000\033\133\045\160\061\045\144\101\000\043\000\004\000" + "\033\133\101\000\055\001\011\000\033\133\045\160\061\045\144" + "\115\000\050\000\004\000\033\133\115\000\024\000\004\000\033" + "\133\112\000\023\000\004\000\033\133\113\000\022\000\005\000" + "\033\133\061\113\000\057\000\007\000\033\050\102\033\051\060" + "\000\150\000\022\000\033\133\077\065\150\044\074\061\060\060" + "\057\076\033\133\077\065\154\000\034\000\004\000\033\133\110" + "\000\166\001\002\000\011\000\153\001\003\000\033\110\000\060" + "\001\011\000\033\133\045\160\061\045\144\100\000\165\000\004" + "\000\033\133\100\000\062\001\011\000\033\133\045\160\061\045" + "\144\114\000\166\000\004\000\033\133\114\000\122\001\002\000" + "\012\000\156\000\016\000\033\133\077\064\067\154\033\075\033" + "\133\077\061\154\000\157\000\042\000\033\133\162\033\133\155" + "\033\133\062\112\033\133\110\033\133\077\067\150\033\133\077" + "\061\073\063\073\064\073\066\154\033\133\064\154\000\173\000" + "\002\000\010\000\175\000\004\000\033\133\132\000\332\000\004" + "\000\033\133\115\000\116\001\003\000\033\070\000\105\000\005" + "\000\033\133\067\155\000\123\001\003\000\033\115\000\123\000" + "\002\000\017\000\126\000\015\000\033\133\062\112\033\133\077" + "\064\067\154\033\070\000\131\000\005\000\033\133\064\154\000" + "\016\001\003\000\033\076\000\140\000\006\000\033\133\062\067" + "\155\000\143\000\006\000\033\133\062\064\155\000\112\001\041" + "\000\033\076\033\133\061\073\063\073\064\073\065\073\066\154" + "\033\133\077\067\150\033\133\155\033\133\162\033\133\062\112" + "\033\133\110\000\113\001\062\000\033\133\162\033\133\155\033" + "\133\062\112\033\133\110\033\133\077\067\150\033\133\077\061" + "\073\063\073\064\073\066\154\033\133\064\154\033\076\033\133" + "\077\061\060\060\060\154\033\133\077\062\065\150\000\125\001" + "\004\000\033\050\102\000\126\001\004\000\033\050\060\000\120" + "\001\003\000\033\067\000\134\001\103\000\033\133\060\045\077" + "\045\160\066\045\164\073\061\045\073\045\077\045\160\062\045" + "\164\073\064\045\073\045\077\045\160\061\045\160\063\045\174" + "\045\164\073\067\045\073\045\077\045\160\064\045\164\073\065" + "\045\073\155\045\077\045\160\071\045\164\016\045\145\017\045" + "\073\000\061\000\002\000\016\000\065\000\011\000\033\067\033" + "\133\077\064\067\150\000\073\000\005\000\033\133\064\150\000" + "\017\001\003\000\033\075\000\112\000\005\000\033\133\067\155" + "\000\116\000\005\000\033\133\064\155\000\017\000\005\000\033" + "\133\063\147\000\202\001\010\000\033\133\077\061\073\062\143" + "\000\200\001\013\000\033\133\045\151\045\144\073\045\144\122" + "\000\201\001\005\000\033\133\066\156\000\203\001\004\000\033" + "\133\143\000\360\000\005\000\033\133\063\044\000\363\000\005" + "\000\033\133\070\044\000\371\000\005\000\033\133\067\044\000" + "\372\000\005\000\033\133\062\044\000\373\000\004\000\033\133" + "\144\000\376\000\005\000\033\133\066\044\000\000\001\005\000" + "\033\133\065\044\000\005\001\004\000\033\133\143\000\326\000" + "\004\000\033\133\104\000\212\000\004\000\033\133\102\000\351" + "\000\004\000\033\133\103\000\015\001\004\000\033\133\101\000" + "\210\000\005\000\033\133\063\176\000\216\000\005\000\033\133" + "\070\136\000\214\000\005\000\033\133\070\176\000\222\000\006" + "\000\033\133\061\061\176\000\233\000\006\000\033\133\062\061" + "\176\000\234\000\006\000\033\133\062\063\176\000\235\000\006" + "\000\033\133\062\064\176\000\236\000\006\000\033\133\062\065" + "\176\000\237\000\006\000\033\133\062\066\176\000\240\000\006" + "\000\033\133\062\070\176\000\241\000\006\000\033\133\062\071" + "\176\000\242\000\006\000\033\133\063\061\176\000\243\000\006" + "\000\033\133\063\062\176\000\244\000\006\000\033\133\063\063" + "\176\000\223\000\006\000\033\133\061\062\176\000\245\000\006" + "\000\033\133\063\064\176\000\246\000\006\000\033\133\062\063" + "\044\000\247\000\006\000\033\133\062\064\044\000\250\000\006" + "\000\033\133\061\061\136\000\251\000\006\000\033\133\061\062" + "\136\000\252\000\006\000\033\133\061\063\136\000\253\000\006" + "\000\033\133\061\064\136\000\254\000\006\000\033\133\061\065" + "\136\000\255\000\006\000\033\133\061\067\136\000\256\000\006" + "\000\033\133\061\070\136\000\224\000\006\000\033\133\061\063" + "\176\000\257\000\006\000\033\133\061\071\136\000\260\000\006" + "\000\033\133\062\060\136\000\261\000\006\000\033\133\062\061" + "\136\000\262\000\006\000\033\133\062\063\136\000\263\000\006" + "\000\033\133\062\064\136\000\264\000\006\000\033\133\062\065" + "\136\000\265\000\006\000\033\133\062\066\136\000\266\000\006" + "\000\033\133\062\070\136\000\267\000\006\000\033\133\062\071" + "\136\000\270\000\006\000\033\133\063\061\136\000\225\000\006" + "\000\033\133\061\064\176\000\271\000\006\000\033\133\063\062" + "\136\000\272\000\006\000\033\133\063\063\136\000\273\000\006" + "\000\033\133\063\064\136\000\274\000\006\000\033\133\062\063" + "\100\000\275\000\006\000\033\133\062\064\100\000\226\000\006" + "\000\033\133\061\065\176\000\227\000\006\000\033\133\061\067" + "\176\000\230\000\006\000\033\133\061\070\176\000\231\000\006" + "\000\033\133\061\071\176\000\232\000\006\000\033\133\062\060" + "\176\000\321\000\005\000\033\133\061\176\000\323\000\005\000" + "\033\133\067\176\000\324\000\005\000\033\133\062\176\000\366" + "\000\004\000\033\133\141\000\335\000\005\000\033\133\066\176" + "\000\340\000\005\000\033\133\065\176\000\002\001\004\000\033" + "\133\142\000\362\000\005\000\033\133\064\176\000\170\000\004" + "\000\033\117\167\000\171\000\004\000\033\117\171\000\172\000" + "\004\000\033\117\165\000\176\000\004\000\033\117\161\000\177" + "\000\004\000\033\117\163\000\215\000\004\000\033\117\115\000" + "\052\001\011\000\033\133\063\071\073\064\071\155\000\133\001" + "\031\000\003\000\142\163\000\146\001\003\000\130\124\000\146" + "\001\005\000\153\104\103\065\000\163\005\000\033\133\063\136" + "\000\005\000\153\104\103\066\000\163\005\000\033\133\063\100" + "\000\004\000\153\104\116\000\163\004\000\033\133\142\000\005" + "\000\153\104\116\065\000\163\004\000\033\117\142\000\006\000" + "\153\105\116\104\065\000\163\005\000\033\133\070\136\000\006" + "\000\153\105\116\104\066\000\163\005\000\033\133\070\100\000" + "\006\000\153\110\117\115\065\000\163\005\000\033\133\067\136" + "\000\006\000\153\110\117\115\066\000\163\005\000\033\133\067" + "\100\000\005\000\153\111\103\065\000\163\005\000\033\133\062" + "\136\000\005\000\153\111\103\066\000\163\005\000\033\133\062" + "\100\000\006\000\153\114\106\124\065\000\163\004\000\033\117" + "\144\000\006\000\153\116\130\124\065\000\163\005\000\033\133" + "\066\136\000\006\000\153\116\130\124\066\000\163\005\000\033" + "\133\066\100\000\006\000\153\120\122\126\065\000\163\005\000" + "\033\133\065\136\000\006\000\153\120\122\126\066\000\163\005" + "\000\033\133\065\100\000\006\000\153\122\111\124\065\000\163" + "\004\000\033\117\143\000\004\000\153\125\120\000\163\004\000" + "\033\133\141\000\005\000\153\125\120\065\000\163\004\000\033" + "\117\141\000\004\000\153\141\062\000\163\004\000\033\117\170" + "\000\004\000\153\142\061\000\163\004\000\033\117\164\000\004" + "\000\153\142\063\000\163\004\000\033\117\166\000\004\000\153" + "\143\062\000\163\004\000\033\117\162\000\003\000\101\130\000" + "\146\001", + 2267 + }, + { + "screen", + "\001\007\000\163\143\162\145\145\156\000\000\000\000\000\021" + "\000\005\000\001\000\001\016\000\001\026\000\001\027\000\001" + "\011\000\001\032\000\006\000\016\000\010\000\004\000\120\000" + "\007\000\010\000\012\000\030\000\025\000\376\377\021\000\100" + "\000\330\003\127\000\000\000\101\000\053\053\054\054\055\055" + "\056\056\060\060\140\140\141\141\146\146\147\147\150\150\151" + "\151\152\152\153\153\154\154\155\155\156\156\157\157\160\160" + "\161\161\162\162\163\163\164\164\165\165\166\166\167\167\170" + "\170\171\171\172\172\173\173\174\174\175\175\176\176\000\003" + "\000\002\000\007\000\063\000\005\000\033\133\065\155\000\064" + "\000\005\000\033\133\061\155\000\002\000\004\000\033\133\132" + "\000\035\000\007\000\033\133\077\062\065\154\000\021\000\007" + "\000\033\133\110\033\133\112\000\040\000\014\000\033\133\063" + "\064\150\033\133\077\062\065\150\000\007\000\002\000\015\000" + "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045" + "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061" + "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033" + "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065" + "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\004" + "\000\033\133\103\000\032\000\021\000\033\133\045\151\045\160" + "\061\045\144\073\045\160\062\045\144\110\000\070\001\011\000" + "\033\133\045\160\061\045\144\101\000\043\000\003\000\033\115" + "\000\044\000\006\000\033\133\063\064\154\000\054\001\011\000" + "\033\133\045\160\061\045\144\120\000\047\000\004\000\033\133" + "\120\000\067\000\005\000\033\133\062\155\000\055\001\011\000" + "\033\133\045\160\061\045\144\115\000\050\000\004\000\033\133" + "\115\000\024\000\004\000\033\133\112\000\023\000\004\000\033" + "\133\113\000\022\000\005\000\033\133\061\113\000\057\000\007" + "\000\033\050\102\033\051\060\000\150\000\003\000\033\147\000" + "\034\000\004\000\033\133\110\000\166\001\002\000\011\000\153" + "\001\003\000\033\110\000\060\001\011\000\033\133\045\160\061" + "\045\144\100\000\062\001\011\000\033\133\045\160\061\045\144" + "\114\000\166\000\004\000\033\133\114\000\122\001\002\000\012" + "\000\157\000\004\000\033\051\060\000\173\000\002\000\010\000" + "\175\000\004\000\033\133\132\000\326\000\004\000\033\117\104" + "\000\212\000\004\000\033\117\102\000\351\000\004\000\033\117" + "\103\000\015\001\004\000\033\117\101\000\210\000\005\000\033" + "\133\063\176\000\214\000\005\000\033\133\064\176\000\222\000" + "\004\000\033\117\120\000\233\000\006\000\033\133\062\061\176" + "\000\234\000\006\000\033\133\062\063\176\000\235\000\006\000" + "\033\133\062\064\176\000\223\000\004\000\033\117\121\000\224" + "\000\004\000\033\117\122\000\225\000\004\000\033\117\123\000" + "\226\000\006\000\033\133\061\065\176\000\227\000\006\000\033" + "\133\061\067\176\000\230\000\006\000\033\133\061\070\176\000" + "\231\000\006\000\033\133\061\071\176\000\232\000\006\000\033" + "\133\062\060\176\000\323\000\005\000\033\133\061\176\000\324" + "\000\005\000\033\133\062\176\000\332\000\004\000\033\133\115" + "\000\335\000\005\000\033\133\066\176\000\340\000\005\000\033" + "\133\065\176\000\047\001\003\000\033\105\000\116\001\003\000" + "\033\070\000\105\000\005\000\033\133\067\155\000\123\001\003" + "\000\033\115\000\123\000\002\000\017\000\126\000\011\000\033" + "\133\077\061\060\064\071\154\000\131\000\005\000\033\133\064" + "\154\000\016\001\010\000\033\133\077\061\154\033\076\000\140" + "\000\006\000\033\133\062\063\155\000\143\000\006\000\033\133" + "\062\064\155\000\113\001\021\000\033\143\033\133\077\061\060" + "\060\060\154\033\133\077\062\065\150\000\120\001\003\000\033" + "\067\000\134\001\124\000\033\133\060\045\077\045\160\066\045" + "\164\073\061\045\073\045\077\045\160\061\045\164\073\063\045" + "\073\045\077\045\160\062\045\164\073\064\045\073\045\077\045" + "\160\063\045\164\073\067\045\073\045\077\045\160\064\045\164" + "\073\065\045\073\045\077\045\160\065\045\164\073\062\045\073" + "\155\045\077\045\160\071\045\164\016\045\145\017\045\073\000" + "\125\000\005\000\033\133\155\017\000\061\000\002\000\016\000" + "\065\000\011\000\033\133\077\061\060\064\071\150\000\073\000" + "\005\000\033\133\064\150\000\017\001\010\000\033\133\077\061" + "\150\033\075\000\112\000\005\000\033\133\063\155\000\116\000" + "\005\000\033\133\064\155\000\017\000\005\000\033\133\063\147" + "\000\052\001\011\000\033\133\063\071\073\064\071\155\000\132" + "\001\012\000\033\133\064\045\160\061\045\144\155\000\133\001" + "\012\000\033\133\063\045\160\061\045\144\155\000\102\000\007" + "\000\003\000\142\163\000\146\001\003\000\160\164\000\146\001" + "\003\000\107\060\000\146\001\003\000\125\070\000\156\001\000" + "\003\000\105\060\000\163\004\000\033\050\102\000\003\000\123" + "\060\000\163\010\000\033\050\045\160\061\045\143\000\003\000" + "\101\130\000\146\001", + 1115 + }, + { + "screen-256color", + "\001\020\000\163\143\162\145\145\156\055\062\065\066\143\157" + "\154\157\162\000\000\000\000\000\021\000\005\000\001\000\001" + "\016\000\001\026\000\001\027\000\001\011\000\001\026\000\005" + "\000\016\000\000\001\021\000\377\177\004\000\120\000\007\000" + "\010\000\012\000\030\000\103\004\127\000\132\001\100\000\033" + "\133\045\077\045\160\061\045\173\070\175\045\074\045\164\064" + "\045\160\061\045\144\045\145\045\160\061\045\173\061\066\175" + "\045\074\045\164\061\060\045\160\061\045\173\070\175\045\055" + "\045\144\045\145\064\070\073\065\073\045\160\061\045\144\045" + "\073\155\000\133\001\077\000\033\133\045\077\045\160\061\045" + "\173\070\175\045\074\045\164\063\045\160\061\045\144\045\145" + "\045\160\061\045\173\061\066\175\045\074\045\164\071\045\160" + "\061\045\173\070\175\045\055\045\144\045\145\063\070\073\065" + "\073\045\160\061\045\144\045\073\155\000\000\000\101\000\053" + "\053\054\054\055\055\056\056\060\060\140\140\141\141\146\146" + "\147\147\150\150\151\151\152\152\153\153\154\154\155\155\156" + "\156\157\157\160\160\161\161\162\162\163\163\164\164\165\165" + "\166\166\167\167\170\170\171\171\172\172\173\173\174\174\175" + "\175\176\176\000\003\000\002\000\007\000\063\000\005\000\033" + "\133\065\155\000\064\000\005\000\033\133\061\155\000\002\000" + "\004\000\033\133\132\000\035\000\007\000\033\133\077\062\065" + "\154\000\021\000\007\000\033\133\110\033\133\112\000\040\000" + "\014\000\033\133\063\064\150\033\133\077\062\065\150\000\007" + "\000\002\000\015\000\014\000\021\000\033\133\045\151\045\160" + "\061\045\144\073\045\160\062\045\144\162\000\063\001\011\000" + "\033\133\045\160\061\045\144\104\000\036\000\002\000\010\000" + "\056\001\011\000\033\133\045\160\061\045\144\102\000\033\000" + "\002\000\012\000\065\001\011\000\033\133\045\160\061\045\144" + "\103\000\041\000\004\000\033\133\103\000\032\000\021\000\033" + "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\110" + "\000\070\001\011\000\033\133\045\160\061\045\144\101\000\043" + "\000\003\000\033\115\000\044\000\006\000\033\133\063\064\154" + "\000\054\001\011\000\033\133\045\160\061\045\144\120\000\047" + "\000\004\000\033\133\120\000\067\000\005\000\033\133\062\155" + "\000\055\001\011\000\033\133\045\160\061\045\144\115\000\050" + "\000\004\000\033\133\115\000\024\000\004\000\033\133\112\000" + "\023\000\004\000\033\133\113\000\022\000\005\000\033\133\061" + "\113\000\057\000\007\000\033\050\102\033\051\060\000\150\000" + "\003\000\033\147\000\034\000\004\000\033\133\110\000\166\001" + "\002\000\011\000\153\001\003\000\033\110\000\060\001\011\000" + "\033\133\045\160\061\045\144\100\000\062\001\011\000\033\133" + "\045\160\061\045\144\114\000\166\000\004\000\033\133\114\000" + "\122\001\002\000\012\000\157\000\004\000\033\051\060\000\173" + "\000\002\000\010\000\175\000\004\000\033\133\132\000\326\000" + "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351" + "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000" + "\210\000\005\000\033\133\063\176\000\214\000\005\000\033\133" + "\064\176\000\222\000\004\000\033\117\120\000\233\000\006\000" + "\033\133\062\061\176\000\234\000\006\000\033\133\062\063\176" + "\000\235\000\006\000\033\133\062\064\176\000\223\000\004\000" + "\033\117\121\000\224\000\004\000\033\117\122\000\225\000\004" + "\000\033\117\123\000\226\000\006\000\033\133\061\065\176\000" + "\227\000\006\000\033\133\061\067\176\000\230\000\006\000\033" + "\133\061\070\176\000\231\000\006\000\033\133\061\071\176\000" + "\232\000\006\000\033\133\062\060\176\000\323\000\005\000\033" + "\133\061\176\000\324\000\005\000\033\133\062\176\000\332\000" + "\004\000\033\133\115\000\335\000\005\000\033\133\066\176\000" + "\340\000\005\000\033\133\065\176\000\047\001\003\000\033\105" + "\000\116\001\003\000\033\070\000\105\000\005\000\033\133\067" + "\155\000\123\001\003\000\033\115\000\123\000\002\000\017\000" + "\126\000\011\000\033\133\077\061\060\064\071\154\000\131\000" + "\005\000\033\133\064\154\000\016\001\010\000\033\133\077\061" + "\154\033\076\000\140\000\006\000\033\133\062\063\155\000\143" + "\000\006\000\033\133\062\064\155\000\113\001\021\000\033\143" + "\033\133\077\061\060\060\060\154\033\133\077\062\065\150\000" + "\120\001\003\000\033\067\000\134\001\124\000\033\133\060\045" + "\077\045\160\066\045\164\073\061\045\073\045\077\045\160\061" + "\045\164\073\063\045\073\045\077\045\160\062\045\164\073\064" + "\045\073\045\077\045\160\063\045\164\073\067\045\073\045\077" + "\045\160\064\045\164\073\065\045\073\045\077\045\160\065\045" + "\164\073\062\045\073\155\045\077\045\160\071\045\164\016\045" + "\145\017\045\073\000\125\000\005\000\033\133\155\017\000\061" + "\000\002\000\016\000\065\000\011\000\033\133\077\061\060\064" + "\071\150\000\073\000\005\000\033\133\064\150\000\017\001\010" + "\000\033\133\077\061\150\033\075\000\112\000\005\000\033\133" + "\063\155\000\116\000\005\000\033\133\064\155\000\017\000\005" + "\000\033\133\063\147\000\052\001\011\000\033\133\063\071\073" + "\064\071\155\000\102\000\007\000\003\000\142\163\000\146\001" + "\003\000\160\164\000\146\001\003\000\107\060\000\146\001\003" + "\000\125\070\000\156\001\000\003\000\105\060\000\163\004\000" + "\033\050\102\000\003\000\123\060\000\163\010\000\033\050\045" + "\160\061\045\143\000\003\000\101\130\000\146\001", + 1227 + }, + { + "st", + "\001\003\000\163\164\000\000\000\000\000\027\000\007\000\001" + "\000\001\002\000\001\020\000\001\026\000\001\027\000\001\032" + "\000\001\011\000\001\026\000\005\000\016\000\010\000\004\000" + "\120\000\007\000\010\000\012\000\030\000\021\000\100\000\134" + "\010\261\000\000\000\101\000\053\103\054\104\055\101\056\102" + "\060\105\140\140\141\141\146\146\147\147\150\106\151\107\152" + "\152\153\153\154\154\155\155\156\156\157\157\160\160\161\161" + "\162\162\163\163\164\164\165\165\166\166\167\167\170\170\171" + "\171\172\172\173\173\174\174\175\175\176\176\000\003\000\002" + "\000\007\000\063\000\005\000\033\133\065\155\000\064\000\005" + "\000\033\133\061\155\000\002\000\004\000\033\133\132\000\035" + "\000\007\000\033\133\077\062\065\154\000\021\000\010\000\033" + "\133\110\033\133\062\112\000\040\000\015\000\033\133\077\061" + "\062\154\033\133\077\062\065\150\000\007\000\002\000\015\000" + "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045" + "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061" + "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033" + "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065" + "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\004" + "\000\033\133\103\000\032\000\021\000\033\133\045\151\045\160" + "\061\045\144\073\045\160\062\045\144\110\000\070\001\011\000" + "\033\133\045\160\061\045\144\101\000\043\000\004\000\033\133" + "\101\000\044\000\007\000\033\133\077\062\065\150\000\054\001" + "\011\000\033\133\045\160\061\045\144\120\000\047\000\004\000" + "\033\133\120\000\055\001\011\000\033\133\045\160\061\045\144" + "\115\000\050\000\004\000\033\133\115\000\122\000\011\000\033" + "\133\045\160\061\045\144\130\000\024\000\004\000\033\133\112" + "\000\023\000\004\000\033\133\113\000\022\000\005\000\033\133" + "\061\113\000\057\000\004\000\033\051\060\000\150\000\022\000" + "\033\133\077\065\150\044\074\061\060\060\057\076\033\133\077" + "\065\154\000\152\000\002\000\007\000\034\000\004\000\033\133" + "\110\000\027\000\013\000\033\133\045\151\045\160\061\045\144" + "\107\000\166\001\002\000\011\000\153\001\003\000\033\110\000" + "\060\001\011\000\033\133\045\160\061\045\144\100\000\062\001" + "\011\000\033\133\045\160\061\045\144\114\000\166\000\004\000" + "\033\133\114\000\122\001\002\000\012\000\061\001\011\000\033" + "\133\045\160\061\045\144\123\000\110\000\005\000\033\133\070" + "\155\000\157\000\017\000\033\133\064\154\033\076\033\133\077" + "\061\060\063\064\154\000\360\000\007\000\033\133\063\073\062" + "\176\000\363\000\007\000\033\133\061\073\062\106\000\371\000" + "\007\000\033\133\061\073\062\110\000\372\000\007\000\033\133" + "\062\073\062\176\000\373\000\007\000\033\133\061\073\062\104" + "\000\376\000\007\000\033\133\066\073\062\176\000\000\001\007" + "\000\033\133\065\073\062\176\000\005\001\007\000\033\133\061" + "\073\062\103\000\170\000\005\000\033\133\061\176\000\171\000" + "\005\000\033\133\065\176\000\172\000\004\000\033\117\165\000" + "\173\000\002\000\177\000\176\000\005\000\033\133\064\176\000" + "\177\000\005\000\033\133\066\176\000\175\000\004\000\033\133" + "\132\000\202\000\007\000\033\133\063\073\065\176\000\326\000" + "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351" + "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000" + "\210\000\005\000\033\133\063\176\000\211\000\007\000\033\133" + "\063\073\062\176\000\217\000\007\000\033\133\061\073\065\106" + "\000\216\000\007\000\033\133\061\073\062\106\000\214\000\005" + "\000\033\133\064\176\000\215\000\004\000\033\117\115\000\222" + "\000\004\000\033\117\120\000\233\000\006\000\033\133\062\061" + "\176\000\234\000\006\000\033\133\062\063\176\000\235\000\006" + "\000\033\133\062\064\176\000\236\000\007\000\033\133\061\073" + "\062\120\000\237\000\007\000\033\133\061\073\062\121\000\240" + "\000\007\000\033\133\061\073\062\122\000\241\000\007\000\033" + "\133\061\073\062\123\000\242\000\010\000\033\133\061\065\073" + "\062\176\000\243\000\010\000\033\133\061\067\073\062\176\000" + "\244\000\010\000\033\133\061\070\073\062\176\000\223\000\004" + "\000\033\117\121\000\245\000\010\000\033\133\061\071\073\062" + "\176\000\246\000\010\000\033\133\062\060\073\062\176\000\247" + "\000\010\000\033\133\062\061\073\062\176\000\250\000\010\000" + "\033\133\062\063\073\062\176\000\251\000\010\000\033\133\062" + "\064\073\062\176\000\252\000\007\000\033\133\061\073\065\120" + "\000\253\000\007\000\033\133\061\073\065\121\000\254\000\007" + "\000\033\133\061\073\065\122\000\255\000\007\000\033\133\061" + "\073\065\123\000\256\000\010\000\033\133\061\065\073\065\176" + "\000\224\000\004\000\033\117\122\000\257\000\010\000\033\133" + "\061\067\073\065\176\000\260\000\010\000\033\133\061\070\073" + "\065\176\000\261\000\010\000\033\133\061\071\073\065\176\000" + "\262\000\010\000\033\133\062\060\073\065\176\000\263\000\010" + "\000\033\133\062\061\073\065\176\000\264\000\010\000\033\133" + "\062\063\073\065\176\000\265\000\010\000\033\133\062\064\073" + "\065\176\000\266\000\007\000\033\133\061\073\066\120\000\267" + "\000\007\000\033\133\061\073\066\121\000\270\000\007\000\033" + "\133\061\073\066\122\000\225\000\004\000\033\117\123\000\271" + "\000\007\000\033\133\061\073\066\123\000\272\000\010\000\033" + "\133\061\065\073\066\176\000\273\000\010\000\033\133\061\067" + "\073\066\176\000\274\000\010\000\033\133\061\070\073\066\176" + "\000\275\000\010\000\033\133\061\071\073\066\176\000\276\000" + "\010\000\033\133\062\060\073\066\176\000\277\000\010\000\033" + "\133\062\061\073\066\176\000\300\000\010\000\033\133\062\063" + "\073\066\176\000\301\000\010\000\033\133\062\064\073\066\176" + "\000\302\000\007\000\033\133\061\073\063\120\000\226\000\006" + "\000\033\133\061\065\176\000\303\000\007\000\033\133\061\073" + "\063\121\000\304\000\007\000\033\133\061\073\063\122\000\305" + "\000\007\000\033\133\061\073\063\123\000\306\000\010\000\033" + "\133\061\065\073\063\176\000\307\000\010\000\033\133\061\067" + "\073\063\176\000\310\000\010\000\033\133\061\070\073\063\176" + "\000\311\000\010\000\033\133\061\071\073\063\176\000\312\000" + "\010\000\033\133\062\060\073\063\176\000\313\000\010\000\033" + "\133\062\061\073\063\176\000\314\000\010\000\033\133\062\063" + "\073\063\176\000\227\000\006\000\033\133\061\067\176\000\315" + "\000\010\000\033\133\062\064\073\063\176\000\316\000\007\000" + "\033\133\061\073\064\120\000\317\000\007\000\033\133\061\073" + "\064\121\000\320\000\007\000\033\133\061\073\064\122\000\230" + "\000\006\000\033\133\061\070\176\000\231\000\006\000\033\133" + "\061\071\176\000\232\000\006\000\033\133\062\060\176\000\323" + "\000\005\000\033\133\061\176\000\324\000\005\000\033\133\062" + "\176\000\325\000\007\000\033\133\062\073\065\176\000\366\000" + "\007\000\033\133\061\073\062\102\000\332\000\004\000\033\133" + "\115\000\335\000\005\000\033\133\066\176\000\340\000\005\000" + "\033\133\065\176\000\002\001\007\000\033\133\061\073\062\101" + "\000\213\000\007\000\033\133\062\073\062\176\000\100\001\004" + "\000\033\133\151\000\102\001\005\000\033\133\064\151\000\103" + "\001\005\000\033\133\065\151\000\052\001\011\000\033\133\063" + "\071\073\064\071\155\000\116\001\003\000\033\070\000\105\000" + "\005\000\033\133\067\155\000\123\001\003\000\033\115\000\132" + "\000\006\000\033\133\062\063\155\000\123\000\004\000\033\050" + "\102\000\126\000\011\000\033\133\077\061\060\064\071\154\000" + "\131\000\005\000\033\133\064\154\000\016\001\010\000\033\133" + "\077\061\154\033\076\000\140\000\006\000\033\133\062\067\155" + "\000\143\000\006\000\033\133\062\064\155\000\112\001\003\000" + "\033\143\000\113\001\017\000\033\133\064\154\033\076\033\133" + "\077\061\060\063\064\154\000\120\001\003\000\033\067\000\132" + "\001\012\000\033\133\064\045\160\061\045\144\155\000\133\001" + "\012\000\033\133\063\045\160\061\045\144\155\000\135\001\106" + "\000\033\133\064\045\077\045\160\061\045\173\061\175\045\075" + "\045\164\064\045\145\045\160\061\045\173\063\175\045\075\045" + "\164\066\045\145\045\160\061\045\173\064\175\045\075\045\164" + "\061\045\145\045\160\061\045\173\066\175\045\075\045\164\063" + "\045\145\045\160\061\045\144\045\073\155\000\143\001\106\000" + "\033\133\063\045\077\045\160\061\045\173\061\175\045\075\045" + "\164\064\045\145\045\160\061\045\173\063\175\045\075\045\164" + "\066\045\145\045\160\061\045\173\064\175\045\075\045\164\061" + "\045\145\045\160\061\045\173\066\175\045\075\045\164\063\045" + "\145\045\160\061\045\144\045\073\155\000\134\001\122\000\045" + "\077\045\160\071\045\164\033\050\060\045\145\033\050\102\045" + "\073\033\133\060\045\077\045\160\066\045\164\073\061\045\073" + "\045\077\045\160\062\045\164\073\064\045\073\045\077\045\160" + "\061\045\160\063\045\174\045\164\073\067\045\073\045\077\045" + "\160\064\045\164\073\065\045\073\045\077\045\160\067\045\164" + "\073\070\045\073\155\000\125\000\005\000\033\133\060\155\000" + "\074\000\005\000\033\133\063\155\000\061\000\004\000\033\050" + "\060\000\065\000\011\000\033\133\077\061\060\064\071\150\000" + "\073\000\005\000\033\133\064\150\000\017\001\010\000\033\133" + "\077\061\150\033\075\000\112\000\005\000\033\133\067\155\000" + "\116\000\005\000\033\133\064\155\000\017\000\005\000\033\133" + "\063\147\000\170\001\005\000\033\135\060\073\000\200\001\013" + "\000\033\133\045\151\045\144\073\045\144\122\000\201\001\005" + "\000\033\133\066\156\000\202\001\010\000\033\133\077\061\073" + "\062\143\000\203\001\004\000\033\133\143\000\117\001\013\000" + "\033\133\045\151\045\160\061\045\144\144\000\060\000\004\000" + "\003\000\124\143\000\146\001\003\000\130\124\000\146\001\003" + "\000\123\145\000\163\006\000\033\133\062\040\161\000\003\000" + "\123\163\000\163\012\000\033\133\045\160\061\045\144\040\161" + "\000", + 2251 + }, + { + "tmux", + "\001\005\000\164\155\165\170\000\000\000\000\000\024\000\006" + "\000\020\000\001\001\000\001\016\000\001\026\000\001\027\000" + "\001\011\000\001\026\000\005\000\016\000\010\000\004\000\120" + "\000\007\000\010\000\012\000\030\000\021\000\100\000\303\006" + "\231\000\132\000\006\000\033\133\062\063\155\000\140\000\006" + "\000\033\133\062\067\155\000\074\000\005\000\033\133\063\155" + "\000\112\000\005\000\033\133\067\155\000\210\000\005\000\033" + "\133\063\176\000\324\000\005\000\033\133\062\176\000\335\000" + "\005\000\033\133\066\176\000\340\000\005\000\033\133\065\176" + "\000\214\000\005\000\033\133\064\176\000\323\000\005\000\033" + "\133\061\176\000\326\000\004\000\033\117\104\000\212\000\004" + "\000\033\117\102\000\351\000\004\000\033\117\103\000\015\001" + "\004\000\033\117\101\000\222\000\004\000\033\117\120\000\233" + "\000\006\000\033\133\062\061\176\000\234\000\006\000\033\133" + "\062\063\176\000\235\000\006\000\033\133\062\064\176\000\236" + "\000\007\000\033\133\061\073\062\120\000\237\000\007\000\033" + "\133\061\073\062\121\000\240\000\007\000\033\133\061\073\062" + "\122\000\241\000\007\000\033\133\061\073\062\123\000\242\000" + "\010\000\033\133\061\065\073\062\176\000\243\000\010\000\033" + "\133\061\067\073\062\176\000\244\000\010\000\033\133\061\070" + "\073\062\176\000\223\000\004\000\033\117\121\000\245\000\010" + "\000\033\133\061\071\073\062\176\000\246\000\010\000\033\133" + "\062\060\073\062\176\000\247\000\010\000\033\133\062\061\073" + "\062\176\000\250\000\010\000\033\133\062\063\073\062\176\000" + "\251\000\010\000\033\133\062\064\073\062\176\000\252\000\007" + "\000\033\133\061\073\065\120\000\253\000\007\000\033\133\061" + "\073\065\121\000\254\000\007\000\033\133\061\073\065\122\000" + "\255\000\007\000\033\133\061\073\065\123\000\256\000\010\000" + "\033\133\061\065\073\065\176\000\224\000\004\000\033\117\122" + "\000\257\000\010\000\033\133\061\067\073\065\176\000\260\000" + "\010\000\033\133\061\070\073\065\176\000\261\000\010\000\033" + "\133\061\071\073\065\176\000\262\000\010\000\033\133\062\060" + "\073\065\176\000\263\000\010\000\033\133\062\061\073\065\176" + "\000\264\000\010\000\033\133\062\063\073\065\176\000\265\000" + "\010\000\033\133\062\064\073\065\176\000\266\000\007\000\033" + "\133\061\073\066\120\000\267\000\007\000\033\133\061\073\066" + "\121\000\270\000\007\000\033\133\061\073\066\122\000\225\000" + "\004\000\033\117\123\000\271\000\007\000\033\133\061\073\066" + "\123\000\272\000\010\000\033\133\061\065\073\066\176\000\273" + "\000\010\000\033\133\061\067\073\066\176\000\274\000\010\000" + "\033\133\061\070\073\066\176\000\275\000\010\000\033\133\061" + "\071\073\066\176\000\276\000\010\000\033\133\062\060\073\066" + "\176\000\277\000\010\000\033\133\062\061\073\066\176\000\300" + "\000\010\000\033\133\062\063\073\066\176\000\301\000\010\000" + "\033\133\062\064\073\066\176\000\302\000\007\000\033\133\061" + "\073\063\120\000\226\000\006\000\033\133\061\065\176\000\303" + "\000\007\000\033\133\061\073\063\121\000\304\000\007\000\033" + "\133\061\073\063\122\000\305\000\007\000\033\133\061\073\063" + "\123\000\306\000\010\000\033\133\061\065\073\063\176\000\307" + "\000\010\000\033\133\061\067\073\063\176\000\310\000\010\000" + "\033\133\061\070\073\063\176\000\311\000\010\000\033\133\061" + "\071\073\063\176\000\312\000\010\000\033\133\062\060\073\063" + "\176\000\313\000\010\000\033\133\062\061\073\063\176\000\314" + "\000\010\000\033\133\062\063\073\063\176\000\227\000\006\000" + "\033\133\061\067\176\000\315\000\010\000\033\133\062\064\073" + "\063\176\000\316\000\007\000\033\133\061\073\064\120\000\317" + "\000\007\000\033\133\061\073\064\121\000\320\000\007\000\033" + "\133\061\073\064\122\000\230\000\006\000\033\133\061\070\176" + "\000\231\000\006\000\033\133\061\071\176\000\232\000\006\000" + "\033\133\062\060\176\000\373\000\007\000\033\133\061\073\062" + "\104\000\005\001\007\000\033\133\061\073\062\103\000\366\000" + "\007\000\033\133\061\073\062\102\000\002\001\007\000\033\133" + "\061\073\062\101\000\360\000\007\000\033\133\063\073\062\176" + "\000\363\000\007\000\033\133\061\073\062\106\000\371\000\007" + "\000\033\133\061\073\062\110\000\372\000\007\000\033\133\062" + "\073\062\176\000\376\000\007\000\033\133\066\073\062\176\000" + "\000\001\007\000\033\133\065\073\062\176\000\053\000\006\000" + "\033\135\060\073\007\000\152\000\002\000\007\000\170\001\005" + "\000\033\135\060\073\000\000\000\101\000\053\053\054\054\055" + "\055\056\056\060\060\140\140\141\141\146\146\147\147\150\150" + "\151\151\152\152\153\153\154\154\155\155\156\156\157\157\160" + "\160\161\161\162\162\163\163\164\164\165\165\166\166\167\167" + "\170\170\171\171\172\172\173\173\174\174\175\175\176\176\000" + "\003\000\002\000\007\000\063\000\005\000\033\133\065\155\000" + "\064\000\005\000\033\133\061\155\000\002\000\004\000\033\133" + "\132\000\035\000\007\000\033\133\077\062\065\154\000\021\000" + "\007\000\033\133\110\033\133\112\000\040\000\014\000\033\133" + "\063\064\150\033\133\077\062\065\150\000\007\000\002\000\015" + "\000\014\000\021\000\033\133\045\151\045\160\061\045\144\073" + "\045\160\062\045\144\162\000\063\001\011\000\033\133\045\160" + "\061\045\144\104\000\036\000\002\000\010\000\056\001\011\000" + "\033\133\045\160\061\045\144\102\000\033\000\002\000\012\000" + "\065\001\011\000\033\133\045\160\061\045\144\103\000\041\000" + "\004\000\033\133\103\000\032\000\021\000\033\133\045\151\045" + "\160\061\045\144\073\045\160\062\045\144\110\000\070\001\011" + "\000\033\133\045\160\061\045\144\101\000\043\000\003\000\033" + "\115\000\044\000\006\000\033\133\063\064\154\000\054\001\011" + "\000\033\133\045\160\061\045\144\120\000\047\000\004\000\033" + "\133\120\000\067\000\005\000\033\133\062\155\000\055\001\011" + "\000\033\133\045\160\061\045\144\115\000\050\000\004\000\033" + "\133\115\000\024\000\004\000\033\133\112\000\023\000\004\000" + "\033\133\113\000\022\000\005\000\033\133\061\113\000\057\000" + "\007\000\033\050\102\033\051\060\000\150\000\003\000\033\147" + "\000\034\000\004\000\033\133\110\000\166\001\002\000\011\000" + "\153\001\003\000\033\110\000\060\001\011\000\033\133\045\160" + "\061\045\144\100\000\062\001\011\000\033\133\045\160\061\045" + "\144\114\000\166\000\004\000\033\133\114\000\122\001\002\000" + "\012\000\157\000\004\000\033\051\060\000\173\000\002\000\010" + "\000\175\000\004\000\033\133\132\000\332\000\004\000\033\133" + "\115\000\047\001\003\000\033\105\000\116\001\003\000\033\070" + "\000\105\000\005\000\033\133\067\155\000\123\001\003\000\033" + "\115\000\123\000\002\000\017\000\126\000\011\000\033\133\077" + "\061\060\064\071\154\000\131\000\005\000\033\133\064\154\000" + "\016\001\010\000\033\133\077\061\154\033\076\000\143\000\006" + "\000\033\133\062\064\155\000\113\001\021\000\033\143\033\133" + "\077\061\060\060\060\154\033\133\077\062\065\150\000\120\001" + "\003\000\033\067\000\134\001\124\000\033\133\060\045\077\045" + "\160\066\045\164\073\061\045\073\045\077\045\160\061\045\164" + "\073\063\045\073\045\077\045\160\062\045\164\073\064\045\073" + "\045\077\045\160\063\045\164\073\067\045\073\045\077\045\160" + "\064\045\164\073\065\045\073\045\077\045\160\065\045\164\073" + "\062\045\073\155\045\077\045\160\071\045\164\016\045\145\017" + "\045\073\000\125\000\005\000\033\133\155\017\000\061\000\002" + "\000\016\000\065\000\011\000\033\133\077\061\060\064\071\150" + "\000\073\000\005\000\033\133\064\150\000\017\001\010\000\033" + "\133\077\061\150\033\075\000\116\000\005\000\033\133\064\155" + "\000\017\000\005\000\033\133\063\147\000\052\001\011\000\033" + "\133\063\071\073\064\071\155\000\132\001\012\000\033\133\064" + "\045\160\061\045\144\155\000\133\001\012\000\033\133\063\045" + "\160\061\045\144\155\000\074\004\101\000\004\000\153\104\116" + "\000\163\007\000\033\133\061\073\062\102\000\005\000\153\104" + "\116\063\000\163\007\000\033\133\061\073\063\102\000\005\000" + "\153\104\116\064\000\163\007\000\033\133\061\073\064\102\000" + "\005\000\153\104\116\065\000\163\007\000\033\133\061\073\065" + "\102\000\005\000\153\104\116\066\000\163\007\000\033\133\061" + "\073\066\102\000\005\000\153\104\116\067\000\163\007\000\033" + "\133\061\073\067\102\000\006\000\153\114\106\124\063\000\163" + "\007\000\033\133\061\073\063\104\000\006\000\153\114\106\124" + "\064\000\163\007\000\033\133\061\073\064\104\000\006\000\153" + "\114\106\124\065\000\163\007\000\033\133\061\073\065\104\000" + "\006\000\153\114\106\124\066\000\163\007\000\033\133\061\073" + "\066\104\000\006\000\153\114\106\124\067\000\163\007\000\033" + "\133\061\073\067\104\000\006\000\153\122\111\124\063\000\163" + "\007\000\033\133\061\073\063\103\000\006\000\153\122\111\124" + "\064\000\163\007\000\033\133\061\073\064\103\000\006\000\153" + "\122\111\124\065\000\163\007\000\033\133\061\073\065\103\000" + "\006\000\153\122\111\124\066\000\163\007\000\033\133\061\073" + "\066\103\000\006\000\153\122\111\124\067\000\163\007\000\033" + "\133\061\073\067\103\000\004\000\153\125\120\000\163\007\000" + "\033\133\061\073\062\101\000\005\000\153\125\120\063\000\163" + "\007\000\033\133\061\073\063\101\000\005\000\153\125\120\064" + "\000\163\007\000\033\133\061\073\064\101\000\005\000\153\125" + "\120\065\000\163\007\000\033\133\061\073\065\101\000\005\000" + "\153\125\120\066\000\163\007\000\033\133\061\073\066\101\000" + "\005\000\153\125\120\067\000\163\007\000\033\133\061\073\067" + "\101\000\005\000\153\104\103\063\000\163\007\000\033\133\063" + "\073\063\176\000\005\000\153\104\103\064\000\163\007\000\033" + "\133\063\073\064\176\000\005\000\153\104\103\065\000\163\007" + "\000\033\133\063\073\065\176\000\005\000\153\104\103\066\000" + "\163\007\000\033\133\063\073\066\176\000\005\000\153\104\103" + "\067\000\163\007\000\033\133\063\073\067\176\000\006\000\153" + "\105\116\104\063\000\163\007\000\033\133\061\073\063\106\000" + "\006\000\153\105\116\104\064\000\163\007\000\033\133\061\073" + "\064\106\000\006\000\153\105\116\104\065\000\163\007\000\033" + "\133\061\073\065\106\000\006\000\153\105\116\104\066\000\163" + "\007\000\033\133\061\073\066\106\000\006\000\153\105\116\104" + "\067\000\163\007\000\033\133\061\073\067\106\000\006\000\153" + "\110\117\115\063\000\163\007\000\033\133\061\073\063\110\000" + "\006\000\153\110\117\115\064\000\163\007\000\033\133\061\073" + "\064\110\000\006\000\153\110\117\115\065\000\163\007\000\033" + "\133\061\073\065\110\000\006\000\153\110\117\115\066\000\163" + "\007\000\033\133\061\073\066\110\000\006\000\153\110\117\115" + "\067\000\163\007\000\033\133\061\073\067\110\000\005\000\153" + "\111\103\063\000\163\007\000\033\133\062\073\063\176\000\005" + "\000\153\111\103\064\000\163\007\000\033\133\062\073\064\176" + "\000\005\000\153\111\103\065\000\163\007\000\033\133\062\073" + "\065\176\000\005\000\153\111\103\066\000\163\007\000\033\133" + "\062\073\066\176\000\005\000\153\111\103\067\000\163\007\000" + "\033\133\062\073\067\176\000\006\000\153\116\130\124\063\000" + "\163\007\000\033\133\066\073\063\176\000\006\000\153\116\130" + "\124\064\000\163\007\000\033\133\066\073\064\176\000\006\000" + "\153\116\130\124\065\000\163\007\000\033\133\066\073\065\176" + "\000\006\000\153\116\130\124\066\000\163\007\000\033\133\066" + "\073\066\176\000\006\000\153\116\130\124\067\000\163\007\000" + "\033\133\066\073\067\176\000\006\000\153\120\122\126\063\000" + "\163\007\000\033\133\065\073\063\176\000\006\000\153\120\122" + "\126\064\000\163\007\000\033\133\065\073\064\176\000\006\000" + "\153\120\122\126\065\000\163\007\000\033\133\065\073\065\176" + "\000\006\000\153\120\122\126\066\000\163\007\000\033\133\065" + "\073\066\176\000\006\000\153\120\122\126\067\000\163\007\000" + "\033\133\065\073\067\176\000\003\000\124\123\000\163\005\000" + "\033\135\060\073\000\003\000\103\162\000\163\007\000\033\135" + "\061\061\062\007\000\003\000\103\163\000\163\014\000\033\135" + "\061\062\073\045\160\061\045\163\007\000\003\000\115\163\000" + "\163\022\000\033\135\065\062\073\045\160\061\045\163\073\045" + "\160\062\045\163\007\000\003\000\123\145\000\163\006\000\033" + "\133\062\040\161\000\003\000\123\163\000\163\012\000\033\133" + "\045\160\061\045\144\040\161\000\003\000\142\163\000\146\001" + "\003\000\160\164\000\146\001\003\000\107\060\000\146\001\003" + "\000\125\070\000\156\001\000\003\000\105\060\000\163\004\000" + "\033\050\102\000\003\000\123\060\000\163\010\000\033\050\045" + "\160\061\045\143\000\003\000\101\130\000\146\001", + 2877 + }, + { + "tmux-256color", + "\001\016\000\164\155\165\170\055\062\065\066\143\157\154\157" + "\162\000\000\000\000\000\024\000\006\000\020\000\001\001\000" + "\001\016\000\001\026\000\001\027\000\001\011\000\001\026\000" + "\005\000\016\000\000\001\021\000\377\177\004\000\120\000\007" + "\000\010\000\012\000\030\000\056\007\231\000\132\001\100\000" + "\033\133\045\077\045\160\061\045\173\070\175\045\074\045\164" + "\064\045\160\061\045\144\045\145\045\160\061\045\173\061\066" + "\175\045\074\045\164\061\060\045\160\061\045\173\070\175\045" + "\055\045\144\045\145\064\070\073\065\073\045\160\061\045\144" + "\045\073\155\000\133\001\077\000\033\133\045\077\045\160\061" + "\045\173\070\175\045\074\045\164\063\045\160\061\045\144\045" + "\145\045\160\061\045\173\061\066\175\045\074\045\164\071\045" + "\160\061\045\173\070\175\045\055\045\144\045\145\063\070\073" + "\065\073\045\160\061\045\144\045\073\155\000\132\000\006\000" + "\033\133\062\063\155\000\140\000\006\000\033\133\062\067\155" + "\000\074\000\005\000\033\133\063\155\000\112\000\005\000\033" + "\133\067\155\000\210\000\005\000\033\133\063\176\000\324\000" + "\005\000\033\133\062\176\000\335\000\005\000\033\133\066\176" + "\000\340\000\005\000\033\133\065\176\000\214\000\005\000\033" + "\133\064\176\000\323\000\005\000\033\133\061\176\000\326\000" + "\004\000\033\117\104\000\212\000\004\000\033\117\102\000\351" + "\000\004\000\033\117\103\000\015\001\004\000\033\117\101\000" + "\222\000\004\000\033\117\120\000\233\000\006\000\033\133\062" + "\061\176\000\234\000\006\000\033\133\062\063\176\000\235\000" + "\006\000\033\133\062\064\176\000\236\000\007\000\033\133\061" + "\073\062\120\000\237\000\007\000\033\133\061\073\062\121\000" + "\240\000\007\000\033\133\061\073\062\122\000\241\000\007\000" + "\033\133\061\073\062\123\000\242\000\010\000\033\133\061\065" + "\073\062\176\000\243\000\010\000\033\133\061\067\073\062\176" + "\000\244\000\010\000\033\133\061\070\073\062\176\000\223\000" + "\004\000\033\117\121\000\245\000\010\000\033\133\061\071\073" + "\062\176\000\246\000\010\000\033\133\062\060\073\062\176\000" + "\247\000\010\000\033\133\062\061\073\062\176\000\250\000\010" + "\000\033\133\062\063\073\062\176\000\251\000\010\000\033\133" + "\062\064\073\062\176\000\252\000\007\000\033\133\061\073\065" + "\120\000\253\000\007\000\033\133\061\073\065\121\000\254\000" + "\007\000\033\133\061\073\065\122\000\255\000\007\000\033\133" + "\061\073\065\123\000\256\000\010\000\033\133\061\065\073\065" + "\176\000\224\000\004\000\033\117\122\000\257\000\010\000\033" + "\133\061\067\073\065\176\000\260\000\010\000\033\133\061\070" + "\073\065\176\000\261\000\010\000\033\133\061\071\073\065\176" + "\000\262\000\010\000\033\133\062\060\073\065\176\000\263\000" + "\010\000\033\133\062\061\073\065\176\000\264\000\010\000\033" + "\133\062\063\073\065\176\000\265\000\010\000\033\133\062\064" + "\073\065\176\000\266\000\007\000\033\133\061\073\066\120\000" + "\267\000\007\000\033\133\061\073\066\121\000\270\000\007\000" + "\033\133\061\073\066\122\000\225\000\004\000\033\117\123\000" + "\271\000\007\000\033\133\061\073\066\123\000\272\000\010\000" + "\033\133\061\065\073\066\176\000\273\000\010\000\033\133\061" + "\067\073\066\176\000\274\000\010\000\033\133\061\070\073\066" + "\176\000\275\000\010\000\033\133\061\071\073\066\176\000\276" + "\000\010\000\033\133\062\060\073\066\176\000\277\000\010\000" + "\033\133\062\061\073\066\176\000\300\000\010\000\033\133\062" + "\063\073\066\176\000\301\000\010\000\033\133\062\064\073\066" + "\176\000\302\000\007\000\033\133\061\073\063\120\000\226\000" + "\006\000\033\133\061\065\176\000\303\000\007\000\033\133\061" + "\073\063\121\000\304\000\007\000\033\133\061\073\063\122\000" + "\305\000\007\000\033\133\061\073\063\123\000\306\000\010\000" + "\033\133\061\065\073\063\176\000\307\000\010\000\033\133\061" + "\067\073\063\176\000\310\000\010\000\033\133\061\070\073\063" + "\176\000\311\000\010\000\033\133\061\071\073\063\176\000\312" + "\000\010\000\033\133\062\060\073\063\176\000\313\000\010\000" + "\033\133\062\061\073\063\176\000\314\000\010\000\033\133\062" + "\063\073\063\176\000\227\000\006\000\033\133\061\067\176\000" + "\315\000\010\000\033\133\062\064\073\063\176\000\316\000\007" + "\000\033\133\061\073\064\120\000\317\000\007\000\033\133\061" + "\073\064\121\000\320\000\007\000\033\133\061\073\064\122\000" + "\230\000\006\000\033\133\061\070\176\000\231\000\006\000\033" + "\133\061\071\176\000\232\000\006\000\033\133\062\060\176\000" + "\373\000\007\000\033\133\061\073\062\104\000\005\001\007\000" + "\033\133\061\073\062\103\000\366\000\007\000\033\133\061\073" + "\062\102\000\002\001\007\000\033\133\061\073\062\101\000\360" + "\000\007\000\033\133\063\073\062\176\000\363\000\007\000\033" + "\133\061\073\062\106\000\371\000\007\000\033\133\061\073\062" + "\110\000\372\000\007\000\033\133\062\073\062\176\000\376\000" + "\007\000\033\133\066\073\062\176\000\000\001\007\000\033\133" + "\065\073\062\176\000\053\000\006\000\033\135\060\073\007\000" + "\152\000\002\000\007\000\170\001\005\000\033\135\060\073\000" + "\000\000\101\000\053\053\054\054\055\055\056\056\060\060\140" + "\140\141\141\146\146\147\147\150\150\151\151\152\152\153\153" + "\154\154\155\155\156\156\157\157\160\160\161\161\162\162\163" + "\163\164\164\165\165\166\166\167\167\170\170\171\171\172\172" + "\173\173\174\174\175\175\176\176\000\003\000\002\000\007\000" + "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133" + "\061\155\000\002\000\004\000\033\133\132\000\035\000\007\000" + "\033\133\077\062\065\154\000\021\000\007\000\033\133\110\033" + "\133\112\000\040\000\014\000\033\133\063\064\150\033\133\077" + "\062\065\150\000\007\000\002\000\015\000\014\000\021\000\033" + "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\162" + "\000\063\001\011\000\033\133\045\160\061\045\144\104\000\036" + "\000\002\000\010\000\056\001\011\000\033\133\045\160\061\045" + "\144\102\000\033\000\002\000\012\000\065\001\011\000\033\133" + "\045\160\061\045\144\103\000\041\000\004\000\033\133\103\000" + "\032\000\021\000\033\133\045\151\045\160\061\045\144\073\045" + "\160\062\045\144\110\000\070\001\011\000\033\133\045\160\061" + "\045\144\101\000\043\000\003\000\033\115\000\044\000\006\000" + "\033\133\063\064\154\000\054\001\011\000\033\133\045\160\061" + "\045\144\120\000\047\000\004\000\033\133\120\000\067\000\005" + "\000\033\133\062\155\000\055\001\011\000\033\133\045\160\061" + "\045\144\115\000\050\000\004\000\033\133\115\000\024\000\004" + "\000\033\133\112\000\023\000\004\000\033\133\113\000\022\000" + "\005\000\033\133\061\113\000\057\000\007\000\033\050\102\033" + "\051\060\000\150\000\003\000\033\147\000\034\000\004\000\033" + "\133\110\000\166\001\002\000\011\000\153\001\003\000\033\110" + "\000\060\001\011\000\033\133\045\160\061\045\144\100\000\062" + "\001\011\000\033\133\045\160\061\045\144\114\000\166\000\004" + "\000\033\133\114\000\122\001\002\000\012\000\157\000\004\000" + "\033\051\060\000\173\000\002\000\010\000\175\000\004\000\033" + "\133\132\000\332\000\004\000\033\133\115\000\047\001\003\000" + "\033\105\000\116\001\003\000\033\070\000\105\000\005\000\033" + "\133\067\155\000\123\001\003\000\033\115\000\123\000\002\000" + "\017\000\126\000\011\000\033\133\077\061\060\064\071\154\000" + "\131\000\005\000\033\133\064\154\000\016\001\010\000\033\133" + "\077\061\154\033\076\000\143\000\006\000\033\133\062\064\155" + "\000\113\001\021\000\033\143\033\133\077\061\060\060\060\154" + "\033\133\077\062\065\150\000\120\001\003\000\033\067\000\134" + "\001\124\000\033\133\060\045\077\045\160\066\045\164\073\061" + "\045\073\045\077\045\160\061\045\164\073\063\045\073\045\077" + "\045\160\062\045\164\073\064\045\073\045\077\045\160\063\045" + "\164\073\067\045\073\045\077\045\160\064\045\164\073\065\045" + "\073\045\077\045\160\065\045\164\073\062\045\073\155\045\077" + "\045\160\071\045\164\016\045\145\017\045\073\000\125\000\005" + "\000\033\133\155\017\000\061\000\002\000\016\000\065\000\011" + "\000\033\133\077\061\060\064\071\150\000\073\000\005\000\033" + "\133\064\150\000\017\001\010\000\033\133\077\061\150\033\075" + "\000\116\000\005\000\033\133\064\155\000\017\000\005\000\033" + "\133\063\147\000\052\001\011\000\033\133\063\071\073\064\071" + "\155\000\074\004\101\000\004\000\153\104\116\000\163\007\000" + "\033\133\061\073\062\102\000\005\000\153\104\116\063\000\163" + "\007\000\033\133\061\073\063\102\000\005\000\153\104\116\064" + "\000\163\007\000\033\133\061\073\064\102\000\005\000\153\104" + "\116\065\000\163\007\000\033\133\061\073\065\102\000\005\000" + "\153\104\116\066\000\163\007\000\033\133\061\073\066\102\000" + "\005\000\153\104\116\067\000\163\007\000\033\133\061\073\067" + "\102\000\006\000\153\114\106\124\063\000\163\007\000\033\133" + "\061\073\063\104\000\006\000\153\114\106\124\064\000\163\007" + "\000\033\133\061\073\064\104\000\006\000\153\114\106\124\065" + "\000\163\007\000\033\133\061\073\065\104\000\006\000\153\114" + "\106\124\066\000\163\007\000\033\133\061\073\066\104\000\006" + "\000\153\114\106\124\067\000\163\007\000\033\133\061\073\067" + "\104\000\006\000\153\122\111\124\063\000\163\007\000\033\133" + "\061\073\063\103\000\006\000\153\122\111\124\064\000\163\007" + "\000\033\133\061\073\064\103\000\006\000\153\122\111\124\065" + "\000\163\007\000\033\133\061\073\065\103\000\006\000\153\122" + "\111\124\066\000\163\007\000\033\133\061\073\066\103\000\006" + "\000\153\122\111\124\067\000\163\007\000\033\133\061\073\067" + "\103\000\004\000\153\125\120\000\163\007\000\033\133\061\073" + "\062\101\000\005\000\153\125\120\063\000\163\007\000\033\133" + "\061\073\063\101\000\005\000\153\125\120\064\000\163\007\000" + "\033\133\061\073\064\101\000\005\000\153\125\120\065\000\163" + "\007\000\033\133\061\073\065\101\000\005\000\153\125\120\066" + "\000\163\007\000\033\133\061\073\066\101\000\005\000\153\125" + "\120\067\000\163\007\000\033\133\061\073\067\101\000\005\000" + "\153\104\103\063\000\163\007\000\033\133\063\073\063\176\000" + "\005\000\153\104\103\064\000\163\007\000\033\133\063\073\064" + "\176\000\005\000\153\104\103\065\000\163\007\000\033\133\063" + "\073\065\176\000\005\000\153\104\103\066\000\163\007\000\033" + "\133\063\073\066\176\000\005\000\153\104\103\067\000\163\007" + "\000\033\133\063\073\067\176\000\006\000\153\105\116\104\063" + "\000\163\007\000\033\133\061\073\063\106\000\006\000\153\105" + "\116\104\064\000\163\007\000\033\133\061\073\064\106\000\006" + "\000\153\105\116\104\065\000\163\007\000\033\133\061\073\065" + "\106\000\006\000\153\105\116\104\066\000\163\007\000\033\133" + "\061\073\066\106\000\006\000\153\105\116\104\067\000\163\007" + "\000\033\133\061\073\067\106\000\006\000\153\110\117\115\063" + "\000\163\007\000\033\133\061\073\063\110\000\006\000\153\110" + "\117\115\064\000\163\007\000\033\133\061\073\064\110\000\006" + "\000\153\110\117\115\065\000\163\007\000\033\133\061\073\065" + "\110\000\006\000\153\110\117\115\066\000\163\007\000\033\133" + "\061\073\066\110\000\006\000\153\110\117\115\067\000\163\007" + "\000\033\133\061\073\067\110\000\005\000\153\111\103\063\000" + "\163\007\000\033\133\062\073\063\176\000\005\000\153\111\103" + "\064\000\163\007\000\033\133\062\073\064\176\000\005\000\153" + "\111\103\065\000\163\007\000\033\133\062\073\065\176\000\005" + "\000\153\111\103\066\000\163\007\000\033\133\062\073\066\176" + "\000\005\000\153\111\103\067\000\163\007\000\033\133\062\073" + "\067\176\000\006\000\153\116\130\124\063\000\163\007\000\033" + "\133\066\073\063\176\000\006\000\153\116\130\124\064\000\163" + "\007\000\033\133\066\073\064\176\000\006\000\153\116\130\124" + "\065\000\163\007\000\033\133\066\073\065\176\000\006\000\153" + "\116\130\124\066\000\163\007\000\033\133\066\073\066\176\000" + "\006\000\153\116\130\124\067\000\163\007\000\033\133\066\073" + "\067\176\000\006\000\153\120\122\126\063\000\163\007\000\033" + "\133\065\073\063\176\000\006\000\153\120\122\126\064\000\163" + "\007\000\033\133\065\073\064\176\000\006\000\153\120\122\126" + "\065\000\163\007\000\033\133\065\073\065\176\000\006\000\153" + "\120\122\126\066\000\163\007\000\033\133\065\073\066\176\000" + "\006\000\153\120\122\126\067\000\163\007\000\033\133\065\073" + "\067\176\000\003\000\124\123\000\163\005\000\033\135\060\073" + "\000\003\000\103\162\000\163\007\000\033\135\061\061\062\007" + "\000\003\000\103\163\000\163\014\000\033\135\061\062\073\045" + "\160\061\045\163\007\000\003\000\115\163\000\163\022\000\033" + "\135\065\062\073\045\160\061\045\163\073\045\160\062\045\163" + "\007\000\003\000\123\145\000\163\006\000\033\133\062\040\161" + "\000\003\000\123\163\000\163\012\000\033\133\045\160\061\045" + "\144\040\161\000\003\000\142\163\000\146\001\003\000\160\164" + "\000\146\001\003\000\107\060\000\146\001\003\000\125\070\000" + "\156\001\000\003\000\105\060\000\163\004\000\033\050\102\000" + "\003\000\123\060\000\163\010\000\033\050\045\160\061\045\143" + "\000\003\000\101\130\000\146\001", + 2993 + }, + { + "vt100", + "\001\006\000\166\164\061\060\060\000\000\000\000\000\021\000" + "\005\000\001\000\001\036\000\001\027\000\001\011\000\001\044" + "\000\001\022\000\004\000\004\000\120\000\007\000\010\000\012" + "\000\030\000\036\000\003\000\100\003\107\000\000\000\063\000" + "\140\140\141\141\146\146\147\147\152\152\153\153\154\154\155" + "\155\156\156\157\157\160\160\161\161\162\162\163\163\164\164" + "\165\165\166\166\167\167\170\170\171\171\172\172\173\173\174" + "\174\175\175\176\176\000\003\000\002\000\007\000\063\000\011" + "\000\033\133\065\155\044\074\062\076\000\064\000\011\000\033" + "\133\061\155\044\074\062\076\000\021\000\014\000\033\133\110" + "\033\133\112\044\074\065\060\076\000\007\000\002\000\015\000" + "\014\000\021\000\033\133\045\151\045\160\061\045\144\073\045" + "\160\062\045\144\162\000\063\001\011\000\033\133\045\160\061" + "\045\144\104\000\036\000\002\000\010\000\056\001\011\000\033" + "\133\045\160\061\045\144\102\000\033\000\002\000\012\000\065" + "\001\011\000\033\133\045\160\061\045\144\103\000\041\000\010" + "\000\033\133\103\044\074\062\076\000\032\000\025\000\033\133" + "\045\151\045\160\061\045\144\073\045\160\062\045\144\110\044" + "\074\065\076\000\070\001\011\000\033\133\045\160\061\045\144" + "\101\000\043\000\010\000\033\133\101\044\074\062\076\000\024" + "\000\011\000\033\133\112\044\074\065\060\076\000\023\000\010" + "\000\033\133\113\044\074\063\076\000\022\000\011\000\033\133" + "\061\113\044\074\063\076\000\057\000\007\000\033\050\102\033" + "\051\060\000\034\000\004\000\033\133\110\000\166\001\002\000" + "\011\000\153\001\003\000\033\110\000\122\001\002\000\012\000" + "\173\000\002\000\010\000\326\000\004\000\033\117\104\000\212" + "\000\004\000\033\117\102\000\351\000\004\000\033\117\103\000" + "\015\001\004\000\033\117\101\000\021\001\004\000\160\146\061" + "\000\022\001\004\000\160\146\062\000\023\001\004\000\160\146" + "\063\000\024\001\004\000\160\146\064\000\100\001\005\000\033" + "\133\060\151\000\102\001\005\000\033\133\064\151\000\103\001" + "\005\000\033\133\065\151\000\116\001\003\000\033\070\000\105" + "\000\011\000\033\133\067\155\044\074\062\076\000\123\001\007" + "\000\033\115\044\074\065\076\000\123\000\002\000\017\000\124" + "\000\006\000\033\133\077\067\154\000\016\001\010\000\033\133" + "\077\061\154\033\076\000\140\000\010\000\033\133\155\044\074" + "\062\076\000\143\000\010\000\033\133\155\044\074\062\076\000" + "\113\001\034\000\033\076\033\133\077\063\154\033\133\077\064" + "\154\033\133\077\065\154\033\133\077\067\150\033\133\077\070" + "\150\000\120\001\003\000\033\067\000\134\001\114\000\033\133" + "\060\045\077\045\160\061\045\160\066\045\174\045\164\073\061" + "\045\073\045\077\045\160\062\045\164\073\064\045\073\045\077" + "\045\160\061\045\160\063\045\174\045\164\073\067\045\073\045" + "\077\045\160\064\045\164\073\065\045\073\155\045\077\045\160" + "\071\045\164\016\045\145\017\045\073\044\074\062\076\000\125" + "\000\011\000\033\133\155\017\044\074\062\076\000\061\000\002" + "\000\016\000\062\000\006\000\033\133\077\067\150\000\017\001" + "\010\000\033\133\077\061\150\033\075\000\112\000\011\000\033" + "\133\067\155\044\074\062\076\000\116\000\011\000\033\133\064" + "\155\044\074\062\076\000\017\000\005\000\033\133\063\147\000" + "\221\000\004\000\033\117\171\000\233\000\004\000\033\117\170" + "\000\226\000\004\000\033\117\164\000\227\000\004\000\033\117" + "\165\000\230\000\004\000\033\117\166\000\231\000\004\000\033" + "\117\154\000\232\000\004\000\033\117\167\000\215\000\004\000" + "\033\117\115\000\222\000\004\000\033\117\120\000\223\000\004" + "\000\033\117\121\000\224\000\004\000\033\117\122\000\225\000" + "\004\000\033\117\123\000\170\000\004\000\033\117\161\000\171" + "\000\004\000\033\117\163\000\172\000\004\000\033\117\162\000" + "\176\000\004\000\033\117\160\000\177\000\004\000\033\117\156" + "\000\011\000\001\000\003\000\142\163\000\146\001", + 897 + }, + { + "vt220", + "\001\006\000\166\164\062\062\060\000\000\000\000\000\024\000" + "\006\000\001\000\001\026\000\001\027\000\001\011\000\001\044" + "\000\001\036\000\001\022\000\004\000\004\000\120\000\007\000" + "\010\000\012\000\030\000\036\000\003\000\067\004\137\000\000" + "\000\063\000\140\140\141\141\146\146\147\147\152\152\153\153" + "\154\154\155\155\156\156\157\157\160\160\161\161\162\162\163" + "\163\164\164\165\165\166\166\167\167\170\170\171\171\172\172" + "\173\173\174\174\175\175\176\176\000\003\000\002\000\007\000" + "\063\000\005\000\033\133\065\155\000\064\000\005\000\033\133" + "\061\155\000\021\000\007\000\033\133\110\033\133\112\000\007" + "\000\002\000\015\000\014\000\021\000\033\133\045\151\045\160" + "\061\045\144\073\045\160\062\045\144\162\000\063\001\011\000" + "\033\133\045\160\061\045\144\104\000\036\000\002\000\010\000" + "\056\001\011\000\033\133\045\160\061\045\144\102\000\033\000" + "\002\000\012\000\065\001\011\000\033\133\045\160\061\045\144" + "\103\000\041\000\004\000\033\133\103\000\032\000\021\000\033" + "\133\045\151\045\160\061\045\144\073\045\160\062\045\144\110" + "\000\070\001\011\000\033\133\045\160\061\045\144\101\000\043" + "\000\004\000\033\133\101\000\054\001\011\000\033\133\045\160" + "\061\045\144\120\000\047\000\004\000\033\133\120\000\055\001" + "\011\000\033\133\045\160\061\045\144\115\000\050\000\004\000" + "\033\133\115\000\122\000\011\000\033\133\045\160\061\045\144" + "\130\000\024\000\004\000\033\133\112\000\023\000\004\000\033" + "\133\113\000\022\000\005\000\033\133\061\113\000\057\000\004" + "\000\033\051\060\000\150\000\022\000\033\133\077\065\150\044" + "\074\062\060\060\057\076\033\133\077\065\154\000\034\000\004" + "\000\033\133\110\000\166\001\002\000\011\000\153\001\003\000" + "\033\110\000\060\001\011\000\033\133\045\160\061\045\144\100" + "\000\161\000\030\000\057\165\163\162\057\163\150\141\162\145" + "\057\164\141\142\163\145\164\057\166\164\061\060\060\000\062" + "\001\011\000\033\133\045\160\061\045\144\114\000\166\000\004" + "\000\033\133\114\000\122\001\003\000\033\104\000\157\000\026" + "\000\033\133\077\067\150\033\133\076\033\133\077\061\154\033" + "\040\106\033\133\077\064\154\000\173\000\002\000\010\000\326" + "\000\004\000\033\133\104\000\212\000\004\000\033\133\102\000" + "\351\000\004\000\033\133\103\000\015\001\004\000\033\133\101" + "\000\210\000\005\000\033\133\063\176\000\222\000\004\000\033" + "\117\120\000\233\000\006\000\033\133\062\061\176\000\234\000" + "\006\000\033\133\062\063\176\000\235\000\006\000\033\133\062" + "\064\176\000\236\000\006\000\033\133\062\065\176\000\237\000" + "\006\000\033\133\062\066\176\000\242\000\006\000\033\133\063" + "\061\176\000\243\000\006\000\033\133\063\062\176\000\244\000" + "\006\000\033\133\063\063\176\000\223\000\004\000\033\117\121" + "\000\245\000\006\000\033\133\063\064\176\000\224\000\004\000" + "\033\117\122\000\225\000\004\000\033\117\123\000\227\000\006" + "\000\033\133\061\067\176\000\230\000\006\000\033\133\061\070" + "\176\000\231\000\006\000\033\133\061\071\176\000\232\000\006" + "\000\033\133\062\060\176\000\321\000\005\000\033\133\061\176" + "\000\322\000\006\000\033\133\062\070\176\000\324\000\005\000" + "\033\133\062\176\000\335\000\005\000\033\133\066\176\000\340" + "\000\005\000\033\133\065\176\000\343\000\006\000\033\133\062" + "\071\176\000\362\000\005\000\033\133\064\176\000\021\001\004" + "\000\160\146\061\000\022\001\004\000\160\146\062\000\023\001" + "\004\000\160\146\063\000\024\001\004\000\160\146\064\000\047" + "\001\003\000\033\105\000\116\001\003\000\033\070\000\105\000" + "\005\000\033\133\067\155\000\123\001\003\000\033\115\000\123" + "\000\010\000\033\050\102\044\074\064\076\000\124\000\006\000" + "\033\133\077\067\154\000\131\000\005\000\033\133\064\154\000" + "\140\000\006\000\033\133\062\067\155\000\143\000\006\000\033" + "\133\062\064\155\000\112\001\006\000\033\133\077\063\154\000" + "\120\001\003\000\033\067\000\134\001\113\000\033\133\060\045" + "\077\045\160\066\045\164\073\061\045\073\045\077\045\160\062" + "\045\164\073\064\045\073\045\077\045\160\064\045\164\073\065" + "\045\073\045\077\045\160\061\045\160\063\045\174\045\164\073" + "\067\045\073\155\045\077\045\160\071\045\164\033\050\060\045" + "\145\033\050\102\045\073\044\074\062\076\000\125\000\007\000" + "\033\133\155\033\050\102\000\061\000\010\000\033\050\060\044" + "\074\062\076\000\062\000\006\000\033\133\077\067\150\000\073" + "\000\005\000\033\133\064\150\000\112\000\005\000\033\133\067" + "\155\000\116\000\005\000\033\133\064\155\000\017\000\005\000" + "\033\133\063\147\000\100\001\004\000\033\133\151\000\102\001" + "\005\000\033\133\064\151\000\103\001\005\000\033\133\065\151" + "\000\200\001\013\000\033\133\045\151\045\144\073\045\144\122" + "\000\201\001\005\000\033\133\066\156\000\202\001\023\000\033" + "\133\077\045\133\073\060\061\062\063\064\065\066\067\070\071" + "\135\143\000\203\001\004\000\033\133\143\000\011\000\001\000" + "\003\000\142\163\000\146\001", + 1147 + }, + { + "wsvt25", + "\001\007\000\167\163\166\164\062\065\000\000\000\000\000\027" + "\000\007\000\002\000\001\027\000\001\001\000\001\026\000\001" + "\011\000\001\044\000\001\036\000\001\032\000\006\000\016\000" + "\010\000\004\000\120\000\007\000\010\000\012\000\031\000\021" + "\000\100\000\036\000\003\000\207\004\147\000\035\000\007\000" + "\033\133\077\062\065\154\000\040\000\007\000\033\133\077\062" + "\065\150\000\157\000\013\000\033\133\162\033\133\062\065\073" + "\061\110\000\210\000\005\000\033\133\063\176\000\214\000\005" + "\000\033\133\070\176\000\222\000\006\000\033\133\061\061\176" + "\000\233\000\006\000\033\133\062\061\176\000\234\000\006\000" + "\033\133\062\063\176\000\235\000\006\000\033\133\062\064\176" + "\000\223\000\006\000\033\133\061\062\176\000\224\000\006\000" + "\033\133\061\063\176\000\225\000\006\000\033\133\061\064\176" + "\000\226\000\006\000\033\133\061\065\176\000\227\000\006\000" + "\033\133\061\067\176\000\230\000\006\000\033\133\061\070\176" + "\000\231\000\006\000\033\133\061\071\176\000\232\000\006\000" + "\033\133\062\060\176\000\323\000\005\000\033\133\067\176\000" + "\052\001\004\000\033\133\155\000\112\001\003\000\033\143\000" + "\132\001\012\000\033\133\064\045\160\061\045\144\155\000\133" + "\001\012\000\033\133\063\045\160\061\045\144\155\000\000\000" + "\063\000\140\140\141\141\146\146\147\147\152\152\153\153\154" + "\154\155\155\156\156\157\157\160\160\161\161\162\162\163\163" + "\164\164\165\165\166\166\167\167\170\170\171\171\172\172\173" + "\173\174\174\175\175\176\176\000\003\000\002\000\007\000\063" + "\000\005\000\033\133\065\155\000\064\000\005\000\033\133\061" + "\155\000\021\000\007\000\033\133\110\033\133\112\000\007\000" + "\002\000\015\000\014\000\021\000\033\133\045\151\045\160\061" + "\045\144\073\045\160\062\045\144\162\000\063\001\011\000\033" + "\133\045\160\061\045\144\104\000\036\000\002\000\010\000\056" + "\001\011\000\033\133\045\160\061\045\144\102\000\033\000\002" + "\000\012\000\065\001\011\000\033\133\045\160\061\045\144\103" + "\000\041\000\004\000\033\133\103\000\032\000\021\000\033\133" + "\045\151\045\160\061\045\144\073\045\160\062\045\144\110\000" + "\070\001\011\000\033\133\045\160\061\045\144\101\000\043\000" + "\004\000\033\133\101\000\054\001\011\000\033\133\045\160\061" + "\045\144\120\000\047\000\004\000\033\133\120\000\055\001\011" + "\000\033\133\045\160\061\045\144\115\000\050\000\004\000\033" + "\133\115\000\122\000\011\000\033\133\045\160\061\045\144\130" + "\000\024\000\004\000\033\133\112\000\023\000\004\000\033\133" + "\113\000\022\000\005\000\033\133\061\113\000\057\000\004\000" + "\033\051\060\000\150\000\022\000\033\133\077\065\150\044\074" + "\062\060\060\057\076\033\133\077\065\154\000\034\000\004\000" + "\033\133\110\000\166\001\002\000\011\000\153\001\003\000\033" + "\110\000\060\001\011\000\033\133\045\160\061\045\144\100\000" + "\161\000\030\000\057\165\163\162\057\163\150\141\162\145\057" + "\164\141\142\163\145\164\057\166\164\061\060\060\000\062\001" + "\011\000\033\133\045\160\061\045\144\114\000\166\000\004\000" + "\033\133\114\000\122\001\003\000\033\104\000\173\000\002\000" + "\010\000\326\000\004\000\033\133\104\000\212\000\004\000\033" + "\133\102\000\351\000\004\000\033\133\103\000\015\001\004\000" + "\033\133\101\000\236\000\006\000\033\133\062\065\176\000\237" + "\000\006\000\033\133\062\066\176\000\242\000\006\000\033\133" + "\063\061\176\000\243\000\006\000\033\133\063\062\176\000\244" + "\000\006\000\033\133\063\063\176\000\245\000\006\000\033\133" + "\063\064\176\000\321\000\005\000\033\133\061\176\000\322\000" + "\006\000\033\133\062\070\176\000\324\000\005\000\033\133\062" + "\176\000\335\000\005\000\033\133\066\176\000\340\000\005\000" + "\033\133\065\176\000\343\000\006\000\033\133\062\071\176\000" + "\362\000\005\000\033\133\064\176\000\021\001\004\000\160\146" + "\061\000\022\001\004\000\160\146\062\000\023\001\004\000\160" + "\146\063\000\024\001\004\000\160\146\064\000\047\001\003\000" + "\033\105\000\116\001\003\000\033\070\000\105\000\005\000\033" + "\133\067\155\000\123\001\003\000\033\115\000\123\000\010\000" + "\033\050\102\044\074\064\076\000\124\000\006\000\033\133\077" + "\067\154\000\131\000\005\000\033\133\064\154\000\140\000\006" + "\000\033\133\062\067\155\000\143\000\006\000\033\133\062\064" + "\155\000\120\001\003\000\033\067\000\134\001\113\000\033\133" + "\060\045\077\045\160\066\045\164\073\061\045\073\045\077\045" + "\160\062\045\164\073\064\045\073\045\077\045\160\064\045\164" + "\073\065\045\073\045\077\045\160\061\045\160\063\045\174\045" + "\164\073\067\045\073\155\045\077\045\160\071\045\164\033\050" + "\060\045\145\033\050\102\045\073\044\074\062\076\000\125\000" + "\007\000\033\133\155\033\050\102\000\061\000\010\000\033\050" + "\060\044\074\062\076\000\062\000\006\000\033\133\077\067\150" + "\000\073\000\005\000\033\133\064\150\000\112\000\005\000\033" + "\133\067\155\000\116\000\005\000\033\133\064\155\000\017\000" + "\005\000\033\133\063\147\000\100\001\004\000\033\133\151\000" + "\102\001\005\000\033\133\064\151\000\103\001\005\000\033\133" + "\065\151\000\200\001\013\000\033\133\045\151\045\144\073\045" + "\144\122\000\201\001\005\000\033\133\066\156\000\202\001\023" + "\000\033\133\077\045\133\073\060\061\062\063\064\065\066\067" + "\070\071\135\143\000\203\001\004\000\033\133\143\000\011\000" + "\001\000\003\000\142\163\000\146\001", + 1239 + }, + { + "xterm", + "\001\006\000\170\164\145\162\155\000\000\000\000\000\032\000" + "\010\000\032\000\001\001\000\001\002\000\001\016\000\001\026" + "\000\001\027\000\001\011\000\001\036\000\001\026\000\005\000" + "\016\000\010\000\004\000\120\000\007\000\010\000\012\000\030" + "\000\021\000\100\000\066\010\252\000\061\001\011\000\033\133" + "\045\160\061\045\144\123\000\172\000\004\000\033\117\105\000" + "\175\000\004\000\033\133\132\000\215\000\004\000\033\117\115" + "\000\067\001\011\000\033\133\045\160\061\045\144\124\000\200" + "\001\013\000\033\133\045\151\045\144\073\045\144\122\000\201" + "\001\005\000\033\133\066\156\000\202\001\023\000\033\133\077" + "\045\133\073\060\061\062\063\064\065\066\067\070\071\135\143" + "\000\203\001\004\000\033\133\143\000\326\000\004\000\033\117" + "\104\000\212\000\004\000\033\117\102\000\351\000\004\000\033" + "\117\103\000\015\001\004\000\033\117\101\000\214\000\004\000" + "\033\117\106\000\323\000\004\000\033\117\110\000\222\000\004" + "\000\033\117\120\000\233\000\006\000\033\133\062\061\176\000" + "\234\000\006\000\033\133\062\063\176\000\235\000\006\000\033" + "\133\062\064\176\000\236\000\007\000\033\133\061\073\062\120" + "\000\237\000\007\000\033\133\061\073\062\121\000\240\000\007" + "\000\033\133\061\073\062\122\000\241\000\007\000\033\133\061" + "\073\062\123\000\242\000\010\000\033\133\061\065\073\062\176" + "\000\243\000\010\000\033\133\061\067\073\062\176\000\244\000" + "\010\000\033\133\061\070\073\062\176\000\223\000\004\000\033" + "\117\121\000\245\000\010\000\033\133\061\071\073\062\176\000" + "\246\000\010\000\033\133\062\060\073\062\176\000\247\000\010" + "\000\033\133\062\061\073\062\176\000\250\000\010\000\033\133" + "\062\063\073\062\176\000\251\000\010\000\033\133\062\064\073" + "\062\176\000\252\000\007\000\033\133\061\073\065\120\000\253" + "\000\007\000\033\133\061\073\065\121\000\254\000\007\000\033" + "\133\061\073\065\122\000\255\000\007\000\033\133\061\073\065" + "\123\000\256\000\010\000\033\133\061\065\073\065\176\000\224" + "\000\004\000\033\117\122\000\257\000\010\000\033\133\061\067" + "\073\065\176\000\260\000\010\000\033\133\061\070\073\065\176" + "\000\261\000\010\000\033\133\061\071\073\065\176\000\262\000" + "\010\000\033\133\062\060\073\065\176\000\263\000\010\000\033" + "\133\062\061\073\065\176\000\264\000\010\000\033\133\062\063" + "\073\065\176\000\265\000\010\000\033\133\062\064\073\065\176" + "\000\266\000\007\000\033\133\061\073\066\120\000\267\000\007" + "\000\033\133\061\073\066\121\000\270\000\007\000\033\133\061" + "\073\066\122\000\225\000\004\000\033\117\123\000\271\000\007" + "\000\033\133\061\073\066\123\000\272\000\010\000\033\133\061" + "\065\073\066\176\000\273\000\010\000\033\133\061\067\073\066" + "\176\000\274\000\010\000\033\133\061\070\073\066\176\000\275" + "\000\010\000\033\133\061\071\073\066\176\000\276\000\010\000" + "\033\133\062\060\073\066\176\000\277\000\010\000\033\133\062" + "\061\073\066\176\000\300\000\010\000\033\133\062\063\073\066" + "\176\000\301\000\010\000\033\133\062\064\073\066\176\000\302" + "\000\007\000\033\133\061\073\063\120\000\226\000\006\000\033" + "\133\061\065\176\000\303\000\007\000\033\133\061\073\063\121" + "\000\304\000\007\000\033\133\061\073\063\122\000\305\000\007" + "\000\033\133\061\073\063\123\000\306\000\010\000\033\133\061" + "\065\073\063\176\000\307\000\010\000\033\133\061\067\073\063" + "\176\000\310\000\010\000\033\133\061\070\073\063\176\000\311" + "\000\010\000\033\133\061\071\073\063\176\000\312\000\010\000" + "\033\133\062\060\073\063\176\000\313\000\010\000\033\133\062" + "\061\073\063\176\000\314\000\010\000\033\133\062\063\073\063" + "\176\000\227\000\006\000\033\133\061\067\176\000\315\000\010" + "\000\033\133\062\064\073\063\176\000\316\000\007\000\033\133" + "\061\073\064\120\000\317\000\007\000\033\133\061\073\064\121" + "\000\320\000\007\000\033\133\061\073\064\122\000\230\000\006" + "\000\033\133\061\070\176\000\231\000\006\000\033\133\061\071" + "\176\000\232\000\006\000\033\133\062\060\176\000\373\000\007" + "\000\033\133\061\073\062\104\000\005\001\007\000\033\133\061" + "\073\062\103\000\366\000\007\000\033\133\061\073\062\102\000" + "\002\001\007\000\033\133\061\073\062\101\000\360\000\007\000" + "\033\133\063\073\062\176\000\363\000\007\000\033\133\061\073" + "\062\106\000\371\000\007\000\033\133\061\073\062\110\000\372" + "\000\007\000\033\133\062\073\062\176\000\376\000\007\000\033" + "\133\066\073\062\176\000\000\001\007\000\033\133\065\073\062" + "\176\000\324\000\005\000\033\133\062\176\000\335\000\005\000" + "\033\133\066\176\000\340\000\005\000\033\133\065\176\000\210" + "\000\005\000\033\133\063\176\000\000\000\065\000\140\140\141" + "\141\146\146\147\147\151\151\152\152\153\153\154\154\155\155" + "\156\156\157\157\160\160\161\161\162\162\163\163\164\164\165" + "\165\166\166\167\167\170\170\171\171\172\172\173\173\174\174" + "\175\175\176\176\000\003\000\002\000\007\000\063\000\005\000" + "\033\133\065\155\000\064\000\005\000\033\133\061\155\000\002" + "\000\004\000\033\133\132\000\035\000\007\000\033\133\077\062" + "\065\154\000\021\000\010\000\033\133\110\033\133\062\112\000" + "\040\000\015\000\033\133\077\061\062\154\033\133\077\062\065" + "\150\000\007\000\002\000\015\000\014\000\021\000\033\133\045" + "\151\045\160\061\045\144\073\045\160\062\045\144\162\000\063" + "\001\011\000\033\133\045\160\061\045\144\104\000\036\000\002" + "\000\010\000\056\001\011\000\033\133\045\160\061\045\144\102" + "\000\033\000\002\000\012\000\065\001\011\000\033\133\045\160" + "\061\045\144\103\000\041\000\004\000\033\133\103\000\032\000" + "\021\000\033\133\045\151\045\160\061\045\144\073\045\160\062" + "\045\144\110\000\070\001\011\000\033\133\045\160\061\045\144" + "\101\000\043\000\004\000\033\133\101\000\044\000\012\000\033" + "\133\077\061\062\073\062\065\150\000\054\001\011\000\033\133" + "\045\160\061\045\144\120\000\047\000\004\000\033\133\120\000" + "\067\000\005\000\033\133\062\155\000\055\001\011\000\033\133" + "\045\160\061\045\144\115\000\050\000\004\000\033\133\115\000" + "\122\000\011\000\033\133\045\160\061\045\144\130\000\024\000" + "\004\000\033\133\112\000\023\000\004\000\033\133\113\000\022" + "\000\005\000\033\133\061\113\000\150\000\022\000\033\133\077" + "\065\150\044\074\061\060\060\057\076\033\133\077\065\154\000" + "\034\000\004\000\033\133\110\000\027\000\013\000\033\133\045" + "\151\045\160\061\045\144\107\000\166\001\002\000\011\000\153" + "\001\003\000\033\110\000\060\001\011\000\033\133\045\160\061" + "\045\144\100\000\062\001\011\000\033\133\045\160\061\045\144" + "\114\000\166\000\004\000\033\133\114\000\122\001\002\000\012" + "\000\110\000\005\000\033\133\070\155\000\157\000\022\000\033" + "\133\041\160\033\133\077\063\073\064\154\033\133\064\154\033" + "\076\000\332\000\004\000\033\133\115\000\052\001\011\000\033" + "\133\063\071\073\064\071\155\000\116\001\003\000\033\070\000" + "\105\000\005\000\033\133\067\155\000\123\001\003\000\033\115" + "\000\132\000\006\000\033\133\062\063\155\000\123\000\004\000" + "\033\050\102\000\124\000\006\000\033\133\077\067\154\000\126" + "\000\011\000\033\133\077\061\060\064\071\154\000\131\000\005" + "\000\033\133\064\154\000\016\001\010\000\033\133\077\061\154" + "\033\076\000\036\001\011\000\033\133\077\061\060\063\064\154" + "\000\140\000\006\000\033\133\062\067\155\000\143\000\006\000" + "\033\133\062\064\155\000\112\001\003\000\033\143\000\113\001" + "\022\000\033\133\041\160\033\133\077\063\073\064\154\033\133" + "\064\154\033\076\000\120\001\003\000\033\067\000\132\001\012" + "\000\033\133\064\045\160\061\045\144\155\000\133\001\012\000" + "\033\133\063\045\160\061\045\144\155\000\135\001\106\000\033" + "\133\064\045\077\045\160\061\045\173\061\175\045\075\045\164" + "\064\045\145\045\160\061\045\173\063\175\045\075\045\164\066" + "\045\145\045\160\061\045\173\064\175\045\075\045\164\061\045" + "\145\045\160\061\045\173\066\175\045\075\045\164\063\045\145" + "\045\160\061\045\144\045\073\155\000\143\001\106\000\033\133" + "\063\045\077\045\160\061\045\173\061\175\045\075\045\164\064" + "\045\145\045\160\061\045\173\063\175\045\075\045\164\066\045" + "\145\045\160\061\045\173\064\175\045\075\045\164\061\045\145" + "\045\160\061\045\173\066\175\045\075\045\164\063\045\145\045" + "\160\061\045\144\045\073\155\000\134\001\135\000\045\077\045" + "\160\071\045\164\033\050\060\045\145\033\050\102\045\073\033" + "\133\060\045\077\045\160\066\045\164\073\061\045\073\045\077" + "\045\160\065\045\164\073\062\045\073\045\077\045\160\062\045" + "\164\073\064\045\073\045\077\045\160\061\045\160\063\045\174" + "\045\164\073\067\045\073\045\077\045\160\064\045\164\073\065" + "\045\073\045\077\045\160\067\045\164\073\070\045\073\155\000" + "\125\000\007\000\033\050\102\033\133\155\000\074\000\005\000" + "\033\133\063\155\000\061\000\004\000\033\050\060\000\062\000" + "\006\000\033\133\077\067\150\000\065\000\011\000\033\133\077" + "\061\060\064\071\150\000\073\000\005\000\033\133\064\150\000" + "\017\001\010\000\033\133\077\061\150\033\075\000\037\001\011" + "\000\033\133\077\061\060\063\064\150\000\112\000\005\000\033" + "\133\067\155\000\116\000\005\000\033\133\064\155\000\017\000" + "\005\000\033\133\063\147\000\117\001\013\000\033\133\045\151" + "\045\160\061\045\144\144\000\100\001\004\000\033\133\151\000" + "\102\001\005\000\033\133\064\151\000\103\001\005\000\033\133" + "\065\151\000\173\000\002\000\010\000\053\004\077\000\004\000" + "\153\104\116\000\163\007\000\033\133\061\073\062\102\000\005" + "\000\153\104\116\063\000\163\007\000\033\133\061\073\063\102" + "\000\005\000\153\104\116\064\000\163\007\000\033\133\061\073" + "\064\102\000\005\000\153\104\116\065\000\163\007\000\033\133" + "\061\073\065\102\000\005\000\153\104\116\066\000\163\007\000" + "\033\133\061\073\066\102\000\005\000\153\104\116\067\000\163" + "\007\000\033\133\061\073\067\102\000\006\000\153\114\106\124" + "\063\000\163\007\000\033\133\061\073\063\104\000\006\000\153" + "\114\106\124\064\000\163\007\000\033\133\061\073\064\104\000" + "\006\000\153\114\106\124\065\000\163\007\000\033\133\061\073" + "\065\104\000\006\000\153\114\106\124\066\000\163\007\000\033" + "\133\061\073\066\104\000\006\000\153\114\106\124\067\000\163" + "\007\000\033\133\061\073\067\104\000\006\000\153\122\111\124" + "\063\000\163\007\000\033\133\061\073\063\103\000\006\000\153" + "\122\111\124\064\000\163\007\000\033\133\061\073\064\103\000" + "\006\000\153\122\111\124\065\000\163\007\000\033\133\061\073" + "\065\103\000\006\000\153\122\111\124\066\000\163\007\000\033" + "\133\061\073\066\103\000\006\000\153\122\111\124\067\000\163" + "\007\000\033\133\061\073\067\103\000\004\000\153\125\120\000" + "\163\007\000\033\133\061\073\062\101\000\005\000\153\125\120" + "\063\000\163\007\000\033\133\061\073\063\101\000\005\000\153" + "\125\120\064\000\163\007\000\033\133\061\073\064\101\000\005" + "\000\153\125\120\065\000\163\007\000\033\133\061\073\065\101" + "\000\005\000\153\125\120\066\000\163\007\000\033\133\061\073" + "\066\101\000\005\000\153\125\120\067\000\163\007\000\033\133" + "\061\073\067\101\000\005\000\153\104\103\063\000\163\007\000" + "\033\133\063\073\063\176\000\005\000\153\104\103\064\000\163" + "\007\000\033\133\063\073\064\176\000\005\000\153\104\103\065" + "\000\163\007\000\033\133\063\073\065\176\000\005\000\153\104" + "\103\066\000\163\007\000\033\133\063\073\066\176\000\005\000" + "\153\104\103\067\000\163\007\000\033\133\063\073\067\176\000" + "\006\000\153\105\116\104\063\000\163\007\000\033\133\061\073" + "\063\106\000\006\000\153\105\116\104\064\000\163\007\000\033" + "\133\061\073\064\106\000\006\000\153\105\116\104\065\000\163" + "\007\000\033\133\061\073\065\106\000\006\000\153\105\116\104" + "\066\000\163\007\000\033\133\061\073\066\106\000\006\000\153" + "\105\116\104\067\000\163\007\000\033\133\061\073\067\106\000" + "\006\000\153\110\117\115\063\000\163\007\000\033\133\061\073" + "\063\110\000\006\000\153\110\117\115\064\000\163\007\000\033" + "\133\061\073\064\110\000\006\000\153\110\117\115\065\000\163" + "\007\000\033\133\061\073\065\110\000\006\000\153\110\117\115" + "\066\000\163\007\000\033\133\061\073\066\110\000\006\000\153" + "\110\117\115\067\000\163\007\000\033\133\061\073\067\110\000" + "\005\000\153\111\103\063\000\163\007\000\033\133\062\073\063" + "\176\000\005\000\153\111\103\064\000\163\007\000\033\133\062" + "\073\064\176\000\005\000\153\111\103\065\000\163\007\000\033" + "\133\062\073\065\176\000\005\000\153\111\103\066\000\163\007" + "\000\033\133\062\073\066\176\000\005\000\153\111\103\067\000" + "\163\007\000\033\133\062\073\067\176\000\006\000\153\116\130" + "\124\063\000\163\007\000\033\133\066\073\063\176\000\006\000" + "\153\116\130\124\064\000\163\007\000\033\133\066\073\064\176" + "\000\006\000\153\116\130\124\065\000\163\007\000\033\133\066" + "\073\065\176\000\006\000\153\116\130\124\066\000\163\007\000" + "\033\133\066\073\066\176\000\006\000\153\116\130\124\067\000" + "\163\007\000\033\133\066\073\067\176\000\006\000\153\120\122" + "\126\063\000\163\007\000\033\133\065\073\063\176\000\006\000" + "\153\120\122\126\064\000\163\007\000\033\133\065\073\064\176" + "\000\006\000\153\120\122\126\065\000\163\007\000\033\133\065" + "\073\065\176\000\006\000\153\120\122\126\066\000\163\007\000" + "\033\133\065\073\066\176\000\006\000\153\120\122\126\067\000" + "\163\007\000\033\133\065\073\067\176\000\003\000\103\162\000" + "\163\007\000\033\135\061\061\062\007\000\003\000\103\163\000" + "\163\014\000\033\135\061\062\073\045\160\061\045\163\007\000" + "\003\000\115\163\000\163\022\000\033\135\065\062\073\045\160" + "\061\045\163\073\045\160\062\045\163\007\000\003\000\123\145" + "\000\163\006\000\033\133\062\040\161\000\003\000\123\163\000" + "\163\012\000\033\133\045\160\061\045\144\040\161\000\003\000" + "\142\163\000\146\001\003\000\101\130\000\146\001\003\000\130" + "\124\000\146\001\005\000\155\145\155\154\000\163\003\000\033" + "\154\000\005\000\155\145\155\165\000\163\003\000\033\155\000" + "\003\000\105\063\000\163\005\000\033\133\063\112\000", + 3238 + }, + { + "xterm-256color", + "\001\017\000\170\164\145\162\155\055\062\065\066\143\157\154" + "\157\162\000\000\000\000\000\035\000\011\000\003\000\001\032" + "\000\001\001\000\001\002\000\001\016\000\001\026\000\001\027" + "\000\001\011\000\001\036\000\001\026\000\005\000\016\000\000" + "\001\021\000\377\177\004\000\120\000\007\000\010\000\012\000" + "\030\000\024\011\254\000\112\001\011\000\033\143\033\135\061" + "\060\064\007\000\163\000\136\000\033\135\064\073\045\160\061" + "\045\144\073\162\147\142\072\045\160\062\045\173\062\065\065" + "\175\045\052\045\173\061\060\060\060\175\045\057\045\062\056" + "\062\130\057\045\160\063\045\173\062\065\065\175\045\052\045" + "\173\061\060\060\060\175\045\057\045\062\056\062\130\057\045" + "\160\064\045\173\062\065\065\175\045\052\045\173\061\060\060" + "\060\175\045\057\045\062\056\062\130\033\134\000\051\001\007" + "\000\033\135\061\060\064\007\000\132\001\100\000\033\133\045" + "\077\045\160\061\045\173\070\175\045\074\045\164\064\045\160" + "\061\045\144\045\145\045\160\061\045\173\061\066\175\045\074" + "\045\164\061\060\045\160\061\045\173\070\175\045\055\045\144" + "\045\145\064\070\073\065\073\045\160\061\045\144\045\073\155" + "\000\133\001\077\000\033\133\045\077\045\160\061\045\173\070" + "\175\045\074\045\164\063\045\160\061\045\144\045\145\045\160" + "\061\045\173\061\066\175\045\074\045\164\071\045\160\061\045" + "\173\070\175\045\055\045\144\045\145\063\070\073\065\073\045" + "\160\061\045\144\045\073\155\000\061\001\011\000\033\133\045" + "\160\061\045\144\123\000\172\000\004\000\033\117\105\000\175" + "\000\004\000\033\133\132\000\215\000\004\000\033\117\115\000" + "\067\001\011\000\033\133\045\160\061\045\144\124\000\200\001" + "\013\000\033\133\045\151\045\144\073\045\144\122\000\201\001" + "\005\000\033\133\066\156\000\202\001\023\000\033\133\077\045" + "\133\073\060\061\062\063\064\065\066\067\070\071\135\143\000" + "\203\001\004\000\033\133\143\000\326\000\004\000\033\117\104" + "\000\212\000\004\000\033\117\102\000\351\000\004\000\033\117" + "\103\000\015\001\004\000\033\117\101\000\214\000\004\000\033" + "\117\106\000\323\000\004\000\033\117\110\000\222\000\004\000" + "\033\117\120\000\233\000\006\000\033\133\062\061\176\000\234" + "\000\006\000\033\133\062\063\176\000\235\000\006\000\033\133" + "\062\064\176\000\236\000\007\000\033\133\061\073\062\120\000" + "\237\000\007\000\033\133\061\073\062\121\000\240\000\007\000" + "\033\133\061\073\062\122\000\241\000\007\000\033\133\061\073" + "\062\123\000\242\000\010\000\033\133\061\065\073\062\176\000" + "\243\000\010\000\033\133\061\067\073\062\176\000\244\000\010" + "\000\033\133\061\070\073\062\176\000\223\000\004\000\033\117" + "\121\000\245\000\010\000\033\133\061\071\073\062\176\000\246" + "\000\010\000\033\133\062\060\073\062\176\000\247\000\010\000" + "\033\133\062\061\073\062\176\000\250\000\010\000\033\133\062" + "\063\073\062\176\000\251\000\010\000\033\133\062\064\073\062" + "\176\000\252\000\007\000\033\133\061\073\065\120\000\253\000" + "\007\000\033\133\061\073\065\121\000\254\000\007\000\033\133" + "\061\073\065\122\000\255\000\007\000\033\133\061\073\065\123" + "\000\256\000\010\000\033\133\061\065\073\065\176\000\224\000" + "\004\000\033\117\122\000\257\000\010\000\033\133\061\067\073" + "\065\176\000\260\000\010\000\033\133\061\070\073\065\176\000" + "\261\000\010\000\033\133\061\071\073\065\176\000\262\000\010" + "\000\033\133\062\060\073\065\176\000\263\000\010\000\033\133" + "\062\061\073\065\176\000\264\000\010\000\033\133\062\063\073" + "\065\176\000\265\000\010\000\033\133\062\064\073\065\176\000" + "\266\000\007\000\033\133\061\073\066\120\000\267\000\007\000" + "\033\133\061\073\066\121\000\270\000\007\000\033\133\061\073" + "\066\122\000\225\000\004\000\033\117\123\000\271\000\007\000" + "\033\133\061\073\066\123\000\272\000\010\000\033\133\061\065" + "\073\066\176\000\273\000\010\000\033\133\061\067\073\066\176" + "\000\274\000\010\000\033\133\061\070\073\066\176\000\275\000" + "\010\000\033\133\061\071\073\066\176\000\276\000\010\000\033" + "\133\062\060\073\066\176\000\277\000\010\000\033\133\062\061" + "\073\066\176\000\300\000\010\000\033\133\062\063\073\066\176" + "\000\301\000\010\000\033\133\062\064\073\066\176\000\302\000" + "\007\000\033\133\061\073\063\120\000\226\000\006\000\033\133" + "\061\065\176\000\303\000\007\000\033\133\061\073\063\121\000" + "\304\000\007\000\033\133\061\073\063\122\000\305\000\007\000" + "\033\133\061\073\063\123\000\306\000\010\000\033\133\061\065" + "\073\063\176\000\307\000\010\000\033\133\061\067\073\063\176" + "\000\310\000\010\000\033\133\061\070\073\063\176\000\311\000" + "\010\000\033\133\061\071\073\063\176\000\312\000\010\000\033" + "\133\062\060\073\063\176\000\313\000\010\000\033\133\062\061" + "\073\063\176\000\314\000\010\000\033\133\062\063\073\063\176" + "\000\227\000\006\000\033\133\061\067\176\000\315\000\010\000" + "\033\133\062\064\073\063\176\000\316\000\007\000\033\133\061" + "\073\064\120\000\317\000\007\000\033\133\061\073\064\121\000" + "\320\000\007\000\033\133\061\073\064\122\000\230\000\006\000" + "\033\133\061\070\176\000\231\000\006\000\033\133\061\071\176" + "\000\232\000\006\000\033\133\062\060\176\000\373\000\007\000" + "\033\133\061\073\062\104\000\005\001\007\000\033\133\061\073" + "\062\103\000\366\000\007\000\033\133\061\073\062\102\000\002" + "\001\007\000\033\133\061\073\062\101\000\360\000\007\000\033" + "\133\063\073\062\176\000\363\000\007\000\033\133\061\073\062" + "\106\000\371\000\007\000\033\133\061\073\062\110\000\372\000" + "\007\000\033\133\062\073\062\176\000\376\000\007\000\033\133" + "\066\073\062\176\000\000\001\007\000\033\133\065\073\062\176" + "\000\324\000\005\000\033\133\062\176\000\335\000\005\000\033" + "\133\066\176\000\340\000\005\000\033\133\065\176\000\210\000" + "\005\000\033\133\063\176\000\000\000\065\000\140\140\141\141" + "\146\146\147\147\151\151\152\152\153\153\154\154\155\155\156" + "\156\157\157\160\160\161\161\162\162\163\163\164\164\165\165" + "\166\166\167\167\170\170\171\171\172\172\173\173\174\174\175" + "\175\176\176\000\003\000\002\000\007\000\063\000\005\000\033" + "\133\065\155\000\064\000\005\000\033\133\061\155\000\002\000" + "\004\000\033\133\132\000\035\000\007\000\033\133\077\062\065" + "\154\000\021\000\010\000\033\133\110\033\133\062\112\000\040" + "\000\015\000\033\133\077\061\062\154\033\133\077\062\065\150" + "\000\007\000\002\000\015\000\014\000\021\000\033\133\045\151" + "\045\160\061\045\144\073\045\160\062\045\144\162\000\063\001" + "\011\000\033\133\045\160\061\045\144\104\000\036\000\002\000" + "\010\000\056\001\011\000\033\133\045\160\061\045\144\102\000" + "\033\000\002\000\012\000\065\001\011\000\033\133\045\160\061" + "\045\144\103\000\041\000\004\000\033\133\103\000\032\000\021" + "\000\033\133\045\151\045\160\061\045\144\073\045\160\062\045" + "\144\110\000\070\001\011\000\033\133\045\160\061\045\144\101" + "\000\043\000\004\000\033\133\101\000\044\000\012\000\033\133" + "\077\061\062\073\062\065\150\000\054\001\011\000\033\133\045" + "\160\061\045\144\120\000\047\000\004\000\033\133\120\000\067" + "\000\005\000\033\133\062\155\000\055\001\011\000\033\133\045" + "\160\061\045\144\115\000\050\000\004\000\033\133\115\000\122" + "\000\011\000\033\133\045\160\061\045\144\130\000\024\000\004" + "\000\033\133\112\000\023\000\004\000\033\133\113\000\022\000" + "\005\000\033\133\061\113\000\150\000\022\000\033\133\077\065" + "\150\044\074\061\060\060\057\076\033\133\077\065\154\000\034" + "\000\004\000\033\133\110\000\027\000\013\000\033\133\045\151" + "\045\160\061\045\144\107\000\166\001\002\000\011\000\153\001" + "\003\000\033\110\000\060\001\011\000\033\133\045\160\061\045" + "\144\100\000\062\001\011\000\033\133\045\160\061\045\144\114" + "\000\166\000\004\000\033\133\114\000\122\001\002\000\012\000" + "\110\000\005\000\033\133\070\155\000\157\000\022\000\033\133" + "\041\160\033\133\077\063\073\064\154\033\133\064\154\033\076" + "\000\332\000\004\000\033\133\115\000\052\001\011\000\033\133" + "\063\071\073\064\071\155\000\116\001\003\000\033\070\000\105" + "\000\005\000\033\133\067\155\000\123\001\003\000\033\115\000" + "\132\000\006\000\033\133\062\063\155\000\123\000\004\000\033" + "\050\102\000\124\000\006\000\033\133\077\067\154\000\126\000" + "\011\000\033\133\077\061\060\064\071\154\000\131\000\005\000" + "\033\133\064\154\000\016\001\010\000\033\133\077\061\154\033" + "\076\000\036\001\011\000\033\133\077\061\060\063\064\154\000" + "\140\000\006\000\033\133\062\067\155\000\143\000\006\000\033" + "\133\062\064\155\000\113\001\022\000\033\133\041\160\033\133" + "\077\063\073\064\154\033\133\064\154\033\076\000\120\001\003" + "\000\033\067\000\135\001\106\000\033\133\064\045\077\045\160" + "\061\045\173\061\175\045\075\045\164\064\045\145\045\160\061" + "\045\173\063\175\045\075\045\164\066\045\145\045\160\061\045" + "\173\064\175\045\075\045\164\061\045\145\045\160\061\045\173" + "\066\175\045\075\045\164\063\045\145\045\160\061\045\144\045" + "\073\155\000\143\001\106\000\033\133\063\045\077\045\160\061" + "\045\173\061\175\045\075\045\164\064\045\145\045\160\061\045" + "\173\063\175\045\075\045\164\066\045\145\045\160\061\045\173" + "\064\175\045\075\045\164\061\045\145\045\160\061\045\173\066" + "\175\045\075\045\164\063\045\145\045\160\061\045\144\045\073" + "\155\000\134\001\135\000\045\077\045\160\071\045\164\033\050" + "\060\045\145\033\050\102\045\073\033\133\060\045\077\045\160" + "\066\045\164\073\061\045\073\045\077\045\160\065\045\164\073" + "\062\045\073\045\077\045\160\062\045\164\073\064\045\073\045" + "\077\045\160\061\045\160\063\045\174\045\164\073\067\045\073" + "\045\077\045\160\064\045\164\073\065\045\073\045\077\045\160" + "\067\045\164\073\070\045\073\155\000\125\000\007\000\033\050" + "\102\033\133\155\000\074\000\005\000\033\133\063\155\000\061" + "\000\004\000\033\050\060\000\062\000\006\000\033\133\077\067" + "\150\000\065\000\011\000\033\133\077\061\060\064\071\150\000" + "\073\000\005\000\033\133\064\150\000\017\001\010\000\033\133" + "\077\061\150\033\075\000\037\001\011\000\033\133\077\061\060" + "\063\064\150\000\112\000\005\000\033\133\067\155\000\116\000" + "\005\000\033\133\064\155\000\017\000\005\000\033\133\063\147" + "\000\117\001\013\000\033\133\045\151\045\160\061\045\144\144" + "\000\100\001\004\000\033\133\151\000\102\001\005\000\033\133" + "\064\151\000\103\001\005\000\033\133\065\151\000\173\000\002" + "\000\010\000\053\004\077\000\004\000\153\104\116\000\163\007" + "\000\033\133\061\073\062\102\000\005\000\153\104\116\063\000" + "\163\007\000\033\133\061\073\063\102\000\005\000\153\104\116" + "\064\000\163\007\000\033\133\061\073\064\102\000\005\000\153" + "\104\116\065\000\163\007\000\033\133\061\073\065\102\000\005" + "\000\153\104\116\066\000\163\007\000\033\133\061\073\066\102" + "\000\005\000\153\104\116\067\000\163\007\000\033\133\061\073" + "\067\102\000\006\000\153\114\106\124\063\000\163\007\000\033" + "\133\061\073\063\104\000\006\000\153\114\106\124\064\000\163" + "\007\000\033\133\061\073\064\104\000\006\000\153\114\106\124" + "\065\000\163\007\000\033\133\061\073\065\104\000\006\000\153" + "\114\106\124\066\000\163\007\000\033\133\061\073\066\104\000" + "\006\000\153\114\106\124\067\000\163\007\000\033\133\061\073" + "\067\104\000\006\000\153\122\111\124\063\000\163\007\000\033" + "\133\061\073\063\103\000\006\000\153\122\111\124\064\000\163" + "\007\000\033\133\061\073\064\103\000\006\000\153\122\111\124" + "\065\000\163\007\000\033\133\061\073\065\103\000\006\000\153" + "\122\111\124\066\000\163\007\000\033\133\061\073\066\103\000" + "\006\000\153\122\111\124\067\000\163\007\000\033\133\061\073" + "\067\103\000\004\000\153\125\120\000\163\007\000\033\133\061" + "\073\062\101\000\005\000\153\125\120\063\000\163\007\000\033" + "\133\061\073\063\101\000\005\000\153\125\120\064\000\163\007" + "\000\033\133\061\073\064\101\000\005\000\153\125\120\065\000" + "\163\007\000\033\133\061\073\065\101\000\005\000\153\125\120" + "\066\000\163\007\000\033\133\061\073\066\101\000\005\000\153" + "\125\120\067\000\163\007\000\033\133\061\073\067\101\000\005" + "\000\153\104\103\063\000\163\007\000\033\133\063\073\063\176" + "\000\005\000\153\104\103\064\000\163\007\000\033\133\063\073" + "\064\176\000\005\000\153\104\103\065\000\163\007\000\033\133" + "\063\073\065\176\000\005\000\153\104\103\066\000\163\007\000" + "\033\133\063\073\066\176\000\005\000\153\104\103\067\000\163" + "\007\000\033\133\063\073\067\176\000\006\000\153\105\116\104" + "\063\000\163\007\000\033\133\061\073\063\106\000\006\000\153" + "\105\116\104\064\000\163\007\000\033\133\061\073\064\106\000" + "\006\000\153\105\116\104\065\000\163\007\000\033\133\061\073" + "\065\106\000\006\000\153\105\116\104\066\000\163\007\000\033" + "\133\061\073\066\106\000\006\000\153\105\116\104\067\000\163" + "\007\000\033\133\061\073\067\106\000\006\000\153\110\117\115" + "\063\000\163\007\000\033\133\061\073\063\110\000\006\000\153" + "\110\117\115\064\000\163\007\000\033\133\061\073\064\110\000" + "\006\000\153\110\117\115\065\000\163\007\000\033\133\061\073" + "\065\110\000\006\000\153\110\117\115\066\000\163\007\000\033" + "\133\061\073\066\110\000\006\000\153\110\117\115\067\000\163" + "\007\000\033\133\061\073\067\110\000\005\000\153\111\103\063" + "\000\163\007\000\033\133\062\073\063\176\000\005\000\153\111" + "\103\064\000\163\007\000\033\133\062\073\064\176\000\005\000" + "\153\111\103\065\000\163\007\000\033\133\062\073\065\176\000" + "\005\000\153\111\103\066\000\163\007\000\033\133\062\073\066" + "\176\000\005\000\153\111\103\067\000\163\007\000\033\133\062" + "\073\067\176\000\006\000\153\116\130\124\063\000\163\007\000" + "\033\133\066\073\063\176\000\006\000\153\116\130\124\064\000" + "\163\007\000\033\133\066\073\064\176\000\006\000\153\116\130" + "\124\065\000\163\007\000\033\133\066\073\065\176\000\006\000" + "\153\116\130\124\066\000\163\007\000\033\133\066\073\066\176" + "\000\006\000\153\116\130\124\067\000\163\007\000\033\133\066" + "\073\067\176\000\006\000\153\120\122\126\063\000\163\007\000" + "\033\133\065\073\063\176\000\006\000\153\120\122\126\064\000" + "\163\007\000\033\133\065\073\064\176\000\006\000\153\120\122" + "\126\065\000\163\007\000\033\133\065\073\065\176\000\006\000" + "\153\120\122\126\066\000\163\007\000\033\133\065\073\066\176" + "\000\006\000\153\120\122\126\067\000\163\007\000\033\133\065" + "\073\067\176\000\003\000\103\162\000\163\007\000\033\135\061" + "\061\062\007\000\003\000\103\163\000\163\014\000\033\135\061" + "\062\073\045\160\061\045\163\007\000\003\000\115\163\000\163" + "\022\000\033\135\065\062\073\045\160\061\045\163\073\045\160" + "\062\045\163\007\000\003\000\123\145\000\163\006\000\033\133" + "\062\040\161\000\003\000\123\163\000\163\012\000\033\133\045" + "\160\061\045\144\040\161\000\003\000\142\163\000\146\001\003" + "\000\101\130\000\146\001\003\000\130\124\000\146\001\005\000" + "\155\145\155\154\000\163\003\000\033\154\000\005\000\155\145" + "\155\165\000\163\003\000\033\155\000\003\000\105\063\000\163" + "\005\000\033\133\063\112\000", + 3472 + } +}; Index: contrib/mg/configure =================================================================== --- /dev/null +++ contrib/mg/configure @@ -0,0 +1,594 @@ +#!/bin/sh + +# This configure script written by Brian Callahan +# and released into the Public Domain. + +cccheck() { + if [ ! -z "$CC" ] ; then +cat << EOF > conftest.c +int main(void){return 0;} +EOF + $CC -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + ./conftest + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + cc="$CC" + return 0 + else + echo "could not build working executables" + echo "Please ensure your C compiler is a native compiler" + exit 1 + fi + else + rm -f conftest conftest.c + fi + fi + + for compiler in cc clang pcc xlc gcc ; do +cat << EOF > conftest.c +int main(void){return 0;} +EOF + + $compiler -o conftest conftest.c > /dev/null 2>&1 + + if [ $? -eq 0 ] ; then + ./conftest + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + cc="$compiler" + return 0 + else + echo "could not build working executables" + echo "Please ensure your C compiler is a native compiler" + exit 1 + fi + else + rm -f conftest conftest.c + fi + done + return 1 +} + +defaultcflagscheck() { + cat << EOF > conftest.c +int main(void){return 0;} +EOF + $cc $cflags -g -O2 -o conftest.o -c conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.o conftest.c + return 1 + else + rm -f conftest conftest.o conftest.c + return 0 + fi +} + +fparselncheck() { + cat << EOF > conftest.c +#include +#if defined(__APPLE__) +#include "apple.h" +#elif defined(__linux__) +#include "util.h" +#elif defined(__FreeBSD__) +#include +#else +#include +#endif +int main(void){fparseln(NULL,NULL,NULL,NULL,0);return 0;} +EOF + $cc $tflags -o conftest conftest.c $libs > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +fstatatcheck() { + cat << EOF > conftest.c +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200809L +#include +#include +#include +int main(void){fstatat(0, NULL, NULL, 0);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +futimenscheck() { + cat << EOF > conftest.c +#include +#include +#include +int main(void){futimens(0, NULL);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +getlinecheck() { + cat << EOF > conftest.c +#include +int main(void){getline(NULL,NULL,NULL);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +pledgecheck() { + cat << EOF > conftest.c +#include +int main(void){pledge(NULL,NULL);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +reallocarraycheck() { + cat << EOF > conftest.c +#include +int main(void){reallocarray(NULL, 0, 0);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +strlcatcheck() { + cat << EOF > conftest.c +#include +int main(void){strlcat(NULL,NULL,0);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +strlcpycheck() { + cat << EOF > conftest.c +#include +int main(void){strlcpy(NULL,NULL,0);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +strndupcheck() { + cat << EOF > conftest.c +#include +int main(void){strndup(NULL,0);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +strtonumcheck() { + cat << EOF > conftest.c +#include +int main(void){strtonum(NULL, 0, 0, NULL);return 0;} +EOF + $cc $tflags -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.c + return 0 + else + rm -f conftest conftest.c + return 1 + fi +} + +wflagcheck() { + cat << EOF > conftest.c +int main(void){return 0;} +EOF + $cc $cflags -w -o conftest conftest.c > /dev/null 2>&1 + if [ $? -eq 0 ] ; then + rm -f conftest conftest.o conftest.c + return 1 + else + rm -f conftest conftest.o conftest.c + return 0 + fi +} + +# Option variables +if [ ! -z "$PREFIX" ] ; then + prefix="$PREFIX" +else + prefix="/usr/local" +fi + +mandir="$prefix/man" + +prog="mg" +log=0 +static=0 +terminfo=0 + +# Options +for opt +do + case "$opt" in + --prefix=*) + prefix=`echo $opt | cut -d '=' -f 2` + ;; + --mandir=*) + mandir=`echo $opt | cut -d '=' -f 2` + ;; + --disable-logging|--enable-logging) + if [ "x$opt" = "x--enable-logging" ] ; then + log=1 + else + log=0 + fi + ;; + --disable-static|--enable-static) + if [ "x$opt" = "x--enable-static" ] ; then + static=1 + else + static=0 + fi + ;; + --with-builtin-curses|--without-builtin-curses) + if [ "x$opt" = "x--with-builtin-curses" ] ; then + terminfo=1 + else + terminfo=0 + fi + ;; + --help|-h) + echo "Usage: configure [options]" + echo "" + echo "Options:" + printf " --help or -h " + echo "Display this help message" + printf " --prefix=PREFIX " + echo "Top level install directory is PREFIX [$prefix]" + printf " --mandir=MANDIR " + echo "Manual pages are installed to MANDIR [$mandir]" + printf " --enable-logging " + echo "Enable run-time debugging [default=no]" + printf " --enable-static " + echo "Statically link executables [default=no]" + printf " --with-builtin-curses " + echo "Compile with builtin curses library [default=no]" + exit 1 + ;; + *) + ;; + esac +done + +if [ ! -z "$CFLAGS" ] ; then + cflags="$CFLAGS -DREGEX" +else + cflags="-DREGEX" +fi + +if [ ! -z "$LDFLAGS" ] ; then + ldflags="$LDFLAGS " +else + ldflags="" +fi + +if [ $static -ne 0 ] ; then + ldflags="${ldflags}-static" +fi + +printf "checking for C compiler... " +cccheck +if [ $? -ne 0 ] ; then + echo "not found" + echo "Please install a C compiler and re-run configure." + exit 1 +else + echo "$cc" +fi + +if [ "x$cflags" = "x-DREGEX" ] ; then + printf "checking if the compiler accepts -g -O2... " + defaultcflagscheck + if [ $? -eq 0 ] ; then + echo "no" + else + cflags="-g -O2 $cflags" + echo "yes" + fi +fi + +printf "checking for -w compiler flag... " +wflagcheck +if [ $? -eq 0 ] ; then + echo "no" +else + cflags="$cflags -w" + echo "yes" +fi + +printf "checking for OS... " + +if [ $terminfo -eq 0 ] ; then + libs="-lncursesw" +else + libs="" +fi + +os=`uname -s` +echo "$os" + +case "x$os" in + "xLinux"|"xCYGWIN"*) + cflags="$cflags -D_GNU_SOURCE -D__dead=\"__attribute__((__noreturn__))\" -Dst_mtimespec=st_mtim" + libs="$libs -lutil" + ;; + "xDarwin") + cflags="$cflags -DMSG_NOSIGNAL=SO_NOSIGPIPE -DLOGIN_NAME_MAX=MAXLOGNAME" + if [ $terminfo -eq 0 ] ; then + libs="-lncurses -lutil" + else + libs="-lutil" + fi + ;; + "xFreeBSD") + cflags="$cflags -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME" + libs="$libs -lutil" + ;; + "xOpenBSD") + libs="$libs -lutil" + ;; + "xNetBSD") + cflags="$cflags -D_OPENBSD_SOURCE" + if [ $terminfo -eq 0 ] ; then + libs="-lcurses -lutil" + else + libs="-lutil" + fi + ;; + "xDragonFly") + cflags="$cflags -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME" + libs="$libs -lutil" + ;; +esac + +cat << EOF > config.h +/* This file generated automatically by configure. */ + +#include "common.h" +#if defined(__linux__) || defined(__CYGWIN__) +#include "linux.h" +#elif defined(__APPLE__) +#include "apple.h" +#elif defined(__FreeBSD__) +#include "freebsd.h" +#elif defined(__NetBSD__) +#include "netbsd.h" +#elif defined(__DragonFly__) +#include "dragonfly.h" +#endif + +EOF + +printf "checking for fparseln... " +fparselncheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_FPARSELN" >> config.h + echo "yes" +else + echo "extern char *fparseln(FILE *, size_t *, size_t *, const char[3], int);" >> config.h + echo "no" +fi + +printf "checking for fstatat... " +fstatatcheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_FSTATAT" >> config.h + echo "yes" +else + echo "extern int fstatat(int, const char *, struct stat *, int);" >> config.h + echo "no" +fi + +printf "checking for futimens... " +futimenscheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_FUTIMENS" >> config.h + echo "yes" +else + echo "extern int futimens(int, const struct timespec[2]);" >> config.h + echo "no" +fi + +printf "checking for getline... " +getlinecheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_GETLINE" >> config.h + echo "yes" +else + echo "extern ssize_t getline(char **, size_t *, FILE *);" >> config.h + echo "no" +fi + +printf "checking for pledge... " +pledgecheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_PLEDGE" >> config.h + echo "yes" +else + echo "no" +fi + +printf "checking for reallocarray... " +reallocarraycheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_REALLOCARRAY" >> config.h + echo "yes" +else + echo "extern void *reallocarray(void *, size_t, size_t);" >> config.h + echo "no" +fi + +printf "checking for strlcat... " +strlcatcheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_STRLCAT" >> config.h + echo "yes" +else + echo "extern size_t strlcat(char *, const char *, size_t);" >> config.h + echo "no" +fi + +printf "checking for strlcpy... " +strlcpycheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_STRLCPY" >> config.h + echo "yes" +else + echo "extern size_t strlcpy(char *, const char *, size_t);" >> config.h + echo "no" +fi + +printf "checking for strndup... " +strndupcheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_STRNDUP" >> config.h + echo "yes" +else + echo "extern char *strndup(const char *, size_t);" >> config.h + echo "no" +fi + +printf "checking for strtonum... " +strtonumcheck +if [ $? -eq 0 ] ; then + echo "#define HAVE_STRTONUM" >> config.h + echo "yes" +else + echo "extern long long strtonum(const char *, long long, long long, const char **);" >> config.h + echo "no" +fi + +printf "creating Makefile... " +cat << EOF > Makefile +# This Makefile automatically generated by configure. +.POSIX: + +CC = $cc +CFLAGS = $cflags +EOF + +if [ ! -z "$ldflags" ] ; then +cat << EOF >> Makefile +LDFLAGS = $ldflags +EOF +fi + +cat << EOF >> Makefile +PREFIX = $prefix +MANDIR = $mandir + +PROG = $prog +OBJS = autoexec.o basic.o bell.o buffer.o cinfo.o dir.o display.o \\ + echo.o extend.o file.o fileio.o funmap.o help.o kbd.o keymap.o \\ + line.o macro.o main.o match.o modes.o paragraph.o re_search.o \\ + region.o search.o spawn.o tty.o ttyio.o ttykbd.o undo.o util.o \\ + version.o window.o word.o yank.o cmode.o cscope.o dired.o \\ + grep.o tags.o fparseln.o fstatat.o futimens.o getline.o \\ + reallocarray.o strlcat.o strlcpy.o strndup.o strtonum.o \\ + interpreter.o extensions.o + +EOF + +if [ $terminfo -ne 0 ] ; then +cat << EOF >> Makefile +OBJS += term.o ti.o setupterm.o curterm.o tparm.o tputs.o \\ + compile.o hash.o termcap.o mi_vector_hash.o cdbr.o + +EOF +sed -i.bak 's,,"terminfo_term.h",g' display.c echo.c spawn.c tty.c \ + ttyio.c ttykbd.c +else +sed -i.bak 's,"terminfo_term.h",,g' display.c echo.c spawn.c tty.c \ + ttyio.c ttykbd.c +fi + +if [ $log -ne 0 ] ; then +cat << EOF >> Makefile +OBJS += log.o + +EOF +fi + +cat << EOF >> Makefile +all: \${PROG} + +\${PROG}: \${OBJS} + \${CC} \${LDFLAGS} -o \${PROG} \${OBJS} $libs + +install: + install -d \${DESTDIR}\${PREFIX}/bin + install -d \${DESTDIR}\${MANDIR}/man1 + install -c -s -m 755 \${PROG} \${DESTDIR}\${PREFIX}/bin + install -c -m 644 mg.1 \${DESTDIR}\${MANDIR}/man1/\${PROG}.1 + +test: + echo "No tests" + +clean: + rm -f \${PROG} \${OBJS} + +distclean: clean + rm -f Makefile config.h *.bak +EOF +echo "done" Index: contrib/mg/cscope.c =================================================================== --- /dev/null +++ contrib/mg/cscope.c @@ -0,0 +1,611 @@ +/* $OpenBSD: cscope.c,v 1.20 2021/03/01 10:51:14 lum Exp $ */ + +/* + * This file is in the public domain. + * + * Author: Sunil Nimmagadda + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +#define CSSYMBOL 0 +#define CSDEFINITION 1 +#define CSCALLEDFUNCS 2 +#define CSCALLERFUNCS 3 +#define CSTEXT 4 +#define CSEGREP 6 +#define CSFINDFILE 7 +#define CSINCLUDES 8 + +struct cstokens { + const char *fname; + const char *function; + const char *lineno; + const char *pattern; +}; + +struct csmatch { + TAILQ_ENTRY(csmatch) entry; + int lineno; +}; + +struct csrecord { + TAILQ_ENTRY(csrecord) entry; + char *filename; + TAILQ_HEAD(matches, csmatch) matches; +}; + +static TAILQ_HEAD(csrecords, csrecord) csrecords = TAILQ_HEAD_INITIALIZER(csrecords); +static struct csrecord *addentryr; +static struct csrecord *currecord; +static struct csmatch *curmatch; +static const char *addentryfn; +static const char *csprompt[] = { + "Find this symbol: ", + "Find this global definition: ", + "Find functions called by this function: ", + "Find functions calling this function: ", + "Find this text string: ", + "Change this text string: ", + "Find this egrep pattern: ", + "Find this file: ", + "Find files #including this file: " +}; + +static int addentry(struct buffer *, char *); +static void csflush(void); +static int do_cscope(int); +static int csexists(const char *); +static int getattr(char *, struct cstokens *); +static int jumptomatch(void); +static void prettyprint(struct buffer *, struct cstokens *); +static const char *ltrim(const char *); + +/* + * Find this symbol. Bound to C-c s s + */ +/* ARGSUSED */ +int +cssymbol(int f, int n) +{ + return (do_cscope(CSSYMBOL)); +} + +/* + * Find this global definition. Bound to C-c s d + */ +/* ARGSUSED */int +csdefinition(int f, int n) +{ + return (do_cscope(CSDEFINITION)); +} + +/* + * Find functions called by this function. Bound to C-c s l + */ +/* ARGSUSED */ +int +csfuncalled(int f, int n) +{ + return (do_cscope(CSCALLEDFUNCS)); +} + +/* + * Find functions calling this function. Bound to C-c s c + */ +/* ARGSUSED */ +int +cscallerfuncs(int f, int n) +{ + return (do_cscope(CSCALLERFUNCS)); +} + +/* + * Find this text. Bound to C-c s t + */ +/* ARGSUSED */ +int +csfindtext(int f, int n) +{ + return (do_cscope(CSTEXT)); +} + +/* + * Find this egrep pattern. Bound to C-c s e + */ +/* ARGSUSED */ +int +csegrep(int f, int n) +{ + return (do_cscope(CSEGREP)); +} + +/* + * Find this file. Bound to C-c s f + */ +/* ARGSUSED */ +int +csfindfile(int f, int n) +{ + return (do_cscope(CSFINDFILE)); +} + +/* + * Find files #including this file. Bound to C-c s i + */ +/* ARGSUSED */ +int +csfindinc(int f, int n) +{ + return (do_cscope(CSINCLUDES)); +} + +/* + * Create list of files to index in the given directory + * using cscope-indexer. + */ +/* ARGSUSED */ +int +cscreatelist(int f, int n) +{ + struct buffer *bp; + struct stat sb; + FILE *fpipe; + char dir[NFILEN], cmd[BUFSIZ], title[BUFSIZ], *line, *bufp; + size_t sz; + ssize_t len; + int clen; + + line = NULL; + sz = 0; + + if (getbufcwd(dir, sizeof(dir)) == FALSE) + dir[0] = '\0'; + + bufp = eread("Index files in directory: ", dir, + sizeof(dir), EFCR | EFDEF | EFNEW | EFNUL); + + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + + if (stat(dir, &sb) == -1) + return(dobeep_msgs("stat: %s", strerror(errno))); + else if (S_ISDIR(sb.st_mode) == 0) + return(dobeep_msgs("%s: Not a directory", dir)); + + if (csexists("cscope-indexer") == FALSE) + return(dobeep_msg("no such file or directory, cscope-indexer")); + + clen = snprintf(cmd, sizeof(cmd), "cscope-indexer -v %s", dir); + if (clen < 0 || clen >= sizeof(cmd)) + return (FALSE); + + if ((fpipe = popen(cmd, "r")) == NULL) + return(dobeep_msg("problem opening pipe")); + + bp = bfind("*cscope*", TRUE); + if (bclear(bp) != TRUE) { + pclose(fpipe); + return (FALSE); + } + bp->b_flag |= BFREADONLY; + + clen = snprintf(title, sizeof(title), "%s%s", + "Creating cscope file list 'cscope.files' in: ", dir); + if (clen < 0 || clen >= sizeof(title)) { + pclose(fpipe); + return (FALSE); + } + addline(bp, title); + addline(bp, ""); + while ((len = getline(&line, &sz, fpipe)) != -1) { + if (line[len - 1] == *bp->b_nlchr) + line[len - 1] = '\0'; + addline(bp, line); + } + free(line); + if (ferror(fpipe)) + ewprintf("Problem reading pipe"); + pclose(fpipe); + return (popbuftop(bp, WNONE)); +} + +/* + * Next Symbol. Bound to C-c s n + */ +/* ARGSUSED */ +int +csnextmatch(int f, int n) +{ + struct csrecord *r; + struct csmatch *m; + + if (curmatch == NULL) { + if ((r = TAILQ_FIRST(&csrecords)) == NULL) + return(dobeep_msg("The *cscope* buffer does " + "not exist yet")); + + currecord = r; + curmatch = TAILQ_FIRST(&r->matches); + } else { + m = TAILQ_NEXT(curmatch, entry); + if (m == NULL) { + r = TAILQ_NEXT(currecord, entry); + if (r == NULL) { + return(dobeep_msg("The end of *cscope* buffer " + "has been reached")); + } else { + currecord = r; + curmatch = TAILQ_FIRST(&currecord->matches); + } + } else + curmatch = m; + } + return (jumptomatch()); +} + +/* + * Previous Symbol. Bound to C-c s p + */ +/* ARGSUSED */ +int +csprevmatch(int f, int n) +{ + struct csmatch *m; + struct csrecord *r; + + if (curmatch == NULL) + return (FALSE); + else { + m = TAILQ_PREV(curmatch, matches, entry); + if (m) + curmatch = m; + else { + r = TAILQ_PREV(currecord, csrecords, entry); + if (r == NULL) { + return(dobeep_msg("The beginning of *cscope* " + "buffer has been reached")); + } else { + currecord = r; + curmatch = TAILQ_LAST(&currecord->matches, + matches); + } + } + } + return (jumptomatch()); +} + +/* + * Next file. + */ +int +csnextfile(int f, int n) +{ + struct csrecord *r; + + if (curmatch == NULL) { + if ((r = TAILQ_FIRST(&csrecords)) == NULL) + return(dobeep_msg("The *cscope* buffer does not " + "exist yet")); + } else { + if ((r = TAILQ_NEXT(currecord, entry)) == NULL) + return(dobeep_msg("The end of *cscope* buffer has " + "been reached")); + } + currecord = r; + curmatch = TAILQ_FIRST(&currecord->matches); + return (jumptomatch()); +} + +/* + * Previous file. + */ +int +csprevfile(int f, int n) +{ + struct csrecord *r; + + if (curmatch == NULL) { + if ((r = TAILQ_FIRST(&csrecords)) == NULL) + return(dobeep_msg("The *cscope* buffer does not" + "exist yet")); + } else { + if ((r = TAILQ_PREV(currecord, csrecords, entry)) == NULL) + return(dobeep_msg("The beginning of *cscope* buffer " + "has been reached")); + } + currecord = r; + curmatch = TAILQ_FIRST(&currecord->matches); + return (jumptomatch()); +} + +/* + * The current symbol location is extracted from currecord->filename and + * curmatch->lineno. Load the file similar to filevisit and goto the + * lineno recorded. + */ +int +jumptomatch(void) +{ + struct buffer *bp; + char *adjf; + + if (curmatch == NULL || currecord == NULL) + return (FALSE); + adjf = adjustname(currecord->filename, TRUE); + if (adjf == NULL) + return (FALSE); + if ((bp = findbuffer(adjf)) == NULL) + return (FALSE); + curbp = bp; + if (showbuffer(bp, curwp, WFFULL) != TRUE) + return (FALSE); + if (bp->b_fname[0] == '\0') { + if (readin(adjf) != TRUE) + killbuffer(bp); + } + gotoline(FFARG, curmatch->lineno); + return (TRUE); +} + +/* + * Ask for the symbol, construct cscope commandline with the symbol + * and passed in index. Popen cscope, read the output into *cscope* + * buffer and pop it. + */ +int +do_cscope(int i) +{ + struct buffer *bp; + FILE *fpipe; + char pattern[MAX_TOKEN], cmd[BUFSIZ], title[BUFSIZ]; + char *p, *buf; + int clen, nores = 0; + size_t sz; + ssize_t len; + + buf = NULL; + sz = 0; + + /* If current buffer isn't a source file just return */ + if (fnmatch("*.[chy]", curbp->b_fname, 0) != 0) + return(dobeep_msg("C-c s not defined")); + + if (curtoken(0, 1, pattern) == FALSE) + return (FALSE); + p = eread("%s", pattern, MAX_TOKEN, EFNEW | EFCR | EFDEF, csprompt[i]); + if (p == NULL) + return (ABORT); + else if (p[0] == '\0') + return (FALSE); + + if (csexists("cscope") == FALSE) + return(dobeep_msg("no such file or directory, cscope")); + + csflush(); + clen = snprintf(cmd, sizeof(cmd), "cscope -L -%d %s 2>/dev/null", + i, pattern); + if (clen < 0 || clen >= sizeof(cmd)) + return (FALSE); + + if ((fpipe = popen(cmd, "r")) == NULL) + return(dobeep_msg("problem opening pipe")); + + bp = bfind("*cscope*", TRUE); + if (bclear(bp) != TRUE) { + pclose(fpipe); + return (FALSE); + } + bp->b_flag |= BFREADONLY; + + clen = snprintf(title, sizeof(title), "%s%s", csprompt[i], pattern); + if (clen < 0 || clen >= sizeof(title)) { + pclose(fpipe); + return (FALSE); + } + addline(bp, title); + addline(bp, ""); + addline(bp, "-------------------------------------------------------------------------------"); + while ((len = getline(&buf, &sz, fpipe)) != -1) { + if (buf[len - 1] == *bp->b_nlchr) + buf[len - 1] = '\0'; + if (addentry(bp, buf) != TRUE) { + free(buf); + return (FALSE); + } + nores = 1; + } + free(buf); + if (ferror(fpipe)) + ewprintf("Problem reading pipe"); + pclose(fpipe); + addline(bp, "-------------------------------------------------------------------------------"); + if (nores == 0) + ewprintf("No matches were found."); + return (popbuftop(bp, WNONE)); +} + +/* + * For each line read from cscope output, extract the tokens, + * add them to list and pretty print a line in *cscope* buffer. + */ +int +addentry(struct buffer *bp, char *csline) +{ + struct csrecord *r; + struct csmatch *m; + struct cstokens t; + int lineno; + char buf[BUFSIZ]; + const char *errstr; + + r = NULL; + if (getattr(csline, &t) == FALSE) + return (FALSE); + + lineno = strtonum(t.lineno, INT_MIN, INT_MAX, &errstr); + if (errstr) + return (FALSE); + + if (addentryfn == NULL || strcmp(addentryfn, t.fname) != 0) { + if ((r = malloc(sizeof(struct csrecord))) == NULL) + return (FALSE); + addentryr = r; + if ((r->filename = strndup(t.fname, NFILEN)) == NULL) + goto cleanup; + addentryfn = r->filename; + TAILQ_INIT(&r->matches); + if ((m = malloc(sizeof(struct csmatch))) == NULL) + goto cleanup; + m->lineno = lineno; + TAILQ_INSERT_TAIL(&r->matches, m, entry); + TAILQ_INSERT_TAIL(&csrecords, r, entry); + addline(bp, ""); + if (snprintf(buf, sizeof(buf), "*** %s", t.fname) < 0) + goto cleanup; + addline(bp, buf); + } else { + if ((m = malloc(sizeof(struct csmatch))) == NULL) + goto cleanup; + m->lineno = lineno; + TAILQ_INSERT_TAIL(&addentryr->matches, m, entry); + } + prettyprint(bp, &t); + return (TRUE); +cleanup: + free(r); + return (FALSE); +} + +/* + * Cscope line: + */ +int +getattr(char *line, struct cstokens *t) +{ + char *p; + + if ((p = strchr(line, ' ')) == NULL) + return (FALSE); + *p++ = '\0'; + t->fname = line; + line = p; + + if ((p = strchr(line, ' ')) == NULL) + return (FALSE); + *p++ = '\0'; + t->function = line; + line = p; + + if ((p = strchr(line, ' ')) == NULL) + return (FALSE); + *p++ = '\0'; + t->lineno = line; + + if (*p == '\0') + return (FALSE); + t->pattern = p; + + return (TRUE); +} + +void +prettyprint(struct buffer *bp, struct cstokens *t) +{ + char buf[BUFSIZ]; + + if (snprintf(buf, sizeof(buf), "%s[%s]\t\t%s", + t->function, t->lineno, ltrim(t->pattern)) < 0) + return; + addline(bp, buf); +} + +const char * +ltrim(const char *s) +{ + while (isblank((unsigned char)*s)) + s++; + return s; +} + +void +csflush(void) +{ + struct csrecord *r; + struct csmatch *m; + + while ((r = TAILQ_FIRST(&csrecords)) != NULL) { + free(r->filename); + while ((m = TAILQ_FIRST(&r->matches)) != NULL) { + TAILQ_REMOVE(&r->matches, m, entry); + free(m); + } + TAILQ_REMOVE(&csrecords, r, entry); + free(r); + } + addentryr = NULL; + addentryfn = NULL; + currecord = NULL; + curmatch = NULL; +} + +/* + * Check if the cmd exists in $PATH. Split on ":" and iterate through + * all paths in $PATH. + */ +int +csexists(const char *cmd) +{ + char fname[NFILEN], *dir, *path, *pathc, *tmp; + int len, dlen; + + /* Special case if prog contains '/' */ + if (strchr(cmd, '/')) { + if (access(cmd, F_OK) == -1) + return (FALSE); + else + return (TRUE); + } + if ((tmp = getenv("PATH")) == NULL) + return (FALSE); + if ((pathc = path = strndup(tmp, NFILEN)) == NULL) + return(dobeep_msg("out of memory")); + + while ((dir = strsep(&path, ":")) != NULL) { + if (*dir == '\0') + continue; + + dlen = strlen(dir); + while (dlen > 0 && dir[dlen-1] == '/') + dir[--dlen] = '\0'; /* strip trailing '/' */ + + len = snprintf(fname, sizeof(fname), "%s/%s", dir, cmd); + if (len < 0 || len >= sizeof(fname)) { + (void)dobeep_msg("path too long"); + goto cleanup; + } + if(access(fname, F_OK) == 0) { + free(pathc); + return (TRUE); + } + } +cleanup: + free(pathc); + return (FALSE); +} Index: contrib/mg/curterm.c =================================================================== --- /dev/null +++ contrib/mg/curterm.c @@ -0,0 +1,170 @@ +/* $NetBSD: curterm.c,v 1.13 2017/05/04 09:42:23 roy Exp $ */ + +/* + * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +#ifndef __UNCONST +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif + +TERMINAL *cur_term; + +/* + * There is no standard way of getting a list of aliases for the + * terminal. However, some applications such as telnet want to know this. + * ncurses dumps the terminfo header into an undefined variable ttytype + * and these applications then parse it to work out the aliases. + * We should do the same for now, until a standard mechanism for getting + * the information is available or the need for it goes away. + */ +#define NAMESIZE 256 +char ttytype[NAMESIZE]; + +static const speed_t bauds[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 2400, 4800, 9600, + 19200, 38400, 57600, 115200, 230400, 460800, 921600 +}; + +void +_ti_setospeed(TERMINAL *term) +{ + struct termios termios; + speed_t os; + size_t i; + + term->_ospeed = 0; + if (tcgetattr(term->fildes, &termios) == 0) { + os = cfgetospeed(&termios); + for (i = 0; i < __arraycount(bauds); i++) + if (bauds[i] == os) { + term->_ospeed = (short)i; + break; + } + } +} + +TERMINAL * +set_curterm(TERMINAL *nterm) +{ + TERMINAL *oterm; + size_t l, n; + char *p; + + oterm = cur_term; + cur_term = nterm; + + ospeed = 0; + if (cur_term == NULL) + PC = '\0'; + else { + if (pad_char == NULL) + PC = '\0'; + else + PC = *pad_char; + _ti_setospeed(nterm); + ospeed = nterm->_ospeed; + + p = ttytype; + l = sizeof(ttytype); + if ((n = strlcpy(p, nterm->name, l)) == strlen(p)) { + p += n; + l -= n; + *p++ = '|'; + l--; + if (nterm->_alias && + (n = strlcpy(p, nterm->_alias, l)) == strlen(p)) + { + p += n; + l -= n; + *p++ = '|'; + l--; + } + if (nterm->desc && + (n = strlcpy(p, nterm->desc, l)) == strlen(p)) + { + p += n; + l -= n; + *p++ = '|'; + l--; + } + p--; + } + *p = '\0'; + } + + return oterm; +} + +int +del_curterm(TERMINAL *oterm) +{ + + if (oterm == NULL) + return ERR; + free(oterm->_area); + free(oterm->strs); + free(oterm->nums); + free(oterm->flags); + free(oterm->_userdefs); + free(oterm->_buf); + free(oterm); + if (oterm == cur_term) + cur_term = NULL; + return OK; +} + +char * +termname(void) +{ + + return __UNCONST(cur_term->name); +} + +static const char * nullname = ""; + +char * +longname(void) +{ + + if (cur_term->desc == NULL) + return __UNCONST(nullname); + return __UNCONST(cur_term->desc); +} Index: contrib/mg/def.h =================================================================== --- /dev/null +++ contrib/mg/def.h @@ -0,0 +1,788 @@ +/* $OpenBSD: def.h,v 1.176 2021/05/06 14:16:12 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * This file is the general header file for all parts + * of the Mg display editor. It contains all of the + * general definitions and macros. It also contains some + * conditional compilation flags. All of the per-system and + * per-terminal definitions are in special header files. + */ + +/* + * We open with Mg portable things. + */ +#include "config.h" + +#include "chrdef.h" + +typedef int (*PF)(int, int); /* generally useful type */ + +/* + * Table sizes, etc. + */ +#define NFILEN 1024 /* Length, file name. */ +#define NBUFN NFILEN /* Length, buffer name. */ +#define NLINE 256 /* Length, line. */ +#define PBMODES 4 /* modes per buffer */ +#define NPAT 80 /* Length, pattern. */ +#define HUGE 1000 /* A rather large number. */ +#define NSRCH 128 /* Undoable search commands. */ +#define NXNAME 64 /* Length, extended command. */ +#define NKNAME 20 /* Length, key names. */ +#define NTIME 50 /* Length, timestamp string. */ + +/* + * Universal. + */ +#define FALSE 0 /* False, no, bad, etc. */ +#define TRUE 1 /* True, yes, good, etc. */ +#define ABORT 2 /* Death, ^G, abort, etc. */ +#define UERROR 3 /* User Error. */ +#define REVERT 4 /* Revert the buffer */ + +#define KCLEAR 2 /* clear echo area */ + +/* + * These flag bits keep track of + * some aspects of the last command. The CFCPCN + * flag controls goal column setting. The CFKILL + * flag controls the clearing versus appending + * of data in the kill buffer. + */ +#define CFCPCN 0x0001 /* Last command was C-p or C-n */ +#define CFKILL 0x0002 /* Last command was a kill */ +#define CFINS 0x0004 /* Last command was self-insert */ + +/* + * File I/O. + */ +#define FIOSUC 0 /* Success. */ +#define FIOFNF 1 /* File not found. */ +#define FIOEOF 2 /* End of file. */ +#define FIOERR 3 /* Error. */ +#define FIOLONG 4 /* long line partially read */ +#define FIODIR 5 /* File is a directory */ + +/* + * Display colors. + */ +#define CNONE 0 /* Unknown color. */ +#define CTEXT 1 /* Text color. */ +#define CMODE 2 /* Mode line color. */ + +/* + * Flags for keyboard invoked functions. + */ +#define FFUNIV 1 /* universal argument */ +#define FFNEGARG 2 /* negative only argument */ +#define FFOTHARG 4 /* other argument */ +#define FFARG 7 /* any argument */ +#define FFRAND 8 /* Called by other function */ + +/* + * Flags for "eread". + */ +#define EFFUNC 0x0001 /* Autocomplete functions. */ +#define EFBUF 0x0002 /* Autocomplete buffers. */ +#define EFFILE 0x0004 /* " files (maybe someday) */ +#define EFAUTO 0x0007 /* Some autocompletion on */ +#define EFNEW 0x0008 /* New prompt. */ +#define EFCR 0x0010 /* Echo CR at end; last read. */ +#define EFDEF 0x0020 /* buffer contains default args */ +#define EFNUL 0x0040 /* Null Minibuffer OK */ + +/* + * Direction of insert into kill ring + */ +#define KNONE 0x00 +#define KFORW 0x01 /* forward insert into kill ring */ +#define KBACK 0x02 /* Backwards insert into kill ring */ +#define KREG 0x04 /* This is a region-based kill */ + +#define MAX_TOKEN 64 + +#define BUFSIZE 128 /* Size of line contents in extend.c */ +/* + * Previously from sysdef.h + */ +typedef int RSIZE; /* Type for file/region sizes */ +typedef short KCHAR; /* Type for internal keystrokes */ + +/* + * This structure holds the starting position + * (as a line/offset pair) and the number of characters in a + * region of a buffer. This makes passing the specification + * of a region around a little bit easier. + */ +struct region { + struct line *r_linep; /* Origin line address. */ + int r_offset; /* Origin line offset. */ + int r_lineno; /* Origin line number */ + RSIZE r_size; /* Length in characters. */ +}; + + +/* + * All text is kept in circularly linked + * lists of "line" structures. These begin at the + * header line (which is the blank line beyond the + * end of the buffer). This line is pointed to by + * the "buffer" structure. Each line contains the number of + * bytes in the line (the "used" size), the size + * of the text array, and the text. The end of line + * is not stored as a byte; it's implied. Future + * additions will include update hints, and a + * list of marks into the line. + */ +struct line { + struct line *l_fp; /* Link to the next line */ + struct line *l_bp; /* Link to the previous line */ + int l_size; /* Allocated size */ + int l_used; /* Used size */ + char *l_text; /* Content of the line */ +}; + +/* + * The rationale behind these macros is that you + * could (with some editing, like changing the type of a line + * link from a "struct line *" to a "REFLINE", and fixing the commands + * like file reading that break the rules) change the actual + * storage representation of lines to use something fancy on + * machines with small address spaces. + */ +#define lforw(lp) ((lp)->l_fp) +#define lback(lp) ((lp)->l_bp) +#define lgetc(lp, n) (CHARMASK((lp)->l_text[(n)])) +#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c)) +#define llength(lp) ((lp)->l_used) +#define ltext(lp) ((lp)->l_text) + +/* + * All repeated structures are kept as linked lists of structures. + * All of these start with a LIST structure (except lines, which + * have their own abstraction). This will allow for + * later conversion to generic list manipulation routines should + * I decide to do that. It does mean that there are four extra + * bytes per window. I feel that this is an acceptable price, + * considering that there are usually only one or two windows. + */ +struct list { + union { + struct mgwin *l_wp; + struct buffer *x_bp; /* l_bp is used by struct line */ + struct list *l_nxt; + } l_p; + char *l_name; +}; + +/* + * Usual hack - to keep from uglifying the code with lotsa + * references through the union, we #define something for it. + */ +#define l_next l_p.l_nxt + +/* + * There is a window structure allocated for + * every active display window. The windows are kept in a + * big list, in top to bottom screen order, with the listhead at + * "wheadp". Each window contains its own values of dot and mark. + * The flag field contains some bits that are set by commands + * to guide redisplay; although this is a bit of a compromise in + * terms of decoupling, the full blown redisplay is just too + * expensive to run for every input character. + */ +struct mgwin { + struct list w_list; /* List header */ + struct buffer *w_bufp; /* Buffer displayed in window */ + struct line *w_linep; /* Top line in the window */ + struct line *w_dotp; /* Line containing "." */ + struct line *w_markp; /* Line containing "mark" */ + int w_doto; /* Byte offset for "." */ + int w_marko; /* Byte offset for "mark" */ + int w_toprow; /* Origin 0 top row of window */ + int w_ntrows; /* # of rows of text in window */ + int w_frame; /* #lines to reframe by. */ + char w_rflag; /* Redisplay Flags. */ + char w_flag; /* Flags. */ + struct line *w_wrapline; + int w_dotline; /* current line number of dot */ + int w_markline; /* current line number of mark */ +}; +#define w_wndp w_list.l_p.l_wp +#define w_name w_list.l_name + +/* + * Window redisplay flags are set by command processors to + * tell the display system what has happened to the buffer + * mapped by the window. Setting "WFFULL" is always a safe thing + * to do, but it may do more work than is necessary. Always try + * to set the simplest action that achieves the required update. + * Because commands set bits in the "w_flag", update will see + * all change flags, and do the most general one. + */ +#define WFFRAME 0x01 /* Force reframe. */ +#define WFMOVE 0x02 /* Movement from line to line. */ +#define WFEDIT 0x04 /* Editing within a line. */ +#define WFFULL 0x08 /* Do a full display. */ +#define WFMODE 0x10 /* Update mode line. */ + +/* + * Window flags + */ +#define WNONE 0x00 /* No special window options. */ +#define WEPHEM 0x01 /* Window is ephemeral. */ + +struct undo_rec; +TAILQ_HEAD(undoq, undo_rec); + +/* + * Previously from sysdef.h + * Only used in struct buffer. + */ +struct fileinfo { + uid_t fi_uid; + gid_t fi_gid; + mode_t fi_mode; + struct timespec fi_mtime; /* Last modified time */ +}; + +/* + * Text is kept in buffers. A buffer header, described + * below, exists for every buffer in the system. The buffers are + * kept in a big list, so that commands that search for a buffer by + * name can find the buffer header. There is a safe store for the + * dot and mark in the header, but this is only valid if the buffer + * is not being displayed (that is, if "b_nwnd" is 0). The text for + * the buffer is kept in a circularly linked list of lines, with + * a pointer to the header line in "b_headp". + */ +struct buffer { + struct list b_list; /* buffer list pointer */ + struct buffer *b_altb; /* Link to alternate buffer */ + struct line *b_dotp; /* Link to "." line structure */ + struct line *b_markp; /* ditto for mark */ + struct line *b_headp; /* Link to the header line */ + struct maps_s *b_modes[PBMODES]; /* buffer modes */ + int b_doto; /* Offset of "." in above line */ + int b_marko; /* ditto for the "mark" */ + short b_nmodes; /* number of non-fundamental modes */ + char b_nwnd; /* Count of windows on buffer */ + char b_flag; /* Flags */ + char b_fname[NFILEN]; /* File name */ + char b_cwd[NFILEN]; /* working directory */ + char *b_nlseq; /* Newline sequence of chars */ + char *b_nlchr; /* 1st newline character */ + struct fileinfo b_fi; /* File attributes */ + struct undoq b_undo; /* Undo actions list */ + struct undo_rec *b_undoptr; + int b_dotline; /* Line number of dot */ + int b_markline; /* Line number of mark */ + int b_lines; /* Number of lines in file */ +}; +#define b_bufp b_list.l_p.x_bp +#define b_bname b_list.l_name + +/* Some helper macros, in case they ever change to functions */ +#define bfirstlp(buf) (lforw((buf)->b_headp)) +#define blastlp(buf) (lback((buf)->b_headp)) + +#define BFCHG 0x01 /* Changed. */ +#define BFBAK 0x02 /* Need to make a backup. */ +#ifdef NOTAB +#define BFNOTAB 0x04 /* no tab mode */ +#endif +#define BFOVERWRITE 0x08 /* overwrite mode */ +#define BFREADONLY 0x10 /* read only mode */ +#define BFDIRTY 0x20 /* Buffer was modified elsewhere */ +#define BFIGNDIRTY 0x40 /* Ignore modifications */ +#define BFDIREDDEL 0x80 /* Dired has a deleted 'D' file */ +/* + * This structure holds information about recent actions for the Undo command. + */ +struct undo_rec { + TAILQ_ENTRY(undo_rec) next; + enum { + INSERT = 1, + DELETE, + BOUNDARY, + MODIFIED, + DELREG + } type; + struct region region; + int pos; + char *content; +}; + +/* + * Variable structure. + */ +struct varentry { + SLIST_ENTRY(varentry) entry; + char v_buf[BUFSIZE]; + char *v_name; + char *v_vals; + int v_count; +}; +SLIST_HEAD(vhead, varentry); + +/* + * Previously from ttydef.h + */ +#define STANDOUT_GLITCH /* possible standout glitch */ + +#define putpad(str, num) tputs(str, num, ttputc) + +#define KFIRST K00 +#define KLAST K00 + +/* + * Prototypes. + */ + +/* tty.c X */ +void ttinit(void); +void ttreinit(void); +void tttidy(void); +void ttmove(int, int); +void tteeol(void); +void tteeop(void); +void ttbeep(void); +void ttinsl(int, int, int); +void ttdell(int, int, int); +void ttwindow(int, int); +void ttnowindow(void); +void ttcolor(int); +void ttresize(void); + +extern volatile sig_atomic_t winch_flag; + +/* ttyio.c */ +void ttopen(void); +int ttraw(void); +void ttclose(void); +int ttcooked(void); +int ttputc(int); +void ttflush(void); +int ttgetc(void); +int ttwait(int); +int charswaiting(void); + +/* dir.c */ +void dirinit(void); +int changedir(int, int); +int showcwdir(int, int); +int getcwdir(char *, size_t); +int makedir(int, int); +int do_makedir(char *); +int ask_makedir(void); + +/* dired.c */ +struct buffer *dired_(char *); +int dired_jump(int, int); +int do_dired(char *); + +/* file.c X */ +int fileinsert(int, int); +int filevisit(int, int); +int filevisitalt(int, int); +int filevisitro(int, int); +int poptofile(int, int); +int readin(char *); +int insertfile(char *, char *, int); +int filewrite(int, int); +int filesave(int, int); +int buffsave(struct buffer *); +int makebkfile(int, int); +int writeout(FILE **, struct buffer *, char *); +void upmodes(struct buffer *); +size_t xbasename(char *, const char *, size_t); +int do_filevisitalt(char *); + +/* line.c X */ +struct line *lalloc(int); +int lrealloc(struct line *, int); +void lfree(struct line *); +void lchange(int); +int linsert(int, int); +int lnewline_at(struct line *, int); +int lnewline(void); +int ldelete(RSIZE, int); +int ldelnewline(void); +int lreplace(RSIZE, char *); +char * linetostr(const struct line *); +int setcasereplace(int, int); + +/* yank.c X */ + +void kdelete(void); +int kinsert(int, int); +int kremove(int); +int kchunk(char *, RSIZE, int); +int killline(int, int); +int yank(int, int); + +/* window.c X */ +struct mgwin *new_window(struct buffer *); +int reposition(int, int); +int redraw(int, int); +int do_redraw(int, int, int); +int nextwind(int, int); +int prevwind(int, int); +int onlywind(int, int); +int splitwind(int, int); +int enlargewind(int, int); +int shrinkwind(int, int); +int delwind(int, int); + +/* buffer.c */ +int togglereadonly(int, int); +int togglereadonlyall(int, int); +struct buffer *bfind(const char *, int); +int poptobuffer(int, int); +int killbuffer(struct buffer *); +int killbuffer_cmd(int, int); +int savebuffers(int, int); +int listbuffers(int, int); +int addlinef(struct buffer *, char *, ...); +#define addline(bp, text) addlinef(bp, "%s", text) +int anycb(int); +int bclear(struct buffer *); +int showbuffer(struct buffer *, struct mgwin *, int); +int augbname(char *, const char *, size_t); +struct mgwin *popbuf(struct buffer *, int); +int bufferinsert(int, int); +int usebuffer(int, int); +int notmodified(int, int); +int popbuftop(struct buffer *, int); +int getbufcwd(char *, size_t); +int checkdirty(struct buffer *); +int revertbuffer(int, int); +int dorevert(void); +int diffbuffer(int, int); +struct buffer *findbuffer(char *); + +/* display.c */ +int vtresize(int, int, int); +void vtinit(void); +void vttidy(void); +void update(int); +int linenotoggle(int, int); +int colnotoggle(int, int); + +/* echo.c X */ +void eerase(void); +int eyorn(const char *); +int eynorr(const char *); +int eyesno(const char *); +void ewprintf(const char *fmt, ...); +char *eread(const char *, char *, size_t, int, ...) + __attribute__((__format__ (printf, 1, 5))); +int getxtra(struct list *, struct list *, int, int); +void free_file_list(struct list *); + +/* fileio.c */ +int ffropen(FILE **, const char *, struct buffer *); +void ffstat(FILE *, struct buffer *); +int ffwopen(FILE **, const char *, struct buffer *); +int ffclose(FILE *, struct buffer *); +int ffputbuf(FILE *, struct buffer *, int); +int ffgetline(FILE *, char *, int, int *); +int fbackupfile(const char *); +char *adjustname(const char *, int); +char *startupfile(char *, char *); +int copy(char *, char *); +struct list *make_file_list(char *); +int fisdir(const char *); +int fchecktime(struct buffer *); +int fupdstat(struct buffer *); +int backuptohomedir(int, int); +int toggleleavetmp(int, int); +char *expandtilde(const char *); + +/* kbd.c X */ +int do_meta(int, int); +int bsmap(int, int); +void ungetkey(int); +int getkey(int); +int doin(void); +int rescan(int, int); +int universal_argument(int, int); +int digit_argument(int, int); +int negative_argument(int, int); +int ask_selfinsert(int, int); +int selfinsert(int, int); +int quote(int, int); + +/* main.c */ +int ctrlg(int, int); +int quit(int, int); + +/* ttyio.c */ +void panic(char *); + +/* cinfo.c */ +char *getkeyname(char *, size_t, int); + +/* basic.c */ +int gotobol(int, int); +int backchar(int, int); +int gotoeol(int, int); +int forwchar(int, int); +int gotobob(int, int); +int gotoeob(int, int); +int forwline(int, int); +int backline(int, int); +void setgoal(void); +int getgoal(struct line *); +int forwpage(int, int); +int backpage(int, int); +int forw1page(int, int); +int back1page(int, int); +int pagenext(int, int); +void isetmark(void); +int setmark(int, int); +int clearmark(int, int); +int swapmark(int, int); +int gotoline(int, int); +int setlineno(int); + +/* util.c X */ +int showcpos(int, int); +int getcolpos(struct mgwin *); +int twiddle(int, int); +int openline(int, int); +int enewline(int, int); +int deblank(int, int); +int justone(int, int); +int delwhite(int, int); +int delleadwhite(int, int); +int deltrailwhite(int, int); +int lfindent(int, int); +int indent(int, int); +int forwdel(int, int); +int backdel(int, int); +int space_to_tabstop(int, int); +int backtoindent(int, int); +int joinline(int, int); + +/* tags.c X */ +int findtag(int, int); +int poptag(int, int); +int tagsvisit(int, int); +int curtoken(int, int, char *); + +/* cscope.c */ +int cssymbol(int, int); +int csdefinition(int, int); +int csfuncalled(int, int); +int cscallerfuncs(int, int); +int csfindtext(int, int); +int csegrep(int, int); +int csfindfile(int, int); +int csfindinc(int, int); +int csnextfile(int, int); +int csnextmatch(int, int); +int csprevfile(int, int); +int csprevmatch(int, int); +int cscreatelist(int, int); + +/* extend.c X */ +int insert(int, int); +int bindtokey(int, int); +int localbind(int, int); +int redefine_key(int, int); +int unbindtokey(int, int); +int localunbind(int, int); +int extend(int, int); +int evalexpr(int, int); +int evalbuffer(int, int); +int evalfile(int, int); +int load(const char *); +int excline(char *, int, int); +char *skipwhite(char *); + +/* help.c X */ +int desckey(int, int); +int wallchart(int, int); +int help_help(int, int); +int apropos_command(int, int); + +/* paragraph.c X */ +int gotobop(int, int); +int gotoeop(int, int); +int fillpara(int, int); +int killpara(int, int); +int fillword(int, int); +int setfillcol(int, int); +int markpara(int, int); +int transposepara(int, int); +int sentencespace(int, int); + +/* word.c X */ +int backword(int, int); +int forwword(int, int); +int upperword(int, int); +int lowerword(int, int); +int capword(int, int); +int delfword(int, int); +int delbword(int, int); +int inword(void); +int transposeword(int, int); + +/* region.c X */ +int killregion(int, int); +int copyregion(int, int); +int lowerregion(int, int); +int upperregion(int, int); +int prefixregion(int, int); +int setprefix(int, int); +int region_get_data(struct region *, char *, int); +void region_put_data(const char *, int); +int markbuffer(int, int); +int piperegion(int, int); +int shellcommand(int, int); +int pipeio(const char * const, char * const[], char * const, int, + struct buffer *); + +/* search.c X */ +int forwsearch(int, int); +int backsearch(int, int); +int searchagain(int, int); +int forwisearch(int, int); +int backisearch(int, int); +int queryrepl(int, int); +int forwsrch(void); +int backsrch(void); +int readpattern(char *); + +/* spawn.c X */ +int spawncli(int, int); + +/* ttykbd.c X */ +void ttykeymapinit(void); +void ttykeymaptidy(void); + +/* match.c X */ +int showmatch(int, int); + +/* version.c X */ +int showversion(int, int); + +/* macro.c X */ +int definemacro(int, int); +int finishmacro(int, int); +int executemacro(int, int); + +/* modes.c X */ +int indentmode(int, int); +int fillmode(int, int); +#ifdef NOTAB +int notabmode(int, int); +#endif /* NOTAB */ +int overwrite_mode(int, int); +int set_default_mode(int,int); + +#ifdef REGEX +/* re_search.c X */ +int re_forwsearch(int, int); +int re_backsearch(int, int); +int re_searchagain(int, int); +int re_queryrepl(int, int); +int re_repl(int, int); +int replstr(int, int); +int setcasefold(int, int); +int delmatchlines(int, int); +int delnonmatchlines(int, int); +int cntmatchlines(int, int); +int cntnonmatchlines(int, int); +#endif /* REGEX */ + +/* undo.c X */ +void free_undo_record(struct undo_rec *); +int undo_dump(int, int); +int undo_enabled(void); +int undo_enable(int, int); +int undo_add_boundary(int, int); +void undo_add_modified(void); +int undo_add_insert(struct line *, int, int); +int undo_add_delete(struct line *, int, int, int); +int undo_boundary_enable(int, int); +int undo_add_change(struct line *, int, int); +int undo(int, int); + +/* autoexec.c X */ +int auto_execute(int, int); +PF *find_autoexec(const char *); +int add_autoexec(const char *, const char *); + +/* cmode.c X */ +int cmode(int, int); +int cc_brace(int, int); +int cc_char(int, int); +int cc_tab(int, int); +int cc_indent(int, int); +int cc_lfindent(int, int); + +/* grep.c X */ +int next_error(int, int); +int globalwdtoggle(int, int); +int compile(int, int); + +/* bell.c */ +void bellinit(void); +int toggleaudiblebell(int, int); +int togglevisiblebell(int, int); +int dobeep_num(const char *, int); +int dobeep_msgs(const char *, const char *); +int dobeep_msg(const char *); +void dobeep(void); + +/* interpreter.c */ +int foundparen(char *, int, int); +void cleanup(void); + +/* + * Externals. + */ +extern struct buffer *bheadp; +extern struct buffer *curbp; +extern struct mgwin *curwp; +extern struct mgwin *wheadp; +extern struct vhead varhead; +extern int thisflag; +extern int lastflag; +extern int curgoal; +extern int startrow; +extern int epresf; +extern int sgarbf; +extern int mode; +extern int nrow; +extern int ncol; +extern int ttrow; +extern int ttcol; +extern int tttop; +extern int ttbot; +extern int tthue; +extern int defb_nmodes; +extern int defb_flag; +extern int doaudiblebell; +extern int dovisiblebell; +extern int dblspace; +extern int allbro; +extern int batch; +extern char cinfo[]; +extern char *keystrings[]; +extern char pat[NPAT]; +extern char prompt[]; +extern int tceeol; +extern int tcinsl; +extern int tcdell; +extern int rptcount; /* successive invocation count */ + +/* + * Extensions. + */ +extern int shownlprompt; +int togglenewlineprompt(int, int); Index: contrib/mg/dir.c =================================================================== --- /dev/null +++ contrib/mg/dir.c @@ -0,0 +1,183 @@ +/* $OpenBSD: dir.c,v 1.31 2019/06/28 13:35:02 deraadt Exp $ */ + +/* This file is in the public domain. */ + +/* + * Name: MG 2a + * Directory management functions + * Created: Ron Flax (ron@vsedev.vse.com) + * Modified for MG 2a by Mic Kaczmarczik 03-Aug-1987 + */ + +#include +#include +#include +#include +#include +#include + +#include "def.h" + +static char mgcwd[NFILEN]; + +/* + * Initialize anything the directory management routines need. + */ +void +dirinit(void) +{ + mgcwd[0] = '\0'; + if (getcwd(mgcwd, sizeof(mgcwd)) == NULL) + ewprintf("Can't get current directory!"); + if (mgcwd[0] != '\0' && !(mgcwd[0] == '/' && mgcwd[1] == '\0')) + (void)strlcat(mgcwd, "/", sizeof(mgcwd)); +} + +/* + * Change current working directory. + */ +/* ARGSUSED */ +int +changedir(int f, int n) +{ + char bufc[NFILEN], *bufp; + + (void)strlcpy(bufc, mgcwd, sizeof(bufc)); + if ((bufp = eread("Change default directory: ", bufc, NFILEN, + EFDEF | EFNEW | EFCR | EFFILE)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + /* Append trailing slash */ + if (chdir(bufc) == -1) { + dobeep(); + ewprintf("Can't change dir to %s", bufc); + return (FALSE); + } + if ((bufp = getcwd(mgcwd, sizeof(mgcwd))) == NULL) { + if (bufc[0] == '/') + (void)strlcpy(mgcwd, bufc, sizeof(mgcwd)); + else + (void)strlcat(mgcwd, bufc, sizeof(mgcwd)); + } + if (mgcwd[strlen(mgcwd) - 1] != '/') + (void)strlcat(mgcwd, "/", sizeof(mgcwd)); + ewprintf("Current directory is now %s", mgcwd); + return (TRUE); +} + +/* + * Show current directory. + */ +/* ARGSUSED */ +int +showcwdir(int f, int n) +{ + ewprintf("Current directory: %s", mgcwd); + return (TRUE); +} + +int +getcwdir(char *buf, size_t len) +{ + if (strlcpy(buf, mgcwd, len) >= len) + return (FALSE); + + return (TRUE); +} + +/* Create the directory and it's parents. */ +/* ARGSUSED */ +int +makedir(int f, int n) +{ + return (ask_makedir()); +} + +int +ask_makedir(void) +{ + + char bufc[NFILEN]; + char *path; + + if (getbufcwd(bufc, sizeof(bufc)) != TRUE) + return (ABORT); + if ((path = eread("Make directory: ", bufc, NFILEN, + EFDEF | EFNEW | EFCR | EFFILE)) == NULL) + return (ABORT); + else if (path[0] == '\0') + return (FALSE); + + return (do_makedir(path)); +} + +int +do_makedir(char *path) +{ + struct stat sb; + int finished, ishere; + mode_t dir_mode, f_mode, oumask; + char *slash; + + if ((path = adjustname(path, TRUE)) == NULL) + return (FALSE); + + /* Remove trailing slashes */ + slash = strrchr(path, '\0'); + while (--slash > path && *slash == '/') + *slash = '\0'; + + slash = path; + + oumask = umask(0); + f_mode = 0777 & ~oumask; + dir_mode = f_mode | S_IWUSR | S_IXUSR; + + for (;;) { + slash += strspn(slash, "/"); + slash += strcspn(slash, "/"); + + finished = (*slash == '\0'); + *slash = '\0'; + + ishere = !stat(path, &sb); + if (finished && ishere) { + dobeep(); + ewprintf("Cannot create directory %s: file exists", + path); + return(FALSE); + } else if (!finished && ishere && S_ISDIR(sb.st_mode)) { + *slash = '/'; + continue; + } + + if (mkdir(path, finished ? f_mode : dir_mode) == 0) { + if (f_mode > 0777 && chmod(path, f_mode) == -1) { + umask(oumask); + return (ABORT); + } + } else { + if (!ishere || !S_ISDIR(sb.st_mode)) { + if (!ishere) { + dobeep(); + ewprintf("Creating directory: " + "permission denied, %s", path); + } else + eerase(); + + umask(oumask); + return (FALSE); + } + } + + if (finished) + break; + + *slash = '/'; + } + + eerase(); + umask(oumask); + return (TRUE); +} Index: contrib/mg/dired.c =================================================================== --- /dev/null +++ contrib/mg/dired.c @@ -0,0 +1,1206 @@ +/* $OpenBSD: dired.c,v 1.100 2021/05/02 14:13:17 lum Exp $ */ + +/* This file is in the public domain. */ + +/* dired module for mg 2a + * by Robert A. Larson + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" +#include "kbd.h" + +void dired_init(void); +static int dired(int, int); +static int d_otherwindow(int, int); +static int d_undel(int, int); +static int d_undelbak(int, int); +static int d_findfile(int, int); +static int d_ffotherwindow(int, int); +static int d_expunge(int, int); +static int d_copy(int, int); +static int d_del(int, int); +static int d_rename(int, int); +static int d_exec(int, struct buffer *, const char *, const char *, ...); +static int d_shell_command(int, int); +static int d_create_directory(int, int); +static int d_makename(struct line *, char *, size_t); +static int d_warpdot(struct line *, int *); +static int d_forwpage(int, int); +static int d_backpage(int, int); +static int d_forwline(int, int); +static int d_backline(int, int); +static int d_killbuffer_cmd(int, int); +static int d_refreshbuffer(int, int); +static int d_filevisitalt(int, int); +static int d_gotofile(int, int); +static void reaper(int); +static int gotofile(char*); +static struct buffer *refreshbuffer(struct buffer *); +static int createlist(struct buffer *); +static void redelete(struct buffer *); +static char *findfname(struct line *, char *); + +extern struct keymap_s helpmap, cXmap, metamap; + +const char DDELCHAR = 'D'; + +/* + * Structure which holds a linked list of file names marked for + * deletion. Used to maintain dired buffer 'state' between refreshes. + */ +struct delentry { + SLIST_ENTRY(delentry) entry; + char *fn; +}; +SLIST_HEAD(slisthead, delentry) delhead = SLIST_HEAD_INITIALIZER(delhead); + +static PF dirednul[] = { + setmark, /* ^@ */ + gotobol, /* ^A */ + backchar, /* ^B */ + rescan, /* ^C */ + d_del, /* ^D */ + gotoeol, /* ^E */ + forwchar, /* ^F */ + ctrlg, /* ^G */ + NULL, /* ^H */ +}; + +static PF diredcl[] = { + reposition, /* ^L */ + d_findfile, /* ^M */ + d_forwline, /* ^N */ + rescan, /* ^O */ + d_backline, /* ^P */ + rescan, /* ^Q */ + backisearch, /* ^R */ + forwisearch, /* ^S */ + rescan, /* ^T */ + universal_argument, /* ^U */ + d_forwpage, /* ^V */ + rescan, /* ^W */ + NULL /* ^X */ +}; + +static PF diredcz[] = { + spawncli, /* ^Z */ + NULL, /* esc */ + rescan, /* ^\ */ + rescan, /* ^] */ + rescan, /* ^^ */ + rescan, /* ^_ */ + d_forwline, /* SP */ + d_shell_command, /* ! */ + rescan, /* " */ + rescan, /* # */ + rescan, /* $ */ + rescan, /* % */ + rescan, /* & */ + rescan, /* ' */ + rescan, /* ( */ + rescan, /* ) */ + rescan, /* * */ + d_create_directory /* + */ +}; + +static PF direda[] = { + d_filevisitalt, /* a */ + rescan, /* b */ + d_copy, /* c */ + d_del, /* d */ + d_findfile, /* e */ + d_findfile, /* f */ + d_refreshbuffer, /* g */ + rescan, /* h */ + rescan, /* i */ + d_gotofile /* j */ +}; + +static PF diredn[] = { + d_forwline, /* n */ + d_ffotherwindow, /* o */ + d_backline, /* p */ + d_killbuffer_cmd, /* q */ + d_rename, /* r */ + rescan, /* s */ + rescan, /* t */ + d_undel, /* u */ + rescan, /* v */ + rescan, /* w */ + d_expunge /* x */ +}; + +static PF direddl[] = { + d_undelbak /* del */ +}; + +static PF diredbp[] = { + d_backpage /* v */ +}; + +static PF dirednull[] = { + NULL +}; + +static struct KEYMAPE (1) d_backpagemap = { + 1, + 1, + rescan, + { + { + 'v', 'v', diredbp, NULL + } + } +}; + +static struct KEYMAPE (7) diredmap = { + 7, + 7, + rescan, + { + { + CCHR('@'), CCHR('H'), dirednul, (KEYMAP *) & helpmap + }, + { + CCHR('L'), CCHR('X'), diredcl, (KEYMAP *) & cXmap + }, + { + CCHR('['), CCHR('['), dirednull, (KEYMAP *) & + d_backpagemap + }, + { + CCHR('Z'), '+', diredcz, (KEYMAP *) & metamap + }, + { + 'a', 'j', direda, NULL + }, + { + 'n', 'x', diredn, NULL + }, + { + CCHR('?'), CCHR('?'), direddl, NULL + }, + } +}; + +void +dired_init(void) +{ + funmap_add(dired, "dired", 1); + funmap_add(d_create_directory, "dired-create-directory", 1); + funmap_add(d_copy, "dired-do-copy", 1); + funmap_add(d_expunge, "dired-do-flagged-delete", 0); + funmap_add(d_rename, "dired-do-rename", 1); + funmap_add(d_findfile, "dired-find-file", 1); + funmap_add(d_ffotherwindow, "dired-find-file-other-window", 1); + funmap_add(d_del, "dired-flag-file-deletion", 0); + funmap_add(d_gotofile, "dired-goto-file", 1); + funmap_add(d_forwline, "dired-next-line", 0); + funmap_add(d_otherwindow, "dired-other-window", 0); + funmap_add(d_backline, "dired-previous-line", 0); + funmap_add(d_refreshbuffer, "dired-revert", 0); + funmap_add(d_backpage, "dired-scroll-down", 0); + funmap_add(d_forwpage, "dired-scroll-up", 0); + funmap_add(d_shell_command, "dired-shell-command", 1); + funmap_add(d_undel, "dired-unmark", 0); + funmap_add(d_undelbak, "dired-unmark-backward", 0); + funmap_add(d_killbuffer_cmd, "quit-window", 0); + maps_add((KEYMAP *)&diredmap, "dired"); + dobindkey(fundamental_map, "dired", "^Xd"); +} + +/* ARGSUSED */ +int +dired(int f, int n) +{ + char dname[NFILEN], *bufp, *slash; + struct buffer *bp; + + if (curbp->b_fname[0] != '\0') { + (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); + if ((slash = strrchr(dname, '/')) != NULL) { + *(slash + 1) = '\0'; + } + } else { + if (getcwd(dname, sizeof(dname)) == NULL) + dname[0] = '\0'; + } + + if ((bufp = eread("Dired (directory): ", dname, NFILEN, + EFDEF | EFNEW | EFCR)) == NULL) + return (ABORT); + if (bufp[0] == '\0') + return (FALSE); + if ((bp = dired_(bufp)) == NULL) + return (FALSE); + + curbp = bp; + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} + +/* ARGSUSED */ +int +d_otherwindow(int f, int n) +{ + char dname[NFILEN], *bufp, *slash; + struct buffer *bp; + struct mgwin *wp; + + if (curbp->b_fname[0] != '\0') { + (void)strlcpy(dname, curbp->b_fname, sizeof(dname)); + if ((slash = strrchr(dname, '/')) != NULL) { + *(slash + 1) = '\0'; + } + } else { + if (getcwd(dname, sizeof(dname)) == NULL) + dname[0] = '\0'; + } + + if ((bufp = eread("Dired other window: ", dname, NFILEN, + EFDEF | EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if ((bp = dired_(bufp)) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + curwp = wp; + return (TRUE); +} + +/* ARGSUSED */ +int +d_del(int f, int n) +{ + if (n < 0) + return (FALSE); + while (n--) { + if (d_warpdot(curwp->w_dotp, &curwp->w_doto) == TRUE) { + lputc(curwp->w_dotp, 0, DDELCHAR); + curbp->b_flag |= BFDIREDDEL; + } + if (lforw(curwp->w_dotp) != curbp->b_headp) { + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_dotline++; + } + } + curwp->w_rflag |= WFEDIT | WFMOVE; + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +/* ARGSUSED */ +int +d_undel(int f, int n) +{ + if (n < 0) + return (d_undelbak(f, -n)); + while (n--) { + if (llength(curwp->w_dotp) > 0) + lputc(curwp->w_dotp, 0, ' '); + if (lforw(curwp->w_dotp) != curbp->b_headp) { + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_dotline++; + } + } + curwp->w_rflag |= WFEDIT | WFMOVE; + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +/* ARGSUSED */ +int +d_undelbak(int f, int n) +{ + if (n < 0) + return (d_undel(f, -n)); + while (n--) { + if (lback(curwp->w_dotp) != curbp->b_headp) { + curwp->w_dotp = lback(curwp->w_dotp); + curwp->w_dotline--; + } + if (llength(curwp->w_dotp) > 0) + lputc(curwp->w_dotp, 0, ' '); + } + curwp->w_rflag |= WFEDIT | WFMOVE; + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +/* ARGSUSED */ +int +d_findfile(int f, int n) +{ + struct buffer *bp; + int s; + char fname[NFILEN]; + + if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) + return (FALSE); + if (s == TRUE) + bp = dired_(fname); + else + bp = findbuffer(fname); + if (bp == NULL) + return (FALSE); + curbp = bp; + if (showbuffer(bp, curwp, WFFULL) != TRUE) + return (FALSE); + if (bp->b_fname[0] != 0) + return (TRUE); + return (readin(fname)); +} + +/* ARGSUSED */ +int +d_ffotherwindow(int f, int n) +{ + char fname[NFILEN]; + int s; + struct buffer *bp; + struct mgwin *wp; + + if ((s = d_makename(curwp->w_dotp, fname, sizeof(fname))) == ABORT) + return (FALSE); + if ((bp = (s ? dired_(fname) : findbuffer(fname))) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + curwp = wp; + if (bp->b_fname[0] != 0) + return (TRUE); /* never true for dired buffers */ + return (readin(fname)); +} + +/* ARGSUSED */ +int +d_expunge(int f, int n) +{ + struct line *lp, *nlp; + char fname[NFILEN], sname[NFILEN]; + int tmp; + + tmp = curwp->w_dotline; + curwp->w_dotline = 0; + + for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { + curwp->w_dotline++; + nlp = lforw(lp); + if (llength(lp) && lgetc(lp, 0) == 'D') { + switch (d_makename(lp, fname, sizeof(fname))) { + case ABORT: + dobeep(); + ewprintf("Bad line in dired buffer"); + curwp->w_dotline = tmp; + return (FALSE); + case FALSE: + if (unlink(fname) == -1) { + (void)xbasename(sname, fname, NFILEN); + dobeep(); + ewprintf("Could not delete '%s'", sname); + curwp->w_dotline = tmp; + return (FALSE); + } + break; + case TRUE: + if (rmdir(fname) == -1) { + (void)xbasename(sname, fname, NFILEN); + dobeep(); + ewprintf("Could not delete directory " + "'%s'", sname); + curwp->w_dotline = tmp; + return (FALSE); + } + break; + } + lfree(lp); + curwp->w_bufp->b_lines--; + if (tmp > curwp->w_dotline) + tmp--; + curwp->w_rflag |= WFFULL; + } + } + curwp->w_dotline = tmp; + d_warpdot(curwp->w_dotp, &curwp->w_doto); + + /* we have deleted all items successfully, remove del flag */ + curbp->b_flag &= ~BFDIREDDEL; + + return (TRUE); +} + +/* ARGSUSED */ +int +d_copy(int f, int n) +{ + struct stat statbuf; + char frname[NFILEN], toname[NFILEN], sname[NFILEN]; + char *topath, *bufp; + int ret; + size_t off; + struct buffer *bp; + + if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { + dobeep(); + ewprintf("Not a file"); + return (FALSE); + } + off = strlcpy(toname, curbp->b_fname, sizeof(toname)); + if (off >= sizeof(toname) - 1) { /* can't happen, really */ + dobeep(); + ewprintf("Directory name too long"); + return (FALSE); + } + (void)xbasename(sname, frname, NFILEN); + bufp = eread("Copy %s to: ", toname, sizeof(toname), + EFDEF | EFNEW | EFCR, sname); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + + topath = adjustname(toname, TRUE); + if (stat(topath, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + ret = snprintf(toname, sizeof(toname), "%s/%s", + topath, sname); + if (ret < 0 || ret >= sizeof(toname) - 1) { + dobeep(); + ewprintf("Directory name too long"); + return (FALSE); + } + topath = adjustname(toname, TRUE); + } + } + if (topath == NULL) + return (FALSE); + if (strcmp(frname, topath) == 0) { + ewprintf("Cannot copy to same file: %s", frname); + return (TRUE); + } + ret = (copy(frname, topath) >= 0) ? TRUE : FALSE; + if (ret != TRUE) + return (ret); + if ((bp = refreshbuffer(curbp)) == NULL) + return (FALSE); + + ewprintf("Copy: 1 file"); + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} + +/* ARGSUSED */ +int +d_rename(int f, int n) +{ + struct stat statbuf; + char frname[NFILEN], toname[NFILEN]; + char *topath, *bufp; + int ret; + size_t off; + struct buffer *bp; + char sname[NFILEN]; + + if (d_makename(curwp->w_dotp, frname, sizeof(frname)) != FALSE) { + dobeep(); + ewprintf("Not a file"); + return (FALSE); + } + off = strlcpy(toname, curbp->b_fname, sizeof(toname)); + if (off >= sizeof(toname) - 1) { /* can't happen, really */ + dobeep(); + ewprintf("Name too long"); + return (FALSE); + } + (void)xbasename(sname, frname, NFILEN); + bufp = eread("Rename %s to: ", toname, + sizeof(toname), EFDEF | EFNEW | EFCR, sname); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + + topath = adjustname(toname, TRUE); + if (stat(topath, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + ret = snprintf(toname, sizeof(toname), "%s/%s", + topath, sname); + if (ret < 0 || ret >= sizeof(toname) - 1) { + dobeep(); + ewprintf("Directory name too long"); + return (FALSE); + } + topath = adjustname(toname, TRUE); + } + } + if (topath == NULL) + return (FALSE); + if (strcmp(frname, topath) == 0) { + ewprintf("Cannot move to same file: %s", frname); + return (TRUE); + } + ret = (rename(frname, topath) >= 0) ? TRUE : FALSE; + if (ret != TRUE) + return (ret); + if ((bp = refreshbuffer(curbp)) == NULL) + return (FALSE); + + ewprintf("Move: 1 file"); + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} + +/* ARGSUSED */ +void +reaper(int signo __attribute__((unused))) +{ + int save_errno = errno, status; + + while (waitpid(-1, &status, WNOHANG) >= 0) + ; + errno = save_errno; +} + +/* + * Pipe the currently selected file through a shell command. + */ +/* ARGSUSED */ +int +d_shell_command(int f, int n) +{ + char command[512], fname[PATH_MAX], *bufp; + struct buffer *bp; + struct mgwin *wp; + char sname[NFILEN]; + + bp = bfind("*Shell Command Output*", TRUE); + if (bclear(bp) != TRUE) + return (ABORT); + + if (d_makename(curwp->w_dotp, fname, sizeof(fname)) != FALSE) { + dobeep(); + ewprintf("bad line"); + return (ABORT); + } + + command[0] = '\0'; + (void)xbasename(sname, fname, NFILEN); + bufp = eread("! on %s: ", command, sizeof(command), EFNEW, sname); + if (bufp == NULL) + return (ABORT); + + if (d_exec(0, bp, fname, "sh", "-c", command, NULL) != TRUE) + return (ABORT); + + if ((wp = popbuf(bp, WNONE)) == NULL) + return (ABORT); /* XXX - free the buffer?? */ + curwp = wp; + curbp = wp->w_bufp; + return (TRUE); +} + +/* + * Pipe input file to cmd and insert the command's output in the + * given buffer. Each line will be prefixed with the given + * number of spaces. + */ +static int +d_exec(int space, struct buffer *bp, const char *input, const char *cmd, ...) +{ + char buf[BUFSIZ]; + va_list ap; + struct sigaction olda, newa; + char **argv = NULL, *cp; + FILE *fin; + int fds[2] = { -1, -1 }; + int infd = -1; + int ret = (ABORT), n; + pid_t pid; + + if (sigaction(SIGCHLD, NULL, &olda) == -1) + return (ABORT); + + /* Find the number of arguments. */ + va_start(ap, cmd); + for (n = 2; va_arg(ap, char *) != NULL; n++) + ; + va_end(ap); + + /* Allocate and build the argv. */ + if ((argv = calloc(n, sizeof(*argv))) == NULL) { + dobeep(); + ewprintf("Can't allocate argv : %s", strerror(errno)); + goto out; + } + + n = 1; + argv[0] = (char *)cmd; + va_start(ap, cmd); + while ((argv[n] = va_arg(ap, char *)) != NULL) + n++; + va_end(ap); + + if (input == NULL) + input = "/dev/null"; + + if ((infd = open(input, O_RDONLY)) == -1) { + dobeep(); + ewprintf("Can't open input file : %s", strerror(errno)); + goto out; + } + + if (pipe(fds) == -1) { + dobeep(); + ewprintf("Can't create pipe : %s", strerror(errno)); + goto out; + } + + newa.sa_handler = reaper; + newa.sa_flags = 0; + if (sigaction(SIGCHLD, &newa, NULL) == -1) + goto out; + + if ((pid = fork()) == -1) { + dobeep(); + ewprintf("Can't fork"); + goto out; + } + + switch (pid) { + case 0: /* Child */ + close(fds[0]); + dup2(infd, STDIN_FILENO); + dup2(fds[1], STDOUT_FILENO); + dup2(fds[1], STDERR_FILENO); + if (execvp(argv[0], argv) == -1) + ewprintf("Can't exec %s: %s", argv[0], strerror(errno)); + exit(1); + break; + default: /* Parent */ + close(infd); + close(fds[1]); + infd = fds[1] = -1; + if ((fin = fdopen(fds[0], "r")) == NULL) + goto out; + while (fgets(buf, sizeof(buf), fin) != NULL) { + cp = strrchr(buf, *bp->b_nlchr); + if (cp == NULL && !feof(fin)) { /* too long a line */ + int c; + addlinef(bp, "%*s%s...", space, "", buf); + while ((c = getc(fin)) != EOF && + c != *bp->b_nlchr) + ; + continue; + } else if (cp) + *cp = '\0'; + addlinef(bp, "%*s%s", space, "", buf); + } + fclose(fin); + break; + } + ret = (TRUE); + +out: + if (sigaction(SIGCHLD, &olda, NULL) == -1) + ewprintf("Warning, couldn't reset previous signal handler"); + if (fds[0] != -1) + close(fds[0]); + if (fds[1] != -1) + close(fds[1]); + if (infd != -1) + close(infd); + free(argv); + return ret; +} + +/* ARGSUSED */ +int +d_create_directory(int f, int n) +{ + int ret; + struct buffer *bp; + + ret = ask_makedir(); + if (ret != TRUE) + return(ret); + + if ((bp = refreshbuffer(curbp)) == NULL) + return (FALSE); + + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} + +/* ARGSUSED */ +int +d_killbuffer_cmd(int f, int n) +{ + return(killbuffer_cmd(FFRAND, 0)); +} + +int +d_refreshbuffer(int f, int n) +{ + struct buffer *bp; + + if ((bp = refreshbuffer(curbp)) == NULL) + return (FALSE); + + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} + +/* + * Kill then re-open the requested dired buffer. + * If required, take a note of any files marked for deletion. Then once + * the buffer has been re-opened, remark the same files as deleted. + */ +struct buffer * +refreshbuffer(struct buffer *bp) +{ + char *tmp_b_fname; + int i, tmp_w_dotline, ddel = 0; + + /* remember directory path to open later */ + tmp_b_fname = strdup(bp->b_fname); + if (tmp_b_fname == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (NULL); + } + tmp_w_dotline = curwp->w_dotline; + + /* create a list of files for deletion */ + if (bp->b_flag & BFDIREDDEL) + ddel = createlist(bp); + + killbuffer(bp); + + /* dired_() uses findbuffer() to create new buffer */ + if ((bp = dired_(tmp_b_fname)) == NULL) { + free(tmp_b_fname); + return (NULL); + } + free(tmp_b_fname); + + /* remark any previously deleted files with a 'D' */ + if (ddel) + redelete(bp); + + /* find dot line */ + bp->b_dotp = bfirstlp(bp); + if (tmp_w_dotline > bp->b_lines) + tmp_w_dotline = bp->b_lines - 1; + for (i = 1; i < tmp_w_dotline; i++) + bp->b_dotp = lforw(bp->b_dotp); + + bp->b_dotline = i; + bp->b_doto = 0; + d_warpdot(bp->b_dotp, &bp->b_doto); + + curbp = bp; + + return (bp); +} + +static int +d_makename(struct line *lp, char *fn, size_t len) +{ + int start, nlen, ret; + char *namep; + + if (d_warpdot(lp, &start) == FALSE) + return (ABORT); + namep = &lp->l_text[start]; + nlen = llength(lp) - start; + + ret = snprintf(fn, len, "%s%.*s", curbp->b_fname, nlen, namep); + if (ret < 0 || ret >= (int)len) + return (ABORT); /* Name is too long. */ + + /* Return TRUE if the entry is a directory. */ + return ((lgetc(lp, 2) == 'd') ? TRUE : FALSE); +} + +#define NAME_FIELD 9 + +static int +d_warpdot(struct line *dotp, int *doto) +{ + char *tp = dotp->l_text; + int off = 0, field = 0, len; + + /* + * Find the byte offset to the (space-delimited) filename + * field in formatted ls output. + */ + len = llength(dotp); + while (off < len) { + if (tp[off++] == ' ') { + if (++field == NAME_FIELD) { + *doto = off; + return (TRUE); + } + /* Skip the space. */ + while (off < len && tp[off] == ' ') + off++; + } + } + /* We didn't find the field. */ + *doto = 0; + return (FALSE); +} + +static int +d_forwpage(int f, int n) +{ + forwpage(f | FFRAND, n); + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +static int +d_backpage (int f, int n) +{ + backpage(f | FFRAND, n); + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +static int +d_forwline (int f, int n) +{ + forwline(f | FFRAND, n); + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +static int +d_backline (int f, int n) +{ + backline(f | FFRAND, n); + return (d_warpdot(curwp->w_dotp, &curwp->w_doto)); +} + +int +d_filevisitalt (int f, int n) +{ + char fname[NFILEN]; + + if (d_makename(curwp->w_dotp, fname, sizeof(fname)) == ABORT) + return (FALSE); + + return(do_filevisitalt(fname)); +} + +/* + * XXX dname needs to have enough place to store an additional '/'. + */ +struct buffer * +dired_(char *dname) +{ + struct buffer *bp; + int i; + size_t len; + + if ((dname = adjustname(dname, TRUE)) == NULL) { + dobeep(); + ewprintf("Bad directory name"); + return (NULL); + } + /* this should not be done, instead adjustname() should get a flag */ + len = strlen(dname); + if (dname[len - 1] != '/') { + dname[len++] = '/'; + dname[len] = '\0'; + } + if ((access(dname, R_OK | X_OK)) == -1) { + if (errno == EACCES) { + dobeep(); + ewprintf("Permission denied: %s", dname); + } else { + dobeep(); + ewprintf("Error opening: %s", dname); + } + return (NULL); + } + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + if (strcmp(bp->b_fname, dname) == 0) { + if (fchecktime(bp) != TRUE) + ewprintf("Directory has changed on disk;" + " type g to update Dired"); + return (bp); + } + + } + bp = bfind(dname, TRUE); + bp->b_flag |= BFREADONLY | BFIGNDIRTY; + + if ((d_exec(2, bp, NULL, "ls", "-al", dname, NULL)) != TRUE) + return (NULL); + + /* Find the line with ".." on it. */ + bp->b_dotp = bfirstlp(bp); + bp->b_dotline = 1; + for (i = 0; i < bp->b_lines; i++) { + bp->b_dotp = lforw(bp->b_dotp); + bp->b_dotline++; + if (d_warpdot(bp->b_dotp, &bp->b_doto) == FALSE) + continue; + if (strcmp(ltext(bp->b_dotp) + bp->b_doto, "..") == 0) + break; + } + + /* We want dot on the entry right after "..", if possible. */ + if (++i < bp->b_lines - 2) { + bp->b_dotp = lforw(bp->b_dotp); + bp->b_dotline++; + } + d_warpdot(bp->b_dotp, &bp->b_doto); + + (void)strlcpy(bp->b_fname, dname, sizeof(bp->b_fname)); + (void)strlcpy(bp->b_cwd, dname, sizeof(bp->b_cwd)); + if ((bp->b_modes[1] = name_mode("dired")) == NULL) { + bp->b_modes[0] = name_mode("fundamental"); + dobeep(); + ewprintf("Could not find mode dired"); + return (NULL); + } + (void)fupdstat(bp); + bp->b_nmodes = 1; + return (bp); +} + +/* + * Iterate through the lines of the dired buffer looking for files + * collected in the linked list made in createlist(). If a line is found + * replace 'D' as first char in a line. As lines are found, remove the + * corresponding item from the linked list. Iterate for as long as there + * are items in the linked list or until end of buffer is found. + */ +void +redelete(struct buffer *bp) +{ + struct delentry *dt, *d1 = NULL; + struct line *lp, *nlp; + char fname[NFILEN]; + char *p = fname; + size_t plen, fnlen; + int finished = 0; + + /* reset the deleted file buffer flag until a deleted file is found */ + bp->b_flag &= ~BFDIREDDEL; + + for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) { + bp->b_dotp = lp; + if ((p = findfname(lp, p)) == NULL) { + nlp = lforw(lp); + continue; + } + plen = strlen(p); + SLIST_FOREACH_SAFE(d1, &delhead, entry, dt) { + fnlen = strlen(d1->fn); + if ((plen == fnlen) && + (strncmp(p, d1->fn, plen) == 0)) { + lputc(bp->b_dotp, 0, DDELCHAR); + bp->b_flag |= BFDIREDDEL; + SLIST_REMOVE(&delhead, d1, delentry, entry); + if (SLIST_EMPTY(&delhead)) { + finished = 1; + break; + } + } + } + if (finished) + break; + nlp = lforw(lp); + } + while (!SLIST_EMPTY(&delhead)) { + d1 = SLIST_FIRST(&delhead); + SLIST_REMOVE_HEAD(&delhead, entry); + free(d1->fn); + free(d1); + } + return; +} + +/* + * Create a list of files marked for deletion. + */ +int +createlist(struct buffer *bp) +{ + struct delentry *d1 = NULL, *d2; + struct line *lp, *nlp; + char fname[NFILEN]; + char *p = fname; + int ret = FALSE; + + for (lp = bfirstlp(bp); lp != bp->b_headp; lp = nlp) { + /* + * Check if the line has 'D' on the first char and if a valid + * filename can be extracted from it. + */ + if (((lp->l_text[0] != DDELCHAR)) || + ((p = findfname(lp, p)) == NULL)) { + nlp = lforw(lp); + continue; + } + if (SLIST_EMPTY(&delhead)) { + if ((d1 = malloc(sizeof(struct delentry))) + == NULL) + return (ABORT); + if ((d1->fn = strdup(p)) == NULL) { + free(d1); + return (ABORT); + } + SLIST_INSERT_HEAD(&delhead, d1, entry); + } else { + if ((d2 = malloc(sizeof(struct delentry))) + == NULL) { + free(d1->fn); + free(d1); + return (ABORT); + } + if ((d2->fn = strdup(p)) == NULL) { + free(d1->fn); + free(d1); + free(d2); + return (ABORT); + } + if (!d1) + SLIST_INSERT_HEAD(&delhead, d2, entry); + else + SLIST_INSERT_AFTER(d1, d2, entry); + d1 = d2; + } + ret = TRUE; + nlp = lforw(lp); + } + return (ret); +} + +int +dired_jump(int f, int n) +{ + struct buffer *bp; + const char *modename; + char dname[NFILEN], *fname; + int ret, i; + + /* + * We use fundamental mode in dired, so just check we aren't in + * dired mode for this specific function. Seems like a corner + * case at the moment. + */ + for (i = 0; i <= curbp->b_nmodes; i++) { + modename = curbp->b_modes[i]->p_name; + if (strncmp(modename, "dired", 5) == 0) + return (dobeep_msg("In dired mode already")); + } + + if (getbufcwd(dname, sizeof(dname)) != TRUE) + return (FALSE); + + fname = curbp->b_fname; + + if ((bp = dired_(dname)) == NULL) + return (FALSE); + curbp = bp; + + ret = showbuffer(bp, curwp, WFFULL | WFMODE); + if (ret != TRUE) + return ret; + + fname = adjustname(fname, TRUE); + if (fname != NULL) + gotofile(fname); + + return (TRUE); +} + +int +d_gotofile(int f, int n) +{ + size_t lenfpath; + char fpath[NFILEN]; + char *fpth, *fnp = NULL; + + if (getbufcwd(fpath, sizeof(fpath)) != TRUE) + fpath[0] = '\0'; + lenfpath = strlen(fpath); + fnp = eread("Goto file: ", fpath, NFILEN, + EFNEW | EFCR | EFFILE | EFDEF); + if (fnp == NULL) + return (ABORT); + else if (fnp[0] == '\0') + return (FALSE); + + fpth = adjustname(fpath, TRUE); /* Removes last '/' if dir... */ + if (fpth == NULL || strlen(fpth) == lenfpath - 1) { /* ...hence -1. */ + ewprintf("No file to find"); /* Current directory given so */ + return (TRUE); /* return at present location. */ + } + return gotofile(fpth); +} + +int +gotofile(char *fpth) +{ + struct line *lp, *nlp; + char fname[NFILEN]; + char *p; + int tmp; + + (void)xbasename(fname, fpth, NFILEN); + tmp = 0; + for (lp = bfirstlp(curbp); lp != curbp->b_headp; lp = nlp) { + tmp++; + if ((p = findfname(lp, p)) == NULL) { + nlp = lforw(lp); + continue; + } + if (strcmp(fname, p) == 0) { + curwp->w_dotp = lp; + curwp->w_dotline = tmp; + (void)d_warpdot(curwp->w_dotp, &curwp->w_doto); + tmp--; + break; + } + nlp = lforw(lp); + } + if (tmp == curbp->b_lines - 1) { + ewprintf("File not found %s", fname); + return (FALSE); + } else { + ewprintf(""); + return (TRUE); + } +} + +/* + * Look for and extract a file name on a dired buffer line. + */ +char * +findfname(struct line *lp, char *fn) +{ + int start; + + (void)d_warpdot(lp, &start); + if (start < 1) + return NULL; + fn = &lp->l_text[start]; + return fn; +} Index: contrib/mg/display.c =================================================================== --- /dev/null +++ contrib/mg/display.c @@ -0,0 +1,1081 @@ +/* $OpenBSD: display.c,v 1.48 2017/07/06 19:27:37 schwarze Exp $ */ + +/* This file is in the public domain. */ + +/* + * The functions in this file handle redisplay. The + * redisplay system knows almost nothing about the editing + * process; the editing functions do, however, set some + * hints to eliminate a lot of the grinding. There is more + * that can be done; the "vtputc" interface is a real + * pig. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" + +/* + * A video structure always holds + * an array of characters whose length is equal to + * the longest line possible. v_text is allocated + * dynamically to fit the screen width. + */ +struct video { + short v_hash; /* Hash code, for compares. */ + short v_flag; /* Flag word. */ + short v_color; /* Color of the line. */ + int v_cost; /* Cost of display. */ + char *v_text; /* The actual characters. */ +}; + +#define VFCHG 0x0001 /* Changed. */ +#define VFHBAD 0x0002 /* Hash and cost are bad. */ +#define VFEXT 0x0004 /* extended line (beond ncol) */ + +/* + * SCORE structures hold the optimal + * trace trajectory, and the cost of redisplay, when + * the dynamic programming redisplay code is used. + */ +struct score { + int s_itrace; /* "i" index for track back. */ + int s_jtrace; /* "j" index for trace back. */ + int s_cost; /* Display cost. */ +}; + +void vtmove(int, int); +void vtputc(int); +void vtpute(int); +int vtputs(const char *); +void vteeol(void); +void updext(int, int); +void modeline(struct mgwin *, int); +void setscores(int, int); +void traceback(int, int, int, int); +void ucopy(struct video *, struct video *); +void uline(int, struct video *, struct video *); +void hash(struct video *); + + +int sgarbf = TRUE; /* TRUE if screen is garbage. */ +int vtrow = HUGE; /* Virtual cursor row. */ +int vtcol = HUGE; /* Virtual cursor column. */ +int tthue = CNONE; /* Current color. */ +int ttrow = HUGE; /* Physical cursor row. */ +int ttcol = HUGE; /* Physical cursor column. */ +int tttop = HUGE; /* Top of scroll region. */ +int ttbot = HUGE; /* Bottom of scroll region. */ +int lbound = 0; /* leftmost bound of the current */ + /* line being displayed */ + +struct video **vscreen; /* Edge vector, virtual. */ +struct video **pscreen; /* Edge vector, physical. */ +struct video *video; /* Actual screen data. */ +struct video blanks; /* Blank line image. */ + +/* + * This matrix is written as an array because + * we do funny things in the "setscores" routine, which + * is very compute intensive, to make the subscripts go away. + * It would be "SCORE score[NROW][NROW]" in old speak. + * Look at "setscores" to understand what is up. + */ +struct score *score; /* [NROW * NROW] */ + +static int linenos = TRUE; +static int colnos = FALSE; + +/* Is macro recording enabled? */ +extern int macrodef; +/* Is working directory global? */ +extern int globalwd; + +/* + * Since we don't have variables (we probably should) these are command + * processors for changing the values of mode flags. + */ +/* ARGSUSED */ +int +linenotoggle(int f, int n) +{ + if (f & FFARG) + linenos = n > 0; + else + linenos = !linenos; + + sgarbf = TRUE; + + return (TRUE); +} + +/* ARGSUSED */ +int +colnotoggle(int f, int n) +{ + if (f & FFARG) + colnos = n > 0; + else + colnos = !colnos; + + sgarbf = TRUE; + + return (TRUE); +} + +/* + * Reinit the display data structures, this is called when the terminal + * size changes. + */ +int +vtresize(int force, int newrow, int newcol) +{ + int i; + int rowchanged, colchanged; + static int first_run = 1; + struct video *vp; + + if (newrow < 1 || newcol < 1) + return (FALSE); + + rowchanged = (newrow != nrow); + colchanged = (newcol != ncol); + +#define TRYREALLOC(a, n) do { \ + void *tmp; \ + if ((tmp = realloc((a), (n))) == NULL) { \ + panic("out of memory in display code"); \ + } \ + (a) = tmp; \ + } while (0) + +#define TRYREALLOCARRAY(a, n, m) do { \ + void *tmp; \ + if ((tmp = reallocarray((a), (n), (m))) == NULL) {\ + panic("out of memory in display code"); \ + } \ + (a) = tmp; \ + } while (0) + + /* No update needed */ + if (!first_run && !force && !rowchanged && !colchanged) + return (TRUE); + + if (first_run) + memset(&blanks, 0, sizeof(blanks)); + + if (rowchanged || first_run) { + int vidstart; + + /* + * This is not pretty. + */ + if (nrow == 0) + vidstart = 0; + else + vidstart = 2 * (nrow - 1); + + /* + * We're shrinking, free some internal data. + */ + if (newrow < nrow) { + for (i = 2 * (newrow - 1); i < 2 * (nrow - 1); i++) { + free(video[i].v_text); + video[i].v_text = NULL; + } + } + + TRYREALLOCARRAY(score, newrow, newrow * sizeof(struct score)); + TRYREALLOCARRAY(vscreen, (newrow - 1), sizeof(struct video *)); + TRYREALLOCARRAY(pscreen, (newrow - 1), sizeof(struct video *)); + TRYREALLOCARRAY(video, (newrow - 1), 2 * sizeof(struct video)); + + /* + * Zero-out the entries we just allocated. + */ + for (i = vidstart; i < 2 * (newrow - 1); i++) + memset(&video[i], 0, sizeof(struct video)); + + /* + * Reinitialize vscreen and pscreen arrays completely. + */ + vp = &video[0]; + for (i = 0; i < newrow - 1; ++i) { + vscreen[i] = vp; + ++vp; + pscreen[i] = vp; + ++vp; + } + } + if (rowchanged || colchanged || first_run) { + for (i = 0; i < 2 * (newrow - 1); i++) + TRYREALLOC(video[i].v_text, newcol); + TRYREALLOC(blanks.v_text, newcol); + } + + nrow = newrow; + ncol = newcol; + + if (ttrow > nrow) + ttrow = nrow; + if (ttcol > ncol) + ttcol = ncol; + + first_run = 0; + return (TRUE); +} + +#undef TRYREALLOC +#undef TRYREALLOCARRAY + +/* + * Initialize the data structures used + * by the display code. The edge vectors used + * to access the screens are set up. The operating + * system's terminal I/O channel is set up. Fill the + * "blanks" array with ASCII blanks. The rest is done + * at compile time. The original window is marked + * as needing full update, and the physical screen + * is marked as garbage, so all the right stuff happens + * on the first call to redisplay. + */ +void +vtinit(void) +{ + int i; + + ttopen(); + ttinit(); + + /* + * ttinit called ttresize(), which called vtresize(), so our data + * structures are setup correctly. + */ + + blanks.v_color = CTEXT; + for (i = 0; i < ncol; ++i) + blanks.v_text[i] = ' '; +} + +/* + * Tidy up the virtual display system + * in anticipation of a return back to the host + * operating system. Right now all we do is position + * the cursor to the last line, erase the line, and + * close the terminal channel. + */ +void +vttidy(void) +{ + ttcolor(CTEXT); + ttnowindow(); /* No scroll window. */ + ttmove(nrow - 1, 0); /* Echo line. */ + tteeol(); + tttidy(); + ttflush(); + ttclose(); +} + +/* + * Move the virtual cursor to an origin + * 0 spot on the virtual display screen. I could + * store the column as a character pointer to the spot + * on the line, which would make "vtputc" a little bit + * more efficient. No checking for errors. + */ +void +vtmove(int row, int col) +{ + vtrow = row; + vtcol = col; +} + +/* + * Write a character to the virtual display, + * dealing with long lines and the display of unprintable + * things like control characters. Also expand tabs every 8 + * columns. This code only puts printing characters into + * the virtual display image. Special care must be taken when + * expanding tabs. On a screen whose width is not a multiple + * of 8, it is possible for the virtual cursor to hit the + * right margin before the next tab stop is reached. This + * makes the tab code loop if you are not careful. + * Three guesses how we found this. + */ +void +vtputc(int c) +{ + struct video *vp; + + c &= 0xff; + + vp = vscreen[vtrow]; + if (vtcol >= ncol) + vp->v_text[ncol - 1] = '$'; + else if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif + ) { + do { + vtputc(' '); + } while (vtcol < ncol && (vtcol & 0x07) != 0); + } else if (ISCTRL(c)) { + vtputc('^'); + vtputc(CCHR(c)); + } else if (isprint(c)) + vp->v_text[vtcol++] = c; + else { + char bf[5]; + + snprintf(bf, sizeof(bf), "\\%o", c); + vtputs(bf); + } +} + +/* + * Put a character to the virtual screen in an extended line. If we are not + * yet on left edge, don't print it yet. Check for overflow on the right + * margin. + */ +void +vtpute(int c) +{ + struct video *vp; + + c &= 0xff; + + vp = vscreen[vtrow]; + if (vtcol >= ncol) + vp->v_text[ncol - 1] = '$'; + else if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif + ) { + do { + vtpute(' '); + } while (((vtcol + lbound) & 0x07) != 0 && vtcol < ncol); + } else if (ISCTRL(c) != FALSE) { + vtpute('^'); + vtpute(CCHR(c)); + } else if (isprint(c)) { + if (vtcol >= 0) + vp->v_text[vtcol] = c; + ++vtcol; + } else { + char bf[5], *cp; + + snprintf(bf, sizeof(bf), "\\%o", c); + for (cp = bf; *cp != '\0'; cp++) + vtpute(*cp); + } +} + +/* + * Erase from the end of the software cursor to the end of the line on which + * the software cursor is located. The display routines will decide if a + * hardware erase to end of line command should be used to display this. + */ +void +vteeol(void) +{ + struct video *vp; + + vp = vscreen[vtrow]; + while (vtcol < ncol) + vp->v_text[vtcol++] = ' '; +} + +/* + * Make sure that the display is + * right. This is a three part process. First, + * scan through all of the windows looking for dirty + * ones. Check the framing, and refresh the screen. + * Second, make sure that "currow" and "curcol" are + * correct for the current window. Third, make the + * virtual and physical screens the same. + */ +void +update(int modelinecolor) +{ + struct line *lp; + struct mgwin *wp; + struct video *vp1; + struct video *vp2; + int c, i, j; + int hflag; + int currow, curcol; + int offs, size; + + if (charswaiting()) + return; + if (sgarbf) { /* must update everything */ + wp = wheadp; + while (wp != NULL) { + wp->w_rflag |= WFMODE | WFFULL; + wp = wp->w_wndp; + } + } + if (linenos || colnos) { + wp = wheadp; + while (wp != NULL) { + wp->w_rflag |= WFMODE; + wp = wp->w_wndp; + } + } + hflag = FALSE; /* Not hard. */ + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + /* + * Nothing to be done. + */ + if (wp->w_rflag == 0) + continue; + + if ((wp->w_rflag & WFFRAME) == 0) { + lp = wp->w_linep; + for (i = 0; i < wp->w_ntrows; ++i) { + if (lp == wp->w_dotp) + goto out; + if (lp == wp->w_bufp->b_headp) + break; + lp = lforw(lp); + } + } + /* + * Put the middle-line in place. + */ + i = wp->w_frame; + if (i > 0) { + --i; + if (i >= wp->w_ntrows) + i = wp->w_ntrows - 1; + } else if (i < 0) { + i += wp->w_ntrows; + if (i < 0) + i = 0; + } else + i = wp->w_ntrows / 2; /* current center, no change */ + + /* + * Find the line. + */ + lp = wp->w_dotp; + while (i != 0 && lback(lp) != wp->w_bufp->b_headp) { + --i; + lp = lback(lp); + } + wp->w_linep = lp; + wp->w_rflag |= WFFULL; /* Force full. */ + out: + lp = wp->w_linep; /* Try reduced update. */ + i = wp->w_toprow; + if ((wp->w_rflag & ~WFMODE) == WFEDIT) { + while (lp != wp->w_dotp) { + ++i; + lp = lforw(lp); + } + vscreen[i]->v_color = CTEXT; + vscreen[i]->v_flag |= (VFCHG | VFHBAD); + vtmove(i, 0); + for (j = 0; j < llength(lp); ++j) + vtputc(lgetc(lp, j)); + vteeol(); + } else if ((wp->w_rflag & (WFEDIT | WFFULL)) != 0) { + hflag = TRUE; + while (i < wp->w_toprow + wp->w_ntrows) { + vscreen[i]->v_color = CTEXT; + vscreen[i]->v_flag |= (VFCHG | VFHBAD); + vtmove(i, 0); + if (lp != wp->w_bufp->b_headp) { + for (j = 0; j < llength(lp); ++j) + vtputc(lgetc(lp, j)); + lp = lforw(lp); + } + vteeol(); + ++i; + } + } + if ((wp->w_rflag & WFMODE) != 0) + modeline(wp, modelinecolor); + wp->w_rflag = 0; + wp->w_frame = 0; + } + lp = curwp->w_linep; /* Cursor location. */ + currow = curwp->w_toprow; + while (lp != curwp->w_dotp) { + ++currow; + lp = lforw(lp); + } + curcol = 0; + i = 0; + while (i < curwp->w_doto) { + c = lgetc(lp, i++); + if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif + ) { + curcol |= 0x07; + curcol++; + } else if (ISCTRL(c) != FALSE) + curcol += 2; + else if (isprint(c)) + curcol++; + else { + char bf[5]; + + snprintf(bf, sizeof(bf), "\\%o", c); + curcol += strlen(bf); + } + } + if (curcol >= ncol - 1) { /* extended line. */ + /* flag we are extended and changed */ + vscreen[currow]->v_flag |= VFEXT | VFCHG; + updext(currow, curcol); /* and output extended line */ + } else + lbound = 0; /* not extended line */ + + /* + * Make sure no lines need to be de-extended because the cursor is no + * longer on them. + */ + wp = wheadp; + while (wp != NULL) { + lp = wp->w_linep; + i = wp->w_toprow; + while (i < wp->w_toprow + wp->w_ntrows) { + if (vscreen[i]->v_flag & VFEXT) { + /* always flag extended lines as changed */ + vscreen[i]->v_flag |= VFCHG; + if ((wp != curwp) || (lp != wp->w_dotp) || + (curcol < ncol - 1)) { + vtmove(i, 0); + for (j = 0; j < llength(lp); ++j) + vtputc(lgetc(lp, j)); + vteeol(); + /* this line no longer is extended */ + vscreen[i]->v_flag &= ~VFEXT; + } + } + lp = lforw(lp); + ++i; + } + /* if garbaged then fix up mode lines */ + if (sgarbf != FALSE) + vscreen[i]->v_flag |= VFCHG; + /* and onward to the next window */ + wp = wp->w_wndp; + } + + if (sgarbf != FALSE) { /* Screen is garbage. */ + sgarbf = FALSE; /* Erase-page clears. */ + epresf = FALSE; /* The message area. */ + tttop = HUGE; /* Forget where you set. */ + ttbot = HUGE; /* scroll region. */ + tthue = CNONE; /* Color unknown. */ + ttmove(0, 0); + tteeop(); + for (i = 0; i < nrow - 1; ++i) { + uline(i, vscreen[i], &blanks); + ucopy(vscreen[i], pscreen[i]); + } + ttmove(currow, curcol - lbound); + ttflush(); + return; + } + if (hflag != FALSE) { /* Hard update? */ + for (i = 0; i < nrow - 1; ++i) {/* Compute hash data. */ + hash(vscreen[i]); + hash(pscreen[i]); + } + offs = 0; /* Get top match. */ + while (offs != nrow - 1) { + vp1 = vscreen[offs]; + vp2 = pscreen[offs]; + if (vp1->v_color != vp2->v_color + || vp1->v_hash != vp2->v_hash) + break; + uline(offs, vp1, vp2); + ucopy(vp1, vp2); + ++offs; + } + if (offs == nrow - 1) { /* Might get it all. */ + ttmove(currow, curcol - lbound); + ttflush(); + return; + } + size = nrow - 1; /* Get bottom match. */ + while (size != offs) { + vp1 = vscreen[size - 1]; + vp2 = pscreen[size - 1]; + if (vp1->v_color != vp2->v_color + || vp1->v_hash != vp2->v_hash) + break; + uline(size - 1, vp1, vp2); + ucopy(vp1, vp2); + --size; + } + if ((size -= offs) == 0) /* Get screen size. */ + panic("Illegal screen size in update"); + setscores(offs, size); /* Do hard update. */ + traceback(offs, size, size, size); + for (i = 0; i < size; ++i) + ucopy(vscreen[offs + i], pscreen[offs + i]); + ttmove(currow, curcol - lbound); + ttflush(); + return; + } + for (i = 0; i < nrow - 1; ++i) { /* Easy update. */ + vp1 = vscreen[i]; + vp2 = pscreen[i]; + if ((vp1->v_flag & VFCHG) != 0) { + uline(i, vp1, vp2); + ucopy(vp1, vp2); + } + } + ttmove(currow, curcol - lbound); + ttflush(); +} + +/* + * Update a saved copy of a line, + * kept in a video structure. The "vvp" is + * the one in the "vscreen". The "pvp" is the one + * in the "pscreen". This is called to make the + * virtual and physical screens the same when + * display has done an update. + */ +void +ucopy(struct video *vvp, struct video *pvp) +{ + vvp->v_flag &= ~VFCHG; /* Changes done. */ + pvp->v_flag = vvp->v_flag; /* Update model. */ + pvp->v_hash = vvp->v_hash; + pvp->v_cost = vvp->v_cost; + pvp->v_color = vvp->v_color; + bcopy(vvp->v_text, pvp->v_text, ncol); +} + +/* + * updext: update the extended line which the cursor is currently on at a + * column greater than the terminal width. The line will be scrolled right or + * left to let the user see where the cursor is. + */ +void +updext(int currow, int curcol) +{ + struct line *lp; /* pointer to current line */ + int j; /* index into line */ + + if (ncol < 2) + return; + + /* + * calculate what column the left bound should be + * (force cursor into middle half of screen) + */ + lbound = curcol - (curcol % (ncol >> 1)) - (ncol >> 2); + + /* + * scan through the line outputing characters to the virtual screen + * once we reach the left edge + */ + vtmove(currow, -lbound); /* start scanning offscreen */ + lp = curwp->w_dotp; /* line to output */ + for (j = 0; j < llength(lp); ++j) /* until the end-of-line */ + vtpute(lgetc(lp, j)); + vteeol(); /* truncate the virtual line */ + vscreen[currow]->v_text[0] = '$'; /* and put a '$' in column 1 */ +} + +/* + * Update a single line. This routine only + * uses basic functionality (no insert and delete character, + * but erase to end of line). The "vvp" points at the video + * structure for the line on the virtual screen, and the "pvp" + * is the same for the physical screen. Avoid erase to end of + * line when updating CMODE color lines, because of the way that + * reverse video works on most terminals. + */ +void +uline(int row, struct video *vvp, struct video *pvp) +{ + char *cp1; + char *cp2; + char *cp3; + char *cp4; + char *cp5; + int nbflag; + + if (vvp->v_color != pvp->v_color) { /* Wrong color, do a */ + ttmove(row, 0); /* full redraw. */ +#ifdef STANDOUT_GLITCH + if (pvp->v_color != CTEXT && magic_cookie_glitch >= 0) + tteeol(); +#endif + ttcolor(vvp->v_color); +#ifdef STANDOUT_GLITCH + cp1 = &vvp->v_text[magic_cookie_glitch > 0 ? magic_cookie_glitch : 0]; + /* + * The odd code for magic_cookie_glitch==0 is to avoid + * putting the invisible glitch character on the next line. + * (Hazeltine executive 80 model 30) + */ + cp2 = &vvp->v_text[ncol - (magic_cookie_glitch >= 0 ? + (magic_cookie_glitch != 0 ? magic_cookie_glitch : 1) : 0)]; +#else + cp1 = &vvp->v_text[0]; + cp2 = &vvp->v_text[ncol]; +#endif + while (cp1 != cp2) { + ttputc(*cp1++); + ++ttcol; + } + ttcolor(CTEXT); + return; + } + cp1 = &vvp->v_text[0]; /* Compute left match. */ + cp2 = &pvp->v_text[0]; + while (cp1 != &vvp->v_text[ncol] && cp1[0] == cp2[0]) { + ++cp1; + ++cp2; + } + if (cp1 == &vvp->v_text[ncol]) /* All equal. */ + return; + nbflag = FALSE; + cp3 = &vvp->v_text[ncol]; /* Compute right match. */ + cp4 = &pvp->v_text[ncol]; + while (cp3[-1] == cp4[-1]) { + --cp3; + --cp4; + if (cp3[0] != ' ') /* Note non-blanks in */ + nbflag = TRUE; /* the right match. */ + } + cp5 = cp3; /* Is erase good? */ + if (nbflag == FALSE && vvp->v_color == CTEXT) { + while (cp5 != cp1 && cp5[-1] == ' ') + --cp5; + /* Alcyon hack */ + if ((int) (cp3 - cp5) <= tceeol) + cp5 = cp3; + } + /* Alcyon hack */ + ttmove(row, (int) (cp1 - &vvp->v_text[0])); +#ifdef STANDOUT_GLITCH + if (vvp->v_color != CTEXT && magic_cookie_glitch > 0) { + if (cp1 < &vvp->v_text[magic_cookie_glitch]) + cp1 = &vvp->v_text[magic_cookie_glitch]; + if (cp5 > &vvp->v_text[ncol - magic_cookie_glitch]) + cp5 = &vvp->v_text[ncol - magic_cookie_glitch]; + } else if (magic_cookie_glitch < 0) +#endif + ttcolor(vvp->v_color); + while (cp1 != cp5) { + ttputc(*cp1++); + ++ttcol; + } + if (cp5 != cp3) /* Do erase. */ + tteeol(); +} + +/* + * Redisplay the mode line for the window pointed to by the "wp". + * This is the only routine that has any idea of how the mode line is + * formatted. You can change the modeline format by hacking at this + * routine. Called by "update" any time there is a dirty window. Note + * that if STANDOUT_GLITCH is defined, first and last magic_cookie_glitch + * characters may never be seen. + */ +void +modeline(struct mgwin *wp, int modelinecolor) +{ + int n, md; + struct buffer *bp; + char sl[21]; /* Overkill. Space for 2^64 in base 10. */ + int len; + + n = wp->w_toprow + wp->w_ntrows; /* Location. */ + vscreen[n]->v_color = modelinecolor; /* Mode line color. */ + vscreen[n]->v_flag |= (VFCHG | VFHBAD); /* Recompute, display. */ + vtmove(n, 0); /* Seek to right line. */ + bp = wp->w_bufp; + vtputc('-'); + vtputc('-'); + if ((bp->b_flag & BFREADONLY) != 0) { + vtputc('%'); + if ((bp->b_flag & BFCHG) != 0) + vtputc('*'); + else + vtputc('%'); + } else if ((bp->b_flag & BFCHG) != 0) { /* "*" if changed. */ + vtputc('*'); + vtputc('*'); + } else { + vtputc('-'); + vtputc('-'); + } + vtputc('-'); + n = 5; + n += vtputs("Mg: "); + if (bp->b_bname[0] != '\0') + n += vtputs(&(bp->b_bname[0])); + while (n < 42) { /* Pad out with blanks. */ + vtputc(' '); + ++n; + } + vtputc('('); + ++n; + for (md = 0; ; ) { + n += vtputs(bp->b_modes[md]->p_name); + if (++md > bp->b_nmodes) + break; + vtputc('-'); + ++n; + } + /* XXX These should eventually move to a real mode */ + if (macrodef == TRUE) + n += vtputs("-def"); + if (globalwd == TRUE) + n += vtputs("-gwd"); + vtputc(')'); + ++n; + + if (linenos && colnos) + len = snprintf(sl, sizeof(sl), "--L%d--C%d", wp->w_dotline, + getcolpos(wp)); + else if (linenos) + len = snprintf(sl, sizeof(sl), "--L%d", wp->w_dotline); + else if (colnos) + len = snprintf(sl, sizeof(sl), "--C%d", getcolpos(wp)); + if ((linenos || colnos) && len < sizeof(sl) && len != -1) + n += vtputs(sl); + + while (n < ncol) { /* Pad out. */ + vtputc('-'); + ++n; + } +} + +/* + * Output a string to the mode line, report how long it was. + */ +int +vtputs(const char *s) +{ + int n = 0; + + while (*s != '\0') { + vtputc(*s++); + ++n; + } + return (n); +} + +/* + * Compute the hash code for the line pointed to by the "vp". + * Recompute it if necessary. Also set the approximate redisplay + * cost. The validity of the hash code is marked by a flag bit. + * The cost understand the advantages of erase to end of line. + * Tuned for the VAX by Bob McNamara; better than it used to be on + * just about any machine. + */ +void +hash(struct video *vp) +{ + int i, n; + char *s; + + if ((vp->v_flag & VFHBAD) != 0) { /* Hash bad. */ + s = &vp->v_text[ncol - 1]; + for (i = ncol; i != 0; --i, --s) + if (*s != ' ') + break; + n = ncol - i; /* Erase cheaper? */ + if (n > tceeol) + n = tceeol; + vp->v_cost = i + n; /* Bytes + blanks. */ + for (n = 0; i != 0; --i, --s) + n = (n << 5) + n + *s; + vp->v_hash = n; /* Hash code. */ + vp->v_flag &= ~VFHBAD; /* Flag as all done. */ + } +} + +/* + * Compute the Insert-Delete + * cost matrix. The dynamic programming algorithm + * described by James Gosling is used. This code assumes + * that the line above the echo line is the last line involved + * in the scroll region. This is easy to arrange on the VT100 + * because of the scrolling region. The "offs" is the origin 0 + * offset of the first row in the virtual/physical screen that + * is being updated; the "size" is the length of the chunk of + * screen being updated. For a full screen update, use offs=0 + * and size=nrow-1. + * + * Older versions of this code implemented the score matrix by + * a two dimensional array of SCORE nodes. This put all kinds of + * multiply instructions in the code! This version is written to + * use a linear array and pointers, and contains no multiplication + * at all. The code has been carefully looked at on the VAX, with + * only marginal checking on other machines for efficiency. In + * fact, this has been tuned twice! Bob McNamara tuned it even + * more for the VAX, which is a big issue for him because of + * the 66 line X displays. + * + * On some machines, replacing the "for (i=1; i<=size; ++i)" with + * i = 1; do { } while (++i <=size)" will make the code quite a + * bit better; but it looks ugly. + */ +void +setscores(int offs, int size) +{ + struct score *sp; + struct score *sp1; + struct video **vp, **pp; + struct video **vbase, **pbase; + int tempcost; + int bestcost; + int j, i; + + vbase = &vscreen[offs - 1]; /* By hand CSE's. */ + pbase = &pscreen[offs - 1]; + score[0].s_itrace = 0; /* [0, 0] */ + score[0].s_jtrace = 0; + score[0].s_cost = 0; + sp = &score[1]; /* Row 0, inserts. */ + tempcost = 0; + vp = &vbase[1]; + for (j = 1; j <= size; ++j) { + sp->s_itrace = 0; + sp->s_jtrace = j - 1; + tempcost += tcinsl; + tempcost += (*vp)->v_cost; + sp->s_cost = tempcost; + ++vp; + ++sp; + } + sp = &score[nrow]; /* Column 0, deletes. */ + tempcost = 0; + for (i = 1; i <= size; ++i) { + sp->s_itrace = i - 1; + sp->s_jtrace = 0; + tempcost += tcdell; + sp->s_cost = tempcost; + sp += nrow; + } + sp1 = &score[nrow + 1]; /* [1, 1]. */ + pp = &pbase[1]; + for (i = 1; i <= size; ++i) { + sp = sp1; + vp = &vbase[1]; + for (j = 1; j <= size; ++j) { + sp->s_itrace = i - 1; + sp->s_jtrace = j; + bestcost = (sp - nrow)->s_cost; + if (j != size) /* Cd(A[i])=0 @ Dis. */ + bestcost += tcdell; + tempcost = (sp - 1)->s_cost; + tempcost += (*vp)->v_cost; + if (i != size) /* Ci(B[j])=0 @ Dsj. */ + tempcost += tcinsl; + if (tempcost < bestcost) { + sp->s_itrace = i; + sp->s_jtrace = j - 1; + bestcost = tempcost; + } + tempcost = (sp - nrow - 1)->s_cost; + if ((*pp)->v_color != (*vp)->v_color + || (*pp)->v_hash != (*vp)->v_hash) + tempcost += (*vp)->v_cost; + if (tempcost < bestcost) { + sp->s_itrace = i - 1; + sp->s_jtrace = j - 1; + bestcost = tempcost; + } + sp->s_cost = bestcost; + ++sp; /* Next column. */ + ++vp; + } + ++pp; + sp1 += nrow; /* Next row. */ + } +} + +/* + * Trace back through the dynamic programming cost + * matrix, and update the screen using an optimal sequence + * of redraws, insert lines, and delete lines. The "offs" is + * the origin 0 offset of the chunk of the screen we are about to + * update. The "i" and "j" are always started in the lower right + * corner of the matrix, and imply the size of the screen. + * A full screen traceback is called with offs=0 and i=j=nrow-1. + * There is some do-it-yourself double subscripting here, + * which is acceptable because this routine is much less compute + * intensive then the code that builds the score matrix! + */ +void +traceback(int offs, int size, int i, int j) +{ + int itrace, jtrace; + int k; + int ninsl, ndraw, ndell; + + if (i == 0 && j == 0) /* End of update. */ + return; + itrace = score[(nrow * i) + j].s_itrace; + jtrace = score[(nrow * i) + j].s_jtrace; + if (itrace == i) { /* [i, j-1] */ + ninsl = 0; /* Collect inserts. */ + if (i != size) + ninsl = 1; + ndraw = 1; + while (itrace != 0 || jtrace != 0) { + if (score[(nrow * itrace) + jtrace].s_itrace != itrace) + break; + jtrace = score[(nrow * itrace) + jtrace].s_jtrace; + if (i != size) + ++ninsl; + ++ndraw; + } + traceback(offs, size, itrace, jtrace); + if (ninsl != 0) { + ttcolor(CTEXT); + ttinsl(offs + j - ninsl, offs + size - 1, ninsl); + } + do { /* B[j], A[j] blank. */ + k = offs + j - ndraw; + uline(k, vscreen[k], &blanks); + } while (--ndraw); + return; + } + if (jtrace == j) { /* [i-1, j] */ + ndell = 0; /* Collect deletes. */ + if (j != size) + ndell = 1; + while (itrace != 0 || jtrace != 0) { + if (score[(nrow * itrace) + jtrace].s_jtrace != jtrace) + break; + itrace = score[(nrow * itrace) + jtrace].s_itrace; + if (j != size) + ++ndell; + } + if (ndell != 0) { + ttcolor(CTEXT); + ttdell(offs + i - ndell, offs + size - 1, ndell); + } + traceback(offs, size, itrace, jtrace); + return; + } + traceback(offs, size, itrace, jtrace); + k = offs + j - 1; + uline(k, vscreen[k], pscreen[offs + i - 1]); +} Index: contrib/mg/echo.c =================================================================== --- /dev/null +++ contrib/mg/echo.c @@ -0,0 +1,1020 @@ +/* $OpenBSD: echo.c,v 1.68 2021/03/02 15:03:35 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Echo line reading and writing. + * + * Common routines for reading and writing characters in the echo line area + * of the display screen. Used by the entire known universe. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" +#include "key.h" +#include "macro.h" + +static char *veread(const char *, char *, size_t, int, va_list) + __attribute__((__format__ (printf, 1, 0))); +static int complt(int, int, char *, size_t, int, int *); +static int complt_list(int, char *, int); +static void eformat(const char *, va_list) + __attribute__((__format__ (printf, 1, 0))); +static void eputi(int, int); +static void eputl(long, int); +static void eputs(const char *); +static void eputc(char); +static struct list *copy_list(struct list *); + +int epresf = FALSE; /* stuff in echo line flag */ + +/* + * Erase the echo line. + */ +void +eerase(void) +{ + ttcolor(CTEXT); + ttmove(nrow - 1, 0); + tteeol(); + ttflush(); + epresf = FALSE; +} + +/* + * Ask a "yes" or "no" question. Return ABORT if the user answers the + * question with the abort ("^G") character. Return FALSE for "no" and + * TRUE for "yes". No formatting services are available. No newline + * required. + */ +int +eyorn(const char *sp) +{ + int s; + + if (inmacro) + return (TRUE); + + ewprintf("%s? (y or n) ", sp); + for (;;) { + s = getkey(FALSE); + if (s == 'y' || s == 'Y' || s == ' ') { + ewprintf(""); + return (TRUE); + } + if (s == 'n' || s == 'N' || s == CCHR('M')) { + ewprintf(""); + return (FALSE); + } + if (s == CCHR('G')) { + ewprintf(""); + return (ctrlg(FFRAND, 1)); + } + ewprintf("Please answer y or n. %s? (y or n) ", sp); + } + /* NOTREACHED */ +} + +/* + * Ask a "yes", "no" or "revert" question. Return ABORT if the user answers + * the question with the abort ("^G") character. Return FALSE for "no", + * TRUE for "yes" and REVERT for "revert". No formatting services are + * available. No newline required. + */ +int +eynorr(const char *sp) +{ + int s; + + if (inmacro) + return (TRUE); + + ewprintf("%s? (y, n or r) ", sp); + for (;;) { + s = getkey(FALSE); + if (s == 'y' || s == 'Y' || s == ' ') { + ewprintf(""); + return (TRUE); + } + if (s == 'n' || s == 'N' || s == CCHR('M')) { + ewprintf(""); + return (FALSE); + } + if (s == 'r' || s == 'R') { + ewprintf(""); + return (REVERT); + } + if (s == CCHR('G')) { + ewprintf(""); + return (ctrlg(FFRAND, 1)); + } + ewprintf("Please answer y, n or r."); + } + /* NOTREACHED */ +} + +/* + * Like eyorn, but for more important questions. User must type all of + * "yes" or "no" and the trailing newline. + */ +int +eyesno(const char *sp) +{ + char buf[64], *rep; + + if (inmacro) + return (TRUE); + + rep = eread("%s? (yes or no) ", buf, sizeof(buf), + EFNUL | EFNEW | EFCR, sp); + for (;;) { + if (rep == NULL) { + ewprintf(""); + return (ABORT); + } + if (rep[0] != '\0') { + if (macrodef) { + struct line *lp = maclcur; + + maclcur = lp->l_bp; + maclcur->l_fp = lp->l_fp; + free(lp); + } + if (strcasecmp(rep, "yes") == 0) { + ewprintf(""); + return (TRUE); + } + if (strcasecmp(rep, "no") == 0) { + ewprintf(""); + return (FALSE); + } + } + rep = eread("Please answer yes or no. %s? (yes or no) ", + buf, sizeof(buf), EFNUL | EFNEW | EFCR, sp); + } + /* NOTREACHED */ +} + +/* + * This is the general "read input from the echo line" routine. The basic + * idea is that the prompt string "prompt" is written to the echo line, and + * a one line reply is read back into the supplied "buf" (with maximum + * length "len"). + * XXX: When checking for an empty return value, always check rep, *not* buf + * as buf may be freed in pathological cases. + */ +char * +eread(const char *fmt, char *buf, size_t nbuf, int flag, ...) +{ + va_list ap; + char *rep; + + va_start(ap, flag); + rep = veread(fmt, buf, nbuf, flag, ap); + va_end(ap); + return (rep); +} + +static char * +veread(const char *fp, char *buf, size_t nbuf, int flag, va_list ap) +{ + int dynbuf = (buf == NULL); + int cpos, epos; /* cursor, end position in buf */ + int c, i, y; + int cplflag; /* display completion list */ + int cwin = FALSE; /* completion list created */ + int mr, ml; /* match left/right arrows */ + int esc; /* position in esc pattern */ + struct buffer *bp; /* completion list buffer */ + struct mgwin *wp; /* window for compl list */ + int match; /* esc match found */ + int cc, rr; /* saved ttcol, ttrow */ + char *ret; /* return value */ + + static char emptyval[] = ""; /* XXX hackish way to return err msg*/ + + if (inmacro) { + if (dynbuf) { + if ((buf = malloc(maclcur->l_used + 1)) == NULL) + return (NULL); + } else if (maclcur->l_used >= nbuf) + return (NULL); + bcopy(maclcur->l_text, buf, maclcur->l_used); + buf[maclcur->l_used] = '\0'; + maclcur = maclcur->l_fp; + return (buf); + } + epos = cpos = 0; + ml = mr = esc = 0; + cplflag = FALSE; + + if ((flag & EFNEW) != 0 || ttrow != nrow - 1) { + ttcolor(CTEXT); + ttmove(nrow - 1, 0); + epresf = TRUE; + } else + eputc(' '); + eformat(fp, ap); + if ((flag & EFDEF) != 0) { + if (buf == NULL) + return (NULL); + eputs(buf); + epos = cpos += strlen(buf); + } + tteeol(); + ttflush(); + for (;;) { + c = getkey(FALSE); + if ((flag & EFAUTO) != 0 && c == CCHR('I')) { + if (cplflag == TRUE) { + complt_list(flag, buf, cpos); + cwin = TRUE; + } else if (complt(flag, c, buf, nbuf, epos, &i) == TRUE) { + cplflag = TRUE; + epos += i; + cpos = epos; + } + continue; + } + cplflag = FALSE; + + if (esc > 0) { /* ESC sequence started */ + match = 0; + if (ml == esc && key_left[ml] && c == key_left[ml]) { + match++; + if (key_left[++ml] == '\0') { + c = CCHR('B'); + esc = 0; + } + } + if (mr == esc && key_right[mr] && c == key_right[mr]) { + match++; + if (key_right[++mr] == '\0') { + c = CCHR('F'); + esc = 0; + } + } + if (match == 0) { + esc = 0; + continue; + /* hack. how do we know esc pattern is done? */ + } + if (esc > 0) { + esc++; + continue; + } + } + switch (c) { + case CCHR('A'): /* start of line */ + while (cpos > 0) { + if (ISCTRL(buf[--cpos]) != FALSE) { + ttputc('\b'); + --ttcol; + } + ttputc('\b'); + --ttcol; + } + ttflush(); + break; + case CCHR('D'): + if (cpos != epos) { + tteeol(); + epos--; + rr = ttrow; + cc = ttcol; + for (i = cpos; i < epos; i++) { + buf[i] = buf[i + 1]; + eputc(buf[i]); + } + ttmove(rr, cc); + ttflush(); + } + break; + case CCHR('E'): /* end of line */ + while (cpos < epos) { + eputc(buf[cpos++]); + } + ttflush(); + break; + case CCHR('B'): /* back */ + if (cpos > 0) { + if (ISCTRL(buf[--cpos]) != FALSE) { + ttputc('\b'); + --ttcol; + } + ttputc('\b'); + --ttcol; + ttflush(); + } + break; + case CCHR('F'): /* forw */ + if (cpos < epos) { + eputc(buf[cpos++]); + ttflush(); + } + break; + case CCHR('Y'): /* yank from kill buffer */ + i = 0; + while ((y = kremove(i++)) >= 0 && y != *curbp->b_nlchr) { + int t; + if (dynbuf && epos + 1 >= nbuf) { + void *newp; + size_t newsize = epos + epos + 16; + if ((newp = realloc(buf, newsize)) + == NULL) + goto memfail; + buf = newp; + nbuf = newsize; + } + if (!dynbuf && epos + 1 >= nbuf) { + dobeep(); + ewprintf("Line too long. Press Control-g to escape."); + goto skipkey; + } + for (t = epos; t > cpos; t--) + buf[t] = buf[t - 1]; + buf[cpos++] = (char)y; + epos++; + eputc((char)y); + cc = ttcol; + rr = ttrow; + for (t = cpos; t < epos; t++) + eputc(buf[t]); + ttmove(rr, cc); + } + ttflush(); + break; + case CCHR('K'): /* copy here-EOL to kill buffer */ + kdelete(); + for (i = cpos; i < epos; i++) + kinsert(buf[i], KFORW); + tteeol(); + epos = cpos; + ttflush(); + break; + case CCHR('['): + ml = mr = esc = 1; + break; + case CCHR('J'): + c = CCHR('M'); + /* FALLTHROUGH */ + case CCHR('M'): /* return, done */ + /* if there's nothing in the minibuffer, abort */ + if (epos == 0 && !(flag & EFNUL)) { + (void)ctrlg(FFRAND, 0); + ttflush(); + return (NULL); + } + if ((flag & EFFUNC) != 0) { + if (complt(flag, c, buf, nbuf, epos, &i) + == FALSE) + continue; + if (i > 0) + epos += i; + } + buf[epos] = '\0'; + if ((flag & EFCR) != 0) { + ttputc(CCHR('M')); + ttflush(); + } + if (macrodef) { + struct line *lp; + + if ((lp = lalloc(cpos)) == NULL) + goto memfail; + lp->l_fp = maclcur->l_fp; + maclcur->l_fp = lp; + lp->l_bp = maclcur; + maclcur = lp; + bcopy(buf, lp->l_text, cpos); + } + ret = buf; + goto done; + case CCHR('G'): /* bell, abort */ + eputc(CCHR('G')); + (void)ctrlg(FFRAND, 0); + ttflush(); + ret = NULL; + goto done; + case CCHR('H'): /* rubout, erase */ + case CCHR('?'): + if (cpos != 0) { + y = buf[--cpos]; + epos--; + ttputc('\b'); + ttcol--; + if (ISCTRL(y) != FALSE) { + ttputc('\b'); + ttcol--; + } + rr = ttrow; + cc = ttcol; + for (i = cpos; i < epos; i++) { + buf[i] = buf[i + 1]; + eputc(buf[i]); + } + ttputc(' '); + if (ISCTRL(y) != FALSE) { + ttputc(' '); + ttputc('\b'); + } + ttputc('\b'); + ttmove(rr, cc); + ttflush(); + } + break; + case CCHR('X'): /* kill line */ + case CCHR('U'): + while (cpos != 0) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + if (ISCTRL(buf[--cpos]) != FALSE) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + } + epos--; + } + ttflush(); + break; + case CCHR('W'): /* kill to beginning of word */ + while ((cpos > 0) && !ISWORD(buf[cpos - 1])) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + if (ISCTRL(buf[--cpos]) != FALSE) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + } + epos--; + } + while ((cpos > 0) && ISWORD(buf[cpos - 1])) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + if (ISCTRL(buf[--cpos]) != FALSE) { + ttputc('\b'); + ttputc(' '); + ttputc('\b'); + --ttcol; + } + epos--; + } + ttflush(); + break; + case CCHR('\\'): + case CCHR('Q'): /* quote next */ + c = getkey(FALSE); + /* FALLTHROUGH */ + default: + if (dynbuf && epos + 1 >= nbuf) { + void *newp; + size_t newsize = epos + epos + 16; + if ((newp = realloc(buf, newsize)) == NULL) + goto memfail; + buf = newp; + nbuf = newsize; + } + if (!dynbuf && epos + 1 >= nbuf) { + dobeep(); + ewprintf("Line too long. Press Control-g to escape."); + goto skipkey; + } + for (i = epos; i > cpos; i--) + buf[i] = buf[i - 1]; + buf[cpos++] = (char)c; + epos++; + eputc((char)c); + cc = ttcol; + rr = ttrow; + for (i = cpos; i < epos; i++) + eputc(buf[i]); + ttmove(rr, cc); + ttflush(); + } + +skipkey: /* ignore key press */ +; + } +done: + if (cwin == TRUE) { + /* blow away cpltion window */ + bp = bfind("*Completions*", TRUE); + if ((wp = popbuf(bp, WEPHEM)) != NULL) { + if (wp->w_flag & WEPHEM) { + curwp = wp; + delwind(FFRAND, 1); + } else { + killbuffer(bp); + } + } + } + return (ret); +memfail: + if (dynbuf && buf) + free(buf); + dobeep(); + ewprintf("Out of memory"); + return (emptyval); +} + +/* + * Do completion on a list of objects. + * c is SPACE, TAB, or CR + * return TRUE if matched (or partially matched) + * FALSE is result is ambiguous, + * ABORT on error. + */ +static int +complt(int flags, int c, char *buf, size_t nbuf, int cpos, int *nx) +{ + struct list *lh, *lh2; + struct list *wholelist = NULL; + int i, nxtra, nhits, bxtra, msglen, nshown; + int wflag = FALSE; + char *msg; + + lh = lh2 = NULL; + + if ((flags & EFFUNC) != 0) { + buf[cpos] = '\0'; + wholelist = lh = complete_function_list(buf); + } else if ((flags & EFBUF) != 0) { + lh = &(bheadp->b_list); + } else if ((flags & EFFILE) != 0) { + buf[cpos] = '\0'; + wholelist = lh = make_file_list(buf); + } else + panic("broken complt call: flags"); + + if (c == ' ') + wflag = TRUE; + else if (c != '\t' && c != CCHR('M')) + panic("broken complt call: c"); + + nhits = 0; + nxtra = HUGE; + + for (; lh != NULL; lh = lh->l_next) { + if (memcmp(buf, lh->l_name, cpos) != 0) + continue; + if (nhits == 0) + lh2 = lh; + ++nhits; + if (lh->l_name[cpos] == '\0') + nxtra = -1; /* exact match */ + else { + bxtra = getxtra(lh, lh2, cpos, wflag); + if (bxtra < nxtra) + nxtra = bxtra; + lh2 = lh; + } + } + if (nhits == 0) + msg = " [No match]"; + else if (nhits > 1 && nxtra == 0) + msg = " [Ambiguous. Ctrl-G to cancel]"; + else { + /* + * Being lazy - ought to check length, but all things + * autocompleted have known types/lengths. + */ + if (nxtra < 0 && nhits > 1 && c == ' ') + nxtra = 1; /* ??? */ + for (i = 0; i < nxtra && cpos < nbuf; ++i) { + buf[cpos] = lh2->l_name[cpos]; + eputc(buf[cpos++]); + } + /* XXX should grow nbuf */ + ttflush(); + free_file_list(wholelist); + *nx = nxtra; + if (nxtra < 0 && c != CCHR('M')) /* exact */ + *nx = 0; + return (TRUE); + } + + /* + * wholelist is NULL if we are doing buffers. Want to free lists + * that were created for us, but not the buffer list! + */ + free_file_list(wholelist); + + /* Set up backspaces, etc., being mindful of echo line limit. */ + msglen = strlen(msg); + nshown = (ttcol + msglen + 2 > ncol) ? + ncol - ttcol - 2 : msglen; + eputs(msg); + ttcol -= (i = nshown); /* update ttcol! */ + while (i--) /* move back before msg */ + ttputc('\b'); + ttflush(); /* display to user */ + i = nshown; + while (i--) /* blank out on next flush */ + eputc(' '); + ttcol -= (i = nshown); /* update ttcol on BS's */ + while (i--) + ttputc('\b'); /* update ttcol again! */ + *nx = nxtra; + return ((nhits > 0) ? TRUE : FALSE); +} + +/* + * Do completion on a list of objects, listing instead of completing. + */ +static int +complt_list(int flags, char *buf, int cpos) +{ + struct list *lh, *lh2, *lh3; + struct list *wholelist = NULL; + struct buffer *bp; + int i, maxwidth, width; + int preflen = 0; + int oldrow = ttrow; + int oldcol = ttcol; + int oldhue = tthue; + char *linebuf; + size_t linesize, len; + char *cp; + + lh = NULL; + + ttflush(); + + /* The results are put into a completion buffer. */ + bp = bfind("*Completions*", TRUE); + if (bclear(bp) == FALSE) + return (FALSE); + bp->b_flag |= BFREADONLY; + + /* + * First get the list of objects. This list may contain only + * the ones that complete what has been typed, or may be the + * whole list of all objects of this type. They are filtered + * later in any case. Set wholelist if the list has been + * cons'ed up just for us, so we can free it later. We have + * to copy the buffer list for this function even though we + * didn't for complt. The sorting code does destructive + * changes to the list, which we don't want to happen to the + * main buffer list! + */ + if ((flags & EFBUF) != 0) + wholelist = lh = copy_list(&(bheadp->b_list)); + else if ((flags & EFFUNC) != 0) { + buf[cpos] = '\0'; + wholelist = lh = complete_function_list(buf); + } else if ((flags & EFFILE) != 0) { + buf[cpos] = '\0'; + wholelist = lh = make_file_list(buf); + /* + * We don't want to display stuff up to the / for file + * names preflen is the list of a prefix of what the + * user typed that should not be displayed. + */ + cp = strrchr(buf, '/'); + if (cp) + preflen = cp - buf + 1; + } else + panic("broken complt call: flags"); + + /* + * Sort the list, since users expect to see it in alphabetic + * order. + */ + lh2 = lh; + while (lh2 != NULL) { + lh3 = lh2->l_next; + while (lh3 != NULL) { + if (strcmp(lh2->l_name, lh3->l_name) > 0) { + cp = lh2->l_name; + lh2->l_name = lh3->l_name; + lh3->l_name = cp; + } + lh3 = lh3->l_next; + } + lh2 = lh2->l_next; + } + + /* + * First find max width of object to be displayed, so we can + * put several on a line. + */ + maxwidth = 0; + lh2 = lh; + while (lh2 != NULL) { + for (i = 0; i < cpos; ++i) { + if (buf[i] != lh2->l_name[i]) + break; + } + if (i == cpos) { + width = strlen(lh2->l_name); + if (width > maxwidth) + maxwidth = width; + } + lh2 = lh2->l_next; + } + maxwidth += 1 - preflen; + + /* + * Now do the display. Objects are written into linebuf until + * it fills, and then put into the help buffer. + */ + linesize = (ncol > maxwidth ? ncol : maxwidth) + 1; + if ((linebuf = malloc(linesize)) == NULL) { + free_file_list(wholelist); + return (FALSE); + } + width = 0; + + /* + * We're going to strlcat() into the buffer, so it has to be + * NUL terminated. + */ + linebuf[0] = '\0'; + for (lh2 = lh; lh2 != NULL; lh2 = lh2->l_next) { + for (i = 0; i < cpos; ++i) { + if (buf[i] != lh2->l_name[i]) + break; + } + /* if we have a match */ + if (i == cpos) { + /* if it wraps */ + if ((width + maxwidth) > ncol) { + addline(bp, linebuf); + linebuf[0] = '\0'; + width = 0; + } + len = strlcat(linebuf, lh2->l_name + preflen, + linesize); + width += maxwidth; + if (len < width && width < linesize) { + /* pad so the objects nicely line up */ + memset(linebuf + len, ' ', + maxwidth - strlen(lh2->l_name + preflen)); + linebuf[width] = '\0'; + } + } + } + if (width > 0) + addline(bp, linebuf); + free(linebuf); + + /* + * Note that we free lists only if they are put in wholelist lists + * that were built just for us should be freed. However when we use + * the buffer list, obviously we don't want it freed. + */ + free_file_list(wholelist); + popbuftop(bp, WEPHEM); /* split the screen and put up the help + * buffer */ + update(CMODE); /* needed to make the new stuff actually + * appear */ + ttmove(oldrow, oldcol); /* update leaves cursor in arbitrary place */ + ttcolor(oldhue); /* with arbitrary color */ + ttflush(); + return (0); +} + +/* + * The "lp1" and "lp2" point to list structures. The "cpos" is a horizontal + * position in the name. Return the longest block of characters that can be + * autocompleted at this point. Sometimes the two symbols are the same, but + * this is normal. + */ +int +getxtra(struct list *lp1, struct list *lp2, int cpos, int wflag) +{ + int i; + + i = cpos; + for (;;) { + if (lp1->l_name[i] != lp2->l_name[i]) + break; + if (lp1->l_name[i] == '\0') + break; + ++i; + if (wflag && !ISWORD(lp1->l_name[i - 1])) + break; + } + return (i - cpos); +} + +/* + * Special "printf" for the echo line. Each call to "ewprintf" starts a + * new line in the echo area, and ends with an erase to end of the echo + * line. The formatting is done by a call to the standard formatting + * routine. + */ +void +ewprintf(const char *fmt, ...) +{ + va_list ap; + + if (inmacro) + return; + + va_start(ap, fmt); + ttcolor(CTEXT); + ttmove(nrow - 1, 0); + eformat(fmt, ap); + va_end(ap); + tteeol(); + ttflush(); + epresf = TRUE; +} + +/* + * Printf style formatting. This is called by "ewprintf" to provide + * formatting services to its clients. The move to the start of the + * echo line, and the erase to the end of the echo line, is done by + * the caller. + * %c prints the "name" of the supplied character. + * %k prints the name of the current key (and takes no arguments). + * %d prints a decimal integer + * %o prints an octal integer + * %p prints a pointer + * %s prints a string + * %ld prints a long word + * Anything else is echoed verbatim + */ +static void +eformat(const char *fp, va_list ap) +{ + char kname[NKNAME], tmp[100], *cp; + int c; + + while ((c = *fp++) != '\0') { + if (c != '%') + eputc(c); + else { + c = *fp++; + switch (c) { + case 'c': + getkeyname(kname, sizeof(kname), + va_arg(ap, int)); + eputs(kname); + break; + + case 'k': + for (cp = kname, c = 0; c < key.k_count; c++) { + if (c) + *cp++ = ' '; + cp = getkeyname(cp, sizeof(kname) - + (cp - kname) - 1, key.k_chars[c]); + } + eputs(kname); + break; + + case 'd': + eputi(va_arg(ap, int), 10); + break; + + case 'o': + eputi(va_arg(ap, int), 8); + break; + + case 'p': + snprintf(tmp, sizeof(tmp), "%p", + va_arg(ap, void *)); + eputs(tmp); + break; + + case 's': + eputs(va_arg(ap, char *)); + break; + + case 'l': + /* explicit longword */ + c = *fp++; + switch (c) { + case 'd': + eputl(va_arg(ap, long), 10); + break; + default: + eputc(c); + break; + } + break; + + default: + eputc(c); + } + } + } +} + +/* + * Put integer, in radix "r". + */ +static void +eputi(int i, int r) +{ + int q; + + if (i < 0) { + eputc('-'); + i = -i; + } + if ((q = i / r) != 0) + eputi(q, r); + eputc(i % r + '0'); +} + +/* + * Put long, in radix "r". + */ +static void +eputl(long l, int r) +{ + long q; + + if (l < 0) { + eputc('-'); + l = -l; + } + if ((q = l / r) != 0) + eputl(q, r); + eputc((int)(l % r) + '0'); +} + +/* + * Put string. + */ +static void +eputs(const char *s) +{ + int c; + + while ((c = *s++) != '\0') + eputc(c); +} + +/* + * Put character. Watch for control characters, and for the line getting + * too long. + */ +static void +eputc(char c) +{ + if (ttcol + 2 < ncol) { + if (ISCTRL(c)) { + eputc('^'); + c = CCHR(c); + } + ttputc(c); + ++ttcol; + } +} + +void +free_file_list(struct list *lp) +{ + struct list *next; + + while (lp) { + next = lp->l_next; + free(lp->l_name); + free(lp); + lp = next; + } +} + +static struct list * +copy_list(struct list *lp) +{ + struct list *current, *last, *nxt; + + last = NULL; + while (lp) { + current = malloc(sizeof(struct list)); + if (current == NULL) { + /* Free what we have allocated so far */ + for (current = last; current; current = nxt) { + nxt = current->l_next; + free(current->l_name); + free(current); + } + return (NULL); + } + current->l_next = last; + current->l_name = strdup(lp->l_name); + last = current; + lp = lp->l_next; + } + return (last); +} Index: contrib/mg/extend.c =================================================================== --- /dev/null +++ contrib/mg/extend.c @@ -0,0 +1,942 @@ +/* $OpenBSD: extend.c,v 1.75 2021/05/06 14:16:12 lum Exp $ */ +/* This file is in the public domain. */ + +/* + * Extended (M-x) commands, rebinding, and startup file processing. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "chrdef.h" +#include "def.h" +#include "funmap.h" +#include "kbd.h" +#include "key.h" +#include "macro.h" + +static int remap(KEYMAP *, int, PF, KEYMAP *); +static KEYMAP *reallocmap(KEYMAP *); +static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *); +static int dobind(KEYMAP *, const char *, int); +static char *parsetoken(char *); +static int bindkey(KEYMAP **, const char *, KCHAR *, int); + +/* + * Insert a string, mainly for use from macros (created by selfinsert). + */ +/* ARGSUSED */ +int +insert(int f, int n) +{ + char buf[BUFSIZE], *bufp, *cp; + int count, c; + + if (inmacro) { + while (--n >= 0) { + for (count = 0; count < maclcur->l_used; count++) { + if ((((c = maclcur->l_text[count]) == + *curbp->b_nlchr) + ? lnewline() : linsert(1, c)) != TRUE) + return (FALSE); + } + } + maclcur = maclcur->l_fp; + return (TRUE); + } + if (n == 1) + /* CFINS means selfinsert can tack on the end */ + thisflag |= CFINS; + + if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + while (--n >= 0) { + cp = buf; + while (*cp) { + if (((*cp == *curbp->b_nlchr) ? + lnewline() : linsert(1, *cp)) + != TRUE) + return (FALSE); + cp++; + } + } + return (TRUE); +} + +/* + * Bind a key to a function. Cases range from the trivial (replacing an + * existing binding) to the extremely complex (creating a new prefix in a + * map_element that already has one, so the map_element must be split, + * but the keymap doesn't have enough room for another map_element, so + * the keymap is reallocated). No attempt is made to reclaim space no + * longer used, if this is a problem flags must be added to indicate + * malloced versus static storage in both keymaps and map_elements. + * Structure assignments would come in real handy, but K&R based compilers + * don't have them. Care is taken so running out of memory will leave + * the keymap in a usable state. + * Parameters are: + * curmap: pointer to the map being changed + * c: character being changed + * funct: function being changed to + * pref_map: if funct==NULL, map to bind to or NULL for new + */ +static int +remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map) +{ + int i, n1, n2, nold; + KEYMAP *mp, *newmap; + PF *pfp; + struct map_element *mep; + + if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) { + if (ele > &curmap->map_element[0] && (funct != NULL || + (ele - 1)->k_prefmap == NULL)) + n1 = c - (ele - 1)->k_num; + else + n1 = HUGE; + if (ele < &curmap->map_element[curmap->map_num] && + (funct != NULL || ele->k_prefmap == NULL)) + n2 = ele->k_base - c; + else + n2 = HUGE; + if (n1 <= MAPELEDEF && n1 <= n2) { + ele--; + if ((pfp = calloc(c - ele->k_base + 1, + sizeof(PF))) == NULL) + return (dobeep_msg("Out of memory")); + + nold = ele->k_num - ele->k_base + 1; + for (i = 0; i < nold; i++) + pfp[i] = ele->k_funcp[i]; + while (--n1) + pfp[i++] = curmap->map_default; + pfp[i] = funct; + ele->k_num = c; + ele->k_funcp = pfp; + } else if (n2 <= MAPELEDEF) { + if ((pfp = calloc(ele->k_num - c + 1, + sizeof(PF))) == NULL) + return (dobeep_msg("Out of memory")); + + nold = ele->k_num - ele->k_base + 1; + for (i = 0; i < nold; i++) + pfp[i + n2] = ele->k_funcp[i]; + while (--n2) + pfp[n2] = curmap->map_default; + pfp[0] = funct; + ele->k_base = c; + ele->k_funcp = pfp; + } else { + if (curmap->map_num >= curmap->map_max) { + if ((newmap = reallocmap(curmap)) == NULL) + return (FALSE); + curmap = newmap; + } + if ((pfp = malloc(sizeof(PF))) == NULL) + return (dobeep_msg("Out of memory")); + + pfp[0] = funct; + for (mep = &curmap->map_element[curmap->map_num]; + mep > ele; mep--) { + mep->k_base = (mep - 1)->k_base; + mep->k_num = (mep - 1)->k_num; + mep->k_funcp = (mep - 1)->k_funcp; + mep->k_prefmap = (mep - 1)->k_prefmap; + } + ele->k_base = c; + ele->k_num = c; + ele->k_funcp = pfp; + ele->k_prefmap = NULL; + curmap->map_num++; + } + if (funct == NULL) { + if (pref_map != NULL) + ele->k_prefmap = pref_map; + else { + if ((mp = malloc(sizeof(KEYMAP) + + (MAPINIT - 1) * sizeof(struct map_element))) == NULL) { + (void)dobeep_msg("Out of memory"); + ele->k_funcp[c - ele->k_base] = + curmap->map_default; + return (FALSE); + } + mp->map_num = 0; + mp->map_max = MAPINIT; + mp->map_default = rescan; + ele->k_prefmap = mp; + } + } + } else { + n1 = c - ele->k_base; + if (ele->k_funcp[n1] == funct && (funct != NULL || + pref_map == NULL || pref_map == ele->k_prefmap)) + /* no change */ + return (TRUE); + if (funct != NULL || ele->k_prefmap == NULL) { + if (ele->k_funcp[n1] == NULL) + ele->k_prefmap = NULL; + /* easy case */ + ele->k_funcp[n1] = funct; + if (funct == NULL) { + if (pref_map != NULL) + ele->k_prefmap = pref_map; + else { + if ((mp = malloc(sizeof(KEYMAP) + + (MAPINIT - 1) * + sizeof(struct map_element))) == NULL) { + (void)dobeep_msg("Out of memory"); + ele->k_funcp[c - ele->k_base] = + curmap->map_default; + return (FALSE); + } + mp->map_num = 0; + mp->map_max = MAPINIT; + mp->map_default = rescan; + ele->k_prefmap = mp; + } + } + } else { + /* + * This case is the splits. + * Determine which side of the break c goes on + * 0 = after break; 1 = before break + */ + n2 = 1; + for (i = 0; n2 && i < n1; i++) + n2 &= ele->k_funcp[i] != NULL; + if (curmap->map_num >= curmap->map_max) { + if ((newmap = reallocmap(curmap)) == NULL) + return (FALSE); + curmap = newmap; + } + if ((pfp = calloc(ele->k_num - c + !n2, + sizeof(PF))) == NULL) + return (dobeep_msg("Out of memory")); + + ele->k_funcp[n1] = NULL; + for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++) + pfp[i - n1 - n2] = ele->k_funcp[i]; + for (mep = &curmap->map_element[curmap->map_num]; + mep > ele; mep--) { + mep->k_base = (mep - 1)->k_base; + mep->k_num = (mep - 1)->k_num; + mep->k_funcp = (mep - 1)->k_funcp; + mep->k_prefmap = (mep - 1)->k_prefmap; + } + ele->k_num = c - !n2; + (ele + 1)->k_base = c + n2; + (ele + 1)->k_funcp = pfp; + ele += !n2; + ele->k_prefmap = NULL; + curmap->map_num++; + if (pref_map == NULL) { + if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1) + * sizeof(struct map_element))) == NULL) { + (void)dobeep_msg("Out of memory"); + ele->k_funcp[c - ele->k_base] = + curmap->map_default; + return (FALSE); + } + mp->map_num = 0; + mp->map_max = MAPINIT; + mp->map_default = rescan; + ele->k_prefmap = mp; + } else + ele->k_prefmap = pref_map; + } + } + return (TRUE); +} + +/* + * Reallocate a keymap. Returns NULL (without trashing the current map) + * on failure. + */ +static KEYMAP * +reallocmap(KEYMAP *curmap) +{ + struct maps_s *mps; + KEYMAP *mp; + int i; + + if (curmap->map_max > SHRT_MAX - MAPGROW) { + (void)dobeep_msg("keymap too large"); + return (NULL); + } + if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) * + sizeof(struct map_element))) == NULL) { + (void)dobeep_msg("Out of memory"); + return (NULL); + } + mp->map_num = curmap->map_num; + mp->map_max = curmap->map_max + MAPGROW; + mp->map_default = curmap->map_default; + for (i = curmap->map_num; i--;) { + mp->map_element[i].k_base = curmap->map_element[i].k_base; + mp->map_element[i].k_num = curmap->map_element[i].k_num; + mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp; + mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap; + } + for (mps = maps; mps != NULL; mps = mps->p_next) { + if (mps->p_map == curmap) + mps->p_map = mp; + else + fixmap(curmap, mp, mps->p_map); + } + ele = &mp->map_element[ele - &curmap->map_element[0]]; + return (mp); +} + +/* + * Fix references to a reallocated keymap (recursive). + */ +static void +fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt) +{ + int i; + + for (i = mt->map_num; i--;) { + if (mt->map_element[i].k_prefmap != NULL) { + if (mt->map_element[i].k_prefmap == curmap) + mt->map_element[i].k_prefmap = mp; + else + fixmap(curmap, mp, mt->map_element[i].k_prefmap); + } + } +} + +/* + * Do the input for local-set-key, global-set-key and define-key + * then call remap to do the work. + */ +static int +dobind(KEYMAP *curmap, const char *p, int unbind) +{ + KEYMAP *pref_map = NULL; + PF funct; + char bprompt[80], *bufp, *pep; + int c, s, n; + + if (macrodef) { + /* + * Keystrokes aren't collected. Not hard, but pretty useless. + * Would not work for function keys in any case. + */ + return (dobeep_msg("Can't rebind key in macro")); + } + if (inmacro) { + for (s = 0; s < maclcur->l_used - 1; s++) { + if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap) + != NULL) { + if (remap(curmap, c, NULL, NULL) + != TRUE) + return (FALSE); + } + } + (void)doscan(curmap, c = maclcur->l_text[s], NULL); + maclcur = maclcur->l_fp; + } else { + n = strlcpy(bprompt, p, sizeof(bprompt)); + if (n >= sizeof(bprompt)) + n = sizeof(bprompt) - 1; + pep = bprompt + n; + for (;;) { + ewprintf("%s", bprompt); + pep[-1] = ' '; + pep = getkeyname(pep, sizeof(bprompt) - + (pep - bprompt), c = getkey(FALSE)); + if (doscan(curmap, c, &curmap) != NULL) + break; + *pep++ = '-'; + *pep = '\0'; + } + } + if (unbind) + funct = rescan; + else { + if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt), + EFFUNC | EFNEW, bprompt)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if (((funct = name_function(bprompt)) == NULL) ? + (pref_map = name_map(bprompt)) == NULL : funct == NULL) + return (dobeep_msg("[No match]")); + + } + return (remap(curmap, c, funct, pref_map)); +} + +/* + * bindkey: bind key sequence to a function in the specified map. Used by + * excline so it can bind function keys. To close to release to change + * calling sequence, should just pass KEYMAP *curmap rather than + * KEYMAP **mapp. + */ +static int +bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount) +{ + KEYMAP *curmap = *mapp; + KEYMAP *pref_map = NULL; + PF funct; + int c; + + if (fname == NULL) + funct = rescan; + else if (((funct = name_function(fname)) == NULL) ? + (pref_map = name_map(fname)) == NULL : funct == NULL) { + dobeep(); + ewprintf("[No match: %s]", fname); + return (FALSE); + } + while (--kcount) { + if (doscan(curmap, c = *keys++, &curmap) != NULL) { + if (remap(curmap, c, NULL, NULL) != TRUE) + return (FALSE); + /* + * XXX - Bizzarreness. remap creates an empty KEYMAP + * that the last key is supposed to point to. + */ + curmap = ele->k_prefmap; + } + } + (void)doscan(curmap, c = *keys, NULL); + return (remap(curmap, c, funct, pref_map)); +} + +/* + * Wrapper for bindkey() that converts escapes. + */ +int +dobindkey(KEYMAP *map, const char *func, const char *str) +{ + int i; + + for (i = 0; *str && i < MAXKEY; i++) { + /* XXX - convert numbers w/ strol()? */ + if (*str == '^' && *(str + 1) != '\0') { + key.k_chars[i] = CCHR(toupper((unsigned char)*++str)); + } else if (*str == '\\' && *(str + 1) != '\0') { + switch (*++str) { + case '^': + key.k_chars[i] = '^'; + break; + case 't': + case 'T': + key.k_chars[i] = '\t'; + break; + case 'n': + case 'N': + key.k_chars[i] = *curbp->b_nlchr; + break; + case 'r': + case 'R': + key.k_chars[i] = '\r'; + break; + case 'e': + case 'E': + key.k_chars[i] = CCHR('['); + break; + case '\\': + key.k_chars[i] = '\\'; + break; + } + } else + key.k_chars[i] = *str; + str++; + } + key.k_count = i; + return (bindkey(&map, func, key.k_chars, key.k_count)); +} + +/* + * This function modifies the fundamental keyboard map. + */ +/* ARGSUSED */ +int +bindtokey(int f, int n) +{ + return (dobind(fundamental_map, "Global set key: ", FALSE)); +} + +/* + * This function modifies the current mode's keyboard map. + */ +/* ARGSUSED */ +int +localbind(int f, int n) +{ + return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, + "Local set key: ", FALSE)); +} + +/* + * This function redefines a key in any keymap. + */ +/* ARGSUSED */ +int +redefine_key(int f, int n) +{ + static char buf[48]; + char tmp[32], *bufp; + KEYMAP *mp; + + (void)strlcpy(buf, "Define key map: ", sizeof(buf)); + if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + (void)strlcat(buf, tmp, sizeof(buf)); + if ((mp = name_map(tmp)) == NULL) + return (dobeep_msgs("Unknown map ", tmp)); + + if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf)) + return (FALSE); + + return (dobind(mp, buf, FALSE)); +} + +/* ARGSUSED */ +int +unbindtokey(int f, int n) +{ + return (dobind(fundamental_map, "Global unset key: ", TRUE)); +} + +/* ARGSUSED */ +int +localunbind(int f, int n) +{ + return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map, + "Local unset key: ", TRUE)); +} + +/* + * Extended command. Call the message line routine to read in the command + * name and apply autocompletion to it. When it comes back, look the name + * up in the symbol table and run the command if it is found. Print an + * error if there is anything wrong. + */ +int +extend(int f, int n) +{ + PF funct; + char xname[NXNAME], *bufp; + + if (!(f & FFARG)) + bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC); + else + bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if ((funct = name_function(bufp)) != NULL) { + if (macrodef) { + struct line *lp = maclcur; + macro[macrocount - 1].m_funct = funct; + maclcur = lp->l_bp; + maclcur->l_fp = lp->l_fp; + free(lp); + } + return ((*funct)(f, n)); + } + return (dobeep_msg("[No match]")); +} + +/* + * Define the commands needed to do startup-file processing. + * This code is mostly a kludge just so we can get startup-file processing. + * + * If you're serious about having this code, you should rewrite it. + * To wit: + * It has lots of funny things in it to make the startup-file look + * like a GNU startup file; mostly dealing with parens and semicolons. + * This should all vanish. + * + * We define eval-expression because it's easy. It can make + * *-set-key or define-key set an arbitrary key sequence, so it isn't + * useless. + */ + +/* + * evalexpr - get one line from the user, and run it. + * Use strlen for length of line, assume user is not typing in a '\0' in the + * modeline. llen only used for foundparen() so old-school will be ok. + */ +/* ARGSUSED */ +int +evalexpr(int f, int n) +{ + char exbuf[BUFSIZE], *bufp; + int llen; + + if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf), + EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + llen = strlen(bufp); + + return (excline(exbuf, llen, 1)); +} + +/* + * evalbuffer - evaluate the current buffer as line commands. Useful for + * testing startup files. + */ +/* ARGSUSED */ +int +evalbuffer(int f, int n) +{ + struct line *lp; + struct buffer *bp = curbp; + int s, llen, lnum = 0; + static char excbuf[BUFSIZE]; + + for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) { + lnum++; + llen = llength(lp); + if (llen >= BUFSIZE) + return (FALSE); + (void)strncpy(excbuf, ltext(lp), llen); + + /* make sure the line is terminated */ + excbuf[llen] = '\0'; + if ((s = excline(excbuf, llen, lnum)) != TRUE) { + cleanup(); + return (s); + } + } + cleanup(); + return (TRUE); +} + +/* + * evalfile - go get a file and evaluate it as line commands. You can + * go get your own startup file if need be. + */ +/* ARGSUSED */ +int +evalfile(int f, int n) +{ + char fname[NFILEN], *bufp; + + if ((bufp = eread("Load file: ", fname, NFILEN, + EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + return (load(fname)); +} + +/* + * load - go load the file name we got passed. + */ +int +load(const char *fname) +{ + int s = TRUE, line, ret; + int nbytes = 0; + char excbuf[BUFSIZE], fncpy[NFILEN]; + FILE *ffp; + + if ((fname = adjustname(fname, TRUE)) == NULL) + /* just to be careful */ + return (FALSE); + + ret = ffropen(&ffp, fname, NULL); + if (ret != FIOSUC) { + if (ret == FIODIR) + (void)ffclose(ffp, NULL); + return (FALSE); + } + + /* keep a note of fname incase of errors in loaded file. */ + (void)strlcpy(fncpy, fname, sizeof(fncpy)); + line = 0; + while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes)) + == FIOSUC) { + line++; + excbuf[nbytes] = '\0'; + if (excline(excbuf, nbytes, line) != TRUE) { + s = FIOERR; + dobeep(); + ewprintf("Error loading file %s at line %d", fncpy, line); + break; + } + } + (void)ffclose(ffp, NULL); + excbuf[nbytes] = '\0'; + if (s != FIOEOF || (nbytes && excline(excbuf, nbytes, ++line) != TRUE)) + return (FALSE); + return (TRUE); +} + +/* + * excline - run a line from a load file or eval-expression. + */ +int +excline(char *line, int llen, int lnum) +{ + PF fp; + struct line *lp, *np; + int status, c, f, n; + char *funcp, *tmp; + char *argp = NULL; + long nl; + int bind; + KEYMAP *curmap = NULL; +#define BINDARG 0 /* this arg is key to bind (local/global set key) */ +#define BINDNO 1 /* not binding or non-quoted BINDARG */ +#define BINDNEXT 2 /* next arg " (define-key) */ +#define BINDDO 3 /* already found key to bind */ +#define BINDEXT 1 /* space for trailing \0 */ + + lp = NULL; + + if (macrodef || inmacro) + return (dobeep_msg("Not now!")); + + f = 0; + n = 1; + funcp = skipwhite(line); + if (*funcp == '\0') + return (TRUE); /* No error on blank lines */ + if (*funcp == '(') + return (foundparen(funcp, llen, lnum)); + line = parsetoken(funcp); + if (*line != '\0') { + *line++ = '\0'; + line = skipwhite(line); + if (ISDIGIT(*line) || *line == '-') { + argp = line; + line = parsetoken(line); + } + } + if (argp != NULL) { + f = FFARG; + nl = strtol(argp, &tmp, 10); + if (*tmp != '\0') + return (FALSE); + if (nl >= INT_MAX || nl <= INT_MIN) + return (FALSE); + n = (int)nl; + } + if ((fp = name_function(funcp)) == NULL) + return (dobeep_msgs("Unknown function: ", funcp)); + + if (fp == bindtokey || fp == unbindtokey) { + bind = BINDARG; + curmap = fundamental_map; + } else if (fp == localbind || fp == localunbind) { + bind = BINDARG; + curmap = curbp->b_modes[curbp->b_nmodes]->p_map; + } else if (fp == redefine_key) + bind = BINDNEXT; + else + bind = BINDNO; + /* Pack away all the args now... */ + if ((np = lalloc(0)) == FALSE) + return (FALSE); + np->l_fp = np->l_bp = maclcur = np; + while (*line != '\0') { + argp = skipwhite(line); + if (*argp == '\0') + break; + line = parsetoken(argp); + if (*argp != '"') { + if (*argp == '\'') + ++argp; + if ((lp = lalloc((int) (line - argp) + BINDEXT)) == + NULL) { + status = FALSE; + goto cleanup; + } + bcopy(argp, ltext(lp), (int)(line - argp)); + /* don't count BINDEXT */ + lp->l_used--; + if (bind == BINDARG) + bind = BINDNO; + } else { + /* quoted strings are special */ + ++argp; + if (bind != BINDARG) { + lp = lalloc((int)(line - argp) + BINDEXT); + if (lp == NULL) { + status = FALSE; + goto cleanup; + } + lp->l_used = 0; + } else + key.k_count = 0; + while (*argp != '"' && *argp != '\0') { + if (*argp != '\\') + c = *argp++; + else { + switch (*++argp) { + case 't': + case 'T': + c = CCHR('I'); + break; + case 'n': + case 'N': + c = CCHR('J'); + break; + case 'r': + case 'R': + c = CCHR('M'); + break; + case 'e': + case 'E': + c = CCHR('['); + break; + case '^': + /* + * split into two statements + * due to bug in OSK cpp + */ + c = CHARMASK(*++argp); + c = ISLOWER(c) ? + CCHR(TOUPPER(c)) : CCHR(c); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + c = *argp - '0'; + if (argp[1] <= '7' && + argp[1] >= '0') { + c <<= 3; + c += *++argp - '0'; + if (argp[1] <= '7' && + argp[1] >= '0') { + c <<= 3; + c += *++argp + - '0'; + } + } + break; + case 'f': + case 'F': + c = *++argp - '0'; + if (ISDIGIT(argp[1])) { + c *= 10; + c += *++argp - '0'; + } + c += KFIRST; + break; + default: + c = CHARMASK(*argp); + break; + } + argp++; + } + if (bind == BINDARG) + key.k_chars[key.k_count++] = c; + else + lp->l_text[lp->l_used++] = c; + } + if (*line) + line++; + } + switch (bind) { + case BINDARG: + bind = BINDDO; + break; + case BINDNEXT: + lp->l_text[lp->l_used] = '\0'; + if ((curmap = name_map(lp->l_text)) == NULL) { + (void)dobeep_msgs("No such mode: ", lp->l_text); + status = FALSE; + free(lp); + goto cleanup; + } + free(lp); + bind = BINDARG; + break; + default: + lp->l_fp = np->l_fp; + lp->l_bp = np; + np->l_fp = lp; + np = lp; + } + } + switch (bind) { + default: + (void)dobeep_msg("Bad args to set key"); + status = FALSE; + break; + case BINDDO: + if (fp != unbindtokey && fp != localunbind) { + lp->l_text[lp->l_used] = '\0'; + status = bindkey(&curmap, lp->l_text, key.k_chars, + key.k_count); + } else + status = bindkey(&curmap, NULL, key.k_chars, + key.k_count); + break; + case BINDNO: + inmacro = TRUE; + maclcur = maclcur->l_fp; + status = (*fp)(f, n); + inmacro = FALSE; + } +cleanup: + lp = maclcur->l_fp; + while (lp != maclcur) { + np = lp->l_fp; + free(lp); + lp = np; + } + free(lp); + maclhead = NULL; + macrodef = FALSE; + return (status); +} + +/* + * a pair of utility functions for the above + */ +char * +skipwhite(char *s) +{ + while (*s == ' ' || *s == '\t') + s++; + if ((*s == ';') || (*s == '#')) + *s = '\0'; + return (s); +} + +static char * +parsetoken(char *s) +{ + if (*s != '"') { + while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(') + s++; + if (*s == ';') + *s = '\0'; + } else + do { + /* + * Strings get special treatment. + * Beware: You can \ out the end of the string! + */ + if (*s == '\\') + ++s; + } while (*++s != '"' && *s != '\0'); + return (s); +} Index: contrib/mg/extensions.c =================================================================== --- /dev/null +++ contrib/mg/extensions.c @@ -0,0 +1,33 @@ +/* + * This file is in the public domain. + * + * Authors: + * * Brian Callahan + * * Kyle Isom + */ + +#include + +#include +#include + +#include "def.h" + +/* + * Extensions unique to Mg portable. + */ + +int shownlprompt = TRUE; + +/* Check for a newline at the end of a file? */ +int +togglenewlineprompt(int f, int n) +{ + + if (shownlprompt == TRUE) + shownlprompt = FALSE; + else + shownlprompt = TRUE; + + return (TRUE); +} Index: contrib/mg/file.c =================================================================== --- /dev/null +++ contrib/mg/file.c @@ -0,0 +1,778 @@ +/* $OpenBSD: file.c,v 1.102 2019/06/22 15:03:43 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * File commands. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +size_t xdirname(char *, const char *, size_t); + +/* + * Insert a file into the current buffer. Real easy - just call the + * insertfile routine with the file name. + */ +/* ARGSUSED */ +int +fileinsert(int f, int n) +{ + char fname[NFILEN], *bufp, *adjf; + + if (getbufcwd(fname, sizeof(fname)) != TRUE) + fname[0] = '\0'; + bufp = eread("Insert file: ", fname, NFILEN, + EFNEW | EFCR | EFFILE | EFDEF); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + adjf = adjustname(bufp, TRUE); + if (adjf == NULL) + return (FALSE); + return (insertfile(adjf, NULL, FALSE)); +} + +/* + * Select a file for editing. If the file is a directory, invoke dired. + * Otherwise, look around to see if you can find the file in another buffer; + * if you can find it, just switch to the buffer. If you cannot find the + * file, create a new buffer, read in the text, and switch to the new buffer. + */ +/* ARGSUSED */ +int +filevisit(int f, int n) +{ + struct buffer *bp; + char fname[NFILEN], *bufp, *adjf; + int status; + + if (getbufcwd(fname, sizeof(fname)) != TRUE) + fname[0] = '\0'; + bufp = eread("Find file: ", fname, NFILEN, + EFNEW | EFCR | EFFILE | EFDEF); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + adjf = adjustname(fname, TRUE); + if (adjf == NULL) + return (FALSE); + if (fisdir(adjf) == TRUE) + return (do_dired(adjf)); + if ((bp = findbuffer(adjf)) == NULL) + return (FALSE); + curbp = bp; + if (showbuffer(bp, curwp, WFFULL) != TRUE) + return (FALSE); + if (bp->b_fname[0] == '\0') { + if ((status = readin(adjf)) != TRUE) + killbuffer(bp); + return (status); + } + return (TRUE); +} + +/* + * Replace the current file with an alternate one. Semantics for finding + * the replacement file are the same as 'filevisit', except the current + * buffer is killed before the switch. If the kill fails, or is aborted, + * revert to the original file. + */ +/* ARGSUSED */ +int +filevisitalt(int f, int n) +{ + char fname[NFILEN], *bufp; + + if (getbufcwd(fname, sizeof(fname)) != TRUE) + fname[0] = '\0'; + bufp = eread("Find alternate file: ", fname, NFILEN, + EFNEW | EFCR | EFFILE | EFDEF); + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + + return (do_filevisitalt(fname)); +} + +int +do_filevisitalt(char *fn) +{ + struct buffer *bp; + int status; + char *adjf; + + status = killbuffer(curbp); + if (status == ABORT || status == FALSE) + return (ABORT); + + adjf = adjustname(fn, TRUE); + if (adjf == NULL) + return (FALSE); + if (fisdir(adjf) == TRUE) + return (do_dired(adjf)); + if ((bp = findbuffer(adjf)) == NULL) + return (FALSE); + curbp = bp; + if (showbuffer(bp, curwp, WFFULL) != TRUE) + return (FALSE); + if (bp->b_fname[0] == '\0') { + if ((status = readin(adjf)) != TRUE) + killbuffer(bp); + return (status); + } + return (TRUE); +} + +int +filevisitro(int f, int n) +{ + int error; + + error = filevisit(f, n); + if (error != TRUE) + return (error); + curbp->b_flag |= BFREADONLY; + return (TRUE); +} + +/* + * Pop to a file in the other window. Same as the last function, but uses + * popbuf instead of showbuffer. + */ +/* ARGSUSED */ +int +poptofile(int f, int n) +{ + struct buffer *bp; + struct mgwin *wp; + char fname[NFILEN], *adjf, *bufp; + int status; + + if (getbufcwd(fname, sizeof(fname)) != TRUE) + fname[0] = '\0'; + if ((bufp = eread("Find file in other window: ", fname, NFILEN, + EFNEW | EFCR | EFFILE | EFDEF)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + adjf = adjustname(fname, TRUE); + if (adjf == NULL) + return (FALSE); + if (fisdir(adjf) == TRUE) + return (do_dired(adjf)); + if ((bp = findbuffer(adjf)) == NULL) + return (FALSE); + if (bp == curbp) + return (splitwind(f, n)); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + curwp = wp; + if (bp->b_fname[0] == '\0') { + if ((status = readin(adjf)) != TRUE) + killbuffer(bp); + return (status); + } + return (TRUE); +} + +/* + * Read the file "fname" into the current buffer. Make all of the text + * in the buffer go away, after checking for unsaved changes. This is + * called by the "read" command, the "visit" command, and the mainline + * (for "mg file"). + */ +int +readin(char *fname) +{ + struct mgwin *wp; + struct stat statbuf; + int status, i, ro = FALSE; + PF *ael; + char dp[NFILEN]; + + /* might be old */ + if (bclear(curbp) != TRUE) + return (TRUE); + /* Clear readonly. May be set by autoexec path */ + curbp->b_flag &= ~BFREADONLY; + if ((status = insertfile(fname, fname, TRUE)) != TRUE) { + dobeep(); + ewprintf("File is not readable: %s", fname); + return (FALSE); + } + + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == curbp) { + wp->w_dotp = wp->w_linep = bfirstlp(curbp); + wp->w_doto = 0; + wp->w_markp = NULL; + wp->w_marko = 0; + } + } + + /* + * Call auto-executing function if we need to. + */ + if ((ael = find_autoexec(fname)) != NULL) { + for (i = 0; ael[i] != NULL; i++) + (*ael[i])(0, 1); + free(ael); + } + + /* no change */ + curbp->b_flag &= ~BFCHG; + + /* + * Set the buffer READONLY flag if any of following are true: + * 1. file is a directory. + * 2. file is read-only. + * 3. file doesn't exist and directory is read-only. + */ + if (fisdir(fname) == TRUE) { + ro = TRUE; + } else if ((access(fname, W_OK) == -1)) { + if (errno != ENOENT) { + ro = TRUE; + } else if (errno == ENOENT) { + (void)xdirname(dp, fname, sizeof(dp)); + (void)strlcat(dp, "/", sizeof(dp)); + + /* Missing directory; keep buffer rw, like emacs */ + if (stat(dp, &statbuf) == -1 && errno == ENOENT) { + if (eyorn("Missing directory, create") == TRUE) + (void)do_makedir(dp); + } else if (access(dp, W_OK) == -1 && errno == EACCES) { + ewprintf("File not found and directory" + " write-protected"); + ro = TRUE; + } + } + } + if (ro == TRUE) + curbp->b_flag |= BFREADONLY; + + if (startrow) { + gotoline(FFARG, startrow); + startrow = 0; + } + + undo_add_modified(); + return (status); +} + +/* + * NB, getting file attributes is done here under control of a flag + * rather than in readin, which would be cleaner. I was concerned + * that some operating system might require the file to be open + * in order to get the information. Similarly for writing. + */ + +/* + * Insert a file in the current buffer, after dot. If file is a directory, + * and 'replacebuf' is TRUE, invoke dired mode, else die with an error. + * If file is a regular file, set mark at the end of the text inserted; + * point at the beginning. Return a standard status. Print a summary + * (lines read, error message) out as well. This routine also does the + * read end of backup processing. The BFBAK flag, if set in a buffer, + * says that a backup should be taken. It is set when a file is read in, + * but not on a new file. You don't need to make a backup copy of nothing. + */ + +static char *line = NULL; +static int linesize = 0; + +int +insertfile(char *fname, char *newname, int replacebuf) +{ + struct buffer *bp; + struct line *lp1, *lp2; + struct line *olp; /* line we started at */ + struct mgwin *wp; + int nbytes, s, nline = 0, siz, x, x2; + int opos; /* offset we started at */ + int oline; /* original line number */ + FILE *ffp; + + if (replacebuf == TRUE) + x = undo_enable(FFRAND, 0); + else + x = undo_enabled(); + + lp1 = NULL; + if (line == NULL) { + line = malloc(NLINE); + if (line == NULL) + panic("out of memory"); + linesize = NLINE; + } + + /* cheap */ + bp = curbp; + if (newname != NULL) { + (void)strlcpy(bp->b_fname, newname, sizeof(bp->b_fname)); + (void)xdirname(bp->b_cwd, newname, sizeof(bp->b_cwd)); + (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); + } + + /* hard file open */ + if ((s = ffropen(&ffp, fname, (replacebuf == TRUE) ? bp : NULL)) + == FIOERR) + goto out; + if (s == FIOFNF) { + /* file not found */ + if (newname != NULL) + ewprintf("(New file)"); + else + ewprintf("(File not found)"); + goto out; + } else if (s == FIODIR) { + /* file was a directory */ + if (replacebuf == FALSE) { + dobeep(); + ewprintf("Cannot insert: file is a directory, %s", + fname); + goto cleanup; + } + killbuffer(bp); + bp = dired_(fname); + undo_enable(FFRAND, x); + if (bp == NULL) + return (FALSE); + curbp = bp; + return (showbuffer(bp, curwp, WFFULL | WFMODE)); + } else { + (void)xdirname(bp->b_cwd, fname, sizeof(bp->b_cwd)); + (void)strlcat(bp->b_cwd, "/", sizeof(bp->b_cwd)); + } + opos = curwp->w_doto; + oline = curwp->w_dotline; + /* + * Open a new line at dot and start inserting after it. + * We will delete this newline after insertion. + * Disable undo, as we create the undo record manually. + */ + x2 = undo_enable(FFRAND, 0); + (void)lnewline(); + olp = lback(curwp->w_dotp); + undo_enable(FFRAND, x2); + + nline = 0; + siz = 0; + while ((s = ffgetline(ffp, line, linesize, &nbytes)) != FIOERR) { +retry: + siz += nbytes + 1; + switch (s) { + case FIOSUC: + /* FALLTHRU */ + case FIOEOF: + ++nline; + if ((lp1 = lalloc(nbytes)) == NULL) { + /* keep message on the display */ + s = FIOERR; + undo_add_insert(olp, opos, + siz - nbytes - 1 - 1); + goto endoffile; + } + bcopy(line, <ext(lp1)[0], nbytes); + lp2 = lback(curwp->w_dotp); + lp2->l_fp = lp1; + lp1->l_fp = curwp->w_dotp; + lp1->l_bp = lp2; + curwp->w_dotp->l_bp = lp1; + if (s == FIOEOF) { + undo_add_insert(olp, opos, siz - 1); + goto endoffile; + } + break; + case FIOLONG: { + /* a line too long to fit in our buffer */ + char *cp; + int newsize; + + newsize = linesize * 2; + if (newsize < 0 || + (cp = malloc(newsize)) == NULL) { + dobeep(); + ewprintf("Could not allocate %d bytes", + newsize); + s = FIOERR; + goto endoffile; + } + bcopy(line, cp, linesize); + free(line); + line = cp; + s = ffgetline(ffp, line + linesize, linesize, + &nbytes); + nbytes += linesize; + linesize = newsize; + if (s == FIOERR) + goto endoffile; + goto retry; + } + default: + dobeep(); + ewprintf("Unknown code %d reading file", s); + s = FIOERR; + break; + } + } +endoffile: + /* ignore errors */ + (void)ffclose(ffp, NULL); + /* don't zap an error */ + if (s == FIOEOF) { + if (nline == 1) + ewprintf("(Read 1 line)"); + else + ewprintf("(Read %d lines)", nline); + } + /* set mark at the end of the text */ + curwp->w_dotp = curwp->w_markp = lback(curwp->w_dotp); + curwp->w_marko = llength(curwp->w_markp); + curwp->w_markline = oline + nline + 1; + /* + * if we are at the end of the file, ldelnewline is a no-op, + * but we still need to decrement the line and markline counts + * as we've accounted for this fencepost in our arithmetic + */ + if (lforw(curwp->w_dotp) == curwp->w_bufp->b_headp) { + curwp->w_bufp->b_lines--; + curwp->w_markline--; + } else + (void)ldelnewline(); + curwp->w_dotp = olp; + curwp->w_doto = opos; + curwp->w_dotline = oline; + if (olp == curbp->b_headp) + curwp->w_dotp = lforw(olp); + if (newname != NULL) + bp->b_flag |= BFCHG | BFBAK; /* Need a backup. */ + else + bp->b_flag |= BFCHG; + /* + * If the insert was at the end of buffer, set lp1 to the end of + * buffer line, and lp2 to the beginning of the newly inserted text. + * (Otherwise lp2 is set to NULL.) This is used below to set + * pointers in other windows correctly if they are also at the end of + * buffer. + */ + lp1 = bp->b_headp; + if (curwp->w_markp == lp1) { + lp2 = curwp->w_dotp; + } else { + /* delete extraneous newline */ + (void)ldelnewline(); +out: lp2 = NULL; + } + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == curbp) { + wp->w_rflag |= WFMODE | WFEDIT; + if (wp != curwp && lp2 != NULL) { + if (wp->w_dotp == lp1) + wp->w_dotp = lp2; + if (wp->w_markp == lp1) + wp->w_markp = lp2; + if (wp->w_linep == lp1) + wp->w_linep = lp2; + } + } + } + bp->b_lines += nline; +cleanup: + undo_enable(FFRAND, x); + + /* return FALSE if error */ + return (s != FIOERR); +} + +/* + * Ask for a file name and write the contents of the current buffer to that + * file. Update the remembered file name and clear the buffer changed flag. + * This handling of file names is different from the earlier versions and + * is more compatible with Gosling EMACS than with ITS EMACS. + */ +/* ARGSUSED */ +int +filewrite(int f, int n) +{ + struct stat statbuf; + int s; + char fname[NFILEN], bn[NBUFN], tmp[NFILEN + 25]; + char *adjfname, *bufp; + FILE *ffp; + + if (getbufcwd(fname, sizeof(fname)) != TRUE) + fname[0] = '\0'; + if ((bufp = eread("Write file: ", fname, NFILEN, + EFDEF | EFNEW | EFCR | EFFILE)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + + adjfname = adjustname(fname, TRUE); + if (adjfname == NULL) + return (FALSE); + + /* Check if file exists; write checks done later */ + if (stat(adjfname, &statbuf) == 0) { + if (S_ISDIR(statbuf.st_mode)) { + dobeep(); + ewprintf("%s is a directory", adjfname); + return (FALSE); + } + snprintf(tmp, sizeof(tmp), "File `%s' exists; overwrite", + adjfname); + if ((s = eyorn(tmp)) != TRUE) + return (s); + } + + /* old attributes are no longer current */ + bzero(&curbp->b_fi, sizeof(curbp->b_fi)); + if ((s = writeout(&ffp, curbp, adjfname)) == TRUE) { + (void)strlcpy(curbp->b_fname, adjfname, sizeof(curbp->b_fname)); + if (getbufcwd(curbp->b_cwd, sizeof(curbp->b_cwd)) != TRUE) + (void)strlcpy(curbp->b_cwd, "/", sizeof(curbp->b_cwd)); + if (augbname(bn, curbp->b_fname, sizeof(bn)) + == FALSE) + return (FALSE); + free(curbp->b_bname); + if ((curbp->b_bname = strdup(bn)) == NULL) + return (FALSE); + (void)fupdstat(curbp); + curbp->b_flag &= ~(BFBAK | BFCHG); + upmodes(curbp); + undo_add_boundary(FFRAND, 1); + undo_add_modified(); + } + return (s); +} + +/* + * Save the contents of the current buffer back into its associated file. + */ +static int makebackup = TRUE; + +/* ARGSUSED */ +int +filesave(int f, int n) +{ + if (curbp->b_fname[0] == '\0') + return (filewrite(f, n)); + else + return (buffsave(curbp)); +} + +/* + * Save the contents of the buffer argument into its associated file. Do + * nothing if there have been no changes (is this a bug, or a feature?). + * Error if there is no remembered file name. If this is the first write + * since the read or visit, then a backup copy of the file is made. + * Allow user to select whether or not to make backup files by looking at + * the value of makebackup. + */ +int +buffsave(struct buffer *bp) +{ + int s; + FILE *ffp; + + /* return, no changes */ + if ((bp->b_flag & BFCHG) == 0) { + ewprintf("(No changes need to be saved)"); + return (TRUE); + } + + /* must have a name */ + if (bp->b_fname[0] == '\0') { + dobeep(); + ewprintf("No file name"); + return (FALSE); + } + + /* Ensure file has not been modified elsewhere */ + /* We don't use the ignore flag here */ + if (fchecktime(bp) != TRUE) { + if ((s = eyesno("File has changed on disk since last save. " + "Save anyway")) != TRUE) + return (s); + } + + if (makebackup && (bp->b_flag & BFBAK)) { + s = fbackupfile(bp->b_fname); + /* hard error */ + if (s == ABORT) + return (FALSE); + /* softer error */ + if (s == FALSE && + (s = eyesno("Backup error, save anyway")) != TRUE) + return (s); + } + if ((s = writeout(&ffp, bp, bp->b_fname)) == TRUE) { + (void)fupdstat(bp); + bp->b_flag &= ~(BFCHG | BFBAK); + upmodes(bp); + undo_add_boundary(FFRAND, 1); + undo_add_modified(); + } + return (s); +} + +/* + * Since we don't have variables (we probably should) this is a command + * processor for changing the value of the make backup flag. If no argument + * is given, sets makebackup to true, so backups are made. If an argument is + * given, no backup files are made when saving a new version of a file. + */ +/* ARGSUSED */ +int +makebkfile(int f, int n) +{ + if (f & FFARG) + makebackup = n > 0; + else + makebackup = !makebackup; + ewprintf("Backup files %sabled", makebackup ? "en" : "dis"); + return (TRUE); +} + +/* + * NB: bp is passed to both ffwopen and ffclose because some + * attribute information may need to be updated at open time + * and others after the close. This is OS-dependent. Note + * that the ff routines are assumed to be able to tell whether + * the attribute information has been set up in this buffer + * or not. + */ + +/* + * This function performs the details of file writing; writing the file + * in buffer bp to file fn. Uses the file management routines in the + * "fileio.c" package. Most of the grief is checking of some sort. + * You may want to call fupdstat() after using this function. + */ +int +writeout(FILE ** ffp, struct buffer *bp, char *fn) +{ + struct stat statbuf; + struct line *lpend; + int s, eobnl; + char dp[NFILEN]; + + if (stat(fn, &statbuf) == -1 && errno == ENOENT) { + errno = 0; + (void)xdirname(dp, fn, sizeof(dp)); + (void)strlcat(dp, "/", sizeof(dp)); + if (access(dp, W_OK) && errno == EACCES) { + dobeep(); + ewprintf("Directory %s write-protected", dp); + return (FIOERR); + } else if (errno == ENOENT) { + dobeep(); + ewprintf("%s: no such directory", dp); + return (FIOERR); + } + } + lpend = bp->b_headp; + eobnl = 0; + if (llength(lback(lpend)) != 0) { + eobnl = eyorn("No newline at end of file, add one"); + if (eobnl != TRUE && eobnl != FALSE) + return (eobnl); /* abort */ + } + /* open writes message */ + if ((s = ffwopen(ffp, fn, bp)) != FIOSUC) + return (FALSE); + s = ffputbuf(*ffp, bp, eobnl); + if (s == FIOSUC) { + /* no write error */ + s = ffclose(*ffp, bp); + if (s == FIOSUC) + ewprintf("Wrote %s", fn); + } else { + /* print a message indicating write error */ + (void)ffclose(*ffp, bp); + dobeep(); + ewprintf("Unable to write %s", fn); + } + return (s == FIOSUC); +} + +/* + * Tag all windows for bp (all windows if bp == NULL) as needing their + * mode line updated. + */ +void +upmodes(struct buffer *bp) +{ + struct mgwin *wp; + + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) + if (bp == NULL || curwp->w_bufp == bp) + wp->w_rflag |= WFMODE; +} + +/* + * dirname using strlcpy semantic. + * Like dirname() except an empty string is returned in + * place of "/". This means we can always add a trailing + * slash and be correct. + * Address portability issues by copying argument + * before using. Some implementations modify the input string. + */ +size_t +xdirname(char *dp, const char *path, size_t dplen) +{ + char ts[NFILEN]; + size_t len; + + (void)strlcpy(ts, path, NFILEN); + len = strlcpy(dp, dirname(ts), dplen); + if (dplen > 0 && dp[0] == '/' && dp[1] == '\0') { + dp[0] = '\0'; + len = 0; + } + return (len); +} + +/* + * basename using strlcpy/strlcat semantic. + * Address portability issue by copying argument + * before using: some implementations modify the input string. + */ +size_t +xbasename(char *bp, const char *path, size_t bplen) +{ + char ts[NFILEN]; + + (void)strlcpy(ts, path, NFILEN); + return (strlcpy(bp, basename(ts), bplen)); +} + +/* + * The adjusted file name refers to a directory, so open dired mode. + */ +int +do_dired(char *adjf) +{ + struct buffer *bp; + + if ((bp = dired_(adjf)) == FALSE) + return (FALSE); + curbp = bp; + return (showbuffer(bp, curwp, WFFULL | WFMODE)); +} Index: contrib/mg/fileio.c =================================================================== --- /dev/null +++ contrib/mg/fileio.c @@ -0,0 +1,752 @@ +/* $OpenBSD: fileio.c,v 1.108 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * POSIX fileio.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" +#include "pathnames.h" + +static char *bkuplocation(const char *); +static int bkupleavetmp(const char *); + +static char *bkupdir; +static int leavetmp = 0; /* 1 = leave any '~' files in tmp dir */ + +/* + * Open a file for reading. + */ +int +ffropen(FILE ** ffp, const char *fn, struct buffer *bp) +{ + if ((*ffp = fopen(fn, "r")) == NULL) { + if (errno == ENOENT) + return (FIOFNF); + return (FIOERR); + } + + /* If 'fn' is a directory open it with dired. */ + if (fisdir(fn) == TRUE) + return (FIODIR); + + ffstat(*ffp, bp); + + return (FIOSUC); +} + +/* + * Update stat/dirty info + */ +void +ffstat(FILE *ffp, struct buffer *bp) +{ + struct stat sb; + + if (bp && fstat(fileno(ffp), &sb) == 0) { + /* set highorder bit to make sure this isn't all zero */ + bp->b_fi.fi_mode = sb.st_mode | 0x8000; + bp->b_fi.fi_uid = sb.st_uid; + bp->b_fi.fi_gid = sb.st_gid; + bp->b_fi.fi_mtime = sb.st_mtimespec; + /* Clear the ignore flag */ + bp->b_flag &= ~(BFIGNDIRTY | BFDIRTY); + } +} + +/* + * Update the status/dirty info. If there is an error, + * there's not a lot we can do. + */ +int +fupdstat(struct buffer *bp) +{ + FILE *ffp; + + if ((ffp = fopen(bp->b_fname, "r")) == NULL) { + if (errno == ENOENT) + return (FIOFNF); + return (FIOERR); + } + ffstat(ffp, bp); + (void)ffclose(ffp, bp); + return (FIOSUC); +} + +/* + * Open a file for writing. + */ +int +ffwopen(FILE ** ffp, const char *fn, struct buffer *bp) +{ + int fd; + mode_t fmode = DEFFILEMODE; + + if (bp && bp->b_fi.fi_mode) + fmode = bp->b_fi.fi_mode & 07777; + + fd = open(fn, O_RDWR | O_CREAT | O_TRUNC, fmode); + if (fd == -1) { + ffp = NULL; + dobeep(); + ewprintf("Cannot open file for writing : %s", strerror(errno)); + return (FIOERR); + } + + if ((*ffp = fdopen(fd, "w")) == NULL) { + dobeep(); + ewprintf("Cannot open file for writing : %s", strerror(errno)); + close(fd); + return (FIOERR); + } + + /* + * If we have file information, use it. We don't bother to check for + * errors, because there's no a lot we can do about it. Certainly + * trying to change ownership will fail if we aren't root. That's + * probably OK. If we don't have info, no need to get it, since any + * future writes will do the same thing. + */ + if (bp && bp->b_fi.fi_mode) { + fchmod(fd, bp->b_fi.fi_mode & 07777); + fchown(fd, bp->b_fi.fi_uid, bp->b_fi.fi_gid); + } + return (FIOSUC); +} + +/* + * Close a file. + */ +/* ARGSUSED */ +int +ffclose(FILE *ffp, struct buffer *bp) +{ + if (fclose(ffp) == 0) + return (FIOSUC); + return (FIOERR); +} + +/* + * Write a buffer to the already opened file. bp points to the + * buffer. Return the status. + */ +int +ffputbuf(FILE *ffp, struct buffer *bp, int eobnl) +{ + struct line *lp, *lpend; + + lpend = bp->b_headp; + + for (lp = lforw(lpend); lp != lpend; lp = lforw(lp)) { + if (fwrite(ltext(lp), 1, llength(lp), ffp) != llength(lp)) { + dobeep(); + ewprintf("Write I/O error"); + return (FIOERR); + } + if (lforw(lp) != lpend) /* no implied \n on last line */ + putc(*bp->b_nlchr, ffp); + } + if (eobnl) { + lnewline_at(lback(lpend), llength(lback(lpend))); + putc(*bp->b_nlchr, ffp); + } + return (FIOSUC); +} + +/* + * Read a line from a file, and store the bytes + * in the supplied buffer. Stop on end of file or end of + * line. When FIOEOF is returned, there is a valid line + * of data without the normally implied \n. + * If the line length exceeds nbuf, FIOLONG is returned. + */ +int +ffgetline(FILE *ffp, char *buf, int nbuf, int *nbytes) +{ + int c, i; + + i = 0; + while ((c = getc(ffp)) != EOF && c != *curbp->b_nlchr) { + buf[i++] = c; + if (i >= nbuf) + return (FIOLONG); + } + if (c == EOF && ferror(ffp) != FALSE) { + dobeep(); + ewprintf("File read error"); + return (FIOERR); + } + *nbytes = i; + return (c == EOF ? FIOEOF : FIOSUC); +} + +/* + * Make a backup copy of "fname". On Unix the backup has the same + * name as the original file, with a "~" on the end; this seems to + * be newest of the new-speak. The error handling is all in "file.c". + * We do a copy instead of a rename since otherwise another process + * with an open fd will get the backup, not the new file. This is + * a problem when using mg with things like crontab and vipw. + */ +int +fbackupfile(const char *fn) +{ + struct stat sb; + struct timespec new_times[2]; + int from, to, serrno; + ssize_t nread; + char buf[BUFSIZ]; + char *nname, *tname, *bkpth; + + if (stat(fn, &sb) == -1) { + dobeep(); + ewprintf("Can't stat %s : %s", fn, strerror(errno)); + return (FALSE); + } + + if ((bkpth = bkuplocation(fn)) == NULL) + return (FALSE); + + if (asprintf(&nname, "%s~", bkpth) == -1) { + dobeep(); + ewprintf("Can't allocate backup file name : %s", strerror(errno)); + free(bkpth); + return (ABORT); + } + if (asprintf(&tname, "%s.XXXXXXXXXX", bkpth) == -1) { + dobeep(); + ewprintf("Can't allocate temp file name : %s", strerror(errno)); + free(bkpth); + free(nname); + return (ABORT); + } + free(bkpth); + + if ((from = open(fn, O_RDONLY)) == -1) { + free(nname); + free(tname); + return (FALSE); + } + to = mkstemp(tname); + if (to == -1) { + serrno = errno; + close(from); + free(nname); + free(tname); + errno = serrno; + return (FALSE); + } + while ((nread = read(from, buf, sizeof(buf))) > 0) { + if (write(to, buf, (size_t)nread) != nread) { + nread = -1; + break; + } + } + serrno = errno; + (void) fchmod(to, (sb.st_mode & 0777)); + + /* copy the mtime to the backupfile */ + new_times[0] = sb.st_atim; + new_times[1] = sb.st_mtim; + futimens(to, new_times); + + close(from); + close(to); + if (nread == -1) { + if (unlink(tname) == -1) + ewprintf("Can't unlink temp : %s", strerror(errno)); + } else { + if (rename(tname, nname) == -1) { + ewprintf("Can't rename temp : %s", strerror(errno)); + (void) unlink(tname); + nread = -1; + } + } + free(nname); + free(tname); + errno = serrno; + + return (nread == -1 ? FALSE : TRUE); +} + +/* + * Convert "fn" to a canonicalized absolute filename, replacing + * a leading ~/ with the user's home dir, following symlinks, and + * remove all occurrences of /./ and /../ + */ +char * +adjustname(const char *fn, int slashslash) +{ + static char fnb[PATH_MAX]; + const char *cp, *ep = NULL; + char *path; + + if (slashslash == TRUE) { + cp = fn + strlen(fn) - 1; + for (; cp >= fn; cp--) { + if (ep && (*cp == '/')) { + fn = ep; + break; + } + if (*cp == '/' || *cp == '~') + ep = cp; + else + ep = NULL; + } + } + if ((path = expandtilde(fn)) == NULL) + return (NULL); + + if (realpath(path, fnb) == NULL) + (void)strlcpy(fnb, path, sizeof(fnb)); + + free(path); + return (fnb); +} + +/* + * Find a startup file for the user and return its name. As a service + * to other pieces of code that may want to find a startup file (like + * the terminal driver in particular), accepts a suffix to be appended + * to the startup file name. + */ +char * +startupfile(char *suffix, char *conffile) +{ + static char file[NFILEN]; + char *home; + int ret; + + if ((home = getenv("HOME")) == NULL || *home == '\0') + goto nohome; + + if (conffile != NULL) { + (void)strncpy(file, conffile, NFILEN); + } else if (suffix == NULL) { + ret = snprintf(file, sizeof(file), _PATH_MG_STARTUP, home); + if (ret < 0 || ret >= sizeof(file)) + return (NULL); + } else { + ret = snprintf(file, sizeof(file), _PATH_MG_TERM, home, suffix); + if (ret < 0 || ret >= sizeof(file)) + return (NULL); + } + + if (access(file, R_OK) == 0) + return (file); +nohome: +#ifdef STARTUPFILE + if (suffix == NULL) { + ret = snprintf(file, sizeof(file), "%s", STARTUPFILE); + if (ret < 0 || ret >= sizeof(file)) + return (NULL); + } else { + ret = snprintf(file, sizeof(file), "%s%s", STARTUPFILE, + suffix); + if (ret < 0 || ret >= sizeof(file)) + return (NULL); + } + + if (access(file, R_OK) == 0) + return (file); +#endif /* STARTUPFILE */ + return (NULL); +} + +int +copy(char *frname, char *toname) +{ + int ifd, ofd; + char buf[BUFSIZ]; + mode_t fmode = DEFFILEMODE; /* XXX?? */ + struct stat orig; + ssize_t sr; + + if ((ifd = open(frname, O_RDONLY)) == -1) + return (FALSE); + if (fstat(ifd, &orig) == -1) { + dobeep(); + ewprintf("fstat: %s", strerror(errno)); + close(ifd); + return (FALSE); + } + + if ((ofd = open(toname, O_WRONLY|O_CREAT|O_TRUNC, fmode)) == -1) { + close(ifd); + return (FALSE); + } + while ((sr = read(ifd, buf, sizeof(buf))) > 0) { + if (write(ofd, buf, (size_t)sr) != sr) { + ewprintf("write error : %s", strerror(errno)); + break; + } + } + if (fchmod(ofd, orig.st_mode) == -1) + ewprintf("Cannot set original mode : %s", strerror(errno)); + + if (sr == -1) { + ewprintf("Read error : %s", strerror(errno)); + close(ifd); + close(ofd); + return (FALSE); + } + /* + * It is "normal" for this to fail since we can't guarantee that + * we will be running as root. + */ + if (fchown(ofd, orig.st_uid, orig.st_gid) && errno != EPERM) + ewprintf("Cannot set owner : %s", strerror(errno)); + + (void) close(ifd); + (void) close(ofd); + + return (TRUE); +} + +/* + * return list of file names that match the name in buf. + */ +struct list * +make_file_list(char *buf) +{ + char *dir, *file, *cp; + size_t len, preflen; + int ret; + DIR *dirp; + struct dirent *dent; + struct list *last, *current; + char fl_name[NFILEN + 2]; + char prefixx[NFILEN + 1]; + + /* + * We need three different strings: + + * dir - the name of the directory containing what the user typed. + * Must be a real unix file name, e.g. no ~user, etc.. + * Must not end in /. + * prefix - the portion of what the user typed that is before the + * names we are going to find in the directory. Must have a + * trailing / if the user typed it. + * names from the directory - We open dir, and return prefix + * concatenated with names. + */ + + /* first we get a directory name we can look up */ + /* + * Names ending in . are potentially odd, because adjustname will + * treat foo/bar/.. as a foo/, whereas we are + * interested in names starting with .. + */ + len = strlen(buf); + if (len && buf[len - 1] == '.') { + buf[len - 1] = 'x'; + dir = adjustname(buf, TRUE); + buf[len - 1] = '.'; + } else + dir = adjustname(buf, TRUE); + if (dir == NULL) + return (NULL); + /* + * If the user typed a trailing / or the empty string + * he wants us to use his file spec as a directory name. + */ + if (len && buf[len - 1] != '/') { + file = strrchr(dir, '/'); + if (file) { + *file = '\0'; + if (*dir == '\0') + dir = "/"; + } else + return (NULL); + } + /* Now we get the prefix of the name the user typed. */ + if (strlcpy(prefixx, buf, sizeof(prefixx)) >= sizeof(prefixx)) + return (NULL); + cp = strrchr(prefixx, '/'); + if (cp == NULL) + prefixx[0] = '\0'; + else + cp[1] = '\0'; + + preflen = strlen(prefixx); + /* cp is the tail of buf that really needs to be compared. */ + cp = buf + preflen; + len = strlen(cp); + + /* + * Now make sure that file names will fit in the buffers allocated. + * SV files are fairly short. For BSD, something more general would + * be required. + */ + if (preflen > NFILEN - NAME_MAX) + return (NULL); + + /* loop over the specified directory, making up the list of files */ + + /* + * Note that it is worth our time to filter out names that don't + * match, even though our caller is going to do so again, and to + * avoid doing the stat if completion is being done, because stat'ing + * every file in the directory is relatively expensive. + */ + + dirp = opendir(dir); + if (dirp == NULL) + return (NULL); + last = NULL; + + while ((dent = readdir(dirp)) != NULL) { + int isdir; + if (strncmp(cp, dent->d_name, len) != 0) + continue; + isdir = 0; + if (dent->d_type == DT_DIR) { + isdir = 1; + } else if (dent->d_type == DT_LNK || + dent->d_type == DT_UNKNOWN) { + struct stat statbuf; + + if (fstatat(dirfd(dirp), dent->d_name, &statbuf, 0) < 0) + continue; + if (S_ISDIR(statbuf.st_mode)) + isdir = 1; + } + + if ((current = malloc(sizeof(struct list))) == NULL) { + free_file_list(last); + closedir(dirp); + return (NULL); + } + ret = snprintf(fl_name, sizeof(fl_name), + "%s%s%s", prefixx, dent->d_name, isdir ? "/" : ""); + if (ret < 0 || ret >= sizeof(fl_name)) { + free(current); + continue; + } + current->l_next = last; + current->l_name = strdup(fl_name); + last = current; + } + closedir(dirp); + + return (last); +} + +/* + * Test if a supplied filename refers to a directory + * Returns ABORT on error, TRUE if directory. FALSE otherwise + */ +int +fisdir(const char *fname) +{ + struct stat statbuf; + + if (stat(fname, &statbuf) != 0) + return (ABORT); + + if (S_ISDIR(statbuf.st_mode)) + return (TRUE); + + return (FALSE); +} + +/* + * Check the mtime of the supplied filename. + * Return TRUE if last mtime matches, FALSE if not, + * If the stat fails, return TRUE and try the save anyway + */ +int +fchecktime(struct buffer *bp) +{ + struct stat sb; + + if (stat(bp->b_fname, &sb) == -1) + return (TRUE); + + if (bp->b_fi.fi_mtime.tv_sec != sb.st_mtimespec.tv_sec || + bp->b_fi.fi_mtime.tv_nsec != sb.st_mtimespec.tv_nsec) + return (FALSE); + + return (TRUE); + +} + +/* + * Location of backup file. This function creates the correct path. + */ +static char * +bkuplocation(const char *fn) +{ + struct stat sb; + char *ret; + + if (bkupdir != NULL && (stat(bkupdir, &sb) == 0) && + S_ISDIR(sb.st_mode) && !bkupleavetmp(fn)) { + char fname[NFILEN]; + const char *c; + int i = 0, len; + + c = fn; + len = strlen(bkupdir); + + while (*c != '\0') { + /* Make sure we don't go over combined: + * strlen(bkupdir + '/' + fname + '\0') + */ + if (i >= NFILEN - len - 1) + return (NULL); + if (*c == '/') { + fname[i] = '!'; + } else if (*c == '!') { + if (i >= NFILEN - len - 2) + return (NULL); + fname[i++] = '!'; + fname[i] = '!'; + } else + fname[i] = *c; + i++; + c++; + } + fname[i] = '\0'; + if (asprintf(&ret, "%s/%s", bkupdir, fname) == -1) + return (NULL); + + } else if ((ret = strndup(fn, NFILEN)) == NULL) + return (NULL); + + return (ret); +} + +int +backuptohomedir(int f, int n) +{ + const char *c = _PATH_MG_DIR; + char *p; + + if (bkupdir == NULL) { + p = adjustname(c, TRUE); + bkupdir = strndup(p, NFILEN); + if (bkupdir == NULL) + return(FALSE); + + if (mkdir(bkupdir, 0700) == -1 && errno != EEXIST) { + free(bkupdir); + bkupdir = NULL; + } + } else { + free(bkupdir); + bkupdir = NULL; + } + + return (TRUE); +} + +/* + * For applications that use mg as the editor and have a desire to keep + * '~' files in /tmp, toggle the location: /tmp | ~/.mg.d + */ +int +toggleleavetmp(int f, int n) +{ + leavetmp = !leavetmp; + + return (TRUE); +} + +/* + * Returns TRUE if fn is located in the temp directory and we want to save + * those backups there. + */ +int +bkupleavetmp(const char *fn) +{ + if (!leavetmp) + return(FALSE); + + if (strncmp(fn, "/tmp", 4) == 0) + return (TRUE); + + return (FALSE); +} + +/* + * Expand file names beginning with '~' if appropriate: + * 1, if ./~fn exists, continue without expanding tilde. + * 2, else, if username 'fn' exists, expand tilde with home directory path. + * 3, otherwise, continue and create new buffer called ~fn. + */ +char * +expandtilde(const char *fn) +{ + struct passwd *pw; + struct stat statbuf; + const char *cp; + char user[LOGIN_NAME_MAX], path[NFILEN]; + char *ret; + size_t ulen, plen; + + path[0] = '\0'; + + if (fn[0] != '~' || stat(fn, &statbuf) == 0) { + if ((ret = strndup(fn, NFILEN)) == NULL) + return (NULL); + return(ret); + } + cp = strchr(fn, '/'); + if (cp == NULL) + cp = fn + strlen(fn); /* point to the NUL byte */ + ulen = cp - &fn[1]; + if (ulen >= sizeof(user)) { + if ((ret = strndup(fn, NFILEN)) == NULL) + return (NULL); + return(ret); + } + if (ulen == 0) /* ~/ or ~ */ + pw = getpwuid(geteuid()); + else { /* ~user/ or ~user */ + memcpy(user, &fn[1], ulen); + user[ulen] = '\0'; + pw = getpwnam(user); + } + if (pw != NULL) { + plen = strlcpy(path, pw->pw_dir, sizeof(path)); + if (plen == 0 || path[plen - 1] != '/') { + if (strlcat(path, "/", sizeof(path)) >= sizeof(path)) { + dobeep(); + ewprintf("Path too long"); + return (NULL); + } + } + fn = cp; + if (*fn == '/') + fn++; + } + if (strlcat(path, fn, sizeof(path)) >= sizeof(path)) { + dobeep(); + ewprintf("Path too long"); + return (NULL); + } + if ((ret = strndup(path, NFILEN)) == NULL) + return (NULL); + + return (ret); +} Index: contrib/mg/fparseln.c =================================================================== --- /dev/null +++ contrib/mg/fparseln.c @@ -0,0 +1,258 @@ +/* $NetBSD: fgetln.c,v 1.9 2008/04/29 06:53:03 martin Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* $OpenBSD: fparseln.c,v 1.7 2012/12/05 23:20:06 deraadt Exp $ */ +/* $NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $ */ + +/* + * Copyright (c) 1997 Christos Zoulas. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Christos Zoulas. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include "config.h" + +#ifndef HAVE_FPARSELN + +#ifndef __APPLE__ +#include "util.h" +#endif + +static char * +efgetln(FILE *fp, size_t *len) +{ + static char *buf = NULL; + static size_t bufsiz = 0; + char *ptr; + + + if (buf == NULL) { + bufsiz = BUFSIZ; + if ((buf = malloc(bufsiz)) == NULL) + return NULL; + } + + if (fgets(buf, bufsiz, fp) == NULL) + return NULL; + + *len = 0; + while ((ptr = strchr(&buf[*len], '\n')) == NULL) { + size_t nbufsiz = bufsiz + BUFSIZ; + char *nbuf = realloc(buf, nbufsiz); + + if (nbuf == NULL) { + int oerrno = errno; + free(buf); + errno = oerrno; + buf = NULL; + return NULL; + } else + buf = nbuf; + + if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) { + buf[bufsiz] = '\0'; + *len = strlen(buf); + return buf; + } + + *len = bufsiz; + bufsiz = nbufsiz; + } + + *len = (ptr - buf) + 1; + return buf; +} + +static int isescaped(const char *, const char *, int); + +/* isescaped(): + * Return true if the character in *p that belongs to a string + * that starts in *sp, is escaped by the escape character esc. + */ +static int +isescaped(const char *sp, const char *p, int esc) +{ + const char *cp; + size_t ne; + + /* No escape character */ + if (esc == '\0') + return 1; + + /* Count the number of escape characters that precede ours */ + for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++) + continue; + + /* Return true if odd number of escape characters */ + return (ne & 1) != 0; +} + + +/* fparseln(): + * Read a line from a file parsing continuations ending in \ + * and eliminating trailing newlines, or comments starting with + * the comment char. + */ +char * +fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3], + int flags) +{ + static const char dstr[3] = { '\\', '\\', '#' }; + char *buf = NULL, *ptr, *cp, esc, con, nl, com; + size_t s, len = 0; + int cnt = 1; + + if (str == NULL) + str = dstr; + + esc = str[0]; + con = str[1]; + com = str[2]; + + /* + * XXX: it would be cool to be able to specify the newline character, + * but unfortunately, fgetln does not let us + */ + nl = '\n'; + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + if ((ptr = efgetln(fp, &s)) == NULL) + break; + + if (s && com) { /* Check and eliminate comments */ + for (cp = ptr; cp < ptr + s; cp++) + if (*cp == com && !isescaped(ptr, cp, esc)) { + s = cp - ptr; + cnt = s == 0 && buf == NULL; + break; + } + } + + if (s && nl) { /* Check and eliminate newlines */ + cp = &ptr[s - 1]; + + if (*cp == nl) + s--; /* forget newline */ + } + + if (s && con) { /* Check and eliminate continuations */ + cp = &ptr[s - 1]; + + if (*cp == con && !isescaped(ptr, cp, esc)) { + s--; /* forget escape */ + cnt = 1; + } + } + + if (s == 0 && buf != NULL) + continue; + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + free(buf); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL && + strchr(buf, esc) != NULL) { + ptr = cp = buf; + while (cp[0] != '\0') { + int skipesc; + + while (cp[0] != '\0' && cp[0] != esc) + *ptr++ = *cp++; + if (cp[0] == '\0' || cp[1] == '\0') + break; + + skipesc = 0; + if (cp[1] == com) + skipesc += (flags & FPARSELN_UNESCCOMM); + if (cp[1] == con) + skipesc += (flags & FPARSELN_UNESCCONT); + if (cp[1] == esc) + skipesc += (flags & FPARSELN_UNESCESC); + if (cp[1] != com && cp[1] != con && cp[1] != esc) + skipesc = (flags & FPARSELN_UNESCREST); + + if (skipesc) + cp++; + else + *ptr++ = *cp++; + *ptr++ = *cp++; + } + *ptr = '\0'; + len = strlen(buf); + } + + if (size) + *size = len; + return buf; +} + +#endif /* !HAVE_FPARSELN */ Index: contrib/mg/freebsd.h =================================================================== --- /dev/null +++ contrib/mg/freebsd.h @@ -0,0 +1,6 @@ +/* + * FreeBSD-specific support. + */ + +#include +#include Index: contrib/mg/fstatat.c =================================================================== --- /dev/null +++ contrib/mg/fstatat.c @@ -0,0 +1,22 @@ +/* + * fstatat(2) wrapper for systems that do not support it. + * Written by Brian Callahan and released + * into the Public Domain. + */ + +#include + +#include + +#include "config.h" + +#ifndef HAVE_FSTATAT + +int +fstatat(int fd, const char *path, struct stat *buf, int flag) +{ + + return stat(path, buf); +} + +#endif /* !HAVE_FSTATAT */ Index: contrib/mg/funmap.h =================================================================== --- /dev/null +++ contrib/mg/funmap.h @@ -0,0 +1,10 @@ +/* $OpenBSD: funmap.h,v 1.8 2019/07/11 18:20:18 lum Exp $ */ + +/* This file is in the public domain */ + +void funmap_init(void); +PF name_function(const char *); +const char *function_name(PF); +struct list *complete_function_list(const char *); +int funmap_add(PF, const char *, int); +int numparams_function(PF); Index: contrib/mg/funmap.c =================================================================== --- /dev/null +++ contrib/mg/funmap.c @@ -0,0 +1,339 @@ +/* $OpenBSD: funmap.c,v 1.63 2021/04/22 19:50:55 lum Exp $ */ + +/* This file is in the public domain */ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" +#include "kbd.h" + +/* + * funmap structure: a list of functions and their command-names/#parameters. + * + * If the function is NULL, it must be listed with the same name in the + * map_table. + */ +struct funmap { + PF fn_funct; + const char *fn_name; + int fn_nparams; + struct funmap *fn_next; +}; +static struct funmap *funs; + +/* + * 3rd column in the functnames structure indicates how many parameters the + * function takes in 'normal' usage. This column is only used to identify + * function profiles when lines of a buffer are being evaluated via excline(). + * + * 0 = a toggle, non-modifiable insert/delete, region modifier, etc + * 1 = value can be string or number value (like: file/buf name, search string) + * 2 = multiple type value required, see auto-execute, or global-set-key, etc + * -1 = error: interactive commmand, unsuitable for interpreter + * + * Some functions when used interactively may ask for a 'y' or 'n' (or another + * character) to continue, in excline, a 'y' is assumed. Functions like this + * have '0' in the 3rd column below. + */ +static struct funmap functnames[] = { + {apropos_command, "apropos", 1}, + {toggleaudiblebell, "audible-bell", 0}, + {auto_execute, "auto-execute", 2}, + {fillmode, "auto-fill-mode", 0}, + {indentmode, "auto-indent-mode", 0}, + {backtoindent, "back-to-indentation", 0}, + {backuptohomedir, "backup-to-home-directory", 0}, + {backchar, "backward-char", 1}, + {delbword, "backward-kill-word", 1}, + {gotobop, "backward-paragraph", 1}, + {backword, "backward-word", 1}, + {gotobob, "beginning-of-buffer", 0}, + {gotobol, "beginning-of-line", 0}, + {showmatch, "blink-and-insert", 1}, /* startup only */ + {bsmap, "bsmap-mode", 0}, + {NULL, "c-x 4 prefix", 0}, /* internal */ + {NULL, "c-x prefix", 0}, /* internal */ + {executemacro, "call-last-kbd-macro", 0}, + {capword, "capitalize-word", 1}, + {changedir, "cd", 1}, + {clearmark, "clear-mark", 0}, + {colnotoggle, "column-number-mode", 0}, + {copyregion, "copy-region-as-kill", 0}, +#ifdef REGEX + {cntmatchlines, "count-matches", 1}, + {cntnonmatchlines, "count-non-matches", 1}, +#endif /* REGEX */ + {cscreatelist, "cscope-create-list-of-files-to-index", 1}, + {csfuncalled, "cscope-find-called-functions", 1}, + {csegrep, "cscope-find-egrep-pattern", 1}, + {csfindinc, "cscope-find-files-including-file", 1}, + {cscallerfuncs, "cscope-find-functions-calling-this-function", 1}, + {csdefinition, "cscope-find-global-definition", 1}, + {csfindfile, "cscope-find-this-file", 1}, + {cssymbol, "cscope-find-this-symbol", 1}, + {csfindtext, "cscope-find-this-text-string", 1}, + {csnextfile, "cscope-next-file", 0}, + {csnextmatch, "cscope-next-symbol", 0}, + {csprevfile, "cscope-prev-file", 0}, + {csprevmatch, "cscope-prev-symbol", 0}, + {redefine_key, "define-key", 3}, + {backdel, "delete-backward-char", 1}, + {deblank, "delete-blank-lines", 0}, + {forwdel, "delete-char", 1}, + {delwhite, "delete-horizontal-space", 0}, + {delleadwhite, "delete-leading-space", 0}, +#ifdef REGEX + {delmatchlines, "delete-matching-lines", 1}, + {delnonmatchlines, "delete-non-matching-lines", 1}, +#endif /* REGEX */ + {onlywind, "delete-other-windows", 0}, + {deltrailwhite, "delete-trailing-space", 0}, + {delwind, "delete-window", 0}, + {wallchart, "describe-bindings", 0}, + {desckey, "describe-key-briefly", 1}, + {diffbuffer, "diff-buffer-with-file", 0}, + {digit_argument, "digit-argument", 1}, + {dired_jump, "dired-jump", 1}, + {lowerregion, "downcase-region", 0}, + {lowerword, "downcase-word", 1}, + {showversion, "emacs-version", 0}, + {finishmacro, "end-kbd-macro", 0}, + {gotoeob, "end-of-buffer", 0}, + {gotoeol, "end-of-line", 0}, + {enlargewind, "enlarge-window", 0}, + {NULL, "esc prefix", 0}, /* internal */ + {evalbuffer, "eval-current-buffer", 0}, + {evalexpr, "eval-expression", 0}, + {swapmark, "exchange-point-and-mark", 0}, + {extend, "execute-extended-command", 1}, + {fillpara, "fill-paragraph", 0}, + {filevisitalt, "find-alternate-file", 1}, + {filevisit, "find-file", 1}, + {poptofile, "find-file-other-window", 1}, + {filevisitro, "find-file-read-only", 1}, + {findtag, "find-tag", 1}, + {forwchar, "forward-char", 1}, + {gotoeop, "forward-paragraph", 1}, + {forwword, "forward-word", 1}, + {bindtokey, "global-set-key", 2}, + {unbindtokey, "global-unset-key", 1}, + {globalwdtoggle, "global-wd-mode", 0}, + {gotoline, "goto-line", 1}, + {help_help, "help-help", 0}, + {indent, "indent-current-line", 0}, + {insert, "insert", 1}, + {bufferinsert, "insert-buffer", 1}, + {fileinsert, "insert-file", 1}, + {fillword, "insert-with-wrap", 1}, /* startup only */ + {backisearch, "isearch-backward", 1}, + {forwisearch, "isearch-forward", 1}, + {joinline, "join-line", 0}, + {justone, "just-one-space", 0}, + {ctrlg, "keyboard-quit", 0}, + {killbuffer_cmd, "kill-buffer", 1}, + {killline, "kill-line", 1}, + {killpara, "kill-paragraph", 1}, + {killregion, "kill-region", 0}, + {delfword, "kill-word", 1}, + {toggleleavetmp, "leave-tmpdir-backups", 0}, + {linenotoggle, "line-number-mode", 0}, + {listbuffers, "list-buffers", 0}, + {evalfile, "load", 1}, + {localbind, "local-set-key", 1}, + {localunbind, "local-unset-key", 1}, + {makebkfile, "make-backup-files", 0}, + {makedir, "make-directory", 1}, + {markpara, "mark-paragraph", 1}, + {markbuffer, "mark-whole-buffer", 0}, + {do_meta, "meta-key-mode", 0}, /* better name, anyone? */ + {negative_argument, "negative-argument", 1}, + {enewline, "newline", 1}, + {lfindent, "newline-and-indent", 1}, + {forwline, "next-line", 1}, +#ifdef NOTAB + {notabmode, "no-tab-mode", 0}, +#endif /* NOTAB */ + {notmodified, "not-modified", 0}, + {openline, "open-line", 1}, + {nextwind, "other-window", 0}, + {overwrite_mode, "overwrite-mode", 0}, + {poptag, "pop-tag-mark", 0}, + {prefixregion, "prefix-region", 0}, + {backline, "previous-line", 1}, + {prevwind, "previous-window", 0}, + {spawncli, "push-shell", 0}, + {showcwdir, "pwd", 0}, + {queryrepl, "query-replace", -1}, +#ifdef REGEX + {re_queryrepl, "query-replace-regexp", -1}, +#endif /* REGEX */ + {quote, "quoted-insert", 1}, +#ifdef REGEX + {re_searchagain, "re-search-again", 0}, + {re_backsearch, "re-search-backward", 0}, + {re_forwsearch, "re-search-forward", 0}, +#endif /* REGEX */ + {reposition, "recenter", 0}, + {redraw, "redraw-display", 0}, +#ifdef REGEX + {re_repl, "replace-regexp", 2}, + {replstr, "replace-string", 2}, +#endif /* REGEX */ + {revertbuffer, "revert-buffer", 0}, + {filesave, "save-buffer", 1}, + {quit, "save-buffers-kill-emacs", 0}, + {savebuffers, "save-some-buffers", 0}, + {backpage, "scroll-down", 1}, + {back1page, "scroll-one-line-down", 1}, + {forw1page, "scroll-one-line-up", 1}, + {pagenext, "scroll-other-window", 1}, + {forwpage, "scroll-up", 1}, + {searchagain, "search-again", 0}, + {backsearch, "search-backward", 0}, + {forwsearch, "search-forward", 0}, + {ask_selfinsert, "self-insert-char", 1}, + {selfinsert, "self-insert-command", 1}, /* startup only */ + {sentencespace, "sentence-end-double-space", 0}, +#ifdef REGEX + {setcasefold, "set-case-fold-search", 0}, +#endif /* REGEX */ + {setcasereplace, "set-case-replace", 0}, + {set_default_mode, "set-default-mode", 1}, + {setfillcol, "set-fill-column", 1}, + {setmark, "set-mark-command", 0}, + {setprefix, "set-prefix-string", 1}, + {shellcommand, "shell-command", 1}, + {piperegion, "shell-command-on-region", 1}, + {shrinkwind, "shrink-window", 1}, +#ifdef NOTAB + {space_to_tabstop, "space-to-tabstop", 0}, +#endif /* NOTAB */ + {splitwind, "split-window-vertically", 0}, + {definemacro, "start-kbd-macro", 0}, + {spawncli, "suspend-emacs", 0}, + {usebuffer, "switch-to-buffer", 1}, + {poptobuffer, "switch-to-buffer-other-window", 1}, +#ifndef NOEXTENSIONS + {togglenewlineprompt, "toggle-newline-prompt",}, +#endif /* NOEXTENSIONS */ + {togglereadonly, "toggle-read-only", 0}, + {togglereadonlyall, "toggle-read-only-all", 0}, + {twiddle, "transpose-chars", 0}, + {transposepara, "transpose-paragraphs", 0}, + {transposeword, "transpose-words", 0}, + {undo, "undo", 0}, + {undo_add_boundary, "undo-boundary", 0}, + {undo_boundary_enable, "undo-boundary-toggle", 0}, + {undo_enable, "undo-enable", 0}, + {undo_dump, "undo-list", 0}, + {universal_argument, "universal-argument", 1}, + {upperregion, "upcase-region", 0}, + {upperword, "upcase-word", 1}, + {togglevisiblebell, "visible-bell", 0}, + {tagsvisit, "visit-tags-table", 0}, + {showcpos, "what-cursor-position", 0}, + {filewrite, "write-file", 1}, + {yank, "yank", 1}, + {NULL, NULL, 0} +}; + +void +funmap_init(void) +{ + struct funmap *fn; + + for (fn = functnames; fn->fn_name != NULL; fn++) { + fn->fn_next = funs; + funs = fn; + } +} + +int +funmap_add(PF fun, const char *fname, int fparams) +{ + struct funmap *fn; + + if ((fn = malloc(sizeof(*fn))) == NULL) + return (FALSE); + + fn->fn_funct = fun; + fn->fn_name = fname; + fn->fn_nparams = fparams; + fn->fn_next = funs; + + funs = fn; + return (TRUE); +} + +/* + * Translate from function name to function pointer. + */ +PF +name_function(const char *fname) +{ + struct funmap *fn; + + for (fn = funs; fn != NULL; fn = fn->fn_next) { + if (strcmp(fn->fn_name, fname) == 0) + return (fn->fn_funct); + } + return (NULL); +} + +const char * +function_name(PF fun) +{ + struct funmap *fn; + + for (fn = funs; fn != NULL; fn = fn->fn_next) { + if (fn->fn_funct == fun) + return (fn->fn_name); + } + return (NULL); +} + +/* + * List possible function name completions. + */ +struct list * +complete_function_list(const char *fname) +{ + struct funmap *fn; + struct list *head, *el; + int len; + + len = strlen(fname); + head = NULL; + for (fn = funs; fn != NULL; fn = fn->fn_next) { + if (memcmp(fname, fn->fn_name, len) == 0) { + if ((el = malloc(sizeof(*el))) == NULL) { + free_file_list(head); + return (NULL); + } + el->l_name = strdup(fn->fn_name); + el->l_next = head; + head = el; + } + } + return (head); +} + +/* + * Find number of parameters for function name. + */ +int +numparams_function(PF fun) +{ + struct funmap *fn; + + for (fn = funs; fn != NULL; fn = fn->fn_next) { + if (fn->fn_funct == fun) + return (fn->fn_nparams); + } + return (FALSE); +} Index: contrib/mg/futimens.c =================================================================== --- /dev/null +++ contrib/mg/futimens.c @@ -0,0 +1,20 @@ +/* This file is in the public domain. */ + +#include + +#include + +#include "config.h" + +#ifndef HAVE_FUTIMENS + +int +futimens(int fildes, const struct timespec times[2]) +{ + struct timeval timevals[2]; + TIMESPEC_TO_TIMEVAL(&timevals[0], ×[0]); + TIMESPEC_TO_TIMEVAL(&timevals[1], ×[1]); + return futimes(fildes, timevals); +} + +#endif /* !HAVE_FUTIMENS */ Index: contrib/mg/getline.c =================================================================== --- /dev/null +++ contrib/mg/getline.c @@ -0,0 +1,89 @@ +/* $NetBSD: getdelim.c,v 1.2 2015/12/25 20:12:46 joerg Exp $ */ +/* NetBSD-src: getline.c,v 1.2 2014/09/16 17:23:50 christos Exp */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "config.h" + +#ifndef HAVE_GETLINE + +static ssize_t +getdelim(char **buf, size_t *bufsiz, int delimiter, FILE *fp) +{ + char *ptr, *eptr; + + + if (*buf == NULL || *bufsiz == 0) { + *bufsiz = BUFSIZ; + if ((*buf = malloc(*bufsiz)) == NULL) + return -1; + } + + for (ptr = *buf, eptr = *buf + *bufsiz;;) { + int c = fgetc(fp); + if (c == -1) { + if (feof(fp)) { + ssize_t diff = (ssize_t)(ptr - *buf); + if (diff != 0) { + *ptr = '\0'; + return diff; + } + } + return -1; + } + *ptr++ = c; + if (c == delimiter) { + *ptr = '\0'; + return ptr - *buf; + } + if (ptr + 2 >= eptr) { + char *nbuf; + size_t nbufsiz = *bufsiz * 2; + ssize_t d = ptr - *buf; + if ((nbuf = realloc(*buf, nbufsiz)) == NULL) + return -1; + *buf = nbuf; + *bufsiz = nbufsiz; + eptr = nbuf + nbufsiz; + ptr = nbuf + d; + } + } +} + +ssize_t +getline(char **buf, size_t *bufsiz, FILE *fp) +{ + return getdelim(buf, bufsiz, '\n', fp); +} + +#endif Index: contrib/mg/graph2.h =================================================================== --- /dev/null +++ contrib/mg/graph2.h @@ -0,0 +1,63 @@ +/* $NetBSD: graph2.h,v 1.1 2009/08/15 16:21:05 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Implementation of common 2-graph routines: + * - build a 2-graph with hash-pairs as edges + * - check a 2-graph for acyclicness and compute an output order + */ + +struct vertex2 { + uint32_t l_edge, r_edge; +}; + +struct edge2 { + uint32_t left, right; + uint32_t l_prev, l_next; + uint32_t r_prev, r_next; +}; + +struct graph2 { + struct vertex2 *verts; + struct edge2 *edges; + uint32_t output_index; + uint32_t *output_order; + uint8_t *visited; + uint32_t e, v; +}; + +void graph2_setup(struct graph2 *, uint32_t, uint32_t); +void graph2_free(struct graph2 *); + +int graph2_hash(struct nbperf *, struct graph2 *); +int graph2_output_order(struct graph2 *graph); Index: contrib/mg/graph2.c =================================================================== --- /dev/null +++ contrib/mg/graph2.c @@ -0,0 +1,206 @@ +/* $NetBSD: graph2.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "nbperf.h" +#include "graph2.h" + +static const uint32_t unused = 0xffffffffU; + +void +graph2_setup(struct graph2 *graph, uint32_t v, uint32_t e) +{ + graph->v = v; + graph->e = e; + + graph->verts = calloc(sizeof(struct vertex2), v); + graph->edges = calloc(sizeof(struct edge2), e); + graph->output_order = calloc(sizeof(uint32_t), e); + + if (graph->verts == NULL || graph->edges == NULL || + graph->output_order == NULL) + err(1, "malloc failed"); +} + +void +graph2_free(struct graph2 *graph) +{ + free(graph->verts); + free(graph->edges); + free(graph->output_order); + + graph->verts = NULL; + graph->edges = NULL; + graph->output_order = NULL; +} + +static int +graph2_check_duplicates(struct nbperf *nbperf, struct graph2 *graph) +{ + struct vertex2 *v; + struct edge2 *e, *e2; + uint32_t i, j; + + for (i = 0; i < graph->e; ++i) { + e = &graph->edges[i]; + v = &graph->verts[e->left]; + j = v->l_edge; + e2 = &graph->edges[j]; + for (;;) { + if (i < j && e->right == e2->right && + nbperf->keylens[i] == nbperf->keylens[j] && + memcmp(nbperf->keys[i], nbperf->keys[j], + nbperf->keylens[i]) == 0) { + nbperf->has_duplicates = 1; + return -1; + } + if (e2->l_next == unused) + break; + j = e2->l_next; + e2 = &graph->edges[j]; + } + } + return 0; +} + +int +graph2_hash(struct nbperf *nbperf, struct graph2 *graph) +{ + struct vertex2 *v; + uint32_t hashes[NBPERF_MAX_HASH_SIZE]; + size_t i; + + for (i = 0; i < graph->e; ++i) { + (*nbperf->compute_hash)(nbperf, + nbperf->keys[i], nbperf->keylens[i], hashes); + graph->edges[i].left = hashes[0] % graph->v; + graph->edges[i].right = hashes[1] % graph->v; + if (graph->edges[i].left == graph->edges[i].right) + return -1; + } + + for (i = 0; i < graph->v; ++i) { + graph->verts[i].l_edge = unused; + graph->verts[i].r_edge = unused; + } + + for (i = 0; i < graph->e; ++i) { + v = &graph->verts[graph->edges[i].left]; + if (v->l_edge != unused) + graph->edges[v->l_edge].l_prev = i; + graph->edges[i].l_next = v->l_edge; + graph->edges[i].l_prev = unused; + v->l_edge = i; + + v = &graph->verts[graph->edges[i].right]; + if (v->r_edge != unused) + graph->edges[v->r_edge].r_prev = i; + graph->edges[i].r_next = v->r_edge; + graph->edges[i].r_prev = unused; + v->r_edge = i; + } + + if (nbperf->first_round) { + nbperf->first_round = 0; + return graph2_check_duplicates(nbperf, graph); + } + + return 0; +} + +static void +graph2_remove_vertex(struct graph2 *graph, struct vertex2 *v) +{ + struct edge2 *e; + struct vertex2 *v2; + + for (;;) { + if (v->l_edge != unused && v->r_edge != unused) + break; + if (v->l_edge == unused && v->r_edge == unused) + break; + + if (v->l_edge != unused) { + e = &graph->edges[v->l_edge]; + if (e->l_next != unused) + break; + v->l_edge = unused; /* No other elements possible! */ + v2 = &graph->verts[e->right]; + if (e->r_prev == unused) + v2->r_edge = e->r_next; + else + graph->edges[e->r_prev].r_next = e->r_next; + if (e->r_next != unused) + graph->edges[e->r_next].r_prev = e->r_prev; + v = v2; + } else { + e = &graph->edges[v->r_edge]; + if (e->r_next != unused) + break; + v->r_edge = unused; /* No other elements possible! */ + v2 = &graph->verts[e->left]; + if (e->l_prev == unused) + v2->l_edge = e->l_next; + else + graph->edges[e->l_prev].l_next = e->l_next; + if (e->l_next != unused) + graph->edges[e->l_next].l_prev = e->l_prev; + v = v2; + } + + graph->output_order[--graph->output_index] = e - graph->edges; + } +} + +int +graph2_output_order(struct graph2 *graph) +{ + size_t i; + + graph->output_index = graph->e; + + for (i = 0; i < graph->v; ++i) + graph2_remove_vertex(graph, &graph->verts[i]); + + if (graph->output_index != 0) + return -1; + + return 0; +} Index: contrib/mg/graph3.h =================================================================== --- /dev/null +++ contrib/mg/graph3.h @@ -0,0 +1,62 @@ +/* $NetBSD: graph3.h,v 1.1 2009/08/15 16:21:05 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Implementation of common 3-graph routines: + * - build a 3-graph with hash-triple as edges + * - check a 3-graph for acyclicness and compute an output order + */ + +struct vertex3 { + uint32_t l_edge, m_edge, r_edge; +}; + +struct edge3 { + uint32_t left, middle, right; + uint32_t l_prev, m_prev, l_next; + uint32_t r_prev, m_next, r_next; +}; + +struct graph3 { + struct vertex3 *verts; + struct edge3 *edges; + uint32_t output_index; + uint32_t *output_order; + uint32_t e, v; +}; + +void graph3_setup(struct graph3 *, uint32_t, uint32_t); +void graph3_free(struct graph3 *); + +int graph3_hash(struct nbperf *, struct graph3 *); +int graph3_output_order(struct graph3 *); Index: contrib/mg/graph3.c =================================================================== --- /dev/null +++ contrib/mg/graph3.c @@ -0,0 +1,245 @@ +/* $NetBSD: graph3.c,v 1.4 2011/10/21 23:47:11 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "nbperf.h" +#include "graph3.h" + +static const uint32_t unused = 0xffffffffU; + +void +graph3_setup(struct graph3 *graph, uint32_t v, uint32_t e) +{ + graph->v = v; + graph->e = e; + + graph->verts = calloc(sizeof(struct vertex3), v); + graph->edges = calloc(sizeof(struct edge3), e); + graph->output_order = calloc(sizeof(uint32_t), e); + + if (graph->verts == NULL || graph->edges == NULL || + graph->output_order == NULL) + err(1, "malloc failed"); +} + +void +graph3_free(struct graph3 *graph) +{ + free(graph->verts); + free(graph->edges); + free(graph->output_order); + + graph->verts = NULL; + graph->edges = NULL; + graph->output_order = NULL; +} + +static int +graph3_check_duplicates(struct nbperf *nbperf, struct graph3 *graph) +{ + struct vertex3 *v; + struct edge3 *e, *e2; + uint32_t i, j; + + for (i = 0; i < graph->e; ++i) { + e = &graph->edges[i]; + v = &graph->verts[e->left]; + j = v->l_edge; + e2 = &graph->edges[j]; + for (;;) { + if (i < j && e->middle == e2->middle && + e->right == e2->right && + nbperf->keylens[i] == nbperf->keylens[j] && + memcmp(nbperf->keys[i], nbperf->keys[j], + nbperf->keylens[i]) == 0) { + nbperf->has_duplicates = 1; + return -1; + } + if (e2->l_next == unused) + break; + j = e2->l_next; + e2 = &graph->edges[j]; + } + } + return 0; +} + +int +graph3_hash(struct nbperf *nbperf, struct graph3 *graph) +{ + struct vertex3 *v; + uint32_t hashes[NBPERF_MAX_HASH_SIZE]; + size_t i; + + for (i = 0; i < graph->e; ++i) { + (*nbperf->compute_hash)(nbperf, + nbperf->keys[i], nbperf->keylens[i], hashes); + graph->edges[i].left = hashes[0] % graph->v; + graph->edges[i].middle = hashes[1] % graph->v; + graph->edges[i].right = hashes[2] % graph->v; + if (graph->edges[i].left == graph->edges[i].middle) + return -1; + if (graph->edges[i].left == graph->edges[i].right) + return -1; + if (graph->edges[i].middle == graph->edges[i].right) + return -1; + } + + for (i = 0; i < graph->v; ++i) { + graph->verts[i].l_edge = unused; + graph->verts[i].m_edge = unused; + graph->verts[i].r_edge = unused; + } + + for (i = 0; i < graph->e; ++i) { + v = &graph->verts[graph->edges[i].left]; + if (v->l_edge != unused) + graph->edges[v->l_edge].l_prev = i; + graph->edges[i].l_next = v->l_edge; + graph->edges[i].l_prev = unused; + v->l_edge = i; + + v = &graph->verts[graph->edges[i].middle]; + if (v->m_edge != unused) + graph->edges[v->m_edge].m_prev = i; + graph->edges[i].m_next = v->m_edge; + graph->edges[i].m_prev = unused; + v->m_edge = i; + + v = &graph->verts[graph->edges[i].right]; + if (v->r_edge != unused) + graph->edges[v->r_edge].r_prev = i; + graph->edges[i].r_next = v->r_edge; + graph->edges[i].r_prev = unused; + v->r_edge = i; + } + + if (nbperf->first_round) { + nbperf->first_round = 0; + return graph3_check_duplicates(nbperf, graph); + } + + return 0; +} + +static void +graph3_remove_vertex(struct graph3 *graph, struct vertex3 *v) +{ + struct edge3 *e; + struct vertex3 *vl, *vm, *vr; + + if (v->l_edge != unused && v->m_edge != unused) + return; + if (v->l_edge != unused && v->r_edge != unused) + return; + if (v->m_edge != unused && v->r_edge != unused) + return; + if (v->l_edge == unused && v->m_edge == unused && v->r_edge == unused) + return; + + if (v->l_edge != unused) { + e = &graph->edges[v->l_edge]; + if (e->l_next != unused) + return; + } else if (v->m_edge != unused) { + e = &graph->edges[v->m_edge]; + if (e->m_next != unused) + return; + } else { + if (v->r_edge == unused) + abort(); + e = &graph->edges[v->r_edge]; + if (e->r_next != unused) + return; + } + + graph->output_order[--graph->output_index] = e - graph->edges; + + vl = &graph->verts[e->left]; + vm = &graph->verts[e->middle]; + vr = &graph->verts[e->right]; + + if (e->l_prev == unused) + vl->l_edge = e->l_next; + else + graph->edges[e->l_prev].l_next = e->l_next; + if (e->l_next != unused) + graph->edges[e->l_next].l_prev = e->l_prev; + + if (e->m_prev == unused) + vm->m_edge = e->m_next; + else + graph->edges[e->m_prev].m_next = e->m_next; + if (e->m_next != unused) + graph->edges[e->m_next].m_prev = e->m_prev; + + if (e->r_prev == unused) + vr->r_edge = e->r_next; + else + graph->edges[e->r_prev].r_next = e->r_next; + if (e->r_next != unused) + graph->edges[e->r_next].r_prev = e->r_prev; +} + +int +graph3_output_order(struct graph3 *graph) +{ + struct edge3 *e; + size_t i; + + graph->output_index = graph->e; + + for (i = 0; i < graph->v; ++i) + graph3_remove_vertex(graph, &graph->verts[i]); + + for (i = graph->e; i > 0 && i > graph->output_index;) { + --i; + e = &graph->edges[graph->output_order[i]]; + + graph3_remove_vertex(graph, &graph->verts[e->left]); + graph3_remove_vertex(graph, &graph->verts[e->middle]); + graph3_remove_vertex(graph, &graph->verts[e->right]); + } + + if (graph->output_index != 0) + return -1; + + return 0; +} Index: contrib/mg/grep.c =================================================================== --- /dev/null +++ contrib/mg/grep.c @@ -0,0 +1,362 @@ +/* $OpenBSD: grep.c,v 1.49 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" +#include "funmap.h" + +int globalwd = FALSE; +static int compile_goto_error(int, int); +int next_error(int, int); +static int grep(int, int); +static int gid(int, int); +static struct buffer *compile_mode(const char *, const char *); +void grep_init(void); + +static char compile_last_command[NFILEN] = "make "; + +/* + * Hints for next-error + * + * XXX - need some kind of callback to find out when those get killed. + */ +struct mgwin *compile_win; +struct buffer *compile_buffer; + +static PF compile_pf[] = { + compile_goto_error +}; + +static struct KEYMAPE (1) compilemap = { + 1, + 1, + rescan, + { + { CCHR('M'), CCHR('M'), compile_pf, NULL } + } +}; + +void +grep_init(void) +{ + funmap_add(compile_goto_error, "compile-goto-error", 0); + funmap_add(next_error, "next-error", 0); + funmap_add(grep, "grep", 1); + funmap_add(compile, "compile", 0); + funmap_add(gid, "gid", 1); + maps_add((KEYMAP *)&compilemap, "compile"); +} + +/* ARGSUSED */ +static int +grep(int f, int n) +{ + char cprompt[NFILEN], *bufp; + struct buffer *bp; + struct mgwin *wp; + + (void)strlcpy(cprompt, "grep -n ", sizeof(cprompt)); + if ((bufp = eread("Run grep: ", cprompt, NFILEN, + EFDEF | EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if (strlcat(cprompt, " /dev/null", sizeof(cprompt)) >= sizeof(cprompt)) + return (FALSE); + + if ((bp = compile_mode("*grep*", cprompt)) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + compile_win = curwp = wp; + return (TRUE); +} + +/* ARGSUSED */ +int +compile(int f, int n) +{ + char cprompt[NFILEN], *bufp; + struct buffer *bp; + struct mgwin *wp; + + (void)strlcpy(cprompt, compile_last_command, sizeof(cprompt)); + if ((bufp = eread("Compile command: ", cprompt, NFILEN, + EFDEF | EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if (savebuffers(f, n) == ABORT) + return (ABORT); + (void)strlcpy(compile_last_command, bufp, sizeof(compile_last_command)); + + if ((bp = compile_mode("*compile*", cprompt)) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + compile_win = curwp = wp; + gotoline(FFARG, 0); + return (TRUE); +} + +/* id-utils foo. */ +/* ARGSUSED */ +static int +gid(int f, int n) +{ + char command[NFILEN]; + char cprompt[NFILEN], *bufp; + int c; + struct buffer *bp; + struct mgwin *wp; + int i, j, len; + + /* catch ([^\s(){}]+)[\s(){}]* */ + + i = curwp->w_doto; + /* Skip backwards over delimiters we are currently on */ + while (i > 0) { + c = lgetc(curwp->w_dotp, i); + if (isalnum(c) || c == '_') + break; + + i--; + } + + /* Skip the symbol itself */ + for (; i > 0; i--) { + c = lgetc(curwp->w_dotp, i - 1); + if (!isalnum(c) && c != '_') + break; + } + /* Fill the symbol in cprompt[] */ + for (j = 0; j < sizeof(cprompt) - 1 && i < llength(curwp->w_dotp); + j++, i++) { + c = lgetc(curwp->w_dotp, i); + if (!isalnum(c) && c != '_') + break; + cprompt[j] = c; + } + cprompt[j] = '\0'; + + if ((bufp = eread("Run gid (with args): ", cprompt, NFILEN, + (j ? EFDEF : 0) | EFNEW | EFCR)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + len = snprintf(command, sizeof(command), "gid %s", cprompt); + if (len < 0 || len >= sizeof(command)) + return (FALSE); + + if ((bp = compile_mode("*gid*", command)) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + compile_win = curwp = wp; + return (TRUE); +} + +struct buffer * +compile_mode(const char *name, const char *command) +{ + struct buffer *bp; + FILE *fpipe; + char *buf; + size_t sz; + ssize_t len; + int ret, n, status; + char cwd[NFILEN], qcmd[NFILEN]; + char timestr[NTIME]; + time_t t; + + buf = NULL; + sz = 0; + + n = snprintf(qcmd, sizeof(qcmd), "%s 2>&1", command); + if (n < 0 || n >= sizeof(qcmd)) + return (NULL); + + bp = bfind(name, TRUE); + if (bclear(bp) != TRUE) + return (NULL); + + if (getbufcwd(bp->b_cwd, sizeof(bp->b_cwd)) != TRUE) + return (NULL); + addlinef(bp, "cd %s", bp->b_cwd); + addline(bp, qcmd); + addline(bp, ""); + + if (getcwd(cwd, sizeof(cwd)) == NULL) + panic("Can't get current directory!"); + if (chdir(bp->b_cwd) == -1) { + dobeep(); + ewprintf("Can't change dir to %s", bp->b_cwd); + return (NULL); + } + if ((fpipe = popen(qcmd, "r")) == NULL) { + dobeep(); + ewprintf("Problem opening pipe"); + return (NULL); + } + while ((len = getline(&buf, &sz, fpipe)) != -1) { + if (buf[len - 1] == *bp->b_nlchr) + buf[len - 1] = '\0'; + addline(bp, buf); + } + free(buf); + if (ferror(fpipe)) + ewprintf("Problem reading pipe"); + ret = pclose(fpipe); + t = time(NULL); + strftime(timestr, sizeof(timestr), "%a %b %e %T %Y", localtime(&t)); + addline(bp, ""); + if (WIFEXITED(ret)) { + status = WEXITSTATUS(ret); + if (status == 0) + addlinef(bp, "Command finished at %s", timestr); + else + addlinef(bp, "Command exited abnormally with code %d " + "at %s", status, timestr); + } else + addlinef(bp, "Subshell killed by signal %d at %s", + WTERMSIG(ret), timestr); + + bp->b_dotp = bfirstlp(bp); + bp->b_modes[0] = name_mode("fundamental"); + bp->b_modes[1] = name_mode("compile"); + bp->b_nmodes = 1; + + compile_buffer = bp; + + if (chdir(cwd) == -1) { + dobeep(); + ewprintf("Can't change dir back to %s", cwd); + return (NULL); + } + return (bp); +} + +/* ARGSUSED */ +static int +compile_goto_error(int f, int n) +{ + struct buffer *bp; + struct mgwin *wp; + char *fname, *line, *lp, *ln; + int lineno; + char *adjf, path[NFILEN]; + const char *errstr; + struct line *last; + + compile_win = curwp; + compile_buffer = curbp; + last = blastlp(compile_buffer); + + retry: + /* last line is compilation result */ + if (curwp->w_dotp == last) + return (FALSE); + + if ((line = linetostr(curwp->w_dotp)) == NULL) + return (FALSE); + lp = line; + if ((fname = strsep(&lp, ":")) == NULL || *fname == '\0') + goto fail; + if ((ln = strsep(&lp, ":")) == NULL || *ln == '\0') + goto fail; + lineno = (int)strtonum(ln, INT_MIN, INT_MAX, &errstr); + if (errstr) + goto fail; + + if (fname && fname[0] != '/') { + if (getbufcwd(path, sizeof(path)) == FALSE) + goto fail; + if (strlcat(path, fname, sizeof(path)) >= sizeof(path)) + goto fail; + adjf = path; + } else { + adjf = adjustname(fname, TRUE); + } + free(line); + + if (adjf == NULL) + return (FALSE); + + if ((bp = findbuffer(adjf)) == NULL) + return (FALSE); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + curbp = bp; + curwp = wp; + if (bp->b_fname[0] == '\0') + readin(adjf); + gotoline(FFARG, lineno); + return (TRUE); +fail: + free(line); + if (curwp->w_dotp != blastlp(curbp)) { + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_rflag |= WFMOVE; + goto retry; + } + dobeep(); + ewprintf("No more hits"); + return (FALSE); +} + +/* ARGSUSED */ +int +next_error(int f, int n) +{ + if (compile_win == NULL || compile_buffer == NULL) { + dobeep(); + ewprintf("No compilation active"); + return (FALSE); + } + curwp = compile_win; + curbp = compile_buffer; + if (curwp->w_dotp == blastlp(curbp)) { + dobeep(); + ewprintf("No more hits"); + return (FALSE); + } + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_rflag |= WFMOVE; + + return (compile_goto_error(f, n)); +} + +/* + * Since we don't have variables (we probably should) these are command + * processors for changing the values of mode flags. + */ +/* ARGSUSED */ +int +globalwdtoggle(int f, int n) +{ + if (f & FFARG) + globalwd = n > 0; + else + globalwd = !globalwd; + + sgarbf = TRUE; + + return (TRUE); +} Index: contrib/mg/hash.c =================================================================== --- /dev/null +++ contrib/mg/hash.c @@ -0,0 +1,709 @@ +/* DO NOT EDIT + * Automatically generated from term.h */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +extern void mi_vector_hash(const void * __restrict, size_t, uint32_t, + uint32_t[3]); + +static const char _ti_flagids[][6] = { + "bw", + "am", + "bce", + "ccc", + "xhp", + "xhpa", + "cpix", + "crxm", + "xt", + "xenl", + "eo", + "gn", + "hc", + "chts", + "km", + "daisy", + "hs", + "hls", + "in", + "lpix", + "da", + "db", + "mir", + "msgr", + "nxon", + "xsb", + "npc", + "ndscr", + "nrrmc", + "os", + "mc5i", + "xvpa", + "sam", + "eslok", + "hz", + "ul", + "xon", +}; + +#include + +static uint32_t +_ti_flaghash(const void * __restrict key, size_t keylen) +{ + static const uint8_t g[75] = { + 0x13, 0x06, 0x00, 0x17, 0x1f, 0x20, 0x23, 0x00, 0x00, 0x12, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x0d, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x11, 0x02, 0x03, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x15, 0x00, 0x22, 0x02, 0x09, 0x04, 0x08, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1f, 0x22, 0x24, 0x1b, 0x19, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x00, 0x16, 0x00, 0x18, 0x1d, 0x17, 0x00, 0x00, 0x0f, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x05, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000000U, h); + return (g[h[0] % 75] + g[h[1] % 75]) % 37; +} + +const char * +_ti_flagid(ssize_t idx) +{ + + if ((size_t)idx >= __arraycount(_ti_flagids)) + return NULL; + return _ti_flagids[idx]; +} + +ssize_t +_ti_flagindex(const char *key) +{ + uint32_t idx; + + idx = _ti_flaghash((const unsigned char *)key, strlen(key)); + if (idx >= __arraycount(_ti_flagids) || + strcmp(key, _ti_flagids[idx]) != 0) + return -1; + return idx; +} + +static const char _ti_numids[][7] = { + "bitwin", + "bitype", + "bufsz", + "btns", + "cols", + "spinh", + "spinv", + "it", + "lh", + "lw", + "lines", + "lm", + "ma", + "xmc", + "colors", + "maddr", + "mjump", + "pairs", + "wnum", + "mcs", + "mls", + "ncv", + "nlab", + "npins", + "orc", + "orl", + "orhi", + "orvi", + "pb", + "cps", + "vt", + "widcs", + "wsl", +}; + +#include + +static uint32_t +_ti_numhash(const void * __restrict key, size_t keylen) +{ + static const uint8_t g[67] = { + 0x0b, 0x01, 0x16, 0x00, 0x00, 0x11, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x1b, 0x01, 0x00, 0x05, + 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x04, 0x02, 0x00, 0x0f, 0x00, 0x02, 0x1f, 0x06, + 0x15, 0x00, 0x10, 0x0f, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x11, 0x0d, 0x1e, 0x19, 0x0a, 0x00, 0x06, + 0x00, 0x04, 0x1c, 0x00, 0x10, 0x00, 0x0c, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000000U, h); + return (g[h[0] % 67] + g[h[1] % 67]) % 33; +} + +const char * +_ti_numid(ssize_t idx) +{ + + if ((size_t)idx >= __arraycount(_ti_numids)) + return NULL; + return _ti_numids[idx]; +} + +ssize_t +_ti_numindex(const char *key) +{ + uint32_t idx; + + idx = _ti_numhash((const unsigned char *)key, strlen(key)); + if (idx >= __arraycount(_ti_numids) || + strcmp(key, _ti_numids[idx]) != 0) + return -1; + return idx; +} + +static const char _ti_strids[][9] = { + "acsc", + "scesa", + "cbt", + "bel", + "bicr", + "binel", + "birep", + "cr", + "cpi", + "lpi", + "chr", + "cvr", + "csr", + "rmp", + "csnm", + "tbc", + "mgc", + "clear", + "el1", + "el", + "ed", + "csin", + "colornm", + "hpa", + "cmdch", + "cwin", + "cup", + "cud1", + "home", + "civis", + "cub1", + "mrcup", + "cnorm", + "cuf1", + "ll", + "cuu1", + "cvvis", + "defbi", + "defc", + "dch1", + "dl1", + "devt", + "dial", + "dsl", + "dclk", + "dispc", + "hd", + "enacs", + "endbi", + "smacs", + "smam", + "blink", + "bold", + "smcup", + "smdc", + "dim", + "swidm", + "sdrfq", + "ehhlm", + "smir", + "sitm", + "elhlm", + "slm", + "elohlm", + "smicm", + "snlq", + "snrmq", + "smpch", + "prot", + "rev", + "erhlm", + "smsc", + "invis", + "sshm", + "smso", + "ssubm", + "ssupm", + "ethlm", + "smul", + "sum", + "evhlm", + "smxon", + "ech", + "rmacs", + "rmam", + "sgr0", + "rmcup", + "rmdc", + "rwidm", + "rmir", + "ritm", + "rlm", + "rmicm", + "rmpch", + "rmsc", + "rshm", + "rmso", + "rsubm", + "rsupm", + "rmul", + "rum", + "rmxon", + "pause", + "hook", + "flash", + "ff", + "fsl", + "getm", + "wingo", + "hup", + "is1", + "is2", + "is3", + "if", + "iprog", + "initc", + "initp", + "ich1", + "il1", + "ip", + "ka1", + "ka3", + "kb2", + "kbs", + "kbeg", + "kcbt", + "kc1", + "kc3", + "kcan", + "ktbc", + "kclr", + "kclo", + "kcmd", + "kcpy", + "kcrt", + "kctab", + "kdch1", + "kdl1", + "kcud1", + "krmir", + "kend", + "kent", + "kel", + "ked", + "kext", + "kf0", + "kf1", + "kf2", + "kf3", + "kf4", + "kf5", + "kf6", + "kf7", + "kf8", + "kf9", + "kf10", + "kf11", + "kf12", + "kf13", + "kf14", + "kf15", + "kf16", + "kf17", + "kf18", + "kf19", + "kf20", + "kf21", + "kf22", + "kf23", + "kf24", + "kf25", + "kf26", + "kf27", + "kf28", + "kf29", + "kf30", + "kf31", + "kf32", + "kf33", + "kf34", + "kf35", + "kf36", + "kf37", + "kf38", + "kf39", + "kf40", + "kf41", + "kf42", + "kf43", + "kf44", + "kf45", + "kf46", + "kf47", + "kf48", + "kf49", + "kf50", + "kf51", + "kf52", + "kf53", + "kf54", + "kf55", + "kf56", + "kf57", + "kf58", + "kf59", + "kf60", + "kf61", + "kf62", + "kf63", + "kfnd", + "khlp", + "khome", + "kich1", + "kil1", + "kcub1", + "kll", + "kmrk", + "kmsg", + "kmous", + "kmov", + "knxt", + "knp", + "kopn", + "kopt", + "kpp", + "kprv", + "kprt", + "krdo", + "kref", + "krfr", + "krpl", + "krst", + "kres", + "kcuf1", + "ksav", + "kBEG", + "kCAN", + "kCMD", + "kCPY", + "kCRT", + "kDC", + "kDL", + "kslt", + "kEND", + "kEOL", + "kEXT", + "kind", + "kFND", + "kHLP", + "kHOM", + "kIC", + "kLFT", + "kMSG", + "kMOV", + "kNXT", + "kOPT", + "kPRV", + "kPRT", + "kri", + "kRDO", + "kRPL", + "kRIT", + "kRES", + "kSAV", + "kSPD", + "khts", + "kUND", + "kspd", + "kund", + "kcuu1", + "rmkx", + "smkx", + "lf0", + "lf1", + "lf2", + "lf3", + "lf4", + "lf5", + "lf6", + "lf7", + "lf8", + "lf9", + "lf10", + "fln", + "rmln", + "smln", + "rmm", + "smm", + "mhpa", + "mcud1", + "mcub1", + "mcuf1", + "mvpa", + "mcuu1", + "minfo", + "nel", + "porder", + "oc", + "op", + "pad", + "dch", + "dl", + "cud", + "mcud", + "ich", + "indn", + "il", + "cub", + "mcub", + "cuf", + "mcuf", + "rin", + "cuu", + "mcuu", + "pctrm", + "pfkey", + "pfloc", + "pfxl", + "pfx", + "pln", + "mc0", + "mc5p", + "mc4", + "mc5", + "pulse", + "qdial", + "rmclk", + "rep", + "rfi", + "reqmp", + "rs1", + "rs2", + "rs3", + "rf", + "rc", + "vpa", + "sc", + "scesc", + "ind", + "ri", + "scs", + "s0ds", + "s1ds", + "s2ds", + "s3ds", + "sgr1", + "setab", + "setaf", + "sgr", + "setb", + "smgb", + "smgbp", + "sclk", + "setcolor", + "scp", + "setf", + "smgl", + "smglp", + "smglr", + "slines", + "slength", + "smgr", + "smgrp", + "hts", + "smgtb", + "smgt", + "smgtp", + "wind", + "sbim", + "scsd", + "rbim", + "rcsd", + "subcs", + "supcs", + "ht", + "docr", + "tsl", + "tone", + "u0", + "u1", + "u2", + "u3", + "u4", + "u5", + "u6", + "u7", + "u8", + "u9", + "uc", + "hu", + "wait", + "xoffc", + "xonc", + "zerom", +}; + +#include + +static uint32_t +_ti_strhash(const void * __restrict key, size_t keylen) +{ + static const uint16_t g[789] = { + 0x0000, 0x0000, 0x0000, 0x0081, 0x003c, 0x0000, 0x00db, 0x0123, + 0x0000, 0x0045, 0x0000, 0x0000, 0x0000, 0x00b1, 0x0131, 0x0096, + 0x0000, 0x006a, 0x0160, 0x0000, 0x0172, 0x0138, 0x0000, 0x0000, + 0x00a4, 0x00bd, 0x0127, 0x0084, 0x0067, 0x0109, 0x008f, 0x0000, + 0x0000, 0x017c, 0x0000, 0x0053, 0x0156, 0x0000, 0x0000, 0x0000, + 0x00b3, 0x0000, 0x00b9, 0x0096, 0x0058, 0x00b6, 0x00fc, 0x0000, + 0x0058, 0x0000, 0x017d, 0x0000, 0x0006, 0x0000, 0x011f, 0x0000, + 0x0000, 0x00bb, 0x0021, 0x0151, 0x00c7, 0x008c, 0x0000, 0x0058, + 0x008a, 0x011a, 0x0093, 0x0000, 0x0000, 0x0000, 0x013e, 0x0163, + 0x0000, 0x0000, 0x0000, 0x0047, 0x0110, 0x00be, 0x0000, 0x001e, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00b1, 0x0000, 0x0000, + 0x0000, 0x0000, 0x00c5, 0x0034, 0x0181, 0x00be, 0x0094, 0x0000, + 0x0103, 0x002a, 0x0072, 0x0000, 0x0000, 0x0000, 0x0085, 0x0000, + 0x00d5, 0x0000, 0x0000, 0x0000, 0x00c3, 0x0158, 0x00f6, 0x0000, + 0x00e3, 0x0000, 0x0076, 0x0055, 0x016c, 0x0000, 0x008b, 0x0013, + 0x0000, 0x0000, 0x0183, 0x0000, 0x016a, 0x0152, 0x0183, 0x0013, + 0x0000, 0x0000, 0x0032, 0x0043, 0x006c, 0x012c, 0x0000, 0x0000, + 0x0155, 0x0073, 0x0000, 0x0000, 0x0168, 0x0000, 0x0000, 0x0000, + 0x0078, 0x001b, 0x00d2, 0x0020, 0x0000, 0x0000, 0x0015, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0090, 0x00fb, 0x0007, 0x0000, + 0x0000, 0x00ee, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00c2, + 0x0095, 0x0034, 0x0089, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0022, 0x00b9, 0x0173, 0x0157, 0x0000, 0x0000, + 0x0000, 0x0129, 0x0000, 0x000f, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x00ef, 0x0000, 0x0000, 0x00a4, + 0x0000, 0x0000, 0x0000, 0x0168, 0x00ec, 0x0000, 0x0000, 0x0000, + 0x0000, 0x016d, 0x0000, 0x0003, 0x0000, 0x0000, 0x0000, 0x0000, + 0x00e3, 0x0000, 0x0000, 0x0000, 0x00d1, 0x0000, 0x0000, 0x006b, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0063, + 0x0000, 0x004b, 0x0175, 0x00ab, 0x011f, 0x0000, 0x0000, 0x003d, + 0x0000, 0x0105, 0x007a, 0x013e, 0x0000, 0x013b, 0x0127, 0x000a, + 0x00ee, 0x0044, 0x0068, 0x013d, 0x008d, 0x0000, 0x0000, 0x0000, + 0x012a, 0x00f8, 0x0077, 0x0000, 0x0000, 0x0048, 0x0000, 0x0155, + 0x0000, 0x0107, 0x0159, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0073, 0x010f, 0x0166, 0x00fa, 0x0117, 0x0000, 0x0034, + 0x0000, 0x0000, 0x00d0, 0x0000, 0x001d, 0x012f, 0x0000, 0x0000, + 0x017e, 0x0019, 0x0000, 0x0087, 0x0000, 0x0000, 0x013e, 0x013a, + 0x0000, 0x0125, 0x0000, 0x0023, 0x014b, 0x0000, 0x0000, 0x0000, + 0x009d, 0x0133, 0x0000, 0x0000, 0x017d, 0x0000, 0x0000, 0x0000, + 0x0081, 0x0183, 0x0000, 0x0000, 0x0000, 0x00b7, 0x0000, 0x0000, + 0x0000, 0x012a, 0x0112, 0x0000, 0x0000, 0x0102, 0x0000, 0x0074, + 0x0000, 0x0000, 0x012d, 0x0179, 0x00aa, 0x0000, 0x00be, 0x0000, + 0x0000, 0x0164, 0x0161, 0x002c, 0x0030, 0x0000, 0x00ae, 0x013f, + 0x00ba, 0x0075, 0x0000, 0x00bd, 0x0000, 0x0086, 0x0000, 0x0000, + 0x0188, 0x008e, 0x0000, 0x00e1, 0x007f, 0x0000, 0x0116, 0x0000, + 0x0000, 0x0000, 0x011a, 0x015a, 0x0000, 0x00a0, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0076, 0x0000, 0x000d, 0x0044, 0x016e, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x011d, 0x00aa, 0x0064, 0x0010, + 0x0000, 0x003e, 0x0000, 0x0000, 0x0117, 0x00a4, 0x0063, 0x0000, + 0x012b, 0x0000, 0x0000, 0x012b, 0x0031, 0x0074, 0x0044, 0x0000, + 0x017f, 0x0017, 0x0179, 0x0041, 0x0000, 0x004c, 0x0041, 0x0000, + 0x0061, 0x000d, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0064, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a2, 0x0000, 0x00a3, + 0x011b, 0x014e, 0x0000, 0x00be, 0x0000, 0x006e, 0x00d3, 0x0000, + 0x0000, 0x0000, 0x0180, 0x0000, 0x00a1, 0x0000, 0x0000, 0x00e6, + 0x0000, 0x0000, 0x0089, 0x0000, 0x0000, 0x00d0, 0x009f, 0x0032, + 0x0000, 0x0000, 0x00e8, 0x00d9, 0x0000, 0x0002, 0x004b, 0x0000, + 0x0000, 0x0129, 0x0000, 0x016b, 0x0104, 0x0163, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0165, 0x0000, + 0x0000, 0x0000, 0x0155, 0x0017, 0x00c3, 0x0069, 0x0000, 0x00e2, + 0x0000, 0x0000, 0x0000, 0x0092, 0x0000, 0x00b0, 0x0045, 0x0107, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0103, 0x0000, 0x0000, 0x0000, 0x0034, 0x0000, 0x0130, 0x0000, + 0x0000, 0x00cb, 0x0000, 0x0000, 0x0138, 0x00e7, 0x009a, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0119, 0x0000, 0x0000, 0x0120, 0x0000, + 0x00c0, 0x012a, 0x008e, 0x0000, 0x0012, 0x014a, 0x0000, 0x0000, + 0x0000, 0x009c, 0x0000, 0x00fc, 0x0000, 0x0121, 0x001f, 0x016b, + 0x0000, 0x0000, 0x0021, 0x00fd, 0x0022, 0x00af, 0x0051, 0x0000, + 0x00d6, 0x0000, 0x0000, 0x00d0, 0x010e, 0x0000, 0x0000, 0x00c1, + 0x0159, 0x0000, 0x00b8, 0x0000, 0x0000, 0x010f, 0x0024, 0x0115, + 0x0109, 0x0000, 0x001b, 0x0000, 0x00b5, 0x00d8, 0x0039, 0x00de, + 0x0000, 0x008f, 0x011d, 0x004a, 0x0000, 0x00a5, 0x00ea, 0x0144, + 0x0000, 0x0027, 0x016f, 0x014e, 0x000b, 0x002c, 0x014b, 0x000a, + 0x0046, 0x00b3, 0x00bb, 0x0180, 0x0000, 0x0000, 0x0157, 0x0000, + 0x0057, 0x0000, 0x0000, 0x00d1, 0x0181, 0x00d1, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0078, 0x00e3, 0x0000, 0x0000, 0x0000, 0x0036, + 0x00d0, 0x0000, 0x0000, 0x0000, 0x0065, 0x0140, 0x0000, 0x0000, + 0x0000, 0x006f, 0x00a4, 0x0107, 0x0000, 0x0000, 0x0000, 0x0046, + 0x0000, 0x00b4, 0x015b, 0x0000, 0x004d, 0x0000, 0x0000, 0x0000, + 0x0041, 0x0111, 0x00ff, 0x0000, 0x0000, 0x0000, 0x00a5, 0x0086, + 0x007d, 0x0162, 0x0000, 0x00f5, 0x0000, 0x0080, 0x0079, 0x0000, + 0x0000, 0x0000, 0x0000, 0x00a6, 0x00e7, 0x0172, 0x0000, 0x0000, + 0x00dd, 0x0000, 0x001e, 0x0136, 0x0000, 0x0000, 0x00fb, 0x00e6, + 0x0099, 0x0159, 0x0000, 0x0000, 0x0000, 0x0000, 0x00ff, 0x0000, + 0x0000, 0x014b, 0x0000, 0x0000, 0x0000, 0x000b, 0x0000, 0x0000, + 0x017d, 0x0000, 0x009a, 0x012e, 0x0135, 0x0000, 0x0000, 0x015c, + 0x0171, 0x0000, 0x0000, 0x0036, 0x010f, 0x000b, 0x0171, 0x00f9, + 0x0000, 0x0000, 0x00cb, 0x0000, 0x0000, 0x0000, 0x005a, 0x0091, + 0x0000, 0x0135, 0x004c, 0x0118, 0x0000, 0x0000, 0x0000, 0x0138, + 0x0000, 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0041, 0x0000, 0x00b1, 0x0119, 0x0000, 0x0111, 0x0111, 0x0146, + 0x0059, 0x0005, 0x0000, 0x0000, 0x0186, 0x0000, 0x014a, 0x00df, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x000a, 0x015f, + 0x0000, 0x0000, 0x0000, 0x0163, 0x0000, 0x0000, 0x000c, 0x0000, + 0x0000, 0x00dd, 0x0000, 0x0000, 0x00f9, 0x0000, 0x0000, 0x0164, + 0x0176, 0x00bf, 0x0133, 0x0032, 0x013a, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0088, 0x015d, 0x00b8, 0x0078, 0x005b, 0x0000, 0x0027, + 0x017e, 0x0000, 0x007c, 0x011f, 0x0000, 0x0000, 0x0169, 0x0000, + 0x00f2, 0x0000, 0x00b2, 0x00e1, 0x010d, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000001U, h); + return (g[h[0] % 789] + g[h[1] % 789]) % 394; +} + +const char * +_ti_strid(ssize_t idx) +{ + + if ((size_t)idx >= __arraycount(_ti_strids)) + return NULL; + return _ti_strids[idx]; +} + +ssize_t +_ti_strindex(const char *key) +{ + uint32_t idx; + + idx = _ti_strhash((const unsigned char *)key, strlen(key)); + if (idx >= __arraycount(_ti_strids) || + strcmp(key, _ti_strids[idx]) != 0) + return -1; + return idx; +} Index: contrib/mg/help.c =================================================================== --- /dev/null +++ contrib/mg/help.c @@ -0,0 +1,235 @@ +/* $OpenBSD: help.c,v 1.35 2015/03/19 21:22:15 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * Help functions for Mg 2 + */ + +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" +#include "kbd.h" +#include "key.h" +#include "macro.h" + +static int showall(struct buffer *, KEYMAP *, char *); +static int findbind(KEYMAP *, PF, char *, size_t); + +/* + * Read a key from the keyboard, and look it up in the keymap. + * Display the name of the function currently bound to the key. + */ +/* ARGSUSED */ +int +desckey(int f, int n) +{ + KEYMAP *curmap; + PF funct; + int c, m, i, num; + char *pep; + char dprompt[80]; + + if (inmacro) + return (TRUE); /* ignore inside keyboard macro */ + + num = strlcpy(dprompt, "Describe key briefly: ", sizeof(dprompt)); + if (num >= sizeof(dprompt)) + num = sizeof(dprompt) - 1; + pep = dprompt + num; + key.k_count = 0; + m = curbp->b_nmodes; + curmap = curbp->b_modes[m]->p_map; + for (;;) { + for (;;) { + ewprintf("%s", dprompt); + pep[-1] = ' '; + pep = getkeyname(pep, sizeof(dprompt) - (pep - dprompt), + key.k_chars[key.k_count++] = c = getkey(FALSE)); + if ((funct = doscan(curmap, c, &curmap)) != NULL) + break; + *pep++ = '-'; + *pep = '\0'; + } + if (funct != rescan) + break; + if (ISUPPER(key.k_chars[key.k_count - 1])) { + funct = doscan(curmap, + TOLOWER(key.k_chars[key.k_count - 1]), &curmap); + if (funct == NULL) { + *pep++ = '-'; + *pep = '\0'; + continue; + } + if (funct != rescan) + break; + } +nextmode: + if (--m < 0) + break; + curmap = curbp->b_modes[m]->p_map; + for (i = 0; i < key.k_count; i++) { + funct = doscan(curmap, key.k_chars[i], &curmap); + if (funct != NULL) { + if (i == key.k_count - 1 && funct != rescan) + goto found; + funct = rescan; + goto nextmode; + } + } + *pep++ = '-'; + *pep = '\0'; + } +found: + if (funct == rescan || funct == selfinsert) + ewprintf("%k is not bound to any function"); + else if ((pep = (char *)function_name(funct)) != NULL) + ewprintf("%k runs the command %s", pep); + else + ewprintf("%k is bound to an unnamed function"); + return (TRUE); +} + +/* + * This function creates a table, listing all of the command + * keys and their current bindings, and stores the table in the + * *help* pop-up buffer. This lets Mg produce it's own wall chart. + */ +/* ARGSUSED */ +int +wallchart(int f, int n) +{ + int m; + struct buffer *bp; + + bp = bfind("*help*", TRUE); + if (bclear(bp) != TRUE) + /* clear it out */ + return (FALSE); + bp->b_flag |= BFREADONLY; + for (m = curbp->b_nmodes; m > 0; m--) { + if ((addlinef(bp, "Local keybindings for mode %s:", + curbp->b_modes[m]->p_name) == FALSE) || + (showall(bp, curbp->b_modes[m]->p_map, "") == FALSE) || + (addline(bp, "") == FALSE)) + return (FALSE); + } + if ((addline(bp, "Global bindings:") == FALSE) || + (showall(bp, fundamental_map, "") == FALSE)) + return (FALSE); + return (popbuftop(bp, WNONE)); +} + +static int +showall(struct buffer *bp, KEYMAP *map, char *prefix) +{ + KEYMAP *newmap; + char buf[80], keybuf[16]; + PF fun; + int c; + + if (addline(bp, "") == FALSE) + return (FALSE); + + /* XXX - 256 ? */ + for (c = 0; c < 256; c++) { + fun = doscan(map, c, &newmap); + if (fun == rescan || fun == selfinsert) + continue; + getkeyname(buf, sizeof(buf), c); + (void)snprintf(keybuf, sizeof(keybuf), "%s%s ", prefix, buf); + if (fun == NULL) { + if (showall(bp, newmap, keybuf) == FALSE) + return (FALSE); + } else { + if (addlinef(bp, "%-16s%s", keybuf, + function_name(fun)) == FALSE) + return (FALSE); + } + } + return (TRUE); +} + +int +help_help(int f, int n) +{ + KEYMAP *kp; + PF funct; + + if ((kp = name_map("help")) == NULL) + return (FALSE); + ewprintf("a b c: "); + do { + funct = doscan(kp, getkey(FALSE), NULL); + } while (funct == NULL || funct == help_help); + + if (macrodef && macrocount < MAXMACRO) + macro[macrocount - 1].m_funct = funct; + + return ((*funct)(f, n)); +} + +/* ARGSUSED */ +int +apropos_command(int f, int n) +{ + struct buffer *bp; + struct list *fnames, *el; + char string[32]; + + if (eread("apropos: ", string, sizeof(string), EFNUL | EFNEW) == NULL) + return (ABORT); + /* FALSE means we got a 0 character string, which is fine */ + bp = bfind("*help*", TRUE); + if (bclear(bp) == FALSE) + return (FALSE); + + fnames = complete_function_list(""); + for (el = fnames; el != NULL; el = el->l_next) { + char buf[32]; + + if (strstr(el->l_name, string) == NULL) + continue; + + buf[0] = '\0'; + findbind(fundamental_map, name_function(el->l_name), + buf, sizeof(buf)); + + if (addlinef(bp, "%-32s%s", el->l_name, buf) == FALSE) { + free_file_list(fnames); + return (FALSE); + } + } + free_file_list(fnames); + return (popbuftop(bp, WNONE)); +} + +static int +findbind(KEYMAP *map, PF fun, char *buf, size_t len) +{ + KEYMAP *newmap; + PF nfun; + char buf2[16], keybuf[16]; + int c; + + /* XXX - 256 ? */ + for (c = 0; c < 256; c++) { + nfun = doscan(map, c, &newmap); + if (nfun == fun) { + getkeyname(buf, len, c); + return (TRUE); + } + if (nfun == NULL) { + if (findbind(newmap, fun, buf2, sizeof(buf2)) == TRUE) { + getkeyname(keybuf, sizeof(keybuf), c); + (void)snprintf(buf, len, "%s %s", keybuf, buf2); + return (TRUE); + } + } + } + return (FALSE); +} Index: contrib/mg/interpreter.c =================================================================== --- /dev/null +++ contrib/mg/interpreter.c @@ -0,0 +1,979 @@ +/* $OpenBSD: interpreter.c,v 1.32 2021/05/12 11:13:23 lum Exp $ */ +/* + * This file is in the public domain. + * + * Author: Mark Lumsden + */ + +/* + * This file attempts to add some 'scripting' functionality into mg. + * + * The initial goal is to give mg the ability to use it's existing functions + * and structures in a linked-up way. Hopefully resulting in user definable + * functions. The syntax is 'scheme' like but currently it is not a scheme + * interpreter. + * + * At the moment there is no manual page reference to this file. The code below + * is liable to change, so use at your own risk! + * + * If you do want to do some testing, you can add some lines to your .mg file + * like: + * + * 1. Give multiple arguments to a function that usually would accept only one: + * (find-file "a.txt" "b.txt" "c.txt") + * + * 2. Define a single value variable: + * (define myfile "d.txt") + * + * 3. Define a list: + * (define myfiles(list "e.txt" "f.txt")) + * + * 4. Use the previously defined variable or list: + * (find-file myfiles) + * + * To do: + * 1. multiline parsing - currently only single lines supported. + * 2. parsing for '(' and ')' throughout whole string and evaluate correctly. + * 3. conditional execution. + * 4. have memory allocated dynamically for variable values. + * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-] + * at the moment. + * 6. display line numbers with parsing errors. + * 7. oh so many things.... + * [...] + * n. implement user definable functions. + * + * Notes: + * - Currently calls to excline() from this file have the line length and + * line number set to zero. + * That's because excline() uses '\0' as the end of line indicator + * and only the call to foundparen() within excline() uses excline's 2nd + * and 3rd arguments. + * Importantly, any lines sent to there from here will not be + * coming back here. + */ +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "funmap.h" + +#ifdef MGLOG +#include "kbd.h" +#include "log.h" +#endif + +static int multiarg(char *, char *, int); +static int isvar(char **, char **, int); +/*static int dofunc(char **, char **, int);*/ +static int founddef(char *, int, int, int, int); +static int foundlst(char *, int, int, int); +static int expandvals(char *, char *, char *); +static int foundfun(char *, int); +static int doregex(char *, char *); +static void clearexp(void); +static int parse(char *, const char *, const char *, int, int, int, int); +static int parsdef(char *, const char *, const char *, int, int, int); +static int parsval(char *, const char *, const char *, int, int, int); +static int parsexp(char *, const char *, const char *, int, int, int); + +static int exitinterpreter(char *, char *, int); + +TAILQ_HEAD(exphead, expentry) ehead; +struct expentry { + TAILQ_ENTRY(expentry) eentry; + char *fun; /* The 1st string found between parens. */ + char funbuf[BUFSIZE]; + const char *par1; /* Parenthesis at start of string */ + const char *par2; /* Parenthesis at end of string */ + int expctr; /* An incremental counter:+1 for each exp */ + int blkid; /* Which block are we in? */ +}; + +/* + * Structure for scheme keywords. + */ +#define NUMSCHKEYS 4 +#define MAXLENSCHKEYS 17 /* 17 = longest keyword (16) + 1 */ + +char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] = + { + "define", + "list", + "if", + "lambda" + }; + +static const char lp = '('; +static const char rp = ')'; +static char *defnam = NULL; +static int lnm; + +/* + * Line has a '(' as the first non-white char. + * Do some very basic parsing of line. + * Multi-line not supported at the moment, To do. + */ +int +foundparen(char *funstr, int llen, int lnum) +{ + const char *lrp = NULL; + char *p, *begp = NULL, *endp = NULL, *prechr; + char *lastchr = NULL; + int i, ret, pctr, expctr, blkid, inquote, esc; + int elen, spc, ns; + + pctr = expctr = inquote = esc = elen = spc = ns = 0; + blkid = 1; + lnm = lnum; + + /* + * load expressions into a list called 'expentry', to be processd + * when all are obtained. + * Not really live code at the moment. Just part of the process of + * working out what needs to be done. + */ + TAILQ_INIT(&ehead); + + /* + * Check for blocks of code with opening and closing (). + * One block = (cmd p a r a m) + * Two blocks = (cmd p a r a m s)(hola) + * Two blocks = (cmd p a r (list a m s))(hola) + * Only single line at moment, but more for multiline. + */ + p = funstr; + + for (i = 0; i < llen; ++i, p++) { + if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') { + if (*p == ')') + return(dobeep_num("Extra ')' found on line:", + lnm)); + return(dobeep_num("Error line:", lnm)); + } + if (begp != NULL) + elen++; + + if (*p == '\\') { + esc = 1; + } else if (*p == '(') { + if (lastchr != NULL && *lastchr == '(') + return(dobeep_num("Multiple consecutive "\ + "left parantheses line", lnm)); + if (inquote == 0) { + if (begp != NULL) { + if (*prechr == ' ') + ns--; + if (endp == NULL) + *p = '\0'; + else + *endp = '\0'; + + ret = parse(begp, lrp, &lp, blkid, + ++expctr, elen - spc, ns); + if (!ret) { + cleanup(); + return(ret); + } + elen = 0; + } + lrp = &lp; + begp = endp = NULL; + pctr++; + } else if (inquote != 1) { + cleanup(); + return(dobeep_num("Opening and closing quote "\ + "char error line:", lnm)); + } + esc = spc = 0; + } else if (*p == ')') { + if (lastchr != NULL && *lastchr == '(') + return(dobeep_num("Empty parenthesis "\ + "not supported line", lnm)); + if (inquote == 0) { + if (begp != NULL) { + if (*prechr == ' ') + ns--; + if (endp == NULL) + *p = '\0'; + else + *endp = '\0'; + + ret = parse(begp, lrp, &rp, blkid, + ++expctr, elen - spc, ns); + if (!ret) { + cleanup(); + return(ret); + } + elen = 0; + } + lrp = &rp; + begp = endp = NULL; + pctr--; + } else if (inquote != 1) { + cleanup(); + return(dobeep_num("Opening and closing quote "\ + "char error line:", lnm)); + } + esc = spc = 0; + } else if (*p != ' ' && *p != '\t') { + if (begp == NULL) { + begp = p; + if (*begp == '"' || isdigit(*begp)) + return(dobeep_num("First char of "\ + "expression error line:", lnm)); + } + if (*p == '"') { + if (inquote == 0 && esc == 0) { + if (*prechr != ' ' && *prechr != '\t') + return(dobeep_num("Parse error"\ + " line:", lnm)); + inquote++; + } else if (inquote > 0 && esc == 1) + esc = 0; + else + inquote--; + } else if (*prechr == '"' && inquote == 0) { + return(dobeep_num("Parse error line:", lnm)); + } + endp = NULL; + spc = 0; + } else if (endp == NULL && (*p == ' ' || *p == '\t')) { + if (inquote == 0) { + *p = ' '; + endp = p; + spc++; + if (begp != NULL) + ns++; + } + esc = 0; + } else if (*p == '\t' || *p == ' ') { + if (inquote == 0) { + *p = ' '; + spc++; + } + esc = 0; + } + if (*p != '\t' && *p != ' ' && inquote == 0) + lastchr = p; + + if (pctr == 0) { + blkid++; + expctr = 0; + defnam = NULL; + } + prechr = p; + } + + if (pctr != 0) { + cleanup(); + return(dobeep_num("Opening and closing parentheses error line:", + lnm)); + } + if (ret == FALSE) + cleanup(); + else + clearexp(); /* leave lists but remove expressions */ + + return (ret); +} + + +static int +parse(char *begp, const char *par1, const char *par2, int blkid, int expctr, + int elen, int ns) +{ + char *regs; + int ret = FALSE; + + if (strncmp(begp, "define", 6) == 0) { + ret = parsdef(begp, par1, par2, blkid, expctr, elen); + if (ret == TRUE || ret == FALSE) + return (ret); + } else if (strncmp(begp, "list", 4) == 0) + return(parsval(begp, par1, par2, blkid, expctr, elen)); + + regs = "^exit$"; + if (doregex(regs, begp)) + return(exitinterpreter(NULL, NULL, FALSE)); + + /* mg function name regex */ + regs = "^[A-Za-z-]+$"; + if (doregex(regs, begp)) + return(excline(begp, 0, 0)); + + /* Corner case 1 */ + if (strncmp(begp, "global-set-key ", 15) == 0) + /* function name as 2nd param screws up multiarg. */ + return(excline(begp, 0, 0)); + + /* Corner case 2 */ + if (strncmp(begp, "define-key ", 11) == 0) + /* function name as 3rd param screws up multiarg. */ + return(excline(begp, 0, 0)); + + return (parsexp(begp, par1, par2, blkid, expctr, elen)); +} + +static int +parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr, + int elen) +{ + char *regs; + + if ((defnam == NULL) && (expctr != 1)) + return(dobeep_num("'define' incorrectly used line:", lnm)); + + /* Does the line have a incorrect variable 'define' like: */ + /* (define i y z) */ + regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$"; + if (doregex(regs, begp)) + return(dobeep_num("Invalid use of define line:", lnm)); + + /* Does the line have a single variable 'define' like: */ + /* (define i 0) */ + regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$"; + if (doregex(regs, begp)) { + if (par1 == &lp && par2 == &rp && expctr == 1) + return(founddef(begp, blkid, expctr, 1, elen)); + return(dobeep_num("Invalid use of define line:", lnm)); + } + /* Does the line have '(define i(' */ + regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$"; + if (doregex(regs, begp)) { + if (par1 == &lp && par2 == &lp && expctr == 1) + return(founddef(begp, blkid, expctr, 0, elen)); + return(dobeep_num("Invalid use of 'define' line:", lnm)); + } + /* Does the line have '(define (' */ + regs = "^define$"; + if (doregex(regs, begp)) { + if (par1 == &lp && par2 == &lp && expctr == 1) + return(foundfun(begp, expctr)); + return(dobeep_num("Invalid use of 'define' line:", lnm)); + } + + return (ABORT); +} + +static int +parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr, + int elen) +{ + char *regs; + + /* Does the line have 'list' */ + regs = "^list$"; + if (doregex(regs, begp)) + return(dobeep_num("Invalid use of list line:", lnm)); + + /* Does the line have a 'list' like: */ + /* (list "a" "b") */ + regs = "^list[ ]+.*$"; + if (doregex(regs, begp)) { + if (expctr == 1) + return(dobeep_num("list with no-where to go.", lnm)); + + if (par1 == &lp && expctr > 1) + return(foundlst(begp, blkid, expctr, elen)); + + return(dobeep_num("Invalid use of list line:", lnm)); + } + return (FALSE); +} + +static int +parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr, + int elen) +{ + struct expentry *e1 = NULL; + PF funcp; + char *cmdp, *fendp, *valp, *fname, *funb = NULL;; + int numparams, ret; + + cmdp = begp; + fendp = strchr(cmdp, ' '); + *fendp = '\0'; + + /* + * If no extant mg command found, just return. + */ + if ((funcp = name_function(cmdp)) == NULL) + return (dobeep_msgs("Unknown command: ", cmdp)); + + numparams = numparams_function(funcp); + if (numparams == 0) + return (dobeep_msgs("Command takes no arguments:", cmdp)); + + if (numparams == -1) + return (dobeep_msgs("Interactive command found:", cmdp)); + + if ((e1 = malloc(sizeof(struct expentry))) == NULL) { + cleanup(); + return (dobeep_msg("malloc Error")); + } + TAILQ_INSERT_HEAD(&ehead, e1, eentry); + if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) { + cleanup(); + return(dobeep_msg("strndup error")); + } + cmdp = e1->fun; + fname = e1->fun; + e1->funbuf[0] = '\0'; + funb = e1->funbuf; + e1->expctr = expctr; + e1->blkid = blkid; + /* need to think about these two */ + e1->par1 = par1; + e1->par2 = par2; + + *fendp = ' '; + valp = fendp + 1; + + ret = expandvals(cmdp, valp, funb); + if (!ret) + return (ret); + + return (multiarg(fname, funb, numparams)); +} + +/* + * Pass a list of arguments to a function. + */ +static int +multiarg(char *cmdp, char *argbuf, int numparams) +{ + char excbuf[BUFSIZE]; + char *argp, *p, *s = " "; + char *regs; + int spc, numspc; + int fin, inquote; + + argp = argbuf; + spc = 1; /* initially fake a space so we find first argument */ + numspc = fin = inquote = 0; + + for (p = argbuf; *p != '\0'; p++) { + if (*(p + 1) == '\0') + fin = 1; + + if (*p != ' ') { + if (*p == '"') { + if (inquote == 1) + inquote = 0; + else + inquote = 1; + } + if (spc == 1) + if ((numspc % numparams) == 0) { + argp = p; + } + spc = 0; + } + if ((*p == ' ' && inquote == 0) || fin) { + if (spc == 1)/* || (numspc % numparams == 0))*/ + continue; + if ((numspc % numparams) != (numparams - 1)) { + numspc++; + continue; + } + if (*p == ' ') { + *p = '\0'; /* terminate arg string */ + } + excbuf[0] = '\0'; + regs = "[\"]+.*[\"]+"; + + if (!doregex(regs, argp)) { + const char *errstr; + int iters; + + iters = strtonum(argp, 0, INT_MAX, &errstr); + if (errstr != NULL) + return (dobeep_msgs("Var not found:", + argp)); + } + + if (strlcpy(excbuf, cmdp, sizeof(excbuf)) + >= sizeof(excbuf)) + return (dobeep_msg("strlcpy error")); + if (strlcat(excbuf, s, sizeof(excbuf)) + >= sizeof(excbuf)) + return (dobeep_msg("strlcat error")); + if (strlcat(excbuf, argp, sizeof(excbuf)) + >= sizeof(excbuf)) + return (dobeep_msg("strlcat error")); + + excline(excbuf, 0, 0); + + if (fin) + break; + + *p = ' '; /* unterminate arg string */ + numspc++; + spc = 1; + } + } + return (TRUE); +} + +/* + * Is an item a value or a variable? + */ +static int +isvar(char **argp, char **varbuf, int sizof) +{ + struct varentry *v1 = NULL; + + if (SLIST_EMPTY(&varhead)) + return (FALSE); +#ifdef MGLOG + mglog_isvar(*varbuf, *argp, sizof); +#endif + SLIST_FOREACH(v1, &varhead, entry) { + if (strcmp(*argp, v1->v_name) == 0) { + (void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof); + return (TRUE); + } + } + return (FALSE); +} + + +static int +foundfun(char *defstr, int expctr) +{ + return (TRUE); +} + +static int +foundlst(char *defstr, int blkid, int expctr, int elen) +{ + char *p; + + p = strstr(defstr, " "); + p = skipwhite(p); + expandvals(NULL, p, defnam); + + return (TRUE); +} + +/* + * 'define' strings follow the regex in parsdef(). + */ +static int +founddef(char *defstr, int blkid, int expctr, int hasval, int elen) +{ + struct varentry *vt, *v1 = NULL; + char *p, *vnamep, *vendp = NULL, *valp; + + p = strstr(defstr, " "); /* move to first ' ' char. */ + vnamep = skipwhite(p); /* find first char of var name. */ + vendp = vnamep; + + /* now find the end of the define/list name */ + while (1) { + ++vendp; + if (*vendp == ' ') + break; + } + *vendp = '\0'; + + /* + * Check list name is not an existing mg function. + */ + if (name_function(vnamep) != NULL) + return(dobeep_msgs("Variable/function name clash:", vnamep)); + + if (!SLIST_EMPTY(&varhead)) { + SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) { + if (strcmp(vnamep, v1->v_name) == 0) + SLIST_REMOVE(&varhead, v1, varentry, entry); + } + } + if ((v1 = malloc(sizeof(struct varentry))) == NULL) + return (ABORT); + SLIST_INSERT_HEAD(&varhead, v1, entry); + if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL) + return(dobeep_msg("strndup error")); + vnamep = v1->v_name; + v1->v_count = 0; + v1->v_vals = NULL; + v1->v_buf[0] = '\0'; + + defnam = v1->v_buf; + + if (hasval) { + valp = skipwhite(vendp + 1); + + expandvals(NULL, valp, defnam); + defnam = NULL; + } + *vendp = ' '; + return (TRUE); +} + + +static int +expandvals(char *cmdp, char *valp, char *bp) +{ + char excbuf[BUFSIZE], argbuf[BUFSIZE]; + char contbuf[BUFSIZE], varbuf[BUFSIZE]; + char *argp, *endp, *p, *v, *s = " "; + char *regs; + int spc, cnt; + int inlist, sizof, fin, inquote; + + /* now find the first argument */ + p = skipwhite(valp); + + if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) + return (dobeep_msg("strlcpy error")); + argp = argbuf; + spc = 1; /* initially fake a space so we find first argument */ + inlist = fin = inquote = cnt = spc = 0; + + for (p = argbuf; *p != '\0'; p++) { + if (*(p + 1) == '\0') + fin = 1; + + if (*p != ' ') { + if (*p == '"') { + if (inquote == 1) + inquote = 0; + else + inquote = 1; + } + if (spc == 1) + argp = p; + spc = 0; + } + if ((*p == ' ' && inquote == 0) || fin) { + if (spc == 1) + continue; + /* terminate arg string */ + if (*p == ' ') { + *p = '\0'; + } + endp = p + 1; + excbuf[0] = '\0'; + varbuf[0] = '\0'; + contbuf[0] = '\0'; + sizof = sizeof(varbuf); + v = varbuf; + regs = "[\"]+.*[\"]+"; + if (doregex(regs, argp)) + ; /* found quotes */ + else if (isvar(&argp, &v, sizof)) { + + (void)(strlcat(varbuf, " ", + sizof) >= sizof); + + *p = ' '; + (void)(strlcpy(contbuf, endp, + sizeof(contbuf)) >= sizeof(contbuf)); + + (void)(strlcat(varbuf, contbuf, + sizof) >= sizof); + + argbuf[0] = ' '; + argbuf[1] = '\0'; + (void)(strlcat(argbuf, varbuf, + sizof) >= sizof); + + p = argp = argbuf; + spc = 1; + fin = 0; + continue; + } else { + const char *errstr; + int iters; + + iters = strtonum(argp, 0, INT_MAX, &errstr); + if (errstr != NULL) + return (dobeep_msgs("Var not found:", + argp)); + } +#ifdef MGLOG + mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE); +#endif + if (*bp != '\0') { + if (strlcat(bp, s, BUFSIZE) >= BUFSIZE) + return (dobeep_msg("strlcat error")); + } + if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) { + return (dobeep_msg("strlcat error")); + } +/* v1->v_count++;*/ + + if (fin) + break; + + *p = ' '; /* unterminate arg string */ + spc = 1; + } + } + return (TRUE); +} + +/* + * Finished with buffer evaluation, so clean up any vars. + * Perhaps keeps them in mg even after use,... + */ +/*static int +clearvars(void) +{ + struct varentry *v1 = NULL; + + while (!SLIST_EMPTY(&varhead)) { + v1 = SLIST_FIRST(&varhead); + SLIST_REMOVE_HEAD(&varhead, entry); + free(v1->v_name); + free(v1); + } + return (FALSE); +} +*/ +/* + * Finished with block evaluation, so clean up any expressions. + */ +static void +clearexp(void) +{ + struct expentry *e1 = NULL; + + while (!TAILQ_EMPTY(&ehead)) { + e1 = TAILQ_FIRST(&ehead); + TAILQ_REMOVE(&ehead, e1, eentry); + free(e1->fun); + free(e1); + } + return; +} + +/* + * Cleanup before leaving. + */ +void +cleanup(void) +{ + defnam = NULL; + + clearexp(); +/* clearvars();*/ +} + +/* + * Test a string against a regular expression. + */ +static int +doregex(char *r, char *e) +{ + regex_t regex_buff; + + if (regcomp(®ex_buff, r, REG_EXTENDED)) { + regfree(®ex_buff); + return(dobeep_num("Regex compilation error line:", lnm)); + } + if (!regexec(®ex_buff, e, 0, NULL, 0)) { + regfree(®ex_buff); + return(TRUE); + } + regfree(®ex_buff); + return(FALSE); +} + +/* + * Display a message so it is apparent that this is the method which stopped + * execution. + */ +static int +exitinterpreter(char *ptr, char *dobuf, int dosiz) +{ + cleanup(); + if (batch == 0) + return(dobeep_msg("Interpreter exited via exit command.")); + return(FALSE); +} + +/* + * All code below commented out (until end of file). + * + * Need to think about how interpreter functions are done. + * Probably don't have a choice with string-append(). + +static int getenvironmentvariable(char *, char *, int); +static int stringappend(char *, char *, int); + +typedef int (*PFI)(char *, char *, int); + + +struct ifunmap { + PFI fn_funct; + const char *fn_name; + struct ifunmap *fn_next; +}; +static struct ifunmap *ifuns; + +static struct ifunmap ifunctnames[] = { + {exitinterpreter, "exit"}, + {getenvironmentvariable, "get-environment-variable"}, + {stringappend, "string-append"}, + {NULL, NULL} +}; + +void +ifunmap_init(void) +{ + struct ifunmap *fn; + + for (fn = ifunctnames; fn->fn_name != NULL; fn++) { + fn->fn_next = ifuns; + ifuns = fn; + } +} + +PFI +name_ifun(const char *ifname) +{ + struct ifunmap *fn; + + for (fn = ifuns; fn != NULL; fn = fn->fn_next) { + if (strcmp(fn->fn_name, ifname) == 0) + return (fn->fn_funct); + } + + return (NULL); +} + + +int +dofunc(char **ifname, char **tmpbuf, int sizof) +{ + PFI fnc; + char *p, *tmp; + + p = strstr(*ifname, " "); + *p = '\0'; + + fnc = name_ifun(*ifname); + if (fnc == NULL) + return (FALSE); + + *p = ' '; + + tmp = *tmpbuf; + + fnc(p, tmp, sizof); + + return (TRUE); +} + +static int +getenvironmentvariable(char *ptr, char *dobuf, int dosiz) +{ + char *t; + char *tmp; + const char *q = "\""; + + t = skipwhite(ptr); + + if (t[0] == *q || t[strlen(t) - 1] == *q) + return (dobeep_msgs("Please remove '\"' around:", t)); + if ((tmp = getenv(t)) == NULL || *tmp == '\0') + return(dobeep_msgs("Envar not found:", t)); + + dobuf[0] = '\0'; + if (strlcat(dobuf, q, dosiz) >= dosiz) + return (dobeep_msg("strlcat error")); + if (strlcat(dobuf, tmp, dosiz) >= dosiz) + return (dobeep_msg("strlcat error")); + if (strlcat(dobuf, q, dosiz) >= dosiz) + return (dobeep_msg("strlcat error")); + + return (TRUE); +} + +static int +stringappend(char *ptr, char *dobuf, int dosiz) +{ + char varbuf[BUFSIZE], funbuf[BUFSIZE]; + char *p, *f, *v, *vendp; + int sizof, fin = 0; + + varbuf[0] = funbuf[0] = '\0'; + f = funbuf; + v = varbuf; + sizof = sizeof(varbuf); + *dobuf = '\0'; + + p = skipwhite(ptr); + + while (*p != '\0') { + vendp = p; + while (1) { + if (*vendp == ' ') { + break; + } else if (*vendp == '\0') { + fin = 1; + break; + } + ++vendp; + } + *vendp = '\0'; + + if (isvar(&p, &v, sizof)) { + if (v[0] == '"' && v[strlen(v) - 1] == '"' ) { + v[strlen(v) - 1] = '\0'; + v = v + 1; + } + if (strlcat(f, v, sizof) >= sizof) + return (dobeep_msg("strlcat error")); + } else { + if (p[0] == '"' && p[strlen(p) - 1] == '"' ) { + p[strlen(p) - 1] = '\0'; + p = p + 1; + } + if (strlcat(f, p, sizof) >= sizof) + return (dobeep_msg("strlcat error")); + } + if (fin) + break; + vendp++; + if (*vendp == '\0') + break; + p = skipwhite(vendp); + } + + (void)snprintf(dobuf, dosiz, "\"%s\"", f); + + return (TRUE); +} + +Index: main.c +=================================================================== +RCS file: /cvs/src/usr.bin/mg/main.c,v +retrieving revision 1.89 +diff -u -p -u -p -r1.89 main.c +--- main.c 20 Mar 2021 09:00:49 -0000 1.89 ++++ main.c 12 Apr 2021 17:58:52 -0000 +@@ -133,10 +133,12 @@ main(int argc, char **argv) + extern void grep_init(void); + extern void cmode_init(void); + extern void dired_init(void); ++ extern void ifunmap_init(void); + + dired_init(); + grep_init(); + cmode_init(); ++ ifunmap_init(); + } + + +*/ Index: contrib/mg/kbd.h =================================================================== --- /dev/null +++ contrib/mg/kbd.h @@ -0,0 +1,57 @@ +/* $OpenBSD: kbd.h,v 1.19 2015/03/19 21:48:05 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * kbd.h: type definitions for symbol.c and kbd.c for mg experimental + */ + +struct map_element { + KCHAR k_base; /* first key in element */ + KCHAR k_num; /* last key in element */ + PF *k_funcp; /* pointer to array of pointers */ + /* to functions */ + struct keymap_s *k_prefmap; /* keymap of ONLY prefix key in */ + /* element */ +}; + +/* + * Predefined keymaps are NOT type KEYMAP because final array needs + * dimension. If any changes are made to this struct, they must be reflected + * in all keymap declarations. + */ + +#define KEYMAPE(NUM) { \ + short map_num; /* elements used */ \ + short map_max; /* elements allocated */\ + PF map_default; /* default function */ \ + struct map_element map_element[NUM]; /* really [e_max] */ \ +} +typedef struct keymap_s KEYMAPE(1) KEYMAP; + +/* Number of map_elements to grow an overflowed keymap by */ +#define MAPGROW 3 +#define MAPINIT (MAPGROW+1) + +/* Max number of default bindings added to avoid creating new element */ +#define MAPELEDEF 4 + +struct maps_s { + KEYMAP *p_map; + const char *p_name; + struct maps_s *p_next; +}; + +extern struct maps_s *maps; +extern struct maps_s fundamental_mode; +#define fundamental_map (fundamental_mode.p_map) + +int dobindkey(KEYMAP *, const char *, const char *); +KEYMAP *name_map(const char *); +struct maps_s *name_mode(const char *); +PF doscan(KEYMAP *, int, KEYMAP **); +void maps_init(void); +int maps_add(KEYMAP *, const char *); + +extern struct map_element *ele; +extern struct maps_s *defb_modes[]; Index: contrib/mg/kbd.c =================================================================== --- /dev/null +++ contrib/mg/kbd.c @@ -0,0 +1,462 @@ +/* $OpenBSD: kbd.c,v 1.35 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Terminal independent keyboard handling. + */ + +#include +#include +#include + +#include "def.h" +#include "kbd.h" +#include "key.h" +#include "macro.h" + +#ifdef MGLOG +#include "log.h" +#endif + +#define METABIT 0x80 + +#define PROMPTL 80 +char prompt[PROMPTL] = "", *promptp = prompt; + +static int mgwrap(PF, int, int); + +static int use_metakey = TRUE; +static int pushed = FALSE; +static int pushedc; + +struct map_element *ele; +struct key key; +int rptcount; + +/* + * Toggle the value of use_metakey + */ +int +do_meta(int f, int n) +{ + if (f & FFARG) + use_metakey = n > 0; + else + use_metakey = !use_metakey; + ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis"); + return (TRUE); +} + +static int bs_map = 0; + +/* + * Toggle backspace mapping + */ +int +bsmap(int f, int n) +{ + if (f & FFARG) + bs_map = n > 0; + else + bs_map = !bs_map; + ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis"); + return (TRUE); +} + +void +ungetkey(int c) +{ + if (use_metakey && pushed && c == CCHR('[')) + pushedc |= METABIT; + else + pushedc = c; + pushed = TRUE; +} + +int +getkey(int flag) +{ + int c; + + if (flag && !pushed) { + if (prompt[0] != '\0' && ttwait(2000)) { + /* avoid problems with % */ + ewprintf("%s", prompt); + /* put the cursor back */ + update(CMODE); + epresf = KCLEAR; + } + if (promptp > prompt) + *(promptp - 1) = ' '; + } + if (pushed) { + c = pushedc; + pushed = FALSE; + } else + c = ttgetc(); + + if (bs_map) { + if (c == CCHR('H')) + c = CCHR('?'); + else if (c == CCHR('?')) + c = CCHR('H'); + } + if (use_metakey && (c & METABIT)) { + pushedc = c & ~METABIT; + pushed = TRUE; + c = CCHR('['); + } + if (flag && promptp < &prompt[PROMPTL - 5]) { + promptp = getkeyname(promptp, + sizeof(prompt) - (promptp - prompt) - 1, c); + *promptp++ = '-'; + *promptp = '\0'; + } + return (c); +} + +/* + * doscan scans a keymap for a keyboard character and returns a pointer + * to the function associated with that character. Sets ele to the + * keymap element the keyboard was found in as a side effect. + */ +PF +doscan(KEYMAP *map, int c, KEYMAP **newmap) +{ + struct map_element *elec = &map->map_element[0]; + struct map_element *last = &map->map_element[map->map_num]; + PF ret; + + while (elec < last && c > elec->k_num) + elec++; + + /* used by prefix and binding code */ + ele = elec; + if (elec >= last || c < elec->k_base) + ret = map->map_default; + else + ret = elec->k_funcp[c - elec->k_base]; + if (ret == NULL && newmap != NULL) + *newmap = elec->k_prefmap; + + return (ret); +} + +int +doin(void) +{ + KEYMAP *curmap; + PF funct; + + *(promptp = prompt) = '\0'; + curmap = curbp->b_modes[curbp->b_nmodes]->p_map; + key.k_count = 0; + while ((funct = doscan(curmap, (key.k_chars[key.k_count++] = + getkey(TRUE)), &curmap)) == NULL) + /* nothing */; + +#ifdef MGLOG + if (!mglog(funct, curmap)) + ewprintf("Problem with logging"); +#endif + + if (macrodef && macrocount < MAXMACRO) + macro[macrocount++].m_funct = funct; + + return (mgwrap(funct, 0, 1)); +} + +int +rescan(int f, int n) +{ + int c; + KEYMAP *curmap; + int i; + PF fp = NULL; + int md = curbp->b_nmodes; + + for (;;) { + if (ISUPPER(key.k_chars[key.k_count - 1])) { + c = TOLOWER(key.k_chars[key.k_count - 1]); + curmap = curbp->b_modes[md]->p_map; + for (i = 0; i < key.k_count - 1; i++) { + if ((fp = doscan(curmap, (key.k_chars[i]), + &curmap)) != NULL) + break; + } + if (fp == NULL) { + if ((fp = doscan(curmap, c, NULL)) == NULL) + while ((fp = doscan(curmap, + key.k_chars[key.k_count++] = + getkey(TRUE), &curmap)) == NULL) + /* nothing */; + if (fp != rescan) { + if (macrodef && macrocount <= MAXMACRO) + macro[macrocount - 1].m_funct + = fp; + return (mgwrap(fp, f, n)); + } + } + } + /* try previous mode */ + if (--md < 0) + return (ABORT); + curmap = curbp->b_modes[md]->p_map; + for (i = 0; i < key.k_count; i++) { + if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL) + break; + } + if (fp == NULL) { + while ((fp = doscan(curmap, key.k_chars[i++] = + getkey(TRUE), &curmap)) == NULL) + /* nothing */; + key.k_count = i; + } + if (fp != rescan && i >= key.k_count - 1) { + if (macrodef && macrocount <= MAXMACRO) + macro[macrocount - 1].m_funct = fp; + return (mgwrap(fp, f, n)); + } + } +} + +int +universal_argument(int f, int n) +{ + KEYMAP *curmap; + PF funct; + int c, nn = 4; + + if (f & FFUNIV) + nn *= n; + for (;;) { + key.k_chars[0] = c = getkey(TRUE); + key.k_count = 1; + if (c == '-') + return (negative_argument(f, nn)); + if (c >= '0' && c <= '9') + return (digit_argument(f, nn)); + curmap = curbp->b_modes[curbp->b_nmodes]->p_map; + while ((funct = doscan(curmap, c, &curmap)) == NULL) { + key.k_chars[key.k_count++] = c = getkey(TRUE); + } + if (funct != universal_argument) { + if (macrodef && macrocount < MAXMACRO - 1) { + if (f & FFARG) + macrocount--; + macro[macrocount++].m_count = nn; + macro[macrocount++].m_funct = funct; + } + return (mgwrap(funct, FFUNIV, nn)); + } + nn <<= 2; + } +} + +/* ARGSUSED */ +int +digit_argument(int f, int n) +{ + KEYMAP *curmap; + PF funct; + int nn, c; + + nn = key.k_chars[key.k_count - 1] - '0'; + for (;;) { + c = getkey(TRUE); + if (c < '0' || c > '9') + break; + nn *= 10; + nn += c - '0'; + } + key.k_chars[0] = c; + key.k_count = 1; + curmap = curbp->b_modes[curbp->b_nmodes]->p_map; + while ((funct = doscan(curmap, c, &curmap)) == NULL) { + key.k_chars[key.k_count++] = c = getkey(TRUE); + } + if (macrodef && macrocount < MAXMACRO - 1) { + if (f & FFARG) + macrocount--; + else + macro[macrocount - 1].m_funct = universal_argument; + macro[macrocount++].m_count = nn; + macro[macrocount++].m_funct = funct; + } + return (mgwrap(funct, FFOTHARG, nn)); +} + +int +negative_argument(int f, int n) +{ + KEYMAP *curmap; + PF funct; + int c; + int nn = 0; + + for (;;) { + c = getkey(TRUE); + if (c < '0' || c > '9') + break; + nn *= 10; + nn += c - '0'; + } + if (nn) + nn = -nn; + else + nn = -n; + key.k_chars[0] = c; + key.k_count = 1; + curmap = curbp->b_modes[curbp->b_nmodes]->p_map; + while ((funct = doscan(curmap, c, &curmap)) == NULL) { + key.k_chars[key.k_count++] = c = getkey(TRUE); + } + if (macrodef && macrocount < MAXMACRO - 1) { + if (f & FFARG) + macrocount--; + else + macro[macrocount - 1].m_funct = universal_argument; + macro[macrocount++].m_count = nn; + macro[macrocount++].m_funct = funct; + } + return (mgwrap(funct, FFNEGARG, nn)); +} + +/* + * Insert a character. While defining a macro, create a "LINE" containing + * all inserted characters. + */ +int +selfinsert(int f, int n) +{ + struct line *lp; + int c; + int count; + + if (n < 0) + return (FALSE); + if (n == 0) + return (TRUE); + c = key.k_chars[key.k_count - 1]; + + if (macrodef && macrocount < MAXMACRO) { + if (f & FFARG) + macrocount -= 2; + + /* last command was insert -- tack on the end */ + if (lastflag & CFINS) { + macrocount--; + /* Ensure the line can handle the new characters */ + if (maclcur->l_size < maclcur->l_used + n) { + if (lrealloc(maclcur, maclcur->l_used + n) == + FALSE) + return (FALSE); + } + maclcur->l_used += n; + /* Copy in the new data */ + for (count = maclcur->l_used - n; + count < maclcur->l_used; count++) + maclcur->l_text[count] = c; + } else { + macro[macrocount - 1].m_funct = insert; + if ((lp = lalloc(n)) == NULL) + return (FALSE); + lp->l_bp = maclcur; + lp->l_fp = maclcur->l_fp; + maclcur->l_fp = lp; + maclcur = lp; + for (count = 0; count < n; count++) + lp->l_text[count] = c; + } + thisflag |= CFINS; + } + if (c == *curbp->b_nlchr) { + do { + count = lnewline(); + } while (--n && count == TRUE); + return (count); + } + + /* overwrite mode */ + if (curbp->b_flag & BFOVERWRITE) { + lchange(WFEDIT); + while (curwp->w_doto < llength(curwp->w_dotp) && n--) + lputc(curwp->w_dotp, curwp->w_doto++, c); + if (n <= 0) + return (TRUE); + } + return (linsert(n, c)); +} + +/* + * selfinsert() can't be called directly from a startup file or by + * 'eval-current-buffer' since it is by design, meant to be called interactively + * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to + * be used by excline(). Having ask_selfinsert() helps with regression testing. + * No manual page entry since use case is a bit obscure. See 'insert' command. + */ +int +ask_selfinsert(int f, int n) +{ + char *c, cbuf[2]; + + if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf), + EFNEW)) == NULL || (c[0] == '\0')) + return (ABORT); + + key.k_chars[0] = *c; + key.k_chars[1] = '\0'; + key.k_count = 1; + + return (selfinsert(FFRAND, 1)); +} + +/* + * This could be implemented as a keymap with everything defined as self-insert. + */ +int +quote(int f, int n) +{ + int c; + + key.k_count = 1; + if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') { + key.k_chars[0] -= '0'; + if ((c = getkey(TRUE)) >= '0' && c <= '7') { + key.k_chars[0] <<= 3; + key.k_chars[0] += c - '0'; + if ((c = getkey(TRUE)) >= '0' && c <= '7') { + key.k_chars[0] <<= 3; + key.k_chars[0] += c - '0'; + } else + ungetkey(c); + } else + ungetkey(c); + } + return (selfinsert(f, n)); +} + +/* + * Wraper function to count invocation repeats. + * We ignore any function whose sole purpose is to get us + * to the intended function. + */ +static int +mgwrap(PF funct, int f, int n) +{ + static PF ofp; + + if (funct != rescan && + funct != negative_argument && + funct != digit_argument && + funct != universal_argument) { + if (funct == ofp) + rptcount++; + else + rptcount = 0; + ofp = funct; + } + + return ((*funct)(f, n)); +} Index: contrib/mg/key.h =================================================================== --- /dev/null +++ contrib/mg/key.h @@ -0,0 +1,14 @@ +/* $OpenBSD: key.h,v 1.6 2019/06/22 15:38:15 lum Exp $ */ + +/* This file is in the public domain. */ + +/* key.h: Insert file for mg 2 functions that need to reference key pressed */ + +#define MAXKEY 8 /* maximum number of prefix chars */ + +struct key { /* the character sequence in a key */ + int k_count; /* number of chars */ + KCHAR k_chars[MAXKEY]; /* chars */ +}; + +extern struct key key; Index: contrib/mg/keymap.c =================================================================== --- /dev/null +++ contrib/mg/keymap.c @@ -0,0 +1,577 @@ +/* $OpenBSD: keymap.c,v 1.59 2021/04/20 10:02:50 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Keyboard maps. This is character set dependent. The terminal specific + * parts of building the keymap has been moved to a better place. + */ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" + +/* + * initial keymap declarations, deepest first + */ + +static PF cHcG[] = { + ctrlg, /* ^G */ + help_help /* ^H */ +}; + +static PF cHa[] = { + apropos_command, /* a */ + wallchart, /* b */ + desckey /* c */ +}; + +struct KEYMAPE (2) helpmap = { + 2, + 2, + rescan, + { + { + CCHR('G'), CCHR('H'), cHcG, NULL + }, + { + 'a', 'c', cHa, NULL + } + } +}; + +static PF cCsc[] = { + cscallerfuncs, /* c */ + csdefinition, /* d */ + csegrep, /* e */ + csfindfile, /* f */ + rescan, /* g */ + rescan, /* h */ + csfindinc, /* i */ + rescan, /* j */ + rescan, /* k */ + rescan, /* l */ + rescan, /* m */ + csnextmatch, /* n */ + rescan, /* o */ + csprevmatch, /* p */ + rescan, /* q */ + rescan, /* r */ + cssymbol, /* s */ + csfindtext /* t */ +}; + +static struct KEYMAPE (1) cCsmap = { + 1, + 1, + rescan, + { + { + 'c', 't', cCsc, NULL + } + } +}; + +static PF cCs[] = { + NULL /* s */ +}; + +struct KEYMAPE (2) ccmap = { + 2, + 2, + rescan, + { + { + CCHR('@'), CCHR('@'), (PF[]){ rescan }, NULL + }, + { + 's', 's', cCs, (KEYMAP *) & cCsmap + } + } +}; + +static PF cX4cF[] = { + poptofile, /* ^f */ + ctrlg /* ^g */ +}; +static PF cX4b[] = { + poptobuffer, /* b */ + rescan, /* c */ + rescan, /* d */ + rescan, /* e */ + poptofile /* f */ +}; +static struct KEYMAPE (2) cX4map = { + 2, + 2, + rescan, + { + { + CCHR('F'), CCHR('G'), cX4cF, NULL + }, + { + 'b', 'f', cX4b, NULL + } + } +}; + +static PF cXcB[] = { + listbuffers, /* ^B */ + quit, /* ^C */ + rescan, /* ^D */ + rescan, /* ^E */ + filevisit, /* ^F */ + ctrlg /* ^G */ +}; + +static PF cXcJ[] = { + dired_jump, /* ^J */ + rescan, /* ^K */ + lowerregion, /* ^L */ + rescan, /* ^M */ + rescan, /* ^N */ + deblank, /* ^O */ + rescan, /* ^P */ + togglereadonly, /* ^Q */ + filevisitro, /* ^R */ + filesave, /* ^S */ + rescan, /* ^T */ + upperregion, /* ^U */ + filevisitalt, /* ^V */ + filewrite, /* ^W */ + swapmark /* ^X */ +}; + +static PF cXlp[] = { + definemacro, /* ( */ + finishmacro /* ) */ +}; + +static PF cX0[] = { + delwind, /* 0 */ + onlywind, /* 1 */ + splitwind, /* 2 */ + rescan, /* 3 */ + NULL /* 4 */ +}; + +static PF cXeq[] = { + showcpos /* = */ +}; + +static PF cXcar[] = { + enlargewind, /* ^ */ + rescan, /* _ */ + next_error, /* ` */ + rescan, /* a */ + usebuffer, /* b */ + rescan, /* c */ + rescan, /* d */ + executemacro, /* e */ + setfillcol, /* f */ + gotoline, /* g */ + markbuffer, /* h */ + fileinsert, /* i */ + rescan, /* j */ + killbuffer_cmd, /* k */ + rescan, /* l */ + rescan, /* m */ + nextwind, /* n */ + nextwind, /* o */ + prevwind, /* p */ + rescan, /* q */ + rescan, /* r */ + savebuffers, /* s */ + rescan, /* t */ + undo /* u */ +}; + +struct KEYMAPE (6) cXmap = { + 6, + 6, + rescan, + { + { + CCHR('B'), CCHR('G'), cXcB, NULL + }, + { + CCHR('J'), CCHR('X'), cXcJ, NULL + }, + { + '(', ')', cXlp, NULL + }, + { + '0', '4', cX0, (KEYMAP *) & cX4map + }, + { + '=', '=', cXeq, NULL + }, + { + '^', 'u', cXcar, NULL + } + } +}; + +static PF metacG[] = { + ctrlg /* ^G */ +}; + +static PF metacV[] = { + pagenext /* ^V */ +}; + +static PF metaspex[] = { + justone, /* space */ + shellcommand /* ! */ +}; + +static PF metapct[] = { + queryrepl /* % */ +}; + +static PF metami[] = { + poptag, /* * */ + rescan, /* + */ + rescan, /* , */ + negative_argument, /* - */ + findtag, /* . */ + rescan, /* / */ + digit_argument, /* 0 */ + digit_argument, /* 1 */ + digit_argument, /* 2 */ + digit_argument, /* 3 */ + digit_argument, /* 4 */ + digit_argument, /* 5 */ + digit_argument, /* 6 */ + digit_argument, /* 7 */ + digit_argument, /* 8 */ + digit_argument, /* 9 */ + rescan, /* : */ + rescan, /* ; */ + gotobob, /* < */ + rescan, /* = */ + gotoeob /* > */ +}; + +static PF metasqf[] = { + NULL, /* [ */ + delwhite, /* \ */ + rescan, /* ] */ + joinline, /* ^ */ + rescan, /* _ */ + rescan, /* ` */ + rescan, /* a */ + backword, /* b */ + capword, /* c */ + delfword, /* d */ + rescan, /* e */ + forwword, /* f */ + rescan, /* g */ + markpara /* h */ +}; + +static PF metal[] = { + lowerword, /* l */ + backtoindent, /* m */ + rescan, /* n */ + rescan, /* o */ + rescan, /* p */ + fillpara, /* q */ + backsearch, /* r */ + forwsearch, /* s */ + transposeword, /* t */ + upperword, /* u */ + backpage, /* v */ + copyregion, /* w */ + extend, /* x */ + rescan, /* y */ + rescan, /* z */ + gotobop, /* { */ + piperegion, /* | */ + gotoeop /* } */ +}; + +static PF metasqlZ[] = { + rescan /* Z */ +}; + +static PF metatilde[] = { + notmodified, /* ~ */ + delbword /* DEL */ +}; + +struct KEYMAPE (1) metasqlmap = { + 1, + 1, + rescan, + { + { + 'Z', 'Z', metasqlZ, NULL + } + } +}; + +struct KEYMAPE (8) metamap = { + 8, + 8, + rescan, + { + { + CCHR('G'), CCHR('G'), metacG, NULL + }, + { + CCHR('V'), CCHR('V'), metacV, NULL + }, + { + ' ', '!', metaspex, NULL + }, + { + '%', '%', metapct, NULL + }, + { + '*', '>', metami, NULL + }, + { + '[', 'h', metasqf, (KEYMAP *) &metasqlmap + }, + { + 'l', '}', metal, NULL + }, + { + '~', CCHR('?'), metatilde, NULL + } + } +}; + +static PF fund_at[] = { + setmark, /* ^@ */ + gotobol, /* ^A */ + backchar, /* ^B */ + NULL, /* ^C */ + forwdel, /* ^D */ + gotoeol, /* ^E */ + forwchar, /* ^F */ + ctrlg, /* ^G */ +}; + +static PF fund_h[] = { + NULL, /* ^H */ +}; + + +/* ^I is selfinsert */ +static PF fund_CJ[] = { + lfindent, /* ^J */ + killline, /* ^K */ + reposition, /* ^L */ + enewline, /* ^M */ + forwline, /* ^N */ + openline, /* ^O */ + backline, /* ^P */ + quote, /* ^Q */ + backisearch, /* ^R */ + forwisearch, /* ^S */ + twiddle, /* ^T */ + universal_argument, /* ^U */ + forwpage, /* ^V */ + killregion, /* ^W */ + NULL, /* ^X */ + yank, /* ^Y */ + spawncli /* ^Z */ +}; + +static PF fund_esc[] = { + NULL, /* esc */ + rescan, /* ^\ selfinsert is default on fundamental */ + rescan, /* ^] */ + rescan, /* ^^ */ + undo /* ^_ */ +}; + +static PF fund_del[] = { + backdel /* DEL */ +}; + +static PF fund_cb[] = { + showmatch /* ) ] } */ +}; + +static struct KEYMAPE (8) fundmap = { + 8, + 8, + selfinsert, + { + { + CCHR('@'), CCHR('G'), fund_at, (KEYMAP *) & ccmap + }, + { + CCHR('H'), CCHR('H'), fund_h, (KEYMAP *) & helpmap + }, + { + CCHR('J'), CCHR('Z'), fund_CJ, (KEYMAP *) & cXmap + }, + { + CCHR('['), CCHR('_'), fund_esc, (KEYMAP *) & metamap + }, + { + ')', ')', fund_cb, NULL + }, + { + ']', ']', fund_cb, NULL + }, + { + '}', '}', fund_cb, NULL + }, + { + CCHR('?'), CCHR('?'), fund_del, NULL + }, + } +}; + +static PF fill_sp[] = { + fillword /* ' ' */ +}; + +static struct KEYMAPE (1) fillmap = { + 1, + 1, + rescan, + { + { ' ', ' ', fill_sp, NULL } + } +}; + +static PF indent_lf[] = { + enewline, /* ^J */ + rescan, /* ^K */ + rescan, /* ^L */ + lfindent /* ^M */ +}; + +static struct KEYMAPE (1) indntmap = { + 1, + 1, + rescan, + { + { + CCHR('J'), CCHR('M'), indent_lf, NULL + } + } +}; + +#ifdef NOTAB +static PF notab_tab[] = { + space_to_tabstop /* ^I */ +}; + +static struct KEYMAPE (1) notabmap = { + 1, + 1, + rescan, + { + { + CCHR('I'), CCHR('I'), notab_tab, NULL + } + } +}; +#endif /* NOTAB */ + +static struct KEYMAPE (1) overwmap = { + 0, + 1, /* 1 to avoid 0 sized array */ + rescan, + { + /* unused dummy entry for VMS C */ + { + (KCHAR)0, (KCHAR)0, NULL, NULL + } + } +}; + + +/* + * The basic (root) keyboard map + */ +struct maps_s fundamental_mode = { (KEYMAP *)&fundmap, "fundamental" }; + +/* + * give names to the maps, for use by help etc. If the map is to be bindable, + * it must also be listed in the function name table below with the same + * name. Maps created dynamically currently don't get added here, thus are + * unnamed. Modes are just named keymaps with functions to add/subtract them + * from a buffer's list of modes. If you change a mode name, change it in + * modes.c also. + */ + +static struct maps_s map_table[] = { + {(KEYMAP *) &fillmap, "fill",}, + {(KEYMAP *) &indntmap, "indent",}, +#ifdef NOTAB + {(KEYMAP *) ¬abmap, "notab",}, +#endif /* NOTAB */ + {(KEYMAP *) &overwmap, "overwrite",}, + {(KEYMAP *) &metamap, "esc prefix",}, + {(KEYMAP *) &cXmap, "c-x prefix",}, + {(KEYMAP *) &cX4map, "c-x 4 prefix",}, + {(KEYMAP *) &helpmap, "help",}, + {NULL, NULL} +}; + +struct maps_s *maps; + +void +maps_init(void) +{ + int i; + struct maps_s *mp; + + maps = &fundamental_mode; + for (i = 0; map_table[i].p_name != NULL; i++) { + mp = &map_table[i]; + mp->p_next = maps; + maps = mp; + } +} + +/* + * Insert a new (named) keymap at the head of the keymap list. + */ +int +maps_add(KEYMAP *map, const char *name) +{ + struct maps_s *mp; + + if ((mp = malloc(sizeof(*mp))) == NULL) + return (FALSE); + + mp->p_name = name; + mp->p_map = map; + mp->p_next = maps; + maps = mp; + + return (TRUE); +} + +struct maps_s * +name_mode(const char *name) +{ + struct maps_s *mp; + + for (mp = maps; mp != NULL; mp = mp->p_next) + if (strcmp(mp->p_name, name) == 0) + return (mp); + return (NULL); +} + +KEYMAP * +name_map(const char *name) +{ + struct maps_s *mp; + + return ((mp = name_mode(name)) == NULL ? NULL : mp->p_map); +} Index: contrib/mg/line.c =================================================================== --- /dev/null +++ contrib/mg/line.c @@ -0,0 +1,624 @@ +/* $OpenBSD: line.c,v 1.63 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Text line handling. + * + * The functions in this file are a general set of line management + * utilities. They are the only routines that touch the text. They + * also touch the buffer and window structures to make sure that the + * necessary updating gets done. + * + * Note that this code only updates the dot and mark values in the window + * list. Since all the code acts on the current window, the buffer that + * we are editing must be displayed, which means that "b_nwnd" is non-zero, + * which means that the dot and mark values in the buffer headers are + * nonsense. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +int casereplace = TRUE; + +/* + * Preserve the case of the replaced string. + */ +int +setcasereplace(int f, int n) +{ + if (f & FFARG) + casereplace = n > 0; + else + casereplace = !casereplace; + ewprintf("Case-replace is %sabled", casereplace ? "en" : "dis"); + return (TRUE); +} + +/* + * Allocate a new line of size `used'. lrealloc() can be called if the line + * ever needs to grow beyond that. + */ +struct line * +lalloc(int used) +{ + struct line *lp; + + if ((lp = malloc(sizeof(*lp))) == NULL) + return (NULL); + lp->l_text = NULL; + lp->l_size = 0; + lp->l_used = used; /* XXX */ + if (lrealloc(lp, used) == FALSE) { + free(lp); + return (NULL); + } + return (lp); +} + +int +lrealloc(struct line *lp, int newsize) +{ + char *tmp; + + if (lp->l_size < newsize) { + if ((tmp = realloc(lp->l_text, newsize)) == NULL) + return (FALSE); + lp->l_text = tmp; + lp->l_size = newsize; + } + return (TRUE); +} + +/* + * Delete line "lp". Fix all of the links that might point to it (they are + * moved to offset 0 of the next line. Unlink the line from whatever buffer + * it might be in, and release the memory. The buffers are updated too; the + * magic conditions described in the above comments don't hold here. + */ +void +lfree(struct line *lp) +{ + struct buffer *bp; + struct mgwin *wp; + + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_linep == lp) + wp->w_linep = lp->l_fp; + if (wp->w_dotp == lp) { + wp->w_dotp = lp->l_fp; + wp->w_doto = 0; + } + if (wp->w_markp == lp) { + wp->w_markp = lp->l_fp; + wp->w_marko = 0; + } + } + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + if (bp->b_nwnd == 0) { + if (bp->b_dotp == lp) { + bp->b_dotp = lp->l_fp; + bp->b_doto = 0; + } + if (bp->b_markp == lp) { + bp->b_markp = lp->l_fp; + bp->b_marko = 0; + } + } + } + lp->l_bp->l_fp = lp->l_fp; + lp->l_fp->l_bp = lp->l_bp; + free(lp->l_text); + free(lp); +} + +/* + * This routine is called when a character changes in place in the current + * buffer. It updates all of the required flags in the buffer and window + * system. The flag used is passed as an argument; if the buffer is being + * displayed in more than 1 window we change EDIT to HARD. Set MODE if the + * mode line needs to be updated (the "*" has to be set). + */ +void +lchange(int flag) +{ + struct mgwin *wp; + + /* update mode lines if this is the first change. */ + if ((curbp->b_flag & BFCHG) == 0) { + flag |= WFMODE; + curbp->b_flag |= BFCHG; + } + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == curbp) { + wp->w_rflag |= flag; + if (wp != curwp) + wp->w_rflag |= WFFULL; + } + } +} + +/* + * Insert "n" copies of the character "c" at the current location of dot. + * In the easy case all that happens is the text is stored in the line. + * In the hard case, the line has to be reallocated. When the window list + * is updated, take special care; I screwed it up once. You always update + * dot in the current window. You update mark and a dot in another window + * if it is greater than the place where you did the insert. Return TRUE + * if all is well, and FALSE on errors. + */ +int +linsert(int n, int c) +{ + struct line *lp1; + struct mgwin *wp; + RSIZE i; + int doto; + int s; + + if (!n) + return (TRUE); + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read only"); + return (FALSE); + } + + lchange(WFEDIT); + + /* current line */ + lp1 = curwp->w_dotp; + + /* special case for the end */ + if (lp1 == curbp->b_headp) { + struct line *lp2, *lp3; + + /* now should only happen in empty buffer */ + if (curwp->w_doto != 0) { + dobeep(); + ewprintf("bug: linsert"); + return (FALSE); + } + /* allocate a new line */ + if ((lp2 = lalloc(n)) == NULL) + return (FALSE); + /* previous line */ + lp3 = lp1->l_bp; + /* link in */ + lp3->l_fp = lp2; + lp2->l_fp = lp1; + lp1->l_bp = lp2; + lp2->l_bp = lp3; + for (i = 0; i < n; ++i) + lp2->l_text[i] = c; + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_linep == lp1) + wp->w_linep = lp2; + if (wp->w_dotp == lp1) + wp->w_dotp = lp2; + if (wp->w_markp == lp1) + wp->w_markp = lp2; + } + undo_add_insert(lp2, 0, n); + curwp->w_doto = n; + return (TRUE); + } + /* save for later */ + doto = curwp->w_doto; + + if ((lp1->l_used + n) > lp1->l_size) { + if (lrealloc(lp1, lp1->l_used + n) == FALSE) + return (FALSE); + } + lp1->l_used += n; + if (lp1->l_used != n) + memmove(&lp1->l_text[doto + n], &lp1->l_text[doto], + lp1->l_used - n - doto); + + /* Add the characters */ + for (i = 0; i < n; ++i) + lp1->l_text[doto + i] = c; + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_dotp == lp1) { + if (wp == curwp || wp->w_doto > doto) + wp->w_doto += n; + } + if (wp->w_markp == lp1) { + if (wp->w_marko > doto) + wp->w_marko += n; + } + } + undo_add_insert(curwp->w_dotp, doto, n); + return (TRUE); +} + +/* + * Do the work of inserting a newline at the given line/offset. + * If mark is on the current line, we may have to move the markline + * to keep line numbers in sync. + * lnewline_at assumes the current buffer is writable. Checking for + * this fact should be done by the caller. + */ +int +lnewline_at(struct line *lp1, int doto) +{ + struct line *lp2; + struct mgwin *wp; + int nlen, tcurwpdotline; + + lchange(WFFULL); + + curwp->w_bufp->b_lines++; + /* Check if mark is past dot (even on current line) */ + if (curwp->w_markline > curwp->w_dotline || + (curwp->w_dotline == curwp->w_markline && + curwp->w_marko >= doto)) + curwp->w_markline++; + + tcurwpdotline = curwp->w_dotline; + + /* If start of line, allocate a new line instead of copying */ + if (doto == 0) { + /* new first part */ + if ((lp2 = lalloc(0)) == NULL) + return (FALSE); + lp2->l_bp = lp1->l_bp; + lp1->l_bp->l_fp = lp2; + lp2->l_fp = lp1; + lp1->l_bp = lp2; + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_linep == lp1) + wp->w_linep = lp2; + if (wp->w_dotline >= tcurwpdotline && + wp->w_bufp == curwp->w_bufp) + wp->w_dotline++; + } + undo_add_boundary(FFRAND, 1); + undo_add_insert(lp2, 0, 1); + undo_add_boundary(FFRAND, 1); + return (TRUE); + } + + /* length of new part */ + nlen = llength(lp1) - doto; + + /* new second half line */ + if ((lp2 = lalloc(nlen)) == NULL) + return (FALSE); + if (nlen != 0) + bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen); + lp1->l_used = doto; + lp2->l_bp = lp1; + lp2->l_fp = lp1->l_fp; + lp1->l_fp = lp2; + lp2->l_fp->l_bp = lp2; + /* Windows */ + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_dotp == lp1 && wp->w_doto >= doto) { + wp->w_dotp = lp2; + wp->w_doto -= doto; + wp->w_dotline++; + } else if (wp->w_dotline > tcurwpdotline && + wp->w_bufp == curwp->w_bufp) + wp->w_dotline++; + if (wp->w_markp == lp1 && wp->w_marko >= doto) { + wp->w_markp = lp2; + wp->w_marko -= doto; + } + } + undo_add_boundary(FFRAND, 1); + undo_add_insert(lp1, llength(lp1), 1); + undo_add_boundary(FFRAND, 1); + return (TRUE); +} + +/* + * Insert a newline into the buffer at the current location of dot in the + * current window. + */ +int +lnewline(void) +{ + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read only"); + return (FALSE); + } + return (lnewline_at(curwp->w_dotp, curwp->w_doto)); +} + +/* + * This function deletes "n" bytes, starting at dot. (actually, n+1, as the + * newline is included) It understands how to deal with end of lines, etc. + * It returns TRUE if all of the characters were deleted, and FALSE if + * they were not (because dot ran into the end of the buffer). + * The "kflag" indicates either no insertion, or direction of insertion + * into the kill buffer. + */ +int +ldelete(RSIZE n, int kflag) +{ + struct line *dotp; + RSIZE chunk; + struct mgwin *wp; + int doto; + char *cp1, *cp2; + size_t len; + char *sv = NULL; + int end; + int s; + int rval = FALSE; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read only"); + goto out; + } + len = n; + if ((sv = calloc(1, len + 1)) == NULL) + goto out; + end = 0; + + undo_add_delete(curwp->w_dotp, curwp->w_doto, n, (kflag & KREG)); + + while (n != 0) { + dotp = curwp->w_dotp; + doto = curwp->w_doto; + /* Hit the end of the buffer */ + if (dotp == curbp->b_headp) + goto out; + /* Size of the chunk */ + chunk = dotp->l_used - doto; + + if (chunk > n) + chunk = n; + /* End of line, merge */ + if (chunk == 0) { + if (dotp == blastlp(curbp)) + goto out; + lchange(WFFULL); + if (ldelnewline() == FALSE) + goto out; + end = strlcat(sv, curbp->b_nlchr, len + 1); + --n; + continue; + } + lchange(WFEDIT); + /* Scrunch text */ + cp1 = &dotp->l_text[doto]; + memcpy(&sv[end], cp1, chunk); + end += chunk; + sv[end] = '\0'; + for (cp2 = cp1 + chunk; cp2 < &dotp->l_text[dotp->l_used]; + cp2++) + *cp1++ = *cp2; + dotp->l_used -= (int)chunk; + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_dotp == dotp && wp->w_doto >= doto) { + wp->w_doto -= chunk; + if (wp->w_doto < doto) + wp->w_doto = doto; + } + if (wp->w_markp == dotp && wp->w_marko >= doto) { + wp->w_marko -= chunk; + if (wp->w_marko < doto) + wp->w_marko = doto; + } + } + n -= chunk; + } + if (kchunk(sv, (RSIZE)len, kflag) != TRUE) + goto out; + rval = TRUE; +out: + free(sv); + return (rval); +} + +/* + * Delete a newline and join the current line with the next line. If the next + * line is the magic header line always return TRUE; merging the last line + * with the header line can be thought of as always being a successful + * operation. Even if nothing is done, this makes the kill buffer work + * "right". If the mark is past the dot (actually, markline > dotline), + * decrease the markline accordingly to keep line numbers in sync. + * Easy cases can be done by shuffling data around. Hard cases + * require that lines be moved about in memory. Return FALSE on error and + * TRUE if all looks ok. We do not update w_dotline here, as deletes are done + * after moves. + */ +int +ldelnewline(void) +{ + struct line *lp1, *lp2, *lp3; + struct mgwin *wp; + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read only"); + return (FALSE); + } + + lp1 = curwp->w_dotp; + lp2 = lp1->l_fp; + /* at the end of the buffer */ + if (lp2 == curbp->b_headp) + return (TRUE); + /* Keep line counts in sync */ + curwp->w_bufp->b_lines--; + if (curwp->w_markline > curwp->w_dotline) + curwp->w_markline--; + if (lp2->l_used <= lp1->l_size - lp1->l_used) { + bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], lp2->l_used); + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_linep == lp2) + wp->w_linep = lp1; + if (wp->w_dotp == lp2) { + wp->w_dotp = lp1; + wp->w_doto += lp1->l_used; + } + if (wp->w_markp == lp2) { + wp->w_markp = lp1; + wp->w_marko += lp1->l_used; + } + } + lp1->l_used += lp2->l_used; + lp1->l_fp = lp2->l_fp; + lp2->l_fp->l_bp = lp1; + free(lp2); + return (TRUE); + } + if ((lp3 = lalloc(lp1->l_used + lp2->l_used)) == NULL) + return (FALSE); + bcopy(&lp1->l_text[0], &lp3->l_text[0], lp1->l_used); + bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], lp2->l_used); + lp1->l_bp->l_fp = lp3; + lp3->l_fp = lp2->l_fp; + lp2->l_fp->l_bp = lp3; + lp3->l_bp = lp1->l_bp; + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_linep == lp1 || wp->w_linep == lp2) + wp->w_linep = lp3; + if (wp->w_dotp == lp1) + wp->w_dotp = lp3; + else if (wp->w_dotp == lp2) { + wp->w_dotp = lp3; + wp->w_doto += lp1->l_used; + } + if (wp->w_markp == lp1) + wp->w_markp = lp3; + else if (wp->w_markp == lp2) { + wp->w_markp = lp3; + wp->w_marko += lp1->l_used; + } + } + free(lp1); + free(lp2); + return (TRUE); +} + +/* + * Replace plen characters before dot with argument string. Control-J + * characters in st are interpreted as newlines. There is a casehack + * disable flag (normally it likes to match case of replacement to what + * was there). + */ +int +lreplace(RSIZE plen, char *st) +{ + RSIZE rlen; /* replacement length */ + struct line *lp; + RSIZE n; + int s, doto, is_query_capitalised = 0, is_query_allcaps = 0; + int is_replace_alllower = 0; + char *repl = NULL; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read only"); + return (FALSE); + } + + if ((repl = strdup(st)) == NULL) { + dobeep(); + ewprintf("out of memory"); + return (FALSE); + } + rlen = strlen(repl); + + undo_boundary_enable(FFRAND, 0); + (void)backchar(FFARG | FFRAND, (int)plen); + + if (casereplace != TRUE) + goto done; + + lp = curwp->w_dotp; + if (ltext(lp) == NULL) + goto done; + doto = curwp->w_doto; + n = plen; + + is_query_capitalised = isupper((unsigned char)lgetc(lp, doto)); + + if (is_query_capitalised) { + for (n = 0, is_query_allcaps = 1; n < plen && is_query_allcaps; + n++) { + is_query_allcaps = !isalpha((unsigned char)lgetc(lp, + doto)) || isupper((unsigned char)lgetc(lp, doto)); + doto++; + if (doto == llength(lp)) { + doto = 0; + lp = lforw(lp); + n++; /* \n is implicit in the buffer */ + } + } + } + + for (n = 0, is_replace_alllower = 1; n < rlen && is_replace_alllower; + n++) + is_replace_alllower = !isupper((unsigned char)repl[n]); + + if (is_replace_alllower) { + if (is_query_allcaps) { + for (n = 0; n < rlen; n++) + repl[n] = toupper((unsigned char)repl[n]); + } else if (is_query_capitalised) { + repl[0] = toupper((unsigned char)repl[0]); + } + } + + done: + (void)ldelete(plen, KNONE); + region_put_data(repl, rlen); + lchange(WFFULL); + + undo_boundary_enable(FFRAND, 1); + + free(repl); + return (TRUE); +} + +/* + * Allocate and return the supplied line as a C string + */ +char * +linetostr(const struct line *ln) +{ + int len; + char *line; + + len = llength(ln); + if (len == INT_MAX) /* (len + 1) overflow */ + return (NULL); + + if ((line = malloc(len + 1)) == NULL) + return (NULL); + + (void)memcpy(line, ltext(ln), len); + line[len] = '\0'; + + return (line); +} Index: contrib/mg/log.h =================================================================== --- /dev/null +++ contrib/mg/log.h @@ -0,0 +1,33 @@ +/* $OpenBSD: log.h,v 1.6 2021/03/02 13:06:50 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Specifically for mg logging functionality. + * + */ +int mglog(PF, void *); +int mgloginit(void); +int mglog_execbuf( const char* const, + const char* const, + const char* const, + const char* const, + const int, + const int, + const char* const, + const char* const, + const char* const + ); + +int mglog_isvar( const char* const, + const char* const, + const int + ); +int mglog_misc( const char *, ...); + +extern const char *mglogpath_lines; +extern const char *mglogpath_undo; +extern const char *mglogpath_window; +extern const char *mglogpath_key; +extern const char *mglogpath_interpreter; +extern const char *mglogpath_misc; Index: contrib/mg/log.c =================================================================== --- /dev/null +++ contrib/mg/log.c @@ -0,0 +1,408 @@ +/* $OpenBSD: log.c,v 1.12 2021/03/02 13:06:50 lum Exp $ */ + +/* + * This file is in the public domain. + * + * Author: Mark Lumsden + * + */ + +/* + * Record a history of an mg session for temporal debugging. + * Sometimes pressing a key will set the scene for a bug only visible + * dozens of keystrokes later. gdb has its limitations in this scenario. + * + * Note this file is not compiled into mg by default, you will need to + * amend the 'Makefile' for that to happen. Because of this, the code + * is subject to bit-rot. However, I know myself and others have + * written similar functionally often enough, that recording the below + * in a code repository could aid the developement efforts of mg, even + * if it requires a bit of effort to get working. The current code is + * written in the spirit of debugging (quickly and perhaps not ideal, + * but it does what is required well enough). Should debugging become + * more formalised within mg, then I would expect that to change. + * + * If you open a file with long lines to run through this debugging + * code, you may run into problems with the 1st fprintf statement in + * in the mglog_lines() function. mg sometimes segvs at a strlen call + * in fprintf - possibly something to do with the format string? + * "%s%p b^%p f.%p %d %d\t%c|%s\n" + * When I get time I will look into it. But since my debugging + * generally revolves around a file like: + * + * abc + * def + * ghk + * + * I don't experience this bug. Just note it for future investigation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "key.h" +#include "kbd.h" +#include "funmap.h" +#include "chrdef.h" + +#include "log.h" + +static char *mglogfiles_create(FILE **, char *); +static int mglog_lines(PF); +static int mglog_undo(void); +static int mglog_window(void); +static int mglog_key(KEYMAP *map); + +const char *mglogdir; +const char *mglogpath_lines; +const char *mglogpath_undo; +const char *mglogpath_window; +const char *mglogpath_key; +const char *mglogpath_interpreter; +const char *mglogpath_misc; +int mgloglevel; + +FILE *fd_lines; +FILE *fd_undo; +FILE *fd_window; +FILE *fd_key; +FILE *fd_interpreter; +FILE *fd_misc; + +int +mglog(PF funct, void *map) +{ + if(!mglog_lines(funct)) + ewprintf("Problem logging lines"); + if(!mglog_undo()) + ewprintf("Problem logging undo"); + if(!mglog_window()) + ewprintf("Problem logging window"); + if(!mglog_key(map)) + ewprintf("Problem logging key"); + + return (TRUE); +} + + +static int +mglog_key(KEYMAP *map) +{ + PF *pfp; + + if (ISWORD(*key.k_chars)) { + fprintf(fd_key, "k_count:%d k_chars:%hd\tchr:%c\t", key.k_count, + *key.k_chars, CHARMASK(*key.k_chars)); + } else { + fprintf(fd_key, "k_count:%d k_chars:%hd\t\t", key.k_count, + *key.k_chars); + } + fprintf(fd_key, "map:%p %d %d %p %hd %hd\n", + map, + map->map_num, + map->map_max, + map->map_default, + map->map_element->k_base, + map->map_element->k_num + ); + for (pfp = map->map_element->k_funcp; *pfp != NULL; pfp++) + fprintf(fd_key, "%s ", function_name(*pfp)); + + fprintf(fd_key, "\n\n"); + fflush(fd_key); + return (TRUE); +} + +static int +mglog_window(void) +{ + struct mgwin *wp; + int i; + + for (wp = wheadp, i = 0; wp != NULL; wp = wp->w_wndp, ++i) { + fprintf(fd_window, + "%d wh%p wlst%p wbfp%p wlp%p wdtp%p wmkp%p wdto%d wmko%d" \ + " wtpr%d wntr%d wfrm%d wrfl%c wflg%c wwrl%p wdtl%d" \ + " wmkl%d\n", + i, + wp, + &wp->w_list, + wp->w_bufp, + wp->w_linep, + wp->w_dotp, + wp->w_markp, + wp->w_doto, + wp->w_marko, + wp->w_toprow, + wp->w_ntrows, + wp->w_frame, + wp->w_rflag, + wp->w_flag, + wp->w_wrapline, + wp->w_dotline, + wp->w_markline + ); + } + fflush(fd_window); + return (TRUE); +} + +static int +mglog_undo(void) +{ + struct undo_rec *rec; + char buf[4096], tmp[1024]; + int num; + char *jptr; + + jptr = "^J"; /* :) */ + /* + * From undo_dump() + */ + num = 0; + TAILQ_FOREACH(rec, &curbp->b_undo, next) { + num++; + fprintf(fd_undo, "%d:\t %s at %d ", num, + (rec->type == DELETE) ? "DELETE": + (rec->type == DELREG) ? "DELREGION": + (rec->type == INSERT) ? "INSERT": + (rec->type == BOUNDARY) ? "----" : + (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN", + rec->pos + ); + if (rec->content) { + (void)strlcat(buf, "\"", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%.*s", + *rec->content == '\n' ? 2 : rec->region.r_size, + *rec->content == '\n' ? jptr : rec->content); + (void)strlcat(buf, tmp, sizeof(buf)); + (void)strlcat(buf, "\"", sizeof(buf)); + } + snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size); + if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) { + dobeep(); + ewprintf("Undo record too large. Aborted."); + return (FALSE); + } + fprintf(fd_undo, "%s\n", buf); + tmp[0] = buf[0] = '\0'; + } + fprintf(fd_undo, "\t [end-of-undo]\n\n"); + fflush(fd_undo); + + return (TRUE); +} + +static int +mglog_lines(PF funct) +{ + struct line *lp; + char *curline, *tmp, o; + int i; + + i = 0; + + fprintf(fd_lines, "%s\n", function_name(funct)); + lp = bfirstlp(curbp); + + for(;;) { + i++; + curline = " "; + o = ' '; + if (i == curwp->w_dotline) { + curline = ">"; + if (lp->l_used > 0 && curwp->w_doto < lp->l_used) + o = lp->l_text[curwp->w_doto]; + else + o = '-'; + } + if (lp->l_size == 0) + tmp = " "; + else + tmp = lp->l_text; + + /* segv on fprintf below with long lines */ + fprintf(fd_lines, "%s%p b^%p f.%p %d %d\t%c|%s\n", curline, + lp, lp->l_bp, lp->l_fp, + lp->l_size, lp->l_used, o, tmp); + + lp = lforw(lp); + if (lp == curbp->b_headp) { + fprintf(fd_lines, " %p b^%p f.%p [bhead]\n(EOB)\n", + lp, lp->l_bp, lp->l_fp); + + fprintf(fd_lines, "lines:raw:%d buf:%d wdot:%d\n\n", + i, curbp->b_lines, curwp->w_dotline); + + break; + } + } + fflush(fd_lines); + + return (TRUE); +} + +/* + * See what the eval variable code is up to. + */ +int +mglog_isvar( + const char* const argbuf, + const char* const argp, + const int sizof +) +{ + + fprintf(fd_interpreter, " argbuf:%s,argp:%s,sizof:%d<\n", + argbuf, + argp, + sizof); + + fflush(fd_interpreter); + return (TRUE); +} + +/* + * See what the eval line code is up to. + */ +int +mglog_execbuf( + const char* const pre, + const char* const excbuf, + const char* const argbuf, + const char* const argp, + const int last, + const int inlist, + const char* const cmdp, + const char* const p, + const char* const contbuf +) +{ + fprintf(fd_interpreter, "%sexcbuf:%s,argbuf:%s,argp:%s,last:%d,inlist:%d,"\ + "cmdp:%s,p:%s,contbuf:%s<\n", + pre, + excbuf, + argbuf, + argp, + last, + inlist, + cmdp, + p, + contbuf + ); + fflush(fd_interpreter); + return (TRUE); +} + +/* + * Misc. logging for various subsystems + */ +int +mglog_misc( + const char *fmt, + ... +) +{ + va_list ap; + int rc; + + va_start(ap, fmt); + rc = vfprintf(fd_misc, fmt, ap); + va_end(ap); + fflush(fd_misc); + + if (rc < 0) + return (FALSE); + + return (TRUE); +} + + + +/* + * Make sure logging to log files can happen. + */ +int +mgloginit(void) +{ + struct stat sb; + mode_t dir_mode, f_mode, oumask; + char *mglogfile_lines, *mglogfile_undo, *mglogfile_window; + char *mglogfile_key, *mglogfile_interpreter, *mglogfile_misc; + + mglogdir = "./log/"; + mglogfile_lines = "line.log"; + mglogfile_undo = "undo.log"; + mglogfile_window = "window.log"; + mglogfile_key = "key.log"; + mglogfile_interpreter = "interpreter.log"; + mglogfile_misc = "misc.log"; + + /* + * Change mgloglevel for desired level of logging. + * log.h has relevant level info. + */ + mgloglevel = 1; + + oumask = umask(0); + f_mode = 0777& ~oumask; + dir_mode = f_mode | S_IWUSR | S_IXUSR; + + if(stat(mglogdir, &sb)) { + if (mkdir(mglogdir, dir_mode) != 0) + return (FALSE); + if (chmod(mglogdir, f_mode) == -1) + return (FALSE); + } + mglogpath_lines = mglogfiles_create(&fd_lines, mglogfile_lines); + if (mglogpath_lines == NULL) + return (FALSE); + mglogpath_undo = mglogfiles_create(&fd_undo, mglogfile_undo); + if (mglogpath_undo == NULL) + return (FALSE); + mglogpath_window = mglogfiles_create(&fd_window, mglogfile_window); + if (mglogpath_window == NULL) + return (FALSE); + mglogpath_key = mglogfiles_create(&fd_key, mglogfile_key); + if (mglogpath_key == NULL) + return (FALSE); + mglogpath_interpreter = mglogfiles_create(&fd_interpreter, + mglogfile_interpreter); + if (mglogpath_interpreter == NULL) + return (FALSE); + mglogpath_misc = mglogfiles_create(&fd_misc, mglogfile_misc); + if (mglogpath_misc == NULL) + return (FALSE); + + return (TRUE); +} + + +static char * +mglogfiles_create(FILE ** fd, char *mglogfile) +{ + char tmp[NFILEN], *tmp2; + + if (strlcpy(tmp, mglogdir, sizeof(tmp)) > + sizeof(tmp)) + return (NULL); + if (strlcat(tmp, mglogfile, sizeof(tmp)) > + sizeof(tmp)) + return (NULL); + if ((tmp2 = strndup(tmp, NFILEN)) == NULL) + return (NULL); + + if ((*fd = fopen(tmp2, "w")) == NULL) + return (NULL); + + return (tmp2); +} Index: contrib/mg/macro.h =================================================================== --- /dev/null +++ contrib/mg/macro.h @@ -0,0 +1,21 @@ +/* $OpenBSD: macro.h,v 1.7 2005/11/18 20:56:53 deraadt Exp $ */ + +/* This file is in the public domain. */ + +/* definitions for keyboard macros */ + +#define MAXMACRO 256 /* maximum functs in a macro */ + +extern int inmacro; +extern int macrodef; +extern int macrocount; + +union macrodef { + PF m_funct; + int m_count; /* for count-prefix */ +}; + +extern union macrodef macro[MAXMACRO]; + +extern struct line *maclhead; +extern struct line *maclcur; Index: contrib/mg/macro.c =================================================================== --- /dev/null +++ contrib/mg/macro.c @@ -0,0 +1,112 @@ +/* $OpenBSD: macro.c,v 1.16 2015/03/19 21:22:15 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * Keyboard macros. + */ + +#include +#include +#include +#include + +#include "def.h" +#include "key.h" +#include "macro.h" + +int inmacro = FALSE; /* Macro playback in progess */ +int macrodef = FALSE; /* Macro recording in progress */ +int macrocount = 0; + +struct line *maclhead = NULL; +struct line *maclcur; + +union macrodef macro[MAXMACRO]; + +/* ARGSUSED */ +int +definemacro(int f, int n) +{ + struct line *lp1, *lp2; + + macrocount = 0; + + if (macrodef) { + ewprintf("already defining macro"); + return (macrodef = FALSE); + } + + /* free lines allocated for string arguments */ + if (maclhead != NULL) { + for (lp1 = maclhead->l_fp; lp1 != maclhead; lp1 = lp2) { + lp2 = lp1->l_fp; + free(lp1); + } + free(lp1); + } + + if ((maclhead = lp1 = lalloc(0)) == NULL) + return (FALSE); + + ewprintf("Defining Keyboard Macro..."); + maclcur = lp1->l_fp = lp1->l_bp = lp1; + return (macrodef = TRUE); +} + +/* ARGSUSED */ +int +finishmacro(int f, int n) +{ + if (macrodef == TRUE) { + macrodef = FALSE; + ewprintf("End Keyboard Macro Definition"); + return (TRUE); + } + return (FALSE); +} + +/* ARGSUSED */ +int +executemacro(int f, int n) +{ + int i, j, flag, num; + PF funct; + + if (macrodef || + (macrocount >= MAXMACRO && macro[MAXMACRO - 1].m_funct + != finishmacro)) { + dobeep(); + ewprintf("Macro too long. Aborting."); + return (FALSE); + } + + if (macrocount == 0) + return (TRUE); + + inmacro = TRUE; + + for (i = n; i > 0; i--) { + maclcur = maclhead->l_fp; + flag = 0; + num = 1; + for (j = 0; j < macrocount - 1; j++) { + funct = macro[j].m_funct; + if (funct == universal_argument) { + flag = FFARG; + num = macro[++j].m_count; + continue; + } + if ((*funct)(flag, num) != TRUE) { + inmacro = FALSE; + return (FALSE); + } + lastflag = thisflag; + thisflag = 0; + flag = 0; + num = 1; + } + } + inmacro = FALSE; + return (TRUE); +} Index: contrib/mg/main.c =================================================================== --- /dev/null +++ contrib/mg/main.c @@ -0,0 +1,343 @@ +/* $OpenBSD: main.c,v 1.90 2021/05/03 12:18:43 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Mainline. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) || defined(__CYGWIN__) +#include +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +#include +#else +#include +#endif + +#include "def.h" +#include "kbd.h" +#include "funmap.h" +#include "macro.h" + +#ifdef MGLOG +#include "log.h" +#endif + +int thisflag; /* flags, this command */ +int lastflag; /* flags, last command */ +int curgoal; /* goal column */ +int startrow; /* row to start */ +int doaudiblebell; /* audible bell toggle */ +int dovisiblebell; /* visible bell toggle */ +int dblspace; /* sentence end #spaces */ +int allbro; /* all buffs read-only */ +int batch; /* for regress tests */ +struct buffer *curbp; /* current buffer */ +struct buffer *bheadp; /* BUFFER list head */ +struct mgwin *curwp; /* current window */ +struct mgwin *wheadp; /* MGWIN listhead */ +struct vhead varhead; /* Variable list head */ +char pat[NPAT]; /* pattern */ + +static void edinit(struct buffer *); +static void pty_init(void); +static __dead void usage(void); + +extern char *__progname; +extern void closetags(void); + +static __dead void +usage() +{ + fprintf(stderr, "usage: %s [-nR] [-b file] [-f mode] [-u file] " + "[+number] [file ...]\n", + __progname); + exit(1); +} + +int +main(int argc, char **argv) +{ + char *cp, *conffile = NULL, *init_fcn_name = NULL; + char *batchfile = NULL; + PF init_fcn = NULL; + int o, i, nfiles; + int nobackups = 0; + struct buffer *bp = NULL; + +#ifdef HAVE_PLEDGE + if (pledge("stdio rpath wpath cpath fattr chown getpw tty proc exec", + NULL) == -1) + err(1, "pledge"); +#endif + + while ((o = getopt(argc, argv, "nRb:f:u:")) != -1) + switch (o) { + case 'b': + batch = 1; + batchfile = optarg; + break; + case 'R': + allbro = 1; + break; + case 'n': + nobackups = 1; + break; + case 'f': + if (init_fcn_name != NULL) + errx(1, "cannot specify more than one " + "initial function"); + init_fcn_name = optarg; + break; + case 'u': + conffile = optarg; + break; + default: + usage(); + } + + if (batch && (conffile != NULL)) { + fprintf(stderr, "%s: -b and -u are mutually exclusive.\n", + __progname); + exit(1); + } + if (batch) { + pty_init(); + conffile = batchfile; + } + if (conffile != NULL && access(conffile, R_OK) != 0) { + fprintf(stderr, "%s: Problem with file: %s\n", __progname, + conffile); + exit(1); + } + + argc -= optind; + argv += optind; + + setlocale(LC_CTYPE, ""); + + maps_init(); /* Keymaps and modes. */ + funmap_init(); /* Functions. */ + +#ifdef MGLOG + if (!mgloginit()) + errx(1, "Unable to create logging environment."); +#endif + + /* + * This is where we initialize standalone extensions that should + * be loaded dynamically sometime in the future. + */ + { + extern void grep_init(void); + extern void cmode_init(void); + extern void dired_init(void); + + dired_init(); + grep_init(); + cmode_init(); + } + + if (init_fcn_name && + (init_fcn = name_function(init_fcn_name)) == NULL) + errx(1, "Unknown function `%s'", init_fcn_name); + + vtinit(); /* Virtual terminal. */ + dirinit(); /* Get current directory. */ + edinit(bp); /* Buffers, windows. */ + ttykeymapinit(); /* Symbols, bindings. */ + bellinit(); /* Audible and visible bell. */ + dblspace = 1; /* two spaces for sentence end. */ + + /* + * doing update() before reading files causes the error messages from + * the file I/O show up on the screen. (and also an extra display of + * the mode line if there are files specified on the command line.) + */ + update(CMODE); + + /* user startup file. */ + if ((cp = startupfile(NULL, conffile)) != NULL) + (void)load(cp); + + if (batch) + return (0); + + /* + * Now ensure any default buffer modes from the startup file are + * given to any files opened when parsing the startup file. + * Note *scratch* will also be updated. + */ + for (bp = bheadp; bp != NULL; bp = bp->b_bufp) { + bp->b_flag = defb_flag; + for (i = 0; i <= defb_nmodes; i++) { + bp->b_modes[i] = defb_modes[i]; + } + } + + /* Force FFOTHARG=1 so that this mode is enabled, not simply toggled */ + if (init_fcn) + init_fcn(FFOTHARG, 1); + + if (nobackups) + makebkfile(FFARG, 0); + + for (nfiles = 0, i = 0; i < argc; i++) { + if (argv[i][0] == '+' && strlen(argv[i]) >= 2) { + long long lval; + const char *errstr; + + lval = strtonum(&argv[i][1], INT_MIN, INT_MAX, &errstr); + if (argv[i][1] == '\0' || errstr != NULL) + goto notnum; + startrow = lval; + } else { +notnum: + cp = adjustname(argv[i], FALSE); + if (cp != NULL) { + if (nfiles == 1) + splitwind(0, 1); + + if (fisdir(cp) == TRUE) { + (void)do_dired(cp); + continue; + } + if ((curbp = findbuffer(cp)) == NULL) { + vttidy(); + errx(1, "Can't find current buffer!"); + } + (void)showbuffer(curbp, curwp, 0); + if (readin(cp) != TRUE) + killbuffer(curbp); + else { + /* Ensure enabled, not just toggled */ + if (init_fcn_name) + init_fcn(FFOTHARG, 1); + nfiles++; + } + if (allbro) + curbp->b_flag |= BFREADONLY; + } + } + } + + if (nfiles > 2) + listbuffers(0, 1); + + /* fake last flags */ + thisflag = 0; + for (;;) { + if (epresf == KCLEAR) + eerase(); + if (epresf == TRUE) + epresf = KCLEAR; + if (winch_flag) { + do_redraw(0, 0, TRUE); + winch_flag = 0; + } + update(CMODE); + lastflag = thisflag; + thisflag = 0; + + switch (doin()) { + case TRUE: + break; + case ABORT: + ewprintf("Quit"); + /* FALLTHRU */ + case FALSE: + default: + macrodef = FALSE; + } + } +} + +/* + * Initialize default buffer and window. Default buffer is called *scratch*. + */ +static void +edinit(struct buffer *bp) +{ + struct mgwin *wp; + + bheadp = NULL; + bp = bfind("*scratch*", TRUE); /* Text buffer. */ + if (bp == NULL) + panic("edinit"); + + wp = new_window(bp); + if (wp == NULL) + panic("edinit: Out of memory"); + + curbp = bp; /* Current buffer. */ + wheadp = wp; + curwp = wp; + wp->w_wndp = NULL; /* Initialize window. */ + wp->w_linep = wp->w_dotp = bp->b_headp; + wp->w_ntrows = nrow - 2; /* 2 = mode, echo. */ + wp->w_rflag = WFMODE | WFFULL; /* Full. */ +} + +/* + * Create pty for batch mode. + */ +static void +pty_init(void) +{ + struct winsize ws; + int master; + int slave; + + memset(&ws, 0, sizeof(ws)); + ws.ws_col = 80, + ws.ws_row = 24; + + openpty(&master, &slave, NULL, NULL, &ws); + login_tty(slave); + + return; +} + +/* + * Quit command. If an argument, always quit. Otherwise confirm if a buffer + * has been changed and not written out. Normally bound to "C-x C-c". + */ +/* ARGSUSED */ +int +quit(int f, int n) +{ + int s; + + if ((s = anycb(FALSE)) == ABORT) + return (ABORT); + if (s == FIOERR || s == UERROR) + return (FALSE); + if (s == FALSE + || eyesno("Modified buffers exist; really exit") == TRUE) { + vttidy(); + closetags(); + exit(0); + } + return (TRUE); +} + +/* + * User abort. Should be called by any input routine that sees a C-g to abort + * whatever C-g is aborting these days. Currently does nothing. + */ +/* ARGSUSED */ +int +ctrlg(int f, int n) +{ + return (ABORT); +} Index: contrib/mg/match.c =================================================================== --- /dev/null +++ contrib/mg/match.c @@ -0,0 +1,190 @@ +/* $OpenBSD: match.c,v 1.22 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Limited parenthesis matching routines + * + * The hacks in this file implement automatic matching * of (), [], {}, and + * other characters. It would be better to have a full-blown syntax table, + * but there's enough overhead in the editor as it is. + */ + +#include +#include +#include + +#include "def.h" +#include "key.h" + +static int balance(void); +static void displaymatch(struct line *, int); + +/* + * Balance table. When balance() encounters a character that is to be + * matched, it first searches this table for a balancing left-side character. + * If the character is not in the table, the character is balanced by itself. + */ +static struct balance { + char left, right; +} bal[] = { + { '(', ')' }, + { '[', ']' }, + { '{', '}' }, + { '<', '>' }, + { '\0', '\0' } +}; + +/* + * Hack to show matching paren. Self-insert character, then show matching + * character, if any. Bound to "blink-and-insert". Used in the mg startup + * file to amend the default cursor behaviour of a key press, e.g: + * global-set-key "%" blink-and-insert + */ +int +showmatch(int f, int n) +{ + int i, s; + + for (i = 0; i < n; i++) { + if ((s = selfinsert(FFRAND, 1)) != TRUE) + return (s); + /* unbalanced -- warn user */ + if (balance() != TRUE) + dobeep(); + } + return (TRUE); +} + +/* + * Search for and display a matching character. + * + * This routine does the real work of searching backward + * for a balancing character. If such a balancing character + * is found, it uses displaymatch() to display the match. + */ +static int +balance(void) +{ + struct line *clp; + int cbo; + int c, i, depth; + int rbal, lbal; + + rbal = key.k_chars[key.k_count - 1]; + + /* See if there is a matching character -- default to the same */ + lbal = rbal; + for (i = 0; bal[i].right != '\0'; i++) + if (bal[i].right == rbal) { + lbal = bal[i].left; + break; + } + + /* + * Move behind the inserted character. We are always guaranteed + * that there is at least one character on the line. + */ + clp = curwp->w_dotp; + cbo = curwp->w_doto - 1; + + /* init nesting depth */ + depth = 0; + + for (;;) { + if (cbo == 0) { + clp = lback(clp); /* beginning of line */ + if (clp == curbp->b_headp) + return (FALSE); + cbo = llength(clp) + 1; + } + if (--cbo == llength(clp)) + c = *curbp->b_nlchr; /* end of line */ + else + c = lgetc(clp, cbo); /* somewhere in middle */ + + /* + * Check for a matching character. If still in a nested + * level, pop out of it and continue search. This check + * is done before the nesting check so single-character + * matches will work too. + */ + if (c == lbal) { + if (depth == 0) { + displaymatch(clp, cbo); + return (TRUE); + } else + depth--; + } + /* Check for another level of nesting. */ + if (c == rbal) + depth++; + } + /* NOTREACHED */ +} + +/* + * Display matching character. Matching characters that are not in the + * current window are displayed in the echo line. If in the current window, + * move dot to the matching character, sit there a while, then move back. + */ +static void +displaymatch(struct line *clp, int cbo) +{ + struct line *tlp; + int tbo; + int cp; + int bufo; + int c; + int inwindow; + char buf[NLINE]; + + /* + * Figure out if matching char is in current window by + * searching from the top of the window to dot. + */ + inwindow = FALSE; + for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); + tlp = lforw(tlp)) + if (tlp == clp) + inwindow = TRUE; + + if (inwindow == TRUE) { + tlp = curwp->w_dotp; /* save current position */ + tbo = curwp->w_doto; + + curwp->w_dotp = clp; /* move to new position */ + curwp->w_doto = cbo; + curwp->w_rflag |= WFMOVE; + + update(CMODE); /* show match */ + ttwait(1000); /* wait for key or 1 second */ + + curwp->w_dotp = tlp; /* return to old position */ + curwp->w_doto = tbo; + curwp->w_rflag |= WFMOVE; + update(CMODE); + } else { + /* match is not in this window, so display line in echo area */ + bufo = 0; + for (cp = 0; cp < llength(clp); cp++) { + c = lgetc(clp, cp); + if (c != '\t' +#ifdef NOTAB + || (curbp->b_flag & BFNOTAB) +#endif + ) + if (ISCTRL(c)) { + buf[bufo++] = '^'; + buf[bufo++] = CCHR(c); + } else + buf[bufo++] = c; + else + do { + buf[bufo++] = ' '; + } while (bufo & 7); + } + buf[bufo++] = '\0'; + ewprintf("Matches %s", buf); + } +} Index: contrib/mg/mg.1 =================================================================== --- /dev/null +++ contrib/mg/mg.1 @@ -0,0 +1,1140 @@ +.\" $OpenBSD: mg.1,v 1.125 2021/05/02 14:13:17 lum Exp $ +.\" This file is in the public domain. +.\" +.Dd $Mdocdate: May 2 2021 $ +.Dt MG 1 +.Os +.Sh NAME +.Nm mg +.Nd emacs-like text editor +.Sh SYNOPSIS +.Nm mg +.Op Fl nR +.Op Fl b Ar file +.Op Fl f Ar mode +.Op Fl u Ar file +.Op + Ns Ar number +.Op Ar +.Sh DESCRIPTION +.Nm +is intended to be a small, fast, and portable editor for +people who can't (or don't want to) run emacs for one +reason or another, or are not familiar with the +.Xr vi 1 +editor. +It is compatible with emacs because there shouldn't +be any reason to learn more editor types than emacs or +.Xr vi 1 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It + Ns Ar number +Go to the line specified by number (do not insert +a space between the +.Sq + +sign and the number). +If a negative number is specified, the line number counts +backwards from the end of the file i.e. +-1 will be the last +line of the file, +-2 will be second last, and so on. +.It Fl b Ar file +Turn on batch mode and execute the +.Nm +commands found in the specified +.Ar file +and then terminate. +.It Fl f Ar mode +Run the mode command for all buffers created from +arguments on the command line, including the +scratch buffer and all files. +.It Fl n +Turn off backup file generation. +.It Fl R +Files specified on the command line will be opened read-only. +.It Fl u Ar file +Use +.Ar file +as the startup file, instead of the default +.Pa ~/.mg . +.El +.Sh WINDOWS AND BUFFERS +When a file is loaded into +.Nm , +it is stored in a +.Em buffer . +This buffer may be displayed on the screen in more than one window. +At present, windows may only be split horizontally, so each window is +delineated by a modeline at the bottom. +If changes are made to a buffer, it will be reflected in all open windows. +.Pp +If a file is changed outside +.Nm +and its buffer is about to be changed, +.Nm +prompts if the change should go ahead (y), not go ahead (n) or if the buffer +should be reverted (r) to the latest file on disk. +.Pp +If a buffer name begins and ends with an asterisk, the buffer is considered +throwaway; i.e. the user will not be prompted to save changes when +the buffer is killed. +.Sh POINT AND MARK +The current cursor location in +.Nm +is called the +.Em point +(or +.Em dot ) . +It is possible to define a window-specific region of text by setting a second +location, called the +.Em mark . +The +.Em region +is the text between point and mark inclusive. +Deleting the character at the mark position leaves +the mark at the point of deletion. +.Pp +Note: The point and mark are window-specific in +.Nm , +not buffer-specific, as in other emacs flavours. +.Sh BACKUP FILES +Backup files have a +.Sq ~ +character appended to the file name and +are created in the current working directory by default. +Whether to create backup files or not can be toggled with the +make-backup-files command. +The backup file location can either be in the current +working directory, or all backups can be moved to a +.Pa ~/.mg.d +directory where files retain their path name to retain uniqueness. +Use the backup-to-home-directory to alternate between these two locations. +Further, if any application creates backup files in +.Pa /tmp , +these can be left with the leave-tmpdir-backups command. +.Sh TAGS +.Nm +supports tag files created by +.Xr ctags 1 , +allowing the user to quickly locate various object definitions. +Note though that emacs uses etags, not ctags. +.Sh CSCOPE +.Nm +supports navigating source code using cscope. +However, +.Nm +requires cscope and cscope-indexer executables to be present in +.Ev PATH +for it to work. +.Sh DEFAULT KEY BINDINGS +Normal editing commands are very similar to GNU Emacs. +In the following examples, C-x means Control-x, and M-x means Meta-x, +where the Meta key may be either a special key on the keyboard +or the ALT key; otherwise ESC followed by the key X works as well. +.Pp +.Bl -tag -width xxxxxxxxxxxx -offset indent -compact +.It C-SPC +set-mark-command +.It C-a +beginning-of-line +.It C-b +backward-char +.It C-c s c +cscope-find-functions-calling-this-function +.It C-c s d +cscope-find-global-definition +.It C-c s e +cscope-find-egrep-pattern +.It C-c s f +cscope-find-this-file +.It C-c s i +cscope-find-files-including-file +.It C-c s n +cscope-next-symbol +.It C-c s p +cscope-prev-symbol +.It C-c s s +cscope-find-this-symbol +.It C-c s t +cscope-find-this-text-string +.It C-d +delete-char +.It C-e +end-of-line +.It C-f +forward-char +.It C-g +keyboard-quit +.It C-h C-h +help-help +.It C-h a +apropos +.It C-h b +describe-bindings +.It C-h c +describe-key-briefly +.It C-j +newline-and-indent +.It C-k +kill-line +.It C-l +recenter +.It RET +newline +.It C-n +next-line +.It C-o +open-line +.It C-p +previous-line +.It C-q +quoted-insert +.It C-r +isearch-backward +.It C-s +isearch-forward +.It C-t +transpose-chars +.It C-u +universal-argument +.It C-v +scroll-up +.It C-w +kill-region +.It C-x C-b +list-buffers +.It C-x C-c +save-buffers-kill-emacs +.It C-x C-f +find-file +.It C-x C-j +dired-jump +.It C-x C-g +keyboard-quit +.It C-x C-l +downcase-region +.It C-x C-o +delete-blank-lines +.It C-x C-q +toggle-read-only +.It C-x C-r +find-file-read-only +.It C-x C-s +save-buffer +.It C-x C-u +upcase-region +.It C-x C-v +find-alternate-file +.It C-x C-w +write-file +.It C-x C-x +exchange-point-and-mark +.It C-x ( +start-kbd-macro +.It C-x \&) +end-kbd-macro +.It C-x 0 +delete-window +.It C-x 1 +delete-other-windows +.It C-x 2 +split-window-vertically +.It C-x 4 C-f +find-file-other-window +.It C-x 4 C-g +keyboard-quit +.It C-x 4 b +switch-to-buffer-other-window +.It C-x 4 f +find-file-other-window +.It C-x = +what-cursor-position +.It C-x ^ +enlarge-window +.It C-x ` +next-error +.It C-x b +switch-to-buffer +.It C-x d +dired +.It C-x e +call-last-kbd-macro +.It C-x f +set-fill-column +.It C-x g +goto-line +.It C-x h +mark-whole-buffer +.It C-x i +insert-file +.It C-x k +kill-buffer +.It C-x n +other-window +.It C-x o +other-window +.It C-x p +previous-window +.It C-x s +save-some-buffers +.It C-x u +undo +.It C-y +yank +.It C-z +suspend-emacs +.It M-C-v +scroll-other-window +.It M-SPC +just-one-space +.It M-! +shell-command +.It M-. +find-tag +.It M-* +pop-tag-mark +.It M-% +query-replace +.It M-< +beginning-of-buffer +.It M-> +end-of-buffer +.It M-\e +delete-horizontal-space +.It M-^ +join-line +.It M-b +backward-word +.It M-c +capitalize-word +.It M-d +kill-word +.It M-f +forward-word +.It M-h +mark-paragraph +.It M-l +downcase-word +.It M-m +back-to-indentation +.It M-q +fill-paragraph +.It M-r +search-backward +.It M-s +search-forward +.It M-t +transpose-words +.It M-u +upcase-word +.It M-v +scroll-down +.It M-w +copy-region-as-kill +.It M-x +execute-extended-command +.It M-{ +backward-paragraph +.It M-| +shell-command-on-region +.It M-} +forward-paragraph +.It M-~ +not-modified +.It M-DEL +backward-kill-word +.It C-_ +undo +.It ) +blink-and-insert +.It DEL +delete-backward-char +.El +.Pp +For a complete description of +.Nm +commands, see +.Sx MG COMMANDS . +To see the active keybindings at any time, type +.Dq M-x describe-bindings . +.Sh MG COMMANDS +Commands are invoked by +.Dq M-x , +or by binding to a key. +Many commands take an optional numerical parameter, +.Va n . +This parameter is set either by +M- (where +.Va n +is the numerical argument) before the command, or by +one or more invocations of the universal argument, usually bound to C-u. +When invoked in this manner, the value of the numeric parameter to +be passed is displayed in the minibuffer before the M-x. +One common use of the parameter is in mode toggles (e.g.\& +make-backup-files). +If no parameter is supplied, the mode is toggled to its +alternate state. +If a positive parameter is supplied, the mode is forced to on. +Otherwise, it is forced to off. +.\" +.Bl -tag -width xxxxx +.It apropos +Help Apropos. +Prompt the user for a string, open the *help* buffer, +and list all +.Nm +commands that contain that string. +.It audible-bell +Toggle the audible system bell. +.It auto-execute +Register an auto-execute hook; that is, specify a filename pattern +(conforming to the shell's filename globbing rules) and an associated +function to execute when a file matching the specified pattern +is read into a buffer. +.It auto-fill-mode +Toggle auto-fill mode (sometimes called mail-mode) in the current buffer, +where text inserted past the fill column is automatically wrapped +to a new line. +Can be set globally with set-default-mode. +.It auto-indent-mode +Toggle indent mode in the current buffer, +where indentation is preserved after a newline. +Can be set globally with set-default-mode. +.It back-to-indentation +Move the dot to the first non-whitespace character on the current line. +.It backup-to-home-directory +Save backup copies to a +.Pa ~/.mg.d +directory instead of working directory. +Requires make-backup-files to be on. +.It backward-char +Move cursor backwards one character. +.It backward-kill-word +Kill text backwards by +.Va n +words. +.It backward-paragraph +Move cursor backwards +.Va n +paragraphs. +Paragraphs are delimited by or or . +.It backward-word +Move cursor backwards by the specified number of words. +.It beginning-of-buffer +Move cursor to the top of the buffer. +If set, keep mark's position, otherwise set at current position. +A numeric argument +.Va n +will move n/10th of the way from the top. +.It beginning-of-line +Move cursor to the beginning of the line. +.It blink-and-insert +Self-insert a character, then search backwards and blink its +matching delimiter. +For delimiters other than +parenthesis, brackets, and braces, the character itself +is used as its own match. +Can be used in the startup file with the global-set-key command. +.It bsmap-mode +Toggle bsmap mode, where DEL and C-h are swapped. +.It c-mode +Toggle a KNF-compliant mode for editing C program files. +.It call-last-kbd-macro +Invoke the keyboard macro. +.It capitalize-word +Capitalize +.Va n +words; i.e. convert the first character of the word to +upper case, and subsequent letters to lower case. +.It cd +Change the global working directory. +See also global-wd-mode. +.It column-number-mode +Toggle whether the column number is displayed in the modeline. +.It copy-region-as-kill +Copy all of the characters in the region to the kill buffer, +clearing the mark afterwards. +This is a bit like a kill-region followed by a yank. +.It count-matches +Count the number of lines matching the supplied regular expression. +.It count-non-matches +Count the number of lines not matching the supplied regular expression. +.It cscope-find-this-symbol +List the matches for the given symbol. +.It cscope-find-global-definition +List global definitions for the given literal. +.It cscope-find-called-functions +List functions called from the given function. +.It cscope-find-functions-calling-this-function +List functions calling the given function. +.It cscope-find-this-text-string +List locations matching the given text string. +.It cscope-find-egrep-pattern +List locations matching the given extended regular expression pattern. +.It cscope-find-this-file +List filenames matching the given filename. +.It cscope-find-files-including-file +List files that #include the given filename. +.It cscope-next-symbol +Navigate to the next match. +.It cscope-prev-symbol +Navigate to the previous match. +.It cscope-next-file +Navigate to the next file. +.It cscope-prev-file +Navigate to the previous file. +.It cscope-create-list-of-files-to-index +Create cscope's List and Index in the given directory. +.It define-key +Prompts the user for a named keymap (mode), +a key, and an +.Nm +command, then creates a keybinding in the appropriate +map. +.It delete-backward-char +Delete backwards +.Va n +characters. +Like delete-char, this actually does a kill if presented +with an argument. +.It delete-blank-lines +Delete blank lines around dot. +If dot is sitting on a blank line, this command +deletes all the blank lines above and below the current line. +Otherwise, it deletes all of the blank lines after the current line. +.It delete-char +Delete +.Va n +characters forward. +If any argument is present, it kills rather than deletes, +saving the result in the kill buffer. +.It delete-horizontal-space +Delete any whitespace around the dot. +.It delete-leading-space +Delete leading whitespace on the current line. +.It delete-trailing-space +Delete trailing whitespace on the current line. +.It delete-matching-lines +Delete all lines after dot that contain a string matching +the supplied regular expression. +.It delete-non-matching-lines +Delete all lines after dot that don't contain a string matching +the supplied regular expression. +.It delete-other-windows +Make the current window the only window visible on the screen. +.It delete-window +Delete current window. +.It describe-bindings +List all global and local keybindings, putting the result in +the *help* buffer. +.It describe-key-briefly +Read a key from the keyboard, and look it up in the keymap. +Display the name of the function currently bound to the key. +.It diff-buffer-with-file +View the differences between buffer and its associated file. +.It digit-argument +Process a numerical argument for keyboard-invoked functions. +.It dired-jump +Open a dired buffer containing the current buffer's directory location. +.It downcase-region +Set all characters in the region to lower case. +.It downcase-word +Set characters to lower case, starting at the dot, and ending +.Va n +words away. +.It emacs-version +Return an +.Nm +version string. +.It end-kbd-macro +Stop defining a keyboard macro. +.It end-of-buffer +Move cursor to the end of the buffer. +If set, keep mark's position, otherwise set at current position. +A numeric argument +.Va n +will move n/10th of the way from the end. +.It end-of-line +Move cursor to the end of the line. +.It enlarge-window +Enlarge the current window by shrinking either the window above +or below it. +.It eval-current-buffer +Evaluate the current buffer as a series of +.Nm +commands. +Useful for testing +.Nm +startup files. +.It eval-expression +Get one line from the user, and run it. +Useful for testing expressions in +.Nm +startup files. +.It exchange-point-and-mark +Swap the values of "dot" and "mark" in the current window. +Return an error if no mark is set. +.It execute-extended-command +Invoke an extended command; i.e. M-x. +Call the message line routine to read in the command name and apply +autocompletion to it. +When it comes back, look the name up in the symbol table and run the +command if it is found, passing arguments as necessary. +Print an error if there is anything wrong. +.It fill-paragraph +Justify a paragraph, wrapping text at the current fill column. +.It find-file +Select a file for editing. +First check if the file can be found +in another buffer; if it is there, just switch to that buffer. +If the file cannot be found, create a new buffer, read in the +file from disk, and switch to the new buffer. +.It find-file-read-only +Same as find-file, except the new buffer is set to read-only. +.It find-alternate-file +Replace the current file with an alternate one. +Semantics for finding the replacement file are the same as +find-file, except the current buffer is killed before the switch. +If the kill fails, or is aborted, revert to the original file. +.It find-file-other-window +Opens the specified file in a second buffer. +Splits the current window if necessary. +.It find-tag +Jump to definition of tag at dot. +.It forward-char +Move cursor forwards (or backwards, if +.Va n +is negative) +.Va n +characters. +Returns an error if the end of buffer is reached. +.It forward-paragraph +Move forward +.Va n +paragraphs. +Paragraphs are delimited by or or . +.It forward-word +Move the cursor forward by the specified number of words. +.It global-set-key +Bind a key in the global (fundamental) key map. +.It global-unset-key +Unbind a key from the global (fundamental) key map; i.e. set it to 'rescan'. +.It global-wd-mode +Toggle global working-directory mode. +When enabled, +.Nm +defaults to opening files (and executing commands like compile and grep) +relative to the global working directory. +When disabled, a working directory is set for each buffer. +.It goto-line +Go to a specific line. +If an argument is present, then +it is the line number, else prompt for a line number to use. +.It help-help +Prompts for one of (a)propos, (b)indings, des(c)ribe key briefly. +.It insert +Insert a string, mainly for use from macros. +.It insert-buffer +Insert the contents of another buffer at dot. +.It insert-file +Insert a file into the current buffer at dot. +.It insert-with-wrap +Insert the bound character with word wrap. +Check to see if we're past the fill column, and if so, +justify this line. +.It isearch-backward +Use incremental searching, initially in the reverse direction. +isearch ignores any explicit arguments. +If invoked during macro definition or evaluation, the non-incremental +search-backward is invoked instead. +.It isearch-forward +Use incremental searching, initially in the forward direction. +isearch ignores any explicit arguments. +If invoked during macro definition or evaluation, the non-incremental +search-forward is invoked instead. +.It join-line +Join the current line to the previous. +If called with an argument, +join the next line to the current one. +.It just-one-space +Delete any whitespace around dot, then insert a space. +.It keyboard-quit +Abort the current action. +.It kill-buffer +Dispose of a buffer, by name. +If the buffer name does not start and end with an asterisk, +prompt the user if the buffer +has been changed. +.It kill-line +Kill line. +If called without an argument, it kills from dot to the end +of the line, unless it is at the end of the line, when it kills the +newline. +If called with an argument of 0, it kills from the start of the +line to dot. +If called with a positive argument, it kills from dot +forward over that number of newlines. +If called with a negative argument +it kills any text before dot on the current line, then it kills back +abs(n) lines. +.It kill-paragraph +Delete +.Va n +paragraphs starting with the current one. +.It kill-region +Kill the currently defined region. +.It kill-word +Delete forward +.Va n +words. +.It leave-tmpdir-backups +Modifies the behaviour of backup-to-home-directory. +Backup files that would normally reside in +.Pa /tmp +are left there and not moved to the +.Pa ~/.mg.d +directory. +.It line-number-mode +Toggle whether the line number is displayed in the modeline. +.It list-buffers +Display the list of available buffers. +The first column in the output indicates which buffer is active with a '>' +character. +The second column indicates which buffers are modified. +The third column indicates which buffers are read-only. +The remaining columns are self-explanatory. +.It load +Prompt the user for a filename, and then execute commands +from that file. +.It local-set-key +Bind a key mapping in the local (topmost) mode. +.It local-unset-key +Unbind a key mapping in the local (topmost) mode. +.It make-backup-files +Toggle generation of backup files. +Enabled by default. +.It make-directory +Prompt the user for a path or directory name which is then created. +.It mark-paragraph +Mark +.Va n +paragraphs. +.It mark-whole-buffer +Marks whole buffer as a region by putting dot at the beginning and mark +at the end of buffer. +.It meta-key-mode +When disabled, the meta key can be used to insert extended-ascii (8-bit) +characters. +When enabled, the meta key acts as usual. +.It negative-argument +Process a negative argument for keyboard-invoked functions. +.It newline +Insert a newline into the current buffer. +.It newline-and-indent +Insert a newline, then enough tabs and spaces to duplicate the indentation +of the previous line. +Assumes tabs are every eight characters. +.It next-line +Move forward +.Va n +lines. +.\" .It no-tab-mode +.\" Toggle notab mode. +.\" In this mode, spaces are inserted rather than tabs. +.It not-modified +Turn off the modified flag in the current buffer. +.It open-line +Open up some blank space. +Essentially, insert +.Va n +newlines, then back up over them. +.It other-window +The command to make the next (down the screen) window the current +window. +There are no real errors, although the command does nothing if +there is only 1 window on the screen. +.It overwrite-mode +Toggle overwrite mode in the current buffer, +where typing overwrites existing characters rather than inserting them. +Can be set globally with set-default-mode. +.It prefix-region +Inserts a prefix string before each line of a region. +The prefix string is settable by using 'set-prefix-string'. +.It previous-line +Move backwards +.Va n +lines. +.It previous-window +This command makes the previous (up the screen) window the +current window. +There are no errors, although the command does not do +a lot if there is only 1 window. +.It pop-tag-mark +Return to position where find-tag was previously invoked. +.It push-shell +Suspend +.Nm +and switch to alternate screen, if available. +.It pwd +Display current (global) working directory in the status area. +.It query-replace +Query Replace. +Search and replace strings selectively, prompting after each match. +.It replace-regexp +Replace regular expression globally without individual prompting. +.It replace-string +Replace string globally without individual prompting. +.It query-replace-regexp +Replace strings selectively. +Does a search and replace operation using regular +expressions for both patterns. +.It quoted-insert +Insert the next character verbatim into the current buffer; i.e. ignore +any function bound to that key. +.It re-search-again +Perform a regular expression search again, using the same search +string and direction as the last search command. +.It re-search-backward +Search backwards using a regular expression. +Get a search string from the user, and search, starting at dot +and proceeding toward the front of the buffer. +If found, dot is left +pointing at the first character of the pattern [the last character that +was matched]. +.It re-search-forward +Search forward using a regular expression. +Get a search string from the user and search for it starting at dot. +If found, move dot to just after the matched characters. +display does all +the hard stuff. +If not found, it just prints a message. +.It recenter +Reposition dot in the current window. +By default, the dot is centered. +If given a positive argument (n), the display is repositioned to line +n. +If +.Va n +is negative, it is that line from the bottom. +.It redraw-display +Refresh the display. +Recomputes all window sizes in case something has changed. +.It revert-buffer +Revert the current buffer to the latest file on disk. +.It save-buffer +Save the contents of the current buffer if it has been changed, +optionally creating a backup copy. +.It save-buffers-kill-emacs +Offer to save modified buffers and quit +.Nm . +.It save-some-buffers +Look through the list of buffers, offering to save any buffer that +has been changed. +Buffers that are not associated with files (such +as *scratch*, *grep*, *compile*) are ignored. +.It scroll-down +Scroll backwards +.Va n +pages. +A two-line overlap between pages is +assumed. +If given a repeat argument, scrolls back lines, not pages. +.It scroll-one-line-down +Scroll the display down +.Va n +lines without changing the cursor position. +.It scroll-one-line-up +Scroll the display +.Va n +lines up without moving the cursor position. +.It scroll-other-window +Scroll the next window in the window list window forward +.Va n +pages. +.It scroll-up +Scroll forward one page. +A two-line overlap between pages is +assumed. +If given a repeat argument, scrolls back lines, not pages. +.It search-again +Search again, using the same search string and direction as the last +search command. +.It search-backward +Reverse search. +Get a search string from the user, and search, starting +at dot and proceeding toward the front of the buffer. +If found, dot is +left pointing at the first character of the pattern (the last character +that was matched). +.It search-forward +Search forward. +Get a search string from the user, and search for it +starting at dot. +If found, dot gets moved to just after the matched +characters, if not found, print a message. +.It self-insert-command +Insert a character. +.It sentence-end-double-space +Toggle double or single spaces for end of sentences. +Double is the default. +Currently only affects fill-paragraph. +.It set-case-fold-search +Set case-fold searching, causing case not to matter +in regular expression searches. +This is the default. +.It set-case-replace +Preserve the case of the replaced string. +This is the default. +.It set-default-mode +Append the supplied mode to the list of default modes +used by subsequent buffer creation. +Built in modes include: fill, indent and overwrite. +.It set-fill-column +Prompt the user for a fill column. +Used by auto-fill-mode. +.It set-mark-command +Sets the mark in the current window to the current dot location. +.It set-prefix-string +Sets the prefix string to be used by the 'prefix-region' command. +.It shell-command +Execute external command from mini-buffer. +.It shell-command-on-region +Provide the text in region to the shell command as input. +.It shrink-window +Shrink current window by one line. +The window immediately below is expanded to pick up the slack. +If only one window is present, this command has no effect. +.It split-window-vertically +Split the current window. +A window smaller than 3 lines cannot be split. +.It start-kbd-macro +Start defining a keyboard macro. +Macro definition is ended by invoking end-kbd-macro. +.It suspend-emacs +Suspend +.Nm +and switch back to alternate screen, if in use. +.It switch-to-buffer +Prompt and switch to a new buffer in the current window. +.It switch-to-buffer-other-window +Switch to buffer in another window. +.It toggle-read-only +Toggle the read-only flag on the current buffer. +.It toggle-read-only-all +Toggle the read-only flag on all non-ephemeral buffers. +A simple toggle that switches a global read-only flag either on +or off. +.It transpose-chars +Transpose the two characters in front of and under dot, +then move forward one character. +Treat newline characters the same as any other. +.It transpose-paragraphs +Transpose adjacent paragraphs. +If multiple iterations are requested, the current paragraph will +be moved +.Va n +paragraphs forward. +.It transpose-words +Transpose adjacent words. +.It undo +Undo the most recent action. +If invoked again without an intervening command, +move the undo pointer to the previous action and undo it. +.It undo-boundary +Add an undo boundary. +This is not usually done interactively. +.It undo-boundary-toggle +Toggle whether undo boundaries are generated. +Undo boundaries are often disabled before operations that should +be considered atomically undoable. +.It undo-enable +Toggle whether undo information is kept. +.It undo-list +Show the undo records for the current buffer in a new buffer. +.It universal-argument +Repeat the next command 4 times. +Usually bound to C-u. +This command may be stacked; e.g.\& +C-u C-u C-f moves the cursor forward 16 characters. +.It upcase-region +Upper case region. +Change all of the lower case characters in the region to +upper case. +.It upcase-word +Move the cursor forward by the specified number of words. +As it moves, convert any characters to upper case. +.It visible-bell +Toggle the visible bell. +If this toggle is on, the modeline will flash. +.It visit-tags-table +Record name of the tags file to be used for subsequent find-tag. +.It what-cursor-position +Display a bunch of useful information about the current location of +dot. +The character under the cursor (in octal), the current line, row, +and column, and approximate position of the cursor in the file (as a +percentage) is displayed. +The column position assumes an infinite +position display; it does not truncate just because the screen does. +.It write-file +Ask for a file name and write the contents of the current buffer to +that file. +Update the remembered file name and clear the buffer +changed flag. +.It yank +Yank text from kill-buffer. +Unlike emacs, the +.Nm +kill buffer consists only +of the most recent kill. +It is not a ring. +.El +.Sh MG DIRED KEY BINDINGS +Specific key bindings are available in dired mode. +.Pp +.Bl -tag -width xxxxxxxxxxxxxxxxxx -offset indent -compact +.It DEL +dired-unmark-backward +.It RET, e, f and C-m +dired-find-file +.It SPC, n +dired-next-line +.It ! +dired-shell-command +.It + +dired-create-directory +.It a +dired-find-alternate-file +.It c +dired-do-copy +.It d and C-d +dired-flag-file-deletion +.It g +dired-revert +.It j +dired-goto-file +.It o +dired-find-file-other-window +.It p +dired-previous-line +.It q +quit-window +.It r +dired-do-rename +.It u +dired-unmark +.It x +dired-do-flagged-delete +.It C-v +dired-scroll-down +.It M-v +dired-scroll-up +.El +.Sh MG DIRED COMMANDS +The following are a list of the commands specific to dired mode: +.Bl -tag -width Ds +.It dired-create-directory +Create a directory. +.It dired-do-copy +Copy the file listed on the current line of the dired buffer. +.It dired-do-flagged-delete +Delete the files that have been flagged for deletion. +.It dired-do-rename +Rename the file listed on the current line of the dired buffer. +.It dired-find-alternate-file +Replace the current dired buffer with an alternate one as specified +by the position of the cursor in the dired buffer. +.It dired-find-file +Open the file on the current line of the dired buffer. +If the cursor is on a directory it will be opened in dired mode. +.It dired-flag-file-deletion +Flag the file listed on the current line for deletion. +This is indicated in the buffer by putting a D at the left margin. +No files are actually deleted until the function dired-do-flagged-delete +is executed. +.It dired-find-file-other-window +Open the file on the current line of the dired buffer in a +different window. +.It dired-goto-file +Move the cursor to a file name in the dired buffer. +.It dired-next-line +Move the cursor to the next line. +.It dired-other-window +This function works just like dired, except that it puts the +dired buffer in another window. +.It dired-previous-line +Move the cursor to the previous line. +.It dired-revert +Refresh the dired buffer while retaining any flags. +.It dired-scroll-down +Scroll down the dired buffer. +.It dired-scroll-up +Scroll up the dired buffer. +.It dired-shell-command +Pipe the file under the current cursor position through a shell command. +.It dired-unmark +Remove the deletion flag for the file on the current line. +.It dired-unmark-backward +Remove the deletion flag from the file listed on the previous line +of the dired buffer, then move up to that line. +.It quit-window +Close the current dired buffer. +.El +.Sh CONFIGURATION FILES +There are two configuration files, +.Pa .mg +and +.Pa .mg-TERM . +Here, +.Ev TERM +represents the name of the terminal type; e.g. if the terminal type +is set to +.Dq vt100 , +.Nm +will use +.Pa .mg-vt100 +as a startup file. +The terminal type startup file is used first. +.Pp +The startup file format is a list of commands, one per line, as used for +interactive evaluation. +Strings that are normally entered by the user at any subsequent prompts +may be specified after the command name; e.g.: +.Bd -literal -offset indent +global-set-key ")" self-insert-command +global-set-key "\e^x\e^f" find-file +global-set-key "\ee[Z" backward-char +set-default-mode fill +set-fill-column 72 +auto-execute *.c c-mode +.Ed +.Pp +Comments can be added to the startup files by placing +.Sq ;\& +or +.Sq # +as the first character of a line. +.Sh FILES +.Bl -tag -width /usr/share/doc/mg/tutorial -compact +.It Pa ~/.mg +normal startup file +.It Pa ~/.mg-TERM +terminal-specific startup file +.It Pa ~/.mg.d +alternative backup file location +.It Pa /usr/share/doc/mg/tutorial +concise tutorial +.El +.Sh SEE ALSO +.Xr ctags 1 , +.Xr vi 1 +.Sh CAVEATS +Since it is written completely in C, there is currently no +language in which extensions can be written; +however, keys can be rebound and certain parameters can be changed +in startup files. +.Pp +In order to use 8-bit characters (such as German umlauts), the Meta key +needs to be disabled via the +.Dq meta-key-mode +command. +.Pp +Multi-byte character sets, such as UTF-8, are not supported. Index: contrib/mg/mi_vector_hash.c =================================================================== --- /dev/null +++ contrib/mg/mi_vector_hash.c @@ -0,0 +1,168 @@ +/* $NetBSD: mi_vector_hash.c,v 1.1 2013/12/11 01:24:08 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * See http://burtleburtle.net/bob/hash/doobs.html for the full description + * and the original version of the code. This version differs by exposing + * the full internal state and avoiding byte operations in the inner loop + * if the key is aligned correctly. + */ + +#include + +#include +#include + +#define mix(a, b, c) do { \ + a -= b; a -= c; a ^= (c >> 13); \ + b -= c; b -= a; b ^= (a << 8); \ + c -= a; c -= b; c ^= (b >> 13); \ + a -= b; a -= c; a ^= (c >> 12); \ + b -= c; b -= a; b ^= (a << 16); \ + c -= a; c -= b; c ^= (b >> 5); \ + a -= b; a -= c; a ^= (c >> 3); \ + b -= c; b -= a; b ^= (a << 10); \ + c -= a; c -= b; c ^= (b >> 15); \ +} while (/* CONSTCOND */0) + +#define FIXED_SEED 0x9e3779b9 /* Golden ratio, arbitrary constant */ + +/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */ +static uint32_t +le32dec(const void *buf) +{ + uint8_t const *p = (uint8_t const *) buf; + + return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +void +mi_vector_hash(const void * __restrict key, size_t len, uint32_t seed, + uint32_t hashes[3]) +{ + static const uint32_t mask[4] = { + 0x000000ff, 0x0000ffff, 0x00ffffff, 0xffffffff + }; + uint32_t orig_len, a, b, c; + const uint8_t *k; + + orig_len = (uint32_t)len; + + a = b = FIXED_SEED; + c = seed; + + if ((uintptr_t)key & 3) { + k = key; + while (len >= 12) { + a += le32dec(k); + b += le32dec(k + 4); + c += le32dec(k + 8); + mix(a, b, c); + k += 12; + len -= 12; + } + c += orig_len; + + if (len > 8) { + switch (len) { + case 11: + c += (uint32_t)k[10] << 24; + /* FALLTHROUGH */ + case 10: + c += (uint32_t)k[9] << 16; + /* FALLTHROUGH */ + case 9: + c += (uint32_t)k[8] << 8; + /* FALLTHROUGH */ + } + b += le32dec(k + 4); + a += le32dec(k); + } else if (len > 4) { + switch (len) { + case 8: + b += (uint32_t)k[7] << 24; + /* FALLTHROUGH */ + case 7: + b += (uint32_t)k[6] << 16; + /* FALLTHROUGH */ + case 6: + b += (uint32_t)k[5] << 8; + /* FALLTHROUGH */ + case 5: + b += k[4]; + /* FALLTHROUGH */ + } + a += le32dec(k); + } else if (len) { + switch (len) { + case 4: + a += (uint32_t)k[3] << 24; + /* FALLTHROUGH */ + case 3: + a += (uint32_t)k[2] << 16; + /* FALLTHROUGH */ + case 2: + a += (uint32_t)k[1] << 8; + /* FALLTHROUGH */ + case 1: + a += k[0]; + /* FALLTHROUGH */ + } + } + } else { + const uint32_t *key32 = key; + while (len >= 12) { + a += le32toh(key32[0]); + b += le32toh(key32[1]); + c += le32toh(key32[2]); + mix(a, b, c); + key32 += 3; + len -= 12; + } + c += orig_len; + + if (len > 8) { + c += (le32toh(key32[2]) & mask[len - 9]) << 8; + b += le32toh(key32[1]); + a += le32toh(key32[0]); + } else if (len > 4) { + b += le32toh(key32[1]) & mask[len - 5]; + a += le32toh(key32[0]); + } else if (len) + a += le32toh(key32[0]) & mask[len - 1]; + } + mix(a, b, c); + hashes[0] = a; + hashes[1] = b; + hashes[2] = c; +} Index: contrib/mg/modes.c =================================================================== --- /dev/null +++ contrib/mg/modes.c @@ -0,0 +1,174 @@ +/* $OpenBSD: modes.c,v 1.21 2017/05/30 07:05:22 florian Exp $ */ + +/* This file is in the public domain. */ + +/* + * Commands to toggle modes. Without an argument, these functions will + * toggle the given mode. A negative or zero argument will turn the mode + * off. A positive argument will turn the mode on. + */ + +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" + +int changemode(int, int, char *); + +int defb_nmodes = 0; +struct maps_s *defb_modes[PBMODES] = { &fundamental_mode }; +int defb_flag = 0; + +int +changemode(int f, int n, char *newmode) +{ + int i; + struct maps_s *m; + + if ((m = name_mode(newmode)) == NULL) { + dobeep(); + ewprintf("Can't find mode %s", newmode); + return (FALSE); + } + if (!(f & FFARG)) { + for (i = 0; i <= curbp->b_nmodes; i++) + if (curbp->b_modes[i] == m) { + /* mode already set */ + n = 0; + break; + } + } + if (n > 0) { + for (i = 0; i <= curbp->b_nmodes; i++) + if (curbp->b_modes[i] == m) + /* mode already set */ + return (TRUE); + if (curbp->b_nmodes >= PBMODES - 1) { + dobeep(); + ewprintf("Too many modes"); + return (FALSE); + } + curbp->b_modes[++(curbp->b_nmodes)] = m; + } else { + /* fundamental is b_modes[0] and can't be unset */ + for (i = 1; i <= curbp->b_nmodes && m != curbp->b_modes[i]; + i++); + if (i > curbp->b_nmodes) + return (TRUE); /* mode wasn't set */ + for (; i < curbp->b_nmodes; i++) + curbp->b_modes[i] = curbp->b_modes[i + 1]; + curbp->b_nmodes--; + } + upmodes(curbp); + return (TRUE); +} + +int +indentmode(int f, int n) +{ + return (changemode(f, n, "indent")); +} + +int +fillmode(int f, int n) +{ + return (changemode(f, n, "fill")); +} + +#ifdef NOTAB +int +notabmode(int f, int n) +{ + if (changemode(f, n, "notab") == FALSE) + return (FALSE); + if (f & FFARG) { + if (n <= 0) + curbp->b_flag &= ~BFNOTAB; + else + curbp->b_flag |= BFNOTAB; + } else + curbp->b_flag ^= BFNOTAB; + return (TRUE); +} +#endif /* NOTAB */ + +int +overwrite_mode(int f, int n) +{ + if (changemode(f, n, "overwrite") == FALSE) + return (FALSE); + if (f & FFARG) { + if (n <= 0) + curbp->b_flag &= ~BFOVERWRITE; + else + curbp->b_flag |= BFOVERWRITE; + } else + curbp->b_flag ^= BFOVERWRITE; + return (TRUE); +} + +int +set_default_mode(int f, int n) +{ + int i; + struct maps_s *m; + char modebuf[32], *bufp; + + if ((bufp = eread("Set Default Mode: ", modebuf, sizeof(modebuf), + EFNEW)) == NULL) + return (ABORT); + else if (bufp[0] == '\0') + return (FALSE); + if ((m = name_mode(modebuf)) == NULL) { + dobeep(); + ewprintf("can't find mode %s", modebuf); + return (FALSE); + } + if (!(f & FFARG)) { + for (i = 0; i <= defb_nmodes; i++) + if (defb_modes[i] == m) { + /* mode already set */ + n = 0; + break; + } + } + if (n > 0) { + for (i = 0; i <= defb_nmodes; i++) + if (defb_modes[i] == m) + /* mode already set */ + return (TRUE); + if (defb_nmodes >= PBMODES - 1) { + dobeep(); + ewprintf("Too many modes"); + return (FALSE); + } + defb_modes[++defb_nmodes] = m; + } else { + /* fundamental is defb_modes[0] and can't be unset */ + for (i = 1; i <= defb_nmodes && m != defb_modes[i]; i++); + if (i > defb_nmodes) + /* mode was not set */ + return (TRUE); + for (; i < defb_nmodes; i++) + defb_modes[i] = defb_modes[i + 1]; + defb_nmodes--; + } + if (strcmp(modebuf, "overwrite") == 0) { + if (n <= 0) + defb_flag &= ~BFOVERWRITE; + else + defb_flag |= BFOVERWRITE; + } +#ifdef NOTAB + if (strcmp(modebuf, "notab") == 0) { + if (n <= 0) + defb_flag &= ~BFNOTAB; + else + defb_flag |= BFNOTAB; + } +#endif /* NOTAB */ + return (TRUE); +} Index: contrib/mg/nbperf-bdz.c =================================================================== --- /dev/null +++ contrib/mg/nbperf-bdz.c @@ -0,0 +1,302 @@ +/* $NetBSD: nbperf-bdz.c,v 1.9 2014/04/30 21:04:58 joerg Exp $ */ +/*- + * Copyright (c) 2009, 2012 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "nbperf.h" + +/* + * A full description of the algorithm can be found in: + * "Simple and Space-Efficient Minimal Perfect Hash Functions" + * by Botelho, Pagh and Ziviani, proceeedings of WADS 2007. + */ + +/* + * The algorithm is based on random, acyclic 3-graphs. + * + * Each edge in the represents a key. The vertices are the reminder of + * the hash function mod n. n = cm with c > 1.23. This ensures that + * an acyclic graph can be found with a very high probality. + * + * An acyclic graph has an edge order, where at least one vertex of + * each edge hasn't been seen before. It is declares the first unvisited + * vertex as authoritive for the edge and assigns a 2bit value to unvisited + * vertices, so that the sum of all vertices of the edge modulo 4 is + * the index of the authoritive vertex. + */ + +#include "graph3.h" + +struct state { + struct graph3 graph; + uint32_t *visited; + uint32_t *holes64k; + uint16_t *holes64; + uint8_t *g; + uint32_t *result_map; +}; + +static void +assign_nodes(struct state *state) +{ + struct edge3 *e; + size_t i, j; + uint32_t t, r, holes; + + for (i = 0; i < state->graph.v; ++i) + state->g[i] = 3; + + for (i = 0; i < state->graph.e; ++i) { + j = state->graph.output_order[i]; + e = &state->graph.edges[j]; + if (!state->visited[e->left]) { + r = 0; + t = e->left; + } else if (!state->visited[e->middle]) { + r = 1; + t = e->middle; + } else { + if (state->visited[e->right]) + abort(); + r = 2; + t = e->right; + } + + state->visited[t] = 2 + j; + if (state->visited[e->left] == 0) + state->visited[e->left] = 1; + if (state->visited[e->middle] == 0) + state->visited[e->middle] = 1; + if (state->visited[e->right] == 0) + state->visited[e->right] = 1; + + state->g[t] = (9 + r - state->g[e->left] - state->g[e->middle] + - state->g[e->right]) % 3; + } + + holes = 0; + for (i = 0; i < state->graph.v; ++i) { + if (i % 65536 == 0) + state->holes64k[i >> 16] = holes; + + if (i % 64 == 0) + state->holes64[i >> 6] = holes - state->holes64k[i >> 16]; + + if (state->visited[i] > 1) { + j = state->visited[i] - 2; + state->result_map[j] = i - holes; + } + + if (state->g[i] == 3) + ++holes; + } +} + +static void +print_hash(struct nbperf *nbperf, struct state *state) +{ + uint64_t sum; + size_t i; + + fprintf(nbperf->output, "#include \n"); + fprintf(nbperf->output, "#include \n\n"); + + fprintf(nbperf->output, "%suint32_t\n", + nbperf->static_hash ? "static " : ""); + fprintf(nbperf->output, + "%s(const void * __restrict key, size_t keylen)\n", + nbperf->hash_name); + fprintf(nbperf->output, "{\n"); + + fprintf(nbperf->output, + "\tstatic const uint64_t g1[%" PRId32 "] = {\n", + (state->graph.v + 63) / 64); + sum = 0; + for (i = 0; i < state->graph.v; ++i) { + sum |= ((uint64_t)state->g[i] & 1) << (i & 63); + if (i % 64 == 63) { + fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s", + (i / 64 % 2 == 0 ? "\t " : " "), + sum, + (i / 64 % 2 == 1 ? "\n" : "")); + sum = 0; + } + } + if (i % 64 != 0) { + fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s", + (i / 64 % 2 == 0 ? "\t " : " "), + sum, + (i / 64 % 2 == 1 ? "\n" : "")); + } + fprintf(nbperf->output, "%s\t};\n", (i % 2 ? "\n" : "")); + + fprintf(nbperf->output, + "\tstatic const uint64_t g2[%" PRId32 "] = {\n", + (state->graph.v + 63) / 64); + sum = 0; + for (i = 0; i < state->graph.v; ++i) { + sum |= (((uint64_t)state->g[i] & 2) >> 1) << (i & 63); + if (i % 64 == 63) { + fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s", + (i / 64 % 2 == 0 ? "\t " : " "), + sum, + (i / 64 % 2 == 1 ? "\n" : "")); + sum = 0; + } + } + if (i % 64 != 0) { + fprintf(nbperf->output, "%s0x%016" PRIx64 "ULL,%s", + (i / 64 % 2 == 0 ? "\t " : " "), + sum, + (i / 64 % 2 == 1 ? "\n" : "")); + } + fprintf(nbperf->output, "%s\t};\n", (i % 2 ? "\n" : "")); + + fprintf(nbperf->output, + "\tstatic const uint32_t holes64k[%" PRId32 "] = {\n", + (state->graph.v + 65535) / 65536); + for (i = 0; i < state->graph.v; i += 65536) + fprintf(nbperf->output, "%s0x%08" PRIx32 ",%s", + (i / 65536 % 4 == 0 ? "\t " : " "), + state->holes64k[i >> 16], + (i / 65536 % 4 == 3 ? "\n" : "")); + fprintf(nbperf->output, "%s\t};\n", (i / 65536 % 4 ? "\n" : "")); + + fprintf(nbperf->output, + "\tstatic const uint16_t holes64[%" PRId32 "] = {\n", + (state->graph.v + 63) / 64); + for (i = 0; i < state->graph.v; i += 64) + fprintf(nbperf->output, "%s0x%04" PRIx32 ",%s", + (i / 64 % 4 == 0 ? "\t " : " "), + state->holes64[i >> 6], + (i / 64 % 4 == 3 ? "\n" : "")); + fprintf(nbperf->output, "%s\t};\n", (i / 64 % 4 ? "\n" : "")); + + fprintf(nbperf->output, "\tuint64_t m;\n"); + fprintf(nbperf->output, "\tuint32_t idx, i, idx2;\n"); + fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size); + + (*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h"); + + fprintf(nbperf->output, "\n\th[0] = h[0] %% %" PRIu32 ";\n", + state->graph.v); + fprintf(nbperf->output, "\th[1] = h[1] %% %" PRIu32 ";\n", + state->graph.v); + fprintf(nbperf->output, "\th[2] = h[2] %% %" PRIu32 ";\n", + state->graph.v); + + fprintf(nbperf->output, + "\tidx = 9 + ((g1[h[0] >> 6] >> (h[0] & 63)) &1)\n" + "\t + ((g1[h[1] >> 6] >> (h[1] & 63)) & 1)\n" + "\t + ((g1[h[2] >> 6] >> (h[2] & 63)) & 1)\n" + "\t - ((g2[h[0] >> 6] >> (h[0] & 63)) & 1)\n" + "\t - ((g2[h[1] >> 6] >> (h[1] & 63)) & 1)\n" + "\t - ((g2[h[2] >> 6] >> (h[2] & 63)) & 1);\n" + ); + + fprintf(nbperf->output, + "\tidx = h[idx %% 3];\n"); + fprintf(nbperf->output, + "\tidx2 = idx - holes64[idx >> 6] - holes64k[idx >> 16];\n" + "\tidx2 -= popcount64(g1[idx >> 6] & g2[idx >> 6]\n" + "\t & (((uint64_t)1 << (idx & 63)) - 1));\n" + "\treturn idx2;\n"); + + fprintf(nbperf->output, "}\n"); + + if (nbperf->map_output != NULL) { + for (i = 0; i < state->graph.e; ++i) + fprintf(nbperf->map_output, "%" PRIu32 "\n", + state->result_map[i]); + } +} + +int +bpz_compute(struct nbperf *nbperf) +{ + struct state state; + int retval = -1; + uint32_t v, e; + + if (nbperf->c == 0) + nbperf->c = 1.24; + if (nbperf->c < 1.24) + errx(1, "The argument for option -c must be at least 1.24"); + if (nbperf->hash_size < 3) + errx(1, "The hash function must generate at least 3 values"); + + (*nbperf->seed_hash)(nbperf); + e = nbperf->n; + v = nbperf->c * nbperf->n; + if (1.24 * nbperf->n > v) + ++v; + if (v < 10) + v = 10; + + graph3_setup(&state.graph, v, e); + + state.holes64k = calloc(sizeof(uint32_t), (v + 65535) / 65536); + state.holes64 = calloc(sizeof(uint16_t), (v + 63) / 64 ); + state.g = calloc(sizeof(uint32_t), v | 63); + state.visited = calloc(sizeof(uint32_t), v); + state.result_map = calloc(sizeof(uint32_t), e); + + if (state.holes64k == NULL || state.holes64 == NULL || + state.g == NULL || state.visited == NULL || + state.result_map == NULL) + err(1, "malloc failed"); + + if (graph3_hash(nbperf, &state.graph)) + goto failed; + if (graph3_output_order(&state.graph)) + goto failed; + assign_nodes(&state); + print_hash(nbperf, &state); + + retval = 0; + +failed: + graph3_free(&state.graph); + free(state.visited); + free(state.g); + free(state.holes64k); + free(state.holes64); + free(state.result_map); + return retval; +} Index: contrib/mg/nbperf-chm.c =================================================================== --- /dev/null +++ contrib/mg/nbperf-chm.c @@ -0,0 +1,268 @@ +/* $NetBSD: nbperf-chm.c,v 1.3 2011/10/21 23:47:11 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include + +#include "nbperf.h" + +#ifdef BUILD_CHM3 +#include "graph3.h" +#else +#include "graph2.h" +#endif + +/* + * A full description of the algorithm can be found in: + * "An optimal algorithm for generating minimal perfect hash functions" + * by Czech, Havas and Majewski in Information Processing Letters, + * 43(5):256-264, October 1992. + */ + +/* + * The algorithm is based on random, acyclic graphs. + * + * Each edge in the represents a key. The vertices are the reminder of + * the hash function mod n. n = cm with c > 2, otherwise the propability + * of finding an acyclic graph is very low (for 2-graphs). The constant + * for 3-graphs is 1.24. + * + * After the hashing phase, the graph is checked for cycles. + * A cycle-free graph is either empty or has a vertex of degree 1. + * Removing the edge for this vertex doesn't change this property, + * so applying this recursively reduces the size of the graph. + * If the graph is empty at the end of the process, it was acyclic. + * + * The assignment step now sets g[i] := 0 and processes the edges + * in reverse order of removal. That ensures that at least one vertex + * is always unvisited and can be assigned. + */ + +struct state { +#ifdef BUILD_CHM3 + struct graph3 graph; +#else + struct graph2 graph; +#endif + uint32_t *g; + uint8_t *visited; +}; + +static void +assign_nodes(struct state *state) +{ +#ifdef BUILD_CHM3 + struct edge3 *e; +#else + struct edge2 *e; +#endif + size_t i; + uint32_t e_idx; + + for (i = 0; i < state->graph.e; ++i) { + e_idx = state->graph.output_order[i]; + e = &state->graph.edges[e_idx]; + +#ifdef BUILD_CHM3 + if (!state->visited[e->left]) { + state->g[e->left] = (2 * state->graph.e + e_idx + - state->g[e->middle] - state->g[e->right]) + % state->graph.e; + } else if (!state->visited[e->middle]) { + state->g[e->middle] = (2 * state->graph.e + e_idx + - state->g[e->left] - state->g[e->right]) + % state->graph.e; + } else { + state->g[e->right] = (2 * state->graph.e + e_idx + - state->g[e->left] - state->g[e->middle]) + % state->graph.e; + } + state->visited[e->left] = 1; + state->visited[e->middle] = 1; + state->visited[e->right] = 1; +#else + if (!state->visited[e->left]) { + state->g[e->left] = (state->graph.e + e_idx + - state->g[e->right]) % state->graph.e; + } else { + state->g[e->right] = (state->graph.e + e_idx + - state->g[e->left]) % state->graph.e; + } + state->visited[e->left] = 1; + state->visited[e->right] = 1; +#endif + } +} + +static void +print_hash(struct nbperf *nbperf, struct state *state) +{ + uint32_t i, per_line; + const char *g_type; + int g_width; + + fprintf(nbperf->output, "#include \n\n"); + + fprintf(nbperf->output, "%suint32_t\n", + nbperf->static_hash ? "static " : ""); + fprintf(nbperf->output, + "%s(const void * __restrict key, size_t keylen)\n", + nbperf->hash_name); + fprintf(nbperf->output, "{\n"); + if (state->graph.v >= 65536) { + g_type = "uint32_t"; + g_width = 8; + per_line = 4; + } else if (state->graph.v >= 256) { + g_type = "uint16_t"; + g_width = 4; + per_line = 8; + } else { + g_type = "uint8_t"; + g_width = 2; + per_line = 10; + } + fprintf(nbperf->output, "\tstatic const %s g[%" PRId32 "] = {\n", + g_type, state->graph.v); + for (i = 0; i < state->graph.v; ++i) { + fprintf(nbperf->output, "%s0x%0*" PRIx32 ",%s", + (i % per_line == 0 ? "\t " : " "), + g_width, state->g[i], + (i % per_line == per_line - 1 ? "\n" : "")); + } + if (i % per_line != 0) + fprintf(nbperf->output, "\n\t};\n"); + else + fprintf(nbperf->output, "\t};\n"); + fprintf(nbperf->output, "\tuint32_t h[%zu];\n\n", nbperf->hash_size); + (*nbperf->print_hash)(nbperf, "\t", "key", "keylen", "h"); +#ifdef BUILD_CHM3 + fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + " + "g[h[1] %% %" PRIu32 "] + " + "g[h[2] %% %" PRIu32"]) %% %" PRIu32 ";\n", + state->graph.v, state->graph.v, state->graph.v, state->graph.e); +#else + fprintf(nbperf->output, "\treturn (g[h[0] %% %" PRIu32 "] + " + "g[h[1] %% %" PRIu32"]) %% %" PRIu32 ";\n", + state->graph.v, state->graph.v, state->graph.e); +#endif + fprintf(nbperf->output, "}\n"); + + if (nbperf->map_output != NULL) { + for (i = 0; i < state->graph.e; ++i) + fprintf(nbperf->map_output, "%" PRIu32 "\n", i); + } +} + +int +#ifdef BUILD_CHM3 +chm3_compute(struct nbperf *nbperf) +#else +chm_compute(struct nbperf *nbperf) +#endif +{ + struct state state; + int retval = -1; + uint32_t v, e; + +#ifdef BUILD_CHM3 + if (nbperf->c == 0) + nbperf-> c = 1.24; + + if (nbperf->c < 1.24) + errx(1, "The argument for option -c must be at least 1.24"); + + if (nbperf->hash_size < 3) + errx(1, "The hash function must generate at least 3 values"); +#else + if (nbperf->c == 0) + nbperf-> c = 2; + + if (nbperf->c < 2) + errx(1, "The argument for option -c must be at least 2"); + + if (nbperf->hash_size < 2) + errx(1, "The hash function must generate at least 2 values"); +#endif + + (*nbperf->seed_hash)(nbperf); + e = nbperf->n; + v = nbperf->c * nbperf->n; +#ifdef BUILD_CHM3 + if (v == 1.24 * nbperf->n) + ++v; + if (v < 10) + v = 10; +#else + if (v == 2 * nbperf->n) + ++v; +#endif + + state.g = calloc(sizeof(uint32_t), v); + state.visited = calloc(sizeof(uint8_t), v); + if (state.g == NULL || state.visited == NULL) + err(1, "malloc failed"); + +#ifdef BUILD_CHM3 + graph3_setup(&state.graph, v, e); + if (graph3_hash(nbperf, &state.graph)) + goto failed; + if (graph3_output_order(&state.graph)) + goto failed; +#else + graph2_setup(&state.graph, v, e); + if (graph2_hash(nbperf, &state.graph)) + goto failed; + if (graph2_output_order(&state.graph)) + goto failed; +#endif + assign_nodes(&state); + print_hash(nbperf, &state); + + retval = 0; + +failed: +#ifdef BUILD_CHM3 + graph3_free(&state.graph); +#else + graph2_free(&state.graph); +#endif + free(state.g); + free(state.visited); + return retval; +} Index: contrib/mg/nbperf-chm3.c =================================================================== --- /dev/null +++ contrib/mg/nbperf-chm3.c @@ -0,0 +1,4 @@ +/* $NetBSD: nbperf-chm3.c,v 1.1 2009/08/15 16:21:05 joerg Exp $ */ + +#define BUILD_CHM3 +#include "nbperf-chm.c" Index: contrib/mg/nbperf.h =================================================================== --- /dev/null +++ contrib/mg/nbperf.h @@ -0,0 +1,61 @@ +/* $NetBSD: nbperf.h,v 1.4 2013/01/31 16:32:02 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#define NBPERF_MAX_HASH_SIZE 3 + +struct nbperf { + FILE *output; + FILE *map_output; + const char *hash_name; + int static_hash; + size_t n; + const void * __restrict * keys; + const size_t *keylens; + int first_round, has_duplicates; + + double c; + + size_t hash_size; + void (*seed_hash)(struct nbperf *); + void (*print_hash)(struct nbperf *, const char *, const char *, const char *, + const char *); + void (*compute_hash)(struct nbperf *, const void *, size_t, + uint32_t *); + uint32_t seed[1]; +}; + +int chm_compute(struct nbperf *); +int chm3_compute(struct nbperf *); +int bpz_compute(struct nbperf *); +void mi_vector_hash(const void * __restrict, size_t, uint32_t, + uint32_t[3]); Index: contrib/mg/nbperf.c =================================================================== --- /dev/null +++ contrib/mg/nbperf.c @@ -0,0 +1,255 @@ +/* $NetBSD: nbperf.c,v 1.5 2013/01/31 16:32:02 joerg Exp $ */ +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Joerg Sonnenberger. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "nbperf.h" + +static int predictable; + +static __dead +void usage(void) +{ + fprintf(stderr, + "%s [-ps] [-c utilisation] [-i iterations] [-n name] " + "[-o output] input\n", + getprogname()); + exit(1); +} + +#if HAVE_NBTOOL_CONFIG_H && !defined(__NetBSD__) +#define arc4random() rand() +#endif + +static void +mi_vector_hash_seed_hash(struct nbperf *nbperf) +{ + static uint32_t predictable_counter; + if (predictable) + nbperf->seed[0] = predictable_counter++; + else + nbperf->seed[0] = arc4random(); +} + +static void +mi_vector_hash_compute(struct nbperf *nbperf, const void *key, size_t keylen, + uint32_t *hashes) +{ + mi_vector_hash(key, keylen, nbperf->seed[0], hashes); +} + +static void +mi_vector_hash_print_hash(struct nbperf *nbperf, const char *indent, + const char *key, const char *keylen, const char *hash) +{ + fprintf(nbperf->output, + "%smi_vector_hash(%s, %s, 0x%08" PRIx32 "U, %s);\n", + indent, key, keylen, nbperf->seed[0], hash); +} + +static void +set_hash(struct nbperf *nbperf, const char *arg) +{ + if (strcmp(arg, "mi_vector_hash") == 0) { + nbperf->hash_size = 3; + nbperf->seed_hash = mi_vector_hash_seed_hash; + nbperf->compute_hash = mi_vector_hash_compute; + nbperf->print_hash = mi_vector_hash_print_hash; + return; + } + if (nbperf->hash_size > NBPERF_MAX_HASH_SIZE) + errx(1, "Hash function creates too many output values"); + errx(1, "Unknown hash function: %s", arg); +} + +int +main(int argc, char **argv) +{ + struct nbperf nbperf = { + .c = 0, + .hash_name = "hash", + .map_output = NULL, + .output = NULL, + .static_hash = 0, + .first_round = 1, + .has_duplicates = 0, + }; + FILE *input; + size_t curlen = 0, curalloc = 0; + char *line, *eos; + ssize_t line_len; + size_t line_allocated; + const void **keys = NULL; + size_t *keylens = NULL; + uint32_t max_iterations = 0xffffffU; + long long tmp; + int looped, ch; + int (*build_hash)(struct nbperf *) = chm_compute; + + set_hash(&nbperf, "mi_vector_hash"); + + while ((ch = getopt(argc, argv, "a:c:h:i:m:n:o:ps")) != -1) { + switch (ch) { + case 'a': + /* Accept bdz as alias for netbsd-6 compat. */ + if (strcmp(optarg, "chm") == 0) + build_hash = chm_compute; + else if (strcmp(optarg, "chm3") == 0) + build_hash = chm3_compute; + else if (strcmp(optarg, "bpz") == 0 || + strcmp(optarg, "bdz") == 0) + build_hash = bpz_compute; + else + errx(1, "Unsupport algorithm: %s", optarg); + break; + case 'c': + errno = 0; + nbperf.c = strtod(optarg, &eos); + if (errno || eos[0] || !nbperf.c) + errx(2, "Invalid argument for -c"); + break; + case 'h': + set_hash(&nbperf, optarg); + break; + case 'i': + errno = 0; + tmp = strtoll(optarg, &eos, 0); + if (errno || eos == optarg || eos[0] || + tmp < 0 || tmp > 0xffffffffU) + errx(2, "Iteration count must be " + "a 32bit integer"); + max_iterations = (uint32_t)tmp; + break; + case 'm': + if (nbperf.map_output) + fclose(nbperf.map_output); + nbperf.map_output = fopen(optarg, "w"); + if (nbperf.map_output == NULL) + err(2, "cannot open map file"); + break; + case 'n': + nbperf.hash_name = optarg; + break; + case 'o': + if (nbperf.output) + fclose(nbperf.output); + nbperf.output = fopen(optarg, "w"); + if (nbperf.output == NULL) + err(2, "cannot open output file"); + break; + case 'p': + predictable = 1; + break; + case 's': + nbperf.static_hash = 1; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + if (argc == 1) { + input = fopen(argv[0], "r"); + if (input == NULL) + err(1, "can't open input file"); + } else + input = stdin; + + if (nbperf.output == NULL) + nbperf.output = stdout; + + line = NULL; + line_allocated = 0; + while ((line_len = getline(&line, &line_allocated, input)) != -1) { + if (line_len && line[line_len - 1] == '\n') + --line_len; + if (curlen == curalloc) { + if (curalloc < 256) + curalloc = 256; + else + curalloc += curalloc; + keys = realloc(keys, curalloc * sizeof(*keys)); + if (keys == NULL) + err(1, "realloc failed"); + keylens = realloc(keylens, + curalloc * sizeof(*keylens)); + if (keylens == NULL) + err(1, "realloc failed"); + } + if ((keys[curlen] = strndup(line, line_len)) == NULL) + err(1, "malloc failed"); + keylens[curlen] = line_len; + ++curlen; + } + free(line); + + if (input != stdin) + fclose(input); + + nbperf.n = curlen; + nbperf.keys = keys; + nbperf.keylens = keylens; + + looped = 0; + while ((*build_hash)(&nbperf)) { + if (nbperf.has_duplicates) + errx(1, "Duplicate keys detected"); + fputc('.', stderr); + looped = 1; + if (max_iterations == 0xffffffffU) + continue; + if (--max_iterations == 0) { + fputc('\n', stderr); + errx(1, "Iteration count reached"); + } + } + if (looped) + fputc('\n', stderr); + + return 0; +} Index: contrib/mg/paragraph.c =================================================================== --- /dev/null +++ contrib/mg/paragraph.c @@ -0,0 +1,499 @@ +/* $OpenBSD: paragraph.c,v 1.46 2018/11/17 09:52:34 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Code for dealing with paragraphs and filling. Adapted from MicroEMACS 3.6 + * and GNU-ified by mwm@ucbvax. Several bug fixes by blarson@usc-oberon. + */ + +#include +#include +#include +#include +#include +#include + +#include "def.h" + +static int fillcol = 70; + +#define MAXWORD 256 + +static int findpara(void); +static int do_gotoeop(int, int, int *); + +/* + * Move to start of paragraph. + * Move backwards by line, checking from the 1st character forwards for the + * existence a non-space. If a non-space character is found, move to the + * preceding line. Keep doing this until a line with only spaces is found or + * the start of buffer. + */ +/* ARGSUSED */ +int +gotobop(int f, int n) +{ + int col, nospace; + + /* the other way... */ + if (n < 0) + return (gotoeop(f, -n)); + + while (n-- > 0) { + nospace = 0; + while (lback(curwp->w_dotp) != curbp->b_headp) { + curwp->w_doto = 0; + col = 0; + + while (col < llength(curwp->w_dotp) && + (isspace(lgetc(curwp->w_dotp, col)))) + col++; + + if (col >= llength(curwp->w_dotp)) { + if (nospace) + break; + } else + nospace = 1; + + curwp->w_dotline--; + curwp->w_dotp = lback(curwp->w_dotp); + } + } + /* force screen update */ + curwp->w_rflag |= WFMOVE; + return (TRUE); +} + +/* + * Move to end of paragraph. + * See comments for gotobop(). Same, but moving forwards. + */ +/* ARGSUSED */ +int +gotoeop(int f, int n) +{ + int i; + + return(do_gotoeop(f, n, &i)); +} + +int +do_gotoeop(int f, int n, int *i) +{ + int col, nospace, j = 0; + + /* the other way... */ + if (n < 0) + return (gotobop(f, -n)); + + /* for each one asked for */ + while (n-- > 0) { + *i = ++j; + nospace = 0; + while (lforw(curwp->w_dotp) != curbp->b_headp) { + col = 0; + curwp->w_doto = 0; + + while (col < llength(curwp->w_dotp) && + (isspace(lgetc(curwp->w_dotp, col)))) + col++; + + if (col >= llength(curwp->w_dotp)) { + if (nospace) + break; + } else + nospace = 1; + + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_dotline++; + + } + } + /* do not continue after end of buffer */ + if (lforw(curwp->w_dotp) == curbp->b_headp) { + gotoeol(FFRAND, 1); + curwp->w_rflag |= WFMOVE; + return (FALSE); + } + + /* force screen update */ + curwp->w_rflag |= WFMOVE; + return (TRUE); +} + +/* + * Justify a paragraph. Fill the current paragraph according to the current + * fill column. + */ +/* ARGSUSED */ +int +fillpara(int f, int n) +{ + int c; /* current char during scan */ + int wordlen; /* length of current word */ + int clength; /* position on line during fill */ + int i; /* index during word copy */ + int eopflag; /* Are we at the End-Of-Paragraph? */ + int firstflag; /* first word? (needs no space) */ + int newlength; /* tentative new line length */ + int eolflag; /* was at end of line */ + int retval; /* return value */ + struct line *eopline; /* pointer to line just past EOP */ + char wbuf[MAXWORD]; /* buffer for current word */ + + if (n == 0) + return (TRUE); + + undo_boundary_enable(FFRAND, 0); + + /* record the pointer to the line just past the EOP */ + (void)gotoeop(FFRAND, 1); + if (curwp->w_doto != 0) { + /* paragraph ends at end of buffer */ + (void)lnewline(); + eopline = lforw(curwp->w_dotp); + } else + eopline = curwp->w_dotp; + + /* and back top the beginning of the paragraph */ + (void)gotobop(FFRAND, 1); + + /* initialize various info */ + while (inword() == 0 && forwchar(FFRAND, 1)); + + clength = curwp->w_doto; + wordlen = 0; + + /* scan through lines, filling words */ + firstflag = TRUE; + eopflag = FALSE; + while (!eopflag) { + + /* get the next character in the paragraph */ + if ((eolflag = (curwp->w_doto == llength(curwp->w_dotp)))) { + c = ' '; + if (lforw(curwp->w_dotp) == eopline) + eopflag = TRUE; + } else + c = lgetc(curwp->w_dotp, curwp->w_doto); + + /* and then delete it */ + if (ldelete((RSIZE) 1, KNONE) == FALSE && !eopflag) { + retval = FALSE; + goto cleanup; + } + + /* if not a separator, just add it in */ + if (c != ' ' && c != '\t') { + if (wordlen < MAXWORD - 1) + wbuf[wordlen++] = c; + else { + /* + * You lose chars beyond MAXWORD if the word + * is too long. I'm too lazy to fix it now; it + * just silently truncated the word before, + * so I get to feel smug. + */ + ewprintf("Word too long!"); + } + } else if (wordlen) { + + /* calculate tentative new length with word added */ + newlength = clength + 1 + wordlen; + + /* + * if at end of line or at doublespace and previous + * character was one of '.','?','!' doublespace here. + * behave the same way if a ')' is preceded by a + * [.?!] and followed by a doublespace. + */ + if (dblspace && (!eopflag && ((eolflag || + curwp->w_doto == llength(curwp->w_dotp) || + (c = lgetc(curwp->w_dotp, curwp->w_doto)) == ' ' + || c == '\t') && (ISEOSP(wbuf[wordlen - 1]) || + (wbuf[wordlen - 1] == ')' && wordlen >= 2 && + ISEOSP(wbuf[wordlen - 2])))) && + wordlen < MAXWORD - 1)) + wbuf[wordlen++] = ' '; + + /* at a word break with a word waiting */ + if (newlength <= fillcol) { + /* add word to current line */ + if (!firstflag) { + (void)linsert(1, ' '); + ++clength; + } + firstflag = FALSE; + } else { + if (curwp->w_doto > 0 && + lgetc(curwp->w_dotp, curwp->w_doto - 1) == ' ') { + curwp->w_doto -= 1; + (void)ldelete((RSIZE) 1, KNONE); + } + /* start a new line */ + (void)lnewline(); + clength = 0; + } + + /* and add the word in in either case */ + for (i = 0; i < wordlen; i++) { + (void)linsert(1, wbuf[i]); + ++clength; + } + wordlen = 0; + } + } + /* and add a last newline for the end of our new paragraph */ + (void)lnewline(); + + /* + * We really should wind up where we started, (which is hard to keep + * track of) but I think the end of the last line is better than the + * beginning of the blank line. + */ + (void)backchar(FFRAND, 1); + retval = TRUE; +cleanup: + undo_boundary_enable(FFRAND, 1); + return (retval); +} + +/* + * Delete n paragraphs. Move to the beginning of the current paragraph, or if + * the cursor is on an empty line, move down the buffer to the first line with + * non-space characters. Then mark n paragraphs and delete. + */ +/* ARGSUSED */ +int +killpara(int f, int n) +{ + int lineno, status; + + if (n == 0) + return (TRUE); + + if (findpara() == FALSE) + return (TRUE); + + /* go to the beginning of the paragraph */ + (void)gotobop(FFRAND, 1); + + /* take a note of the line number for after deletions and set mark */ + lineno = curwp->w_dotline; + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + + (void)gotoeop(FFRAND, n); + + if ((status = killregion(FFRAND, 1)) != TRUE) + return (status); + + curwp->w_dotline = lineno; + return (TRUE); +} + +/* + * Mark n paragraphs starting with the n'th and working our way backwards. + * This leaves the cursor at the beginning of the paragraph where markpara() + * was invoked. + */ +/* ARGSUSED */ +int +markpara(int f, int n) +{ + int i = 0; + + if (n == 0) + return (TRUE); + + clearmark(FFARG, 0); + + if (findpara() == FALSE) + return (TRUE); + + (void)do_gotoeop(FFRAND, n, &i); + + /* set the mark here */ + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + + (void)gotobop(FFRAND, i); + + return (TRUE); +} + +/* + * Transpose the current paragraph with the following paragraph. If invoked + * multiple times, transpose to the n'th paragraph. If invoked between + * paragraphs, move to the previous paragraph, then continue. + */ +/* ARGSUSED */ +int +transposepara(int f, int n) +{ + int i = 0, status; + char flg; + + if (n == 0) + return (TRUE); + + undo_boundary_enable(FFRAND, 0); + + /* find a paragraph, set mark, then goto the end */ + gotobop(FFRAND, 1); + curwp->w_markp = curwp->w_dotp; + curwp->w_marko = curwp->w_doto; + (void)gotoeop(FFRAND, 1); + + /* take a note of buffer flags - we may need them */ + flg = curbp->b_flag; + + /* clean out kill buffer then kill region */ + kdelete(); + if ((status = killregion(FFRAND, 1)) != TRUE) + return (status); + + /* + * Now step through n paragraphs. If we reach the end of buffer, + * stop and paste the killed region back, then display a message. + */ + if (do_gotoeop(FFRAND, n, &i) == FALSE) { + ewprintf("Cannot transpose paragraph, end of buffer reached."); + (void)gotobop(FFRAND, i); + (void)yank(FFRAND, 1); + curbp->b_flag = flg; + return (FALSE); + } + (void)yank(FFRAND, 1); + + undo_boundary_enable(FFRAND, 1); + + return (TRUE); +} + +/* + * Go down the buffer until we find a line with non-space characters. + */ +int +findpara(void) +{ + int col, nospace = 0; + + /* we move forward to find a para to mark */ + do { + curwp->w_doto = 0; + col = 0; + + /* check if we are on a blank line */ + while (col < llength(curwp->w_dotp)) { + if (!isspace(lgetc(curwp->w_dotp, col))) + nospace = 1; + col++; + } + if (nospace) + break; + + if (lforw(curwp->w_dotp) == curbp->b_headp) + return (FALSE); + + curwp->w_dotp = lforw(curwp->w_dotp); + curwp->w_dotline++; + } while (1); + + return (TRUE); +} + +/* + * Insert char with work wrap. Check to see if we're past fillcol, and if so, + * justify this line. As a last step, justify the line. + */ +/* ARGSUSED */ +int +fillword(int f, int n) +{ + char c; + int col, i, nce; + + for (i = col = 0; col <= fillcol; ++i, ++col) { + if (i == curwp->w_doto) + return selfinsert(f, n); + c = lgetc(curwp->w_dotp, i); + if (c == '\t' +#ifdef NOTAB + && !(curbp->b_flag & BFNOTAB) +#endif + ) + col |= 0x07; + else if (ISCTRL(c) != FALSE) + ++col; + } + if (curwp->w_doto != llength(curwp->w_dotp)) { + (void)selfinsert(f, n); + nce = llength(curwp->w_dotp) - curwp->w_doto; + } else + nce = 0; + curwp->w_doto = i; + + if ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && c != '\t') + do { + (void)backchar(FFRAND, 1); + } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && + c != '\t' && curwp->w_doto > 0); + + if (curwp->w_doto == 0) + do { + (void)forwchar(FFRAND, 1); + } while ((c = lgetc(curwp->w_dotp, curwp->w_doto)) != ' ' && + c != '\t' && curwp->w_doto < llength(curwp->w_dotp)); + + (void)delwhite(FFRAND, 1); + (void)lnewline(); + i = llength(curwp->w_dotp) - nce; + curwp->w_doto = i > 0 ? i : 0; + curwp->w_rflag |= WFMOVE; + if (nce == 0 && curwp->w_doto != 0) + return (fillword(f, n)); + return (TRUE); +} + +/* + * Set fill column to n for justify. + */ +int +setfillcol(int f, int n) +{ + char buf[32], *rep; + const char *es; + int nfill; + + if ((f & FFARG) != 0) { + fillcol = n; + } else { + if ((rep = eread("Set fill-column: ", buf, sizeof(buf), + EFNEW | EFCR)) == NULL) + return (ABORT); + else if (rep[0] == '\0') + return (FALSE); + nfill = strtonum(rep, 0, INT_MAX, &es); + if (es != NULL) { + dobeep(); + ewprintf("Invalid fill column: %s", rep); + return (FALSE); + } + fillcol = nfill; + ewprintf("Fill column set to %d", fillcol); + } + return (TRUE); +} + +int +sentencespace(int f, int n) +{ + if (f & FFARG) + dblspace = n > 1; + else + dblspace = !dblspace; + + return (TRUE); +} Index: contrib/mg/pathnames.h =================================================================== --- /dev/null +++ contrib/mg/pathnames.h @@ -0,0 +1,11 @@ +/* $OpenBSD: pathnames.h,v 1.1 2012/06/18 07:14:55 jasper Exp $ */ + +/* This file is in the public domain. */ + +/* + * standard path names + */ + +#define _PATH_MG_DIR "~/.mg.d" +#define _PATH_MG_STARTUP "%s/.mg" +#define _PATH_MG_TERM "%s/.mg-%s" Index: contrib/mg/re_search.c =================================================================== --- /dev/null +++ contrib/mg/re_search.c @@ -0,0 +1,665 @@ +/* $OpenBSD: re_search.c,v 1.36 2021/04/22 19:50:55 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * regular expression search commands for Mg + * + * This file contains functions to implement several of gnuemacs's regular + * expression functions for Mg. Several of the routines below are just minor + * re-arrangements of Mg's non-regular expression search functions. Some of + * them are similar in structure to the original MicroEMACS, others are + * modifications of Rich Ellison's code. Peter Newton re-wrote about half of + * them from scratch. + */ + +#ifdef REGEX +#include +#include +#include +#include +#include +#include + +#include "def.h" +#include "macro.h" + +#define SRCH_BEGIN (0) /* search sub-codes */ +#define SRCH_FORW (-1) +#define SRCH_BACK (-2) +#define SRCH_NOPR (-3) +#define SRCH_ACCM (-4) +#define SRCH_MARK (-5) + +#define RE_NMATCH 10 /* max number of matches */ +#define REPLEN 256 /* max length of replacement string */ + +char re_pat[NPAT]; /* regex pattern */ +int re_srch_lastdir = SRCH_NOPR; /* last search flags */ +int casefoldsearch = TRUE; /* does search ignore case? */ + +static int re_doreplace(RSIZE, char *); +static int re_forwsrch(void); +static int re_backsrch(void); +static int re_readpattern(char *); +static int killmatches(int); +static int countmatches(int); + +/* + * Search forward. + * Get a search string from the user and search for it starting at ".". If + * found, move "." to just after the matched characters. display does all + * the hard stuff. If not found, it just prints a message. + */ +/* ARGSUSED */ +int +re_forwsearch(int f, int n) +{ + int s; + + if ((s = re_readpattern("RE Search")) != TRUE) + return (s); + if (re_forwsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", re_pat); + return (FALSE); + } + re_srch_lastdir = SRCH_FORW; + return (TRUE); +} + +/* + * Reverse search. + * Get a search string from the user, and search, starting at "." + * and proceeding toward the front of the buffer. If found "." is left + * pointing at the first character of the pattern [the last character that + * was matched]. + */ +/* ARGSUSED */ +int +re_backsearch(int f, int n) +{ + int s; + + if ((s = re_readpattern("RE Search backward")) != TRUE) + return (s); + if (re_backsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", re_pat); + return (FALSE); + } + re_srch_lastdir = SRCH_BACK; + return (TRUE); +} + +/* + * Search again, using the same search string and direction as the last search + * command. The direction has been saved in "srch_lastdir", so you know which + * way to go. + * + * XXX: This code has problems -- some incompatibility(?) with extend.c causes + * match to fail when it should not. + */ +/* ARGSUSED */ +int +re_searchagain(int f, int n) +{ + if (re_srch_lastdir == SRCH_NOPR) { + dobeep(); + ewprintf("No last search"); + return (FALSE); + } + if (re_srch_lastdir == SRCH_FORW) { + if (re_forwsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", re_pat); + return (FALSE); + } + return (TRUE); + } + if (re_srch_lastdir == SRCH_BACK) + if (re_backsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", re_pat); + return (FALSE); + } + + return (TRUE); +} + +/* Compiled regex goes here-- changed only when new pattern read */ +static regex_t regex_buff; +static regmatch_t regex_match[RE_NMATCH]; + +/* + * Re-Query Replace. + * Replace strings selectively. Does a search and replace operation. + */ +/* ARGSUSED */ +int +re_queryrepl(int f, int n) +{ + int rcnt = 0; /* replacements made so far */ + int plen, s; /* length of found string */ + char news[NPAT]; /* replacement string */ + + if ((s = re_readpattern("RE Query replace")) != TRUE) + return (s); + if (eread("Query replace %s with: ", news, NPAT, + EFNUL | EFNEW | EFCR, re_pat) == NULL) + return (ABORT); + ewprintf("Query replacing %s with %s:", re_pat, news); + + /* + * Search forward repeatedly, checking each time whether to insert + * or not. The "!" case makes the check always true, so it gets put + * into a tighter loop for efficiency. + */ + while (re_forwsrch() == TRUE) { +retry: + update(CMODE); + switch (getkey(FALSE)) { + case ' ': + plen = regex_match[0].rm_eo - regex_match[0].rm_so; + if (re_doreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + break; + + case '.': + plen = regex_match[0].rm_eo - regex_match[0].rm_so; + if (re_doreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + goto stopsearch; + + case CCHR('G'): /* ^G */ + (void)ctrlg(FFRAND, 0); + goto stopsearch; + case CCHR('['): /* ESC */ + case '`': + goto stopsearch; + case '!': + do { + plen = regex_match[0].rm_eo - regex_match[0].rm_so; + if (re_doreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + } while (re_forwsrch() == TRUE); + goto stopsearch; + + case CCHR('?'): /* To not replace */ + break; + + default: + ewprintf(" replace, [.] rep-end, don't, [!] repl rest quit"); + goto retry; + } + } + +stopsearch: + curwp->w_rflag |= WFFULL; + update(CMODE); + if (!inmacro) { + if (rcnt == 0) + ewprintf("(No replacements done)"); + else if (rcnt == 1) + ewprintf("(1 replacement done)"); + else + ewprintf("(%d replacements done)", rcnt); + } + return (TRUE); +} + +int +re_repl(int f, int n) +{ + int rcnt = 0; /* replacements made so far */ + int plen, s; /* length of found string */ + char news[NPAT]; /* replacement string */ + + if ((s = re_readpattern("RE Replace")) != TRUE) + return (s); + if (eread("Replace %s with: ", news, NPAT, + EFNUL | EFNEW | EFCR, re_pat) == NULL) + return (ABORT); + + while (re_forwsrch() == TRUE) { + plen = regex_match[0].rm_eo - regex_match[0].rm_so; + if (re_doreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + } + + curwp->w_rflag |= WFFULL; + update(CMODE); + if (!inmacro) + ewprintf("(%d replacement(s) done)", rcnt); + + return(TRUE); +} + +/* + * Routine re_doreplace calls lreplace to make replacements needed by + * re_query replace. Its reason for existence is to deal with \1, \2. etc. + * plen: length to remove + * st: replacement string + */ +static int +re_doreplace(RSIZE plen, char *st) +{ + int j, k, s, more, num, state; + struct line *clp; + char repstr[REPLEN]; + + clp = curwp->w_dotp; + more = TRUE; + j = 0; + state = 0; + num = 0; + + /* The following FSA parses the replacement string */ + while (more) { + switch (state) { + case 0: + if (*st == '\\') { + st++; + state = 1; + } else if (*st == '\0') + more = FALSE; + else { + repstr[j] = *st; + j++; + if (j >= REPLEN) + return (FALSE); + st++; + } + break; + case 1: + if (*st >= '0' && *st <= '9') { + num = *st - '0'; + st++; + state = 2; + } else if (*st == '\0') + more = FALSE; + else { + repstr[j] = *st; + j++; + if (j >= REPLEN) + return (FALSE); + st++; + state = 0; + } + break; + case 2: + if (*st >= '0' && *st <= '9') { + num = 10 * num + *st - '0'; + st++; + } else { + if (num >= RE_NMATCH) + return (FALSE); + k = regex_match[num].rm_eo - regex_match[num].rm_so; + if (j + k >= REPLEN) + return (FALSE); + bcopy(&(clp->l_text[regex_match[num].rm_so]), + &repstr[j], k); + j += k; + if (*st == '\0') + more = FALSE; + if (*st == '\\') { + st++; + state = 1; + } else { + repstr[j] = *st; + j++; + if (j >= REPLEN) + return (FALSE); + st++; + state = 0; + } + } + break; + } /* switch (state) */ + } /* while (more) */ + + repstr[j] = '\0'; + s = lreplace(plen, repstr); + return (s); +} + +/* + * This routine does the real work of a forward search. The pattern is + * sitting in the external variable "pat". If found, dot is updated, the + * window system is notified of the change, and TRUE is returned. If the + * string isn't found, FALSE is returned. + */ +static int +re_forwsrch(void) +{ + int re_flags, tbo, tdotline, error; + struct line *clp; + + clp = curwp->w_dotp; + tbo = curwp->w_doto; + tdotline = curwp->w_dotline; + + if (tbo == clp->l_used) + /* + * Don't start matching past end of line -- must move to + * beginning of next line, unless line is empty or at + * end of file. + */ + if (clp != curbp->b_headp && llength(clp) != 0) { + clp = lforw(clp); + tdotline++; + tbo = 0; + } + /* + * Note this loop does not process the last line, but this editor + * always makes the last line empty so this is good. + */ + while (clp != (curbp->b_headp)) { + re_flags = REG_STARTEND; + if (tbo != 0) + re_flags |= REG_NOTBOL; + regex_match[0].rm_so = tbo; + regex_match[0].rm_eo = llength(clp); + error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "", + RE_NMATCH, regex_match, re_flags); + if (error != 0) { + clp = lforw(clp); + tdotline++; + tbo = 0; + } else { + curwp->w_doto = regex_match[0].rm_eo; + curwp->w_dotp = clp; + curwp->w_dotline = tdotline; + curwp->w_rflag |= WFMOVE; + return (TRUE); + } + } + return (FALSE); +} + +/* + * This routine does the real work of a backward search. The pattern is sitting + * in the external variable "re_pat". If found, dot is updated, the window + * system is notified of the change, and TRUE is returned. If the string isn't + * found, FALSE is returned. + */ +static int +re_backsrch(void) +{ + struct line *clp; + int tbo, tdotline; + regmatch_t lastmatch; + + clp = curwp->w_dotp; + tbo = curwp->w_doto; + tdotline = curwp->w_dotline; + + /* Start search one position to the left of dot */ + tbo = tbo - 1; + if (tbo < 0) { + /* must move up one line */ + clp = lback(clp); + tdotline--; + tbo = llength(clp); + } + + /* + * Note this loop does not process the last line, but this editor + * always makes the last line empty so this is good. + */ + while (clp != (curbp->b_headp)) { + regex_match[0].rm_so = 0; + regex_match[0].rm_eo = llength(clp); + lastmatch.rm_so = -1; + /* + * Keep searching until we don't match any longer. Assumes a + * non-match does not modify the regex_match array. We have to + * do this character-by-character after the first match since + * POSIX regexps don't give you a way to do reverse matches. + */ + while (!regexec(®ex_buff, ltext(clp) ? ltext(clp) : "", + RE_NMATCH, regex_match, REG_STARTEND) && + regex_match[0].rm_so <= tbo) { + memcpy(&lastmatch, ®ex_match[0], sizeof(regmatch_t)); + regex_match[0].rm_so++; + regex_match[0].rm_eo = llength(clp); + } + if (lastmatch.rm_so == -1) { + clp = lback(clp); + tdotline--; + tbo = llength(clp); + } else { + memcpy(®ex_match[0], &lastmatch, sizeof(regmatch_t)); + curwp->w_doto = regex_match[0].rm_so; + curwp->w_dotp = clp; + curwp->w_dotline = tdotline; + curwp->w_rflag |= WFMOVE; + return (TRUE); + } + } + return (FALSE); +} + +/* + * Read a pattern. + * Stash it in the external variable "re_pat". The "pat" is + * not updated if the user types in an empty line. If the user typed + * an empty line, and there is no old pattern, it is an error. + * Display the old pattern, in the style of Jeff Lomicka. There is + * some do-it-yourself control expansion. + */ +static int +re_readpattern(char *re_prompt) +{ + static int dofree = 0; + int flags, error, s; + char tpat[NPAT], *rep; + + if (re_pat[0] == '\0') + rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, re_prompt); + else + rep = eread("%s (default %s): ", tpat, NPAT, + EFNUL | EFNEW | EFCR, re_prompt, re_pat); + if (rep == NULL) + return (ABORT); + if (rep[0] != '\0') { + /* New pattern given */ + (void)strlcpy(re_pat, tpat, sizeof(re_pat)); + if (casefoldsearch) + flags = REG_EXTENDED | REG_ICASE; + else + flags = REG_EXTENDED; + if (dofree) + regfree(®ex_buff); + error = regcomp(®ex_buff, re_pat, flags); + if (error != 0) { + char message[256]; + regerror(error, ®ex_buff, message, sizeof(message)); + dobeep(); + ewprintf("Regex Error: %s", message); + re_pat[0] = '\0'; + return (FALSE); + } + dofree = 1; + s = TRUE; + } else if (rep[0] == '\0' && re_pat[0] != '\0') + /* Just using old pattern */ + s = TRUE; + else + s = FALSE; + return (s); +} + +/* + * Cause case to not matter in searches. This is the default. If called + * with argument cause case to matter. + */ +/* ARGSUSED*/ +int +setcasefold(int f, int n) +{ + if (f & FFARG) { + casefoldsearch = FALSE; + ewprintf("Case-fold-search unset"); + } else { + casefoldsearch = TRUE; + ewprintf("Case-fold-search set"); + } + + /* + * Invalidate the regular expression pattern since I'm too lazy to + * recompile it. + */ + re_pat[0] = '\0'; + return (TRUE); +} + +/* + * Delete all lines after dot that contain a string matching regex. + */ +/* ARGSUSED */ +int +delmatchlines(int f, int n) +{ + int s; + + if ((s = re_readpattern("Flush lines (containing match for regexp)")) + != TRUE) + return (s); + + s = killmatches(TRUE); + return (s); +} + +/* + * Delete all lines after dot that don't contain a string matching regex. + */ +/* ARGSUSED */ +int +delnonmatchlines(int f, int n) +{ + int s; + + if ((s = re_readpattern("Keep lines (containing match for regexp)")) + != TRUE) + return (s); + + s = killmatches(FALSE); + return (s); +} + +/* + * This function does the work of deleting matching lines. + */ +static int +killmatches(int cond) +{ + int s, error; + int count = 0; + struct line *clp; + + clp = curwp->w_dotp; + if (curwp->w_doto == llength(clp)) + /* Consider dot on next line */ + clp = lforw(clp); + + while (clp != (curbp->b_headp)) { + /* see if line matches */ + regex_match[0].rm_so = 0; + regex_match[0].rm_eo = llength(clp); + error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "", + RE_NMATCH, regex_match, REG_STARTEND); + + /* Delete line when appropriate */ + if ((cond == FALSE && error) || (cond == TRUE && !error)) { + curwp->w_doto = 0; + curwp->w_dotp = clp; + count++; + s = ldelete(llength(clp) + 1, KNONE); + clp = curwp->w_dotp; + curwp->w_rflag |= WFMOVE; + if (s == FALSE) + return (FALSE); + } else + clp = lforw(clp); + } + + ewprintf("%d line(s) deleted", count); + if (count > 0) + curwp->w_rflag |= WFMOVE; + + return (TRUE); +} + +/* + * Count lines matching regex. + */ +/* ARGSUSED */ +int +cntmatchlines(int f, int n) +{ + int s; + + if ((s = re_readpattern("Count lines (matching regexp)")) != TRUE) + return (s); + s = countmatches(TRUE); + + return (s); +} + +/* + * Count lines that fail to match regex. + */ +/* ARGSUSED */ +int +cntnonmatchlines(int f, int n) +{ + int s; + + if ((s = re_readpattern("Count lines (not matching regexp)")) != TRUE) + return (s); + s = countmatches(FALSE); + + return (s); +} + +/* + * This function does the work of counting matching lines. + */ +int +countmatches(int cond) +{ + int error; + int count = 0; + struct line *clp; + + clp = curwp->w_dotp; + if (curwp->w_doto == llength(clp)) + /* Consider dot on next line */ + clp = lforw(clp); + + while (clp != (curbp->b_headp)) { + /* see if line matches */ + regex_match[0].rm_so = 0; + regex_match[0].rm_eo = llength(clp); + error = regexec(®ex_buff, ltext(clp) ? ltext(clp) : "", + RE_NMATCH, regex_match, REG_STARTEND); + + /* Count line when appropriate */ + if ((cond == FALSE && error) || (cond == TRUE && !error)) + count++; + clp = lforw(clp); + } + + if (cond) + ewprintf("Number of lines matching: %d", count); + else + ewprintf("Number of lines not matching: %d", count); + + return (TRUE); +} +#endif /* REGEX */ Index: contrib/mg/reallocarray.c =================================================================== --- /dev/null +++ contrib/mg/reallocarray.c @@ -0,0 +1,44 @@ +/* $OpenBSD: reallocarray.c,v 1.2 2014/12/08 03:45:00 bcook Exp $ */ +/* + * Copyright (c) 2008 Otto Moerbeek + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include + +#include "config.h" + +#ifndef HAVE_REALLOCARRAY + +/* + * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX + * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW + */ +#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) + +void * +reallocarray(void *optr, size_t nmemb, size_t size) +{ + if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && + nmemb > 0 && SIZE_MAX / nmemb < size) { + errno = ENOMEM; + return NULL; + } + return realloc(optr, size * nmemb); +} + +#endif /* !HAVE_REALLOCARRAY */ Index: contrib/mg/region.c =================================================================== --- /dev/null +++ contrib/mg/region.c @@ -0,0 +1,682 @@ +/* $OpenBSD: region.c,v 1.39 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Region based commands. + * The routines in this file deal with the region, that magic space between + * "." and mark. Some functions are commands. Some functions are just for + * internal use. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +#define TIMEOUT 10000 + +static char leftover[BUFSIZ]; + +static int getregion(struct region *); +static int iomux(int, char * const, int, struct buffer *); +static int preadin(int, struct buffer *); +static void pwriteout(int, char **, int *); +static int setsize(struct region *, RSIZE); +static int shellcmdoutput(char * const[], char * const, int); + +/* + * Kill the region. Ask "getregion" to figure out the bounds of the region. + * Move "." to the start, and kill the characters. Mark is cleared afterwards. + */ +/* ARGSUSED */ +int +killregion(int f, int n) +{ + int s; + struct region region; + + if ((s = getregion(®ion)) != TRUE) + return (s); + /* This is a kill-type command, so do magic kill buffer stuff. */ + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + curwp->w_dotp = region.r_linep; + curwp->w_doto = region.r_offset; + curwp->w_dotline = region.r_lineno; + s = ldelete(region.r_size, KFORW | KREG); + clearmark(FFARG, 0); + + return (s); +} + +/* + * Copy all of the characters in the region to the kill buffer, + * clearing the mark afterwards. + * This is a bit like a kill region followed by a yank. + */ +/* ARGSUSED */ +int +copyregion(int f, int n) +{ + struct line *linep; + struct region region; + int loffs; + int s; + + if ((s = getregion(®ion)) != TRUE) + return (s); + + /* kill type command */ + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + + /* current line */ + linep = region.r_linep; + + /* current offset */ + loffs = region.r_offset; + + while (region.r_size--) { + if (loffs == llength(linep)) { /* End of line. */ + if ((s = kinsert(*curbp->b_nlchr, KFORW)) != TRUE) + return (s); + linep = lforw(linep); + loffs = 0; + } else { /* Middle of line. */ + if ((s = kinsert(lgetc(linep, loffs), KFORW)) != TRUE) + return (s); + ++loffs; + } + } + clearmark(FFARG, 0); + + return (TRUE); +} + +/* + * Lower case region. Zap all of the upper case characters in the region to + * lower case. Use the region code to set the limits. Scan the buffer, doing + * the changes. Call "lchange" to ensure that redisplay is done in all + * buffers. + */ +/* ARGSUSED */ +int +lowerregion(int f, int n) +{ + struct line *linep; + struct region region; + int loffs, c, s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + + if ((s = getregion(®ion)) != TRUE) + return (s); + + undo_add_change(region.r_linep, region.r_offset, region.r_size); + + lchange(WFFULL); + linep = region.r_linep; + loffs = region.r_offset; + while (region.r_size--) { + if (loffs == llength(linep)) { + linep = lforw(linep); + loffs = 0; + } else { + c = lgetc(linep, loffs); + if (ISUPPER(c) != FALSE) + lputc(linep, loffs, TOLOWER(c)); + ++loffs; + } + } + return (TRUE); +} + +/* + * Upper case region. Zap all of the lower case characters in the region to + * upper case. Use the region code to set the limits. Scan the buffer, + * doing the changes. Call "lchange" to ensure that redisplay is done in all + * buffers. + */ +/* ARGSUSED */ +int +upperregion(int f, int n) +{ + struct line *linep; + struct region region; + int loffs, c, s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + if ((s = getregion(®ion)) != TRUE) + return (s); + + undo_add_change(region.r_linep, region.r_offset, region.r_size); + + lchange(WFFULL); + linep = region.r_linep; + loffs = region.r_offset; + while (region.r_size--) { + if (loffs == llength(linep)) { + linep = lforw(linep); + loffs = 0; + } else { + c = lgetc(linep, loffs); + if (ISLOWER(c) != FALSE) + lputc(linep, loffs, TOUPPER(c)); + ++loffs; + } + } + return (TRUE); +} + +/* + * This routine figures out the bound of the region in the current window, + * and stores the results into the fields of the REGION structure. Dot and + * mark are usually close together, but I don't know the order, so I scan + * outward from dot, in both directions, looking for mark. The size is kept + * in a long. At the end, after the size is figured out, it is assigned to + * the size field of the region structure. If this assignment loses any bits, + * then we print an error. This is "type independent" overflow checking. All + * of the callers of this routine should be ready to get an ABORT status, + * because I might add a "if regions is big, ask before clobbering" flag. + */ +static int +getregion(struct region *rp) +{ + struct line *flp, *blp; + long fsize, bsize; + + if (curwp->w_markp == NULL) { + dobeep(); + ewprintf("No mark set in this window"); + return (FALSE); + } + + /* "r_size" always ok */ + if (curwp->w_dotp == curwp->w_markp) { + rp->r_linep = curwp->w_dotp; + rp->r_lineno = curwp->w_dotline; + if (curwp->w_doto < curwp->w_marko) { + rp->r_offset = curwp->w_doto; + rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto); + } else { + rp->r_offset = curwp->w_marko; + rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko); + } + return (TRUE); + } + /* get region size */ + flp = blp = curwp->w_dotp; + bsize = curwp->w_doto; + fsize = llength(flp) - curwp->w_doto + 1; + while (lforw(flp) != curbp->b_headp || lback(blp) != curbp->b_headp) { + if (lforw(flp) != curbp->b_headp) { + flp = lforw(flp); + if (flp == curwp->w_markp) { + rp->r_linep = curwp->w_dotp; + rp->r_offset = curwp->w_doto; + rp->r_lineno = curwp->w_dotline; + return (setsize(rp, + (RSIZE)(fsize + curwp->w_marko))); + } + fsize += llength(flp) + 1; + } + if (lback(blp) != curbp->b_headp) { + blp = lback(blp); + bsize += llength(blp) + 1; + if (blp == curwp->w_markp) { + rp->r_linep = blp; + rp->r_offset = curwp->w_marko; + rp->r_lineno = curwp->w_markline; + return (setsize(rp, + (RSIZE)(bsize - curwp->w_marko))); + } + } + } + dobeep(); + ewprintf("Bug: lost mark"); + return (FALSE); +} + +/* + * Set size, and check for overflow. + */ +static int +setsize(struct region *rp, RSIZE size) +{ + rp->r_size = size; + if (rp->r_size != size) { + dobeep(); + ewprintf("Region is too large"); + return (FALSE); + } + return (TRUE); +} + +#define PREFIXLENGTH 40 +static char prefix_string[PREFIXLENGTH] = {'>', '\0'}; + +/* + * Prefix the region with whatever is in prefix_string. Leaves dot at the + * beginning of the line after the end of the region. If an argument is + * given, prompts for the line prefix string. + */ +/* ARGSUSED */ +int +prefixregion(int f, int n) +{ + struct line *first, *last; + struct region region; + char *prefix = prefix_string; + int nline; + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + if ((f == TRUE) && ((s = setprefix(FFRAND, 1)) != TRUE)) + return (s); + + /* get # of lines to affect */ + if ((s = getregion(®ion)) != TRUE) + return (s); + first = region.r_linep; + last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp; + for (nline = 1; first != last; nline++) + first = lforw(first); + + /* move to beginning of region */ + curwp->w_dotp = region.r_linep; + curwp->w_doto = region.r_offset; + curwp->w_dotline = region.r_lineno; + + /* for each line, go to beginning and insert the prefix string */ + while (nline--) { + (void)gotobol(FFRAND, 1); + for (prefix = prefix_string; *prefix; prefix++) + (void)linsert(1, *prefix); + (void)forwline(FFRAND, 1); + } + (void)gotobol(FFRAND, 1); + return (TRUE); +} + +/* + * Set line prefix string. Used by prefixregion. + */ +/* ARGSUSED */ +int +setprefix(int f, int n) +{ + char buf[PREFIXLENGTH], *rep; + int retval; + + if (prefix_string[0] == '\0') + rep = eread("Prefix string: ", buf, sizeof(buf), + EFNEW | EFCR); + else + rep = eread("Prefix string (default %s): ", buf, sizeof(buf), + EFNUL | EFNEW | EFCR, prefix_string); + if (rep == NULL) + return (ABORT); + if (rep[0] != '\0') { + (void)strlcpy(prefix_string, rep, sizeof(prefix_string)); + retval = TRUE; + } else if (rep[0] == '\0' && prefix_string[0] != '\0') { + /* CR -- use old one */ + retval = TRUE; + } else + retval = FALSE; + return (retval); +} + +int +region_get_data(struct region *reg, char *buf, int len) +{ + int i, off; + struct line *lp; + + off = reg->r_offset; + lp = reg->r_linep; + for (i = 0; i < len; i++) { + if (off == llength(lp)) { + lp = lforw(lp); + if (lp == curbp->b_headp) + break; + off = 0; + buf[i] = *curbp->b_nlchr; + } else { + buf[i] = lgetc(lp, off); + off++; + } + } + buf[i] = '\0'; + return (i); +} + +void +region_put_data(const char *buf, int len) +{ + int i; + + for (i = 0; buf[i] != '\0' && i < len; i++) { + if (buf[i] == *curbp->b_nlchr) + lnewline(); + else + linsert(1, buf[i]); + } +} + +/* + * Mark whole buffer by first traversing to end-of-buffer + * and then to beginning-of-buffer. Mark, dot are implicitly + * set to eob, bob respectively during traversal. + */ +int +markbuffer(int f, int n) +{ + if (gotoeob(f,n) == FALSE) + return (FALSE); + (void) clearmark(f, n); + if (gotobob(f,n) == FALSE) + return (FALSE); + return (TRUE); +} + +/* + * Pipe text from current region to external command. + */ +/*ARGSUSED */ +int +piperegion(int f, int n) +{ + struct region region; + int len; + char *cmd, cmdbuf[NFILEN], *text; + char *argv[] = {"sh", "-c", (char *) NULL, (char *) NULL}; + + /* C-u M-| is not supported yet */ + if (n > 1) + return (ABORT); + + if (curwp->w_markp == NULL) { + dobeep(); + ewprintf("The mark is not set now, so there is no region"); + return (FALSE); + } + + if ((cmd = eread("Shell command on region: ", cmdbuf, sizeof(cmdbuf), + EFNEW | EFCR)) == NULL || (cmd[0] == '\0')) + return (ABORT); + + argv[2] = cmd; + + if (getregion(®ion) != TRUE) + return (FALSE); + + len = region.r_size; + + if ((text = malloc(len + 1)) == NULL) { + dobeep(); + ewprintf("Cannot allocate memory."); + return (FALSE); + } + + region_get_data(®ion, text, len); + + return shellcmdoutput(argv, text, len); +} + +/* + * Get command from mini-buffer and execute externally. + */ +/*ARGSUSED */ +int +shellcommand(int f, int n) +{ + + char *cmd, cmdbuf[NFILEN]; + char *argv[] = {"sh", "-c", (char *) NULL, (char *) NULL}; + + if (n > 1) + return (ABORT); + + if ((cmd = eread("Shell command: ", cmdbuf, sizeof(cmdbuf), + EFNEW | EFCR)) == NULL || (cmd[0] == '\0')) + return (ABORT); + + argv[2] = cmd; + + return shellcmdoutput(argv, NULL, 0); +} + + +int +shellcmdoutput(char* const argv[], char* const text, int len) +{ + + struct buffer *bp; + char *shellp; + int ret; + + bp = bfind("*Shell Command Output*", TRUE); + bp->b_flag |= BFREADONLY; + if (bclear(bp) != TRUE) { + free(text); + return (FALSE); + } + + shellp = getenv("SHELL"); + + ret = pipeio(shellp, argv, text, len, bp); + + if (ret == TRUE) { + eerase(); + if (lforw(bp->b_headp) == bp->b_headp) + addline(bp, "(Shell command succeeded with no output)"); + } + + free(text); + return (ret); +} + +/* + * Create a socketpair, fork and execv path with argv. + * STDIN, STDOUT and STDERR of child process are redirected to socket. + * Parent writes len chars from text to socket. + */ +int +pipeio(const char* const path, char* const argv[], char* const text, int len, + struct buffer *outbp) +{ + int s[2], ret; + char *err; + pid_t pid; + + if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, s) == -1) { + dobeep(); + ewprintf("socketpair error"); + return (FALSE); + } + + switch((pid = fork())) { + case -1: + dobeep(); + ewprintf("Can't fork"); + return (FALSE); + case 0: + /* Child process */ + close(s[0]); + if (dup2(s[1], STDIN_FILENO) == -1) + _exit(1); + if (dup2(s[1], STDOUT_FILENO) == -1) + _exit(1); + if (dup2(s[1], STDERR_FILENO) == -1) + _exit(1); + if (path == NULL) + _exit(1); + + execv(path, argv); + err = strerror(errno); + write(s[1], err, strlen(err)); + _exit(1); + default: + /* Parent process */ + close(s[1]); + ret = iomux(s[0], text, len, outbp); + waitpid(pid, NULL, 0); /* Collect child to prevent zombies */ + + return (ret); + } + return (FALSE); +} + +/* + * Multiplex read, write on socket fd passed. Put output in outbp + * Poll on the fd for both read and write readiness. + */ +int +iomux(int fd, char* const text, int len, struct buffer *outbp) +{ + struct pollfd pfd[1]; + int nfds; + char *textcopy; + + textcopy = text; + fcntl(fd, F_SETFL, O_NONBLOCK); + pfd[0].fd = fd; + + /* There is nothing to write if len is zero + * but the cmd's output should be read so shutdown + * the socket for writing only and don't wait for POLLOUT + */ + if (len == 0) { + shutdown(fd, SHUT_WR); + pfd[0].events = POLLIN; + } else + pfd[0].events = POLLIN | POLLOUT; + + while ((nfds = poll(pfd, 1, TIMEOUT)) != -1 || + (pfd[0].revents & (POLLERR | POLLHUP | POLLNVAL))) { + if (pfd[0].revents & POLLOUT && len > 0) + pwriteout(fd, &textcopy, &len); + else if (pfd[0].revents & POLLIN) + if (preadin(fd, outbp) == FALSE) + break; + if (len == 0 && pfd[0].events & POLLOUT) + pfd[0].events = POLLIN; + } + close(fd); + + /* In case if last line doesn't have a '\n' add the leftover + * characters to buffer. + */ + if (leftover[0] != '\0') { + addline(outbp, leftover); + leftover[0] = '\0'; + } + if (nfds == 0) { + dobeep(); + ewprintf("poll timed out"); + return (FALSE); + } else if (nfds == -1) { + dobeep(); + ewprintf("poll error"); + return (FALSE); + } + return (popbuftop(outbp, WNONE)); +} + +/* + * Write some text from region to fd. Once done shutdown the + * write end. + */ +void +pwriteout(int fd, char **text, int *len) +{ + int w; + + if (((w = send(fd, *text, *len, MSG_NOSIGNAL)) == -1)) { + switch(errno) { + case EPIPE: + *len = -1; + break; + case EAGAIN: + return; + } + } else + *len -= w; + + *text += w; + if (*len <= 0) + shutdown(fd, SHUT_WR); +} + +/* + * Read some data from socket fd, break on '\n' and add + * to buffer. If couldn't break on newline hold leftover + * characters and append in next iteration. + */ +int +preadin(int fd, struct buffer *bp) +{ + int len; + char buf[BUFSIZ], *p, *q; + + if ((len = read(fd, buf, BUFSIZ - 1)) <= 0) + return (FALSE); + + buf[len] = '\0'; + p = q = buf; + if (leftover[0] != '\0' && ((q = strchr(p, *bp->b_nlchr)) != NULL)) { + *q++ = '\0'; + if (strlcat(leftover, p, sizeof(leftover)) >= + sizeof(leftover)) { + dobeep(); + ewprintf("line too long"); + return (FALSE); + } + addline(bp, leftover); + leftover[0] = '\0'; + p = q; + } + while ((q = strchr(p, *bp->b_nlchr)) != NULL) { + *q++ = '\0'; + addline(bp, p); + p = q; + } + if (strlcpy(leftover, p, sizeof(leftover)) >= sizeof(leftover)) { + dobeep(); + ewprintf("line too long"); + return (FALSE); + } + return (TRUE); +} Index: contrib/mg/search.c =================================================================== --- /dev/null +++ contrib/mg/search.c @@ -0,0 +1,855 @@ +/* $OpenBSD: search.c,v 1.47 2018/07/11 12:21:37 krw Exp $ */ + +/* This file is in the public domain. */ + +/* + * Search commands. + * The functions in this file implement the search commands (both plain and + * incremental searches are supported) and the query-replace command. + * + * The plain old search code is part of the original MicroEMACS "distribution". + * The incremental search code and the query-replace code is by Rich Ellison. + */ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "macro.h" + +#define SRCH_BEGIN (0) /* Search sub-codes. */ +#define SRCH_FORW (-1) +#define SRCH_BACK (-2) +#define SRCH_NOPR (-3) +#define SRCH_ACCM (-4) +#define SRCH_MARK (-5) + +struct srchcom { + int s_code; + struct line *s_dotp; + int s_doto; + int s_dotline; +}; + +static int isearch(int); +static void is_cpush(int); +static void is_lpush(void); +static void is_pop(void); +static int is_peek(void); +static void is_undo(int *, int *); +static int is_find(int); +static void is_prompt(int, int, int); +static void is_dspl(char *, int); +static int eq(int, int, int); + +static struct srchcom cmds[NSRCH]; +static int cip; + +int srch_lastdir = SRCH_NOPR; /* Last search flags. */ + +/* + * Search forward. Get a search string from the user, and search for it + * starting at ".". If found, "." gets moved to just after the matched + * characters, and display does all the hard stuff. If not found, it just + * prints a message. + */ +/* ARGSUSED */ +int +forwsearch(int f, int n) +{ + int s; + + if ((s = readpattern("Search")) != TRUE) + return (s); + if (forwsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", pat); + return (FALSE); + } + srch_lastdir = SRCH_FORW; + return (TRUE); +} + +/* + * Reverse search. Get a search string from the user, and search, starting + * at "." and proceeding toward the front of the buffer. If found "." is + * left pointing at the first character of the pattern [the last character + * that was matched]. + */ +/* ARGSUSED */ +int +backsearch(int f, int n) +{ + int s; + + if ((s = readpattern("Search backward")) != TRUE) + return (s); + if (backsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", pat); + return (FALSE); + } + srch_lastdir = SRCH_BACK; + return (TRUE); +} + +/* + * Search again, using the same search string and direction as the last + * search command. The direction has been saved in "srch_lastdir", so you + * know which way to go. + */ +/* ARGSUSED */ +int +searchagain(int f, int n) +{ + if (srch_lastdir == SRCH_FORW) { + if (forwsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", pat); + return (FALSE); + } + return (TRUE); + } + if (srch_lastdir == SRCH_BACK) { + if (backsrch() == FALSE) { + dobeep(); + ewprintf("Search failed: \"%s\"", pat); + return (FALSE); + } + return (TRUE); + } + dobeep(); + ewprintf("No last search"); + return (FALSE); +} + +/* + * Use incremental searching, initially in the forward direction. + * isearch ignores any explicit arguments. + */ +/* ARGSUSED */ +int +forwisearch(int f, int n) +{ + if (macrodef || inmacro) + /* We can't isearch in macro. Use search instead */ + return (forwsearch(f,n)); + else + return (isearch(SRCH_FORW)); +} + +/* + * Use incremental searching, initially in the reverse direction. + * isearch ignores any explicit arguments. + */ +/* ARGSUSED */ +int +backisearch(int f, int n) +{ + if (macrodef || inmacro) + /* We can't isearch in macro. Use search instead */ + return (backsearch(f,n)); + else + return (isearch(SRCH_BACK)); +} + +/* + * Incremental Search. + * dir is used as the initial direction to search. + * ^M exit from Isearch, set mark + * ^S switch direction to forward + * ^R switch direction to reverse + * ^Q quote next character (allows searching for ^N etc.) + * exit from Isearch, set mark + * undoes last character typed. (tricky job to do this correctly). + * other ^ exit search, don't set mark + * else accumulate into search string + */ +static int +isearch(int dir) +{ + struct line *clp; /* Saved line pointer */ + int c; + int cbo; /* Saved offset */ + int success; + int pptr; + int firstc; + int xcase; + int i; + char opat[NPAT]; + int cdotline; /* Saved line number */ + + if (macrodef) { + dobeep(); + ewprintf("Can't isearch in macro"); + return (FALSE); + } + for (cip = 0; cip < NSRCH; cip++) + cmds[cip].s_code = SRCH_NOPR; + + (void)strlcpy(opat, pat, sizeof(opat)); + cip = 0; + pptr = -1; + clp = curwp->w_dotp; + cbo = curwp->w_doto; + cdotline = curwp->w_dotline; + is_lpush(); + is_cpush(SRCH_BEGIN); + success = TRUE; + is_prompt(dir, TRUE, success); + + for (;;) { + update(CMODE); + + switch (c = getkey(FALSE)) { + case CCHR('['): + /* + * If new characters come in the next 300 msec, + * we can assume that they belong to a longer + * escaped sequence so we should ungetkey the + * ESC to avoid writing out garbage. + */ + if (ttwait(300) == FALSE) + ungetkey(c); + /* FALLTHRU */ + case CCHR('M'): + srch_lastdir = dir; + curwp->w_markp = clp; + curwp->w_marko = cbo; + curwp->w_markline = cdotline; + ewprintf("Mark set"); + return (TRUE); + case CCHR('G'): + if (success != TRUE) { + while (is_peek() == SRCH_ACCM) + is_undo(&pptr, &dir); + success = TRUE; + is_prompt(dir, pptr < 0, success); + break; + } + curwp->w_dotp = clp; + curwp->w_doto = cbo; + curwp->w_dotline = cdotline; + curwp->w_rflag |= WFMOVE; + srch_lastdir = dir; + (void)ctrlg(FFRAND, 0); + (void)strlcpy(pat, opat, sizeof(pat)); + return (ABORT); + case CCHR('S'): + if (dir == SRCH_BACK) { + dir = SRCH_FORW; + is_lpush(); + is_cpush(SRCH_FORW); + success = TRUE; + } + if (success == FALSE && dir == SRCH_FORW) { + /* wrap the search to beginning */ + curwp->w_dotp = bfirstlp(curbp); + curwp->w_doto = 0; + curwp->w_dotline = 1; + if (is_find(dir) != FALSE) { + is_cpush(SRCH_MARK); + success = TRUE; + } + ewprintf("Overwrapped I-search: %s", pat); + break; + } + is_lpush(); + pptr = strlen(pat); + if (forwchar(FFRAND, 1) == FALSE) { + dobeep(); + success = FALSE; + ewprintf("Failed I-search: %s", pat); + } else { + if (is_find(SRCH_FORW) != FALSE) + is_cpush(SRCH_MARK); + else { + (void)backchar(FFRAND, 1); + dobeep(); + success = FALSE; + ewprintf("Failed I-search: %s", pat); + } + } + is_prompt(dir, pptr < 0, success); + break; + case CCHR('R'): + if (dir == SRCH_FORW) { + dir = SRCH_BACK; + is_lpush(); + is_cpush(SRCH_BACK); + success = TRUE; + } + if (success == FALSE && dir == SRCH_BACK) { + /* wrap the search to end */ + curwp->w_dotp = blastlp(curbp); + curwp->w_doto = llength(curwp->w_dotp); + curwp->w_dotline = curwp->w_bufp->b_lines; + if (is_find(dir) != FALSE) { + is_cpush(SRCH_MARK); + success = TRUE; + } + ewprintf("Overwrapped I-search: %s", pat); + break; + } + is_lpush(); + pptr = strlen(pat); + if (backchar(FFRAND, 1) == FALSE) { + dobeep(); + success = FALSE; + } else { + if (is_find(SRCH_BACK) != FALSE) + is_cpush(SRCH_MARK); + else { + (void)forwchar(FFRAND, 1); + dobeep(); + success = FALSE; + } + } + is_prompt(dir, pptr < 0, success); + break; + case CCHR('W'): + /* add the rest of the current word to the pattern */ + clp = curwp->w_dotp; + cbo = curwp->w_doto; + firstc = 1; + if (pptr == -1) + pptr = 0; + if (dir == SRCH_BACK) { + /* when isearching backwards, cbo is the start of the pattern */ + cbo += pptr; + } + + /* if the search is case insensitive, add to pattern using lowercase */ + xcase = 0; + for (i = 0; pat[i]; i++) + if (ISUPPER(CHARMASK(pat[i]))) + xcase = 1; + + while (cbo < llength(clp)) { + c = lgetc(clp, cbo++); + if ((!firstc && !isalnum(c))) + break; + + if (pptr == NPAT - 1) { + dobeep(); + break; + } + firstc = 0; + if (!xcase && ISUPPER(c)) + c = TOLOWER(c); + + pat[pptr++] = c; + pat[pptr] = '\0'; + /* cursor only moves when isearching forwards */ + if (dir == SRCH_FORW) { + curwp->w_doto = cbo; + curwp->w_rflag |= WFMOVE; + update(CMODE); + } + } + is_prompt(dir, pptr < 0, success); + break; + case CCHR('H'): + case CCHR('?'): + is_undo(&pptr, &dir); + if (is_peek() != SRCH_ACCM) + success = TRUE; + is_prompt(dir, pptr < 0, success); + break; + case CCHR('\\'): + case CCHR('Q'): + c = (char)getkey(FALSE); + goto addchar; + default: + if (ISCTRL(c)) { + ungetkey(c); + curwp->w_markp = clp; + curwp->w_marko = cbo; + curwp->w_markline = cdotline; + ewprintf("Mark set"); + curwp->w_rflag |= WFMOVE; + return (TRUE); + } + /* FALLTHRU */ + case CCHR('I'): + case CCHR('J'): + addchar: + if (pptr == -1) + pptr = 0; + if (pptr == 0) + success = TRUE; + if (pptr == NPAT - 1) + dobeep(); + else { + pat[pptr++] = c; + pat[pptr] = '\0'; + } + is_lpush(); + if (success != FALSE) { + if (is_find(dir) != FALSE) + is_cpush(c); + else { + success = FALSE; + dobeep(); + is_cpush(SRCH_ACCM); + } + } else + is_cpush(SRCH_ACCM); + is_prompt(dir, FALSE, success); + } + } + /* NOTREACHED */ +} + +static void +is_cpush(int cmd) +{ + if (++cip >= NSRCH) + cip = 0; + cmds[cip].s_code = cmd; +} + +static void +is_lpush(void) +{ + int ctp; + + ctp = cip + 1; + if (ctp >= NSRCH) + ctp = 0; + cmds[ctp].s_code = SRCH_NOPR; + cmds[ctp].s_doto = curwp->w_doto; + cmds[ctp].s_dotp = curwp->w_dotp; + cmds[ctp].s_dotline = curwp->w_dotline; +} + +static void +is_pop(void) +{ + if (cmds[cip].s_code != SRCH_NOPR) { + curwp->w_doto = cmds[cip].s_doto; + curwp->w_dotp = cmds[cip].s_dotp; + curwp->w_dotline = cmds[cip].s_dotline; + curwp->w_rflag |= WFMOVE; + cmds[cip].s_code = SRCH_NOPR; + } + if (--cip <= 0) + cip = NSRCH - 1; +} + +static int +is_peek(void) +{ + return (cmds[cip].s_code); +} + +/* this used to always return TRUE (the return value was checked) */ +static void +is_undo(int *pptr, int *dir) +{ + int redo = FALSE; + + switch (cmds[cip].s_code) { + case SRCH_BEGIN: + case SRCH_NOPR: + *pptr = -1; + break; + case SRCH_MARK: + break; + case SRCH_FORW: + *dir = SRCH_BACK; + redo = TRUE; + break; + case SRCH_BACK: + *dir = SRCH_FORW; + redo = TRUE; + break; + case SRCH_ACCM: + default: + *pptr -= 1; + if (*pptr < 0) + *pptr = 0; + pat[*pptr] = '\0'; + break; + } + is_pop(); + if (redo) + is_undo(pptr, dir); +} + +static int +is_find(int dir) +{ + int plen, odoto, odotline; + struct line *odotp; + + odoto = curwp->w_doto; + odotp = curwp->w_dotp; + odotline = curwp->w_dotline; + plen = strlen(pat); + if (plen != 0) { + if (dir == SRCH_FORW) { + (void)backchar(FFARG | FFRAND, plen); + if (forwsrch() == FALSE) { + curwp->w_doto = odoto; + curwp->w_dotp = odotp; + curwp->w_dotline = odotline; + return (FALSE); + } + return (TRUE); + } + if (dir == SRCH_BACK) { + (void)forwchar(FFARG | FFRAND, plen); + if (backsrch() == FALSE) { + curwp->w_doto = odoto; + curwp->w_dotp = odotp; + curwp->w_dotline = odotline; + return (FALSE); + } + return (TRUE); + } + dobeep(); + ewprintf("bad call to is_find"); + return (FALSE); + } + return (FALSE); +} + +/* + * If called with "dir" not one of SRCH_FORW or SRCH_BACK, this routine used + * to print an error message. It also used to return TRUE or FALSE, depending + * on if it liked the "dir". However, none of the callers looked at the + * status, so I just made the checking vanish. + */ +static void +is_prompt(int dir, int flag, int success) +{ + if (dir == SRCH_FORW) { + if (success != FALSE) + is_dspl("I-search", flag); + else + is_dspl("Failing I-search", flag); + } else if (dir == SRCH_BACK) { + if (success != FALSE) + is_dspl("I-search backward", flag); + else + is_dspl("Failing I-search backward", flag); + } else + ewprintf("Broken call to is_prompt"); +} + +/* + * Prompt writing routine for the incremental search. The "i_prompt" is just + * a string. The "flag" determines whether pat should be printed. + */ +static void +is_dspl(char *i_prompt, int flag) +{ + if (flag != FALSE) + ewprintf("%s: ", i_prompt); + else + ewprintf("%s: %s", i_prompt, pat); +} + +/* + * Query Replace. + * Replace strings selectively. Does a search and replace operation. + */ +/* ARGSUSED */ +int +queryrepl(int f, int n) +{ + int s; + int rcnt = 0; /* replacements made so far */ + int plen; /* length of found string */ + char news[NPAT], *rep; /* replacement string */ + + if (macrodef) { + dobeep(); + ewprintf("Can't query replace in macro"); + return (FALSE); + } + + if ((s = readpattern("Query replace")) != TRUE) + return (s); + if ((rep = eread("Query replace %s with: ", news, NPAT, + EFNUL | EFNEW | EFCR, pat)) == NULL) + return (ABORT); + else if (rep[0] == '\0') + news[0] = '\0'; + ewprintf("Query replacing %s with %s:", pat, news); + plen = strlen(pat); + + /* + * Search forward repeatedly, checking each time whether to insert + * or not. The "!" case makes the check always true, so it gets put + * into a tighter loop for efficiency. + */ + while (forwsrch() == TRUE) { +retry: + update(CMODE); + switch (getkey(FALSE)) { + case 'y': + case ' ': + if (lreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + break; + case '.': + if (lreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + goto stopsearch; + /* ^G, CR or ESC */ + case CCHR('G'): + (void)ctrlg(FFRAND, 0); + goto stopsearch; + case CCHR('['): + case CCHR('M'): + goto stopsearch; + case '!': + do { + if (lreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + rcnt++; + } while (forwsrch() == TRUE); + goto stopsearch; + case 'n': + case CCHR('H'): + /* To not replace */ + case CCHR('?'): + break; + default: + ewprintf("y/n or /: replace/don't, [.] repl-end, [!] repl-rest, / quit"); + goto retry; + } + } +stopsearch: + curwp->w_rflag |= WFFULL; + update(CMODE); + if (rcnt == 1) + ewprintf("Replaced 1 occurrence"); + else + ewprintf("Replaced %d occurrences", rcnt); + return (TRUE); +} + +/* + * Replace string globally without individual prompting. + */ +/* ARGSUSED */ +int +replstr(int f, int n) +{ + char news[NPAT]; + int s, plen, rcnt = 0; + char *r; + + if ((s = readpattern("Replace string")) != TRUE) + return s; + + r = eread("Replace string %s with: ", news, NPAT, + EFNUL | EFNEW | EFCR, pat); + if (r == NULL) + return (ABORT); + + plen = strlen(pat); + while (forwsrch() == TRUE) { + update(CMODE); + if (lreplace((RSIZE)plen, news) == FALSE) + return (FALSE); + + rcnt++; + } + + curwp->w_rflag |= WFFULL; + update(CMODE); + + if (rcnt == 1) + ewprintf("Replaced 1 occurrence"); + else + ewprintf("Replaced %d occurrences", rcnt); + + return (TRUE); +} + +/* + * This routine does the real work of a forward search. The pattern is sitting + * in the external variable "pat". If found, dot is updated, the window system + * is notified of the change, and TRUE is returned. If the string isn't found, + * FALSE is returned. + */ +int +forwsrch(void) +{ + struct line *clp, *tlp; + int cbo, tbo, c, i, xcase = 0; + char *pp; + int nline; + + clp = curwp->w_dotp; + cbo = curwp->w_doto; + nline = curwp->w_dotline; + for (i = 0; pat[i]; i++) + if (ISUPPER(CHARMASK(pat[i]))) + xcase = 1; + for (;;) { + if (cbo == llength(clp)) { + if ((clp = lforw(clp)) == curbp->b_headp) + break; + nline++; + cbo = 0; + c = CCHR('J'); + } else + c = lgetc(clp, cbo++); + if (eq(c, pat[0], xcase) != FALSE) { + tlp = clp; + tbo = cbo; + pp = &pat[1]; + while (*pp != 0) { + if (tbo == llength(tlp)) { + tlp = lforw(tlp); + if (tlp == curbp->b_headp) + goto fail; + tbo = 0; + c = CCHR('J'); + if (eq(c, *pp++, xcase) == FALSE) + goto fail; + nline++; + } else { + c = lgetc(tlp, tbo++); + if (eq(c, *pp++, xcase) == FALSE) + goto fail; + } + } + curwp->w_dotp = tlp; + curwp->w_doto = tbo; + curwp->w_dotline = nline; + curwp->w_rflag |= WFMOVE; + return (TRUE); + } +fail: ; + } + return (FALSE); +} + +/* + * This routine does the real work of a backward search. The pattern is + * sitting in the external variable "pat". If found, dot is updated, the + * window system is notified of the change, and TRUE is returned. If the + * string isn't found, FALSE is returned. + */ +int +backsrch(void) +{ + struct line *clp, *tlp; + int cbo, tbo, c, i, xcase = 0; + char *epp, *pp; + int nline, pline; + + for (epp = &pat[0]; epp[1] != 0; ++epp); + clp = curwp->w_dotp; + cbo = curwp->w_doto; + nline = curwp->w_dotline; + for (i = 0; pat[i]; i++) + if (ISUPPER(CHARMASK(pat[i]))) + xcase = 1; + for (;;) { + if (cbo == 0) { + clp = lback(clp); + if (clp == curbp->b_headp) + return (FALSE); + nline--; + cbo = llength(clp) + 1; + } + if (--cbo == llength(clp)) + c = CCHR('J'); + else + c = lgetc(clp, cbo); + if (eq(c, *epp, xcase) != FALSE) { + tlp = clp; + tbo = cbo; + pp = epp; + pline = nline; + while (pp != &pat[0]) { + if (tbo == 0) { + tlp = lback(tlp); + if (tlp == curbp->b_headp) + goto fail; + nline--; + tbo = llength(tlp) + 1; + } + if (--tbo == llength(tlp)) + c = CCHR('J'); + else + c = lgetc(tlp, tbo); + if (eq(c, *--pp, xcase) == FALSE) { + nline = pline; + goto fail; + } + } + curwp->w_dotp = tlp; + curwp->w_doto = tbo; + curwp->w_dotline = nline; + curwp->w_rflag |= WFMOVE; + return (TRUE); + } +fail: ; + } + /* NOTREACHED */ +} + +/* + * Compare two characters. The "bc" comes from the buffer. It has its case + * folded out. The "pc" is from the pattern. + */ +static int +eq(int bc, int pc, int xcase) +{ + bc = CHARMASK(bc); + pc = CHARMASK(pc); + if (bc == pc) + return (TRUE); + if (xcase) + return (FALSE); + if (ISUPPER(bc)) + return (TOLOWER(bc) == pc); + if (ISUPPER(pc)) + return (bc == TOLOWER(pc)); + return (FALSE); +} + +/* + * Read a pattern. Stash it in the external variable "pat". The "pat" is not + * updated if the user types in an empty line. If the user typed an empty + * line, and there is no old pattern, it is an error. Display the old pattern, + * in the style of Jeff Lomicka. There is some do-it-yourself control + * expansion. + */ +int +readpattern(char *r_prompt) +{ + char tpat[NPAT], *rep; + int retval; + + if (pat[0] == '\0') + rep = eread("%s: ", tpat, NPAT, EFNEW | EFCR, r_prompt); + else + rep = eread("%s (default %s): ", tpat, NPAT, + EFNUL | EFNEW | EFCR, r_prompt, pat); + + /* specified */ + if (rep == NULL) { + retval = ABORT; + } else if (rep[0] != '\0') { + (void)strlcpy(pat, tpat, sizeof(pat)); + retval = TRUE; + } else if (pat[0] != '\0') { + retval = TRUE; + } else + retval = FALSE; + return (retval); +} Index: contrib/mg/setupterm.c =================================================================== --- /dev/null +++ contrib/mg/setupterm.c @@ -0,0 +1,158 @@ +/* $NetBSD: setupterm.c,v 1.9 2019/04/11 23:52:08 jakllsch Exp $ */ + +/* + * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +/* + * use_env is really a curses function - POSIX mandates it's in curses.h + * But it has to live in terminfo because it must precede a call to setupterm(). + */ + +static bool __use_env = true; + +void +use_env(bool value) +{ + + __use_env = value; +} +#define reterr(code, msg) \ + do { \ + if (errret == NULL) \ + errx(EXIT_FAILURE, msg); \ + else { \ + *errret = code; \ + return ERR; \ + } \ + } while (0 /* CONSTCOND */) + +#define reterrarg(code, msg, arg) \ + do { \ + if (errret == NULL) \ + errx(EXIT_FAILURE, msg, arg); \ + else { \ + *errret = code; \ + return ERR; \ + } \ + } while (0 /* CONSTCOND */) + + +int +ti_setupterm(TERMINAL **nterm, const char *term, int fildes, int *errret) +{ + int error; + struct winsize win; + + if (term == NULL) + term = getenv("TERM"); + if (term == NULL || *term == '\0') { + *nterm = NULL; + reterr(0, "TERM environment variable not set"); + } + if (fildes == STDOUT_FILENO && !isatty(fildes)) + fildes = STDERR_FILENO; + + *nterm = calloc(1, sizeof(**nterm)); + if (*nterm == NULL) + reterr(-1, "not enough memory to create terminal structure"); + + error = _ti_getterm(*nterm, term, 0); + if (error != 1) { + del_curterm(*nterm); + *nterm = NULL; + switch (error) { + case -1: + reterr(error, "cannot access the terminfo database"); + /* NOTREACHED */ + case 0: + reterrarg(error, + "%s: terminal not listed in terminfo database", + term); + /* NOTREACHED */ + default: + reterr(-1, "unknown error"); + /* NOTREACHED */ + } + } + + (*nterm)->fildes = fildes; + _ti_setospeed(*nterm); + if (t_generic_type(*nterm)) + reterrarg(0, "%s: generic terminal", term); + if (t_hard_copy(*nterm)) + reterrarg(1, "%s: hardcopy terminal", term); + + /* If TIOCGWINSZ works, then set initial lines and columns. */ + if (ioctl(fildes, TIOCGWINSZ, &win) != -1 && + win.ws_row != 0 && win.ws_col != 0) + { + t_lines(*nterm) = (short)win.ws_row; + t_columns(*nterm) = (short)win.ws_col; + } + + /* POSIX 1003.2 requires that the environment override. */ + if (__use_env) { + char *p; + + if ((p = getenv("LINES")) != NULL) + t_lines(*nterm) = (short)strtol(p, NULL, 0); + if ((p = getenv("COLUMNS")) != NULL) + t_columns(*nterm) = (short)strtol(p, NULL, 0); + } + + /* POSIX requires 1 for success */ + if (errret) + *errret = 1; + return OK; +} + +int +setupterm(const char *term, int fildes, int *errret) +{ + TERMINAL *nterm; + int ret; + + if (errret != NULL) + *errret = ERR; + ret = ti_setupterm(&nterm, term, fildes, errret); + if (nterm != NULL) + set_curterm(nterm); + return ret; +} Index: contrib/mg/spawn.c =================================================================== --- /dev/null +++ contrib/mg/spawn.c @@ -0,0 +1,51 @@ +/* $OpenBSD: spawn.c,v 1.12 2015/03/19 21:22:15 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * Spawn. Actually just suspends Mg. + * Assumes POSIX job control. + */ + +#include +#include +#include +#include +#include + +#include "def.h" + +/* + * This causes mg to send itself a stop signal. It assumes the parent + * shell supports POSIX job control. If the terminal supports an alternate + * screen, we will switch to it. + */ +/* ARGSUSED */ +int +spawncli(int f, int n) +{ + sigset_t oset; + + /* Very similar to what vttidy() does. */ + ttcolor(CTEXT); + ttnowindow(); + ttmove(nrow - 1, 0); + if (epresf != FALSE) { + tteeol(); + epresf = FALSE; + } + if (ttcooked() == FALSE) + return (FALSE); + + /* Exit application mode and tidy. */ + tttidy(); + ttflush(); + (void)sigprocmask(SIG_SETMASK, NULL, &oset); + (void)kill(0, SIGTSTP); + (void)sigprocmask(SIG_SETMASK, &oset, NULL); + ttreinit(); + + /* Force repaint. */ + sgarbf = TRUE; + return (ttraw()); +} Index: contrib/mg/strlcat.c =================================================================== --- /dev/null +++ contrib/mg/strlcat.c @@ -0,0 +1,61 @@ +/* $OpenBSD: strlcat.c,v 1.15 2015/03/02 21:41:08 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "config.h" + +#ifndef HAVE_STRLCAT + +/* + * Appends src to string dst of size dsize (unlike strncat, dsize is the + * full size of dst, not space left). At most dsize-1 characters + * will be copied. Always NUL terminates (unless dsize <= strlen(dst)). + * Returns strlen(src) + MIN(dsize, strlen(initial dst)). + * If retval >= dsize, truncation occurred. + */ +size_t +strlcat(char *dst, const char *src, size_t dsize) +{ + const char *odst = dst; + const char *osrc = src; + size_t n = dsize; + size_t dlen; + + /* Find the end of dst and adjust bytes left but don't go past end. */ + while (n-- != 0 && *dst != '\0') + dst++; + dlen = dst - odst; + n = dsize - dlen; + + if (n-- == 0) + return(dlen + strlen(src)); + while (*src != '\0') { + if (n != 0) { + *dst++ = *src; + n--; + } + src++; + } + *dst = '\0'; + + return(dlen + (src - osrc)); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCAT */ Index: contrib/mg/strlcpy.c =================================================================== --- /dev/null +++ contrib/mg/strlcpy.c @@ -0,0 +1,56 @@ +/* $OpenBSD: strlcpy.c,v 1.12 2015/01/15 03:54:12 millert Exp $ */ + +/* + * Copyright (c) 1998, 2015 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include + +#include "config.h" + +#ifndef HAVE_STRLCPY + +/* + * Copy string src to buffer dst of size dsize. At most dsize-1 + * chars will be copied. Always NUL terminates (unless dsize == 0). + * Returns strlen(src); if retval >= dsize, truncation occurred. + */ +size_t +strlcpy(char *dst, const char *src, size_t dsize) +{ + const char *osrc = src; + size_t nleft = dsize; + + /* Copy as many bytes as will fit. */ + if (nleft != 0) { + while (--nleft != 0) { + if ((*dst++ = *src++) == '\0') + break; + } + } + + /* Not enough room in dst, add NUL and traverse rest of src. */ + if (nleft == 0) { + if (dsize != 0) + *dst = '\0'; /* NUL-terminate dst */ + while (*src++) + ; + } + + return(src - osrc - 1); /* count does not include NUL */ +} + +#endif /* !HAVE_STRLCPY */ Index: contrib/mg/strndup.c =================================================================== --- /dev/null +++ contrib/mg/strndup.c @@ -0,0 +1,55 @@ +/* $OpenBSD: strndup.c,v 1.3 2019/01/25 00:19:25 millert Exp $ */ +/* $OpenBSD: strnlen.c,v 1.9 2019/01/25 00:19:25 millert Exp $ */ + +/* + * Copyright (c) 2010 Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "config.h" + +#ifndef HAVE_STRNDUP + +static size_t +strnlen(const char *str, size_t maxlen) +{ + const char *cp; + + for (cp = str; maxlen != 0 && *cp != '\0'; cp++, maxlen--) + ; + + return (size_t)(cp - str); +} + +char * +strndup(const char *str, size_t maxlen) +{ + char *copy; + size_t len; + + len = strnlen(str, maxlen); + copy = malloc(len + 1); + if (copy != NULL) { + (void)memcpy(copy, str, len); + copy[len] = '\0'; + } + + return copy; +} + +#endif /* !HAVE_STRNDUP */ Index: contrib/mg/strtonum.c =================================================================== --- /dev/null +++ contrib/mg/strtonum.c @@ -0,0 +1,71 @@ +/* $OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "config.h" + +#ifndef HAVE_STRTONUM + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + int error = 0; + char *ep; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) { + error = INVALID; + } else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + +#endif /* !HAVE_STRTONUM */ Index: contrib/mg/tags.c =================================================================== --- /dev/null +++ contrib/mg/tags.c @@ -0,0 +1,565 @@ +/* $OpenBSD: tags.c,v 1.16 2017/08/06 04:39:45 bcallah Exp $ */ + +/* + * This file is in the public domain. + * + * Author: Sunil Nimmagadda + */ + +#include +#include +#if defined(__linux__) || defined(__DragonFly__) || defined(__APPLE__) +#include "tree.h" +#else +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) || defined(__CYGWIN__) +#include "util.h" +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +#include +#else +#include +#endif + +#include "def.h" + +struct ctag; + +static int addctag(char *); +static int atbow(void); +void closetags(void); +static int ctagcmp(struct ctag *, struct ctag *); +static int loadbuffer(char *); +static int loadtags(const char *); +static int pushtag(char *); +static int searchpat(char *); +static struct ctag *searchtag(char *); +static char *strip(char *, size_t); +static void unloadtags(void); + +#define DEFAULTFN "tags" + +char *tagsfn = NULL; +int loaded = FALSE; + +/* ctags(1) entries are parsed and maintained in a tree. */ +struct ctag { + RB_ENTRY(ctag) entry; + char *tag; + char *fname; + char *pat; +}; +RB_HEAD(tagtree, ctag) tags = RB_INITIALIZER(&tags); +RB_GENERATE(tagtree, ctag, entry, ctagcmp); + +struct tagpos { + SLIST_ENTRY(tagpos) entry; + int doto; + int dotline; + char *bname; +}; +SLIST_HEAD(tagstack, tagpos) shead = SLIST_HEAD_INITIALIZER(shead); + +int +ctagcmp(struct ctag *s, struct ctag *t) +{ + return strcmp(s->tag, t->tag); +} + +/* + * Record the filename that contain tags to be used while loading them + * on first use. If a filename is already recorded, ask user to retain + * already loaded tags (if any) and unload them if user chooses not to. + */ +/* ARGSUSED */ +int +tagsvisit(int f, int n) +{ + char fname[NFILEN], *bufp, *temp; + struct stat sb; + + if (getbufcwd(fname, sizeof(fname)) == FALSE) + fname[0] = '\0'; + + if (strlcat(fname, DEFAULTFN, sizeof(fname)) >= sizeof(fname)) { + dobeep(); + ewprintf("Filename too long"); + return (FALSE); + } + + bufp = eread("Visit tags table (default %s): ", fname, + NFILEN, EFFILE | EFCR | EFNEW | EFDEF, DEFAULTFN); + if (bufp == NULL) + return (ABORT); + + if (stat(bufp, &sb) == -1) { + dobeep(); + ewprintf("stat: %s", strerror(errno)); + return (FALSE); + } else if (S_ISREG(sb.st_mode) == 0) { + dobeep(); + ewprintf("Not a regular file"); + return (FALSE); + } else if (access(bufp, R_OK) == -1) { + dobeep(); + ewprintf("Cannot access file %s", bufp); + return (FALSE); + } + + if (tagsfn == NULL) { + if (bufp[0] == '\0') { + if ((tagsfn = strdup(fname)) == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (FALSE); + } + } else { + /* bufp points to local variable, so duplicate. */ + if ((tagsfn = strdup(bufp)) == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (FALSE); + } + } + } else { + if ((temp = strdup(bufp)) == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (FALSE); + } + free(tagsfn); + tagsfn = temp; + if (eyorn("Keep current list of tags table also") == FALSE) { + ewprintf("Starting a new list of tags table"); + unloadtags(); + } + loaded = FALSE; + } + return (TRUE); +} + +/* + * Ask user for a tag while treating word at dot as default. Visit tags + * file if not yet done, load tags and jump to definition of the tag. + */ +int +findtag(int f, int n) +{ + char utok[MAX_TOKEN], dtok[MAX_TOKEN]; + char *tok, *bufp; + int ret; + + if (curtoken(f, n, dtok) == FALSE) { + dtok[0] = '\0'; + bufp = eread("Find tag: ", utok, MAX_TOKEN, EFNUL | EFNEW); + } else + bufp = eread("Find tag (default %s): ", utok, MAX_TOKEN, + EFNUL | EFNEW, dtok); + + if (bufp == NULL) + return (ABORT); + else if (bufp[0] == '\0') + tok = dtok; + else + tok = utok; + + if (tok[0] == '\0') { + dobeep(); + ewprintf("There is no default tag"); + return (FALSE); + } + + if (tagsfn == NULL) + if ((ret = tagsvisit(f, n)) != TRUE) + return (ret); + if (!loaded) { + if (loadtags(tagsfn) == FALSE) { + free(tagsfn); + tagsfn = NULL; + return (FALSE); + } + loaded = TRUE; + } + return pushtag(tok); +} + +/* + * Free tags tree. + */ +void +unloadtags(void) +{ + struct ctag *var, *nxt; + + for (var = RB_MIN(tagtree, &tags); var != NULL; var = nxt) { + nxt = RB_NEXT(tagtree, &tags, var); + RB_REMOVE(tagtree, &tags, var); + /* line parsed with fparseln needs to be freed */ + free(var->tag); + free(var); + } +} + +/* + * Lookup tag passed in tree and if found, push current location and + * buffername onto stack, load the file with tag definition into a new + * buffer and position dot at the pattern. + */ +/*ARGSUSED */ +int +pushtag(char *tok) +{ + struct ctag *res; + struct tagpos *s; + char bname[NFILEN]; + int doto, dotline; + + if ((res = searchtag(tok)) == NULL) + return (FALSE); + + doto = curwp->w_doto; + dotline = curwp->w_dotline; + /* record absolute filenames. Fixes issues when mg's cwd is not the + * same as buffer's directory. + */ + if (strlcpy(bname, curbp->b_cwd, sizeof(bname)) >= sizeof(bname)) { + dobeep(); + ewprintf("filename too long"); + return (FALSE); + } + if (strlcat(bname, curbp->b_bname, sizeof(bname)) >= sizeof(bname)) { + dobeep(); + ewprintf("filename too long"); + return (FALSE); + } + + if (loadbuffer(res->fname) == FALSE) + return (FALSE); + + if (searchpat(res->pat) == TRUE) { + if ((s = malloc(sizeof(struct tagpos))) == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (FALSE); + } + if ((s->bname = strdup(bname)) == NULL) { + dobeep(); + ewprintf("Out of memory"); + free(s); + return (FALSE); + } + s->doto = doto; + s->dotline = dotline; + SLIST_INSERT_HEAD(&shead, s, entry); + return (TRUE); + } else { + dobeep(); + ewprintf("%s: pattern not found", res->tag); + return (FALSE); + } + /* NOTREACHED */ + return (FALSE); +} + +/* + * If tag stack is not empty pop stack and jump to recorded buffer, dot. + */ +/* ARGSUSED */ +int +poptag(int f, int n) +{ + struct line *dotp; + struct tagpos *s; + + if (SLIST_EMPTY(&shead)) { + dobeep(); + ewprintf("No previous location for find-tag invocation"); + return (FALSE); + } + s = SLIST_FIRST(&shead); + SLIST_REMOVE_HEAD(&shead, entry); + if (loadbuffer(s->bname) == FALSE) + return (FALSE); + curwp->w_dotline = s->dotline; + curwp->w_doto = s->doto; + + /* storing of dotp in tagpos wouldn't work out in cases when + * that buffer is killed by user(dangling pointer). Explicitly + * traverse till dotline for correct handling. + */ + dotp = curwp->w_bufp->b_headp; + while (s->dotline--) + dotp = dotp->l_fp; + + curwp->w_dotp = dotp; + free(s->bname); + free(s); + return (TRUE); +} + +/* + * Parse the tags file and construct the tags tree. Remove escape + * characters while parsing the file. + */ +int +loadtags(const char *fn) +{ + char *l; + FILE *fd; + + if ((fd = fopen(fn, "r")) == NULL) { + dobeep(); + ewprintf("Unable to open tags file: %s", fn); + return (FALSE); + } + while ((l = fparseln(fd, NULL, NULL, "\\\\\0", + FPARSELN_UNESCCONT | FPARSELN_UNESCREST)) != NULL) { + if (addctag(l) == FALSE) { + fclose(fd); + return (FALSE); + } + } + fclose(fd); + return (TRUE); +} + +/* + * Cleanup and destroy tree and stack. + */ +void +closetags(void) +{ + struct tagpos *s; + + while (!SLIST_EMPTY(&shead)) { + s = SLIST_FIRST(&shead); + SLIST_REMOVE_HEAD(&shead, entry); + free(s->bname); + free(s); + } + unloadtags(); + free(tagsfn); +} + +/* + * Strip away any special characters in pattern. + * The pattern in ctags isn't a true regular expression. Its of the form + * /^xxx$/ or ?^xxx$? and in some cases the "$" would be missing. Strip + * the leading and trailing special characters so the pattern matching + * would be a simple string compare. Escape character is taken care by + * fparseln. + */ +char * +strip(char *s, size_t len) +{ + /* first strip trailing special chars */ + s[len - 1] = '\0'; + if (s[len - 2] == '$') + s[len - 2] = '\0'; + + /* then strip leading special chars */ + s++; + if (*s == '^') + s++; + + return s; +} + +/* + * tags line is of the format "\t\t". Split them + * by replacing '\t' with '\0'. This wouldn't alter the size of malloc'ed + * l, and can be freed during cleanup. + */ +int +addctag(char *l) +{ + struct ctag *t; + + if ((t = malloc(sizeof(struct ctag))) == NULL) { + dobeep(); + ewprintf("Out of memory"); + return (FALSE); + } + t->tag = l; + if ((l = strchr(l, '\t')) == NULL) + goto cleanup; + *l++ = '\0'; + t->fname = l; + if ((l = strchr(l, '\t')) == NULL) + goto cleanup; + *l++ = '\0'; + if (*l == '\0') + goto cleanup; + t->pat = strip(l, strlen(l)); + RB_INSERT(tagtree, &tags, t); + return (TRUE); +cleanup: + free(t); + free(l); + return (FALSE); +} + +/* + * Search through each line of buffer for pattern. + */ +int +searchpat(char *s_pat) +{ + struct line *lp; + int dotline; + size_t plen; + + plen = strlen(s_pat); + dotline = 1; + lp = lforw(curbp->b_headp); + while (lp != curbp->b_headp) { + if (ltext(lp) != NULL && plen <= llength(lp) && + (strncmp(s_pat, ltext(lp), plen) == 0)) { + curwp->w_doto = 0; + curwp->w_dotp = lp; + curwp->w_dotline = dotline; + return (TRUE); + } else { + lp = lforw(lp); + dotline++; + } + } + return (FALSE); +} + +/* + * Return TRUE if dot is at beginning of a word or at beginning + * of line, else FALSE. + */ +int +atbow(void) +{ + if (curwp->w_doto == 0) + return (TRUE); + if (ISWORD(curwp->w_dotp->l_text[curwp->w_doto]) && + !ISWORD(curwp->w_dotp->l_text[curwp->w_doto - 1])) + return (TRUE); + return (FALSE); +} + +/* + * Extract the word at dot without changing dot position. + */ +int +curtoken(int f, int n, char *token) +{ + struct line *odotp; + int odoto, tdoto, odotline, size, r; + char c; + + /* Underscore character is to be treated as "inword" while + * processing tokens unlike mg's default word traversal. Save + * and restore it's cinfo value so that tag matching works for + * identifier with underscore. + */ + c = cinfo['_']; + cinfo['_'] = _MG_W; + + odotp = curwp->w_dotp; + odoto = curwp->w_doto; + odotline = curwp->w_dotline; + + /* Move backword unless we are at the beginning of a word or at + * beginning of line. + */ + if (!atbow()) + if ((r = backword(f, n)) == FALSE) + goto cleanup; + + tdoto = curwp->w_doto; + + if ((r = forwword(f, n)) == FALSE) + goto cleanup; + + /* strip away leading whitespace if any like emacs. */ + while (ltext(curwp->w_dotp) && + isspace(lgetc(curwp->w_dotp, tdoto))) + tdoto++; + + size = curwp->w_doto - tdoto; + if (size <= 0 || size >= MAX_TOKEN || + ltext(curwp->w_dotp) == NULL) { + r = FALSE; + goto cleanup; + } + strncpy(token, ltext(curwp->w_dotp) + tdoto, size); + token[size] = '\0'; + r = TRUE; + +cleanup: + cinfo['_'] = c; + curwp->w_dotp = odotp; + curwp->w_doto = odoto; + curwp->w_dotline = odotline; + return (r); +} + +/* + * Search tagstree for a given token. + */ +struct ctag * +searchtag(char *tok) +{ + struct ctag t, *res; + + t.tag = tok; + if ((res = RB_FIND(tagtree, &tags, &t)) == NULL) { + dobeep(); + ewprintf("No tag containing %s", tok); + return (NULL); + } + return res; +} + +/* + * This is equivalent to filevisit from file.c. + * Look around to see if we can find the file in another buffer; if we + * can't find it, create a new buffer, read in the text, and switch to + * the new buffer. *scratch*, *grep*, *compile* needs to be handled + * differently from other buffers which have "filenames". + */ +int +loadbuffer(char *bname) +{ + struct buffer *bufp; + char *adjf; + + /* check for special buffers which begin with '*' */ + if (bname[0] == '*') { + if ((bufp = bfind(bname, FALSE)) != NULL) { + curbp = bufp; + return (showbuffer(bufp, curwp, WFFULL)); + } else { + return (FALSE); + } + } else { + if ((adjf = adjustname(bname, TRUE)) == NULL) + return (FALSE); + if ((bufp = findbuffer(adjf)) == NULL) + return (FALSE); + } + curbp = bufp; + if (showbuffer(bufp, curwp, WFFULL) != TRUE) + return (FALSE); + if (bufp->b_fname[0] == '\0') { + if (readin(adjf) != TRUE) { + killbuffer(bufp); + return (FALSE); + } + } + return (TRUE); +} Index: contrib/mg/term.c =================================================================== --- /dev/null +++ contrib/mg/term.c @@ -0,0 +1,473 @@ +/* $NetBSD: term.c,v 1.29 2018/10/08 20:44:34 roy Exp $ */ + +/* + * Copyright (c) 2009, 2010, 2011 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "cdbr.h" +#include "term_private.h" +#include "terminfo_term.h" + +#define _PATH_TERMINFO "/usr/share/misc/terminfo" + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +static char __ti_database[PATH_MAX]; +const char *_ti_database; + +/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */ +uint16_t +le16dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[1] << 8) | p[0]); +} + +/* From NetBSD sys/external/bsd/libnv/dist/nv_compat.h */ +static uint32_t +le32dec(const void *buf) +{ + uint8_t const *p = (uint8_t const *) buf; + + return (((unsigned) p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]); +} + +/* Include a generated list of pre-compiled terminfo descriptions. */ +#include "compiled_terms.c" + +static int +allocset(void *pp, int init, size_t nelem, size_t elemsize) +{ + void **p = pp; + if (*p) { + memset(*p, init, nelem * elemsize); + return 0; + } + + if ((*p = calloc(nelem, elemsize)) == NULL) + return -1; + + if (init != 0) + memset(*p, init, nelem * elemsize); + return 0; +} + +static int +_ti_readterm(TERMINAL *term, const char *cap, size_t caplen, int flags) +{ + char ver; + uint16_t ind, num; + size_t len; + TERMUSERDEF *ud; + + if (caplen == 0) + goto out; + ver = *cap++; + caplen--; + /* Only read version 1 structures */ + if (ver != 1) + goto out; + + if (allocset(&term->flags, 0, TIFLAGMAX+1, sizeof(*term->flags)) == -1) + return -1; + + if (allocset(&term->nums, -1, TINUMMAX+1, sizeof(*term->nums)) == -1) + return -1; + + if (allocset(&term->strs, 0, TISTRMAX+1, sizeof(*term->strs)) == -1) + return -1; + + if (term->_arealen != caplen) { + term->_arealen = caplen; + term->_area = realloc(term->_area, term->_arealen); + if (term->_area == NULL) + return -1; + } + memcpy(term->_area, cap, term->_arealen); + + cap = term->_area; + len = le16dec(cap); + cap += sizeof(uint16_t); + term->name = cap; + cap += len; + len = le16dec(cap); + cap += sizeof(uint16_t); + if (len == 0) + term->_alias = NULL; + else { + term->_alias = cap; + cap += len; + } + len = le16dec(cap); + cap += sizeof(uint16_t); + if (len == 0) + term->desc = NULL; + else { + term->desc = cap; + cap += len; + } + + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num != 0) { + num = le16dec(cap); + cap += sizeof(uint16_t); + for (; num != 0; num--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + term->flags[ind] = *cap++; + if (flags == 0 && !VALID_BOOLEAN(term->flags[ind])) + term->flags[ind] = 0; + } + } + + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num != 0) { + num = le16dec(cap); + cap += sizeof(uint16_t); + for (; num != 0; num--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + term->nums[ind] = (short)le16dec(cap); + if (flags == 0 && !VALID_NUMERIC(term->nums[ind])) + term->nums[ind] = ABSENT_NUMERIC; + cap += sizeof(uint16_t); + } + } + + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num != 0) { + num = le16dec(cap); + cap += sizeof(uint16_t); + for (; num != 0; num--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + len = le16dec(cap); + cap += sizeof(uint16_t); + if (len > 0) + term->strs[ind] = cap; + else if (flags == 0) + term->strs[ind] = ABSENT_STRING; + else + term->strs[ind] = CANCELLED_STRING; + cap += len; + } + } + + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num != 0) { + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num != term->_nuserdefs) { + free(term->_userdefs); + term->_userdefs = NULL; + term->_nuserdefs = num; + } + if (allocset(&term->_userdefs, 0, term->_nuserdefs, + sizeof(*term->_userdefs)) == -1) + return -1; + for (num = 0; num < term->_nuserdefs; num++) { + ud = &term->_userdefs[num]; + len = le16dec(cap); + cap += sizeof(uint16_t); + ud->id = cap; + cap += len; + ud->type = *cap++; + switch (ud->type) { + case 'f': + ud->flag = *cap++; + if (flags == 0 && + !VALID_BOOLEAN(ud->flag)) + ud->flag = 0; + ud->num = ABSENT_NUMERIC; + ud->str = ABSENT_STRING; + break; + case 'n': + ud->flag = ABSENT_BOOLEAN; + ud->num = (short)le16dec(cap); + if (flags == 0 && + !VALID_NUMERIC(ud->num)) + ud->num = ABSENT_NUMERIC; + ud->str = ABSENT_STRING; + cap += sizeof(uint16_t); + break; + case 's': + ud->flag = ABSENT_BOOLEAN; + ud->num = ABSENT_NUMERIC; + len = le16dec(cap); + cap += sizeof(uint16_t); + if (len > 0) + ud->str = cap; + else if (flags == 0) + ud->str = ABSENT_STRING; + else + ud->str = CANCELLED_STRING; + cap += len; + break; + default: + goto out; + } + } + } else { + term->_nuserdefs = 0; + if (term->_userdefs) { + free(term->_userdefs); + term->_userdefs = NULL; + } + } + + return 1; +out: + errno = EINVAL; + return -1; +} + +static int +_ti_checkname(const char *name, const char *termname, const char *termalias) +{ + const char *alias, *s; + size_t len, l; + + /* Check terminal name matches. */ + if (strcmp(termname, name) == 0) + return 1; + + /* Check terminal aliases match. */ + if (termalias == NULL) + return 0; + + len = strlen(name); + alias = termalias; + while (*alias != '\0') { + s = strchr(alias, '|'); + if (s == NULL) + l = strlen(alias); + else + l = (size_t)(s - alias); + if (len == l && memcmp(alias, name, l) == 0) + return 1; + if (s == NULL) + break; + alias = s + 1; + } + + /* No match. */ + return 0; +} + +static int +_ti_dbgetterm(TERMINAL *term, const char *path, const char *name, int flags) +{ + struct cdbr *db; + const void *data; + const uint8_t *data8; + size_t len, klen; + int r; + + r = snprintf(__ti_database, sizeof(__ti_database), "%s.cdb", path); + if (r < 0 || (size_t)r > sizeof(__ti_database)) { + db = NULL; + errno = ENOENT; /* To fall back to a non extension. */ + } else + db = cdbr_open(__ti_database, CDBR_DEFAULT); + + /* Target file *may* be a cdb file without the extension. */ + if (db == NULL && errno == ENOENT) { + len = strlcpy(__ti_database, path, sizeof(__ti_database)); + if (len < sizeof(__ti_database)) + db = cdbr_open(__ti_database, CDBR_DEFAULT); + } + if (db == NULL) + return -1; + + r = 0; + klen = strlen(name) + 1; + if (cdbr_find(db, name, klen, &data, &len) == -1) + goto out; + data8 = data; + if (len == 0) + goto out; + + /* If the entry is an alias, load the indexed terminfo description. */ + if (data8[0] == 2) { + if (cdbr_get(db, le32dec(data8 + 1), &data, &len)) + goto out; + data8 = data; + } + + r = _ti_readterm(term, data, len, flags); + /* Ensure that this is the right terminfo description. */ + if (r == 1) + r = _ti_checkname(name, term->name, term->_alias); + /* Remember the database we read. */ + if (r == 1) + _ti_database = __ti_database; + +out: + cdbr_close(db); + return r; +} + +static int +_ti_dbgettermp(TERMINAL *term, const char *path, const char *name, int flags) +{ + const char *p; + char pathbuf[PATH_MAX]; + size_t l; + int r, e; + + e = -1; + r = 0; + do { + for (p = path; *path != '\0' && *path != ':'; path++) + continue; + l = (size_t)(path - p); + if (l != 0 && l + 1 < sizeof(pathbuf)) { + memcpy(pathbuf, p, l); + pathbuf[l] = '\0'; + r = _ti_dbgetterm(term, pathbuf, name, flags); + if (r == 1) + return 1; + if (r == 0) + e = 0; + } + } while (*path++ == ':'); + return e; +} + +static int +_ti_findterm(TERMINAL *term, const char *name, int flags) +{ + int r; + char *c, *e; + + _ti_database = NULL; + r = 0; + + if ((e = getenv("TERMINFO")) != NULL && *e != '\0') { + if (e[0] == '/') + return _ti_dbgetterm(term, e, name, flags); + } + + c = NULL; + if (e == NULL && (c = getenv("TERMCAP")) != NULL) { + if (*c != '\0' && *c != '/') { + c = strdup(c); + if (c != NULL) { + e = captoinfo(c); + free(c); + } + } + } + + if (e != NULL) { + TIC *tic; + + if (c == NULL) + e = strdup(e); /* So we don't destroy env */ + if (e == NULL) + tic = NULL; + else { + tic = _ti_compile(e, TIC_WARNING | + TIC_ALIAS | TIC_DESCRIPTION | TIC_EXTRA); + free(e); + } + if (tic != NULL && + _ti_checkname(name, tic->name, tic->alias) == 1) + { + uint8_t *f; + ssize_t len; + + len = _ti_flatten(&f, tic); + if (len != -1) { + r = _ti_readterm(term, (char *)f, (size_t)len, + flags); + free(f); + } + } + _ti_freetic(tic); + if (r == 1) { + if (c == NULL) + _ti_database = "$TERMINFO"; + else + _ti_database = "$TERMCAP"; + return r; + } + } + + if ((e = getenv("TERMINFO_DIRS")) != NULL) + return _ti_dbgettermp(term, e, name, flags); + + if ((e = getenv("HOME")) != NULL) { + char homepath[PATH_MAX]; + + if (snprintf(homepath, sizeof(homepath), "%s/.terminfo", e) > 0) + r = _ti_dbgetterm(term, homepath, name, flags); + } + if (r != 1) + r = _ti_dbgettermp(term, _PATH_TERMINFO, name, flags); + + return r; +} + +int +_ti_getterm(TERMINAL *term, const char *name, int flags) +{ + int r; + size_t i; + const struct compiled_term *t; + + r = _ti_findterm(term, name, flags); + if (r == 1) + return r; + + for (i = 0; i < __arraycount(compiled_terms); i++) { + t = &compiled_terms[i]; + if (strcmp(name, t->name) == 0) { + r = _ti_readterm(term, t->cap, t->caplen, flags); + break; + } + } + + return r; +} Index: contrib/mg/term_private.h =================================================================== --- /dev/null +++ contrib/mg/term_private.h @@ -0,0 +1,163 @@ +/* $NetBSD: term_private.h,v 1.11 2013/01/24 10:41:28 roy Exp $ */ + +/* + * Copyright (c) 2009, 2010, 2013 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TERM_PRIVATE_H_ +#define _TERM_PRIVATE_H_ + +/* This header should only be used by libterminfo, tic and infocmp. */ + +/* The terminfo database structure is private to us, + * so it's documented here. + * terminfo defines the largest number as 32767 and the largest + * compiled entry as 4093 bytes long. + * Thus, we store all numbers as uint16_t, including string length. + * All strings are prefixed by length, including the null terminator. + * The largest string length we can handle is 65535 bytes, + * including the null terminator. + * The largest capability block we can handle is 65535 bytes. + * This means that we exceed the current terminfo defined limits. + * + * Version 1 capabilities are defined as: + * header byte (always 1) + * name + * description, + * cap length, num flags, index, char, + * cap length, num numbers, index, number, + * cap length, num strings, index, string, + * cap length, num undefined caps, name, type (char), flag, number, string + * + * Version 2 entries are aliases and defined as: + * header byte (always 2) + * 32bit id of the corresponding terminal in the file + * name + * + * The database itself is created using cdbw(3) and the numbers are + * always stored as little endian. + */ + +#include + +#define _TERMINFO + +/* We use the same ncurses tic macros so that our data is identical + * when a caller uses the long name macros to access te terminfo data + * directly. */ +#define ABSENT_BOOLEAN ((signed char)-1) /* 255 */ +#define ABSENT_NUMERIC (-1) +#define ABSENT_STRING (char *)0 +#define CANCELLED_BOOLEAN ((signed char)-2) /* 254 */ +#define CANCELLED_NUMERIC (-2) +#define CANCELLED_STRING (char *)(-1) +#define VALID_BOOLEAN(s) ((unsigned char)(s) <= 1) /* reject "-1" */ +#define VALID_NUMERIC(s) ((s) >= 0) +#define VALID_STRING(s) ((s) != CANCELLED_STRING && (s) != ABSENT_STRING) + +typedef struct { + const char *id; + char type; + char flag; + short num; + const char *str; +} TERMUSERDEF; + +typedef struct { + int fildes; + /* We need to expose these so that the macros work */ + const char *name; + const char *desc; + signed char *flags; + short *nums; + const char **strs; + /* Storage area for terminfo data */ + char *_area; + size_t _arealen; + size_t _nuserdefs; + TERMUSERDEF *_userdefs; + /* So we don't rely on the global ospeed */ + short _ospeed; + /* Output buffer for tparm */ + char *_buf; + size_t _buflen; + size_t _bufpos; + /* A-Z static variables for tparm */ + long _snums[26]; + /* aliases of the terminal, | separated */ + const char *_alias; +} TERMINAL; + +extern const char * _ti_database; + +ssize_t _ti_flagindex(const char *); +ssize_t _ti_numindex(const char *); +ssize_t _ti_strindex(const char *); +const char * _ti_flagid(ssize_t); +const char * _ti_numid(ssize_t); +const char * _ti_strid(ssize_t); +int _ti_getterm(TERMINAL *, const char *, int); +void _ti_setospeed(TERMINAL *); + +/* libterminfo can compile terminfo strings too */ +#define TIC_WARNING (1 << 0) +#define TIC_DESCRIPTION (1 << 1) +#define TIC_ALIAS (1 << 2) +#define TIC_COMMENT (1 << 3) +#define TIC_EXTRA (1 << 4) + +#define UINT16_T_MAX 0xffff + +typedef struct { + char *buf; + size_t buflen; + size_t bufpos; + size_t entries; +} TBUF; + +typedef struct { + char *name; + char *alias; + char *desc; + TBUF flags; + TBUF nums; + TBUF strs; + TBUF extras; +} TIC; + +char *_ti_grow_tbuf(TBUF *, size_t); +char *_ti_get_token(char **, char); +char *_ti_find_cap(TBUF *, char, short); +char *_ti_find_extra(TBUF *, const char *); +size_t _ti_store_extra(TIC *, int, char *, char, char, short, + char *, size_t, int); +TIC *_ti_compile(char *, int); +ssize_t _ti_flatten(uint8_t **, const TIC *); +void _ti_freetic(TIC *); + +#define TPARM_MAX 9 /* not likely to change */ +int _ti_parm_analyse(const char *, int *, int); +#endif Index: contrib/mg/termcap.c =================================================================== --- /dev/null +++ contrib/mg/termcap.c @@ -0,0 +1,607 @@ +/* $NetBSD: termcap.c,v 1.22 2017/05/04 09:42:23 roy Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#ifndef __UNCONST +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +extern void mi_vector_hash(const void * __restrict, size_t, uint32_t, + uint32_t[3]); + +#include "termcap_map.c" +#include "termcap_hash.c" + +char *UP; +char *BC; + +/* ARGSUSED */ +int +tgetent(__unused char *bp, const char *name) +{ + int errret; + static TERMINAL *last = NULL; + + /* Free the old term */ + if (cur_term != NULL) { + if (last != NULL && cur_term != last) + del_curterm(last); + last = cur_term; + } + errret = -1; + if (setupterm(name, STDOUT_FILENO, &errret) != 0) + return errret; + + if (last == NULL) + last = cur_term; + + if (pad_char != NULL) + PC = pad_char[0]; + UP = __UNCONST(cursor_up); + BC = __UNCONST(cursor_left); + return 1; +} + +int +tgetflag(const char *id2) +{ + uint32_t ind; + size_t i; + TERMUSERDEF *ud; + const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; + + if (cur_term == NULL) + return 0; + + ind = _t_flaghash((const unsigned char *)id, strlen(id)); + if (ind < __arraycount(_ti_cap_flagids)) { + if (strcmp(id, _ti_cap_flagids[ind].id) == 0) + return cur_term->flags[_ti_cap_flagids[ind].ti]; + } + for (i = 0; i < cur_term->_nuserdefs; i++) { + ud = &cur_term->_userdefs[i]; + if (ud->type == 'f' && strcmp(ud->id, id) == 0) + return ud->flag; + } + return 0; +} + +int +tgetnum(const char *id2) +{ + uint32_t ind; + size_t i; + TERMUSERDEF *ud; + const TENTRY *te; + const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; + + if (cur_term == NULL) + return -1; + + ind = _t_numhash((const unsigned char *)id, strlen(id)); + if (ind < __arraycount(_ti_cap_numids)) { + te = &_ti_cap_numids[ind]; + if (strcmp(id, te->id) == 0) { + if (!VALID_NUMERIC(cur_term->nums[te->ti])) + return ABSENT_NUMERIC; + return cur_term->nums[te->ti]; + } + } + for (i = 0; i < cur_term->_nuserdefs; i++) { + ud = &cur_term->_userdefs[i]; + if (ud->type == 'n' && strcmp(ud->id, id) == 0) { + if (!VALID_NUMERIC(ud->num)) + return ABSENT_NUMERIC; + return ud->num; + } + } + return -1; +} + +char * +tgetstr(const char *id2, char **area) +{ + uint32_t ind; + size_t i; + TERMUSERDEF *ud; + const char *str; + const char id[] = { id2[0], id2[0] ? id2[1] : '\0', '\0' }; + + if (cur_term == NULL) + return NULL; + + str = NULL; + ind = _t_strhash((const unsigned char *)id, strlen(id)); + if (ind < __arraycount(_ti_cap_strids)) { + if (strcmp(id, _ti_cap_strids[ind].id) == 0) { + str = cur_term->strs[_ti_cap_strids[ind].ti]; + if (str == NULL) + return NULL; + } + } + if (str != NULL) + for (i = 0; i < cur_term->_nuserdefs; i++) { + ud = &cur_term->_userdefs[i]; + if (ud->type == 's' && strcmp(ud->id, id) == 0) + str = ud->str; + } + + /* XXX: FXIXME + * We should fix sgr0(me) as it has a slightly different meaning + * for termcap. */ + + if (str != NULL && area != NULL && *area != NULL) { + char *s; + s = *area; + strcpy(*area, str); + *area += strlen(*area) + 1; + return s; + } + + return __UNCONST(str); +} + +char * +tgoto(const char *cm, int destcol, int destline) +{ + + return tiparm(cm, destline, destcol); +} + +static const char * +flagname(const char *key) +{ + uint32_t idx; + + idx = _t_flaghash((const unsigned char *)key, strlen(key)); + if (idx < __arraycount(_ti_cap_flagids) && + strcmp(key, _ti_cap_flagids[idx].id) == 0) + return _ti_flagid(_ti_cap_flagids[idx].ti); + return key; +} + +static const char * +numname(const char *key) +{ + uint32_t idx; + + idx = _t_numhash((const unsigned char *)key, strlen(key)); + if (idx < __arraycount(_ti_cap_numids) && + strcmp(key, _ti_cap_numids[idx].id) == 0) + return _ti_numid(_ti_cap_numids[idx].ti); + return key; +} + +static const char * +strname(const char *key) +{ + uint32_t idx; + + idx = _t_strhash((const unsigned char *)key, strlen(key)); + if (idx < __arraycount(_ti_cap_strids) && + strcmp(key, _ti_cap_strids[idx].id) == 0) + return _ti_strid(_ti_cap_strids[idx].ti); + + if (strcmp(key, "tc") == 0) + return "use"; + + return key; +} + +/* Print a parameter if needed */ +static size_t +printparam(char **dst, char p, bool *nop) +{ + if (*nop) { + *nop = false; + return 0; + } + + *(*dst)++ = '%'; + *(*dst)++ = 'p'; + *(*dst)++ = '0' + p; + return 3; +} + +/* Convert a termcap character into terminfo equivalents */ +static size_t +printchar(char **dst, const char **src) +{ + char v; + size_t l; + + l = 4; + v = *++(*src); + if (v == '\\') { + v = *++(*src); + switch (v) { + case '0': + case '1': + case '2': + case '3': + v = 0; + while (isdigit((unsigned char) **src)) + v = 8 * v + (*(*src)++ - '0'); + (*src)--; + break; + case '\0': + v = '\\'; + break; + } + } else if (v == '^') + v = *++(*src) & 0x1f; + *(*dst)++ = '%'; + if (isgraph((unsigned char )v) && + v != ',' && v != '\'' && v != '\\' && v != ':') + { + *(*dst)++ = '\''; + *(*dst)++ = v; + *(*dst)++ = '\''; + } else { + *(*dst)++ = '{'; + if (v > 99) { + *(*dst)++ = '0'+ v / 100; + l++; + } + if (v > 9) { + *(*dst)++ = '0' + ((int) (v / 10)) % 10; + l++; + } + *(*dst)++ = '0' + v % 10; + *(*dst)++ = '}'; + } + return l; +} + +/* Convert termcap commands into terminfo commands */ +static const char fmtB[] = "%p0%{10}%/%{16}%*%p0%{10}%m%+"; +static const char fmtD[] = "%p0%p0%{2}%*%-"; +static const char fmtIf[] = "%p0%p0%?"; +static const char fmtThen[] = "%>%t"; +static const char fmtElse[] = "%+%;"; + +static char * +strval(const char *val) +{ + char *info, *ip, c, p; + const char *ps, *pe; + bool nop; + size_t len, l; + + len = 1024; /* no single string should be bigger */ + info = ip = malloc(len); + if (info == NULL) + return 0; + + /* Move the = */ + *ip++ = *val++; + + /* Set ps and pe to point to the start and end of the padding */ + if (isdigit((unsigned char)*val)) { + for (ps = pe = val; + isdigit((unsigned char)*val) || *val == '.'; + val++) + pe++; + if (*val == '*') { + val++; + pe++; + } + } else + ps = pe = NULL; + + nop = false; + l = 0; + p = 1; + for (; *val != '\0'; val++) { + if (l + 2 > len) + goto elen; + if (*val != '%') { + if (*val == ',') { + if (l + 3 > len) + goto elen; + *ip++ = '\\'; + l++; + } + *ip++ = *val; + l++; + continue; + } + switch (c = *++(val)) { + case 'B': + if (l + sizeof(fmtB) > len) + goto elen; + memcpy(ip, fmtB, sizeof(fmtB) - 1); + /* Replace the embedded parameters with real ones */ + ip[2] += p; + ip[19] += p; + ip += sizeof(fmtB) - 1; + l += sizeof(fmtB) - 1; + nop = true; + continue; + case 'D': + if (l + sizeof(fmtD) > len) + goto elen; + memcpy(ip, fmtD, sizeof(fmtD) - 1); + /* Replace the embedded parameters with real ones */ + ip[2] += p; + ip[5] += p; + ip += sizeof(fmtD) - 1; + l += sizeof(fmtD) - 1; + nop = true; + continue; + case 'r': + /* non op as switched below */ + break; + case '2': /* FALLTHROUGH */ + case '3': /* FALLTHROUGH */ + case 'd': + if (l + 7 > len) + goto elen; + l += printparam(&ip, p, &nop); + *ip++ = '%'; + if (c != 'd') { + *ip++ = c; + l++; + } + *ip++ = 'd'; + l += 2; + break; + case '+': + if (l + 13 > len) + goto elen; + l += printparam(&ip, p, &nop); + l += printchar(&ip, &val); + *ip++ = '%'; + *ip++ = c; + *ip++ = '%'; + *ip++ = 'c'; + l += 7; + break; + case '>': + if (l + sizeof(fmtIf) + sizeof(fmtThen) + + sizeof(fmtElse) + (6 * 2) > len) + goto elen; + + memcpy(ip, fmtIf, sizeof(fmtIf) - 1); + /* Replace the embedded parameters with real ones */ + ip[2] += p; + ip[5] += p; + ip += sizeof(fmtIf) - 1; + l += sizeof(fmtIf) - 1; + l += printchar(&ip, &val); + memcpy(ip, fmtThen, sizeof(fmtThen) - 1); + ip += sizeof(fmtThen) - 1; + l += sizeof(fmtThen) - 1; + l += printchar(&ip, &val); + memcpy(ip, fmtElse, sizeof(fmtElse) - 1); + ip += sizeof(fmtElse) - 1; + l += sizeof(fmtElse) - 1; + l += 16; + nop = true; + continue; + case '.': + if (l + 6 > len) + goto elen; + l += printparam(&ip, p, &nop); + *ip++ = '%'; + *ip++ = 'c'; + l += 2; + break; + default: + /* Hope it matches a terminfo command. */ + *ip++ = '%'; + *ip++ = c; + l += 2; + if (c == 'i') + continue; + break; + } + /* Swap p1 and p2 */ + p = 3 - p; + } + + /* \E\ is valid termcap. + * We need to escape the final \ for terminfo. */ + if (l > 2 && info[l - 1] == '\\' && + (info[l - 2] != '\\' && info[l - 2] != '^')) + { + if (l + 1 > len) + goto elen; + *ip++ = '\\'; + } + + /* Add our padding at the end. */ + if (ps != NULL) { + size_t n = (size_t)(pe - ps); + if (l + n + 4 > len) + goto elen; + *ip++ = '$'; + *ip++ = '<'; + strncpy(ip, ps, n); + ip += n; + *ip++ = '/'; + *ip++ = '>'; + } + + *ip = '\0'; + return info; + +elen: + free(info); + errno = ENOMEM; + return NULL; +} + +typedef struct { + const char *name; + const char *cap; +} DEF_INFO; + +static DEF_INFO def_infos[] = { + { "bel", "^G" }, + { "cr", "^M" }, + { "cud1", "^J" }, + { "ht", "^I" }, + { "ind", "^J" }, + { "kbs", "^H" }, + { "kcub1", "^H" }, + { "kcud1", "^J" }, + { "nel", "^M^J" } +}; + +char * +captoinfo(char *cap) +{ + char *info, *ip, *token, *val, *p, tok[3]; + const char *name; + size_t len, lp, nl, vl, rl; + int defs[__arraycount(def_infos)], fv; + + len = strlen(cap) * 2; + len += __arraycount(def_infos) * (5 + 4 + 3); /* reserve for defs */ + info = ip = malloc(len); + if (info == NULL) + return NULL; + + memset(defs, 0, sizeof(defs)); + lp = 0; + tok[2] = '\0'; + for (token = _ti_get_token(&cap, ':'); + token != NULL; + token = _ti_get_token(&cap, ':')) + { + if (token[0] == '\0') + continue; + name = token; + val = p = NULL; + fv = nl = 0; + if (token[1] != '\0') { + tok[0] = token[0]; + tok[1] = token[1]; + nl = 1; + if (token[2] == '\0') { + name = flagname(tok); + val = NULL; + } else if (token[2] == '#') { + name = numname(tok); + val = token + 2; + } else if (token[2] == '=') { + name = strname(tok); + val = strval(token + 2); + fv = 1; + } else + nl = 0; + } + /* If not matched we may need to convert padding still. */ + if (nl == 0) { + p = strchr(name, '='); + if (p != NULL) { + val = strval(p); + *p = '\0'; + fv = 1; + } + } + + /* See if this sets a default. */ + for (nl = 0; nl < __arraycount(def_infos); nl++) { + if (strcmp(name, def_infos[nl].name) == 0) { + defs[nl] = 1; + break; + } + } + + nl = strlen(name); + if (val == NULL) + vl = 0; + else + vl = strlen(val); + rl = nl + vl + 3; /* , \0 */ + + if (lp + rl > len) { + if (rl < 256) + len += 256; + else + len += rl; + p = realloc(info, len); + if (p == NULL) { + if (fv == 1) + free(val); + return NULL; + } + info = p; + } + + if (ip != info) { + *ip++ = ','; + *ip++ = ' '; + } + + strcpy(ip, name); + ip += nl; + if (val != NULL) { + strcpy(ip, val); + ip += vl; + if (fv == 1) + free(val); + } + } + + /* Add any defaults not set above. */ + for (nl = 0; nl < __arraycount(def_infos); nl++) { + if (defs[nl] == 0) { + *ip++ = ','; + *ip++ = ' '; + strcpy(ip, def_infos[nl].name); + ip += strlen(def_infos[nl].name); + *ip++ = '='; + strcpy(ip, def_infos[nl].cap); + ip += strlen(def_infos[nl].cap); + } + } + + *ip = '\0'; + return info; +} + Index: contrib/mg/termcap_hash.c =================================================================== --- /dev/null +++ contrib/mg/termcap_hash.c @@ -0,0 +1,158 @@ +/* DO NOT EDIT + * Automatically generated from termcap.c */ + +#include +#include + +#include "term_private.h" + +static uint32_t +_t_flaghash(const void * __restrict key, size_t keylen) +{ + static const uint8_t g[75] = { + 0x0b, 0x17, 0x00, 0x06, 0x0b, 0x00, 0x23, 0x03, 0x0f, 0x1a, + 0x13, 0x00, 0x04, 0x24, 0x1f, 0x1a, 0x00, 0x00, 0x1e, 0x00, + 0x18, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x1e, 0x0c, 0x00, + 0x00, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x07, + 0x08, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x15, 0x00, 0x00, 0x00, 0x12, 0x1b, + 0x00, 0x16, 0x12, 0x00, 0x03, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000000U, h); + return (g[h[0] % 75] + g[h[1] % 75]) % 37; +} + +#include + +static uint32_t +_t_numhash(const void * __restrict key, size_t keylen) +{ + static const uint8_t g[67] = { + 0x00, 0x1e, 0x01, 0x16, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x06, 0x00, 0x00, 0x07, 0x00, 0x0f, + 0x03, 0x1c, 0x0c, 0x00, 0x08, 0x11, 0x10, 0x00, 0x0c, 0x0d, + 0x06, 0x00, 0x06, 0x1b, 0x1d, 0x00, 0x00, 0x00, 0x18, 0x1a, + 0x00, 0x04, 0x00, 0x0e, 0x20, 0x1b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x1e, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x19, 0x1e, 0x00, 0x02, 0x00, 0x05, 0x00, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000005U, h); + return (g[h[0] % 67] + g[h[1] % 67]) % 33; +} + +#include + +static uint32_t +_t_strhash(const void * __restrict key, size_t keylen) +{ + static const uint16_t g[789] = { + 0x0172, 0x0088, 0x0000, 0x013f, 0x00a0, 0x0037, 0x0151, 0x0000, + 0x0151, 0x0000, 0x0000, 0x0000, 0x0000, 0x006a, 0x0000, 0x0082, + 0x0000, 0x0000, 0x00d4, 0x0025, 0x015e, 0x0000, 0x002e, 0x00b1, + 0x0000, 0x0122, 0x00b1, 0x0000, 0x017f, 0x0141, 0x0172, 0x0000, + 0x0000, 0x000a, 0x010c, 0x0185, 0x014f, 0x00c1, 0x0011, 0x0000, + 0x0000, 0x012e, 0x0000, 0x0045, 0x0067, 0x00c4, 0x000d, 0x012e, + 0x0000, 0x0004, 0x0000, 0x005f, 0x016c, 0x00df, 0x0000, 0x0000, + 0x0000, 0x0000, 0x017d, 0x0099, 0x014a, 0x0000, 0x0000, 0x0032, + 0x011e, 0x000a, 0x0000, 0x0037, 0x0057, 0x0065, 0x00d4, 0x00d5, + 0x0000, 0x00c2, 0x00f0, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x00d6, 0x016d, 0x0000, 0x0086, 0x0000, 0x0114, + 0x00ac, 0x0000, 0x0000, 0x00d6, 0x0000, 0x0000, 0x0000, 0x008a, + 0x0000, 0x0000, 0x0000, 0x0000, 0x013c, 0x0000, 0x0042, 0x0000, + 0x002e, 0x0000, 0x0000, 0x013e, 0x0032, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0130, 0x0000, 0x0000, 0x013c, 0x0073, + 0x0000, 0x0095, 0x0000, 0x0000, 0x0000, 0x011e, 0x0012, 0x0000, + 0x0067, 0x002b, 0x0000, 0x0000, 0x0000, 0x00c7, 0x0109, 0x0006, + 0x0000, 0x00ff, 0x00d6, 0x0000, 0x0051, 0x0187, 0x0000, 0x0000, + 0x0114, 0x0000, 0x00b5, 0x00c9, 0x000d, 0x0000, 0x0000, 0x0000, + 0x017c, 0x00b5, 0x0171, 0x0000, 0x003b, 0x0000, 0x0000, 0x0089, + 0x016f, 0x0000, 0x0000, 0x009a, 0x002f, 0x007c, 0x013e, 0x0000, + 0x0000, 0x0000, 0x0000, 0x011b, 0x0000, 0x0000, 0x00d4, 0x0087, + 0x0000, 0x0000, 0x0000, 0x0015, 0x00f3, 0x0000, 0x00c2, 0x0092, + 0x0157, 0x0000, 0x0000, 0x0000, 0x0161, 0x0000, 0x0000, 0x0033, + 0x0000, 0x00ff, 0x0156, 0x0000, 0x001c, 0x0000, 0x0000, 0x0139, + 0x0156, 0x0000, 0x0098, 0x0000, 0x0069, 0x0093, 0x0000, 0x0000, + 0x012b, 0x0000, 0x0000, 0x0168, 0x015b, 0x0075, 0x0000, 0x00c1, + 0x00dd, 0x0000, 0x0038, 0x0000, 0x00e6, 0x0000, 0x0000, 0x0165, + 0x00a7, 0x0000, 0x0022, 0x0000, 0x00a9, 0x0000, 0x006d, 0x0000, + 0x0000, 0x0000, 0x013a, 0x0000, 0x004d, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0103, 0x0006, 0x006b, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0106, 0x0000, 0x0000, 0x0000, 0x00aa, 0x013a, 0x0090, 0x00d8, + 0x0000, 0x0000, 0x016b, 0x0000, 0x002b, 0x0000, 0x0000, 0x0183, + 0x0174, 0x0000, 0x0120, 0x0000, 0x0167, 0x00b0, 0x00b0, 0x0000, + 0x0111, 0x0000, 0x0000, 0x015a, 0x0177, 0x0000, 0x0000, 0x0000, + 0x0102, 0x0158, 0x0000, 0x0098, 0x0028, 0x00fd, 0x0168, 0x0032, + 0x0000, 0x0000, 0x0000, 0x0029, 0x00cd, 0x0000, 0x0000, 0x005c, + 0x0041, 0x0000, 0x0000, 0x015f, 0x0000, 0x0034, 0x017c, 0x00fc, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00a4, 0x0176, + 0x0000, 0x0000, 0x0140, 0x0000, 0x0164, 0x009c, 0x010e, 0x0000, + 0x0000, 0x0000, 0x00a2, 0x00ad, 0x0000, 0x0170, 0x0000, 0x00f6, + 0x00a3, 0x0000, 0x017f, 0x0053, 0x0183, 0x0000, 0x0000, 0x00e8, + 0x0188, 0x0103, 0x0000, 0x0080, 0x005c, 0x016e, 0x0027, 0x0000, + 0x00b7, 0x0000, 0x0000, 0x0000, 0x0150, 0x0078, 0x0000, 0x0000, + 0x00b7, 0x0000, 0x0000, 0x0038, 0x004b, 0x007b, 0x0000, 0x0000, + 0x00b6, 0x0103, 0x0000, 0x006b, 0x0083, 0x0000, 0x0013, 0x00f3, + 0x0000, 0x0031, 0x00bd, 0x0119, 0x005a, 0x0000, 0x0000, 0x0000, + 0x0000, 0x000a, 0x008a, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000, + 0x0000, 0x0000, 0x0116, 0x0000, 0x00b8, 0x0156, 0x0000, 0x0054, + 0x0000, 0x0000, 0x0092, 0x0000, 0x0000, 0x0000, 0x0000, 0x008b, + 0x0172, 0x0000, 0x0000, 0x0188, 0x0000, 0x005d, 0x0000, 0x0000, + 0x003b, 0x0000, 0x0066, 0x00ff, 0x0000, 0x0000, 0x0000, 0x007c, + 0x0000, 0x00e0, 0x0000, 0x0000, 0x0151, 0x00cc, 0x007d, 0x0000, + 0x015b, 0x000c, 0x00e1, 0x0000, 0x0000, 0x006b, 0x0076, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x010f, 0x00f2, 0x0000, + 0x012f, 0x0107, 0x00b7, 0x0000, 0x00d8, 0x0174, 0x00ea, 0x009d, + 0x006d, 0x0000, 0x0000, 0x00d0, 0x0046, 0x012f, 0x00bd, 0x0000, + 0x0120, 0x00ed, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0042, + 0x00e8, 0x0000, 0x0000, 0x00c7, 0x0136, 0x0000, 0x00e8, 0x0002, + 0x0000, 0x00ce, 0x0000, 0x0000, 0x0000, 0x017a, 0x0004, 0x00f7, + 0x00a2, 0x0000, 0x00da, 0x0046, 0x0037, 0x007f, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0184, 0x0000, 0x0000, 0x012a, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x00e3, 0x00b7, 0x0000, 0x0000, 0x0000, + 0x0000, 0x013a, 0x0000, 0x00a6, 0x0000, 0x0065, 0x0000, 0x0000, + 0x0000, 0x00cb, 0x010f, 0x0050, 0x0000, 0x0000, 0x0000, 0x0000, + 0x003a, 0x0140, 0x0000, 0x0107, 0x0095, 0x00f2, 0x0000, 0x0000, + 0x005b, 0x0000, 0x015c, 0x012f, 0x0000, 0x0171, 0x0000, 0x001e, + 0x0000, 0x0000, 0x00be, 0x0000, 0x0000, 0x0113, 0x0000, 0x011e, + 0x0000, 0x000c, 0x0000, 0x00a0, 0x0000, 0x0147, 0x006b, 0x00cb, + 0x0025, 0x00f5, 0x0000, 0x0000, 0x0000, 0x00df, 0x00d9, 0x0000, + 0x00e9, 0x00ab, 0x00f3, 0x0000, 0x0000, 0x0000, 0x00eb, 0x0087, + 0x0000, 0x00cb, 0x0000, 0x0000, 0x0123, 0x0000, 0x0016, 0x0000, + 0x011a, 0x0000, 0x00c8, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x011c, 0x0000, 0x0000, 0x0000, 0x0000, 0x0008, 0x0000, 0x0000, + 0x0091, 0x00fb, 0x0000, 0x0000, 0x0000, 0x0165, 0x00bc, 0x0024, + 0x015f, 0x0000, 0x0070, 0x00e1, 0x0000, 0x0000, 0x0175, 0x0048, + 0x00c6, 0x0000, 0x008f, 0x00e2, 0x0176, 0x003f, 0x0000, 0x0000, + 0x0000, 0x0035, 0x0000, 0x0011, 0x009e, 0x0000, 0x00d3, 0x003c, + 0x0000, 0x00b0, 0x003e, 0x0180, 0x0000, 0x0064, 0x00ef, 0x0000, + 0x0000, 0x0145, 0x0000, 0x0000, 0x00cf, 0x0000, 0x00e7, 0x0041, + 0x0000, 0x0000, 0x0000, 0x0000, 0x017c, 0x007e, 0x014d, 0x013b, + 0x0000, 0x0000, 0x0000, 0x0122, 0x0000, 0x0000, 0x0000, 0x0069, + 0x0000, 0x0000, 0x0000, 0x0000, 0x00d5, 0x0187, 0x0137, 0x0000, + 0x00f8, 0x0000, 0x0115, 0x008e, 0x0088, 0x0000, 0x0000, 0x0000, + 0x008f, 0x0000, 0x0000, 0x009d, 0x0068, 0x0139, 0x015b, 0x009c, + 0x0039, 0x0000, 0x00b0, 0x0000, 0x0110, 0x0000, 0x0059, 0x000b, + 0x0000, 0x00d2, 0x0082, 0x0000, 0x0000, 0x0000, 0x0000, 0x005a, + 0x013d, 0x0000, 0x0000, 0x004b, 0x0167, 0x0109, 0x0016, 0x0001, + 0x0000, 0x00e4, 0x0000, 0x013d, 0x0000, 0x0000, 0x004d, 0x0000, + 0x0000, 0x0000, 0x0029, 0x0000, 0x0000, 0x005d, 0x0000, 0x0001, + 0x0000, 0x008d, 0x0000, 0x015c, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x00c1, 0x0000, 0x0000, 0x0062, 0x0173, 0x0000, 0x0182, + 0x0000, 0x006c, 0x0000, 0x00f1, 0x0000, 0x00e9, 0x012a, 0x0145, + 0x0000, 0x0158, 0x000c, 0x015b, 0x0000, 0x0000, 0x0143, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x00fd, + 0x0142, 0x0070, 0x00e3, 0x0116, 0x0168, 0x0000, 0x0000, 0x00e5, + 0x00f2, 0x0139, 0x0000, 0x0000, 0x0010, 0x0160, 0x0125, 0x0000, + 0x00b3, 0x0158, 0x0062, 0x010a, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x016e, 0x0000, 0x0000, 0x0000, + }; + uint32_t h[3]; + + mi_vector_hash(key, keylen, 0x00000002U, h); + return (g[h[0] % 789] + g[h[1] % 789]) % 394; +} Index: contrib/mg/termcap_map.c =================================================================== --- /dev/null +++ contrib/mg/termcap_map.c @@ -0,0 +1,510 @@ +/* $NetBSD: termcap_map.c,v 1.3 2010/09/22 06:10:51 roy Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +typedef struct { + const char id[3]; + int ti; +} TENTRY; + +static const TENTRY _ti_cap_flagids[] = { + { "bw", TICODE_bw }, + { "am", TICODE_am }, + { "ut", TICODE_bce }, + { "cc", TICODE_ccc }, + { "xs", TICODE_xhp }, + { "YA", TICODE_xhpa }, + { "YF", TICODE_cpix }, + { "YB", TICODE_crxm }, + { "xt", TICODE_xt }, + { "xn", TICODE_xenl }, + { "eo", TICODE_eo }, + { "gn", TICODE_gn }, + { "hc", TICODE_hc }, + { "HC", TICODE_chts }, + { "km", TICODE_km }, + { "YC", TICODE_daisy }, + { "hs", TICODE_hs }, + { "hl", TICODE_hls }, + { "in", TICODE_in }, + { "YG", TICODE_lpix }, + { "da", TICODE_da }, + { "db", TICODE_db }, + { "mi", TICODE_mir }, + { "ms", TICODE_msgr }, + { "nx", TICODE_nxon }, + { "xb", TICODE_xsb }, + { "NP", TICODE_npc }, + { "ND", TICODE_ndscr }, + { "NR", TICODE_nrrmc }, + { "os", TICODE_os } , + { "5i", TICODE_mc5i }, + { "YD", TICODE_xvpa }, + { "YE", TICODE_sam }, + { "es", TICODE_eslok }, + { "hz", TICODE_hz }, + { "ul", TICODE_ul }, + { "xo", TICODE_xon } +}; + +static const TENTRY _ti_cap_numids[] = { + { "Yo", TICODE_bitwin }, + { "Yp", TICODE_bitype }, + { "Ya", TICODE_bufsz }, + { "BT", TICODE_btns }, + { "co", TICODE_cols }, + { "Yc", TICODE_spinh }, + { "Yb", TICODE_spinv }, + { "it", TICODE_it }, + { "lh", TICODE_lh }, + { "lw", TICODE_lw }, + { "li", TICODE_lines }, + { "lm", TICODE_lm }, + { "ma", TICODE_ma }, + { "sg", TICODE_xmc }, + { "Co", TICODE_colors }, + { "Yd", TICODE_maddr }, + { "Ye", TICODE_mjump }, + { "pa", TICODE_pairs }, + { "MW", TICODE_wnum }, + { "Yf", TICODE_mcs }, + { "Yg", TICODE_mls }, + { "NC", TICODE_ncv }, + { "Nl", TICODE_nlab }, + { "Yh", TICODE_npins }, + { "Yi", TICODE_orc }, + { "Yj", TICODE_orl }, + { "Yk", TICODE_orhi }, + { "Yl", TICODE_orvi }, + { "pb", TICODE_pb }, + { "Ym", TICODE_cps }, + { "vt", TICODE_vt }, + { "Yn", TICODE_widcs }, + { "ws", TICODE_wsl } +}; + +static const TENTRY _ti_cap_strids[] = { + { "ac", TICODE_acsc }, + { "S8", TICODE_scesa }, + { "bt", TICODE_cbt }, + { "bl", TICODE_bel }, + { "Yv", TICODE_bicr }, + { "Zz", TICODE_binel }, + { "Xy", TICODE_birep }, + { "cr", TICODE_cr }, + { "ZA", TICODE_cpi }, + { "ZB", TICODE_lpi }, + { "ZC", TICODE_chr }, + { "ZD", TICODE_cvr }, + { "cs", TICODE_csr }, + { "rP", TICODE_rmp }, + { "Zy", TICODE_csnm }, + { "ct", TICODE_tbc }, + { "MC", TICODE_mgc }, + { "cl", TICODE_clear }, + { "cb", TICODE_el1 }, + { "ce", TICODE_el }, + { "cd", TICODE_ed }, + { "ci", TICODE_csin }, + { "Yw", TICODE_colornm }, + { "ch", TICODE_hpa }, + { "CC", TICODE_cmdch }, + { "cm", TICODE_cup }, + { "do", TICODE_cud1 }, + { "ho", TICODE_home }, + { "vi", TICODE_civis }, + { "le", TICODE_cub1 }, + { "CM", TICODE_mrcup }, + { "ve", TICODE_cnorm }, + { "nd", TICODE_cuf1 }, + { "ll", TICODE_ll }, + { "up", TICODE_cuu1 }, + { "vs", TICODE_cvvis }, + { "Yx", TICODE_defbi }, + { "ZE", TICODE_defc }, + { "dc", TICODE_dch1 }, + { "dl", TICODE_dl1 }, + { "dv", TICODE_devt }, + { "DI", TICODE_dial }, + { "ds", TICODE_dsl }, + { "DK", TICODE_dclk }, + { "S1", TICODE_dispc }, + { "hd", TICODE_hd }, + { "eA", TICODE_enacs }, + { "Yy", TICODE_endbi }, + { "as", TICODE_smacs }, + { "SA", TICODE_smam }, + { "mb", TICODE_blink }, + { "md", TICODE_bold }, + { "ti", TICODE_smcup }, + { "dm", TICODE_smdc }, + { "mh", TICODE_dim }, + { "ZF", TICODE_swidm }, + { "ZG", TICODE_sdrfq }, + { "Xh", TICODE_ehhlm }, + { "im", TICODE_smir }, + { "ZH", TICODE_sitm }, + { "Xl", TICODE_elhlm }, + { "ZI", TICODE_slm }, + { "Xo", TICODE_elohlm }, + { "ZJ", TICODE_smicm }, + { "ZK", TICODE_snlq }, + { "ZL", TICODE_snrmq }, + { "S2", TICODE_smpch }, + { "mp", TICODE_prot }, + { "mr", TICODE_rev }, + { "Xr", TICODE_erhlm }, + { "S4", TICODE_smsc }, + { "mk", TICODE_invis }, + { "ZM", TICODE_sshm }, + { "so", TICODE_smso }, + { "ZN", TICODE_ssubm }, + { "ZO", TICODE_ssupm }, + { "Xt", TICODE_ethlm }, + { "us", TICODE_smul }, + { "ZP", TICODE_sum }, + { "Xv", TICODE_evhlm }, + { "SX", TICODE_smxon }, + { "ec", TICODE_ech }, + { "ae", TICODE_rmacs }, + { "RA", TICODE_rmam }, + { "me", TICODE_sgr0 }, + { "te", TICODE_rmcup }, + { "ed", TICODE_rmdc }, + { "ZQ", TICODE_rwidm }, + { "ei", TICODE_rmir }, + { "ZR", TICODE_ritm }, + { "ZS", TICODE_rlm }, + { "ZT", TICODE_rmicm }, + { "S3", TICODE_rmpch }, + { "S5", TICODE_rmsc }, + { "ZU", TICODE_rshm }, + { "se", TICODE_rmso }, + { "ZV", TICODE_rsubm }, + { "ZW", TICODE_rsupm }, + { "ue", TICODE_rmul }, + { "ZX", TICODE_rum }, + { "RX", TICODE_rmxon }, + { "PA", TICODE_pause }, + { "fh", TICODE_hook }, + { "vb", TICODE_flash }, + { "ff", TICODE_ff }, + { "fs", TICODE_fsl }, + { "Gm", TICODE_getm }, + { "WG", TICODE_wingo }, + { "HU", TICODE_hup }, + { "i1", TICODE_is1 }, + { "i2", TICODE_is2 }, + { "i3", TICODE_is3 }, + { "is", TICODE_is2 }, + { "if", TICODE_if }, + { "iP", TICODE_iprog }, + { "Ic", TICODE_initc }, + { "Ip", TICODE_initp }, + { "ic", TICODE_ich1 }, + { "al", TICODE_il1 }, + { "ip", TICODE_ip }, + { "K1", TICODE_ka1 }, + { "K3", TICODE_ka3 }, + { "K2", TICODE_kb2 }, + { "kb", TICODE_kbs }, + { "kB", TICODE_kcbt }, + { "K4", TICODE_kc1 }, + { "K5", TICODE_kc3 }, + { "ka", TICODE_ktbc }, + { "kC", TICODE_kclr }, + { "kt", TICODE_kctab }, + { "kD", TICODE_kdch1 }, + { "kL", TICODE_kdl1 }, + { "kd", TICODE_kcud1 }, + { "kM", TICODE_krmir }, + { "kE", TICODE_kel }, + { "kS", TICODE_ked }, + { "k0", TICODE_kf0 }, + { "k1", TICODE_kf1 }, + { "k2", TICODE_kf2 }, + { "k3", TICODE_kf3 }, + { "k4", TICODE_kf4 }, + { "k5", TICODE_kf5 }, + { "k6", TICODE_kf6 }, + { "k7", TICODE_kf7 }, + { "k8", TICODE_kf8 }, + { "k9", TICODE_kf9 }, + { "k;", TICODE_kf10 }, + { "F1", TICODE_kf11 }, + { "F2", TICODE_kf12 }, + { "F3", TICODE_kf13 }, + { "F4", TICODE_kf14 }, + { "F5", TICODE_kf15 }, + { "F6", TICODE_kf16 }, + { "F7", TICODE_kf17 }, + { "F8", TICODE_kf18 }, + { "F9", TICODE_kf19 }, + { "FA", TICODE_kf20 }, + { "FB", TICODE_kf21 }, + { "FC", TICODE_kf22 }, + { "FD", TICODE_kf23 }, + { "FE", TICODE_kf24 }, + { "FF", TICODE_kf25 }, + { "FG", TICODE_kf26 }, + { "FH", TICODE_kf27 }, + { "FI", TICODE_kf28 }, + { "FJ", TICODE_kf29 }, + { "FK", TICODE_kf30 }, + { "FL", TICODE_kf31 }, + { "FM", TICODE_kf32 }, + { "FN", TICODE_kf33 }, + { "FO", TICODE_kf34 }, + { "FP", TICODE_kf35 }, + { "FQ", TICODE_kf36 }, + { "FR", TICODE_kf37 }, + { "FS", TICODE_kf38 }, + { "FT", TICODE_kf39 }, + { "FU", TICODE_kf40 }, + { "FV", TICODE_kf41 }, + { "FW", TICODE_kf42 }, + { "FX", TICODE_kf43 }, + { "FY", TICODE_kf44 }, + { "FZ", TICODE_kf45 }, + { "Fa", TICODE_kf46 }, + { "Fb", TICODE_kf47 }, + { "Fc", TICODE_kf48 }, + { "Fd", TICODE_kf49 }, + { "Fe", TICODE_kf50 }, + { "Ff", TICODE_kf51 }, + { "Fg", TICODE_kf52 }, + { "Fh", TICODE_kf53 }, + { "Fi", TICODE_kf54 }, + { "Fj", TICODE_kf55 }, + { "Fk", TICODE_kf56 }, + { "Fl", TICODE_kf57 }, + { "Fm", TICODE_kf58 }, + { "Fn", TICODE_kf59 }, + { "Fo", TICODE_kf60 }, + { "Fp", TICODE_kf61 }, + { "Fq", TICODE_kf62 }, + { "Fr", TICODE_kf63 }, + { "%1", TICODE_khlp }, + { "kh", TICODE_khome }, + { "kI", TICODE_kich1 }, + { "kA", TICODE_kil1 }, + { "kl", TICODE_kcub1 }, + { "kH", TICODE_kll }, + { "%2", TICODE_kmrk }, + { "%3", TICODE_kmsg }, + { "Km", TICODE_kmous }, + { "%4", TICODE_kmov }, + { "%5", TICODE_knxt }, + { "kN", TICODE_knp }, + { "%6", TICODE_kopn }, + { "%7", TICODE_kopt }, + { "kP", TICODE_kpp }, + { "%8", TICODE_kprv }, + { "%9", TICODE_kprt }, + { "%0", TICODE_krdo }, + { "&1", TICODE_kref }, + { "&2", TICODE_krfr }, + { "&3", TICODE_krpl }, + { "&4", TICODE_krst }, + { "&5", TICODE_kres }, + { "kr", TICODE_kcuf1 }, + { "&6", TICODE_ksav }, + { "&9", TICODE_kBEG }, + { "&0", TICODE_kCAN }, + { "*1", TICODE_kCMD }, + { "*2", TICODE_kCPY }, + { "*3", TICODE_kCRT }, + { "*4", TICODE_kDC }, + { "*5", TICODE_kDL }, + { "*6", TICODE_kslt }, + { "*7", TICODE_kEND }, + { "*8", TICODE_kEOL }, + { "*9", TICODE_kEXT }, + { "kF", TICODE_kind }, + { "*0", TICODE_kFND }, + { "#1", TICODE_kHLP }, + { "#2", TICODE_kHOM }, + { "#3", TICODE_kIC }, + { "#4", TICODE_kLFT }, + { "%a", TICODE_kMSG }, + { "%b", TICODE_kMOV }, + { "%c", TICODE_kNXT }, + { "%d", TICODE_kOPT }, + { "%e", TICODE_kPRV }, + { "%f", TICODE_kPRT }, + { "kR", TICODE_kri }, + { "%g", TICODE_kRDO }, + { "%h", TICODE_kRPL }, + { "%i", TICODE_kRIT }, + { "%j", TICODE_kRES }, + { "!1", TICODE_kSAV }, + { "!2", TICODE_kSPD }, + { "kT", TICODE_khts }, + { "!3", TICODE_kUND }, + { "&7", TICODE_kspd }, + { "&8", TICODE_kund }, + { "ku", TICODE_kcuu1 }, + { "ke", TICODE_rmkx }, + { "ks", TICODE_smkx }, + { "l0", TICODE_lf0 }, + { "l1", TICODE_lf1 }, + { "l2", TICODE_lf2 }, + { "l3", TICODE_lf3 }, + { "l4", TICODE_lf4 }, + { "l5", TICODE_lf5 }, + { "l6", TICODE_lf6 }, + { "l7", TICODE_lf7 }, + { "l8", TICODE_lf8 }, + { "l9", TICODE_lf9 }, + { "la", TICODE_lf10 }, + { "Lf", TICODE_fln }, + { "LF", TICODE_rmln }, + { "LO", TICODE_smln }, + { "mo", TICODE_rmm }, + { "mm", TICODE_smm }, + { "ZY", TICODE_mhpa }, + { "ZZ", TICODE_mcud1 }, + { "Za", TICODE_mcub1 }, + { "Zb", TICODE_mcuf1 }, + { "Zc", TICODE_mvpa }, + { "Zd", TICODE_mcuu1 }, + { "Mi", TICODE_minfo }, + { "nw", TICODE_nel }, + { "Ze", TICODE_porder }, + { "oc", TICODE_oc }, + { "op", TICODE_op }, + { "pc", TICODE_pad }, + { "DC", TICODE_dch }, + { "DL", TICODE_dl }, + { "DO", TICODE_cud }, + { "Zf", TICODE_mcud }, + { "IC", TICODE_ich }, + { "SF", TICODE_indn }, + { "AL", TICODE_il }, + { "LE", TICODE_cub }, + { "Zg", TICODE_mcub }, + { "RI", TICODE_cuf }, + { "Zh", TICODE_mcuf }, + { "SR", TICODE_rin }, + { "UP", TICODE_cuu }, + { "Zi", TICODE_mcuu }, + { "S6", TICODE_pctrm }, + { "pk", TICODE_pfkey }, + { "pl", TICODE_pfloc }, + { "xl", TICODE_pfxl }, + { "px", TICODE_pfx }, + { "pn", TICODE_pln }, + { "ps", TICODE_mc0 }, + { "pO", TICODE_mc5p }, + { "pf", TICODE_mc4 }, + { "po", TICODE_mc5 }, + { "PU", TICODE_pulse }, + { "QD", TICODE_qdial }, + { "RC", TICODE_rmclk }, + { "rp", TICODE_rep }, + { "RF", TICODE_rfi }, + { "RQ", TICODE_reqmp }, + { "r1", TICODE_rs1 }, + { "r2", TICODE_rs2 }, + { "r3", TICODE_rs3 }, + { "rf", TICODE_rf }, + { "rc", TICODE_rc }, + { "cv", TICODE_vpa }, + { "sc", TICODE_sc }, + { "S7", TICODE_scesc }, + { "sf", TICODE_ind }, + { "sr", TICODE_ri }, + { "Zj", TICODE_scs }, + { "s0", TICODE_s0ds }, + { "s1", TICODE_s1ds }, + { "s2", TICODE_s2ds }, + { "s3", TICODE_s3ds }, + { "sA", TICODE_sgr1 }, + { "AB", TICODE_setab }, + { "AF", TICODE_setaf }, + { "sa", TICODE_sgr }, + { "Sb", TICODE_setb }, + { "Zk", TICODE_smgb }, + { "Zl", TICODE_smgbp }, + { "SC", TICODE_sclk }, + { "Yz", TICODE_slines }, + { "sL", TICODE_slength }, + { "sp", TICODE_scp }, + { "Sf", TICODE_setf }, + { "ML", TICODE_smgl }, /* We should fallback to TICODE_smglr */ + { "Zm", TICODE_smglp }, + { "YZ", TICODE_slines }, + { "YI", TICODE_slength }, + { "MR", TICODE_smgr }, + { "Zn", TICODE_smgrp }, + { "st", TICODE_hts }, + { "MT", TICODE_smgtb }, + { "Zo", TICODE_smgt }, + { "Zp", TICODE_smgtp }, + { "wi", TICODE_wind }, + { "Zq", TICODE_sbim }, + { "Zr", TICODE_scsd }, + { "Zs", TICODE_rbim }, + { "Zt", TICODE_rcsd }, + { "Zu", TICODE_subcs }, + { "Zv", TICODE_supcs }, + { "ta", TICODE_ht }, + { "Zw", TICODE_docr }, + { "ts", TICODE_tsl }, + { "TO", TICODE_tone }, + { "u0", TICODE_u0 }, + { "u1", TICODE_u1 }, + { "u2", TICODE_u2 }, + { "u3", TICODE_u3 }, + { "u4", TICODE_u4 }, + { "u5", TICODE_u5 }, + { "u6", TICODE_u6 }, + { "u7", TICODE_u7 }, + { "u8", TICODE_u8 }, + { "u9", TICODE_u9 }, + { "uc", TICODE_uc }, + { "hu", TICODE_hu }, + { "WA", TICODE_wait }, + { "XF", TICODE_xoffc }, + { "XN", TICODE_xonc }, + { "Zx", TICODE_zerom }, + + /* NetBSD extensions */ + { "@0", TICODE_kfnd }, + { "@1", TICODE_kbeg }, + { "@2", TICODE_kcan }, + { "@3", TICODE_kclo }, + { "@4", TICODE_kcmd }, + { "@5", TICODE_kcpy }, + { "@6", TICODE_kcrt }, + { "@7", TICODE_kend }, + { "@8", TICODE_kent }, + { "@9", TICODE_kext }, +}; Index: contrib/mg/terminfo_term.h =================================================================== --- /dev/null +++ contrib/mg/terminfo_term.h @@ -0,0 +1,2005 @@ +/* $NetBSD: term.h,v 1.22 2017/03/23 00:39:06 roy Exp $ */ + +/* + * Copyright (c) 2009, 2010, 2011, 2013 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TERM_H_ +#define _TERM_H_ + +#ifndef ERR +#define ERR (-1) /* Error return */ +#define OK (0) /* Success return */ +#endif + +/* Define available terminfo flags */ +enum TIFLAGS { + TICODE_bw, + TICODE_am, + TICODE_bce, + TICODE_ccc, + TICODE_xhp, + TICODE_xhpa, + TICODE_cpix, + TICODE_crxm, + TICODE_xt, + TICODE_xenl, + TICODE_eo, + TICODE_gn, + TICODE_hc, + TICODE_chts, + TICODE_km, + TICODE_daisy, + TICODE_hs, + TICODE_hls, + TICODE_in, + TICODE_lpix, + TICODE_da, + TICODE_db, + TICODE_mir, + TICODE_msgr, + TICODE_nxon, + TICODE_xsb, + TICODE_npc, + TICODE_ndscr, + TICODE_nrrmc, + TICODE_os, + TICODE_mc5i, + TICODE_xvpa, + TICODE_sam, + TICODE_eslok, + TICODE_hz, + TICODE_ul, + TICODE_xon +}; +#define TIFLAGMAX TICODE_xon + +#define t_auto_left_margin(t) (t)->flags[TICODE_bw] +#define t_auto_right_margin(t) (t)->flags[TICODE_am] +#define t_back_color_erase(t) (t)->flags[TICODE_bce] +#define t_can_change(t) (t)->flags[TICODE_ccc] +#define t_ceol_standout_glitch(t) (t)->flags[TICODE_xhp] +#define t_col_addr_glitch(t) (t)->flags[TICODE_xhpa] +#define t_cpi_changes_res(t) (t)->flags[TICODE_cpix] +#define t_cr_cancels_micro_mode(t) (t)->flags[TICODE_crxm] +#define t_dest_tabs_magic_smso(t) (t)->flags[TICODE_xt] +#define t_eat_newline_glitch(t) (t)->flags[TICODE_xenl] +#define t_erase_overstrike(t) (t)->flags[TICODE_eo] +#define t_generic_type(t) (t)->flags[TICODE_gn] +#define t_hard_copy(t) (t)->flags[TICODE_hc] +#define t_hard_cursor(t) (t)->flags[TICODE_chts] +#define t_has_meta_key(t) (t)->flags[TICODE_km] +#define t_has_print_wheel(t) (t)->flags[TICODE_daisy] +#define t_has_status_line(t) (t)->flags[TICODE_hs] +#define t_hue_light_saturation(t) (t)->flags[TICODE_hls] +#define t_insert_null_glitch(t) (t)->flags[TICODE_in] +#define t_lpi_changes_yes(t) (t)->flags[TICODE_lpix] +#define t_memory_above(t) (t)->flags[TICODE_da] +#define t_memory_below(t) (t)->flags[TICODE_db] +#define t_move_insert_mode(t) (t)->flags[TICODE_mir] +#define t_move_standout_mode(t) (t)->flags[TICODE_msgr] +#define t_needs_xon_xoff(t) (t)->flags[TICODE_nxon] +#define t_no_esc_ctlc(t) (t)->flags[TICODE_xsb] +#define t_no_pad_char(t) (t)->flags[TICODE_npc] +#define t_non_dest_scroll_region(t) (t)->flags[TICODE_ndscr] +#define t_non_rev_rmcup(t) (t)->flags[TICODE_nrrmc] +#define t_over_strike(t) (t)->flags[TICODE_os] +#define t_prtr_silent(t) (t)->flags[TICODE_mc5i] +#define t_row_addr_glitch(t) (t)->flags[TICODE_xvpa] +#define t_semi_auto_right_margin(t) (t)->flags[TICODE_sam] +#define t_status_line_esc_ok(t) (t)->flags[TICODE_eslok] +#define t_tilde_glitch(t) (t)->flags[TICODE_hz] +#define t_transparent_underline(t) (t)->flags[TICODE_ul] +#define t_xon_xoff(t) (t)->flags[TICODE_xon] + +#define auto_left_margin t_auto_left_margin(cur_term) +#define auto_right_margin t_auto_right_margin(cur_term) +#define back_color_erase t_back_color_erase(cur_term) +#define can_change t_can_change(cur_term) +#define ceol_standout_glitch t_ceol_standout_glitch(cur_term) +#define col_addr_glitch t_col_addr_glitch(cur_term) +#define cpi_changes_res t_cpi_changes_res(cur_term) +#define cr_cancels_micro_mode t_cr_cancels_micro_mode(cur_term) +#define dest_tabs_magic_smso t_dest_tabs_magic_smso(cur_term) +#define eat_newline_glitch t_eat_newline_glitch(cur_term) +#define erase_overstrike t_erase_overstrike(cur_term) +#define generic_type t_generic_type(cur_term) +#define hard_copy t_hard_copy(cur_term) +#define hard_cursor t_hard_cursor(cur_term) +#define has_meta_key t_has_meta_key(cur_term) +#define has_print_wheel t_has_print_wheel(cur_term) +#define has_status_line t_has_status_line(cur_term) +#define hue_light_saturation t_hue_light_saturation(cur_term) +#define insert_null_glitch t_insert_null_glitch(cur_term) +#define lpi_changes_yes t_lpi_changes_yes(cur_term) +#define memory_above t_memory_above(cur_term) +#define memory_below t_memory_below(cur_term) +#define move_insert_mode t_move_insert_mode(cur_term) +#define move_standout_mode t_move_standout_mode(cur_term) +#define needs_xon_xoff t_needs_xon_xoff(cur_term) +#define no_esc_ctlc t_no_esc_ctlc(cur_term) +#define no_pad_char t_no_pad_char(cur_term) +#define non_dest_scroll_region t_non_dest_scroll_region(cur_term) +#define non_rev_rmcup t_non_rev_rmcup(cur_term) +#define over_strike t_over_strike(cur_term) +#define prtr_silent t_prtr_silent(cur_term) +#define row_addr_glitch t_row_addr_glitch(cur_term) +#define semi_auto_right_margin t_semi_auto_right_margin(cur_term) +#define status_line_esc_ok t_status_line_esc_ok(cur_term) +#define tilde_glitch t_tilde_glitch(cur_term) +#define transparent_underline t_transparent_underline(cur_term) +#define xon_xoff t_xon_xoff(cur_term) + +/* + * BOOLEAN DESCRIPTIONS + * + * auto_left_margin: cub1 wraps from column 0 to last column + * auto_right_margin: Terminal has automatic margins + * back_color_erase: Screen erased with background colour + * can_change: Terminal can re-define existing colour + * ceol_standout_glitch: Standout not erased by overwriting (hp) + * col_addr_glitch: Only positive motion for hpa/mhba caps + * cpi_changes_res: Changing character pitch changes resolution + * cr_cancels_micro_mode: Using cr turns off micro mode + * dest_tabs_magic_smso: Destructive tabs, magic smso char (t1061) + * eat_newline_glitch: Newline ignored after 80 columns (Concept) + * erase_overstrike: Can erase overstrikes with a blank line + * generic_type: Generic line type (e.g. dialup, switch) + * hard_copy: Hardcopy terminal + * hard_cursor: Cursor is hard to see + * has_meta_key: Has a meta key (shift, sets parity bit) + * has_print_wheel: Printer needs operator to change character set + * has_status_line: Has extra "status line" + * hue_light_saturation: Terminal only uses HLS colour notion (Tektronix) + * insert_null_glitch: Insert mode distinguishes nulls + * lpi_changes_yes: Changing line pitch changes resolution + * memory_above: Display may be retained above the screen + * memory_below: Display may be retained below the screen + * move_insert_mode: Safe to move while in insert mode + * move_standout_mode: Safe to move in standout modes + * needs_xon_xoff: Padding won't work, xon/xoff required + * no_esc_ctlc: Beehive (f1=escape, f2=ctrl C) + * no_pad_char: Pad character doesn't exist + * non_dest_scroll_region: Scrolling region is nondestructive + * non_rev_rmcup: smcup does not reverse rmcup + * over_strike: Terminal overstrikes on hard-copy terminal + * prtr_silent: Printer won't echo on screen + * row_addr_glitch: Only positive motion for vpa/mvpa caps + * semi_auto_right_margin: Printing in last column causes cr + * status_line_esc_ok: Escape can be used on the status line + * tilde_glitch: Hazeltine; can't print tilde (~) + * transparent_underline: Underline character overstrikes + * xon_xoff: Terminal uses xon/xoff handshaking +*/ + +/* Define available terminfo numbers */ +enum TINUMS { + TICODE_bitwin, + TICODE_bitype, + TICODE_bufsz, + TICODE_btns, + TICODE_cols, + TICODE_spinh, + TICODE_spinv, + TICODE_it, + TICODE_lh, + TICODE_lw, + TICODE_lines, + TICODE_lm, + TICODE_ma, + TICODE_xmc, + TICODE_colors, + TICODE_maddr, + TICODE_mjump, + TICODE_pairs, + TICODE_wnum, + TICODE_mcs, + TICODE_mls, + TICODE_ncv, + TICODE_nlab, + TICODE_npins, + TICODE_orc, + TICODE_orl, + TICODE_orhi, + TICODE_orvi, + TICODE_pb, + TICODE_cps, + TICODE_vt, + TICODE_widcs, + TICODE_wsl +}; +#define TINUMMAX TICODE_wsl + +#define t_bit_image_entwining(t) (t)->nums[TICODE_bitwin] +#define t_bit_image_type(t) (t)->nums[TICODE_bitype] +#define t_buffer_capacity(t) (t)->nums[TICODE_bufsz] +#define t_buttons(t) (t)->nums[TICODE_btns] +#define t_columns(t) (t)->nums[TICODE_cols] +#define t_dot_horz_spacing(t) (t)->nums[TICODE_spinh] +#define t_dot_vert_spacing(t) (t)->nums[TICODE_spinv] +#define t_init_tabs(t) (t)->nums[TICODE_it] +#define t_label_height(t) (t)->nums[TICODE_lh] +#define t_label_width(t) (t)->nums[TICODE_lw] +#define t_lines(t) (t)->nums[TICODE_lines] +#define t_lines_of_memory(t) (t)->nums[TICODE_lm] +#define t_max_attributes(t) (t)->nums[TICODE_ma] +#define t_magic_cookie_glitch(t) (t)->nums[TICODE_xmc] +#define t_max_colors(t) (t)->nums[TICODE_colors] +#define t_max_micro_address(t) (t)->nums[TICODE_maddr] +#define t_max_micro_jump(t) (t)->nums[TICODE_mjump] +#define t_max_pairs(t) (t)->nums[TICODE_pairs] +#define t_maximum_windows(t) (t)->nums[TICODE_wnum] +#define t_micro_col_size(t) (t)->nums[TICODE_mcs] +#define t_micro_line_size(t) (t)->nums[TICODE_mls] +#define t_no_color_video(t) (t)->nums[TICODE_ncv] +#define t_num_labels(t) (t)->nums[TICODE_nlab] +#define t_number_of_pins(t) (t)->nums[TICODE_npins] +#define t_output_res_char(t) (t)->nums[TICODE_orc] +#define t_output_res_line(t) (t)->nums[TICODE_orl] +#define t_output_res_horz_inch(t) (t)->nums[TICODE_orhi] +#define t_output_res_vert_inch(t) (t)->nums[TICODE_orvi] +#define t_padding_baud_rate(t) (t)->nums[TICODE_pb] +#define t_print_rate(t) (t)->nums[TICODE_cps] +#define t_virtual_terminal(t) (t)->nums[TICODE_vt] +#define t_wide_char_size(t) (t)->nums[TICODE_widcs] +#define t_width_status_line(t) (t)->nums[TICODE_wsl] + +#define bit_image_entwining t_bit_image_entwining(cur_term) +#define bit_image_type t_bit_image_type(cur_term) +#define buffer_capacity t_buffer_capacity(cur_term) +#define buttons t_buttons(cur_term) +#define columns t_columns(cur_term) +#define dot_horz_spacing t_dot_horz_spacing(cur_term) +#define dot_vert_spacing t_dot_vert_spacing(cur_term) +#define init_tabs t_init_tabs(cur_term) +#define label_height t_label_height(cur_term) +#define label_width t_label_width(cur_term) +#define lines t_lines(cur_term) +#define lines_of_memory t_lines_of_memory(cur_term) +#define max_attributes t_max_attributes(cur_term) +#define magic_cookie_glitch t_magic_cookie_glitch(cur_term) +#define max_colors t_max_colors(cur_term) +#define max_micro_address t_max_micro_address(cur_term) +#define max_micro_jump t_max_micro_jump(cur_term) +#define max_pairs t_max_pairs(cur_term) +#define maximum_windows t_maximum_windows(cur_term) +#define micro_col_size t_micro_col_size(cur_term) +#define micro_line_size t_micro_line_size(cur_term) +#define no_color_video t_no_color_video(cur_term) +#define num_labels t_num_labels(cur_term) +#define number_of_pins t_number_of_pins(cur_term) +#define output_res_char t_output_res_char(cur_term) +#define output_res_line t_output_res_line(cur_term) +#define output_res_horz_inch t_output_res_horz_inch(cur_term) +#define output_res_vert_inch t_output_res_vert_inch(cur_term) +#define padding_baud_rate t_padding_baud_rate(cur_term) +#define print_rate t_print_rate(cur_term) +#define virtual_terminal t_virtual_terminal(cur_term) +#define wide_char_size t_wide_char_size(cur_term) +#define width_status_line t_width_status_line(cur_term) + +/* + * NUMBER DESCRIPTIONS + * + * bit_image_entwining: Number of passes for each bit-map row + * bit_image_type: Type of bit image device + * buffer_capacity: Number of bytes buffered before printing + * buttons: Number of buttons on the mouse + * columns: Number of columns in a line + * dot_horz_spacing: Spacing of dots horizontally in dots per inch + * dot_vert_spacing: Spacing of pins vertically in pins per inch + * init_tabs: Tabs initially every #1 spaces + * label_height: Number of rows in each label + * label_width: Numbre of columns in each label + * lines: Number of lines on a screen or a page + * lines_of_memory: Lines of memory of > lines; 0 means varies + * max_attributes: Maximum combined video attributes terminal can display + * magic_cookie_glitch: Number of blank characters left by smso or rmso + * max_colors: Maximum number of colours on the screen + * max_micro_address: Maximum value in micro_..._addresss + * max_micro_jump: Maximum value in parm_..._micro + * max_pairs: Maximum number of colour-pairs on the screen + * maximum_windows: Maximum number of definable windows + * micro_col_size: Character step size when in micro mode + * micro_line_size: Line step size when in micro mode + * no_color_video: Video attributes that can't be used with colours + * num_labels: Number of labels on screen (start at 1) + * number_of_pins: Number of pins in print-head + * output_res_char: Horizontal resolution in units per character + * output_res_line: Vertical resolution in units per line + * output_res_horz_inch: Horizontal resolution in units per inch + * output_res_vert_inch: Vertical resolution in units per inch + * padding_baud_rate: Lowest baud rate where padding needed + * print_rate: Print rate in characters per second + * virtual_terminal: Virtual terminal number + * wide_char_size: Character step size when in double-wide mode + * width_status_line: Number of columns in status line + */ + +/* Define available terminfo strings */ +enum TISTRS{ + TICODE_acsc, + TICODE_scesa, + TICODE_cbt, + TICODE_bel, + TICODE_bicr, + TICODE_binel, + TICODE_birep, + TICODE_cr, + TICODE_cpi, + TICODE_lpi, + TICODE_chr, + TICODE_cvr, + TICODE_csr, + TICODE_rmp, + TICODE_csnm, + TICODE_tbc, + TICODE_mgc, + TICODE_clear, + TICODE_el1, + TICODE_el, + TICODE_ed, + TICODE_csin, + TICODE_colornm, + TICODE_hpa, + TICODE_cmdch, + TICODE_cwin, + TICODE_cup, + TICODE_cud1, + TICODE_home, + TICODE_civis, + TICODE_cub1, + TICODE_mrcup, + TICODE_cnorm, + TICODE_cuf1, + TICODE_ll, + TICODE_cuu1, + TICODE_cvvis, + TICODE_defbi, + TICODE_defc, + TICODE_dch1, + TICODE_dl1, + TICODE_devt, + TICODE_dial, + TICODE_dsl, + TICODE_dclk, + TICODE_dispc, + TICODE_hd, + TICODE_enacs, + TICODE_endbi, + TICODE_smacs, + TICODE_smam, + TICODE_blink, + TICODE_bold, + TICODE_smcup, + TICODE_smdc, + TICODE_dim, + TICODE_swidm, + TICODE_sdrfq, + TICODE_ehhlm, + TICODE_smir, + TICODE_sitm, + TICODE_elhlm, + TICODE_slm, + TICODE_elohlm, + TICODE_smicm, + TICODE_snlq, + TICODE_snrmq, + TICODE_smpch, + TICODE_prot, + TICODE_rev, + TICODE_erhlm, + TICODE_smsc, + TICODE_invis, + TICODE_sshm, + TICODE_smso, + TICODE_ssubm, + TICODE_ssupm, + TICODE_ethlm, + TICODE_smul, + TICODE_sum, + TICODE_evhlm, + TICODE_smxon, + TICODE_ech, + TICODE_rmacs, + TICODE_rmam, + TICODE_sgr0, + TICODE_rmcup, + TICODE_rmdc, + TICODE_rwidm, + TICODE_rmir, + TICODE_ritm, + TICODE_rlm, + TICODE_rmicm, + TICODE_rmpch, + TICODE_rmsc, + TICODE_rshm, + TICODE_rmso, + TICODE_rsubm, + TICODE_rsupm, + TICODE_rmul, + TICODE_rum, + TICODE_rmxon, + TICODE_pause, + TICODE_hook, + TICODE_flash, + TICODE_ff, + TICODE_fsl, + TICODE_getm, + TICODE_wingo, + TICODE_hup, + TICODE_is1, + TICODE_is2, + TICODE_is3, + TICODE_if, + TICODE_iprog, + TICODE_initc, + TICODE_initp, + TICODE_ich1, + TICODE_il1, + TICODE_ip, + TICODE_ka1, + TICODE_ka3, + TICODE_kb2, + TICODE_kbs, + TICODE_kbeg, + TICODE_kcbt, + TICODE_kc1, + TICODE_kc3, + TICODE_kcan, + TICODE_ktbc, + TICODE_kclr, + TICODE_kclo, + TICODE_kcmd, + TICODE_kcpy, + TICODE_kcrt, + TICODE_kctab, + TICODE_kdch1, + TICODE_kdl1, + TICODE_kcud1, + TICODE_krmir, + TICODE_kend, + TICODE_kent, + TICODE_kel, + TICODE_ked, + TICODE_kext, + TICODE_kf0, + TICODE_kf1, + TICODE_kf2, + TICODE_kf3, + TICODE_kf4, + TICODE_kf5, + TICODE_kf6, + TICODE_kf7, + TICODE_kf8, + TICODE_kf9, + TICODE_kf10, + TICODE_kf11, + TICODE_kf12, + TICODE_kf13, + TICODE_kf14, + TICODE_kf15, + TICODE_kf16, + TICODE_kf17, + TICODE_kf18, + TICODE_kf19, + TICODE_kf20, + TICODE_kf21, + TICODE_kf22, + TICODE_kf23, + TICODE_kf24, + TICODE_kf25, + TICODE_kf26, + TICODE_kf27, + TICODE_kf28, + TICODE_kf29, + TICODE_kf30, + TICODE_kf31, + TICODE_kf32, + TICODE_kf33, + TICODE_kf34, + TICODE_kf35, + TICODE_kf36, + TICODE_kf37, + TICODE_kf38, + TICODE_kf39, + TICODE_kf40, + TICODE_kf41, + TICODE_kf42, + TICODE_kf43, + TICODE_kf44, + TICODE_kf45, + TICODE_kf46, + TICODE_kf47, + TICODE_kf48, + TICODE_kf49, + TICODE_kf50, + TICODE_kf51, + TICODE_kf52, + TICODE_kf53, + TICODE_kf54, + TICODE_kf55, + TICODE_kf56, + TICODE_kf57, + TICODE_kf58, + TICODE_kf59, + TICODE_kf60, + TICODE_kf61, + TICODE_kf62, + TICODE_kf63, + TICODE_kfnd, + TICODE_khlp, + TICODE_khome, + TICODE_kich1, + TICODE_kil1, + TICODE_kcub1, + TICODE_kll, + TICODE_kmrk, + TICODE_kmsg, + TICODE_kmous, + TICODE_kmov, + TICODE_knxt, + TICODE_knp, + TICODE_kopn, + TICODE_kopt, + TICODE_kpp, + TICODE_kprv, + TICODE_kprt, + TICODE_krdo, + TICODE_kref, + TICODE_krfr, + TICODE_krpl, + TICODE_krst, + TICODE_kres, + TICODE_kcuf1, + TICODE_ksav, + TICODE_kBEG, + TICODE_kCAN, + TICODE_kCMD, + TICODE_kCPY, + TICODE_kCRT, + TICODE_kDC, + TICODE_kDL, + TICODE_kslt, + TICODE_kEND, + TICODE_kEOL, + TICODE_kEXT, + TICODE_kind, + TICODE_kFND, + TICODE_kHLP, + TICODE_kHOM, + TICODE_kIC, + TICODE_kLFT, + TICODE_kMSG, + TICODE_kMOV, + TICODE_kNXT, + TICODE_kOPT, + TICODE_kPRV, + TICODE_kPRT, + TICODE_kri, + TICODE_kRDO, + TICODE_kRPL, + TICODE_kRIT, + TICODE_kRES, + TICODE_kSAV, + TICODE_kSPD, + TICODE_khts, + TICODE_kUND, + TICODE_kspd, + TICODE_kund, + TICODE_kcuu1, + TICODE_rmkx, + TICODE_smkx, + TICODE_lf0, + TICODE_lf1, + TICODE_lf2, + TICODE_lf3, + TICODE_lf4, + TICODE_lf5, + TICODE_lf6, + TICODE_lf7, + TICODE_lf8, + TICODE_lf9, + TICODE_lf10, + TICODE_fln, + TICODE_rmln, + TICODE_smln, + TICODE_rmm, + TICODE_smm, + TICODE_mhpa, + TICODE_mcud1, + TICODE_mcub1, + TICODE_mcuf1, + TICODE_mvpa, + TICODE_mcuu1, + TICODE_minfo, + TICODE_nel, + TICODE_porder, + TICODE_oc, + TICODE_op, + TICODE_pad, + TICODE_dch, + TICODE_dl, + TICODE_cud, + TICODE_mcud, + TICODE_ich, + TICODE_indn, + TICODE_il, + TICODE_cub, + TICODE_mcub, + TICODE_cuf, + TICODE_mcuf, + TICODE_rin, + TICODE_cuu, + TICODE_mcuu, + TICODE_pctrm, + TICODE_pfkey, + TICODE_pfloc, + TICODE_pfxl, + TICODE_pfx, + TICODE_pln, + TICODE_mc0, + TICODE_mc5p, + TICODE_mc4, + TICODE_mc5, + TICODE_pulse, + TICODE_qdial, + TICODE_rmclk, + TICODE_rep, + TICODE_rfi, + TICODE_reqmp, + TICODE_rs1, + TICODE_rs2, + TICODE_rs3, + TICODE_rf, + TICODE_rc, + TICODE_vpa, + TICODE_sc, + TICODE_scesc, + TICODE_ind, + TICODE_ri, + TICODE_scs, + TICODE_s0ds, + TICODE_s1ds, + TICODE_s2ds, + TICODE_s3ds, + TICODE_sgr1, + TICODE_setab, + TICODE_setaf, + TICODE_sgr, + TICODE_setb, + TICODE_smgb, + TICODE_smgbp, + TICODE_sclk, + TICODE_setcolor, + TICODE_scp, + TICODE_setf, + TICODE_smgl, + TICODE_smglp, + TICODE_smglr, + TICODE_slines, + TICODE_slength, + TICODE_smgr, + TICODE_smgrp, + TICODE_hts, + TICODE_smgtb, + TICODE_smgt, + TICODE_smgtp, + TICODE_wind, + TICODE_sbim, + TICODE_scsd, + TICODE_rbim, + TICODE_rcsd, + TICODE_subcs, + TICODE_supcs, + TICODE_ht, + TICODE_docr, + TICODE_tsl, + TICODE_tone, + TICODE_u0, + TICODE_u1, + TICODE_u2, + TICODE_u3, + TICODE_u4, + TICODE_u5, + TICODE_u6, + TICODE_u7, + TICODE_u8, + TICODE_u9, + TICODE_uc, + TICODE_hu, + TICODE_wait, + TICODE_xoffc, + TICODE_xonc, + TICODE_zerom +}; +#define TISTRMAX TICODE_zerom + +#define t_acs_chars(t) (t)->strs[TICODE_acsc] +#define t_alt_scancode_esc(t) (t)->strs[TICODE_scesa] +#define t_back_tab(t) (t)->strs[TICODE_cbt] +#define t_bell(t) (t)->strs[TICODE_bel] +#define t_bit_image_carriage_return(t) (t)->strs[TICODE_bicr] +#define t_bit_image_newline(t) (t)->strs[TICODE_binel] +#define t_bit_image_repeat(t) (t)->strs[TICODE_birep] +#define t_carriage_return(t) (t)->strs[TICODE_cr] +#define t_change_char_pitch(t) (t)->strs[TICODE_cpi] +#define t_change_line_pitch(t) (t)->strs[TICODE_lpi] +#define t_change_res_horz(t) (t)->strs[TICODE_chr] +#define t_change_res_vert(t) (t)->strs[TICODE_cvr] +#define t_change_scroll_region(t) (t)->strs[TICODE_csr] +#define t_char_padding(t) (t)->strs[TICODE_rmp] +#define t_char_set_names(t) (t)->strs[TICODE_csnm] +#define t_clear_all_tabs(t) (t)->strs[TICODE_tbc] +#define t_clear_margins(t) (t)->strs[TICODE_mgc] +#define t_clear_screen(t) (t)->strs[TICODE_clear] +#define t_clr_bol(t) (t)->strs[TICODE_el1] +#define t_clr_eol(t) (t)->strs[TICODE_el] +#define t_clr_eos(t) (t)->strs[TICODE_ed] +#define t_code_set_init(t) (t)->strs[TICODE_csin] +#define t_color_names(t) (t)->strs[TICODE_colornm] +#define t_column_address(t) (t)->strs[TICODE_hpa] +#define t_command_character(t) (t)->strs[TICODE_cmdch] +#define t_create_window(t) (t)->strs[TICODE_cwin] +#define t_cursor_address(t) (t)->strs[TICODE_cup] +#define t_cursor_down(t) (t)->strs[TICODE_cud1] +#define t_cursor_home(t) (t)->strs[TICODE_home] +#define t_cursor_invisible(t) (t)->strs[TICODE_civis] +#define t_cursor_left(t) (t)->strs[TICODE_cub1] +#define t_cursor_mem_address(t) (t)->strs[TICODE_mrcup] +#define t_cursor_normal(t) (t)->strs[TICODE_cnorm] +#define t_cursor_right(t) (t)->strs[TICODE_cuf1] +#define t_cursor_to_ll(t) (t)->strs[TICODE_ll] +#define t_cursor_up(t) (t)->strs[TICODE_cuu1] +#define t_cursor_visible(t) (t)->strs[TICODE_cvvis] +#define t_define_bit_image_region(t) (t)->strs[TICODE_defbi] +#define t_define_char(t) (t)->strs[TICODE_defc] +#define t_delete_character(t) (t)->strs[TICODE_dch1] +#define t_delete_line(t) (t)->strs[TICODE_dl1] +#define t_device_type(t) (t)->strs[TICODE_devt] +#define t_dial_phone(t) (t)->strs[TICODE_dial] +#define t_dis_status_line(t) (t)->strs[TICODE_dsl] +#define t_display_clock(t) (t)->strs[TICODE_dclk] +#define t_display_pc_char(t) (t)->strs[TICODE_dispc] +#define t_down_half_time(t) (t)->strs[TICODE_hd] +#define t_ena_acs(t) (t)->strs[TICODE_enacs] +#define t_end_bit_image_region(t) (t)->strs[TICODE_endbi] +#define t_enter_alt_charset_mode(t) (t)->strs[TICODE_smacs] +#define t_enter_am_mode(t) (t)->strs[TICODE_smam] +#define t_enter_blink_mode(t) (t)->strs[TICODE_blink] +#define t_enter_bold_mode(t) (t)->strs[TICODE_bold] +#define t_enter_ca_mode(t) (t)->strs[TICODE_smcup] +#define t_enter_delete_mode(t) (t)->strs[TICODE_smdc] +#define t_enter_dim_mode(t) (t)->strs[TICODE_dim] +#define t_enter_doublewide_mode(t) (t)->strs[TICODE_swidm] +#define t_enter_draft_quality(t) (t)->strs[TICODE_sdrfq] +#define t_enter_horizontal_hl_mode(t) (t)->strs[TICODE_ehhlm] +#define t_enter_insert_mode(t) (t)->strs[TICODE_smir] +#define t_enter_italics_mode(t) (t)->strs[TICODE_sitm] +#define t_enter_left_hl_mode(t) (t)->strs[TICODE_elhlm] +#define t_enter_leftward_mode(t) (t)->strs[TICODE_slm] +#define t_enter_low_hl_mode(t) (t)->strs[TICODE_elohlm] +#define t_enter_micro_mode(t) (t)->strs[TICODE_smicm] +#define t_enter_near_quality_letter(t) (t)->strs[TICODE_snlq] +#define t_enter_normal_quality(t) (t)->strs[TICODE_snrmq] +#define t_enter_pc_charset_mode(t) (t)->strs[TICODE_smpch] +#define t_enter_protected_mode(t) (t)->strs[TICODE_prot] +#define t_enter_reverse_mode(t) (t)->strs[TICODE_rev] +#define t_enter_right_hl_mode(t) (t)->strs[TICODE_erhlm] +#define t_enter_scancode_mode(t) (t)->strs[TICODE_smsc] +#define t_enter_secure_mode(t) (t)->strs[TICODE_invis] +#define t_enter_shadow_mode(t) (t)->strs[TICODE_sshm] +#define t_enter_standout_mode(t) (t)->strs[TICODE_smso] +#define t_enter_subscript_mode(t) (t)->strs[TICODE_ssubm] +#define t_enter_superscript_mode(t) (t)->strs[TICODE_ssupm] +#define t_enter_top_hl_mode(t) (t)->strs[TICODE_ethlm] +#define t_enter_underline_mode(t) (t)->strs[TICODE_smul] +#define t_enter_upward_mode(t) (t)->strs[TICODE_sum] +#define t_enter_vertical_hl_mode(t) (t)->strs[TICODE_evhlm] +#define t_enter_xon_mode(t) (t)->strs[TICODE_smxon] +#define t_erase_chars(t) (t)->strs[TICODE_ech] +#define t_exit_alt_charset_mode(t) (t)->strs[TICODE_rmacs] +#define t_exit_am_mode(t) (t)->strs[TICODE_rmam] +#define t_exit_attribute_mode(t) (t)->strs[TICODE_sgr0] +#define t_exit_ca_mode(t) (t)->strs[TICODE_rmcup] +#define t_exit_delete_mode(t) (t)->strs[TICODE_rmdc] +#define t_exit_doublewide_mode(t) (t)->strs[TICODE_rwidm] +#define t_exit_insert_mode(t) (t)->strs[TICODE_rmir] +#define t_exit_italics_mode(t) (t)->strs[TICODE_ritm] +#define t_exit_leftward_mode(t) (t)->strs[TICODE_rlm] +#define t_exit_micro_mode(t) (t)->strs[TICODE_rmicm] +#define t_exit_pc_charset_mode(t) (t)->strs[TICODE_rmpch] +#define t_exit_scancode_mode(t) (t)->strs[TICODE_rmsc] +#define t_exit_shadow_mode(t) (t)->strs[TICODE_rshm] +#define t_exit_standout_mode(t) (t)->strs[TICODE_rmso] +#define t_exit_subscript_mode(t) (t)->strs[TICODE_rsubm] +#define t_exit_superscript_mode(t) (t)->strs[TICODE_rsupm] +#define t_exit_underline_mode(t) (t)->strs[TICODE_rmul] +#define t_exit_upward_mode(t) (t)->strs[TICODE_rum] +#define t_exit_xon_mode(t) (t)->strs[TICODE_rmxon] +#define t_fixed_pause(t) (t)->strs[TICODE_pause] +#define t_flash_hook(t) (t)->strs[TICODE_hook] +#define t_flash_screen(t) (t)->strs[TICODE_flash] +#define t_form_feed(t) (t)->strs[TICODE_ff] +#define t_from_status_line(t) (t)->strs[TICODE_fsl] +#define t_get_mouse(t) (t)->strs[TICODE_getm] +#define t_goto_window(t) (t)->strs[TICODE_wingo] +#define t_hangup(t) (t)->strs[TICODE_hup] +#define t_init_1string(t) (t)->strs[TICODE_is1] +#define t_init_2string(t) (t)->strs[TICODE_is2] +#define t_init_3string(t) (t)->strs[TICODE_is3] +#define t_init_file(t) (t)->strs[TICODE_if] +#define t_init_prog(t) (t)->strs[TICODE_iprog] +#define t_initialize_color(t) (t)->strs[TICODE_initc] +#define t_initialize_pair(t) (t)->strs[TICODE_initp] +#define t_insert_character(t) (t)->strs[TICODE_ich1] +#define t_insert_line(t) (t)->strs[TICODE_il1] +#define t_insert_padding(t) (t)->strs[TICODE_ip] +#define t_key_a1(t) (t)->strs[TICODE_ka1] +#define t_key_a3(t) (t)->strs[TICODE_ka3] +#define t_key_b2(t) (t)->strs[TICODE_kb2] +#define t_key_backspace(t) (t)->strs[TICODE_kbs] +#define t_key_beg(t) (t)->strs[TICODE_kbeg] +#define t_key_btab(t) (t)->strs[TICODE_kcbt] +#define t_key_c1(t) (t)->strs[TICODE_kc1] +#define t_key_c3(t) (t)->strs[TICODE_kc3] +#define t_key_cancel(t) (t)->strs[TICODE_kcan] +#define t_key_catab(t) (t)->strs[TICODE_ktbc] +#define t_key_clear(t) (t)->strs[TICODE_kclr] +#define t_key_close(t) (t)->strs[TICODE_kclo] +#define t_key_command(t) (t)->strs[TICODE_kcmd] +#define t_key_copy(t) (t)->strs[TICODE_kcpy] +#define t_key_create(t) (t)->strs[TICODE_kcrt] +#define t_key_ctab(t) (t)->strs[TICODE_kctab] +#define t_key_dc(t) (t)->strs[TICODE_kdch1] +#define t_key_dl(t) (t)->strs[TICODE_kdl1] +#define t_key_down(t) (t)->strs[TICODE_kcud1] +#define t_key_eic(t) (t)->strs[TICODE_krmir] +#define t_key_end(t) (t)->strs[TICODE_kend] +#define t_key_enter(t) (t)->strs[TICODE_kent] +#define t_key_eol(t) (t)->strs[TICODE_kel] +#define t_key_eos(t) (t)->strs[TICODE_ked] +#define t_key_exit(t) (t)->strs[TICODE_kext] +#define t_key_f0(t) (t)->strs[TICODE_kf0] +#define t_key_f1(t) (t)->strs[TICODE_kf1] +#define t_key_f2(t) (t)->strs[TICODE_kf2] +#define t_key_f3(t) (t)->strs[TICODE_kf3] +#define t_key_f4(t) (t)->strs[TICODE_kf4] +#define t_key_f5(t) (t)->strs[TICODE_kf5] +#define t_key_f6(t) (t)->strs[TICODE_kf6] +#define t_key_f7(t) (t)->strs[TICODE_kf7] +#define t_key_f8(t) (t)->strs[TICODE_kf8] +#define t_key_f9(t) (t)->strs[TICODE_kf9] +#define t_key_f10(t) (t)->strs[TICODE_kf10] +#define t_key_f11(t) (t)->strs[TICODE_kf11] +#define t_key_f12(t) (t)->strs[TICODE_kf12] +#define t_key_f13(t) (t)->strs[TICODE_kf13] +#define t_key_f14(t) (t)->strs[TICODE_kf14] +#define t_key_f15(t) (t)->strs[TICODE_kf15] +#define t_key_f16(t) (t)->strs[TICODE_kf16] +#define t_key_f17(t) (t)->strs[TICODE_kf17] +#define t_key_f18(t) (t)->strs[TICODE_kf18] +#define t_key_f19(t) (t)->strs[TICODE_kf19] +#define t_key_f20(t) (t)->strs[TICODE_kf20] +#define t_key_f21(t) (t)->strs[TICODE_kf21] +#define t_key_f22(t) (t)->strs[TICODE_kf22] +#define t_key_f23(t) (t)->strs[TICODE_kf23] +#define t_key_f24(t) (t)->strs[TICODE_kf24] +#define t_key_f25(t) (t)->strs[TICODE_kf25] +#define t_key_f26(t) (t)->strs[TICODE_kf26] +#define t_key_f27(t) (t)->strs[TICODE_kf27] +#define t_key_f28(t) (t)->strs[TICODE_kf28] +#define t_key_f29(t) (t)->strs[TICODE_kf29] +#define t_key_f30(t) (t)->strs[TICODE_kf30] +#define t_key_f31(t) (t)->strs[TICODE_kf31] +#define t_key_f32(t) (t)->strs[TICODE_kf32] +#define t_key_f33(t) (t)->strs[TICODE_kf33] +#define t_key_f34(t) (t)->strs[TICODE_kf34] +#define t_key_f35(t) (t)->strs[TICODE_kf35] +#define t_key_f36(t) (t)->strs[TICODE_kf36] +#define t_key_f37(t) (t)->strs[TICODE_kf37] +#define t_key_f38(t) (t)->strs[TICODE_kf38] +#define t_key_f39(t) (t)->strs[TICODE_kf39] +#define t_key_f40(t) (t)->strs[TICODE_kf40] +#define t_key_f41(t) (t)->strs[TICODE_kf41] +#define t_key_f42(t) (t)->strs[TICODE_kf42] +#define t_key_f43(t) (t)->strs[TICODE_kf43] +#define t_key_f44(t) (t)->strs[TICODE_kf44] +#define t_key_f45(t) (t)->strs[TICODE_kf45] +#define t_key_f46(t) (t)->strs[TICODE_kf46] +#define t_key_f47(t) (t)->strs[TICODE_kf47] +#define t_key_f48(t) (t)->strs[TICODE_kf48] +#define t_key_f49(t) (t)->strs[TICODE_kf49] +#define t_key_f50(t) (t)->strs[TICODE_kf50] +#define t_key_f51(t) (t)->strs[TICODE_kf51] +#define t_key_f52(t) (t)->strs[TICODE_kf52] +#define t_key_f53(t) (t)->strs[TICODE_kf53] +#define t_key_f54(t) (t)->strs[TICODE_kf54] +#define t_key_f55(t) (t)->strs[TICODE_kf55] +#define t_key_f56(t) (t)->strs[TICODE_kf56] +#define t_key_f57(t) (t)->strs[TICODE_kf57] +#define t_key_f58(t) (t)->strs[TICODE_kf58] +#define t_key_f59(t) (t)->strs[TICODE_kf59] +#define t_key_f60(t) (t)->strs[TICODE_kf60] +#define t_key_f61(t) (t)->strs[TICODE_kf61] +#define t_key_f62(t) (t)->strs[TICODE_kf62] +#define t_key_f63(t) (t)->strs[TICODE_kf63] +#define t_key_find(t) (t)->strs[TICODE_kfnd] +#define t_key_help(t) (t)->strs[TICODE_khlp] +#define t_key_home(t) (t)->strs[TICODE_khome] +#define t_key_ic(t) (t)->strs[TICODE_kich1] +#define t_key_il(t) (t)->strs[TICODE_kil1] +#define t_key_left(t) (t)->strs[TICODE_kcub1] +#define t_key_ll(t) (t)->strs[TICODE_kll] +#define t_key_mark(t) (t)->strs[TICODE_kmrk] +#define t_key_message(t) (t)->strs[TICODE_kmsg] +#define t_key_mouse(t) (t)->strs[TICODE_kmous] +#define t_key_move(t) (t)->strs[TICODE_kmov] +#define t_key_next(t) (t)->strs[TICODE_knxt] +#define t_key_npage(t) (t)->strs[TICODE_knp] +#define t_key_open(t) (t)->strs[TICODE_kopn] +#define t_key_options(t) (t)->strs[TICODE_kopt] +#define t_key_ppage(t) (t)->strs[TICODE_kpp] +#define t_key_previous(t) (t)->strs[TICODE_kprv] +#define t_key_print(t) (t)->strs[TICODE_kprt] +#define t_key_redo(t) (t)->strs[TICODE_krdo] +#define t_key_reference(t) (t)->strs[TICODE_kref] +#define t_key_refresh(t) (t)->strs[TICODE_krfr] +#define t_key_replace(t) (t)->strs[TICODE_krpl] +#define t_key_restart(t) (t)->strs[TICODE_krst] +#define t_key_resume(t) (t)->strs[TICODE_kres] +#define t_key_right(t) (t)->strs[TICODE_kcuf1] +#define t_key_save(t) (t)->strs[TICODE_ksav] +#define t_key_sbeg(t) (t)->strs[TICODE_kBEG] +#define t_key_scancel(t) (t)->strs[TICODE_kCAN] +#define t_key_scommand(t) (t)->strs[TICODE_kCMD] +#define t_key_scopy(t) (t)->strs[TICODE_kCPY] +#define t_key_screate(t) (t)->strs[TICODE_kCRT] +#define t_key_sdc(t) (t)->strs[TICODE_kDC] +#define t_key_sdl(t) (t)->strs[TICODE_kDL] +#define t_key_select(t) (t)->strs[TICODE_kslt] +#define t_key_send(t) (t)->strs[TICODE_kEND] +#define t_key_seol(t) (t)->strs[TICODE_kEOL] +#define t_key_sexit(t) (t)->strs[TICODE_kEXT] +#define t_key_sf(t) (t)->strs[TICODE_kind] +#define t_key_sfind(t) (t)->strs[TICODE_kFND] +#define t_key_shelp(t) (t)->strs[TICODE_kHLP] +#define t_key_shome(t) (t)->strs[TICODE_kHOM] +#define t_key_sic(t) (t)->strs[TICODE_kIC] +#define t_key_sleft(t) (t)->strs[TICODE_kLFT] +#define t_key_smessage(t) (t)->strs[TICODE_kMSG] +#define t_key_smove(t) (t)->strs[TICODE_kMOV] +#define t_key_snext(t) (t)->strs[TICODE_kNXT] +#define t_key_soptions(t) (t)->strs[TICODE_kOPT] +#define t_key_sprevious(t) (t)->strs[TICODE_kPRV] +#define t_key_sprint(t) (t)->strs[TICODE_kPRT] +#define t_key_sr(t) (t)->strs[TICODE_kri] +#define t_key_sredo(t) (t)->strs[TICODE_kRDO] +#define t_key_sreplace(t) (t)->strs[TICODE_kRPL] +#define t_key_sright(t) (t)->strs[TICODE_kRIT] +#define t_key_srsume(t) (t)->strs[TICODE_kRES] +#define t_key_ssave(t) (t)->strs[TICODE_kSAV] +#define t_key_ssuspend(t) (t)->strs[TICODE_kSPD] +#define t_key_stab(t) (t)->strs[TICODE_khts] +#define t_key_sundo(t) (t)->strs[TICODE_kUND] +#define t_key_suspend(t) (t)->strs[TICODE_kspd] +#define t_key_undo(t) (t)->strs[TICODE_kund] +#define t_key_up(t) (t)->strs[TICODE_kcuu1] +#define t_keypad_local(t) (t)->strs[TICODE_rmkx] +#define t_keypad_xmit(t) (t)->strs[TICODE_smkx] +#define t_lab_f0(t) (t)->strs[TICODE_lf0] +#define t_lab_f1(t) (t)->strs[TICODE_lf1] +#define t_lab_f2(t) (t)->strs[TICODE_lf2] +#define t_lab_f3(t) (t)->strs[TICODE_lf3] +#define t_lab_f4(t) (t)->strs[TICODE_lf4] +#define t_lab_f5(t) (t)->strs[TICODE_lf5] +#define t_lab_f6(t) (t)->strs[TICODE_lf6] +#define t_lab_f7(t) (t)->strs[TICODE_lf7] +#define t_lab_f8(t) (t)->strs[TICODE_lf8] +#define t_lab_f9(t) (t)->strs[TICODE_lf9] +#define t_lab_f10(t) (t)->strs[TICODE_lf10] +#define t_label_format(t) (t)->strs[TICODE_fln] +#define t_label_off(t) (t)->strs[TICODE_rmln] +#define t_label_on(t) (t)->strs[TICODE_smln] +#define t_meta_off(t) (t)->strs[TICODE_rmm] +#define t_meta_on(t) (t)->strs[TICODE_smm] +#define t_micro_column_address(t) (t)->strs[TICODE_mhpa] +#define t_micro_down(t) (t)->strs[TICODE_mcud1] +#define t_micro_left(t) (t)->strs[TICODE_mcub1] +#define t_micro_right(t) (t)->strs[TICODE_mcuf1] +#define t_micro_row_address(t) (t)->strs[TICODE_mvpa] +#define t_micro_up(t) (t)->strs[TICODE_mcuu1] +#define t_mouse_info(t) (t)->strs[TICODE_minfo] +#define t_newline(t) (t)->strs[TICODE_nel] +#define t_order_of_pins(t) (t)->strs[TICODE_porder] +#define t_orig_colors(t) (t)->strs[TICODE_oc] +#define t_orig_pair(t) (t)->strs[TICODE_op] +#define t_pad_char(t) (t)->strs[TICODE_pad] +#define t_parm_dch(t) (t)->strs[TICODE_dch] +#define t_parm_delete_line(t) (t)->strs[TICODE_dl] +#define t_parm_down_cursor(t) (t)->strs[TICODE_cud] +#define t_parm_down_micro(t) (t)->strs[TICODE_mcud] +#define t_parm_ich(t) (t)->strs[TICODE_ich] +#define t_parm_index(t) (t)->strs[TICODE_indn] +#define t_parm_insert_line(t) (t)->strs[TICODE_il] +#define t_parm_left_cursor(t) (t)->strs[TICODE_cub] +#define t_parm_left_micro(t) (t)->strs[TICODE_mcub] +#define t_parm_right_cursor(t) (t)->strs[TICODE_cuf] +#define t_parm_right_micro(t) (t)->strs[TICODE_mcuf] +#define t_parm_rindex(t) (t)->strs[TICODE_rin] +#define t_parm_up_cursor(t) (t)->strs[TICODE_cuu] +#define t_parm_up_micro(t) (t)->strs[TICODE_mcuu] +#define t_pc_term_options(t) (t)->strs[TICODE_pctrm] +#define t_pkey_key(t) (t)->strs[TICODE_pfkey] +#define t_pkey_local(t) (t)->strs[TICODE_pfloc] +#define t_pkey_plab(t) (t)->strs[TICODE_pfxl] +#define t_pkey_xmit(t) (t)->strs[TICODE_pfx] +#define t_plab_norm(t) (t)->strs[TICODE_pln] +#define t_print_screen(t) (t)->strs[TICODE_mc0] +#define t_ptr_non(t) (t)->strs[TICODE_mc5p] +#define t_ptr_off(t) (t)->strs[TICODE_mc4] +#define t_ptr_on(t) (t)->strs[TICODE_mc5] +#define t_pulse(t) (t)->strs[TICODE_pulse] +#define t_quick_dial(t) (t)->strs[TICODE_qdial] +#define t_remove_clock(t) (t)->strs[TICODE_rmclk] +#define t_repeat_char(t) (t)->strs[TICODE_rep] +#define t_req_for_input(t) (t)->strs[TICODE_rfi] +#define t_req_mouse_pos(t) (t)->strs[TICODE_reqmp] +#define t_reset_1string(t) (t)->strs[TICODE_rs1] +#define t_reset_2string(t) (t)->strs[TICODE_rs2] +#define t_reset_3string(t) (t)->strs[TICODE_rs3] +#define t_reset_file(t) (t)->strs[TICODE_rf] +#define t_restore_cursor(t) (t)->strs[TICODE_rc] +#define t_row_address(t) (t)->strs[TICODE_vpa] +#define t_save_cursor(t) (t)->strs[TICODE_sc] +#define t_scancode_escape(t) (t)->strs[TICODE_scesc] +#define t_scroll_forward(t) (t)->strs[TICODE_ind] +#define t_scroll_reverse(t) (t)->strs[TICODE_ri] +#define t_select_char_set(t) (t)->strs[TICODE_scs] +#define t_set0_des_seq(t) (t)->strs[TICODE_s0ds] +#define t_set1_des_seq(t) (t)->strs[TICODE_s1ds] +#define t_set2_des_seq(t) (t)->strs[TICODE_s2ds] +#define t_set3_des_seq(t) (t)->strs[TICODE_s3ds] +#define t_set_a_attributes(t) (t)->strs[TICODE_sgr1] +#define t_set_a_background(t) (t)->strs[TICODE_setab] +#define t_set_a_foreground(t) (t)->strs[TICODE_setaf] +#define t_set_attributes(t) (t)->strs[TICODE_sgr] +#define t_set_background(t) (t)->strs[TICODE_setb] +#define t_set_bottom_margin(t) (t)->strs[TICODE_smgb] +#define t_set_bottom_margin_parm(t) (t)->strs[TICODE_smgbp] +#define t_set_clock(t) (t)->strs[TICODE_sclk] +#define t_set_color_band(t) (t)->strs[TICODE_setcolor] +#define t_set_color_pair(t) (t)->strs[TICODE_scp] +#define t_set_foreground(t) (t)->strs[TICODE_setf] +#define t_set_left_margin(t) (t)->strs[TICODE_smgl] +#define t_set_left_margin_parm(t) (t)->strs[TICODE_smglp] +#define t_set_lr_margin(t) (t)->strs[TICODE_smglr] +#define t_set_page_length(t) (t)->strs[TICODE_slines] +#define t_set_pglen_inch(t) (t)->strs[TICODE_slength] +#define t_set_right_margin(t) (t)->strs[TICODE_smgr] +#define t_set_right_margin_parm(t) (t)->strs[TICODE_smgrp] +#define t_set_tab(t) (t)->strs[TICODE_hts] +#define t_set_tb_margin(t) (t)->strs[TICODE_smgtb] +#define t_set_top_margin(t) (t)->strs[TICODE_smgt] +#define t_set_top_margin_parm(t) (t)->strs[TICODE_smgtp] +#define t_set_window(t) (t)->strs[TICODE_wind] +#define t_start_bit_image(t) (t)->strs[TICODE_sbim] +#define t_start_char_set_def(t) (t)->strs[TICODE_scsd] +#define t_stop_bit_image(t) (t)->strs[TICODE_rbim] +#define t_stop_char_set_def(t) (t)->strs[TICODE_rcsd] +#define t_subscript_characters(t) (t)->strs[TICODE_subcs] +#define t_superscript_characters(t) (t)->strs[TICODE_supcs] +#define t_tab(t) (t)->strs[TICODE_ht] +#define t_these_cause_cr(t) (t)->strs[TICODE_docr] +#define t_to_status_line(t) (t)->strs[TICODE_tsl] +#define t_tone(t) (t)->strs[TICODE_tone] +#define t_user0(t) (t)->strs[TICODE_u0] +#define t_user1(t) (t)->strs[TICODE_u1] +#define t_user2(t) (t)->strs[TICODE_u2] +#define t_user3(t) (t)->strs[TICODE_u3] +#define t_user4(t) (t)->strs[TICODE_u4] +#define t_user5(t) (t)->strs[TICODE_u5] +#define t_user6(t) (t)->strs[TICODE_u6] +#define t_user7(t) (t)->strs[TICODE_u7] +#define t_user8(t) (t)->strs[TICODE_u8] +#define t_user9(t) (t)->strs[TICODE_u9] +#define t_underline_char(t) (t)->strs[TICODE_uc] +#define t_up_half_line(t) (t)->strs[TICODE_hu] +#define t_wait_tone(t) (t)->strs[TICODE_wait] +#define t_xoff_character(t) (t)->strs[TICODE_xoffc] +#define t_xon_character(t) (t)->strs[TICODE_xonc] +#define t_zero_motion(t) (t)->strs[TICODE_zerom] + +#define acs_chars t_acs_chars(cur_term) +#define alt_scancode_esc t_alt_scancode_esc(cur_term) +#define back_tab t_back_tab(cur_term) +#define bell t_bell(cur_term) +#define bit_image_carriage_return t_bit_image_carriage_return(cur_term) +#define bit_image_newline t_bit_image_newline(cur_term) +#define bit_image_repeat t_bit_image_repeat(cur_term) +#define carriage_return t_carriage_return(cur_term) +#define change_char_pitch t_change_char_pitch(cur_term) +#define change_line_pitch t_change_line_pitch(cur_term) +#define change_res_horz t_change_res_horz(cur_term) +#define change_res_vert t_change_res_vert(cur_term) +#define change_scroll_region t_change_scroll_region(cur_term) +#define char_padding t_char_padding(cur_term) +#define char_set_names t_char_set_names(cur_term) +#define clear_all_tabs t_clear_all_tabs(cur_term) +#define clear_margins t_clear_margins(cur_term) +#define clear_screen t_clear_screen(cur_term) +#define clr_bol t_clr_bol(cur_term) +#define clr_eol t_clr_eol(cur_term) +#define clr_eos t_clr_eos(cur_term) +#define code_set_init t_code_set_init(cur_term) +#define color_names t_color_names(cur_term) +#define column_address t_column_address(cur_term) +#define command_character t_command_character(cur_term) +#define create_window t_create_window(cur_term) +#define cursor_address t_cursor_address(cur_term) +#define cursor_down t_cursor_down(cur_term) +#define cursor_home t_cursor_home(cur_term) +#define cursor_invisible t_cursor_invisible(cur_term) +#define cursor_left t_cursor_left(cur_term) +#define cursor_mem_address t_cursor_mem_address(cur_term) +#define cursor_normal t_cursor_normal(cur_term) +#define cursor_right t_cursor_right(cur_term) +#define cursor_to_ll t_cursor_to_ll(cur_term) +#define cursor_up t_cursor_up(cur_term) +#define cursor_visible t_cursor_visible(cur_term) +#define define_bit_image_region t_define_bit_image_region(cur_term) +#define define_char t_define_char(cur_term) +#define delete_character t_delete_character(cur_term) +#define delete_line t_delete_line(cur_term) +#define device_type t_device_type(cur_term) +#define dial_phone t_dial_phone(cur_term) +#define dis_status_line t_dis_status_line(cur_term) +#define display_clock t_display_clock(cur_term) +#define display_pc_char t_display_pc_char(cur_term) +#define down_half_time t_down_half_time(cur_term) +#define ena_acs t_ena_acs(cur_term) +#define end_bit_image_region t_end_bit_image_region(cur_term) +#define enter_alt_charset_mode t_enter_alt_charset_mode(cur_term) +#define enter_am_mode t_enter_am_mode(cur_term) +#define enter_blink_mode t_enter_blink_mode(cur_term) +#define enter_bold_mode t_enter_bold_mode(cur_term) +#define enter_ca_mode t_enter_ca_mode(cur_term) +#define enter_delete_mode t_enter_delete_mode(cur_term) +#define enter_dim_mode t_enter_dim_mode(cur_term) +#define enter_doublewide_mode t_enter_doublewide_mode(cur_term) +#define enter_draft_quality t_enter_draft_quality(cur_term) +#define enter_horizontal_hl_mode t_enter_horizontal_hl_mode(cur_term) +#define enter_insert_mode t_enter_insert_mode(cur_term) +#define enter_italics_mode t_enter_italics_mode(cur_term) +#define enter_left_hl_mode t_enter_left_hl_mode(cur_term) +#define enter_leftward_mode t_enter_leftward_mode(cur_term) +#define enter_low_hl_mode t_enter_low_hl_mode(cur_term) +#define enter_micro_mode t_enter_micro_mode(cur_term) +#define enter_near_quality_letter t_enter_near_quality_letter(cur_term) +#define enter_normal_quality t_enter_normal_quality(cur_term) +#define enter_pc_charset_mode t_enter_pc_charset_mode(cur_term) +#define enter_protected_mode t_enter_protected_mode(cur_term) +#define enter_reverse_mode t_enter_reverse_mode(cur_term) +#define enter_right_hl_mode t_enter_right_hl_mode(cur_term) +#define enter_scancode_mode t_enter_scancode_mode(cur_term) +#define enter_secure_mode t_enter_secure_mode(cur_term) +#define enter_shadow_mode t_enter_shadow_mode(cur_term) +#define enter_standout_mode t_enter_standout_mode(cur_term) +#define enter_subscript_mode t_enter_subscript_mode(cur_term) +#define enter_superscript_mode t_enter_superscript_mode(cur_term) +#define enter_top_hl_mode t_enter_top_hl_mode(cur_term) +#define enter_underline_mode t_enter_underline_mode(cur_term) +#define enter_upward_mode t_enter_upward_mode(cur_term) +#define enter_vertical_hl_mode t_enter_vertical_hl_mode(cur_term) +#define enter_xon_mode t_enter_xon_mode(cur_term) +#define erase_chars t_erase_chars(cur_term) +#define exit_alt_charset_mode t_exit_alt_charset_mode(cur_term) +#define exit_am_mode t_exit_am_mode(cur_term) +#define exit_attribute_mode t_exit_attribute_mode(cur_term) +#define exit_ca_mode t_exit_ca_mode(cur_term) +#define exit_delete_mode t_exit_delete_mode(cur_term) +#define exit_doublewide_mode t_exit_doublewide_mode(cur_term) +#define exit_insert_mode t_exit_insert_mode(cur_term) +#define exit_italics_mode t_exit_italics_mode(cur_term) +#define exit_leftward_mode t_exit_leftward_mode(cur_term) +#define exit_micro_mode t_exit_micro_mode(cur_term) +#define exit_pc_charset_mode t_exit_pc_charset_mode(cur_term) +#define exit_scancode_mode t_exit_scancode_mode(cur_term) +#define exit_shadow_mode t_exit_shadow_mode(cur_term) +#define exit_standout_mode t_exit_standout_mode(cur_term) +#define exit_subscript_mode t_exit_subscript_mode(cur_term) +#define exit_superscript_mode t_exit_superscript_mode(cur_term) +#define exit_underline_mode t_exit_underline_mode(cur_term) +#define exit_upward_mode t_exit_upward_mode(cur_term) +#define exit_xon_mode t_exit_xon_mode(cur_term) +#define fixed_pause t_fixed_pause(cur_term) +#define flash_hook t_flash_hook(cur_term) +#define flash_screen t_flash_screen(cur_term) +#define form_feed t_form_feed(cur_term) +#define from_status_line t_from_status_line(cur_term) +#define get_mouse t_get_mouse(cur_term) +#define goto_window t_goto_window(cur_term) +#define hangup t_hangup(cur_term) +#define init_1string t_init_1string(cur_term) +#define init_2string t_init_2string(cur_term) +#define init_3string t_init_3string(cur_term) +#define init_file t_init_file(cur_term) +#define init_prog t_init_prog(cur_term) +#define initialize_color t_initialize_color(cur_term) +#define initialize_pair t_initialize_pair(cur_term) +#define insert_character t_insert_character(cur_term) +#define insert_line t_insert_line(cur_term) +#define insert_padding t_insert_padding(cur_term) +#define key_a1 t_key_a1(cur_term) +#define key_a3 t_key_a3(cur_term) +#define key_b2 t_key_b2(cur_term) +#define key_backspace t_key_backspace(cur_term) +#define key_beg t_key_beg(cur_term) +#define key_btab t_key_btab(cur_term) +#define key_c1 t_key_c1(cur_term) +#define key_c3 t_key_c3(cur_term) +#define key_cancel t_key_cancel(cur_term) +#define key_catab t_key_catab(cur_term) +#define key_clear t_key_clear(cur_term) +#define key_close t_key_close(cur_term) +#define key_command t_key_command(cur_term) +#define key_copy t_key_copy(cur_term) +#define key_create t_key_create(cur_term) +#define key_ctab t_key_ctab(cur_term) +#define key_dc t_key_dc(cur_term) +#define key_dl t_key_dl(cur_term) +#define key_down t_key_down(cur_term) +#define key_eic t_key_eic(cur_term) +#define key_end t_key_end(cur_term) +#define key_enter t_key_enter(cur_term) +#define key_eol t_key_eol(cur_term) +#define key_eos t_key_eos(cur_term) +#define key_exit t_key_exit(cur_term) +#define key_f0 t_key_f0(cur_term) +#define key_f1 t_key_f1(cur_term) +#define key_f2 t_key_f2(cur_term) +#define key_f3 t_key_f3(cur_term) +#define key_f4 t_key_f4(cur_term) +#define key_f5 t_key_f5(cur_term) +#define key_f6 t_key_f6(cur_term) +#define key_f7 t_key_f7(cur_term) +#define key_f8 t_key_f8(cur_term) +#define key_f9 t_key_f9(cur_term) +#define key_f10 t_key_f10(cur_term) +#define key_f11 t_key_f11(cur_term) +#define key_f12 t_key_f12(cur_term) +#define key_f13 t_key_f13(cur_term) +#define key_f14 t_key_f14(cur_term) +#define key_f15 t_key_f15(cur_term) +#define key_f16 t_key_f16(cur_term) +#define key_f17 t_key_f17(cur_term) +#define key_f18 t_key_f18(cur_term) +#define key_f19 t_key_f19(cur_term) +#define key_f20 t_key_f20(cur_term) +#define key_f21 t_key_f21(cur_term) +#define key_f22 t_key_f22(cur_term) +#define key_f23 t_key_f23(cur_term) +#define key_f24 t_key_f24(cur_term) +#define key_f25 t_key_f25(cur_term) +#define key_f26 t_key_f26(cur_term) +#define key_f27 t_key_f27(cur_term) +#define key_f28 t_key_f28(cur_term) +#define key_f29 t_key_f29(cur_term) +#define key_f30 t_key_f30(cur_term) +#define key_f31 t_key_f31(cur_term) +#define key_f32 t_key_f32(cur_term) +#define key_f33 t_key_f33(cur_term) +#define key_f34 t_key_f34(cur_term) +#define key_f35 t_key_f35(cur_term) +#define key_f36 t_key_f36(cur_term) +#define key_f37 t_key_f37(cur_term) +#define key_f38 t_key_f38(cur_term) +#define key_f39 t_key_f39(cur_term) +#define key_f40 t_key_f40(cur_term) +#define key_f41 t_key_f41(cur_term) +#define key_f42 t_key_f42(cur_term) +#define key_f43 t_key_f43(cur_term) +#define key_f44 t_key_f44(cur_term) +#define key_f45 t_key_f45(cur_term) +#define key_f46 t_key_f46(cur_term) +#define key_f47 t_key_f47(cur_term) +#define key_f48 t_key_f48(cur_term) +#define key_f49 t_key_f49(cur_term) +#define key_f50 t_key_f50(cur_term) +#define key_f51 t_key_f51(cur_term) +#define key_f52 t_key_f52(cur_term) +#define key_f53 t_key_f53(cur_term) +#define key_f54 t_key_f54(cur_term) +#define key_f55 t_key_f55(cur_term) +#define key_f56 t_key_f56(cur_term) +#define key_f57 t_key_f57(cur_term) +#define key_f58 t_key_f58(cur_term) +#define key_f59 t_key_f59(cur_term) +#define key_f60 t_key_f60(cur_term) +#define key_f61 t_key_f61(cur_term) +#define key_f62 t_key_f62(cur_term) +#define key_f63 t_key_f63(cur_term) +#define key_find t_key_find(cur_term) +#define key_help t_key_help(cur_term) +#define key_home t_key_home(cur_term) +#define key_ic t_key_ic(cur_term) +#define key_il t_key_il(cur_term) +#define key_left t_key_left(cur_term) +#define key_ll t_key_ll(cur_term) +#define key_mark t_key_mark(cur_term) +#define key_message t_key_message(cur_term) +#define key_mouse t_key_mouse(cur_term) +#define key_move t_key_move(cur_term) +#define key_next t_key_next(cur_term) +#define key_npage t_key_npage(cur_term) +#define key_open t_key_open(cur_term) +#define key_options t_key_options(cur_term) +#define key_ppage t_key_ppage(cur_term) +#define key_previous t_key_previous(cur_term) +#define key_print t_key_print(cur_term) +#define key_redo t_key_redo(cur_term) +#define key_reference t_key_reference(cur_term) +#define key_refresh t_key_refresh(cur_term) +#define key_replace t_key_replace(cur_term) +#define key_restart t_key_restart(cur_term) +#define key_resume t_key_resume(cur_term) +#define key_right t_key_right(cur_term) +#define key_save t_key_save(cur_term) +#define key_sbeg t_key_sbeg(cur_term) +#define key_scancel t_key_scancel(cur_term) +#define key_scommand t_key_scommand(cur_term) +#define key_scopy t_key_scopy(cur_term) +#define key_screate t_key_screate(cur_term) +#define key_sdc t_key_sdc(cur_term) +#define key_sdl t_key_sdl(cur_term) +#define key_select t_key_select(cur_term) +#define key_send t_key_send(cur_term) +#define key_seol t_key_seol(cur_term) +#define key_sexit t_key_sexit(cur_term) +#define key_sf t_key_sf(cur_term) +#define key_sfind t_key_sfind(cur_term) +#define key_shelp t_key_shelp(cur_term) +#define key_shome t_key_shome(cur_term) +#define key_sic t_key_sic(cur_term) +#define key_sleft t_key_sleft(cur_term) +#define key_smessage t_key_smessage(cur_term) +#define key_smove t_key_smove(cur_term) +#define key_snext t_key_snext(cur_term) +#define key_soptions t_key_soptions(cur_term) +#define key_sprevious t_key_sprevious(cur_term) +#define key_sprint t_key_sprint(cur_term) +#define key_sr t_key_sr(cur_term) +#define key_sredo t_key_sredo(cur_term) +#define key_sreplace t_key_sreplace(cur_term) +#define key_sright t_key_sright(cur_term) +#define key_srsume t_key_srsume(cur_term) +#define key_ssave t_key_ssave(cur_term) +#define key_ssuspend t_key_ssuspend(cur_term) +#define key_stab t_key_stab(cur_term) +#define key_sundo t_key_sundo(cur_term) +#define key_suspend t_key_suspend(cur_term) +#define key_undo t_key_undo(cur_term) +#define key_up t_key_up(cur_term) +#define keypad_local t_keypad_local(cur_term) +#define keypad_xmit t_keypad_xmit(cur_term) +#define lab_f0 t_lab_f0(cur_term) +#define lab_f1 t_lab_f1(cur_term) +#define lab_f2 t_lab_f2(cur_term) +#define lab_f3 t_lab_f3(cur_term) +#define lab_f4 t_lab_f4(cur_term) +#define lab_f5 t_lab_f5(cur_term) +#define lab_f6 t_lab_f6(cur_term) +#define lab_f7 t_lab_f7(cur_term) +#define lab_f8 t_lab_f8(cur_term) +#define lab_f9 t_lab_f9(cur_term) +#define lab_f10 t_lab_f10(cur_term) +#define label_format t_label_format(cur_term) +#define label_off t_label_off(cur_term) +#define label_on t_label_on(cur_term) +#define meta_off t_meta_off(cur_term) +#define meta_on t_meta_on(cur_term) +#define micro_column_address t_micro_column_address(cur_term) +#define micro_down t_micro_down(cur_term) +#define micro_left t_micro_left(cur_term) +#define micro_right t_micro_right(cur_term) +#define micro_row_address t_micro_row_address(cur_term) +#define micro_up t_micro_up(cur_term) +#define mouse_info t_mouse_info(cur_term) +#define newline t_newline(cur_term) +#define order_of_pins t_order_of_pins(cur_term) +#define orig_colors t_orig_colors(cur_term) +#define orig_pair t_orig_pair(cur_term) +#define pad_char t_pad_char(cur_term) +#define parm_dch t_parm_dch(cur_term) +#define parm_delete_line t_parm_delete_line(cur_term) +#define parm_down_cursor t_parm_down_cursor(cur_term) +#define parm_down_micro t_parm_down_micro(cur_term) +#define parm_ich t_parm_ich(cur_term) +#define parm_index t_parm_index(cur_term) +#define parm_insert_line t_parm_insert_line(cur_term) +#define parm_left_cursor t_parm_left_cursor(cur_term) +#define parm_left_micro t_parm_left_micro(cur_term) +#define parm_right_cursor t_parm_right_cursor(cur_term) +#define parm_right_micro t_parm_right_micro(cur_term) +#define parm_rindex t_parm_rindex(cur_term) +#define parm_up_cursor t_parm_up_cursor(cur_term) +#define parm_up_micro t_parm_up_micro(cur_term) +#define pc_term_options t_pc_term_options(cur_term) +#define pkey_key t_pkey_key(cur_term) +#define pkey_local t_pkey_local(cur_term) +#define pkey_plab t_pkey_plab(cur_term) +#define pkey_xmit t_pkey_xmit(cur_term) +#define plab_norm t_plab_norm(cur_term) +#define print_screen t_print_screen(cur_term) +#define ptr_non t_ptr_non(cur_term) +#define ptr_off t_ptr_off(cur_term) +#define ptr_on t_ptr_on(cur_term) +#define pulse t_pulse(cur_term) +#define quick_dial t_quick_dial(cur_term) +#define remove_clock t_remove_clock(cur_term) +#define repeat_char t_repeat_char(cur_term) +#define req_for_input t_req_for_input(cur_term) +#define req_mouse_pos t_req_mouse_pos(cur_term) +#define reset_1string t_reset_1string(cur_term) +#define reset_2string t_reset_2string(cur_term) +#define reset_3string t_reset_3string(cur_term) +#define reset_file t_reset_file(cur_term) +#define restore_cursor t_restore_cursor(cur_term) +#define row_address t_row_address(cur_term) +#define save_cursor t_save_cursor(cur_term) +#define scancode_escape t_scancode_escape(cur_term) +#define scroll_forward t_scroll_forward(cur_term) +#define scroll_reverse t_scroll_reverse(cur_term) +#define select_char_set t_select_char_set(cur_term) +#define set0_des_seq t_set0_des_seq(cur_term) +#define set1_des_seq t_set1_des_seq(cur_term) +#define set2_des_seq t_set2_des_seq(cur_term) +#define set3_des_seq t_set3_des_seq(cur_term) +#define set_a_attributes t_set_a_attributes(cur_term) +#define set_a_background t_set_a_background(cur_term) +#define set_a_foreground t_set_a_foreground(cur_term) +#define set_attributes t_set_attributes(cur_term) +#define set_background t_set_background(cur_term) +#define set_bottom_margin t_set_bottom_margin(cur_term) +#define set_bottom_margin_parm t_set_bottom_margin_parm(cur_term) +#define set_clock t_set_clock(cur_term) +#define set_color_band t_set_color_band(cur_term) +#define set_color_pair t_set_color_pair(cur_term) +#define set_foreground t_set_foreground(cur_term) +#define set_left_margin t_set_left_margin(cur_term) +#define set_left_margin_parm t_set_left_margin_parm(cur_term) +#define set_lr_margin t_set_lr_margin(cur_term) +#define set_page_length t_set_page_length(cur_term) +#define set_pglen_inch t_set_pglen_inch(cur_term) +#define set_right_margin t_set_right_margin(cur_term) +#define set_right_margin_parm t_set_right_margin_parm(cur_term) +#define set_tab t_set_tab(cur_term) +#define set_tb_margin t_set_tb_margin(cur_term) +#define set_top_margin t_set_top_margin(cur_term) +#define set_top_margin_parm t_set_top_margin_parm(cur_term) +#define set_window t_set_window(cur_term) +#define start_bit_image t_start_bit_image(cur_term) +#define start_char_set_def t_start_char_set_def(cur_term) +#define stop_bit_image t_stop_bit_image(cur_term) +#define stop_char_set_def t_stop_char_set_def(cur_term) +#define subscript_characters t_subscript_characters(cur_term) +#define superscript_characters t_superscript_characters(cur_term) +#define tab t_tab(cur_term) +#define these_cause_cr t_these_cause_cr(cur_term) +#define to_status_line t_to_status_line(cur_term) +#define tone t_tone(cur_term) +#define user0 t_user0(cur_term) +#define user1 t_user1(cur_term) +#define user2 t_user2(cur_term) +#define user3 t_user3(cur_term) +#define user4 t_user4(cur_term) +#define user5 t_user5(cur_term) +#define user6 t_user6(cur_term) +#define user7 t_user7(cur_term) +#define user8 t_user8(cur_term) +#define user9 t_user9(cur_term) +#define underline_char t_underline_char(cur_term) +#define up_half_line t_up_half_line(cur_term) +#define wait_tone t_wait_tone(cur_term) +#define xoff_character t_xoff_character(cur_term) +#define xon_character t_xon_character(cur_term) +#define zero_motion t_zero_motion(cur_term) + +/* + * STRING DESCRIPTIONS + * + * acs_chars: Graphic charset pairs aAbBcC + * alt_scancode_esc: Alternate escape for scancode emulation + * back_tab: Back tab + * bell: Audible signal (bell) + * bit_image_carriage_return: Move to beginning of same row + * bit_image_newline: Move to next row of the bit image + * bit_image_repeat: Repeat bit-image cell #1 #2 times + * carriage_return: Carriage return + * change_char_pitch: Change number of characters per inch + * change_line_pitch: Change number of lines per inch + * change_res_horz: Change horizontal resolution + * change_res_vert: Change vertical resolution + * change_scroll_region: Change to lines #1 through #2 (VT100) + * char_padding: Like ip but when in replace mode + * char_set_names: Returns a list of character set names + * clear_all_tabs: Clear all tab stops + * clear_margins: Clear all margins (top, bottom and sides) + * clear_screen: Clear screen and home cursor + * clr_bol: Clear to beginning of line, inclusive + * clr_eol: Clear to end of line + * clr_eos: Clear to end of display + * code_set_init: Init sequence for multiple codesets + * color_names: Give name for colour #1 + * column_address: Set horizontal position to absolute #1 + * command_character: Terminal settable cmd character in prototype + * create_window: Define win #1 to go from #2,#3 to #4,#5 + * cursor_address: Move to row #1, col #2 + * cursor_down: Down one line + * cursor_home: Home cursor (if no cup) + * cursor_invisible: Make cursor invisible + * cursor_left: Move left one space + * cursor_mem_address: Memory relative cursor addressing + * cursor_normal: Make cursor appear normal (under vs/vi) + * cursor_right: Non-destructive space (cursor or carriage right) + * cursor_to_ll: Last line, first column (if no cup) + * cursor_up: Upline (cursor up) + * cursor_visible: Make cursor very visible + * define_bit_image_region: Define rectangular bit-image region + * define_char: Define a character in a character set + * delete_character: Delete character + * delete_line: Delete line + * device_type: Indicate language/codeset support + * dial_phone: Dial phone number #1 + * dis_status_line: Disable status line + * display_clock: Display time-of-day clock + * display_pc_char: Display PC character + * down_half_time: Half-line down (forward 1/2 linefeed) + * ena_acs: Enable alternate character set + * end_bit_image_region: End a bit-image region + * enter_alt_charset_mode: Start alternate character set + * enter_am_mode: Turn on automatic margins + * enter_blink_mode: Turn on blinking + * enter_bold_mode: Turn on bold (extra bright) mode + * enter_ca_mode: String to begin programs that use cup + * enter_delete_mode: Delete mode (enter) + * enter_dim_mode: Turn on half-bright mode + * enter_doublewide_mode: Enable double wide printing + * enter_draft_quality: Set draft qualify print + * enter_horizontal_hl_mode: Turn on horizontal highlight mode + * enter_insert_mode: Insert mode (enter) + * enter_italics_mode: Enable italics + * enter_left_hl_mode: Turn on left highlight mode + * enter_leftward_mode: Enable leftward carriage motion + * enter_low_hl_mode: Turn on low highlight mode + * enter_micro_mode: Enable micro motion capabilities + * enter_near_quality_letter: Set near-letter quality print + * enter_normal_quality: Set normal quality print + * enter_pc_charset_mode: Enter PC character display mode + * enter_protected_mode: Turn on protected mode + * enter_reverse_mode: Turn on reverse video mode + * enter_right_hl_mode: Turn on right highlight mode + * enter_scancode_mode: Enter PC scancode mode + * enter_secure_mode: Turn on blank mode (characters invisible) + * enter_shadow_mode: Enable shadow printing + * enter_standout_mode: Begin standout mode + * enter_subscript_mode: Enable subscript printing + * enter_superscript_mode: Enable superscript printing + * enter_top_hl_mode: Turn on top highlight mode + * enter_underline_mode: Start underscore mode + * enter_upward_mode: Enable upward carriage motion + * enter_vertical_hl_mode: Turn on verticle highlight mode + * enter_xon_mode: Turn on xon/xoff handshaking + * erase_chars: Erase #1 characters + * exit_alt_charset_mode: End alternate character set + * exit_am_mode: Turn off automatic margins + * exit_attribute_mode: Turn off all attributes + * exit_ca_mode: String to end programs that use cup + * exit_delete_mode: End delete mode + * exit_doublewide_mode: Disable double wide printing + * exit_insert_mode: End insert mode + * exit_italics_mode: Disable italics + * exit_leftward_mode: Enable rightward (normal) carriage motion + * exit_micro_mode: Disable micro motion capabilities + * exit_pc_charset_mode: Disable PC character display mode + * exit_scancode_mode: Disable PC scancode mode + * exit_shadow_mode: Disable shadow printing + * exit_standout_mode: End standout mode + * exit_subscript_mode: Disable subscript printing + * exit_superscript_mode: Disable superscript printing + * exit_underline_mode: End underscore mode + * exit_upward_mode: Enable downward (normal) carriage motion + * exit_xon_mode: Turn off xon/xoff handshaking + * fixed_pause: Pause for 2-3 seconds + * flash_hook: Flash the switch hook + * flash_screen: Visible bell (may move cursor) + * form_feed: Hardcopy terminal eject page + * from_status_line: Return from status line + * get_mouse: Curses should get button events + * goto_window: Go to window #1 + * hangup: Hang-up phone + * init_1string: Terminal or printer initialisation string + * init_2string: Terminal or printer initialisation string + * init_3string: Terminal or printer initialisation string + * init_file: Name of initialisation file + * init_prog: Path name of program for initialisation + * initialize_color: Set colour #1 to RGB #2, #3, #4 + * initialize_pair: Set colour-pair #1 to fg #2, bg #3 + * insert_character: Insert character + * insert_line: Add new blank line + * insert_padding: Insert pad after character inserted + * key_a1: upper left of keypad + * key_a3: upper right of keypad + * key_b2: center of keypad + * key_backspace: set by backspace key + * key_beg: 1 + * key_btab: sent by back-tab key + * key_c1: lower left of keypad + * key_c3: lower right of keypad + * key_cancel: 2 + * key_catab: sent by clear-all-tabs key + * key_clear: sent by clear-screen or erase key + * key_close: 3 + * key_command: 4 + * key_copy: 5 + * key_create: 6 + * key_ctab: sent by clear-tab key + * key_dc: sent by delete-character key + * key_dl: sent by delete-line key + * key_down: sent by terminal down-arrow key + * key_eic: sent by rmir or smir in insert mode + * key_end: 7 + * key_enter: 8 + * key_eol: sent by clear-to-end-of-line key + * key_eos: sent by clear-to-end-of-screen key + * key_exit: 9 + * key_f0: sent by function key f0 + * key_f1: sent by function key f1 + * key_f2: sent by function key f2 + * key_f3: sent by function key f3 + * key_f4: sent by function key f4 + * key_f5: sent by function key f5 + * key_f6: sent by function key f6 + * key_f7: sent by function key f7 + * key_f8: sent by function key f8 + * key_f9: sent by function key f9 + * key_f10: sent by function key f10 + * key_f11: sent by function key f11 + * key_f12: sent by function key f12 + * key_f13: sent by function key f13 + * key_f14: sent by function key f14 + * key_f15: sent by function key f15 + * key_f16: sent by function key f16 + * key_f17: sent by function key f17 + * key_f18: sent by function key f18 + * key_f19: sent by function key f19 + * key_f20: sent by function key f20 + * key_f21: sent by function key f21 + * key_f22: sent by function key f22 + * key_f23: sent by function key f23 + * key_f24: sent by function key f24 + * key_f25: sent by function key f25 + * key_f26: sent by function key f26 + * key_f27: sent by function key f27 + * key_f28: sent by function key f28 + * key_f29: sent by function key f29 + * key_f30: sent by function key f30 + * key_f31: sent by function key f31 + * key_f32: sent by function key f32 + * key_f33: sent by function key f33 + * key_f34: sent by function key f34 + * key_f35: sent by function key f35 + * key_f36: sent by function key f36 + * key_f37: sent by function key f37 + * key_f38: sent by function key f38 + * key_f39: sent by function key f39 + * key_f40: sent by function key f40 + * key_f41: sent by function key f41 + * key_f42: sent by function key f42 + * key_f43: sent by function key f43 + * key_f44: sent by function key f44 + * key_f45: sent by function key f45 + * key_f46: sent by function key f46 + * key_f47: sent by function key f47 + * key_f48: sent by function key f48 + * key_f49: sent by function key f49 + * key_f50: sent by function key f50 + * key_f51: sent by function key f51 + * key_f52: sent by function key f52 + * key_f53: sent by function key f53 + * key_f54: sent by function key f54 + * key_f55: sent by function key f55 + * key_f56: sent by function key f56 + * key_f57: sent by function key f57 + * key_f58: sent by function key f58 + * key_f59: sent by function key f59 + * key_f60: sent by function key f60 + * key_f61: sent by function key f61 + * key_f62: sent by function key f62 + * key_f63: sent by function key f63 + * key_find: 0 + * key_help: sent by help key + * key_home: sent by home key + * key_ic: sent by ins-char/enter ins-mode key + * key_il: sent by insert-line key + * key_left: sent by terminal left-arrow key + * key_ll: sent by home-down key + * key_mark: sent by mark key + * key_message: sent by message key + * key_mouse: 0631, Mouse event has occured + * key_move: sent by move key + * key_next: sent by next-object key + * key_npage: sent by next-page key + * key_open: sent by open key + * key_options: sent by options key + * key_ppage: sent by previous-page key + * key_previous: sent by previous-object key + * key_print: sent by print or copy key + * key_redo: sent by redo key + * key_reference: sent by ref(erence) key + * key_refresh: sent by refresh key + * key_replace: sent by replace key + * key_restart: sent by restart key + * key_resume: sent by resume key + * key_right: sent by terminal right-arrow key + * key_save: sent by save key + * key_sbeg: sent by shifted beginning key + * key_scancel: sent by shifted cancel key + * key_scommand: sent by shifted command key + * key_scopy: sent by shifted copy key + * key_screate: sent by shifted create key + * key_sdc: sent by shifted delete-char key + * key_sdl: sent by shifted delete-line key + * key_select: sent by select key + * key_send: sent by shifted end key + * key_seol: sent by shifted clear-line key + * key_sexit: sent by shited exit key + * key_sf: sent by scroll-forward/down key + * key_sfind: sent by shifted find key + * key_shelp: sent by shifted help key + * key_shome: sent by shifted home key + * key_sic: sent by shifted input key + * key_sleft: sent by shifted left-arrow key + * key_smessage: sent by shifted message key + * key_smove: sent by shifted move key + * key_snext: sent by shifted next key + * key_soptions: sent by shifted options key + * key_sprevious: sent by shifted prev key + * key_sprint: sent by shifted print key + * key_sr: sent by scroll-backwards/up key + * key_sredo: sent by shifted redo key + * key_sreplace: sent by shifted replace key + * key_sright: sent by shifted right-arrow key + * key_srsume: sent by shifted resume key + * key_ssave: sent by shifted save key + * key_ssuspend: sent by shifted suspend key + * key_stab: sent by set-tab key + * key_sundo: sent by shifted undo key + * key_suspend: sent by suspend key + * key_undo: sent by undo key + * key_up: sent by terminal up-arrow key + * keypad_local: Out of "keypad-transmit" mode + * keypad_xmit: Put terminal in "keypad-transmit" mode + * lab_f0: Labels on function key f0 if not f0 + * lab_f1: Labels on function key f1 if not f1 + * lab_f2: Labels on function key f2 if not f2 + * lab_f3: Labels on function key f3 if not f3 + * lab_f4: Labels on function key f4 if not f4 + * lab_f5: Labels on function key f5 if not f5 + * lab_f6: Labels on function key f6 if not f6 + * lab_f7: Labels on function key f7 if not f7 + * lab_f8: Labels on function key f8 if not f8 + * lab_f9: Labels on function key f9 if not f9 + * lab_f10: Labels on function key f10 if not f10 + * label_format: Label format + * label_off: Turn off soft labels + * label_on: Turn on soft labels + * meta_off: Turn off "meta mode" + * meta_on: Turn on "meta mode" (8th bit) + * micro_column_address: Like column_address for micro adjustment + * micro_down: Like cursor_down for micro adjustment + * micro_left: Like cursor_left for micro adjustment + * micro_right: Like cursor_right for micro adjustment + * micro_row_address: Like row_address for micro adjustment + * micro_up: Like cursor_up for micro adjustment + * mouse_info: Mouse status information + * newline: Newline (behaves like cr followed by lf) + * order_of_pins: Matches software bits to print-head pins + * orig_colors: Set all colour(-pair)s to original ones + * orig_pair: Set default colour-pair to the original one + * pad_char: Pad character (rather than NULL) + * parm_dch: Delete #1 chars + * parm_delete_line: Delete #1 lines + * parm_down_cursor: Move down #1 lines + * parm_down_micro: Like parm_down_cursor for micro adjustment + * parm_ich: Insert #1 blank chars + * parm_index: Scroll forward #1 lines + * parm_insert_line: Add #1 new blank lines + * parm_left_cursor: Move cursor left #1 lines + * parm_left_micro: Like parm_left_cursor for micro adjustment + * parm_right_cursor: Move right #1 spaces + * parm_right_micro: Like parm_right_cursor for micro adjustment + * parm_rindex: Scroll backward #1 lines + * parm_up_cursor: Move cursor up #1 lines + * parm_up_micro: Like parm_up_cursor for micro adjustment + * pc_term_options: PC terminal options + * pkey_key: Prog funct key #1 to type string #2 + * pkey_local: Prog funct key #1 to execute string #2 + * pkey_plab: Prog key #1 to xmit string #2 and show string #3 + * pkey_xmit: Prog funct key #1 to xmit string #2 + * plab_norm: Prog label #1 to show string #2 + * print_screen: Print contents of screen + * ptr_non: Turn off printer for #1 bytes + * ptr_off: Turn off the printer + * ptr_on: Turn on the printer + * pulse: Select pulse dialing + * quick_dial: Dial phone number #1, without progress detection + * remove_clock: Remove time-of-day clock + * repeat_char: Repeat char #1 #2 times + * req_for_input: Send next input char (for ptys) + * req_mouse_pos: Request mouse position report + * reset_1string: Reset terminal completely to sane modes + * reset_2string: Reset terminal completely to sane modes + * reset_3string: Reset terminal completely to sane modes + * reset_file: Name of file containing reset string + * restore_cursor: Restore cursor to position of last sc + * row_address: Set vertical position to absolute #1 + * save_cursor: Save cursor position + * scancode_escape: Escape for scancode emulation + * scroll_forward: Scroll text up + * scroll_reverse: Scroll text down + * select_char_set: Select character set + * set0_des_seq: Shift into codeset 0 (EUC set 0, ASCII) + * set1_des_seq: Shift into codeset 1 + * set2_des_seq: Shift into codeset 2 + * set3_des_seq: Shift into codeset 3 + * set_a_attributes: Define second set of video attributes #1-#6 + * set_a_background: Set background colour to #1 using ANSI escape + * set_a_foreground: Set foreground colour to #1 using ANSI escape + * set_attributes: Define first set of video attributes #1-#9 + * set_background: Set background colour to #1 + * set_bottom_margin: Set bottom margin at current line + * set_bottom_margin_parm: Set bottom margin at line #1 or #2 lines from bottom + * set_clock: Set clock to hours (#1), minutes (#2), seconds (#3) + * set_color_band: Change ribbon to colour #1 + * set_color_pair: Set current colour pair to #1 + * set_foreground: Set foreground colour to #1 + * set_left_margin: Set left margin at current column + * set_left_margin_parm: Set left (right) margin at column #1 (#2) + * set_lr_margin: Sets both left and right margins + * set_page_length: Set page length to #1 lines + * set_pglen_inch: Set page length to #1 hundredth of an inch + * set_right_margin: Set right margin at current column + * set_right_margin_parm: Set right margin at #1 + * set_tab: Set a tab in all rows, current column + * set_tb_margin: Sets both top and bottom margins + * set_top_margin: Set top margin at current line + * set_top_margin_parm: Set top (bottom) margin at line #1 (#2) + * set_window: Current window is lines #1-#2 cols #3-#4 + * start_bit_image: Start printing bit image graphics + * start_char_set_def: Start definition of a character set + * stop_bit_image: End printing bit image graphics + * stop_char_set_def: End definition of a character set + * subscript_characters: List of "subscript-able" characters + * superscript_characters: List of "superscript-able" characters + * tab: Tab to next 8-space hardware tab stop + * these_cause_cr: Printing any of these characters causes cr + * to_status_line: Go to status line, col #1 + * tone: Select tone touch dialing + * user0: User string 0 + * user1: User string 1 + * user2: User string 2 + * user3: User string 3 + * user4: User string 4 + * user5: User string 5 + * user6: User string 6 + * user7: User string 7 + * user8: User string 8 + * user9: User string 9 + * underline_char: Underscore one char and move past it + * up_half_line: Half-line up (reverse 1/2 linefeed) + * wait_tone: Wait for dial tone + * xoff_character: X-off character + * xon_character: X-on character + * zero_motion: No motion for the subsequent character + */ + +#ifndef _TERMINFO +typedef struct { + int fildes; + /* We need to expose these so that the macros work */ + const char *name; + const char *desc; + signed char *flags; + short *nums; + const char **strs; +} TERMINAL; +#endif + +#include + +__BEGIN_DECLS + +extern TERMINAL *cur_term; + +/* setup functions */ +int setupterm(const char *, int, int *); +TERMINAL * set_curterm(TERMINAL *); +int del_curterm(TERMINAL *); +char * termname(void); +char * longname(void); + +/* information functions */ +int tigetflag(const char *); +int tigetnum(const char *); +char * tigetstr(const char *); +/* You should note that the spec allows stuffing a char * into a long + * if the platform allows and the %pN is followed immediately by %l or %s */ +char * tparm(const char *, long, long, long, long, long, + long, long, long, long); + +/* Non standard functions, but provide a level of thread safety */ +int ti_setupterm(TERMINAL **, const char *, int, int *); +int ti_getflag(const TERMINAL *, const char *); +int ti_getnum(const TERMINAL *, const char *); +const char * ti_getstr(const TERMINAL *, const char *); +char * ti_parm(TERMINAL *, const char *, + long, long, long, long, long, long, long, long, long); + +/* These functions do not use PC or speed, but the terminal */ +int ti_puts(const TERMINAL *, const char *, int, + int (*)(int, void *), void *); +int ti_putp(const TERMINAL *, const char *); + +/* Using tparm can be kunkly, so provide a variadic function + * Numbers have to be passed as int */ +/* This is not standard, but ncurses also provides this */ +char * tiparm(const char *, ...); +/* And a thread safe version */ +char * ti_tiparm(TERMINAL *, const char *, ...); + +#ifdef TPARM_TLPARM +/* Same as the above, but numbers have to be passed as long */ +char * tlparm(const char *, ...); +/* And a thread safe version */ +char * ti_tlparm(TERMINAL *, const char *, ...); +#endif + +/* Default to X/Open tparm, but allow it to be variadic also */ +#ifdef TPARM_VARARGS +# define tparm tiparm +# define ti_parm ti_tiparm +#endif + +/* Convert a termcap string into a terminfo string. + * The passed string is destroyed and the return string needs to be freed. */ +char * captoinfo(char *); + +/* POSIX says that term.h should also pull in our termcap definitions. */ +#include "terminfo_termcap.h" + +__END_DECLS +#endif Index: contrib/mg/terminfo_termcap.h =================================================================== --- /dev/null +++ contrib/mg/terminfo_termcap.h @@ -0,0 +1,54 @@ +/* $NetBSD: termcap.h,v 1.2 2011/04/11 21:13:09 roy Exp $ */ + +/* + * Copyright (c) 2009, 2011 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TERMCAP_H_ +#define _TERMCAP_H_ + +#include + +__BEGIN_DECLS + +/* Output functions. + * These are still valid for terminfo. */ +int putp(const char *); +int tputs(const char *, int, int (*)(int)); + +extern short ospeed; +extern char PC; +extern char *BC; +extern char *UP; + +int tgetent(char *, const char *); +char * tgetstr(const char *, char **); +int tgetflag(const char *); +int tgetnum(const char *); +char * tgoto(const char *, int, int); + +__END_DECLS +#endif Index: contrib/mg/ti.c =================================================================== --- /dev/null +++ contrib/mg/ti.c @@ -0,0 +1,126 @@ +/* $NetBSD: ti.c,v 1.3 2013/06/07 13:16:18 roy Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This id is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source id must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#ifndef __UNCONST +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif + +int +ti_getflag(const TERMINAL *term, const char *id) +{ + ssize_t ind; + size_t i; + TERMUSERDEF *ud; + + ind = _ti_flagindex(id); + if (ind != -1) + return term->flags[ind]; + for (i = 0; i < term->_nuserdefs; i++) { + ud = &term->_userdefs[i]; + if (ud->type == 'f' && strcmp(ud->id, id) == 0) + return ud->flag; + } + return ABSENT_BOOLEAN; +} + +int +tigetflag(const char *id) +{ + + if (cur_term != NULL) + return ti_getflag(cur_term, id); + return ABSENT_BOOLEAN; +} + +int +ti_getnum(const TERMINAL *term, const char *id) +{ + ssize_t ind; + size_t i; + TERMUSERDEF *ud; + + ind = _ti_numindex(id); + if (ind != -1) { + if (!VALID_NUMERIC(term->nums[ind])) + return ABSENT_NUMERIC; + return term->nums[ind]; + } + for (i = 0; i < term->_nuserdefs; i++) { + ud = &term->_userdefs[i]; + if (ud->type == 'n' && strcmp(ud->id, id) == 0) { + if (!VALID_NUMERIC(ud->num)) + return ABSENT_NUMERIC; + return ud->num; + } + } + return CANCELLED_NUMERIC; +} + +int +tigetnum(const char *id) +{ + + if (cur_term != NULL) + return ti_getnum(cur_term, id); + return CANCELLED_NUMERIC; +} + +const char * +ti_getstr(const TERMINAL *term, const char *id) +{ + ssize_t ind; + size_t i; + TERMUSERDEF *ud; + + ind = _ti_strindex(id); + if (ind != -1) + return term->strs[ind]; + for (i = 0; i < term->_nuserdefs; i++) { + ud = &term->_userdefs[i]; + if (ud->type == 's' && strcmp(ud->id, id) == 0) + return ud->str; + } + return (const char *)CANCELLED_STRING; +} + +char * +tigetstr(const char *id) +{ + + if (cur_term != NULL) + return __UNCONST(ti_getstr(cur_term, id)); + return (char *)CANCELLED_STRING; +} Index: contrib/mg/tic.c =================================================================== --- /dev/null +++ contrib/mg/tic.c @@ -0,0 +1,629 @@ +/* $NetBSD: tic.c,v 1.31 2017/10/02 21:53:55 joerg Exp $ */ + +/* + * Copyright (c) 2009, 2010 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__linux__) || defined(__CYGWIN__) +#include "util.h" +#elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) +#include +#else +#include +#endif + +#include "cdbw.h" +#include "term_private.h" +#include "terminfo_term.h" + +#define HASH_SIZE 16384 /* 2012-06-01: 3600 entries */ + +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) + +#ifndef TAILQ_REMOVE_HEAD +#define TAILQ_REMOVE_HEAD(head, field) do { \ + if (((head)->tqh_first = (head)->tqh_first->field.tqe_next) == \ + NULL) \ + (head)->tqh_last = &(head)->tqh_first; \ +} while (/*CONSTCOND*/0) +#endif + +typedef struct term { + TAILQ_ENTRY(term) next; + char *name; + TIC *tic; + uint32_t id; + struct term *base_term; +} TERM; +static TAILQ_HEAD(, term) terms = TAILQ_HEAD_INITIALIZER(terms); + +static int error_exit; +static int Sflag; +static size_t nterm, nalias; + +static void +dowarn(const char *fmt, ...) +{ + va_list va; + + error_exit = 1; + va_start(va, fmt); + vwarnx(fmt, va); + va_end(va); +} + +static char * +grow_tbuf(TBUF *tbuf, size_t len) +{ + char *buf; + + buf = _ti_grow_tbuf(tbuf, len); + if (buf == NULL) + err(1, "_ti_grow_tbuf"); + return buf; +} + +/* From NetBSD sys/arch/hpc/stand/include/machine/endian.h */ +static uint16_t +le16dec(const void *buf) +{ + const uint8_t *p = (const uint8_t *)buf; + + return ((p[1] << 8) | p[0]); +} + +static void +le16enc(void *buf, uint32_t u) +{ + uint8_t *p = (uint8_t *) buf; + + p[0] = u & 0xff; + p[1] = ((unsigned)u >> 8) & 0xff; +} + +void +le32enc(void *buf, uint32_t u) +{ + uint8_t *p = (uint8_t *) buf; + + p[0] = u & 0xff; + p[1] = (u >> 8) & 0xff; + p[2] = (u >> 16) & 0xff; + p[3] = (u >> 24) & 0xff; +} + +static int +save_term(struct cdbw *db, TERM *term) +{ + uint8_t *buf; + ssize_t len; + size_t slen = strlen(term->name) + 1; + + if (term->base_term != NULL) { + len = (ssize_t)slen + 7; + buf = malloc(len); + buf[0] = 2; + le32enc(buf + 1, term->base_term->id); + le16enc(buf + 5, slen); + memcpy(buf + 7, term->name, slen); + if (cdbw_put(db, term->name, slen, buf, len)) + err(1, "cdbw_put"); + free(buf); + return 0; + } + + len = _ti_flatten(&buf, term->tic); + if (len == -1) + return -1; + + if (cdbw_put_data(db, buf, len, &term->id)) + err(1, "cdbw_put_data"); + if (cdbw_put_key(db, term->name, slen, term->id)) + err(1, "cdbw_put_key"); + free(buf); + return 0; +} + +static TERM * +find_term(const char *name) +{ + ENTRY elem, *elemp; + + elem.key = __UNCONST(name); + elem.data = NULL; + elemp = hsearch(elem, FIND); + return elemp ? (TERM *)elemp->data : NULL; +} + +static TERM * +store_term(const char *name, TERM *base_term) +{ + TERM *term; + ENTRY elem; + + term = calloc(1, sizeof(*term)); + term->name = strdup(name); + TAILQ_INSERT_TAIL(&terms, term, next); + elem.key = strdup(name); + elem.data = term; + hsearch(elem, ENTER); + + term->base_term = base_term; + if (base_term != NULL) + nalias++; + else + nterm++; + + return term; +} + +static int +process_entry(TBUF *buf, int flags) +{ + char *p, *e, *alias; + TERM *term; + TIC *tic; + + if (buf->bufpos == 0) + return 0; + /* Terminate the string */ + buf->buf[buf->bufpos - 1] = '\0'; + /* First rewind the buffer for new entries */ + buf->bufpos = 0; + + if (isspace((unsigned char)*buf->buf)) + return 0; + + tic = _ti_compile(buf->buf, flags); + if (tic == NULL) + return 0; + + if (find_term(tic->name) != NULL) { + dowarn("%s: duplicate entry", tic->name); + _ti_freetic(tic); + return 0; + } + term = store_term(tic->name, NULL); + term->tic = tic; + + /* Create aliased terms */ + if (tic->alias != NULL) { + alias = p = strdup(tic->alias); + while (p != NULL && *p != '\0') { + e = strchr(p, '|'); + if (e != NULL) + *e++ = '\0'; + if (find_term(p) != NULL) { + dowarn("%s: has alias for already assigned" + " term %s", tic->name, p); + } else { + store_term(p, term); + } + p = e; + } + free(alias); + } + + return 0; +} + +static void +merge(TIC *rtic, TIC *utic, int flags) +{ + char *cap, flag, *code, type, *str; + short ind, num; + size_t n; + + cap = utic->flags.buf; + for (n = utic->flags.entries; n > 0; n--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + flag = *cap++; + if (VALID_BOOLEAN(flag) && + _ti_find_cap(&rtic->flags, 'f', ind) == NULL) + { + _ti_grow_tbuf(&rtic->flags, sizeof(uint16_t) + 1); + le16enc(rtic->flags.buf + rtic->flags.bufpos, ind); + rtic->flags.bufpos += sizeof(uint16_t); + rtic->flags.buf[rtic->flags.bufpos++] = flag; + rtic->flags.entries++; + } + } + + cap = utic->nums.buf; + for (n = utic->nums.entries; n > 0; n--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + num = le16dec(cap); + cap += sizeof(uint16_t); + if (VALID_NUMERIC(num) && + _ti_find_cap(&rtic->nums, 'n', ind) == NULL) + { + grow_tbuf(&rtic->nums, sizeof(uint16_t) * 2); + le16enc(rtic->nums.buf + rtic->nums.bufpos, ind); + rtic->nums.bufpos += sizeof(uint16_t); + le16enc(rtic->nums.buf + rtic->nums.bufpos, num); + rtic->nums.bufpos += sizeof(uint16_t); + rtic->nums.entries++; + } + } + + cap = utic->strs.buf; + for (n = utic->strs.entries; n > 0; n--) { + ind = le16dec(cap); + cap += sizeof(uint16_t); + num = le16dec(cap); + cap += sizeof(uint16_t); + if (num > 0 && + _ti_find_cap(&rtic->strs, 's', ind) == NULL) + { + grow_tbuf(&rtic->strs, (sizeof(uint16_t) * 2) + num); + le16enc(rtic->strs.buf + rtic->strs.bufpos, ind); + rtic->strs.bufpos += sizeof(uint16_t); + le16enc(rtic->strs.buf + rtic->strs.bufpos, num); + rtic->strs.bufpos += sizeof(uint16_t); + memcpy(rtic->strs.buf + rtic->strs.bufpos, + cap, num); + rtic->strs.bufpos += num; + rtic->strs.entries++; + } + cap += num; + } + + cap = utic->extras.buf; + for (n = utic->extras.entries; n > 0; n--) { + num = le16dec(cap); + cap += sizeof(uint16_t); + code = cap; + cap += num; + type = *cap++; + flag = 0; + str = NULL; + switch (type) { + case 'f': + flag = *cap++; + if (!VALID_BOOLEAN(flag)) + continue; + break; + case 'n': + num = le16dec(cap); + cap += sizeof(uint16_t); + if (!VALID_NUMERIC(num)) + continue; + break; + case 's': + num = le16dec(cap); + cap += sizeof(uint16_t); + str = cap; + cap += num; + if (num == 0) + continue; + break; + } + _ti_store_extra(rtic, 0, code, type, flag, num, str, num, + flags); + } +} + +static size_t +merge_use(int flags) +{ + size_t skipped, merged, memn; + char *cap, *scap; + uint16_t num; + TIC *rtic, *utic; + TERM *term, *uterm;; + + skipped = merged = 0; + TAILQ_FOREACH(term, &terms, next) { + if (term->base_term != NULL) + continue; + rtic = term->tic; + while ((cap = _ti_find_extra(&rtic->extras, "use")) != NULL) { + if (*cap++ != 's') { + dowarn("%s: use is not string", rtic->name); + break; + } + cap += sizeof(uint16_t); + if (strcmp(rtic->name, cap) == 0) { + dowarn("%s: uses itself", rtic->name); + goto remove; + } + uterm = find_term(cap); + if (uterm != NULL && uterm->base_term != NULL) + uterm = uterm->base_term; + if (uterm == NULL) { + dowarn("%s: no use record for %s", + rtic->name, cap); + goto remove; + } + utic = uterm->tic; + if (strcmp(utic->name, rtic->name) == 0) { + dowarn("%s: uses itself", rtic->name); + goto remove; + } + if (_ti_find_extra(&utic->extras, "use") != NULL) { + skipped++; + break; + } + cap = _ti_find_extra(&rtic->extras, "use"); + merge(rtic, utic, flags); + remove: + /* The pointers may have changed, find the use again */ + cap = _ti_find_extra(&rtic->extras, "use"); + if (cap == NULL) + dowarn("%s: use no longer exists - impossible", + rtic->name); + else { + scap = cap - (4 + sizeof(uint16_t)); + cap++; + num = le16dec(cap); + cap += sizeof(uint16_t) + num; + memn = rtic->extras.bufpos - + (cap - rtic->extras.buf); + memmove(scap, cap, memn); + rtic->extras.bufpos -= cap - scap; + cap = scap; + rtic->extras.entries--; + merged++; + } + } + } + + if (merged == 0 && skipped != 0) + dowarn("circular use detected"); + return merged; +} + +static int +print_dump(int argc, char **argv) +{ + TERM *term; + uint8_t *buf; + int i, n; + size_t j, col; + ssize_t len; + + printf("struct compiled_term {\n"); + printf("\tconst char *name;\n"); + printf("\tconst char *cap;\n"); + printf("\tsize_t caplen;\n"); + printf("};\n\n"); + + printf("const struct compiled_term compiled_terms[] = {\n"); + + n = 0; + for (i = 0; i < argc; i++) { + term = find_term(argv[i]); + if (term == NULL) { + warnx("%s: no description for terminal", argv[i]); + continue; + } + if (term->base_term != NULL) { + warnx("%s: cannot dump alias", argv[i]); + continue; + } + /* Don't compile the aliases in, save space */ + free(term->tic->alias); + term->tic->alias = NULL; + len = _ti_flatten(&buf, term->tic); + if (len == 0 || len == -1) + continue; + + printf("\t{\n"); + printf("\t\t\"%s\",\n", argv[i]); + n++; + for (j = 0, col = 0; j < (size_t)len; j++) { + if (col == 0) { + printf("\t\t\""); + col = 16; + } + + col += printf("\\%03o", (uint8_t)buf[j]); + if (col > 75) { + printf("\"%s\n", + j + 1 == (size_t)len ? "," : ""); + col = 0; + } + } + if (col != 0) + printf("\",\n"); + printf("\t\t%zu\n", len); + printf("\t}"); + if (i + 1 < argc) + printf(","); + printf("\n"); + free(buf); + } + printf("};\n"); + + return n; +} + +static void +write_database(const char *dbname) +{ + struct cdbw *db; + char *tmp_dbname; + TERM *term; + int fd; + + db = cdbw_open(); + if (db == NULL) + err(1, "cdbw_open failed"); + /* Save the terms */ + TAILQ_FOREACH(term, &terms, next) + save_term(db, term); + + asprintf(&tmp_dbname, "%s.XXXXXX", dbname); + fd = mkstemp(tmp_dbname); + if (fd == -1) + err(1, "creating temporary database %s failed", tmp_dbname); + if (cdbw_output(db, fd, "NetBSD terminfo", cdbw_stable_seeder)) + err(1, "writing temporary database %s failed", tmp_dbname); + if (fchmod(fd, DEFFILEMODE)) + err(1, "fchmod failed"); + if (close(fd)) + err(1, "writing temporary database %s failed", tmp_dbname); + if (rename(tmp_dbname, dbname)) + err(1, "renaming %s to %s failed", tmp_dbname, dbname); + free(tmp_dbname); + cdbw_close(db); +} + +int +main(int argc, char **argv) +{ + int ch, cflag, sflag, flags; + char *source, *dbname, *buf, *ofile; + FILE *f; + size_t buflen; + ssize_t len; + TBUF tbuf; + struct term *term; + + cflag = sflag = 0; + ofile = NULL; + flags = TIC_ALIAS | TIC_DESCRIPTION | TIC_WARNING; + while ((ch = getopt(argc, argv, "Saco:sx")) != -1) + switch (ch) { + case 'S': + Sflag = 1; + /* We still compile aliases so that use= works. + * However, it's removed before we flatten to save space. */ + flags &= ~TIC_DESCRIPTION; + break; + case 'a': + flags |= TIC_COMMENT; + break; + case 'c': + cflag = 1; + break; + case 'o': + ofile = optarg; + break; + case 's': + sflag = 1; + break; + case 'x': + flags |= TIC_EXTRA; + break; + case '?': /* FALLTHROUGH */ + default: + fprintf(stderr, "usage: %s [-acSsx] [-o file] source\n", + getprogname()); + return EXIT_FAILURE; + } + + if (optind == argc) + errx(1, "No source file given"); + source = argv[optind++]; + f = fopen(source, "r"); + if (f == NULL) + err(1, "fopen: %s", source); + + hcreate(HASH_SIZE); + + buf = tbuf.buf = NULL; + buflen = tbuf.buflen = tbuf.bufpos = 0; + while ((len = getline(&buf, &buflen, f)) != -1) { + /* Skip comments */ + if (*buf == '#') + continue; + if (buf[len - 1] != '\n') { + process_entry(&tbuf, flags); + dowarn("last line is not a comment" + " and does not end with a newline"); + continue; + } + /* + * If the first char is space not a space then we have a + * new entry, so process it. + */ + if (!isspace((unsigned char)*buf) && tbuf.bufpos != 0) + process_entry(&tbuf, flags); + + /* Grow the buffer if needed */ + grow_tbuf(&tbuf, len); + /* Append the string */ + memcpy(tbuf.buf + tbuf.bufpos, buf, len); + tbuf.bufpos += len; + } + free(buf); + /* Process the last entry if not done already */ + process_entry(&tbuf, flags); + free(tbuf.buf); + + /* Merge use entries until we have merged all we can */ + while (merge_use(flags) != 0) + ; + + if (Sflag) { + print_dump(argc - optind, argv + optind); + return error_exit; + } + + if (cflag) + return error_exit; + + if (ofile == NULL) + asprintf(&dbname, "%s.cdb", source); + else + dbname = ofile; + write_database(dbname); + + if (sflag != 0) + fprintf(stderr, "%zu entries and %zu aliases written to %s\n", + nterm, nalias, dbname); + + if (ofile == NULL) + free(dbname); + while ((term = TAILQ_FIRST(&terms)) != NULL) { + TAILQ_REMOVE_HEAD(&terms, next); + _ti_freetic(term->tic); + free(term->name); + free(term); + } + + return EXIT_SUCCESS; +} Index: contrib/mg/tparm.c =================================================================== --- /dev/null +++ contrib/mg/tparm.c @@ -0,0 +1,620 @@ +/* $NetBSD: tparm.c,v 1.17 2017/05/04 09:42:23 roy Exp $ */ + +/* + * Copyright (c) 2009, 2011, 2013 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#define LONG_STR_MAX ((CHAR_BIT * sizeof(long)) / 3) +#define BUFINC 128 /* Size to increament the terminal buffer by */ + +#define VA_LONG_LONG 1 +#define VA_CHAR_INT 2 +//#define VA_CHAR_LONG 3 /* No need for this yet */ + +static TERMINAL *dumbterm; /* For non thread safe functions */ + +typedef struct { + long nums[20]; + char *strings[20]; + size_t offset; +} TPSTACK; + +typedef struct { + long num; + char *string; +} TPVAR; + +static int +push(long num, char *string, TPSTACK *stack) +{ + if (stack->offset >= sizeof(stack->nums)) { + errno = E2BIG; + return -1; + } + stack->nums[stack->offset] = num; + stack->strings[stack->offset] = string; + stack->offset++; + return 0; +} + +static int +pop(long *num, char **string, TPSTACK *stack) +{ + if (stack->offset == 0) { + if (num) + *num = 0; + if (string) + *string = NULL; + errno = E2BIG; + return -1; + } + stack->offset--; + if (num) + *num = stack->nums[stack->offset]; + if (string) + *string = stack->strings[stack->offset]; + return 0; +} + +static char * +checkbuf(TERMINAL *term, size_t len) +{ + char *buf; + + if (term->_bufpos + len >= term->_buflen) { + len = term->_buflen + MAX(len, BUFINC); + buf = realloc(term->_buf, len); + if (buf == NULL) + return NULL; + term->_buf = buf; + term->_buflen = len; + } + return term->_buf; +} + +static size_t +ochar(TERMINAL *term, int c) +{ + if (c == 0) + c = 0200; + /* Check we have space and a terminator */ + if (checkbuf(term, 2) == NULL) + return 0; + term->_buf[term->_bufpos++] = (char)c; + return 1; +} + +static size_t +onum(TERMINAL *term, const char *fmt, int num, size_t len) +{ + int l; + size_t r; + + if (len < LONG_STR_MAX) + len = LONG_STR_MAX; + if (checkbuf(term, len + 2) == NULL) + return 0; + l = snprintf(term->_buf + term->_bufpos, len + 2, fmt, num); + if (l == -1) + return 0; + r = (size_t)l; + term->_bufpos += r; + return r; +} + +/* + Make a pass through the string so we can work out + which parameters are ints and which are char *. + Basically we only use char * if %p[1-9] is followed by %l or %s. +*/ +int +_ti_parm_analyse(const char *str, int *piss, int piss_len) +{ + int nparm, lpop; + char c; + + nparm = 0; + lpop = -1; + while ((c = *str++) != '\0') { + if (c != '%') + continue; + c = *str++; + switch (c) { + case 'l': /* FALLTHROUGH */ + case 's': + if (lpop > 0) { + if (lpop <= piss_len) + piss[lpop - 1] = 1; + else if (piss) + errno = E2BIG; + } + break; + case 'p': + c = *str++; + if (c < '1' || c > '9') { + errno = EINVAL; + continue; + } else { + lpop = c - '0'; + if (lpop > nparm) + nparm = lpop; + } + break; + default: + lpop = -1; + } + } + + return nparm; +} + +static char * +_ti_tiparm(TERMINAL *term, const char *str, int va_type, va_list parms) +{ + char c, fmt[64], *fp, *ostr; + long val, val2; + long dnums[26]; /* dynamic variables a-z, not preserved */ + size_t l, max, width, precision, olen; + TPSTACK stack; + TPVAR params[TPARM_MAX]; + unsigned int done, dot, minus; + int piss[TPARM_MAX]; /* Parameter IS String - piss ;) */ + + if (str == NULL) + return NULL; + + /* + If not passed a terminal, malloc a dummy one. + This means we can preserve buffers and variables per terminal and + still work with non thread safe functions (which sadly are still the + norm and standard). + */ + if (term == NULL) { + if (dumbterm == NULL) { + dumbterm = malloc(sizeof(*dumbterm)); + if (dumbterm == NULL) + return NULL; + dumbterm->_buflen = 0; + } + term = dumbterm; + } + + term->_bufpos = 0; + /* Ensure we have an initial buffer */ + if (term->_buflen == 0) { + term->_buf = malloc(BUFINC); + if (term->_buf == NULL) + return NULL; + term->_buflen = BUFINC; + } + + memset(&piss, 0, sizeof(piss)); + max = (size_t)_ti_parm_analyse(str, piss, TPARM_MAX); + + /* Put our parameters into variables */ + memset(¶ms, 0, sizeof(params)); + for (l = 0; l < max; l++) { + if (piss[l]) { + if (va_type == VA_LONG_LONG) { + /* This only works if char * fits into a long + * on this platform. */ + if (sizeof(char *) <= sizeof(long)/*CONSTCOND*/) + params[l].string = + (char *)va_arg(parms, long); + else { + errno = ENOTSUP; + return NULL; + } + } else + params[l].string = va_arg(parms, char *); + } else { + if (va_type == VA_CHAR_INT) + params[l].num = (long)va_arg(parms, int); + else + params[l].num = va_arg(parms, long); + } + } + + memset(&stack, 0, sizeof(stack)); + while ((c = *str++) != '\0') { + if (c != '%' || (c = *str++) == '%') { + if (c == '\0') + break; + if (ochar(term, c) == 0) + return NULL; + continue; + } + + /* Handle formatting. */ + fp = fmt; + *fp++ = '%'; + done = dot = minus = width = precision = 0; + val = 0; + while (done == 0 && (size_t)(fp - fmt) < sizeof(fmt)) { + switch (c) { + case 'c': /* FALLTHROUGH */ + case 's': + *fp++ = c; + done = 1; + break; + case 'd': /* FALLTHROUGH */ + case 'o': /* FALLTHROUGH */ + case 'x': /* FALLTHROUGH */ + case 'X': /* FALLTHROUGH */ + *fp++ = 'l'; + *fp++ = c; + done = 1; + break; + case '#': /* FALLTHROUGH */ + case ' ': + *fp++ = c; + break; + case '.': + *fp++ = c; + if (dot == 0) { + dot = 1; + width = (size_t)val; + } else + done = 2; + val = 0; + break; + case ':': + minus = 1; + break; + case '-': + if (minus) + *fp++ = c; + else + done = 1; + break; + default: + if (isdigit((unsigned char)c)) { + val = (val * 10) + (c - '0'); + if (val > 10000) + done = 2; + else + *fp++ = c; + } else + done = 1; + } + if (done == 0) + c = *str++; + } + if (done == 2) { + /* Found an error in the format */ + fp = fmt + 1; + *fp = *str; + olen = 0; + } else { + if (dot == 0) + width = (size_t)val; + else + precision = (size_t)val; + olen = MAX(width, precision); + } + *fp++ = '\0'; + + /* Handle commands */ + switch (c) { + case 'c': + pop(&val, NULL, &stack); + if (ochar(term, (unsigned char)val) == 0) + return NULL; + break; + case 's': + pop(NULL, &ostr, &stack); + if (ostr != NULL) { + int r; + + l = strlen(ostr); + if (l < (size_t)olen) + l = olen; + if (checkbuf(term, (size_t)(l + 1)) == NULL) + return NULL; + r = snprintf(term->_buf + term->_bufpos, l + 1, + fmt, ostr); + if (r != -1) + term->_bufpos += (size_t)r; + } + break; + case 'l': + pop(NULL, &ostr, &stack); + if (ostr == NULL) + l = 0; + else + l = strlen(ostr); +#ifdef NCURSES_COMPAT_57 + if (onum(term, "%ld", (long)l, 0) == 0) + return NULL; +#else + push((long)l, NULL, &stack); +#endif + break; + case 'd': /* FALLTHROUGH */ + case 'o': /* FALLTHROUGH */ + case 'x': /* FALLTHROUGH */ + case 'X': + pop(&val, NULL, &stack); + if (onum(term, fmt, (int)val, olen) == 0) + return NULL; + break; + case 'p': + if (*str < '1' || *str > '9') + break; + l = (size_t)(*str++ - '1'); + if (push(params[l].num, params[l].string, &stack)) + return NULL; + break; + case 'P': + pop(&val, NULL, &stack); + if (*str >= 'a' && *str <= 'z') + dnums[*str - 'a'] = val; + else if (*str >= 'A' && *str <= 'Z') + term->_snums[*str - 'A'] = val; + break; + case 'g': + if (*str >= 'a' && *str <= 'z') { + if (push(dnums[*str - 'a'], NULL, &stack)) + return NULL; + } else if (*str >= 'A' && *str <= 'Z') { + if (push(term->_snums[*str - 'A'], + NULL, &stack)) + return NULL; + } + break; + case 'i': + if (piss[0] == 0) + params[0].num++; + if (piss[1] == 0) + params[1].num++; + break; + case '\'': + if (push((long)(unsigned char)*str++, NULL, &stack)) + return NULL; + while (*str != '\0' && *str != '\'') + str++; + if (*str == '\'') + str++; + break; + case '{': + val = 0; + for (; isdigit((unsigned char)*str); str++) + val = (val * 10) + (*str - '0'); + if (push(val, NULL, &stack)) + return NULL; + while (*str != '\0' && *str != '}') + str++; + if (*str == '}') + str++; + break; + case '+': /* FALLTHROUGH */ + case '-': /* FALLTHROUGH */ + case '*': /* FALLTHROUGH */ + case '/': /* FALLTHROUGH */ + case 'm': /* FALLTHROUGH */ + case 'A': /* FALLTHROUGH */ + case 'O': /* FALLTHROUGH */ + case '&': /* FALLTHROUGH */ + case '|': /* FALLTHROUGH */ + case '^': /* FALLTHROUGH */ + case '=': /* FALLTHROUGH */ + case '<': /* FALLTHROUGH */ + case '>': + pop(&val, NULL, &stack); + pop(&val2, NULL, &stack); + switch (c) { + case '+': + val = val + val2; + break; + case '-': + val = val2 - val; + break; + case '*': + val = val * val2; + break; + case '/': + val = val ? val2 / val : 0; + break; + case 'm': + val = val ? val2 % val : 0; + break; + case 'A': + val = val && val2; + break; + case 'O': + val = val || val2; + break; + case '&': + val = val & val2; + break; + case '|': + val = val | val2; + break; + case '^': + val = val ^ val2; + break; + case '=': + val = val == val2; + break; + case '<': + val = val2 < val; + break; + case '>': + val = val2 > val; + break; + } + if (push(val, NULL, &stack)) + return NULL; + break; + case '!': + case '~': + pop(&val, NULL, &stack); + switch (c) { + case '!': + val = !val; + break; + case '~': + val = ~val; + break; + } + if (push(val, NULL, &stack)) + return NULL; + break; + case '?': /* if */ + break; + case 't': /* then */ + pop(&val, NULL, &stack); + if (val == 0) { + l = 0; + for (; *str != '\0'; str++) { + if (*str != '%') + continue; + str++; + if (*str == '?') + l++; + else if (*str == ';') { + if (l > 0) + l--; + else { + str++; + break; + } + } else if (*str == 'e' && l == 0) { + str++; + break; + } + } + } + break; + case 'e': /* else */ + l = 0; + for (; *str != '\0'; str++) { + if (*str != '%') + continue; + str++; + if (*str == '?') + l++; + else if (*str == ';') { + if (l > 0) + l--; + else { + str++; + break; + } + } + } + break; + case ';': /* fi */ + break; + } + } + term->_buf[term->_bufpos] = '\0'; + return term->_buf; +} + +char * +ti_tiparm(TERMINAL *term, const char *str, ...) +{ + va_list va; + char *ret; + + va_start(va, str); + ret = _ti_tiparm(term, str, VA_CHAR_INT, va); + va_end(va); + return ret; +} + +char * +tiparm(const char *str, ...) +{ + va_list va; + char *ret; + + va_start(va, str); + ret = _ti_tiparm(NULL, str, VA_CHAR_INT, va); + va_end(va); + return ret; +} + +#ifdef VA_CHAR_LONG +char * +ti_tlparm(TERMINAL *term, const char *str, ...) +{ + va_list va; + char *ret; + + va_start(va, str); + ret = _ti_tiparm(term, str, VA_CHAR_LONG, va); + va_end(va); + return ret; +} + +char * +tlparm(const char *str, ...) +{ + va_list va; + char *ret; + + va_start(va, str); + ret = _ti_tiparm(NULL, str, VA_CHAR_LONG, va); + va_end(va); + return ret; +} +#endif + +static char * +_tparm(const char *str, ...) +{ + va_list va; + char *ret; + + va_start(va, str); + ret = _ti_tiparm(NULL, str, VA_LONG_LONG, va); + va_end(va); + return ret; +} + +char * +tparm(const char *str, + long p1, long p2, long p3, long p4, long p5, + long p6, long p7, long p8, long p9) +{ + + return _tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9); +} Index: contrib/mg/tputs.c =================================================================== --- /dev/null +++ contrib/mg/tputs.c @@ -0,0 +1,173 @@ +/* $NetBSD: tputs.c,v 1.5 2019/10/03 18:02:05 christos Exp $ */ + +/* + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roy Marples. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include + +#include "term_private.h" +#include "terminfo_term.h" + +#ifndef __arraycount +#define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) +#endif + +/* + * The following array gives the number of tens of milliseconds per + * character for each speed as returned by gtty. Thus since 300 + * baud returns a 7, there are 33.3 milliseconds per char at 300 baud. + */ +static const short tmspc10[] = { + 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5 +}; + +short ospeed; +char PC; + +static int +_ti_calcdelay(const char **str, int affcnt, int *mand) +{ + int i; + + i = 0; + /* Convert the delay */ + while (isdigit(*(const unsigned char *)*str)) + i = i * 10 + *(*str)++ - '0'; + i *= 10; + if (*(*str) == '.') { + (*str)++; + if (isdigit(*(const unsigned char *)*str)) + i += *(*str) - '0'; + while (isdigit(*(const unsigned char *)*str)) + (*str)++; + } + if (*(*str) == '*') { + (*str)++; + i *= affcnt; + } else if (*(*str) == '/') { + (*str)++; + if (mand != NULL) + *mand = 1; + } + return i; +} + +static void +_ti_outputdelay(int delay, short os, char pc, + int (*outc)(int, void *), void *args) +{ + int mspc10; + + if (delay < 1 || os < 1 || (size_t)os >= __arraycount(tmspc10)) + return; + + mspc10 = tmspc10[os]; + delay += mspc10 / 2; + for (delay /= mspc10; delay > 0; delay--) + outc(pc, args); +} + +static int +_ti_puts(int dodelay, short os, char pc, + const char *str, int affcnt, int (*outc)(int, void *), void *args) +{ + int taildelay, delay, mand; + + if (str == NULL) + return OK; + + taildelay = _ti_calcdelay(&str, affcnt, NULL); + + /* Output the string with embedded delays */ + for (; *str != '\0'; str++) { + if (str[0] != '$' || + str[1] != '<' || + !(isdigit((const unsigned char)str[2]) || str[2] == '.') || + strchr(str + 3, '>') == NULL) + { + outc(*str, args); + } else { + str += 2; + mand = 0; + delay = _ti_calcdelay(&str, affcnt, &mand); + if (dodelay != 0 || mand != 0) + _ti_outputdelay(delay, os, pc, outc, args); + } + } + + /* Delay if needed */ + if (dodelay) + _ti_outputdelay(taildelay, os, pc, outc, args); + + return OK; +} + +int +ti_puts(const TERMINAL *term, const char *str, int affcnt, + int (*outc)(int, void *), void *args) +{ + int dodelay; + char pc; + + dodelay = (str == t_bell(term) || + str == t_flash_screen(term) || + (t_xon_xoff(term) == 0 && t_padding_baud_rate(term) != 0)); + + if (t_pad_char(term) == NULL) + pc = '\0'; + else + pc = *t_pad_char(term); + return _ti_puts(dodelay, term->_ospeed, pc, + str, affcnt, outc, args); +} + +int +ti_putp(const TERMINAL *term, const char *str) +{ + + return ti_puts(term, str, 1, + (int (*)(int, void *))(void *)putchar, NULL); +} + +int +tputs(const char *str, int affcnt, int (*outc)(int)) +{ + + return _ti_puts(1, ospeed, PC, str, affcnt, + (int (*)(int, void *))(void *)outc, NULL); +} + +int +putp(const char *str) +{ + + return tputs(str, 1, putchar); +} Index: contrib/mg/tree.h =================================================================== --- /dev/null +++ contrib/mg/tree.h @@ -0,0 +1,748 @@ +/* $OpenBSD: tree.h,v 1.14 2015/05/25 03:07:49 deraadt Exp $ */ +/* + * Copyright 2002 Niels Provos + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SYS_TREE_H_ +#define _SYS_TREE_H_ + +/* + * This file defines data structures for different types of trees: + * splay trees and red-black trees. + * + * A splay tree is a self-organizing data structure. Every operation + * on the tree causes a splay to happen. The splay moves the requested + * node to the root of the tree and partly rebalances it. + * + * This has the benefit that request locality causes faster lookups as + * the requested nodes move to the top of the tree. On the other hand, + * every lookup causes memory writes. + * + * The Balance Theorem bounds the total access time for m operations + * and n inserts on an initially empty tree as O((m + n)lg n). The + * amortized cost for a sequence of m accesses to a splay tree is O(lg n); + * + * A red-black tree is a binary search tree with the node color as an + * extra attribute. It fulfills a set of conditions: + * - every search path from the root to a leaf consists of the + * same number of black nodes, + * - each red node (except for the root) has a black parent, + * - each leaf node is black. + * + * Every operation on a red-black tree is bounded as O(lg n). + * The maximum height of a red-black tree is 2lg (n+1). + */ + +#define SPLAY_HEAD(name, type) \ +struct name { \ + struct type *sph_root; /* root of the tree */ \ +} + +#define SPLAY_INITIALIZER(root) \ + { NULL } + +#define SPLAY_INIT(root) do { \ + (root)->sph_root = NULL; \ +} while (0) + +#define SPLAY_ENTRY(type) \ +struct { \ + struct type *spe_left; /* left element */ \ + struct type *spe_right; /* right element */ \ +} + +#define SPLAY_LEFT(elm, field) (elm)->field.spe_left +#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right +#define SPLAY_ROOT(head) (head)->sph_root +#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL) + +/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */ +#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + (head)->sph_root = tmp; \ +} while (0) + +#define SPLAY_LINKLEFT(head, tmp, field) do { \ + SPLAY_LEFT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_LINKRIGHT(head, tmp, field) do { \ + SPLAY_RIGHT(tmp, field) = (head)->sph_root; \ + tmp = (head)->sph_root; \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \ +} while (0) + +#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \ + SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \ + SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \ + SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \ +} while (0) + +/* Generates prototypes and inline functions */ + +#define SPLAY_PROTOTYPE(name, type, field, cmp) \ +void name##_SPLAY(struct name *, struct type *); \ +void name##_SPLAY_MINMAX(struct name *, int); \ +struct type *name##_SPLAY_INSERT(struct name *, struct type *); \ +struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \ + \ +/* Finds the node with the same key as elm */ \ +static __inline struct type * \ +name##_SPLAY_FIND(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) \ + return(NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) \ + return (head->sph_root); \ + return (NULL); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_NEXT(struct name *head, struct type *elm) \ +{ \ + name##_SPLAY(head, elm); \ + if (SPLAY_RIGHT(elm, field) != NULL) { \ + elm = SPLAY_RIGHT(elm, field); \ + while (SPLAY_LEFT(elm, field) != NULL) { \ + elm = SPLAY_LEFT(elm, field); \ + } \ + } else \ + elm = NULL; \ + return (elm); \ +} \ + \ +static __inline struct type * \ +name##_SPLAY_MIN_MAX(struct name *head, int val) \ +{ \ + name##_SPLAY_MINMAX(head, val); \ + return (SPLAY_ROOT(head)); \ +} + +/* Main splay operation. + * Moves node close to the key of elm to top + */ +#define SPLAY_GENERATE(name, type, field, cmp) \ +struct type * \ +name##_SPLAY_INSERT(struct name *head, struct type *elm) \ +{ \ + if (SPLAY_EMPTY(head)) { \ + SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \ + } else { \ + int __comp; \ + name##_SPLAY(head, elm); \ + __comp = (cmp)(elm, (head)->sph_root); \ + if(__comp < 0) { \ + SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\ + SPLAY_RIGHT(elm, field) = (head)->sph_root; \ + SPLAY_LEFT((head)->sph_root, field) = NULL; \ + } else if (__comp > 0) { \ + SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\ + SPLAY_LEFT(elm, field) = (head)->sph_root; \ + SPLAY_RIGHT((head)->sph_root, field) = NULL; \ + } else \ + return ((head)->sph_root); \ + } \ + (head)->sph_root = (elm); \ + return (NULL); \ +} \ + \ +struct type * \ +name##_SPLAY_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *__tmp; \ + if (SPLAY_EMPTY(head)) \ + return (NULL); \ + name##_SPLAY(head, elm); \ + if ((cmp)(elm, (head)->sph_root) == 0) { \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \ + (head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\ + } else { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + (head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\ + name##_SPLAY(head, elm); \ + SPLAY_RIGHT((head)->sph_root, field) = __tmp; \ + } \ + return (elm); \ + } \ + return (NULL); \ +} \ + \ +void \ +name##_SPLAY(struct name *head, struct type *elm) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ + int __comp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while ((__comp = (cmp)(elm, (head)->sph_root))) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if ((cmp)(elm, __tmp) > 0){ \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} \ + \ +/* Splay with either the minimum or the maximum element \ + * Used to find minimum or maximum element in tree. \ + */ \ +void name##_SPLAY_MINMAX(struct name *head, int __comp) \ +{ \ + struct type __node, *__left, *__right, *__tmp; \ +\ + SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\ + __left = __right = &__node; \ +\ + while (1) { \ + if (__comp < 0) { \ + __tmp = SPLAY_LEFT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp < 0){ \ + SPLAY_ROTATE_RIGHT(head, __tmp, field); \ + if (SPLAY_LEFT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKLEFT(head, __right, field); \ + } else if (__comp > 0) { \ + __tmp = SPLAY_RIGHT((head)->sph_root, field); \ + if (__tmp == NULL) \ + break; \ + if (__comp > 0) { \ + SPLAY_ROTATE_LEFT(head, __tmp, field); \ + if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\ + break; \ + } \ + SPLAY_LINKRIGHT(head, __left, field); \ + } \ + } \ + SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \ +} + +#define SPLAY_NEGINF -1 +#define SPLAY_INF 1 + +#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y) +#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y) +#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y) +#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y) +#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF)) +#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \ + : name##_SPLAY_MIN_MAX(x, SPLAY_INF)) + +#define SPLAY_FOREACH(x, name, head) \ + for ((x) = SPLAY_MIN(name, head); \ + (x) != NULL; \ + (x) = SPLAY_NEXT(name, head, x)) + +/* Macros that define a red-black tree */ +#define RB_HEAD(name, type) \ +struct name { \ + struct type *rbh_root; /* root of the tree */ \ +} + +#define RB_INITIALIZER(root) \ + { NULL } + +#define RB_INIT(root) do { \ + (root)->rbh_root = NULL; \ +} while (0) + +#define RB_BLACK 0 +#define RB_RED 1 +#define RB_ENTRY(type) \ +struct { \ + struct type *rbe_left; /* left element */ \ + struct type *rbe_right; /* right element */ \ + struct type *rbe_parent; /* parent element */ \ + int rbe_color; /* node color */ \ +} + +#define RB_LEFT(elm, field) (elm)->field.rbe_left +#define RB_RIGHT(elm, field) (elm)->field.rbe_right +#define RB_PARENT(elm, field) (elm)->field.rbe_parent +#define RB_COLOR(elm, field) (elm)->field.rbe_color +#define RB_ROOT(head) (head)->rbh_root +#define RB_EMPTY(head) (RB_ROOT(head) == NULL) + +#define RB_SET(elm, parent, field) do { \ + RB_PARENT(elm, field) = parent; \ + RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \ + RB_COLOR(elm, field) = RB_RED; \ +} while (0) + +#define RB_SET_BLACKRED(black, red, field) do { \ + RB_COLOR(black, field) = RB_BLACK; \ + RB_COLOR(red, field) = RB_RED; \ +} while (0) + +#ifndef RB_AUGMENT +#define RB_AUGMENT(x) do {} while (0) +#endif + +#define RB_ROTATE_LEFT(head, elm, tmp, field) do { \ + (tmp) = RB_RIGHT(elm, field); \ + if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field))) { \ + RB_PARENT(RB_LEFT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_LEFT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +#define RB_ROTATE_RIGHT(head, elm, tmp, field) do { \ + (tmp) = RB_LEFT(elm, field); \ + if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field))) { \ + RB_PARENT(RB_RIGHT(tmp, field), field) = (elm); \ + } \ + RB_AUGMENT(elm); \ + if ((RB_PARENT(tmp, field) = RB_PARENT(elm, field))) { \ + if ((elm) == RB_LEFT(RB_PARENT(elm, field), field)) \ + RB_LEFT(RB_PARENT(elm, field), field) = (tmp); \ + else \ + RB_RIGHT(RB_PARENT(elm, field), field) = (tmp); \ + } else \ + (head)->rbh_root = (tmp); \ + RB_RIGHT(tmp, field) = (elm); \ + RB_PARENT(elm, field) = (tmp); \ + RB_AUGMENT(tmp); \ + if ((RB_PARENT(tmp, field))) \ + RB_AUGMENT(RB_PARENT(tmp, field)); \ +} while (0) + +/* Generates prototypes and inline functions */ +#define RB_PROTOTYPE(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp,) +#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \ + RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \ +attr void name##_RB_INSERT_COLOR(struct name *, struct type *); \ +attr void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\ +attr struct type *name##_RB_REMOVE(struct name *, struct type *); \ +attr struct type *name##_RB_INSERT(struct name *, struct type *); \ +attr struct type *name##_RB_FIND(struct name *, struct type *); \ +attr struct type *name##_RB_NFIND(struct name *, struct type *); \ +attr struct type *name##_RB_NEXT(struct type *); \ +attr struct type *name##_RB_PREV(struct type *); \ +attr struct type *name##_RB_MINMAX(struct name *, int); \ + \ + +/* Main rb operation. + * Moves node close to the key of elm to top + */ +#define RB_GENERATE(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp,) +#define RB_GENERATE_STATIC(name, type, field, cmp) \ + RB_GENERATE_INTERNAL(name, type, field, cmp, __attribute__((__unused__)) static) +#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \ +attr void \ +name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \ +{ \ + struct type *parent, *gparent, *tmp; \ + while ((parent = RB_PARENT(elm, field)) && \ + RB_COLOR(parent, field) == RB_RED) { \ + gparent = RB_PARENT(parent, field); \ + if (parent == RB_LEFT(gparent, field)) { \ + tmp = RB_RIGHT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_RIGHT(parent, field) == elm) { \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_RIGHT(head, gparent, tmp, field); \ + } else { \ + tmp = RB_LEFT(gparent, field); \ + if (tmp && RB_COLOR(tmp, field) == RB_RED) { \ + RB_COLOR(tmp, field) = RB_BLACK; \ + RB_SET_BLACKRED(parent, gparent, field);\ + elm = gparent; \ + continue; \ + } \ + if (RB_LEFT(parent, field) == elm) { \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = parent; \ + parent = elm; \ + elm = tmp; \ + } \ + RB_SET_BLACKRED(parent, gparent, field); \ + RB_ROTATE_LEFT(head, gparent, tmp, field); \ + } \ + } \ + RB_COLOR(head->rbh_root, field) = RB_BLACK; \ +} \ + \ +attr void \ +name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \ +{ \ + struct type *tmp; \ + while ((elm == NULL || RB_COLOR(elm, field) == RB_BLACK) && \ + elm != RB_ROOT(head)) { \ + if (RB_LEFT(parent, field) == elm) { \ + tmp = RB_RIGHT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK) {\ + struct type *oleft; \ + if ((oleft = RB_LEFT(tmp, field)))\ + RB_COLOR(oleft, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_RIGHT(head, tmp, oleft, field);\ + tmp = RB_RIGHT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_RIGHT(tmp, field)) \ + RB_COLOR(RB_RIGHT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_LEFT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } else { \ + tmp = RB_LEFT(parent, field); \ + if (RB_COLOR(tmp, field) == RB_RED) { \ + RB_SET_BLACKRED(tmp, parent, field); \ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + if ((RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) &&\ + (RB_RIGHT(tmp, field) == NULL || \ + RB_COLOR(RB_RIGHT(tmp, field), field) == RB_BLACK)) {\ + RB_COLOR(tmp, field) = RB_RED; \ + elm = parent; \ + parent = RB_PARENT(elm, field); \ + } else { \ + if (RB_LEFT(tmp, field) == NULL || \ + RB_COLOR(RB_LEFT(tmp, field), field) == RB_BLACK) {\ + struct type *oright; \ + if ((oright = RB_RIGHT(tmp, field)))\ + RB_COLOR(oright, field) = RB_BLACK;\ + RB_COLOR(tmp, field) = RB_RED; \ + RB_ROTATE_LEFT(head, tmp, oright, field);\ + tmp = RB_LEFT(parent, field); \ + } \ + RB_COLOR(tmp, field) = RB_COLOR(parent, field);\ + RB_COLOR(parent, field) = RB_BLACK; \ + if (RB_LEFT(tmp, field)) \ + RB_COLOR(RB_LEFT(tmp, field), field) = RB_BLACK;\ + RB_ROTATE_RIGHT(head, parent, tmp, field);\ + elm = RB_ROOT(head); \ + break; \ + } \ + } \ + } \ + if (elm) \ + RB_COLOR(elm, field) = RB_BLACK; \ +} \ + \ +attr struct type * \ +name##_RB_REMOVE(struct name *head, struct type *elm) \ +{ \ + struct type *child, *parent, *old = elm; \ + int color; \ + if (RB_LEFT(elm, field) == NULL) \ + child = RB_RIGHT(elm, field); \ + else if (RB_RIGHT(elm, field) == NULL) \ + child = RB_LEFT(elm, field); \ + else { \ + struct type *left; \ + elm = RB_RIGHT(elm, field); \ + while ((left = RB_LEFT(elm, field))) \ + elm = left; \ + child = RB_RIGHT(elm, field); \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ + if (RB_PARENT(elm, field) == old) \ + parent = elm; \ + (elm)->field = (old)->field; \ + if (RB_PARENT(old, field)) { \ + if (RB_LEFT(RB_PARENT(old, field), field) == old)\ + RB_LEFT(RB_PARENT(old, field), field) = elm;\ + else \ + RB_RIGHT(RB_PARENT(old, field), field) = elm;\ + RB_AUGMENT(RB_PARENT(old, field)); \ + } else \ + RB_ROOT(head) = elm; \ + RB_PARENT(RB_LEFT(old, field), field) = elm; \ + if (RB_RIGHT(old, field)) \ + RB_PARENT(RB_RIGHT(old, field), field) = elm; \ + if (parent) { \ + left = parent; \ + do { \ + RB_AUGMENT(left); \ + } while ((left = RB_PARENT(left, field))); \ + } \ + goto color; \ + } \ + parent = RB_PARENT(elm, field); \ + color = RB_COLOR(elm, field); \ + if (child) \ + RB_PARENT(child, field) = parent; \ + if (parent) { \ + if (RB_LEFT(parent, field) == elm) \ + RB_LEFT(parent, field) = child; \ + else \ + RB_RIGHT(parent, field) = child; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = child; \ +color: \ + if (color == RB_BLACK) \ + name##_RB_REMOVE_COLOR(head, parent, child); \ + return (old); \ +} \ + \ +/* Inserts a node into the RB tree */ \ +attr struct type * \ +name##_RB_INSERT(struct name *head, struct type *elm) \ +{ \ + struct type *tmp; \ + struct type *parent = NULL; \ + int comp = 0; \ + tmp = RB_ROOT(head); \ + while (tmp) { \ + parent = tmp; \ + comp = (cmp)(elm, parent); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + RB_SET(elm, parent, field); \ + if (parent != NULL) { \ + if (comp < 0) \ + RB_LEFT(parent, field) = elm; \ + else \ + RB_RIGHT(parent, field) = elm; \ + RB_AUGMENT(parent); \ + } else \ + RB_ROOT(head) = elm; \ + name##_RB_INSERT_COLOR(head, elm); \ + return (NULL); \ +} \ + \ +/* Finds the node with the same key as elm */ \ +attr struct type * \ +name##_RB_FIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) \ + tmp = RB_LEFT(tmp, field); \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (NULL); \ +} \ + \ +/* Finds the first node greater than or equal to the search key */ \ +attr struct type * \ +name##_RB_NFIND(struct name *head, struct type *elm) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *res = NULL; \ + int comp; \ + while (tmp) { \ + comp = cmp(elm, tmp); \ + if (comp < 0) { \ + res = tmp; \ + tmp = RB_LEFT(tmp, field); \ + } \ + else if (comp > 0) \ + tmp = RB_RIGHT(tmp, field); \ + else \ + return (tmp); \ + } \ + return (res); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_NEXT(struct type *elm) \ +{ \ + if (RB_RIGHT(elm, field)) { \ + elm = RB_RIGHT(elm, field); \ + while (RB_LEFT(elm, field)) \ + elm = RB_LEFT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +/* ARGSUSED */ \ +attr struct type * \ +name##_RB_PREV(struct type *elm) \ +{ \ + if (RB_LEFT(elm, field)) { \ + elm = RB_LEFT(elm, field); \ + while (RB_RIGHT(elm, field)) \ + elm = RB_RIGHT(elm, field); \ + } else { \ + if (RB_PARENT(elm, field) && \ + (elm == RB_RIGHT(RB_PARENT(elm, field), field))) \ + elm = RB_PARENT(elm, field); \ + else { \ + while (RB_PARENT(elm, field) && \ + (elm == RB_LEFT(RB_PARENT(elm, field), field)))\ + elm = RB_PARENT(elm, field); \ + elm = RB_PARENT(elm, field); \ + } \ + } \ + return (elm); \ +} \ + \ +attr struct type * \ +name##_RB_MINMAX(struct name *head, int val) \ +{ \ + struct type *tmp = RB_ROOT(head); \ + struct type *parent = NULL; \ + while (tmp) { \ + parent = tmp; \ + if (val < 0) \ + tmp = RB_LEFT(tmp, field); \ + else \ + tmp = RB_RIGHT(tmp, field); \ + } \ + return (parent); \ +} + +#define RB_NEGINF -1 +#define RB_INF 1 + +#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y) +#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y) +#define RB_FIND(name, x, y) name##_RB_FIND(x, y) +#define RB_NFIND(name, x, y) name##_RB_NFIND(x, y) +#define RB_NEXT(name, x, y) name##_RB_NEXT(y) +#define RB_PREV(name, x, y) name##_RB_PREV(y) +#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF) +#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF) + +#define RB_FOREACH(x, name, head) \ + for ((x) = RB_MIN(name, head); \ + (x) != NULL; \ + (x) = name##_RB_NEXT(x)) + +#define RB_FOREACH_SAFE(x, name, head, y) \ + for ((x) = RB_MIN(name, head); \ + ((x) != NULL) && ((y) = name##_RB_NEXT(x), 1); \ + (x) = (y)) + +#define RB_FOREACH_REVERSE(x, name, head) \ + for ((x) = RB_MAX(name, head); \ + (x) != NULL; \ + (x) = name##_RB_PREV(x)) + +#define RB_FOREACH_REVERSE_SAFE(x, name, head, y) \ + for ((x) = RB_MAX(name, head); \ + ((x) != NULL) && ((y) = name##_RB_PREV(x), 1); \ + (x) = (y)) + +#endif /* _SYS_TREE_H_ */ Index: contrib/mg/tty.c =================================================================== --- /dev/null +++ contrib/mg/tty.c @@ -0,0 +1,459 @@ +/* $OpenBSD: tty.c,v 1.39 2021/03/20 09:00:49 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Terminfo display driver + * + * Terminfo is a terminal information database and routines to describe + * terminals on most modern UNIX systems. Many other systems have adopted + * this as a reasonable way to allow for widely varying and ever changing + * varieties of terminal types. This should be used where practical. + */ +/* + * Known problems: If you have a terminal with no clear to end of screen and + * memory of lines below the ones visible on the screen, display will be + * wrong in some cases. I doubt that any such terminal was ever made, but I + * thought everyone with delete line would have clear to end of screen too... + * + * Code for terminals without clear to end of screen and/or clear to end of line + * has not been extensively tested. + * + * Cost calculations are very rough. Costs of insert/delete line may be far + * from the truth. This is accentuated by display.c not knowing about + * multi-line insert/delete. + * + * Using scrolling region vs insert/delete line should probably be based on cost + * rather than the assumption that scrolling region operations look better. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +static int charcost(const char *); + +static int cci; +static int insdel; /* Do we have both insert & delete line? */ +static char *scroll_fwd; /* How to scroll forward. */ + +static void winchhandler(int); + +volatile sig_atomic_t winch_flag; +int tceeol; +int tcinsl; +int tcdell; + +/* ARGSUSED */ +static void +winchhandler(int sig) +{ + winch_flag = 1; +} + +/* + * Initialize the terminal when the editor + * gets started up. + */ +void +ttinit(void) +{ + char *tty; + int errret; + + if (batch == 1) + tty = "pty"; + else + tty = NULL; + + if (setupterm(tty, STDOUT_FILENO, &errret)) + panic("Terminal setup failed"); + + signal(SIGWINCH, winchhandler); + signal(SIGCONT, winchhandler); + siginterrupt(SIGWINCH, 1); + + scroll_fwd = scroll_forward; + if (scroll_fwd == NULL || *scroll_fwd == '\0') { + /* this is what GNU Emacs does */ + scroll_fwd = parm_down_cursor; + if (scroll_fwd == NULL || *scroll_fwd == '\0') + scroll_fwd = curbp->b_nlchr; + } + + if (cursor_address == NULL || cursor_up == NULL) + panic("This terminal is too stupid to run mg"); + + /* set nrow & ncol */ + ttresize(); + + if (!clr_eol) + tceeol = ncol; + else + tceeol = charcost(clr_eol); + + /* Estimate cost of inserting a line */ + if (change_scroll_region && scroll_reverse) + tcinsl = charcost(change_scroll_region) * 2 + + charcost(scroll_reverse); + else if (parm_insert_line) + tcinsl = charcost(parm_insert_line); + else if (insert_line) + tcinsl = charcost(insert_line); + else + /* make this cost high enough */ + tcinsl = nrow * ncol; + + /* Estimate cost of deleting a line */ + if (change_scroll_region) + tcdell = charcost(change_scroll_region) * 2 + + charcost(scroll_fwd); + else if (parm_delete_line) + tcdell = charcost(parm_delete_line); + else if (delete_line) + tcdell = charcost(delete_line); + else + /* make this cost high enough */ + tcdell = nrow * ncol; + + /* Flag to indicate that we can both insert and delete lines */ + insdel = (insert_line || parm_insert_line) && + (delete_line || parm_delete_line); + + if (enter_ca_mode) + /* enter application mode */ + putpad(enter_ca_mode, 1); + + ttresize(); +} + +/* + * Re-initialize the terminal when the editor is resumed. + * The keypad_xmit doesn't really belong here but... + */ +void +ttreinit(void) +{ + /* check if file was modified while we were gone */ + if (fchecktime(curbp) != TRUE) { + curbp->b_flag |= BFDIRTY; + } + + if (enter_ca_mode) + /* enter application mode */ + putpad(enter_ca_mode, 1); + + if (keypad_xmit) + /* turn on keypad */ + putpad(keypad_xmit, 1); + + ttresize(); +} + +/* + * Clean up the terminal, in anticipation of a return to the command + * interpreter. This is a no-op on the ANSI display. On the SCALD display, + * it sets the window back to half screen scrolling. Perhaps it should + * query the display for the increment, and put it back to what it was. + */ +void +tttidy(void) +{ + ttykeymaptidy(); + + /* set the term back to normal mode */ + if (exit_ca_mode) + putpad(exit_ca_mode, 1); +} + +/* + * Move the cursor to the specified origin 0 row and column position. Try to + * optimize out extra moves; redisplay may have left the cursor in the right + * location last time! + */ +void +ttmove(int row, int col) +{ + if (ttrow != row || ttcol != col) { + putpad(tgoto(cursor_address, col, row), 1); + ttrow = row; + ttcol = col; + } +} + +/* + * Erase to end of line. + */ +void +tteeol(void) +{ + int i; + + if (clr_eol) + putpad(clr_eol, 1); + else { + i = ncol - ttcol; + while (i--) + ttputc(' '); + ttrow = ttcol = HUGE; + } +} + +/* + * Erase to end of page. + */ +void +tteeop(void) +{ + int line; + + if (clr_eos) + putpad(clr_eos, nrow - ttrow); + else { + putpad(clr_eol, 1); + if (insdel) + ttdell(ttrow + 1, lines, lines - ttrow - 1); + else { + /* do it by hand */ + for (line = ttrow + 1; line <= lines; ++line) { + ttmove(line, 0); + tteeol(); + } + } + ttrow = ttcol = HUGE; + } +} + +/* + * Make a noise. + */ +void +ttbeep(void) +{ + putpad(bell, 1); + ttflush(); +} + +/* + * Insert nchunk blank line(s) onto the screen, scrolling the last line on + * the screen off the bottom. Use the scrolling region if possible for a + * smoother display. If there is no scrolling region, use a set of insert + * and delete line sequences. + */ +void +ttinsl(int row, int bot, int nchunk) +{ + int i, nl; + + /* One line special cases */ + if (row == bot) { + ttmove(row, 0); + tteeol(); + return; + } + /* Use scroll region and back index */ + if (change_scroll_region && scroll_reverse) { + nl = bot - row; + ttwindow(row, bot); + ttmove(row, 0); + while (nchunk--) + putpad(scroll_reverse, nl); + ttnowindow(); + return; + /* else use insert/delete line */ + } else if (insdel) { + ttmove(1 + bot - nchunk, 0); + nl = nrow - ttrow; + if (parm_delete_line) + putpad(tgoto(parm_delete_line, 0, nchunk), nl); + else + /* For all lines in the chunk */ + for (i = 0; i < nchunk; i++) + putpad(delete_line, nl); + ttmove(row, 0); + + /* ttmove() changes ttrow */ + nl = nrow - ttrow; + + if (parm_insert_line) + putpad(tgoto(parm_insert_line, 0, nchunk), nl); + else + /* For all lines in the chunk */ + for (i = 0; i < nchunk; i++) + putpad(insert_line, nl); + ttrow = HUGE; + ttcol = HUGE; + } else + panic("ttinsl: Can't insert/delete line"); +} + +/* + * Delete nchunk line(s) from "row", replacing the bottom line on the + * screen with a blank line. Unless we're using the scrolling region, + * this is done with crafty sequences of insert and delete lines. The + * presence of the echo area makes a boundary condition go away. + */ +void +ttdell(int row, int bot, int nchunk) +{ + int i, nl; + + /* One line special cases */ + if (row == bot) { + ttmove(row, 0); + tteeol(); + return; + } + /* scrolling region */ + if (change_scroll_region) { + nl = bot - row; + ttwindow(row, bot); + ttmove(bot, 0); + while (nchunk--) + putpad(scroll_fwd, nl); + ttnowindow(); + /* else use insert/delete line */ + } else if (insdel) { + ttmove(row, 0); + nl = nrow - ttrow; + if (parm_delete_line) + putpad(tgoto(parm_delete_line, 0, nchunk), nl); + else + /* For all lines in the chunk */ + for (i = 0; i < nchunk; i++) + putpad(delete_line, nl); + ttmove(1 + bot - nchunk, 0); + + /* ttmove() changes ttrow */ + nl = nrow - ttrow; + + if (parm_insert_line) + putpad(tgoto(parm_insert_line, 0, nchunk), nl); + else + /* For all lines in the chunk */ + for (i = 0; i < nchunk; i++) + putpad(insert_line, nl); + ttrow = HUGE; + ttcol = HUGE; + } else + panic("ttdell: Can't insert/delete line"); +} + +/* + * This routine sets the scrolling window on the display to go from line + * "top" to line "bot" (origin 0, inclusive). The caller checks for the + * pathological 1-line scroll window which doesn't work right and avoids + * it. The "ttrow" and "ttcol" variables are set to a crazy value to + * ensure that the next call to "ttmove" does not turn into a no-op (the + * window adjustment moves the cursor). + */ +void +ttwindow(int top, int bot) +{ + if (change_scroll_region && (tttop != top || ttbot != bot)) { + putpad(tgoto(change_scroll_region, bot, top), nrow - ttrow); + ttrow = HUGE; /* Unknown. */ + ttcol = HUGE; + tttop = top; /* Remember region. */ + ttbot = bot; + } +} + +/* + * Switch to full screen scroll. This is used by "spawn.c" just before it + * suspends the editor and by "display.c" when it is getting ready to + * exit. This function does a full screen scroll by telling the terminal + * to set a scrolling region that is lines or nrow rows high, whichever is + * larger. This behavior seems to work right on systems where you can set + * your terminal size. + */ +void +ttnowindow(void) +{ + if (change_scroll_region) { + putpad(tgoto(change_scroll_region, + (nrow > lines ? nrow : lines) - 1, 0), nrow - ttrow); + ttrow = HUGE; /* Unknown. */ + ttcol = HUGE; + tttop = HUGE; /* No scroll region. */ + ttbot = HUGE; + } +} + +/* + * Set the current writing color to the specified color. Watch for color + * changes that are not going to do anything (the color is already right) + * and don't send anything to the display. The rainbow version does this + * in putline.s on a line by line basis, so don't bother sending out the + * color shift. + */ +void +ttcolor(int color) +{ + if (color != tthue) { + if (color == CTEXT) + /* normal video */ + putpad(exit_standout_mode, 1); + else if (color == CMODE) + /* reverse video */ + putpad(enter_standout_mode, 1); + /* save the color */ + tthue = color; + } +} + +/* + * This routine is called by the "refresh the screen" command to try + * to resize the display. Look in "window.c" to see how + * the caller deals with a change. + * + * We use `newrow' and `newcol' so vtresize() know the difference between the + * new and old settings. + */ +void +ttresize(void) +{ + int newrow = 0, newcol = 0; + + struct winsize winsize; + + if (ioctl(0, TIOCGWINSZ, &winsize) == 0) { + newrow = winsize.ws_row; + newcol = winsize.ws_col; + } + if ((newrow <= 0 || newcol <= 0) && + ((newrow = lines) <= 0 || (newcol = columns) <= 0)) { + newrow = 24; + newcol = 80; + } + if (vtresize(1, newrow, newcol) != TRUE) + panic("vtresize failed"); +} + +/* + * fake char output for charcost() + */ +/* ARGSUSED */ +static int +fakec(int c) +{ + cci++; + return (0); +} + +/* calculate the cost of doing string s */ +static int +charcost(const char *s) +{ + cci = 0; + + tputs(s, nrow, fakec); + return (cci); +} Index: contrib/mg/ttyio.c =================================================================== --- /dev/null +++ contrib/mg/ttyio.c @@ -0,0 +1,228 @@ +/* $OpenBSD: ttyio.c,v 1.40 2021/03/20 09:00:49 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * POSIX terminal I/O. + * + * The functions in this file negotiate with the operating system for + * keyboard characters, and write characters to the display in a barely + * buffered fashion. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "def.h" + +#define NOBUF 512 /* Output buffer size. */ + +int ttstarted; +char obuf[NOBUF]; /* Output buffer. */ +size_t nobuf; /* Buffer count. */ +struct termios oldtty; /* POSIX tty settings. */ +struct termios newtty; +int nrow; /* Terminal size, rows. */ +int ncol; /* Terminal size, columns. */ + +/* + * This function gets called once, to set up the terminal. + * On systems w/o TCSASOFT we turn off off flow control, + * which isn't really the right thing to do. + */ +void +ttopen(void) +{ + if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO)) + panic("standard input and output must be a terminal"); + + if (ttraw() == FALSE) + panic("aborting due to terminal initialize failure"); +} + +/* + * This function sets the terminal to RAW mode, as defined for the current + * shell. This is called both by ttopen() above and by spawncli() to + * get the current terminal settings and then change them to what + * mg expects. Thus, tty changes done while spawncli() is in effect + * will be reflected in mg. + */ +int +ttraw(void) +{ + if (tcgetattr(0, &oldtty) == -1) { + dobeep(); + ewprintf("ttopen can't get terminal attributes"); + return (FALSE); + } + (void)memcpy(&newtty, &oldtty, sizeof(newtty)); + /* Set terminal to 'raw' mode and ignore a 'break' */ + newtty.c_cc[VMIN] = 1; + newtty.c_cc[VTIME] = 0; + newtty.c_iflag |= IGNBRK; + newtty.c_iflag &= ~(BRKINT | PARMRK | INLCR | IGNCR | ICRNL | IXON); + newtty.c_oflag &= ~OPOST; + newtty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + + if (tcsetattr(0, TCSASOFT | TCSADRAIN, &newtty) == -1) { + dobeep(); + ewprintf("ttopen can't tcsetattr"); + return (FALSE); + } + ttstarted = 1; + + return (TRUE); +} + +/* + * This function gets called just before we go back home to the shell. + * Put all of the terminal parameters back. + * Under UN*X this just calls ttcooked(), but the ttclose() hook is in + * because vttidy() in display.c expects it for portability reasons. + */ +void +ttclose(void) +{ + if (ttstarted) { + if (ttcooked() == FALSE) + panic(""); /* ttcooked() already printf'd */ + ttstarted = 0; + } +} + +/* + * This function restores all terminal settings to their default values, + * in anticipation of exiting or suspending the editor. + */ +int +ttcooked(void) +{ + ttflush(); + if (tcsetattr(0, TCSASOFT | TCSADRAIN, &oldtty) == -1) { + dobeep(); + ewprintf("ttclose can't tcsetattr"); + return (FALSE); + } + return (TRUE); +} + +/* + * Write character to the display. Characters are buffered up, + * to make things a little bit more efficient. + */ +int +ttputc(int c) +{ + if (nobuf >= NOBUF) + ttflush(); + obuf[nobuf++] = c; + return (c); +} + +/* + * Flush output. + */ +void +ttflush(void) +{ + ssize_t written; + char *buf = obuf; + + if (nobuf == 0 || batch == 1) + return; + + while ((written = write(fileno(stdout), buf, nobuf)) != nobuf) { + if (written == -1) { + if (errno == EINTR) + continue; + panic("ttflush write failed"); + } + buf += written; + nobuf -= written; + } + nobuf = 0; +} + +/* + * Read character from terminal. All 8 bits are returned, so that you + * can use a multi-national terminal. + */ +int +ttgetc(void) +{ + char c; + ssize_t ret; + + do { + ret = read(STDIN_FILENO, &c, 1); + if (ret == -1 && errno == EINTR) { + if (winch_flag) { + redraw(0, 0); + winch_flag = 0; + } + } else if (ret == -1 && errno == EIO) + panic("lost stdin"); + else if (ret == 1) + break; + } while (1); + return ((int) c) & 0xFF; +} + +/* + * Returns TRUE if there are characters waiting to be read. + */ +int +charswaiting(void) +{ + int x; + + return ((ioctl(0, FIONREAD, &x) == -1) ? 0 : x); +} + +/* + * panic - just exit, as quickly as we can. + */ +void +panic(char *s) +{ + static int panicking = 0; + + if (panicking) + return; + else + panicking = 1; + ttclose(); + (void) fputs("panic: ", stderr); + (void) fputs(s, stderr); + (void) fputc('\n', stderr); /* Use '\n' as no buffers now. */ + exit(1); +} + +/* + * This function returns FALSE if any characters have showed up on the + * tty before 'msec' milliseconds. + */ +int +ttwait(int msec) +{ + struct pollfd pfd[1]; + + pfd[0].fd = 0; + pfd[0].events = POLLIN; + + if ((poll(pfd, 1, msec)) == 0) + return (TRUE); + return (FALSE); +} Index: contrib/mg/ttykbd.c =================================================================== --- /dev/null +++ contrib/mg/ttykbd.c @@ -0,0 +1,80 @@ +/* $OpenBSD: ttykbd.c,v 1.20 2021/02/23 08:10:51 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Name: MG 2a + * Terminfo keyboard driver using key files + * Created: 22-Nov-1987 Mic Kaczmarczik (mic@emx.cc.utexas.edu) + */ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" + +/* + * Get keyboard character. Very simple if you use keymaps and keys files. + */ + +char *keystrings[] = {NULL}; + +/* + * Turn on function keys using keypad_xmit, then load a keys file, if + * available. The keys file is located in the same manner as the startup + * file is, depending on what startupfile() does on your system. + */ +void +ttykeymapinit(void) +{ + char *cp; + + /* Bind keypad function keys. */ + if (key_left) + dobindkey(fundamental_map, "backward-char", key_left); + if (key_right) + dobindkey(fundamental_map, "forward-char", key_right); + if (key_up) + dobindkey(fundamental_map, "previous-line", key_up); + if (key_down) + dobindkey(fundamental_map, "next-line", key_down); + if (key_beg) + dobindkey(fundamental_map, "beginning-of-line", key_beg); + else if (key_home) + dobindkey(fundamental_map, "beginning-of-line", key_home); + if (key_end) + dobindkey(fundamental_map, "end-of-line", key_end); + if (key_npage) + dobindkey(fundamental_map, "scroll-up", key_npage); + if (key_ppage) + dobindkey(fundamental_map, "scroll-down", key_ppage); + if (key_ic) + dobindkey(fundamental_map, "overwrite-mode", key_ic); + if (key_dc) + dobindkey(fundamental_map, "delete-char", key_dc); + + if ((cp = getenv("TERM"))) { + if (((cp = startupfile(cp, NULL)) != NULL) && + (load(cp) != TRUE)) + ewprintf("Error reading key initialization file"); + } + if (keypad_xmit) + /* turn on keypad */ + putpad(keypad_xmit, 1); +} + +/* + * Clean up the keyboard -- called by tttidy() + */ +void +ttykeymaptidy(void) +{ + if (keypad_local) + /* turn off keypad */ + putpad(keypad_local, 1); +} + Index: contrib/mg/tutorial =================================================================== --- /dev/null +++ contrib/mg/tutorial @@ -0,0 +1,356 @@ +The mg Tutorial +--------------- + +The mg editor is a public domain editor intended to loosely resemble GNU Emacs, +while still retaining fast speed and a small memory footprint. + +Most mg commands involve using the Control (sometimes labelled "Ctrl") or the +Meta (sometimes labelled "Alt") key. We will use the following conventions in +this tutorial: + + C- means hold down the Control key while typing the character . + M- means hold down the Meta key while typing the character . + +If you don't have a Meta key, you can use Esc instead. Press and release the +Esc key and type . This is equivalent to M-. + +The first thing to learn is how to move up and down a document. To move your +cursor down a line, use the down-arrow cursor key or C-n (Control and n). + +>> Now type C-n multiple times and move your cursor past this line. + +Congratulations. You have now learned how to move your cursor down. To move +your cursor up one line, you can use the up-arrow cursor key or C-p (Control +and p). + +>> Try using C-p and C-n to move up and down and then move past this line. + +The next commands to learn are how to move your cursor left and right. To do +this, you can use the left-arrow and right-arrow cursor keys. Alternatively, +you can use C-b and C-f to do this. + +>> Practise using the arrow keys or C-b and C-f on this line. + +To make it easy to remember these commands, it helps to remember by letter: +P - Previous line, N - Next line, B - Backwards and F - Forward. + +Now that you've learned how to move single characters at a time, next we learn +how to move one word at a time. To do this, you can use M-f (Meta and f) or +M-b (Meta and b) to move forwards and backwards, one word at a time. + +>> Try moving one word at a time by using M-f and M-b on this line. + +Notice how the Ctrl and Meta key combinations perform related functions. C-f +moves one letter forward, whereas M-f moves one word forward. There are many +key combinations in mg, where C- will perform one function and M- +will perform a similar related function. + +Most probably by now you have moved passed the bottom of the text which was +initially shown to you when you opened this document. mg redraws your terminal +screen so that the cursor is in the middle. This is a feature of mg, which +allows you to see the lines before and after the current cursor position. The +same effect can be achieved manually. + +>> Now move the cursor down to this line and then type C-l (that's Control and + lowercase L). + +Note that C-l refreshes the screen and centers it on the line you typed it on. + +To move to the beginning or end of a line, you can use the Home and End keys, +or you can use C-a and C-e to move to the beginning and end. + +>> Use C-a and C-e to move to the beginning and end of this line. + +The next commands we will learn is how to move up and down, one page at a time. +To do this, you can use the Page Up (sometimes labelled PgUp) and Page Down +(sometimes labelled PgDn) keys. You can also use C-v and M-v to do this. C-v +moves the cursor down one page and M-v moves it up one page. + +>> Try using M-v and C-v to move up and down, one page at a time. + +The final two motion commands we will learn are M-< (Meta-Less than) and +M-> (Meta-Greater than) which move you to the beginning and end of a file, +respectively. You may not want to try that now as you will probably lose your +place in this tutorial. Note that on most terminals, < is above the , key, so +you'll need to press the Shift key to type <. + +Movement Summary +----------------- + +The following is a summary of the movement commands we've learned so far: + + C-f Move forward one character (can also use right arrow key). + C-b Move backward one character (can also use left arrow key). + C-p Move up one line (can also use up arrow key). + C-n Move down one line (can also use down arrow key). + M-f Move forward one word. + M-b Move backward one word. + C-a Move to beginning of line (can also use Home key). + C-e Move to end of line (can also use End key). + C-v Move forward one page (can also use PgDn/Page Down key). + M-v Move backward one page (can also use PgUp/Page Up key). + M-< Move to beginning of file. + M-> Move to end of file. + +Now that you've mastered the basics of moving around in mg, you can cause mg +to execute these commands multiple times. The way to do this is to type C-u +followed by some digits followed by a movement command. + +>> Type C-u 5 C-f to move forward 5 characters. + +In general, C-u allows you to execute any command multiple times, not just +cursor motion commands. The only exception to this rule are C-v and M-v. +When using these two commands with an argument, they move the cursor by that +many lines instead of pages. + +Cancelling mg Commands +---------------------- + +If you have started typing out a command that you didn't mean to finish, you +can use the C-g command to cancel the command immediately. + +>> For example, type C-u 50 and then type C-g to cancel the C-u command. +>> Type Esc and then C-g to cancel the Esc key. + +In general, you can use C-g to stop any mg commands. You may type it multiple +times if you wish. You should see the word "Quit" appear in the bottom of the +screen when you type C-g indicating that a command was cancelled. + +In general, when in doubt, use C-g to get out of trouble. + +Inserting/Deleting Text +----------------------- + +To insert text anywhere, simply move your cursor to the appropriate position +and begin typing. To delete characters, use the backspace key. If you use +M- (Meta and backspace key), you will delete one word instead +of one character at a time. + +To delete characters to the right of the cursor, you can use C-d to delete +characters to the right of the current position. If you use M-d instead of +C-d, you can delete one word at a time instead of one character at a time. + +>> Try inserting and deleting characters and words on this line. + +Note that if you type too many characters on a single line, the line will +scroll off the screen and you will see a $ on the line to indicate that the +line is too long to fit on the screen at one time. + +To delete a line at a time, you can use C-k to kill the line from the current +cursor position to the end of the line. You can type C-k multiple times to +kill many lines. + +You can issue insert or delete commands multiple times using C-u. For example, +C-u 10 e will type out eeeeeeeeee, C-u 4 M-d will delete four words to the +right of the cursor and so on. + +To undo any operation, you can use C-_ (that's control-underscore). + +Now if you kill something that you didn't mean to, you can yank it back from +the dead by using C-y. In general, when you kill something bigger than a single +character, mg saves it in a buffer somewhere and you can restore it by using +C-y. This is useful for moving text around. You can kill text in one place, +move your cursor to the new location and then use C-y to paste it there. + +Search for Text +--------------- + +To search for text, type C-s followed by the text you wish to search for. Note +that as you start typing the characters, mg automatically searches as you type +the characters. + +To continue searching the text you're looking for, type C-s to find the next +instance. To search in reverse, type C-r instead of C-s. If you type C-s or +C-r twice, it will simply search for the last text that you searched for. + +To stop searching for text, simply use the cursor keys (or C-f, C-b etc.) or +C-g to stop the search operation. + +>> Use C-s foo to search for "foo" in the text. You can use C-s again to + find other instances of foo in the file. + +Note that if a word cannot be found, it will say Failing I-search: at the +bottom of the screen. Typing C-s again will wrap the search around from the +top of the file and begin searching from there. + +Replace Text +------------ + +To replace text, use M-%. You will be prompted for the text to search for and +the text to replace it with. You will then be taken to the first instance of +text from the current position. At this point you can do one of the following: + + y - Replace the text at this instance and search for more items. + n - Skip this instance and search for more items. + . or Enter - Stop replacing text (you can also use C-g). + ! - Replace all the instances without prompting at each one. + +>> Try replacing "frobnitz" with "zutwalt" on this line. + +Cut/Copy/Paste Text +------------------- + +As explained above, you can cut regions using C-k to kill multiple lines. To +paste the text that you just cut, simply move your cursor to the point and +then type C-y to restore the text. You may type C-y multiple times to restore +the text. Hence, to copy text, you can use C-k to kill all the lines, use C-y +to restore it immediately, then move to the region you want to copy it to and +then type C-y again to restore the last cut text block again. + +Another way to cut or copy chunks of text is to first position your cursor at +the starting point of the chunk of text. Then type C- to mark this as +the starting point to cut or copy. Then move the cursor to the end point of the +text chunk that you wish to manipulate. Then type C-w to cut the region, or +M-w to copy the region. If you wish to cancel marking a block of text, simply +type C-g to cancel the operation. + +To paste the region that you've cut or copied above, simply move your cursor +to the desired location and then type C-y to paste it. + +Status Line +----------- + +At the bottom of your screen is a reverse highlighted line. This is the status +line and lets you know some useful information about the file you're editing. + +On the status line, you should see "Mg: tutorial". This lets you know that +you're editing a file named "tutorial". If you've edited this file and not +saved it, it should have a "**" to the left of those words. If this file is +read-only, you should see a "%%" to the left of those words. + +To the right of the status line, you should see L followed by digits +and, if column-number-mode is enabled, C followed by some more digits. +Type M-x column-number-mode Enter to enable it if it is disabled (the +default). These indicate the line number and column number of the file +that your cursor is currently on. If you move the cursor around, you +should see the line and column number change. + +In the middle of the screen, you should see the word "(fundamental)" which +indicates that the current editing mode is "fundamental-mode". The mg editor +also supports a c-mode that is more suited to editing C code. There are also +some other useful editing modes for different situations. See the man page +for mg(1) to learn about the various editing modes. + +Opening and Saving Files +------------------------ + +To open a file, you can use C-x C-f. You will then be prompted for a file name. +If you type a file name that doesn't already exist, a new file will be opened +for you. If the file name already exists, then it will be opened for you and +you can begin editing it. Note that you do not need to type the whole file +name for an existing file. You can type part of the file name and then press +the TAB key. If there is only file name that matches, mg will fill in the rest +of the file name for you. If there are multiple files, mg will display that +the choice is ambiguous. If you type the TAB key again, mg will show you all +the available choices for file names. + +NOTE: If you type C-x f instead of C-x C-f, you can use C-g to cancel the +Set-Fill-Column command. You can also use C-g to cancel the C-x C-f command +if you don't wish to open a new file. + +To save the file once you've edited it, use C-x C-s to save the file. When +mg is done saving the file, you should see the words "Wrote /path/to/file" +in the bottom of your screen. In general, it is a good idea to save quite +often. When you save a file, mg saves a backup of the file with a tilde (~) +character at the end. + +If you decide to open a directory instead of a file, mg will transistion into a +mode called dired. Dired fills a buffer with the contents of the selected +directory, one file or sub-directory's details per line. Some basic file +management functions can be performed on the files and sub-directories in the +buffer. For example, with the cursor over a specific file: + + Pressing c will give you the opportunity to copy the file. + Pressing d will mark the file for deletion. + Pressing x will unlink files previously marked for deletion. + Pressing Return will open the highlighted file into it's own buffer for + editing. + +There are more dired commands, see the man page for further information. + +Working with Buffers and Windows +-------------------------------- + +Once a file is loaded into mg, it is often referred to as a buffer. + +The mg editor is capable of editing multiple buffers at the same time. When you +open a second file with C-x C-f, the first buffer is still being edited by mg. + +Both buffers can be viewed simultaneously because mg can support several windows +at the same time, each one displaying different text. To split a screen into two +horizontal windows use C-x 2. To return to one window, use C-x 1 to close the +other windows and only keep the current window. + +>> Use C-x 2 to split the screen into two windows. + +>> Use C-x o to move from one window to the other. You can scroll up and down + in each window using the cursor keys or C-n and C-p keys. + +>> Use C-x 1 to restore back to one window. + +You can list all the buffers that are opened by mg by typing C-x C-b. The +screen should divide into two and the top window will list the buffers that +are currently open. Use C-x o to switch to the top window, then use the arrow +keys to move to the buffer you wish to switch to, and then type the Enter key to +select that buffer. Then use C-x 1 to switch back to only one window. + +You may also move back to the last opened buffer by using C-x b to toggle back +and forth between two buffers. Note the difference between C-x b and C-x C-b. + +>> Use C-x C-f to open a new file +>> Use C-x b to switch back and forth between that buffer and this one. + +To kill any buffer, use C-x k. You will be prompted for the buffer to kill. +By default, the current buffer is selected as the one to kill. You may also +type another buffer name or use C-g to cancel the operation. + +Extended Commands +----------------- + +The mg editor has several extended commands, more than what can be covered +by the Control and Meta keys. The mg editor gets around this by using what is +called the X (eXtend) command. There are two forms of this: + + C-x Character eXtension. Followed by one character. + M-x Named character eXtension. Followed by a long command. + +You've already seen C-x C-f and C-x C-s to open and save a file. There are +other longer commands. For instance, you can also open a file by typing +M-x find-file Enter. When you type a command using M-x, mg prompts you for +the command at the bottom of the screen. You can type out the whole command +if you wish, or you can type out part of the command and then use the TAB key +for autocompleting the command. + +For instance, to replace text, you can type M-x repl TAB enter to execute +the replace-text command. To cancel this command, type C-g. + +To see a list of all available mg(1) commands, consult the man page. + +Exiting mg +---------- + +To exit mg temporarily and return to the shell, use C-z. This will take you +back to the command shell. To return back to mg, type fg in the shell and you +will be returned to your mg session. + +To exit mg permanently, type C-x C-c. If you have any unsaved buffers, you +will be asked if you wish to save them or not. + +Conclusion +---------- + +This tutorial is meant to get new users up and running with mg. There is more +information available via the mg(1) man page. If you have any suggestions for +improvement, please don't hesitate to drop a message or (better still) submit +a diff to tech@openbsd.org. + +Author Info +----------- + +Original Author of this document: Mayukh Bose, +Date last updated: 2018-05-27 + +Copyright +--------- + +None. This document is in the public domain. Index: contrib/mg/undo.c =================================================================== --- /dev/null +++ contrib/mg/undo.c @@ -0,0 +1,602 @@ +/* $OpenBSD: undo.c,v 1.58 2016/09/05 08:10:58 lum Exp $ */ +/* + * This file is in the public domain + */ + +#include +#include +#include +#include +#include + +#include "def.h" +#include "kbd.h" + +#define MAX_FREE_RECORDS 32 + +/* + * Local variables + */ +static struct undoq undo_free; +static int undo_free_num; +static int boundary_flag = TRUE; +static int undo_enable_flag = TRUE; + +/* + * Local functions + */ +static int find_dot(struct line *, int); +static int find_lo(int, struct line **, int *, int *); +static struct undo_rec *new_undo_record(void); +static int drop_oldest_undo_record(void); + +/* + * find_dot, find_lo() + * + * Find an absolute dot in the buffer from a line/offset pair, and vice-versa. + * + * Since lines can be deleted while they are referenced by undo record, we + * need to have an absolute dot to have something reliable. + */ +static int +find_dot(struct line *lp, int off) +{ + int count = 0; + struct line *p; + + for (p = curbp->b_headp; p != lp; p = lforw(p)) { + if (count != 0) { + if (p == curbp->b_headp) { + dobeep(); + ewprintf("Error: Undo stuff called with a" + "nonexistent line"); + return (FALSE); + } + } + count += llength(p) + 1; + } + count += off; + + return (count); +} + +static int +find_lo(int pos, struct line **olp, int *offset, int *lnum) +{ + struct line *p; + int lineno; + + p = curbp->b_headp; + lineno = 0; + while (pos > llength(p)) { + pos -= llength(p) + 1; + if ((p = lforw(p)) == curbp->b_headp) { + *olp = NULL; + *offset = 0; + return (FALSE); + } + lineno++; + } + *olp = p; + *offset = pos; + *lnum = lineno; + + return (TRUE); +} + +static struct undo_rec * +new_undo_record(void) +{ + struct undo_rec *rec; + + rec = TAILQ_FIRST(&undo_free); + if (rec != NULL) { + /* Remove it from the free-list */ + TAILQ_REMOVE(&undo_free, rec, next); + undo_free_num--; + } else { + if ((rec = malloc(sizeof(*rec))) == NULL) + panic("Out of memory in undo code (record)"); + } + memset(rec, 0, sizeof(struct undo_rec)); + + return (rec); +} + +void +free_undo_record(struct undo_rec *rec) +{ + static int initialised = 0; + + /* + * On the first run, do initialisation of the free list. + */ + if (initialised == 0) { + TAILQ_INIT(&undo_free); + initialised = 1; + } + free(rec->content); + rec->content = NULL; + if (undo_free_num >= MAX_FREE_RECORDS) { + free(rec); + return; + } + undo_free_num++; + + TAILQ_INSERT_HEAD(&undo_free, rec, next); +} + +/* + * Drop the oldest undo record in our list. Return 1 if we could remove it, + * 0 if the undo list was empty. + */ +static int +drop_oldest_undo_record(void) +{ + struct undo_rec *rec; + + rec = TAILQ_LAST(&curbp->b_undo, undoq); + if (rec != NULL) { + undo_free_num--; + TAILQ_REMOVE(&curbp->b_undo, rec, next); + free_undo_record(rec); + return (1); + } + return (0); +} + +static int +lastrectype(void) +{ + struct undo_rec *rec; + + if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) + return (rec->type); + return (0); +} + +/* + * Returns TRUE if undo is enabled, FALSE otherwise. + */ +int +undo_enabled(void) +{ + return (undo_enable_flag); +} + +/* + * undo_enable: toggle undo_enable. + * Returns the previous value of the flag. + */ +int +undo_enable(int f, int n) +{ + int pon = undo_enable_flag; + + if (f & (FFARG | FFRAND)) + undo_enable_flag = n > 0; + else + undo_enable_flag = !undo_enable_flag; + + if (!(f & FFRAND)) + ewprintf("Undo %sabled", undo_enable_flag ? "en" : "dis"); + + return (pon); +} + +/* + * If undo is enabled, then: + * Toggle undo boundary recording. + * If called with an argument, (n > 0) => enable. Otherwise disable. + * In either case, add an undo boundary + * If undo is disabled, this function has no effect. + */ +int +undo_boundary_enable(int f, int n) +{ + int bon = boundary_flag; + + if (!undo_enable_flag) + return (FALSE); + + undo_add_boundary(FFRAND, 1); + + if (f & (FFARG | FFRAND)) + boundary_flag = n > 0; + else + boundary_flag = !boundary_flag; + + if (!(f & FFRAND)) + ewprintf("Undo boundaries %sabled", + boundary_flag ? "en" : "dis"); + + return (bon); +} + +/* + * Record an undo boundary, unless boundary_flag == FALSE. + * Does nothing if previous undo entry is already a boundary or 'modified' flag. + */ +int +undo_add_boundary(int f, int n) +{ + struct undo_rec *rec; + int last; + + if (boundary_flag == FALSE) + return (FALSE); + + last = lastrectype(); + if (last == BOUNDARY || last == MODIFIED) + return (TRUE); + + rec = new_undo_record(); + rec->type = BOUNDARY; + + TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); + + return (TRUE); +} + +/* + * Record an undo "modified" boundary + */ +void +undo_add_modified(void) +{ + struct undo_rec *rec, *trec; + + TAILQ_FOREACH_SAFE(rec, &curbp->b_undo, next, trec) + if (rec->type == MODIFIED) { + TAILQ_REMOVE(&curbp->b_undo, rec, next); + free_undo_record(rec); + } + + rec = new_undo_record(); + rec->type = MODIFIED; + + TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); + + return; +} + +int +undo_add_insert(struct line *lp, int offset, int size) +{ + struct region reg; + struct undo_rec *rec; + int pos; + + if (!undo_enable_flag) + return (TRUE); + + memset(®, 0, sizeof(reg)); + reg.r_linep = lp; + reg.r_offset = offset; + reg.r_size = size; + + pos = find_dot(lp, offset); + + /* + * We try to reuse the last undo record to `compress' things. + */ + rec = TAILQ_FIRST(&curbp->b_undo); + if (rec != NULL && rec->type == INSERT) { + if (rec->pos + rec->region.r_size == pos) { + rec->region.r_size += reg.r_size; + return (TRUE); + } + } + + /* + * We couldn't reuse the last undo record, so prepare a new one. + */ + rec = new_undo_record(); + rec->pos = pos; + rec->type = INSERT; + memmove(&rec->region, ®, sizeof(struct region)); + rec->content = NULL; + + undo_add_boundary(FFRAND, 1); + + TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); + + return (TRUE); +} + +/* + * This of course must be done _before_ the actual deletion is done. + */ +int +undo_add_delete(struct line *lp, int offset, int size, int isreg) +{ + struct region reg; + struct undo_rec *rec; + int pos; + + if (!undo_enable_flag) + return (TRUE); + + memset(®, 0, sizeof(reg)); + reg.r_linep = lp; + reg.r_offset = offset; + reg.r_size = size; + + pos = find_dot(lp, offset); + + if (offset == llength(lp)) /* if it's a newline... */ + undo_add_boundary(FFRAND, 1); + else if ((rec = TAILQ_FIRST(&curbp->b_undo)) != NULL) { + /* + * Separate this command from the previous one if we're not + * just before the previous record... + */ + if (!isreg && rec->type == DELETE) { + if (rec->pos - rec->region.r_size != pos) + undo_add_boundary(FFRAND, 1); + } + } + rec = new_undo_record(); + rec->pos = pos; + if (isreg) + rec->type = DELREG; + else + rec->type = DELETE; + memmove(&rec->region, ®, sizeof(struct region)); + do { + rec->content = malloc(reg.r_size + 1); + } while ((rec->content == NULL) && drop_oldest_undo_record()); + + if (rec->content == NULL) + panic("Out of memory"); + + region_get_data(®, rec->content, reg.r_size); + + if (isreg || lastrectype() != DELETE) + undo_add_boundary(FFRAND, 1); + + TAILQ_INSERT_HEAD(&curbp->b_undo, rec, next); + + return (TRUE); +} + +/* + * This of course must be called before the change takes place. + */ +int +undo_add_change(struct line *lp, int offset, int size) +{ + if (!undo_enable_flag) + return (TRUE); + undo_add_boundary(FFRAND, 1); + boundary_flag = FALSE; + undo_add_delete(lp, offset, size, 0); + undo_add_insert(lp, offset, size); + boundary_flag = TRUE; + undo_add_boundary(FFRAND, 1); + + return (TRUE); +} + +/* + * Show the undo records for the current buffer in a new buffer. + */ +/* ARGSUSED */ +int +undo_dump(int f, int n) +{ + struct undo_rec *rec; + struct buffer *bp; + struct mgwin *wp; + char buf[4096], tmp[1024]; + int num; + + /* + * Prepare the buffer for insertion. + */ + if ((bp = bfind("*undo*", TRUE)) == NULL) + return (FALSE); + bp->b_flag |= BFREADONLY; + bclear(bp); + if ((wp = popbuf(bp, WNONE)) == NULL) + return (FALSE); + + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == bp) { + wp->w_dotp = bp->b_headp; + wp->w_doto = 0; + } + } + + num = 0; + TAILQ_FOREACH(rec, &curbp->b_undo, next) { + num++; + snprintf(buf, sizeof(buf), + "%d:\t %s at %d ", num, + (rec->type == DELETE) ? "DELETE": + (rec->type == DELREG) ? "DELREGION": + (rec->type == INSERT) ? "INSERT": + (rec->type == BOUNDARY) ? "----" : + (rec->type == MODIFIED) ? "MODIFIED": "UNKNOWN", + rec->pos); + + if (rec->content) { + (void)strlcat(buf, "\"", sizeof(buf)); + snprintf(tmp, sizeof(tmp), "%.*s", rec->region.r_size, + rec->content); + (void)strlcat(buf, tmp, sizeof(buf)); + (void)strlcat(buf, "\"", sizeof(buf)); + } + snprintf(tmp, sizeof(tmp), " [%d]", rec->region.r_size); + if (strlcat(buf, tmp, sizeof(buf)) >= sizeof(buf)) { + dobeep(); + ewprintf("Undo record too large. Aborted."); + return (FALSE); + } + addlinef(bp, "%s", buf); + } + for (wp = wheadp; wp != NULL; wp = wp->w_wndp) { + if (wp->w_bufp == bp) { + wp->w_dotline = num+1; + wp->w_rflag |= WFFULL; + } + } + return (TRUE); +} + +/* + * After the user did action1, then action2, then action3: + * + * [action3] <--- Undoptr + * [action2] + * [action1] + * ------ + * [undo] + * + * After undo: + * + * [undo of action3] + * [action2] <--- Undoptr + * [action1] + * ------ + * [undo] + * + * After another undo: + * + * + * [undo of action2] + * [undo of action3] + * [action1] <--- Undoptr + * ------ + * [undo] + * + * Note that the "undo of actionX" have no special meaning. Only when + * we undo a deletion, the insertion will be recorded just as if it + * was typed on the keyboard. Resulting in the inverse operation being + * saved in the list. + * + * If undoptr reaches the bottom of the list, or if we moved between + * two undo actions, we make it point back at the topmost record. This is + * how we handle redoing. + */ +/* ARGSUSED */ +int +undo(int f, int n) +{ + struct undo_rec *ptr, *nptr; + int done, rval; + struct line *lp; + int offset, save; + static int nulled = FALSE; + int lineno; + + if (n < 0) + return (FALSE); + + ptr = curbp->b_undoptr; + + /* first invocation, make ptr point back to the top of the list */ + if ((ptr == NULL && nulled == TRUE) || rptcount == 0) { + ptr = TAILQ_FIRST(&curbp->b_undo); + nulled = TRUE; + } + + rval = TRUE; + while (n--) { + /* if we have a spurious boundary, free it and move on.... */ + while (ptr && ptr->type == BOUNDARY) { + nptr = TAILQ_NEXT(ptr, next); + TAILQ_REMOVE(&curbp->b_undo, ptr, next); + free_undo_record(ptr); + ptr = nptr; + } + /* + * Ptr is NULL, but on the next run, it will point to the + * top again, redoing all stuff done in the buffer since + * its creation. + */ + if (ptr == NULL) { + dobeep(); + ewprintf("No further undo information"); + rval = FALSE; + nulled = TRUE; + break; + } + nulled = FALSE; + + /* + * Loop while we don't get a boundary specifying we've + * finished the current action... + */ + + undo_add_boundary(FFRAND, 1); + + save = boundary_flag; + boundary_flag = FALSE; + + done = 0; + do { + /* + * Move to where this has to apply + * + * Boundaries (and the modified flag) are put as + * position 0 (to save lookup time in find_dot) + * so we must not move there... + */ + if (ptr->type != BOUNDARY && ptr->type != MODIFIED) { + if (find_lo(ptr->pos, &lp, + &offset, &lineno) == FALSE) { + dobeep(); + ewprintf("Internal error in Undo!"); + rval = FALSE; + break; + } + curwp->w_dotp = lp; + curwp->w_doto = offset; + curwp->w_markline = curwp->w_dotline; + curwp->w_dotline = lineno; + } + + /* + * Do operation^-1 + */ + switch (ptr->type) { + case INSERT: + ldelete(ptr->region.r_size, KNONE); + break; + case DELETE: + lp = curwp->w_dotp; + offset = curwp->w_doto; + region_put_data(ptr->content, + ptr->region.r_size); + curwp->w_dotp = lp; + curwp->w_doto = offset; + break; + case DELREG: + region_put_data(ptr->content, + ptr->region.r_size); + break; + case BOUNDARY: + done = 1; + break; + case MODIFIED: + curbp->b_flag &= ~BFCHG; + break; + default: + break; + } + + /* And move to next record */ + ptr = TAILQ_NEXT(ptr, next); + } while (ptr != NULL && !done); + + boundary_flag = save; + undo_add_boundary(FFRAND, 1); + + ewprintf("Undo!"); + } + + curbp->b_undoptr = ptr; + + return (rval); +} Index: contrib/mg/util.h =================================================================== --- /dev/null +++ contrib/mg/util.h @@ -0,0 +1,121 @@ +/* $OpenBSD: util.h,v 1.34 2013/06/03 21:07:02 tedu Exp $ */ +/* $NetBSD: util.h,v 1.2 1996/05/16 07:00:22 thorpej Exp $ */ + +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * Portions Copyright (c) 1996, Jason Downs. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include +#include + +/* + * fparseln() specific operation flags. + */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + +/* + * opendev() specific operation flags. + */ +#define OPENDEV_PART 0x01 /* Try to open the raw partition. */ +#define OPENDEV_BLCK 0x04 /* Open block, not character device. */ + +/* + * uucplock(3) specific flags. + */ +#define UU_LOCK_INUSE (1) +#define UU_LOCK_OK (0) +#define UU_LOCK_OPEN_ERR (-1) +#define UU_LOCK_READ_ERR (-2) +#define UU_LOCK_CREAT_ERR (-3) +#define UU_LOCK_WRITE_ERR (-4) +#define UU_LOCK_LINK_ERR (-5) +#define UU_LOCK_TRY_ERR (-6) +#define UU_LOCK_OWNER_ERR (-7) + +/* + * fmt_scaled(3) specific flags. + */ +#define FMT_SCALED_STRSIZE 7 /* minus sign, 4 digits, suffix, null byte */ + +/* + * stub struct definitions. + */ +struct __sFILE; +struct login_cap; +struct passwd; +struct termios; +struct utmp; +struct winsize; + +char *fparseln(FILE *, size_t *, size_t *, const char[3], int); +void login(struct utmp *); +int login_tty(int); +int logout(const char *); +void logwtmp(const char *, const char *, const char *); +int opendev(const char *, int, int, char **); +int pidfile(const char *); +void pw_setdir(const char *); +char *pw_file(const char *); +int pw_lock(int); +int pw_mkdb(char *, int); +int pw_abort(void); +void pw_init(void); +void pw_edit(int, const char *); +void pw_prompt(void); +void pw_copy(int, int, const struct passwd *, const struct passwd *); +int pw_scan(char *, struct passwd *, int *); +void pw_error(const char *, int, int); +int openpty(int *, int *, char *, struct termios *, struct winsize *); +int opendisk(const char *, int, char *, size_t, int); +pid_t forkpty(int *, char *, struct termios *, struct winsize *); +int getmaxpartitions(void); +int getrawpartition(void); +void login_fbtab(const char *, uid_t, gid_t); +int login_check_expire(struct __sFILE *, struct passwd *, char *, int); +char *readlabelfs(char *, int); +const char *uu_lockerr(int); +int uu_lock(const char *); +int uu_lock_txfr(const char *, pid_t); +int uu_unlock(const char *); +int fmt_scaled(long long, char *); +int scan_scaled(char *, long long *); +int isduid(const char *, int); +int pkcs5_pbkdf2(const char *, size_t, const uint8_t *, size_t, + uint8_t *, size_t, unsigned int); +int bcrypt_pbkdf(const char *, size_t, const uint8_t *, size_t, + uint8_t *, size_t, unsigned int); + +#endif /* !_UTIL_H_ */ Index: contrib/mg/util.c =================================================================== --- /dev/null +++ contrib/mg/util.c @@ -0,0 +1,529 @@ +/* $OpenBSD: util.c,v 1.43 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Assorted commands. + * This file contains the command processors for a large assortment of + * unrelated commands. The only thing they have in common is that they + * are all command processors. + */ + +#include +#include +#include +#include + +#include "def.h" + +/* + * Display a bunch of useful information about the current location of dot. + * The character under the cursor (in octal), the current line, row, and + * column, and approximate position of the cursor in the file (as a + * percentage) is displayed. + * Also included at the moment are some values in parenthesis for debugging + * explicit newline inclusion into the buffer. + * The column position assumes an infinite + * position display; it does not truncate just because the screen does. + * This is normally bound to "C-x =". + */ +/* ARGSUSED */ +int +showcpos(int f, int n) +{ + struct line *clp; + char *msg; + long nchar, cchar; + int nline, row; + int cline, cbyte; /* Current line/char/byte */ + int ratio; + + /* collect the data */ + clp = bfirstlp(curbp); + msg = "Char:"; + cchar = 0; + cline = 0; + cbyte = 0; + nchar = 0; + nline = 0; + for (;;) { + /* count lines and display total as (raw) 'lines' and + compare with b_lines */ + ++nline; + if (clp == curwp->w_dotp) { + /* obtain (raw) dot line # and compare with w_dotline */ + cline = nline; + cchar = nchar + curwp->w_doto; + if (curwp->w_doto == llength(clp)) + /* fake a \n at end of line */ + cbyte = *curbp->b_nlchr; + else + cbyte = lgetc(clp, curwp->w_doto); + } + /* include # of chars in this line for point-thru-buff ratio */ + nchar += llength(clp); + clp = lforw(clp); + if (clp == curbp->b_headp) { + if (cbyte == *curbp->b_nlchr && + cline == curbp->b_lines) { + /* swap faked \n for EOB msg */ + cbyte = EOF; + msg = "(EOB)"; + } + break; + } + /* count the implied newline */ + nchar++; + } + /* determine row # within current window */ + row = curwp->w_toprow + 1; + clp = curwp->w_linep; + while (clp != curbp->b_headp && clp != curwp->w_dotp) { + ++row; + clp = lforw(clp); + } + ratio = nchar ? (100L * cchar) / nchar : 100; + ewprintf("%s %c (0%o) point=%ld(%d%%) line=%d row=%d col=%d" \ + " (blines=%d rlines=%d l_size=%d)", msg, + cbyte, cbyte, cchar, ratio, cline, row, getcolpos(curwp), + curbp->b_lines, nline, clp->l_size); + return (TRUE); +} + +int +getcolpos(struct mgwin *wp) +{ + int col, i, c; + char tmp[5]; + + /* determine column */ + col = 0; + + for (i = 0; i < wp->w_doto; ++i) { + c = lgetc(wp->w_dotp, i); + if (c == '\t' +#ifdef NOTAB + && !(wp->w_bufp->b_flag & BFNOTAB) +#endif /* NOTAB */ + ) { + col |= 0x07; + col++; + } else if (ISCTRL(c) != FALSE) + col += 2; + else if (isprint(c)) { + col++; + } else { + col += snprintf(tmp, sizeof(tmp), "\\%o", c); + } + + } + return (col); +} + +/* + * Twiddle the two characters in front of and under dot, then move forward + * one character. Treat new-line characters the same as any other. + * Normally bound to "C-t". This always works within a line, so "WFEDIT" + * is good enough. + */ +/* ARGSUSED */ +int +twiddle(int f, int n) +{ + struct line *dotp; + int doto, cr; + + if (n == 0) + return (TRUE); + + dotp = curwp->w_dotp; + doto = curwp->w_doto; + + /* Don't twiddle if the dot is on the first char of buffer */ + if (doto == 0 && lback(dotp) == curbp->b_headp) { + dobeep(); + ewprintf("Beginning of buffer"); + return(FALSE); + } + /* Don't twiddle if the dot is on the last char of buffer */ + if (doto == llength(dotp) && lforw(dotp) == curbp->b_headp) { + dobeep(); + return(FALSE); + } + undo_boundary_enable(FFRAND, 0); + if (doto == 0 && doto == llength(dotp)) { /* only '\n' on this line */ + (void)forwline(FFRAND, 1); + curwp->w_doto = 0; + } else { + if (doto == 0) { /* 1st twiddle is on 1st character of a line */ + cr = lgetc(dotp, doto); + (void)backdel(FFRAND, 1); + (void)forwchar(FFRAND, 1); + lnewline(); + linsert(1, cr); + (void)backdel(FFRAND, 1); + } else { /* twiddle is elsewhere in line */ + cr = lgetc(dotp, doto - 1); + (void)backdel(FFRAND, 1); + (void)forwchar(FFRAND, 1); + linsert(1, cr); + } + } + undo_boundary_enable(FFRAND, 1); + lchange(WFEDIT); + return (TRUE); +} + +/* + * Open up some blank space. The basic plan is to insert a bunch of + * newlines, and then back up over them. Everything is done by the + * subcommand processors. They even handle the looping. Normally this + * is bound to "C-o". + */ +/* ARGSUSED */ +int +openline(int f, int n) +{ + int i, s; + + if (n < 0) + return (FALSE); + if (n == 0) + return (TRUE); + + /* insert newlines */ + undo_boundary_enable(FFRAND, 0); + i = n; + do { + s = lnewline(); + } while (s == TRUE && --i); + + /* then go back up overtop of them all */ + if (s == TRUE) + s = backchar(f | FFRAND, n); + undo_boundary_enable(FFRAND, 1); + return (s); +} + +/* + * Insert a newline. + */ +/* ARGSUSED */ +int +enewline(int f, int n) +{ + int s; + + if (n < 0) + return (FALSE); + + while (n--) { + if ((s = lnewline()) != TRUE) + return (s); + } + return (TRUE); +} + +/* + * Delete blank lines around dot. What this command does depends if dot is + * sitting on a blank line. If dot is sitting on a blank line, this command + * deletes all the blank lines above and below the current line. If it is + * sitting on a non blank line then it deletes all of the blank lines after + * the line. Normally this command is bound to "C-x C-o". Any argument is + * ignored. + */ +/* ARGSUSED */ +int +deblank(int f, int n) +{ + struct line *lp1, *lp2; + RSIZE nld; + + lp1 = curwp->w_dotp; + while (llength(lp1) == 0 && (lp2 = lback(lp1)) != curbp->b_headp) + lp1 = lp2; + lp2 = lp1; + nld = (RSIZE)0; + while ((lp2 = lforw(lp2)) != curbp->b_headp && llength(lp2) == 0) + ++nld; + if (nld == 0) + return (TRUE); + curwp->w_dotp = lforw(lp1); + curwp->w_doto = 0; + return (ldelete((RSIZE)nld, KNONE)); +} + +/* + * Delete any whitespace around dot, then insert a space. + */ +int +justone(int f, int n) +{ + undo_boundary_enable(FFRAND, 0); + (void)delwhite(f, n); + linsert(1, ' '); + undo_boundary_enable(FFRAND, 1); + return (TRUE); +} + +/* + * Delete any whitespace around dot. + */ +/* ARGSUSED */ +int +delwhite(int f, int n) +{ + int col, s; + + col = curwp->w_doto; + + while (col < llength(curwp->w_dotp) && + (isspace(lgetc(curwp->w_dotp, col)))) + ++col; + do { + if (curwp->w_doto == 0) { + s = FALSE; + break; + } + if ((s = backchar(FFRAND, 1)) != TRUE) + break; + } while (isspace(lgetc(curwp->w_dotp, curwp->w_doto))); + + if (s == TRUE) + (void)forwchar(FFRAND, 1); + (void)ldelete((RSIZE)(col - curwp->w_doto), KNONE); + return (TRUE); +} + +/* + * Delete any leading whitespace on the current line + */ +int +delleadwhite(int f, int n) +{ + int soff, ls; + struct line *slp; + + /* Save current position */ + slp = curwp->w_dotp; + soff = curwp->w_doto; + + for (ls = 0; ls < llength(slp); ls++) + if (!isspace(lgetc(slp, ls))) + break; + gotobol(FFRAND, 1); + forwdel(FFRAND, ls); + soff -= ls; + if (soff < 0) + soff = 0; + forwchar(FFRAND, soff); + + return (TRUE); +} + +/* + * Delete any trailing whitespace on the current line + */ +int +deltrailwhite(int f, int n) +{ + int soff; + + /* Save current position */ + soff = curwp->w_doto; + + gotoeol(FFRAND, 1); + delwhite(FFRAND, 1); + + /* restore original position, if possible */ + if (soff < curwp->w_doto) + curwp->w_doto = soff; + + return (TRUE); +} + + + +/* + * Insert a newline, then enough tabs and spaces to duplicate the indentation + * of the previous line. Assumes tabs are every eight characters. Quite + * simple. Figure out the indentation of the current line. Insert a newline + * by calling the standard routine. Insert the indentation by inserting the + * right number of tabs and spaces. Return TRUE if all ok. Return FALSE if + * one of the subcommands failed. Normally bound to "C-m". + */ +/* ARGSUSED */ +int +lfindent(int f, int n) +{ + int c, i, nicol; + int s = TRUE; + + if (n < 0) + return (FALSE); + + undo_boundary_enable(FFRAND, 0); + while (n--) { + nicol = 0; + for (i = 0; i < llength(curwp->w_dotp); ++i) { + c = lgetc(curwp->w_dotp, i); + if (c != ' ' && c != '\t') + break; + if (c == '\t') + nicol |= 0x07; + ++nicol; + } + if (lnewline() == FALSE || (( +#ifdef NOTAB + curbp->b_flag & BFNOTAB) ? linsert(nicol, ' ') == FALSE : ( +#endif /* NOTAB */ + ((i = nicol / 8) != 0 && linsert(i, '\t') == FALSE) || + ((i = nicol % 8) != 0 && linsert(i, ' ') == FALSE)))) { + s = FALSE; + break; + } + } + undo_boundary_enable(FFRAND, 1); + return (s); +} + +/* + * Indent the current line. Delete existing leading whitespace, + * and use tabs/spaces to achieve correct indentation. Try + * to leave dot where it started. + */ +int +indent(int f, int n) +{ + int soff, i; + + if (n < 0) + return (FALSE); + + delleadwhite(FFRAND, 1); + + /* If not invoked with a numerical argument, done */ + if (!(f & FFARG)) + return (TRUE); + + /* insert appropriate whitespace */ + soff = curwp->w_doto; + (void)gotobol(FFRAND, 1); + if ( +#ifdef NOTAB + (curbp->b_flag & BFNOTAB) ? linsert(n, ' ') == FALSE : +#endif /* NOTAB */ + (((i = n / 8) != 0 && linsert(i, '\t') == FALSE) || + ((i = n % 8) != 0 && linsert(i, ' ') == FALSE))) + return (FALSE); + + forwchar(FFRAND, soff); + + return (TRUE); +} + + +/* + * Delete forward. This is real easy, because the basic delete routine does + * all of the work. Watches for negative arguments, and does the right thing. + * If any argument is present, it kills rather than deletes, to prevent loss + * of text if typed with a big argument. Normally bound to "C-d". + */ +/* ARGSUSED */ +int +forwdel(int f, int n) +{ + if (n < 0) + return (backdel(f | FFRAND, -n)); + + /* really a kill */ + if (f & FFARG) { + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + } + + return (ldelete((RSIZE) n, (f & FFARG) ? KFORW : KNONE)); +} + +/* + * Delete backwards. This is quite easy too, because it's all done with + * other functions. Just move the cursor back, and delete forwards. Like + * delete forward, this actually does a kill if presented with an argument. + */ +/* ARGSUSED */ +int +backdel(int f, int n) +{ + int s; + + if (n < 0) + return (forwdel(f | FFRAND, -n)); + + /* really a kill */ + if (f & FFARG) { + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + } + if ((s = backchar(f | FFRAND, n)) == TRUE) + s = ldelete((RSIZE)n, (f & FFARG) ? KFORW : KNONE); + + return (s); +} + +#ifdef NOTAB +/* ARGSUSED */ +int +space_to_tabstop(int f, int n) +{ + if (n < 0) + return (FALSE); + if (n == 0) + return (TRUE); + return (linsert((n << 3) - (curwp->w_doto & 7), ' ')); +} +#endif /* NOTAB */ + +/* + * Move the dot to the first non-whitespace character of the current line. + */ +int +backtoindent(int f, int n) +{ + gotobol(FFRAND, 1); + while (curwp->w_doto < llength(curwp->w_dotp) && + (isspace(lgetc(curwp->w_dotp, curwp->w_doto)))) + ++curwp->w_doto; + return (TRUE); +} + +/* + * Join the current line to the previous, or with arg, the next line + * to the current one. If the former line is not empty, leave exactly + * one space at the joint. Otherwise, leave no whitespace. + */ +int +joinline(int f, int n) +{ + int doto; + + undo_boundary_enable(FFRAND, 0); + if (f & FFARG) { + gotoeol(FFRAND, 1); + forwdel(FFRAND, 1); + } else { + gotobol(FFRAND, 1); + backdel(FFRAND, 1); + } + + delwhite(FFRAND, 1); + + if ((doto = curwp->w_doto) > 0) { + linsert(1, ' '); + curwp->w_doto = doto; + } + undo_boundary_enable(FFRAND, 1); + + return (TRUE); +} Index: contrib/mg/version.c =================================================================== --- /dev/null +++ contrib/mg/version.c @@ -0,0 +1,28 @@ +/* $OpenBSD: version.c,v 1.10 2015/03/19 21:22:15 bcallah Exp $ */ + +/* This file is in the public domain. */ + +/* + * This file contains the string that gets written + * out by the emacs-version command. + */ + +#include +#include +#include + +#include "def.h" + +const char version[] = "Mg 2a portable 7.0"; + +/* + * Display the version. All this does + * is copy the version string onto the echo line. + */ +/* ARGSUSED */ +int +showversion(int f, int n) +{ + ewprintf("%s", version); + return (TRUE); +} Index: contrib/mg/window.c =================================================================== --- /dev/null +++ contrib/mg/window.c @@ -0,0 +1,440 @@ +/* $OpenBSD: window.c,v 1.36 2015/11/18 18:21:06 jasper Exp $ */ + +/* This file is in the public domain. */ + +/* + * Window handling. + */ + +#include +#include +#include +#include + +#include "def.h" + +struct mgwin * +new_window(struct buffer *bp) +{ + struct mgwin *wp; + + wp = calloc(1, sizeof(struct mgwin)); + if (wp == NULL) + return (NULL); + + wp->w_bufp = bp; + wp->w_dotp = NULL; + wp->w_doto = 0; + wp->w_markp = NULL; + wp->w_marko = 0; + wp->w_rflag = 0; + wp->w_frame = 0; + wp->w_wrapline = NULL; + wp->w_dotline = wp->w_markline = 1; + if (bp) + bp->b_nwnd++; + return (wp); +} + +/* + * Reposition dot in the current window to line "n". If the argument is + * positive, it is that line. If it is negative it is that line from the + * bottom. If it is 0 the window is centered (this is what the standard + * redisplay code does). + */ +/* ARGSUSED */ +int +reposition(int f, int n) +{ + curwp->w_frame = (f & FFARG) ? (n >= 0 ? n + 1 : n) : 0; + curwp->w_rflag |= WFFRAME; + sgarbf = TRUE; + return (TRUE); +} + +/* + * Refresh the display. A call is made to the "ttresize" entry in the + * terminal handler, which tries to reset "nrow" and "ncol". They will, + * however, never be set outside of the NROW or NCOL range. If the display + * changed size, arrange that everything is redone, then call "update" to + * fix the display. We do this so the new size can be displayed. In the + * normal case the call to "update" in "main.c" refreshes the screen, and + * all of the windows need not be recomputed. This call includes a + * 'force' parameter to ensure that the redraw is done, even after a + * a suspend/continue (where the window size parameters will already + * be updated). Note that when you get to the "display unusable" + * message, the screen will be messed up. If you make the window bigger + * again, and send another command, everything will get fixed! + */ +int +redraw(int f, int n) +{ + return (do_redraw(f, n, FALSE)); +} + +/* ARGSUSED */ +int +do_redraw(int f, int n, int force) +{ + struct mgwin *wp; + int oldnrow, oldncol; + + oldnrow = nrow; + oldncol = ncol; + ttresize(); + if (nrow != oldnrow || ncol != oldncol || force) { + + /* find last */ + wp = wheadp; + while (wp->w_wndp != NULL) + wp = wp->w_wndp; + + /* check if too small */ + if (nrow < wp->w_toprow + 3) { + dobeep(); + ewprintf("Display unusable"); + return (FALSE); + } + wp->w_ntrows = nrow - wp->w_toprow - 2; + sgarbf = TRUE; + update(CMODE); + } else + sgarbf = TRUE; + return (TRUE); +} + +/* + * The command to make the next window (next => down the screen) the current + * window. There are no real errors, although the command does nothing if + * there is only 1 window on the screen. + */ +/* ARGSUSED */ +int +nextwind(int f, int n) +{ + struct mgwin *wp; + + if ((wp = curwp->w_wndp) == NULL) + wp = wheadp; + curwp = wp; + curbp = wp->w_bufp; + return (TRUE); +} + +/* not in GNU Emacs */ +/* + * This command makes the previous window (previous => up the screen) the + * current window. There are no errors, although the command does not do + * a lot if there is only 1 window. + */ +/* ARGSUSED */ +int +prevwind(int f, int n) +{ + struct mgwin *wp1, *wp2; + + wp1 = wheadp; + wp2 = curwp; + if (wp1 == wp2) + wp2 = NULL; + while (wp1->w_wndp != wp2) + wp1 = wp1->w_wndp; + curwp = wp1; + curbp = wp1->w_bufp; + return (TRUE); +} + +/* + * This command makes the current window the only window on the screen. Try + * to set the framing so that "." does not have to move on the display. Some + * care has to be taken to keep the values of dot and mark in the buffer + * structures right if the destruction of a window makes a buffer become + * undisplayed. + */ +/* ARGSUSED */ +int +onlywind(int f, int n) +{ + struct mgwin *wp; + struct line *lp; + int i; + + while (wheadp != curwp) { + wp = wheadp; + wheadp = wp->w_wndp; + if (--wp->w_bufp->b_nwnd == 0) { + wp->w_bufp->b_dotp = wp->w_dotp; + wp->w_bufp->b_doto = wp->w_doto; + wp->w_bufp->b_markp = wp->w_markp; + wp->w_bufp->b_marko = wp->w_marko; + wp->w_bufp->b_dotline = wp->w_dotline; + wp->w_bufp->b_markline = wp->w_markline; + } + free(wp); + } + while (curwp->w_wndp != NULL) { + wp = curwp->w_wndp; + curwp->w_wndp = wp->w_wndp; + if (--wp->w_bufp->b_nwnd == 0) { + wp->w_bufp->b_dotp = wp->w_dotp; + wp->w_bufp->b_doto = wp->w_doto; + wp->w_bufp->b_markp = wp->w_markp; + wp->w_bufp->b_marko = wp->w_marko; + wp->w_bufp->b_dotline = wp->w_dotline; + wp->w_bufp->b_markline = wp->w_markline; + } + free(wp); + } + lp = curwp->w_linep; + i = curwp->w_toprow; + while (i != 0 && lback(lp) != curbp->b_headp) { + --i; + lp = lback(lp); + } + curwp->w_toprow = 0; + + /* 2 = mode, echo */ + curwp->w_ntrows = nrow - 2; + curwp->w_linep = lp; + curwp->w_rflag |= WFMODE | WFFULL; + return (TRUE); +} + +/* + * Split the current window. A window smaller than 3 lines cannot be split. + * The only other error that is possible is a "malloc" failure allocating the + * structure for the new window. + * If called with a FFOTHARG, flags on the new window are set to 'n'. + */ +/* ARGSUSED */ +int +splitwind(int f, int n) +{ + struct mgwin *wp, *wp1, *wp2; + struct line *lp; + int ntru, ntrd, ntrl; + + if (curwp->w_ntrows < 3) { + dobeep(); + ewprintf("Cannot split a %d line window", curwp->w_ntrows); + return (FALSE); + } + wp = new_window(curbp); + if (wp == NULL) { + dobeep(); + ewprintf("Unable to create a window"); + return (FALSE); + } + + /* use the current dot and mark */ + wp->w_dotp = curwp->w_dotp; + wp->w_doto = curwp->w_doto; + wp->w_markp = curwp->w_markp; + wp->w_marko = curwp->w_marko; + wp->w_dotline = curwp->w_dotline; + wp->w_markline = curwp->w_markline; + + /* figure out which half of the screen we're in */ + ntru = (curwp->w_ntrows - 1) / 2; /* Upper size */ + ntrl = (curwp->w_ntrows - 1) - ntru; /* Lower size */ + + for (lp = curwp->w_linep, ntrd = 0; lp != curwp->w_dotp; + lp = lforw(lp)) + ntrd++; + + lp = curwp->w_linep; + + /* old is upper window */ + if (ntrd <= ntru) { + /* hit mode line */ + if (ntrd == ntru) + lp = lforw(lp); + curwp->w_ntrows = ntru; + wp->w_wndp = curwp->w_wndp; + curwp->w_wndp = wp; + wp->w_toprow = curwp->w_toprow + ntru + 1; + wp->w_ntrows = ntrl; + /* old is lower window */ + } else { + wp1 = NULL; + wp2 = wheadp; + while (wp2 != curwp) { + wp1 = wp2; + wp2 = wp2->w_wndp; + } + if (wp1 == NULL) + wheadp = wp; + else + wp1->w_wndp = wp; + wp->w_wndp = curwp; + wp->w_toprow = curwp->w_toprow; + wp->w_ntrows = ntru; + + /* mode line */ + ++ntru; + curwp->w_toprow += ntru; + curwp->w_ntrows = ntrl; + while (ntru--) + lp = lforw(lp); + } + + /* adjust the top lines if necessary */ + curwp->w_linep = lp; + wp->w_linep = lp; + + curwp->w_rflag |= WFMODE | WFFULL; + wp->w_rflag |= WFMODE | WFFULL; + /* if FFOTHARG, set flags) */ + if (f & FFOTHARG) + wp->w_flag = n; + + return (TRUE); +} + +/* + * Enlarge the current window. Find the window that loses space. Make sure + * it is big enough. If so, hack the window descriptions, and ask redisplay + * to do all the hard work. You don't just set "force reframe" because dot + * would move. + */ +/* ARGSUSED */ +int +enlargewind(int f, int n) +{ + struct mgwin *adjwp; + struct line *lp; + int i; + + if (n < 0) + return (shrinkwind(f, -n)); + if (wheadp->w_wndp == NULL) { + dobeep(); + ewprintf("Only one window"); + return (FALSE); + } + if ((adjwp = curwp->w_wndp) == NULL) { + adjwp = wheadp; + while (adjwp->w_wndp != curwp) + adjwp = adjwp->w_wndp; + } + if (adjwp->w_ntrows <= n) { + dobeep(); + ewprintf("Impossible change"); + return (FALSE); + } + + /* shrink below */ + if (curwp->w_wndp == adjwp) { + lp = adjwp->w_linep; + for (i = 0; i < n && lp != adjwp->w_bufp->b_headp; ++i) + lp = lforw(lp); + adjwp->w_linep = lp; + adjwp->w_toprow += n; + /* shrink above */ + } else { + lp = curwp->w_linep; + for (i = 0; i < n && lback(lp) != curbp->b_headp; ++i) + lp = lback(lp); + curwp->w_linep = lp; + curwp->w_toprow -= n; + } + curwp->w_ntrows += n; + adjwp->w_ntrows -= n; + curwp->w_rflag |= WFMODE | WFFULL; + adjwp->w_rflag |= WFMODE | WFFULL; + return (TRUE); +} + +/* + * Shrink the current window. Find the window that gains space. Hack at the + * window descriptions. Ask the redisplay to do all the hard work. + */ +int +shrinkwind(int f, int n) +{ + struct mgwin *adjwp; + struct line *lp; + int i; + + if (n < 0) + return (enlargewind(f, -n)); + if (wheadp->w_wndp == NULL) { + dobeep(); + ewprintf("Only one window"); + return (FALSE); + } + /* + * Bit of flakiness - FFRAND means it was an internal call, and + * to be trusted implicitly about sizes. + */ + if (!(f & FFRAND) && curwp->w_ntrows <= n) { + dobeep(); + ewprintf("Impossible change"); + return (FALSE); + } + if ((adjwp = curwp->w_wndp) == NULL) { + adjwp = wheadp; + while (adjwp->w_wndp != curwp) + adjwp = adjwp->w_wndp; + } + + /* grow below */ + if (curwp->w_wndp == adjwp) { + lp = adjwp->w_linep; + for (i = 0; i < n && lback(lp) != adjwp->w_bufp->b_headp; ++i) + lp = lback(lp); + adjwp->w_linep = lp; + adjwp->w_toprow -= n; + /* grow above */ + } else { + lp = curwp->w_linep; + for (i = 0; i < n && lp != curbp->b_headp; ++i) + lp = lforw(lp); + curwp->w_linep = lp; + curwp->w_toprow += n; + } + curwp->w_ntrows -= n; + adjwp->w_ntrows += n; + curwp->w_rflag |= WFMODE | WFFULL; + adjwp->w_rflag |= WFMODE | WFFULL; + return (TRUE); +} + +/* + * Delete current window. Call shrink-window to do the screen updating, then + * throw away the window. + */ +/* ARGSUSED */ +int +delwind(int f, int n) +{ + struct mgwin *wp, *nwp; + + wp = curwp; /* Cheap... */ + + /* shrinkwind returning false means only one window... */ + if (shrinkwind(FFRAND, wp->w_ntrows + 1) == FALSE) + return (FALSE); + if (--wp->w_bufp->b_nwnd == 0) { + wp->w_bufp->b_dotp = wp->w_dotp; + wp->w_bufp->b_doto = wp->w_doto; + wp->w_bufp->b_markp = wp->w_markp; + wp->w_bufp->b_marko = wp->w_marko; + wp->w_bufp->b_dotline = wp->w_dotline; + wp->w_bufp->b_markline = wp->w_markline; + } + + /* since shrinkwind did't crap out, we know we have a second window */ + if (wp == wheadp) + wheadp = curwp = wp->w_wndp; + else if ((curwp = wp->w_wndp) == NULL) + curwp = wheadp; + curbp = curwp->w_bufp; + for (nwp = wheadp; nwp != NULL; nwp = nwp->w_wndp) + if (nwp->w_wndp == wp) { + nwp->w_wndp = wp->w_wndp; + break; + } + free(wp); + return (TRUE); +} Index: contrib/mg/word.c =================================================================== --- /dev/null +++ contrib/mg/word.c @@ -0,0 +1,514 @@ +/* $OpenBSD: word.c,v 1.19 2015/12/30 20:51:51 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * Word mode commands. + * The routines in this file implement commands that work word at a time. + * There are all sorts of word mode commands. + */ + +#include +#include +#include +#include +#include +#include + +#include "def.h" + +RSIZE countfword(void); +int grabword(char **); + +/* + * Move the cursor backward by "n" words. All of the details of motion are + * performed by the "backchar" and "forwchar" routines. + */ +/* ARGSUSED */ +int +backword(int f, int n) +{ + if (n < 0) + return (forwword(f | FFRAND, -n)); + if (backchar(FFRAND, 1) == FALSE) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (backchar(FFRAND, 1) == FALSE) + return (TRUE); + } + while (inword() != FALSE) { + if (backchar(FFRAND, 1) == FALSE) + return (TRUE); + } + } + return (forwchar(FFRAND, 1)); +} + +/* + * Move the cursor forward by the specified number of words. All of the + * motion is done by "forwchar". + */ +/* ARGSUSED */ +int +forwword(int f, int n) +{ + if (n < 0) + return (backword(f | FFRAND, -n)); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + while (inword() != FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + } + return (TRUE); +} + +/* + * Transpose 2 words. + * The function below is artifically restricted to only a maximum of 1 iteration + * at the moment because the 'undo' functionality within mg needs amended for + * multiple movements of point, backwards and forwards. + */ +int +transposeword(int f, int n) +{ + struct line *tmp1_w_dotp = NULL; + struct line *tmp2_w_dotp = NULL; + int tmp2_w_doto = 0; + int tmp1_w_dotline = 0; + int tmp2_w_dotline = 0; + int tmp1_w_doto; + int i; /* start-of-line space counter */ + int ret, s; + int newline; + int leave = 0; + int tmp_len; + char *word1 = NULL; + char *word2 = NULL; + char *chr; + + if (n == 0) + return (TRUE); + + n = 1; /* remove this line to allow muliple-iterations */ + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + undo_boundary_enable(FFRAND, 0); + + /* go backwards to find the start of a word to transpose. */ + (void)backword(FFRAND, 1); + ret = grabword(&word1); + if (ret == ABORT) { + ewprintf("No word to the left to tranpose."); + return (FALSE); + } + if (ret < 0) { + dobeep(); + ewprintf("Error copying word: %s", strerror(ret)); + free(word1); + return (FALSE); + } + + while (n-- > 0) { + i = 0; + newline = 0; + + tmp1_w_doto = curwp->w_doto; + tmp1_w_dotline = curwp->w_dotline; + tmp1_w_dotp = curwp->w_dotp; + + /* go forward and find next word. */ + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) { + leave = 1; + if (tmp1_w_dotline < curwp->w_dotline) + curwp->w_dotline--; + ewprintf("Don't have two things to transpose"); + break; + } + if (curwp->w_doto == 0) { + newline = 1; + i = 0; + } else if (newline) + i++; + } + if (leave) { + tmp2_w_doto = tmp1_w_doto; + tmp2_w_dotline = tmp1_w_dotline; + tmp2_w_dotp = tmp1_w_dotp; + break; + } + tmp2_w_doto = curwp->w_doto; + tmp2_w_dotline = curwp->w_dotline; + tmp2_w_dotp = curwp->w_dotp; + + ret = grabword(&word2); + if (ret < 0 || ret == ABORT) { + dobeep(); + ewprintf("Error copying word: %s", strerror(ret)); + free(word1); + return (FALSE); + } + tmp_len = strlen(word2); + tmp2_w_doto += tmp_len; + + curwp->w_doto = tmp1_w_doto; + curwp->w_dotline = tmp1_w_dotline; + curwp->w_dotp = tmp1_w_dotp; + + /* insert shuffled along word */ + for (chr = word2; *chr != '\0'; ++chr) + linsert(1, *chr); + + if (newline) + tmp2_w_doto = i; + + curwp->w_doto = tmp2_w_doto; + curwp->w_dotline = tmp2_w_dotline; + curwp->w_dotp = tmp2_w_dotp; + + word2 = NULL; + } + curwp->w_doto = tmp2_w_doto; + curwp->w_dotline = tmp2_w_dotline; + curwp->w_dotp = tmp2_w_dotp; + + /* insert very first word in its new position */ + for (chr = word1; *chr != '\0'; ++chr) + linsert(1, *chr); + + if (leave) + (void)backword(FFRAND, 1); + + free(word1); + free(word2); + + undo_boundary_enable(FFRAND, 1); + + return (TRUE); +} + +/* + * copy and delete word. +*/ +int +grabword(char **word) +{ + int c; + + while (inword() == TRUE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (*word == NULL) { + if (asprintf(word, "%c", c) == -1) + return (errno); + } else { + if (asprintf(word, "%s%c", *word, c) == -1) + return (errno); + } + (void)forwdel(FFRAND, 1); + } + if (*word == NULL) + return (ABORT); + return (TRUE); +} + +/* + * Move the cursor forward by the specified number of words. As you move, + * convert any characters to upper case. + */ +/* ARGSUSED */ +int +upperword(int f, int n) +{ + int c, s; + RSIZE size; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + size = countfword(); + undo_add_change(curwp->w_dotp, curwp->w_doto, size); + + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (ISLOWER(c) != FALSE) { + c = TOUPPER(c); + lputc(curwp->w_dotp, curwp->w_doto, c); + lchange(WFFULL); + } + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + } + return (TRUE); +} + +/* + * Move the cursor forward by the specified number of words. As you move + * convert characters to lower case. + */ +/* ARGSUSED */ +int +lowerword(int f, int n) +{ + int c, s; + RSIZE size; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + size = countfword(); + undo_add_change(curwp->w_dotp, curwp->w_doto, size); + + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (ISUPPER(c) != FALSE) { + c = TOLOWER(c); + lputc(curwp->w_dotp, curwp->w_doto, c); + lchange(WFFULL); + } + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + } + return (TRUE); +} + +/* + * Move the cursor forward by the specified number of words. As you move + * convert the first character of the word to upper case, and subsequent + * characters to lower case. Error if you try to move past the end of the + * buffer. + */ +/* ARGSUSED */ +int +capword(int f, int n) +{ + int c, s; + RSIZE size; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + + if (n < 0) + return (FALSE); + while (n--) { + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + size = countfword(); + undo_add_change(curwp->w_dotp, curwp->w_doto, size); + + if (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (ISLOWER(c) != FALSE) { + c = TOUPPER(c); + lputc(curwp->w_dotp, curwp->w_doto, c); + lchange(WFFULL); + } + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + while (inword() != FALSE) { + c = lgetc(curwp->w_dotp, curwp->w_doto); + if (ISUPPER(c) != FALSE) { + c = TOLOWER(c); + lputc(curwp->w_dotp, curwp->w_doto, c); + lchange(WFFULL); + } + if (forwchar(FFRAND, 1) == FALSE) + return (TRUE); + } + } + } + return (TRUE); +} + +/* + * Count characters in word, from current position + */ +RSIZE +countfword() +{ + RSIZE size; + struct line *dotp; + int doto; + + dotp = curwp->w_dotp; + doto = curwp->w_doto; + size = 0; + + while (inword() != FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + /* hit the end of the buffer */ + goto out; + ++size; + } +out: + curwp->w_dotp = dotp; + curwp->w_doto = doto; + return (size); +} + + +/* + * Kill forward by "n" words. + */ +/* ARGSUSED */ +int +delfword(int f, int n) +{ + RSIZE size; + struct line *dotp; + int doto; + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + if (n < 0) + return (FALSE); + + /* purge kill buffer */ + if ((lastflag & CFKILL) == 0) + kdelete(); + + thisflag |= CFKILL; + dotp = curwp->w_dotp; + doto = curwp->w_doto; + size = 0; + + while (n--) { + while (inword() == FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + /* hit the end of the buffer */ + goto out; + ++size; + } + while (inword() != FALSE) { + if (forwchar(FFRAND, 1) == FALSE) + /* hit the end of the buffer */ + goto out; + ++size; + } + } +out: + curwp->w_dotp = dotp; + curwp->w_doto = doto; + return (ldelete(size, KFORW)); +} + +/* + * Kill backwards by "n" words. The rules for success and failure are now + * different, to prevent strange behavior at the start of the buffer. The + * command only fails if something goes wrong with the actual delete of the + * characters. It is successful even if no characters are deleted, or if you + * say delete 5 words, and there are only 4 words left. I considered making + * the first call to "backchar" special, but decided that that would just be + * weird. Normally this is bound to "M-Rubout" and to "M-Backspace". + */ +/* ARGSUSED */ +int +delbword(int f, int n) +{ + RSIZE size; + int s; + + if ((s = checkdirty(curbp)) != TRUE) + return (s); + if (curbp->b_flag & BFREADONLY) { + dobeep(); + ewprintf("Buffer is read-only"); + return (FALSE); + } + + if (n < 0) + return (FALSE); + + /* purge kill buffer */ + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + if (backchar(FFRAND, 1) == FALSE) + /* hit buffer start */ + return (TRUE); + + /* one deleted */ + size = 1; + while (n--) { + while (inword() == FALSE) { + if (backchar(FFRAND, 1) == FALSE) + /* hit buffer start */ + goto out; + ++size; + } + while (inword() != FALSE) { + if (backchar(FFRAND, 1) == FALSE) + /* hit buffer start */ + goto out; + ++size; + } + } + if (forwchar(FFRAND, 1) == FALSE) + return (FALSE); + + /* undo assumed delete */ + --size; +out: + return (ldelete(size, KBACK)); +} + +/* + * Return TRUE if the character at dot is a character that is considered to be + * part of a word. The word character list is hard coded. Should be settable. + */ +int +inword(void) +{ + /* can't use lgetc in ISWORD due to bug in OSK cpp */ + return (curwp->w_doto != llength(curwp->w_dotp) && + ISWORD(curwp->w_dotp->l_text[curwp->w_doto])); +} Index: contrib/mg/yank.c =================================================================== --- /dev/null +++ contrib/mg/yank.c @@ -0,0 +1,267 @@ +/* $OpenBSD: yank.c,v 1.15 2021/03/01 10:51:14 lum Exp $ */ + +/* This file is in the public domain. */ + +/* + * kill ring functions + */ + +#include +#include +#include +#include +#include + +#include "def.h" + +#define KBLOCK 8192 /* Kill grow. */ + +static char *kbufp = NULL; /* Kill buffer data. */ +static RSIZE kused = 0; /* # of bytes used in KB. */ +static RSIZE ksize = 0; /* # of bytes allocated in KB. */ +static RSIZE kstart = 0; /* # of first used byte in KB. */ + +static int kgrow(int); + +/* + * Delete all of the text saved in the kill buffer. Called by commands when + * a new kill context is created. The kill buffer array is released, just in + * case the buffer has grown to an immense size. No errors. + */ +void +kdelete(void) +{ + if (kbufp != NULL) { + free(kbufp); + kbufp = NULL; + kstart = kused = ksize = 0; + } +} + +/* + * Insert a character to the kill buffer, enlarging the buffer if there + * isn't any room. Always grow the buffer in chunks, on the assumption + * that if you put something in the kill buffer you are going to put more + * stuff there too later. Return TRUE if all is well, and FALSE on errors. + * Print a message on errors. Dir says whether to put it at back or front. + * This call is ignored if KNONE is set. + */ +int +kinsert(int c, int dir) +{ + if (dir == KNONE) + return (TRUE); + if (kused == ksize && dir == KFORW && kgrow(dir) == FALSE) + return (FALSE); + if (kstart == 0 && dir == KBACK && kgrow(dir) == FALSE) + return (FALSE); + if (dir == KFORW) + kbufp[kused++] = c; + else if (dir == KBACK) + kbufp[--kstart] = c; + else + panic("broken kinsert call"); /* Oh shit! */ + return (TRUE); +} + +/* + * kgrow - just get more kill buffer for the callee. If dir = KBACK + * we are trying to get space at the beginning of the kill buffer. + */ +static int +kgrow(int dir) +{ + int nstart; + char *nbufp; + + if ((unsigned)(ksize + KBLOCK) <= (unsigned)ksize) { + /* probably 16 bit unsigned */ + dobeep(); + ewprintf("Kill buffer size at maximum"); + return (FALSE); + } + if ((nbufp = malloc((unsigned)(ksize + KBLOCK))) == NULL) { + dobeep(); + ewprintf("Can't get %ld bytes", (long)(ksize + KBLOCK)); + return (FALSE); + } + nstart = (dir == KBACK) ? (kstart + KBLOCK) : (KBLOCK / 4); + bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (int)(kused - kstart)); + free(kbufp); + kbufp = nbufp; + ksize += KBLOCK; + kused = kused - kstart + nstart; + kstart = nstart; + return (TRUE); +} + +/* + * This function gets characters from the kill buffer. If the character + * index "n" is off the end, it returns "-1". This lets the caller just + * scan along until it gets a "-1" back. + */ +int +kremove(int n) +{ + if (n < 0 || n + kstart >= kused) + return (-1); + return (CHARMASK(kbufp[n + kstart])); +} + +/* + * Copy a string into the kill buffer. kflag gives direction. + * if KNONE, do nothing. + */ +int +kchunk(char *cp1, RSIZE chunk, int kflag) +{ + /* + * HACK - doesn't matter, and fixes back-over-nl bug for empty + * kill buffers. + */ + if (kused == kstart) + kflag = KFORW; + + if (kflag & KFORW) { + while (ksize - kused < chunk) + if (kgrow(kflag) == FALSE) + return (FALSE); + bcopy(cp1, &(kbufp[kused]), (int)chunk); + kused += chunk; + } else if (kflag & KBACK) { + while (kstart < chunk) + if (kgrow(kflag) == FALSE) + return (FALSE); + bcopy(cp1, &(kbufp[kstart - chunk]), (int)chunk); + kstart -= chunk; + } + + return (TRUE); +} + +/* + * Kill line. If called without an argument, it kills from dot to the end + * of the line, unless it is at the end of the line, when it kills the + * newline. If called with an argument of 0, it kills from the start of the + * line to dot. If called with a positive argument, it kills from dot + * forward over that number of newlines. If called with a negative argument + * it kills any text before dot on the current line, then it kills back + * abs(arg) lines. + */ +/* ARGSUSED */ +int +killline(int f, int n) +{ + struct line *nextp; + RSIZE chunk; + int i, c; + + /* clear kill buffer if last wasn't a kill */ + if ((lastflag & CFKILL) == 0) + kdelete(); + thisflag |= CFKILL; + if (!(f & FFARG)) { + for (i = curwp->w_doto; i < llength(curwp->w_dotp); ++i) + if ((c = lgetc(curwp->w_dotp, i)) != ' ' && c != '\t') + break; + if (i == llength(curwp->w_dotp)) + chunk = llength(curwp->w_dotp) - curwp->w_doto + 1; + else { + chunk = llength(curwp->w_dotp) - curwp->w_doto; + if (chunk == 0) + chunk = 1; + } + } else if (n > 0) { + chunk = llength(curwp->w_dotp) - curwp->w_doto; + nextp = lforw(curwp->w_dotp); + if (nextp != curbp->b_headp) + chunk++; /* newline */ + if (nextp == curbp->b_headp) + goto done; /* EOL */ + i = n; + while (--i) { + chunk += llength(nextp); + nextp = lforw(nextp); + if (nextp != curbp->b_headp) + chunk++; /* newline */ + if (nextp == curbp->b_headp) + break; /* EOL */ + } + } else { + /* n <= 0 */ + chunk = curwp->w_doto; + curwp->w_doto = 0; + i = n; + while (i++) { + if (lforw(curwp->w_dotp)) + chunk++; + curwp->w_dotp = lback(curwp->w_dotp); + curwp->w_rflag |= WFMOVE; + chunk += llength(curwp->w_dotp); + } + } + /* + * KFORW here is a bug. Should be KBACK/KFORW, but we need to + * rewrite the ldelete code (later)? + */ +done: + if (chunk) + return (ldelete(chunk, KFORW)); + return (TRUE); +} + +/* + * Yank text back from the kill buffer. This is really easy. All of the work + * is done by the standard insert routines. All you do is run the loop, and + * check for errors. The blank lines are inserted with a call to "newline" + * instead of a call to "lnewline" so that the magic stuff that happens when + * you type a carriage return also happens when a carriage return is yanked + * back from the kill buffer. An attempt has been made to fix the cosmetic + * bug associated with a yank when dot is on the top line of the window + * (nothing moves, because all of the new text landed off screen). + */ +/* ARGSUSED */ +int +yank(int f, int n) +{ + struct line *lp; + int c, i, nline; + + if (n < 0) + return (FALSE); + + /* newline counting */ + nline = 0; + + undo_boundary_enable(FFRAND, 0); + while (n--) { + /* mark around last yank */ + isetmark(); + i = 0; + while ((c = kremove(i)) >= 0) { + if (c == *curbp->b_nlchr) { + if (enewline(FFRAND, 1) == FALSE) + return (FALSE); + ++nline; + } else { + if (linsert(1, c) == FALSE) + return (FALSE); + } + ++i; + } + } + /* cosmetic adjustment */ + lp = curwp->w_linep; + + /* if offscreen insert */ + if (curwp->w_dotp == lp) { + while (nline-- && lback(lp) != curbp->b_headp) + lp = lback(lp); + /* adjust framing */ + curwp->w_linep = lp; + curwp->w_rflag |= WFFULL; + } + undo_boundary_enable(FFRAND, 1); + return (TRUE); +} + Index: release/packages/Makefile.package =================================================================== --- release/packages/Makefile.package +++ release/packages/Makefile.package @@ -79,6 +79,8 @@ kernel_DESC= FreeBSD Kernel manuals_COMMENT= Manual Pages manuals_DESC= Manual Pages +mg_COMMENT= Small, fast Emacs-like editor +mg_DESC= Small, fast Emacs-like editor mlx-tools_COMMENT= Mellanox Utilities mlx-tools_DESC= Mellanox Utilities mtree_COMMENT= MTREE Files Index: usr.bin/Makefile =================================================================== --- usr.bin/Makefile +++ usr.bin/Makefile @@ -89,6 +89,7 @@ m4 \ mandoc \ mesg \ + mg \ minigzip \ ministat \ mkdep \ Index: usr.bin/mg/Makefile =================================================================== --- /dev/null +++ usr.bin/mg/Makefile @@ -0,0 +1,64 @@ +PACKAGE= mg +PROG= mg +SRCDIR= ${SRCTOP}/contrib/mg + +.PATH: ${SRCDIR} + +CFLAGS+= -DREGEX -w -D__dead=__dead2 -DLOGIN_NAME_MAX=MAXLOGNAME +CFLAGS+= -I${.CURDIR} -I${SRCDIR} +CWARNFLAGS+= -w + +LIBADD= util ncursesw + +SRCS= autoexec.c \ + basic.c \ + bell.c \ + buffer.c \ + cinfo.c \ + dir.c \ + display.c \ + echo.c \ + extend.c \ + file.c \ + fileio.c \ + funmap.c \ + help.c \ + kbd.c \ + keymap.c \ + line.c \ + macro.c \ + main.c \ + match.c \ + modes.c \ + paragraph.c \ + re_search.c \ + region.c \ + search.c \ + spawn.c \ + tty.c \ + ttyio.c \ + ttykbd.c \ + undo.c \ + util.c \ + version.c \ + window.c \ + word.c \ + yank.c \ + cmode.c \ + cscope.c \ + dired.c \ + grep.c \ + tags.c \ + fparseln.c \ + fstatat.c \ + futimens.c \ + getline.c \ + reallocarray.c \ + strlcat.c \ + strlcpy.c \ + strndup.c \ + strtonum.c \ + interpreter.c \ + extensions.c + +.include Index: usr.bin/mg/config.h =================================================================== --- /dev/null +++ usr.bin/mg/config.h @@ -0,0 +1,14 @@ +/* This file is based on the file generated by configure. */ + +#include "common.h" +#include "freebsd.h" + +#define HAVE_FPARSELN +#define HAVE_FSTATAT +#define HAVE_FUTIMENS +#define HAVE_GETLINE +#define HAVE_REALLOCARRAY +#define HAVE_STRLCAT +#define HAVE_STRLCPY +#define HAVE_STRNDUP +#define HAVE_STRTONUM