Relay-Version: version B 2.10 5/3/83; site utzoo.UUCP Path: utzoo!mnetor!uunet!munnari!mundoe!glf From: glf@mundoe.mu.oz (Giuseppe Fiusco) Newsgroups: comp.emacs Subject: Key bindings for MicroEMACS(3.8i) (2 of 3) Message-ID: <303@mundoe.mu.oz> Date: Wed, 9-Sep-87 03:04:41 EDT Article-I.D.: mundoe.303 Posted: Wed Sep 9 03:04:41 1987 Date-Received: Fri, 11-Sep-87 01:03:16 EDT Organization: Mathematics, Melbourne Uni, Australia Lines: 1366 : ---------------------------------------- cut here echo x - "bind.c" 2>&1 sed "s/^X//" >"bind.c" <<'!The!End!' X/* This file is for functions having to do with key bindings, X descriptions, help commands and startup file. X X written 11-feb-86 by Daniel Lawrence X X Rewritten to use programmable key bindings X - Tue Aug 18 1987 by G.Fiusco X */ X X#include X#include X#include X#include "estruct.h" X#include "edef.h" X#include "epath.h" X X Xextern int ctrlg(); /* dummy prefix binding functions */ Xchar *getenv() ; Xint (*fncmatch())() ; Xextern struct spec_chars *bindh ; Xextern NBIND cbind[] ; Xstatic char spec_c[8] ; X X X/* X * A dummy routine for adding bindings deeper into the list. X */ X Xdummy() X{ X} X X X/* X * A dummy routine for handling integers for function calls. X */ X Xdigit() X{ X} X X X/* X * A dummy routine for handling the universal routine. X */ X Xunarg() X{ X} X X Xdeskey(f, n) /* describe the command for a certain key */ X X{ X register unsigned char *c; X /* command character to describe */ X register char *ptr; /* string pointer to scan output strings */ X struct spec_chars *c_bind, *p_bind ; X /* pointers into key bind list */ X register NBIND *nptr; /* pointer into the name binding table */ X char outseq[80]; /* output buffer for command sequence */ X X /* prompt the user to type us a key to describe */ X mlwrite(": describe-key "); X X /* get the command sequence to describe */ X c = getckey(); /* get a command sequence */ X X /* change it to something we can print as well */ X cmdstr(c, &outseq[0]); X X /* and dump it out */ X if (discmd) { X ptr = &outseq[0]; X while (*ptr) X TTputc(*ptr++); X TTputc(' '); /* space it out */ X } X X /* find the right ->function */ X if (keymatch(bindh,&c_bind,&p_bind,c)) { X nptr = names ; X strcpy(outseq,"[Bad Binding]") ; X /* search for described function */ X while (nptr->n_func) { X if (nptr->n_func == c_bind->com.command) { X strcpy(outseq,nptr->n_name) ; X break ; X } X nptr++ ; X } X } X else X strcpy(outseq,"Not bound") ; X X /* output the command sequence */ X ptr = &outseq[0]; X while (*ptr) X TTputc(*ptr++); X} X Xcmdstr(k, seq) /* change a key command to a string we can print out */ X Xchar *k, *seq; /* destination string for sequence */ X X{ X register unsigned char c ; X int i, set_spec = 0 ; X X while (c = *(k++)) { X for (i=0 ; i<8 ; i++) { X if (c == spec_c[i]) { X *seq++ = '\\' ; X *seq++ = (char)i|0x30 ; X set_spec = 1 ; X break ; X } X } X if (!set_spec) X switch (c) { X case 033 : *seq++ = '\\' ; /* ESC */ X *seq++ = 'e' ; X break ; X case '\n' : *seq++ = '\\' ; X *seq++ = 'n' ; X break ; X case '\r' : *seq++ = '\\' ; X *seq++ = 'r' ; X break ; X case '\\' : *seq++ = '\\' ; X *seq++ = '\\' ; X break ; X case '^' : *seq++ = '\\' ; X *seq++ = '^' ; X break ; X default : if (c <= 037) { /* control character */ X *seq++ = '^' ; X c += 0100 ; X } X *seq++ = c ; X } X set_spec = 0 ; X } X *seq = 0; /* terminate the string */ X} X Xhelp(f, n) /* give me some help!!!! X bring up a fake buffer and read the help file X into it with view mode */ X{ X register WINDOW *wp; /* scaning pointer to windows */ X register BUFFER *bp; /* buffer pointer to help */ X char *fname; /* ptr to file returned by flook() */ X X /* first check if we are already here */ X bp = bfind("emacs.hlp", FALSE, BFINVS); X X if (bp == NULL) { X fname = flook(pathname[1], FALSE); X if (fname == NULL) { X mlwrite("[Help file is not online]"); X return(FALSE); X } X } X X /* split the current window to make room for the help stuff */ X if (splitwind(FALSE, 1) == FALSE) X return(FALSE); X X if (bp == NULL) { X /* and read the stuff in */ X if (getfile(fname, FALSE) == FALSE) X return(FALSE); X } else X swbuffer(bp); X X /* make this window in VIEW mode, update all mode lines */ X curwp->w_bufp->b_mode |= MDVIEW; X curwp->w_bufp->b_flag |= BFINVS; X wp = wheadp; X while (wp != NULL) { X wp->w_flag |= WFMODE; X wp = wp->w_wndp; X } X return(TRUE); X} X X/* X * This values are required by for the new key binding method X */ X X#define VALID 1 X#define E_FUNC -1 X#define E_KCODE -2 X#define DEFAULT 1 X#define CCTRL -1 X#define CSPEC -2 X Xunsigned char X*getbindkey(bkeys) /* change key descriptions into actual characters */ X Xchar *bkeys ; X X{ X static unsigned char buf[20] ; X unsigned char *bufp = buf, c ; X int mode = DEFAULT, i ; X X while (c = *bkeys++) { X switch (mode) { X case CCTRL : c &= 037 ; X mode = DEFAULT ; X break ; X case CSPEC : if (isdigit(c)) X c = spec_c[c&007] ; X else X switch (c) { X case '\\' : c = '\\' ; X break ; X case 'e' : c = 033 ; /* ESC */ X break ; X case 'n' : c = '\n' ; X break ; X case 'r' : c = '\r' ; X break ; X case '^' : c = '^' ; X break ; X default : return((unsigned char *)0) ; X } X mode = DEFAULT ; X break ; X case DEFAULT : switch (c) { X case '\\' : mode = CSPEC ; X continue ; X case '^' : mode = CCTRL ; X continue ; X } X break ; X } X *bufp++ = c ; X } X *bufp = '\0' ; X return(buf) ; X} X X Xsetspecial(f,n) X X{ X char specialchar[40], value[40], c, *valuep=value ; X X if (clexec) { X macarg(specialchar) ; X macarg(value) ; X } X else { X mlwrite(": special-char ") ; X while (1) { X if ((c = tgetc()) == abortc || c == '\r') X return(FALSE) ; X if (c >= '0' && c<= '7') X break ; X putchar(007) ; X } X TTputc(c) ; X TTputc(' ') ; X *specialchar = c ; X *(specialchar+1) = '\0' ; X while (1) { X if ((c = tgetc()) == abortc) X return(FALSE) ; X if (c == '\r') X break ; X if (c >= '0' && c<= '7') { X TTputc(c) ; X *valuep++ = c ; X } X else X putchar(007) ; X } X *valuep = '\0' ; X } X spec_c[atoi(specialchar)&0x07] = atoi(value)&0xff ; X} X X Xint X(*fncmatch(fname))() /* match fname to a function in the names table X and return any match or NULL if none */ X Xchar *fname; /* name to attempt to match */ X X{ X register NBIND *ffp = names ; X X /* scan through the table, returning any match */ X while (ffp->n_func != NULL) { X if (strcmp(fname, ffp->n_name) == 0) X return(ffp->n_func); X ++ffp; X } X return(NULL); X} X X Xkeymatch(head,curr,prev,c) /* match keys to command they invoke and return X function or NULL */ X Xunsigned char *c ; Xstruct spec_chars *head ; Xstruct spec_chars **curr, **prev ; X X{ X struct spec_chars *c_bind = head, *p_bind = NULL ; X X /* scan through the lists matching a key path */ X while(*c) { X scanlist(c_bind,&c_bind,&p_bind,*c) ; X if (c_bind == NULL) X return(NULL) ; X c++ ; X if (c_bind->research) X c_bind = c_bind->com.k_codelist ; X else X if (*c) X return(NULL) ; X else { X *curr = c_bind ; X *prev = p_bind ; X return(1) ; X } X } X return(NULL) ; X} X X Xaddbinding(c,func,prev) /* add binding to list */ X Xunsigned char *c ; Xint (*func)() ; Xstruct spec_chars *prev ; X X{ X struct spec_chars *new ; X int c_pos = 1 ; X extern int quit(), quickexit(); X int bindtokey() ; X extern int exit_def ; X X /* make sure it will be possible to exit emacs after keys loaded */ X if (func == quit || func == quickexit || func == bindtokey) X exit_def = 1 ; X /* bind the key code to the function */ X while (*c) { X new = (struct spec_chars *)malloc(sizeof(struct spec_chars)) ; X new->k_code = *c ; X new->research = 0 ; X new->com.command = func ; X new->next = NULL ; X if (bindh == NULL) X /* No keys yet bound */ X bindh = new ; X else X if (c_pos == 1) X /* add to current key code list */ X prev->next = new ; X else { X /* add key code to the next level */ X prev->research = 1 ; X prev->com.k_codelist = new ; X } X c++ ; X c_pos++ ; X prev = new ; X } X} X X Xscanlist(head,curr,prev,data) X Xstruct spec_chars *head ; Xstruct spec_chars **curr, **prev ; Xunsigned char data ; X X{ X struct spec_chars *t_curr = head, *t_prev = NULL ; X X while (t_curr != NULL) { X if (t_curr->k_code == data) X break ; X t_prev = t_curr ; X t_curr = t_curr->next ; X } X *prev = t_prev ; X *curr = t_curr ; X} X X Xbindkeytofunc(kfunc,keys) X Xunsigned char *keys ; Xint (*kfunc)() ; X X{ X char c ; X struct spec_chars *c_bind = bindh, *p_bind = NULL ; X X if (!*keys) X return(E_KCODE) ; X if (kfunc) { X while (*keys) { X scanlist(c_bind,&c_bind,&p_bind,*keys) ; X if (c_bind == NULL) { X /* keys not yet bound */ X addbinding(keys,kfunc,p_bind) ; X return(VALID) ; X } X if (c_bind->com.command == dummy) { X p_bind->next = c_bind->next ; X free(c_bind) ; X addbinding(keys,kfunc,p_bind) ; X return(VALID) ; X } X keys++ ; X if (c_bind->research) X c_bind = c_bind->com.k_codelist ; X else X if (*keys) X return(E_KCODE) ; X else { X c_bind->com.command = kfunc ; X return(VALID) ; X } X } X return(E_KCODE) ; X } X else X return(E_FUNC) ; X} X X X/* bindtokey: add a new key to the key binding table */ X Xbindtokey(f, n) X Xint f, n; /* command arguments [IGNORED] */ X X{ X register unsigned char *c; X /* command key to bind */ X register (*kfunc)(); /* ptr to the requexted function to bind to */ X register char *ptr; /* ptr to dump out input key string */ X char outseq[80]; /* output buffer for keystroke sequence */ X int (*getname())(); X X /* prompt the user to type in a key to bind */ X mlwrite(": bind-to-key "); X X /* get the function name to bind it to */ X kfunc = getname(); X if (kfunc == NULL) { X mlwrite("[No such function]"); X return(FALSE); X } X if (discmd) { X TTputc(' '); /* space it out */ X TTflush(); X } X X /* get the command sequence to bind */ X c = getckey() ; X X /* change it to something we can print as well */ X cmdstr(c, &outseq[0]); X X /* and dump it out */ X if (discmd) { X ptr = &outseq[0]; X while (*ptr) X TTputc(*ptr++); X } X return(bindkeytofunc(kfunc,c)) ; X X} X X/* unbindkey: delete a key from the key binding table */ X Xunbindkey(f, n) X Xint f, n; /* command arguments [IGNORED] */ X X{ X register unsigned char *c; X /* command key to unbind */ X register char *ptr; /* ptr to dump out input key string */ X char outseq[80]; /* output buffer for keystroke sequence */ X struct spec_chars *c_bind, *p_bind ; X /* pointers in key bind list */ X X /* prompt the user to type in a key to unbind */ X mlwrite(": unbind-key "); X X /* get the command sequence to unbind */ X c = getckey(); /* get a command sequence */ X X /* change it to something we can print as well */ X cmdstr(c, &outseq[0]); X X /* and dump it out */ X if (discmd) { X ptr = &outseq[0]; X while (*ptr) X TTputc(*ptr++); X } X X /* if it isn't bound, bitch */ X if (keymatch(bindh,&c_bind,&p_bind,c)) { X p_bind->next = c_bind->next ; X free(c_bind) ; X return(TRUE) ; X } X else { X mlwrite("[Key not bound]") ; X return(FALSE) ; X } X} X X Xlistscan(bind,nptr,keys) X Xstruct spec_chars *bind ; XNBIND *nptr ; Xunsigned char *keys ; X X{ X char *fname = nptr->n_name, outc ; X unsigned char *c ; X char *alloca() ; X int keylen = strlen(keys), outlen ; X char outseq[80]; /* output buffer for keystroke sequence */ X X c = (unsigned char *)alloca(keylen+2) ; /* temp storage for keys */ X strcpy(c,keys) ; X *(c+keylen) = bind->k_code ; X *(c+keylen+1) = '\0' ; X if (bind->research) { X listscan(bind->com.k_codelist,nptr,c) ; X } X if (nptr->n_func == bind->com.command) { X cmdstr(c,outseq) ; X *(c+keylen) = '\0' ; X for (outlen=0 ; outlen<30 ; outlen++) { X if (*fname) X outc = *fname++ ; X else X outc = ' ' ; X linsert(1,outc) ; X } X fname = outseq ; X while (*fname) X linsert(1,*fname++) ; X lnewline() ; X } X if (bind->next) { X *(c+keylen) = '\0' ; X listscan(bind->next,nptr,c) ; X } X} X X Xdesbind(f, n) /* describe bindings X bring up a fake buffer and list the key bindings X into it with view mode */ X X#if APROP X{ X buildlist(TRUE, ""); X} X Xapro(f, n) /* Apropos (List functions that match a substring) */ X X{ X char mstring[NSTRING]; /* string to match cmd names to */ X int status; /* status return */ X X status = mlreply("Apropos string: ", mstring, NSTRING - 1); X if (status != TRUE) X return(status); X X return(buildlist(FALSE, mstring)); X} X Xbuildlist(type, mstring) /* build a binding list (limited or full) */ X Xint type; /* true = full list, false = partial list */ Xchar *mstring; /* match string if a partial list */ X X#endif X{ X#if ST520 & LATTICE X#define register X#endif X register WINDOW *wp; /* scanning pointer to windows */ X register NBIND *nptr; /* pointer into the name binding table */ X register BUFFER *bp; /* buffer to put binding list into */ X X /* split the current window to make room for the binding list */ X if (splitwind(FALSE, 1) == FALSE) X return(FALSE); X X /* and get a buffer for it */ X bp = bfind("Binding list", TRUE, 0); X if (bp == NULL || bclear(bp) == FALSE) { X mlwrite("Can not display binding list"); X return(FALSE); X } X X /* let us know this is in progress */ X mlwrite("[Building binding list]"); X X /* disconect the current buffer */ X if (--curbp->b_nwnd == 0) { /* Last use. */ X curbp->b_dotp = curwp->w_dotp; X curbp->b_doto = curwp->w_doto; X curbp->b_markp = curwp->w_markp; X curbp->b_marko = curwp->w_marko; X } X X /* connect the current window to this buffer */ X curbp = bp; /* make this buffer current in current window */ X bp->b_mode = 0; /* no modes active in binding list */ X bp->b_nwnd++; /* mark us as more in use */ X wp = curwp; X wp->w_bufp = bp; X wp->w_linep = bp->b_linep; X wp->w_flag = WFHARD|WFFORCE; X wp->w_dotp = bp->b_dotp; X wp->w_doto = bp->b_doto; X wp->w_markp = NULL; X wp->w_marko = 0; X X /* build the contents of this window, inserting it line by line */ X nptr = &names[0]; X while (nptr->n_func != NULL) { X X#if APROP X if (type == FALSE && X /* and current string doesn't include the search string */ X strinc(nptr->n_name, mstring) == FALSE) X goto fail; X#endif X /* search down any keys bound to this */ X X listscan(bindh,nptr,0) ; X Xfail: /* and on to the next name */ X ++nptr; X } X X curwp->w_bufp->b_mode |= MDVIEW;/* put this buffer view mode */ X curbp->b_flag &= ~BFCHG; /* don't flag this as a change */ X wp->w_dotp = lforw(bp->b_linep);/* back to the beginning */ X wp->w_doto = 0; X wp = wheadp; /* and update ALL mode lines */ X while (wp != NULL) { X wp->w_flag |= WFMODE; X wp = wp->w_wndp; X } X mlwrite(""); /* clear the mode line */ X return(TRUE); X} X X#if APROP Xstrinc(source, sub) /* does source include sub? */ X Xchar *source; /* string to search in */ Xchar *sub; /* substring to look for */ X X{ X char *sp; /* ptr into source */ X char *nxtsp; /* next ptr into source */ X char *tp; /* ptr into substring */ X X /* for each character in the source string */ X sp = source; X while (*sp) { X tp = sub; X nxtsp = sp; X X /* is the substring here? */ X while (*tp) { X if (*nxtsp++ != *tp) X break; X else X tp++; X } X X /* yes, return a success */ X if (*tp == 0) X return(TRUE); X X /* no, onward */ X sp++; X } X return(FALSE); X} X#endif X X/* get a command key sequence from the keyboard */ X Xunsigned char * Xgetckey() X X{ X int c; /* fetched keystroke */ X unsigned static char tok[20] ; X unsigned char *tokp = tok ; /* command incoming */ X struct spec_chars *binding = bindh ; X X /* check to see if we are executing a command line */ X if (clexec) { X macarg(tok); /* get the next token */ X return(getbindkey(tok)); X } X X /* or the normal way ie. from the keyboard */ X Xcharreq: X c = tgetc(); X *tokp++ = (char)c ; X /* check if key is bound */ X while (binding) { X if (binding->k_code == c) { X if (binding->research) { X binding = binding->com.k_codelist ; X goto charreq ; /* wops */ X } X if (binding->com.command == dummy) X *tokp++ = tgetc() ; X *tokp = '\0' ; X return(tok) ; X } X binding = binding->next ; X } X *tokp = '\0' ; X return(tok); X} X X/* execute the startup file */ X Xstartup(sfname) X Xchar *sfname; /* name of startup file (null if default) */ X X{ X char *fname; /* resulting file name to execute */ X X /* look up the startup file */ X if (*sfname != 0) X fname = flook(sfname, TRUE); X else X fname = flook(pathname[0], TRUE); X X /* if it isn't around, don't sweat it */ X if (fname == NULL) X return(TRUE); X X /* otherwise, execute the sucker */ X return(dofile(fname)); X} X X/* Look up the existance of a file along the normal or PATH X environment variable. Look first in the HOME directory if X asked and possible X*/ X Xchar *flook(fname, hflag) X Xchar *fname; /* base file name to search for */ Xint hflag; /* Look in the HOME environment variable first? */ X X{ X register char *home; /* path to home directory */ X register char *path; /* environmental PATH variable */ X register char *sp; /* pointer into path spec */ X register int i; /* index */ X register int status; /* return status */ X static char fspec[NSTRING]; /* full path spec to search */ X X#if ((MSDOS) & (LATTICE | AZTEC | MSC)) | V7 | USG | BSD X X if (hflag) { X home = getenv("HOME"); X if (home != NULL) { X /* build home dir file spec */ X strcpy(fspec, home); X strcat(fspec, "/"); X strcat(fspec, fname); X X /* and try it out */ X status = ffropen(fspec); X if (status == FIOSUC) { X ffclose(); X return(fspec); X } X } X } X X /* get the PATH variable */ X path = getenv("PATH"); X if (path != NULL) X while (*path) { X X /* build next possible file spec */ X sp = fspec; X while (*path && (*path != PATHCHR)) X *sp++ = *path++; X *sp++ = '/'; X *sp = 0; X strcat(fspec, fname); X X /* and try it out */ X status = ffropen(fspec); X if (status == FIOSUC) { X ffclose(); X return(fspec); X } X X if (*path == PATHCHR) X ++path; X } X#endif X X /* look it up via the old table method */ X for (i=2; i < NPNAMES; i++) { X strcpy(fspec, pathname[i]); X strcat(fspec, fname); X X /* and try it out */ X status = ffropen(fspec); X if (status == FIOSUC) { X ffclose(); X return(fspec); X } X } X X return(NULL); /* no such luck */ X} X !The!End! echo x - "isearch.c" 2>&1 sed "s/^X//" >"isearch.c" <<'!The!End!' X/* X * The functions in this file implement commands that perform incremental X * searches in the forward and backward directions. This "ISearch" command X * is intended to emulate the same command from the original EMACS X * implementation (ITS). Contains references to routines internal to X * SEARCH.C. X * X * REVISION HISTORY: X * X * D. R. Banks 9-May-86 X * - added ITS EMACSlike ISearch X * X * John M. Gamble 5-Oct-86 X * - Made iterative search use search.c's scanner() routine. X * This allowed the elimination of bakscan(). X * - Put isearch constants into estruct.h X * - Eliminated the passing of 'status' to scanmore() and X * checknext(), since there were no circumstances where X * it ever equalled FALSE. X */ X X#include X#include "estruct.h" X#include "edef.h" X X#if ISRCH X Xextern int scanner(); /* Handy search routine */ Xextern int eq(); /* Compare chars, match case */ X X/* A couple of "own" variables for re-eat */ X Xint (*saved_get_char)(); /* Get character routine */ Xint eaten_char = -1; /* Re-eaten char */ X X/* A couple more "own" variables for the command string */ X Xint cmd_buff[CMDBUFLEN]; /* Save the command args here */ Xint cmd_offset; /* Current offset into command buff */ Xint cmd_reexecute = -1; /* > 0 if re-executing command */ X X X/* X * Subroutine to do incremental reverse search. It actually uses the X * same code as the normal incremental search, as both can go both ways. X */ X Xint risearch(f, n) X{ X LINE *curline; /* Current line on entry */ X int curoff; /* Current offset on entry */ X X /* remember the initial . on entry: */ X X curline = curwp->w_dotp; /* Save the current line pointer */ X curoff = curwp->w_doto; /* Save the current offset */ X X /* Make sure the search doesn't match where we already are: */ X X backchar(TRUE, 1); /* Back up a character */ X X if (!(isearch(f, -n))) /* Call ISearch backwards */ X { /* If error in search: */ X curwp->w_dotp = curline; /* Reset the line pointer */ X curwp->w_doto = curoff; /* and the offset to original value */ X curwp->w_flag |= WFMOVE; /* Say we've moved */ X update(FALSE); /* And force an update */ X mlwrite ("[search failed]"); /* Say we died */ X } else mlerase (); /* If happy, just erase the cmd line */ X} X X/* Again, but for the forward direction */ X Xint fisearch(f, n) X{ X LINE *curline; /* Current line on entry */ X int curoff; /* Current offset on entry */ X X /* remember the initial . on entry: */ X X curline = curwp->w_dotp; /* Save the current line pointer */ X curoff = curwp->w_doto; /* Save the current offset */ X X /* do the search */ X X if (!(isearch(f, n))) /* Call ISearch forwards */ X { /* If error in search: */ X curwp->w_dotp = curline; /* Reset the line pointer */ X curwp->w_doto = curoff; /* and the offset to original value */ X curwp->w_flag |= WFMOVE; /* Say we've moved */ X update(FALSE); /* And force an update */ X mlwrite ("[search failed]"); /* Say we died */ X } else mlerase (); /* If happy, just erase the cmd line */ X} X X/* X * Subroutine to do an incremental search. In general, this works similarly X * to the older micro-emacs search function, except that the search happens X * as each character is typed, with the screen and cursor updated with each X * new search character. X * X * While searching forward, each successive character will leave the cursor X * at the end of the entire matched string. Typing a Control-S or Control-X X * will cause the next occurrence of the string to be searched for (where the X * next occurrence does NOT overlap the current occurrence). A Control-R will X * change to a backwards search, META will terminate the search and Control-G X * will abort the search. Rubout will back up to the previous match of the X * string, or if the starting point is reached first, it will delete the X * last character from the search string. X * X * While searching backward, each successive character will leave the cursor X * at the beginning of the matched string. Typing a Control-R will search X * backward for the next occurrence of the string. Control-S or Control-X X * will revert the search to the forward direction. In general, the reverse X * incremental search is just like the forward incremental search inverted. X * X * In all cases, if the search fails, the user will be feeped, and the search X * will stall until the pattern string is edited back into something that X * exists (or until the search is aborted). X */ X Xisearch(f, n) X{ X int status; /* Search status */ X int col; /* prompt column */ X register int cpos; /* character number in search string */ X register int c; /* current input character */ X char pat_save[NPAT]; /* Saved copy of the old pattern str */ X LINE *curline; /* Current line on entry */ X int curoff; /* Current offset on entry */ X int init_direction; /* The initial search direction */ X X /* Initialize starting conditions */ X X cmd_reexecute = -1; /* We're not re-executing (yet?) */ X cmd_offset = 0; /* Start at the beginning of the buff*/ X cmd_buff[0] = '\0'; /* Init the command buffer */ X strncpy (pat_save, pat, NPAT); /* Save the old pattern string */ X curline = curwp->w_dotp; /* Save the current line pointer */ X curoff = curwp->w_doto; /* Save the current offset */ X init_direction = n; /* Save the initial search direction */ X X /* This is a good place to start a re-execution: */ X Xstart_over: X X /* ask the user for the text of a pattern */ X col = promptpattern("ISearch: "); /* Prompt, remember the col */ X X cpos = 0; /* Start afresh */ X status = TRUE; /* Assume everything's cool */ X X /* X Get the first character in the pattern. If we get an initial Control-S X or Control-R, re-use the old search string and find the first occurrence X */ X X c = get_char(); /* Get the first character */ X if ((c == IS_FORWARD) || X (c == IS_REVERSE) || X (c == IS_VMSFORW)) /* Reuse old search string? */ X { X for (cpos = 0; pat[cpos] != 0; cpos++) /* Yup, find the length */ X col = echochar(pat[cpos],col); /* and re-echo the string */ X if (c == IS_REVERSE) { /* forward search? */ X n = -1; /* No, search in reverse */ X backchar (TRUE, 1); /* Be defensive about EOB */ X } else X n = 1; /* Yes, search forward */ X status = scanmore(pat, n); /* Do the search */ X c = get_char(); /* Get another character */ X } X X /* Top of the per character loop */ X X for (;;) /* ISearch per character loop*/ X { X /* Check for special characters first: */ X /* Most cases here change the search */ X X if (c == metac) /* Want to quit searching? */ X return (TRUE); /* Quit searching now */ X X switch (c) /* dispatch on the input char*/ X { X case IS_ABORT: /* If abort search request */ X return(FALSE); /* Quit searching again */ X X case IS_REVERSE: /* If backward search */ X case IS_FORWARD: /* If forward search */ X case IS_VMSFORW: /* of either flavor */ X if (c == IS_REVERSE) /* If reverse search */ X n = -1; /* Set the reverse direction */ X else /* Otherwise, */ X n = 1; /* go forward */ X status = scanmore(pat, n); /* Start the search again */ X c = get_char(); /* Get the next char */ X continue; /* Go continue with the search*/ X X case IS_NEWLINE: /* Carriage return */ X c = '\n'; /* Make it a new line */ X break; /* Make sure we use it */ X X case IS_QUOTE: /* Quote character */ X case IS_VMSQUOTE: /* of either variety */ X c = get_char(); /* Get the next char */ X X case IS_TAB: /* Generically allowed */ X case '\n': /* controlled characters */ X break; /* Make sure we use it */ X X case IS_BACKSP: /* If a backspace: */ X case IS_RUBOUT: /* or if a Rubout: */ X if (cmd_offset <= 1) /* Anything to delete? */ X return (TRUE); /* No, just exit */ X --cmd_offset; /* Back up over the Rubout */ X cmd_buff[--cmd_offset] = '\0'; /* Yes, delete last char */ X curwp->w_dotp = curline; /* Reset the line pointer */ X curwp->w_doto = curoff; /* and the offset */ X n = init_direction; /* Reset the search direction*/ X strncpy (pat, pat_save, NPAT); /* Restore the old search str*/ X cmd_reexecute = 0; /* Start the whole mess over */ X goto start_over; /* Let it take care of itself*/ X X /* Presumably a quasi-normal character comes here */ X X default: /* All other chars */ X if (c < ' ') /* Is it printable? */ X { /* Nope. */ X reeat (c); /* Re-eat the char */ X return (TRUE); /* And return the last status*/ X } X } /* Switch */ X X /* I guess we got something to search for, so search for it */ X X pat[cpos++] = c; /* put the char in the buffer*/ X if (cpos >= NPAT) /* too many chars in string? */ X { /* Yup. Complain about it */ X mlwrite("? Search string too long"); X return(TRUE); /* Return an error */ X } X pat[cpos] = 0; /* null terminate the buffer */ X col = echochar(c,col); /* Echo the character */ X if (!status) { /* If we lost last time */ X TTputc(BELL); /* Feep again */ X TTflush(); /* see that the feep feeps */ X } else /* Otherwise, we must have won*/ X if (!(status = checknext(c, pat, n))) /* See if match */ X status = scanmore(pat, n); /* or find the next match */ X c = get_char(); /* Get the next char */ X } /* for {;;} */ X} X X/* X * Trivial routine to insure that the next character in the search string is X * still true to whatever we're pointing to in the buffer. This routine will X * not attempt to move the "point" if the match fails, although it will X * implicitly move the "point" if we're forward searching, and find a match, X * since that's the way forward isearch works. X * X * If the compare fails, we return FALSE and assume the caller will call X * scanmore or something. X */ X Xint checknext (chr, patrn, dir) /* Check next character in search string */ Xchar chr; /* Next char to look for */ Xchar *patrn; /* The entire search string (incl chr) */ Xint dir; /* Search direction */ X{ X register LINE *curline; /* current line during scan */ X register int curoff; /* position within current line */ X register int buffchar; /* character at current position */ X int status; /* how well things go */ X X X /* setup the local scan pointer to current "." */ X X curline = curwp->w_dotp; /* Get the current line structure */ X curoff = curwp->w_doto; /* Get the offset within that line */ X X if (dir > 0) /* If searching forward */ X { X if (curoff == llength(curline)) /* If at end of line */ X { X curline = lforw(curline); /* Skip to the next line */ X if (curline == curbp->b_linep) X return (FALSE); /* Abort if at end of buffer */ X curoff = 0; /* Start at the beginning of the line*/ X buffchar = '\n'; /* And say the next char is NL */ X } else X buffchar = lgetc(curline, curoff++); /* Get the next char */ X if (status = eq(buffchar, chr)) /* Is it what we're looking for? */ X { X curwp->w_dotp = curline; /* Yes, set the buffer's point */ X curwp->w_doto = curoff; /* to the matched character */ X curwp->w_flag |= WFMOVE; /* Say that we've moved */ X } X return (status); /* And return the status */ X } else /* Else, if reverse search: */ X return (match_pat (patrn)); /* See if we're in the right place */ X} X X/* X * This hack will search for the next occurrence of in the buffer, either X * forward or backward. It is called with the status of the prior search X * attempt, so that it knows not to bother if it didn't work last time. If X * we can't find any more matches, "point" is left where it was before. If X * we do find a match, "point" will be at the end of the matched string for X * forward searches and at the beginning of the matched string for reverse X * searches. X */ X Xint scanmore(patrn, dir) /* search forward or back for a pattern */ Xchar *patrn; /* string to scan for */ Xint dir; /* direction to search */ X{ X int sts; /* search status */ X X if (dir < 0) /* reverse search? */ X { X rvstrcpy(tap, patrn); /* Put reversed string in tap*/ X sts = scanner(tap, REVERSE, PTBEG); X } X else X sts = scanner(patrn, FORWARD, PTEND); /* Nope. Go forward */ X X if (!sts) X { X TTputc(BELL); /* Feep if search fails */ X TTflush(); /* see that the feep feeps */ X } X X return(sts); /* else, don't even try */ X} X X/* X * The following is a worker subroutine used by the reverse search. It X * compares the pattern string with the characters at "." for equality. If X * any characters mismatch, it will return FALSE. X * X * This isn't used for forward searches, because forward searches leave "." X * at the end of the search string (instead of in front), so all that needs to X * be done is match the last char input. X */ X Xint match_pat (patrn) /* See if the pattern string matches string at "." */ Xchar *patrn; /* String to match to buffer */ X{ X register int i; /* Generic loop index/offset */ X register int buffchar; /* character at current position */ X register LINE *curline; /* current line during scan */ X register int curoff; /* position within current line */ X X /* setup the local scan pointer to current "." */ X X curline = curwp->w_dotp; /* Get the current line structure */ X curoff = curwp->w_doto; /* Get the offset within that line */ X X /* top of per character compare loop: */ X X for (i = 0; i < strlen(patrn); i++) /* Loop for all characters in patrn */ X { X if (curoff == llength(curline)) /* If at end of line */ X { X curline = lforw(curline); /* Skip to the next line */ X curoff = 0; /* Start at the beginning of the line*/ X if (curline == curbp->b_linep) X return (FALSE); /* Abort if at end of buffer */ X buffchar = '\n'; /* And say the next char is NL */ X } else X buffchar = lgetc(curline, curoff++); /* Get the next char */ X if (!eq(buffchar, patrn[i])) /* Is it what we're looking for? */ X return (FALSE); /* Nope, just punt it then */ X } X return (TRUE); /* Everything matched? Let's celebrate*/ X} X X/* Routine to prompt for I-Search string. */ X Xint promptpattern(prompt) Xchar *prompt; X{ X char tpat[NPAT+20]; X X strcpy(tpat, prompt); /* copy prompt to output string */ X strcat(tpat, " ["); /* build new prompt string */ X expandp(pat, &tpat[strlen(tpat)], NPAT/2); /* add old pattern */ X strcat(tpat, "]: "); X X /* check to see if we are executing a command line */ X if (!clexec) { X mlwrite(tpat); X } X return(strlen(tpat)); X} X X/* routine to echo i-search characters */ X Xint echochar(c,col) Xint c; /* character to be echoed */ Xint col; /* column to be echoed in */ X{ X movecursor(term.t_nrow,col); /* Position the cursor */ X if ((c < ' ') || (c == 0x7F)) /* Control character? */ X { X switch (c) /* Yes, dispatch special cases*/ X { X case '\n': /* Newline */ X TTputc('<'); X TTputc('N'); X TTputc('L'); X TTputc('>'); X col += 3; X break; X X case '\t': /* Tab */ X TTputc('<'); X TTputc('T'); X TTputc('A'); X TTputc('B'); X TTputc('>'); X col += 4; X break; X X case 0x7F: /* Rubout: */ X TTputc('^'); /* Output a funny looking */ X TTputc('?'); /* indication of Rubout */ X col++; /* Count the extra char */ X break; X X default: /* Vanilla control char */ X TTputc('^'); /* Yes, output prefix */ X TTputc(c+0x40); /* Make it "^X" */ X col++; /* Count this char */ X } X } else X TTputc(c); /* Otherwise, output raw char */ X TTflush(); /* Flush the output */ X return(++col); /* return the new column no */ X} X X/* X * Routine to get the next character from the input stream. If we're reading X * from the real terminal, force a screen update before we get the char. X * Otherwise, we must be re-executing the command string, so just return the X * next character. X */ X Xint get_char () X{ X int c; /* A place to get a character */ X X /* See if we're re-executing: */ X X if (cmd_reexecute >= 0) /* Is there an offset? */ X if ((c = cmd_buff[cmd_reexecute++]) != 0) X return (c); /* Yes, return any character */ X X /* We're not re-executing (or aren't any more). Try for a real char */ X X cmd_reexecute = -1; /* Say we're in real mode again */ X update(FALSE); /* Pretty up the screen */ X if (cmd_offset >= CMDBUFLEN-1) /* If we're getting too big ... */ X { X mlwrite ("? command too long"); /* Complain loudly and bitterly */ X return (metac); /* And force a quit */ X } X c = tgetc(); /* Get the next character */ X cmd_buff[cmd_offset++] = c; /* Save the char for next time */ X cmd_buff[cmd_offset] = '\0'; /* And terminate the buffer */ X return (c); /* Return the character */ X} X X/* X * Hacky routine to re-eat a character. This will save the character to be X * re-eaten by redirecting the input call to a routine here. Hack, etc. X */ X X/* Come here on the next term.t_getchar call: */ X Xint uneat() X{ X int c; X X term.t_getchar = saved_get_char; /* restore the routine address */ X c = eaten_char; /* Get the re-eaten char */ X eaten_char = -1; /* Clear the old char */ X return(c); /* and return the last char */ X} X Xint reeat(c) Xint c; X{ X if (eaten_char != -1) /* If we've already been here */ X return/*(NULL)*/; /* Don't do it again */ X eaten_char = c; /* Else, save the char for later */ X saved_get_char = term.t_getchar; /* Save the char get routine */ X term.t_getchar = uneat; /* Replace it with ours */ X} X#else Xisearch() X{ X} X#endif !The!End! exit