Path: utzoo!utgpu!cs.utexas.edu!usc!rpi!think.com!cayman!pgf From: pgf@cayman.COM (Paul Fox) Newsgroups: alt.sources Subject: Vile 08/17 - vi feel-alike (multi-window) Message-ID: <4527@cayman.COM> Date: 7 Jun 91 22:09:42 GMT Organization: Cayman Systems Inc., Cambridge Ma Lines: 2075 Submitted-by: pgf@cayman.com Archive-name: Vile/part08 #!/bin/sh # this is vileshar.08 (part 8 of Vile) # do not concatenate these parts, unpack them in order with /bin/sh # file input.c continued # if test ! -r _shar_seq_.tmp; then echo 'Please unpack part 1 first!' exit 1 fi (read Scheck if test "$Scheck" != 8; then echo Please unpack part "$Scheck" next! exit 1 else exit 0 fi ) < _shar_seq_.tmp || exit 1 echo 'x - continuing file input.c' sed 's/^X//' << 'SHAR_EOF' >> 'input.c' && X * macro throw the prompt away, and return the remembered response. This X * lets macros run at full speed. The reply is always terminated by a carriage X * return. Handle erase, kill, and abort keys. X */ X mlreply(prompt, buf, bufn) char *prompt; char *buf; { X return kbd_string(prompt, buf, bufn, '\n',EXPAND); } X /* as above, but don't expand special punctuation, like #, %, ~, etc. */ mlreply_no_exp(prompt, buf, bufn) char *prompt; char *buf; { X return kbd_string(prompt, buf, bufn, '\n',NO_EXPAND); } X /* kcod2key: translate 10-bit keycode to single key value */ /* probably defined as a macro in estruct.h */ #ifndef kcod2key kcod2key(c) int c; { X return c & 0x7f; } #endif X X /* the numbered buffer names increment each time they are referenced */ incr_dot_kregnum() { X if (dotcmdmode == PLAY) { X if (isdigit(*dotcmdptr) && *dotcmdptr < '9') X (*dotcmdptr)++; X } } X int tungotc = -1; X tungetc(c) { X tungotc = c; } X tpeekc() { X return tungotc; } X /* tgetc: Get a key from the terminal driver, resolve any keyboard X macro action */ int tgetc() { X int c; /* fetched character */ X X if (dotcmdmode == PLAY) { X X if (interrupted) { X dotcmdmode = STOP; X return (kcod2key(abortc)); X } X X /* if there is some left... */ X if (dotcmdptr < dotcmdend) X return(*dotcmdptr++); X X /* at the end of last repitition? */ X if (--dotcmdrep < 1) { X dotcmdmode = RECORD; X tmpcmdptr = &tmpcmdm[0]; X tmpcmdend = tmpcmdptr; #if VISMAC == 0 X /* force a screen update after all is done */ X update(FALSE); #endif X } else { X X /* reset the macro to the begining for the next rep */ X dotcmdptr = &dotcmdm[0]; X return((int)*dotcmdptr++); X } X } else if (kbdmode == PLAY) { X /* if we are playing a keyboard macro back, */ X X /* if there is some left... */ X if (kbdptr < kbdend) X return((int)*kbdptr++); X X /* at the end of last repitition? */ X if (--kbdrep < 1) { X kbdmode = STOP; #if VISMAC == 0 X /* force a screen update after all is done */ X update(FALSE); #endif X } else { X X /* reset the macro to the begining for the next rep */ X kbdptr = &kbdm[0]; X return((int)*kbdptr++); X } X } X X X if (tungotc >= 0) { X c = tungotc; X tungotc = -1; X } else { /* fetch a character from the terminal driver */ X interrupted = 0; X c = TTgetc(); X if (c == -1 || c == kcod2key(intrc)) { X c = kcod2key(abortc); X return lastkey = c; X } X } X X /* save it if we need to */ X if (dotcmdmode == RECORD) { X *tmpcmdptr++ = c; X tmpcmdend = tmpcmdptr; X X /* don't overrun the buffer */ X /* (we're recording, so must be using tmp) */ X if (tmpcmdptr == &tmpcmdm[NKBDM - 1]) { X dotcmdmode = STOP; X TTbeep(); X } X } X X /* save it if we need to */ X if (kbdmode == RECORD) { X *kbdptr++ = c; X kbdend = kbdptr; X X /* don't overrun the buffer */ X if (kbdptr == &kbdm[NKBDM - 1]) { X kbdmode = STOP; X TTbeep(); X } X } X X /* and finally give the char back */ X /* record it for $lastkey */ X return(lastkey = c); } X /* KBD_KEY: Get one keystroke. The only prefix legal here X is the SPEC prefix. */ kbd_key() { X int c; X X /* get a keystroke */ X c = tgetc(); X #if MSDOS | ST520 X if (c == 0) { /* Apply SPEC prefix */ X c = tgetc(); X if (insertmode) continue; X return(last1key = SPEC | c); X } #endif X #if AMIGA X /* apply SPEC prefix */ X if ((unsigned)c == 155) { X int d; X c = tgetc(); X X /* first try to see if it is a cursor key */ X if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T') { X if (insertmode) continue; X return(last1key = SPEC | c); X } X X /* next, a 2 char sequence */ X d = tgetc(); X if (d == '~') { X if (insertmode) continue; X return(last1key = SPEC | c); X } X X /* decode a 3 char sequence */ X c = d + ' '; X /* if a shifted function key, eat the tilde */ X if (d >= '0' && d <= '9') X d = tgetc(); X if (insertmode) continue; X return(last1key = SPEC | c); X } #endif X #if WANGPC X if (c == 0x1F) { /* Apply SPEC prefix */ X c = tgetc(); X if (insertmode) continue; X return(last1key = SPEC | c); X } #endif X X return (last1key = c); } X /* KBD_SEQ: Get a command sequence (multiple keystrokes) from X the keyboard. X Process all applicable prefix keys. X Set lastcmd for commands which want stuttering. */ kbd_seq() { X int c; /* fetched keystroke */ X X /* get initial character */ X c = kbd_key(); X X /* process CTLA prefix */ X if (c == cntl_a) { X c = kbd_key(); #if BEFORE X if (islower(c)) /* Force to upper */ X c = toupper(c); #endif X return (lastcmd = CTLA | c); X } X X /* process CTLX prefix */ X if (c == cntl_x) { X c = kbd_key(); X return (lastcmd = CTLX | c); X } X X /* otherwise, just return it */ X return (lastcmd = c); } X screen_string(buf,bufn,inclchartype) char *buf; { X register int i = 0; X register int s = TRUE; X X setmark(); X while (s == TRUE && i < bufn && X curwp->w_doto != llength(curwp->w_dotp)) { X buf[i] = lgetc(curwp->w_dotp, curwp->w_doto); X if (!istype(inclchartype, buf[i])) X break; X s = forwchar(FALSE, 1); X i++; X } X buf[i] = '\0'; X swapmark(); X X return buf[0] != '\0'; } X /* A more generalized prompt/reply function allowing the caller X to specify a terminator other than '\n'. Both are accepted. X Assumes the buffer already contains a valid (possibly X null) string to use as the default response. */ kbd_string(prompt, buf, bufn, eolchar, expand) char *prompt; char *buf; int eolchar; { X register int cpos; /* current character position in string */ X register int c; X register int quotef; /* are we quoting the next char? */ X int firstchar = TRUE; X X if (clexec) X return nextarg(buf); X X lineinput = TRUE; X X cpos = 0; X quotef = FALSE; X X X /* prompt the user for the input string */ X mlwrite(prompt); X /* put out the default response, which comes in already in the X buffer */ X while((c = buf[cpos]) != '\0' && cpos < bufn-1) { X if (!isprint(c)) { X if (disinp) X TTputc('^'); X ++ttcol; X c = toalpha(c); X } X X if (disinp) X TTputc(c); X ++ttcol; X ++cpos; X } X TTflush(); X X for (;;) { X /* get a character from the user */ X c = kbd_key(); X X /* If it is a , change it to a */ X if (c == '\r' && quotef == FALSE) X c = '\n'; X X /* if they hit the line terminate, wrap it up */ X /* don't allow newlines in the string -- they cause real X problems, especially when searching for patterns X containing them -pgf */ X /* never a newline, and only eolchar if ^V or \ precedes it */ X if (c == '\n' || (c == eolchar && X quotef == FALSE && cpos > 0 && buf[cpos-1] != '\\')) X { X if (buf[cpos] != '\0') X buf[cpos++] = 0; X X lineinput = FALSE; X /* if buffer is empty, return FALSE */ X if (buf[0] == 0) X return(FALSE); X X return(TRUE); X } X #if NeWS /* make sure cursor is where we think it is before output */ X TTmove(ttrow,ttcol) ; #endif X /* change from command form back to character form */ X c = kcod2key(c); X X if (c == kcod2key(abortc) && quotef == FALSE) { X buf[cpos] = 0; X esc(FALSE, 0); X TTflush(); X lineinput = FALSE; X return ABORT; X } else if (isbackspace(c) && quotef==FALSE) { X /* rubout/erase */ X if (cpos != 0) { X outstring("\b \b"); X --ttcol; X X if (!isprint(buf[--cpos])) { X outstring("\b \b"); X --ttcol; X } X X TTflush(); X } else { X buf[0] = 0; X mlerase(); X lineinput = FALSE; X return FALSE; X } X X } else if (c == kcod2key(killc) && quotef == FALSE) { X /* C-U, kill */ X killit: X while (cpos != 0) { X outstring("\b \b"); X --ttcol; X X if (!isprint(buf[--cpos])) { X outstring("\b \b"); X --ttcol; X } X } X TTflush(); X X } else if (expand == EXPAND) { X /* we prefer to expand to filenames, but a buffer name X will do */ X char *cp = NULL; X char *hist_lookup(); X if (firstchar == TRUE) { X tungetc(c); X goto killit; X } X switch(c) { X case '%': X cp = curbp->b_fname; X if (!*cp || isspace(*cp)) X cp = curbp->b_bname; X break; X case '#': X cp = hist_lookup(1); /* returns buffer name */ X if (cp) { X /* but we want a file */ X BUFFER *bp; X bp = bfind(cp,NO_CREAT,0); X cp = bp->b_fname; X if (!*cp || isspace(*cp)) { X /* oh well, use the buffer */ X cp = bp->b_bname; X } X } X break; X /* case tocntrl('W'): */ X case ':': X { X char str[80]; X if (screen_string(str, 80, _path)) X cp = str; X break; X } X default: X goto trymore; X } X X if (!cp) { X TTbeep(); X continue; X } X while (cpos < bufn-1 && (c = *cp++)) { X buf[cpos++] = c; X if (!isprint(c)) { X outstring("^"); X ++ttcol; X c = toalpha(c); X } X if (disinp) X TTputc(c); X ++ttcol; X } X TTflush(); X } else { X trymore: X if (c == kcod2key(quotec) && quotef == FALSE) { X quotef = TRUE; X } else { X quotef = FALSE; X if (firstchar == TRUE) { X tungetc(c); X goto killit; X } X if (cpos < bufn-1) { X buf[cpos++] = c; X X if (!isprint(c)) { X outstring("^"); X ++ttcol; X c = toalpha(c); X } X X if (disinp) X TTputc(c); X X ++ttcol; X TTflush(); X } X } X } X firstchar = FALSE; X } } X outstring(s) /* output a string of input characters */ char *s; /* string to output */ { X if (disinp) X while (*s) X TTputc(*s++); } X ostring(s) /* output a string of output characters */ char *s; /* string to output */ { X if (discmd) X while (*s) X TTputc(*s++); } SHAR_EOF echo 'File input.c is complete' && chmod 0444 input.c || echo 'restore of input.c failed' Wc_c="`wc -c < 'input.c'`" test 10225 -eq "$Wc_c" || echo 'input.c: original size 10225, current size' "$Wc_c" # ============= isearch.c ============== echo 'x - extracting isearch.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'isearch.c' && /* vile note, 6/1/91, pgf -- I haven't tried this code in a long time */ /* 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 #include "estruct.h" #include "edef.h" X #if ISRCH X extern int thescanner(); /* Handy search routine */ extern int eq(); /* Compare chars, match case */ X /* A couple of "own" variables for re-eat */ X int (*saved_get_char)(); /* Get character routine */ int eaten_char = -1; /* Re-eaten char */ X /* A couple more "own" variables for the command string */ X int cmd_buff[CMDBUFLEN]; /* Save the command args here */ int cmd_offset; /* Current offset into command buff */ int cmd_reexecute = -1; /* > 0 if re-executing command */ 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 int risearch(f, n) { 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 #if NeWS X newsimmediateon() ; #endif 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 TTbeep(); X } else X mlerase (); /* If happy, just erase the cmd line */ X #if NeWS X newsimmediateoff() ; #endif } X /* Again, but for the forward direction */ X int fisearch(f, n) { 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 #if NeWS X newsimmediateon() ; #endif 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 TTbeep(); X } else X mlerase (); /* If happy, just erase the cmd line */ X #if NeWS X newsimmediateoff() ; #endif } 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 isearch(f, n) { 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 setboundry(FALSE,NULL,0,0); /* keep thescanner() finite */ X X /* This is a good place to start a re-execution: */ X start_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 = kcod2key(get_char()); /* Get the first character */ X if ((c == IS_FORWARD) || X (c == IS_REVERSE)) /* 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 = kcod2key(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 == kcod2key(abortc)) /* Want to quit searching? */ X return (TRUE); /* Quit searching now */ X X if (isbackspace(c)) X c = '\b'; X X if (c == kcod2key(quotec)) /* quote character? */ X c = kcod2key(get_char()); /* Get the next char */ X X switch (c) /* dispatch on the input char */ X { X case IS_REVERSE: /* If backward search */ X case IS_FORWARD: /* If forward search */ 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 = kcod2key(get_char()); /* Get the next char */ X continue; /* Go continue with the search*/ X X case '\r': /* Carriage return */ X c = '\n'; /* Make it a new line */ X break; /* Make sure we use it */ X X case '\t': /* Generically allowed */ X case '\n': /* controlled characters */ X break; /* Make sure we use it */ X X case '\b': /* 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 TTbeep(); /* 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 = kcod2key(get_char()); /* Get the next char */ X } /* for {;;} */ } 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 int checknext (chr, patrn, dir) /* Check next character in search string */ char chr; /* Next char to look for */ char *patrn; /* The entire search string (incl chr) */ int dir; /* Search direction */ { 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 * 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 int scanmore(patrn, dir) /* search forward or back for a pattern */ char *patrn; /* string to scan for */ int dir; /* direction to search */ { X int sts; /* search status */ X X if (dir < 0) /* reverse search? */ X { X rvstrcpy(tap, patrn); /* Put reversed string in tap */ X sts = thescanner(tap, REVERSE, PTBEG, FALSE); X } X else /* Nope. Go forward */ X sts = thescanner(patrn, FORWARD, PTEND, FALSE); X X if (!sts) X { X TTbeep(); /* Feep if search fails */ X TTflush(); /* see that the feep feeps */ X } X X return(sts); /* else, don't even try */ } 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 int match_pat (patrn) /* See if the pattern string matches string at "." */ char *patrn; /* String to match to buffer */ { 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 /* Routine to prompt for I-Search string. */ X int promptpattern(prompt) char *prompt; { 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 /* routine to echo i-search characters */ X int echochar(c,col) int c; /* character to be echoed */ int col; /* column to be echoed in */ { X movecursor(term.t_nrow,col); /* Position the cursor */ X if (!isprint(c)) { /* control char */ X TTputc('^'); /* Yes, output prefix */ X TTputc(toalpha(c)); /* Make it "^X" */ X col++; /* Count this char */ X } else { X TTputc(c); /* Otherwise, output raw char */ X } X TTflush(); /* Flush the output */ X return(++col); /* return the new column no */ } 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 int get_char () { 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 (abortc); /* And force a quit */ X } X c = kbd_key(); /* 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 * 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 /* Come here on the next term.t_getchar call: */ X int uneat() { 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 int reeat(c) int c; { 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 */ } #else isearch() { } #endif SHAR_EOF chmod 0444 isearch.c || echo 'restore of isearch.c failed' Wc_c="`wc -c < 'isearch.c'`" test 18075 -eq "$Wc_c" || echo 'isearch.c: original size 18075, current size' "$Wc_c" # ============= line.c ============== echo 'x - extracting line.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'line.c' && /* X * The functions in this file are a general set of line management utilities. X * They are the only routines that touch the text. They also touch the buffer X * and window structures, to make sure that the necessary updating gets done. X * There are routines in this file that handle the kill register too. It isn't X * here for any good reason. X * X * Note that this code only updates the dot and mark values in the window list. X * Since all the code acts on the current window, the buffer that we are X * editing must be being displayed, which means that "b_nwnd" is non zero, X * which means that the dot and mark values in the buffer headers are nonsense. X */ X #include #include "estruct.h" #include "edef.h" X #define roundup(n) ((n+NBLOCK-1) & ~(NBLOCK-1)) /* X * This routine allocates a block of memory large enough to hold a LINE X * containing "used" characters. The block is always rounded up a bit. Return X * a pointer to the new block, or NULL if there isn't any memory left. Print a X * message in the message line if no space. X */ LINE * lalloc(used) register int used; { X register LINE *lp; X register int size; X X /* lalloc(-1) is used by undo for placeholders */ X if (used < 0) { X size = 0; X } else { X size = roundup(used); X if (size == 0) /* Assume that an empty */ X size = NBLOCK; /* line is for type-in. */ X } X /* malloc 4 less, because struct LINE is 4 too big */ X if ((lp = (LINE *) malloc(sizeof(LINE))) == NULL) { X mlwrite("[OUT OF MEMORY]"); X return NULL; X } X if ((lp->l_text = malloc(size)) == NULL) { X mlwrite("[OUT OF MEMORY]"); X free((char *)lp); X return NULL; X } X lp->l_size = size; X lp->l_used = used; X lsetclear(lp); X lp->l_nxtundo = NULL; X return (lp); } X lfree(lp) register LINE *lp; { X if (lp->l_text) X free(lp->l_text); X free((char *)lp); } X /* X * Delete line "lp". Fix all of the links that might point at it (they are X * moved to offset 0 of the next line. Unlink the line from whatever buffer it X * might be in. The buffers are updated too; the magic X * conditions described in the above comments don't hold here. X * Memory is not released, so line can be saved in undo stacks. X */ lremove(bp,lp) register BUFFER *bp; register LINE *lp; { X register WINDOW *wp; X X wp = wheadp; X while (wp != NULL) { X if (wp->w_linep == lp) X wp->w_linep = lp->l_fp; X if (wp->w_dotp == lp) { X wp->w_dotp = lp->l_fp; X wp->w_doto = 0; X } X if (wp->w_mkp == lp) { X wp->w_mkp = lp->l_fp; X wp->w_mko = 0; X } #if 0 X if (wp->w_ldmkp == lp) { X wp->w_ldmkp = lp->l_fp; X wp->w_ldmko = 0; X } #endif X wp = wp->w_wndp; X } X if (bp->b_nwnd == 0) { X if (bp->b_dotp == lp) { X bp->b_dotp = lp->l_fp; X bp->b_doto = 0; X } X if (bp->b_markp == lp) { X bp->b_markp = lp->l_fp; X bp->b_marko = 0; X } #if 0 X if (bp->b_ldmkp == lp) { X bp->b_ldmkp = lp->l_fp; X bp->b_ldmko = 0; X } #endif X } #if 0 X if (bp->b_nmmarks != NULL) { /* fix the named marks */ X int i; X struct MARK *mp; X for (i = 0; i < 26; i++) { X mp = &(bp->b_nmmarks[i]); X if (mp->markp == lp) { X mp->markp = lp->l_fp; X mp->marko = 0; X } X } X } #endif X lp->l_bp->l_fp = lp->l_fp; X lp->l_fp->l_bp = lp->l_bp; } X /* X * This routine gets called when a character is changed in place in the current X * buffer. It updates all of the required flags in the buffer and window X * system. The flag used is passed as an argument; if the buffer is being X * displayed in more than 1 window we change EDIT to HARD. Set MODE if the X * mode line needs to be updated (the "*" has to be set). X */ lchange(flag) register int flag; { X register WINDOW *wp; X X if (curbp->b_nwnd != 1) { /* Ensure hard. */ X flag |= WFHARD; X } X if ((curbp->b_flag&BFCHG) == 0) { /* First change, so */ X flag |= WFMODE; /* update mode lines. */ X curbp->b_flag |= BFCHG; X } X wp = wheadp; X while (wp != NULL) { X if (wp->w_bufp == curbp) X wp->w_flag |= flag; X wp = wp->w_wndp; X } } X insspace(f, n) /* insert spaces forward into text */ int f, n; /* default flag and numeric argument */ { X linsert(n, ' '); X backchar(f, n); } X /* X * Insert "n" copies of the character "c" at the current location of dot. In X * the easy case all that happens is the text is stored in the line. In the X * hard case, the line has to be reallocated. When the window list is updated, X * take special care; I screwed it up once. You always update dot in the X * current window. You update mark, and a dot in another window, if it is X * greater than the place where you did the insert. Return TRUE if all is X * well, and FALSE on errors. X */ linsert(n, c) { X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register LINE *lp3; X register int doto; X register int i; X register WINDOW *wp; X register char *ntext; X int nsize; X X lchange(WFEDIT); X lp1 = curwp->w_dotp; /* Current line */ X if (lp1 == curbp->b_linep) { /* At the end: special */ X if (curwp->w_doto != 0) { X mlwrite("bug: linsert"); X return (FALSE); X } X if ((lp2=lalloc(n)) == NULL) /* Allocate new line */ X return (FALSE); X copy_for_undo(lp1->l_bp); /* don't want preundodot to point X * at a new line if this is the X * first change */ X lp3 = lp1->l_bp; /* Previous line */ X lp3->l_fp = lp2; /* Link in */ X lp2->l_fp = lp1; X lp1->l_bp = lp2; X lp2->l_bp = lp3; X for (i=0; il_text[i] = c; X curwp->w_dotp = lp2; X curwp->w_doto = n; X tag_for_undo(lp2); X return (TRUE); X } X doto = curwp->w_doto; /* Save for later. */ X if (lp1->l_used+n > lp1->l_size) { /* Hard: reallocate */ X copy_for_undo(lp1); X /* first, create the new image */ X if ((ntext=malloc(nsize = roundup(lp1->l_used+n))) == NULL) X return (FALSE); X memcpy(&ntext[0], &lp1->l_text[0], doto); X memset(&ntext[doto], c, n); X memcpy(&ntext[doto+n], &lp1->l_text[doto], lp1->l_used-doto ); X free((char *)lp1->l_text); X lp1->l_text = ntext; X lp1->l_size = nsize; X lp1->l_used += n; X } else { /* Easy: in place */ X copy_for_undo(lp1); X /* don't used memcpy: overlapping regions.... */ X lp1->l_used += n; X cp2 = &lp1->l_text[lp1->l_used]; X cp1 = cp2-n; X while (cp1 != &lp1->l_text[doto]) X *--cp2 = *--cp1; X for (i=0; il_text[doto+i] = c; X } X wp = wheadp; /* Update windows */ X while (wp != NULL) { X if (wp->w_dotp == lp1) { X if (wp==curwp || wp->w_doto>doto) X wp->w_doto += n; X } X if (wp->w_mkp == lp1) { X if (wp->w_mko > doto) X wp->w_mko += n; X } X if (wp->w_ldmkp == lp1) { X if (wp->w_ldmko > doto) X wp->w_ldmko += n; X } X wp = wp->w_wndp; X } X if (curbp->b_nmmarks != NULL) { /* fix the named marks */ X struct MARK *mp; X for (i = 0; i < 26; i++) { X mp = &(curbp->b_nmmarks[i]); X if (mp->markp == lp1) { X if (mp->marko > doto) X mp->marko += n; X } X } X } X return (TRUE); } X /* X * Insert a newline into the buffer at the current location of dot in the X * current window. The funny ass-backwards way it does things is not a botch; X * it just makes the last line in the file not a special case. Return TRUE if X * everything works out and FALSE on error (memory allocation failure). The X * update of dot and mark is a bit easier then in the above case, because the X * split forces more updating. X */ lnewline() { X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register int doto; X register WINDOW *wp; X X lchange(WFHARD|WFINS); X lp1 = curwp->w_dotp; /* Get the address and */ X doto = curwp->w_doto; /* offset of "." */ X if (lp1 != curbp->b_linep) X copy_for_undo(lp1); X if ((lp2=lalloc(doto)) == NULL) /* New first half line */ X return (FALSE); X cp1 = &lp1->l_text[0]; /* Shuffle text around */ X cp2 = &lp2->l_text[0]; X while (cp1 != &lp1->l_text[doto]) X *cp2++ = *cp1++; X cp2 = &lp1->l_text[0]; X while (cp1 != &lp1->l_text[lp1->l_used]) X *cp2++ = *cp1++; X lp1->l_used -= doto; X /* put lp2 in above lp1 */ X lp2->l_bp = lp1->l_bp; X lp1->l_bp = lp2; X lp2->l_bp->l_fp = lp2; X lp2->l_fp = lp1; X tag_for_undo(lp2); X dumpuline(lp1); X wp = wheadp; /* Windows */ X while (wp != NULL) { X if (wp->w_linep == lp1) X wp->w_linep = lp2; X if (wp->w_dotp == lp1) { X if (wp->w_doto < doto) X wp->w_dotp = lp2; X else X wp->w_doto -= doto; X } X if (wp->w_mkp == lp1) { X if (wp->w_mko < doto) X wp->w_mkp = lp2; X else X wp->w_mko -= doto; X } X if (wp->w_ldmkp == lp1) { X if (wp->w_ldmko < doto) X wp->w_ldmkp = lp2; X else X wp->w_ldmko -= doto; X } X wp = wp->w_wndp; X } X if (curbp->b_nmmarks != NULL) { /* fix the named marks */ X int i; X struct MARK *mp; X for (i = 0; i < 26; i++) { X mp = &(curbp->b_nmmarks[i]); X if (mp->markp == lp1) { X if (mp->marko < doto) X mp->markp = lp2; X else X mp->marko -= doto; X } X } X } X return (TRUE); } X /* X * This function deletes "n" bytes, starting at dot. It understands how do deal X * with end of lines, etc. It returns TRUE if all of the characters were X * deleted, and FALSE if they were not (because dot ran into the end of the X * buffer. The "kflag" is TRUE if the text should be put in the kill buffer. X */ ldelete(n, kflag) long n; /* # of chars to delete */ int kflag; /* put killed text in kill buffer flag */ { X register char *cp1; X register char *cp2; X register LINE *dotp; X register LINE *nlp; X register int doto; X register int chunk; X register WINDOW *wp; X register int i,s; X X while (n != 0) { X dotp = curwp->w_dotp; X doto = curwp->w_doto; X if (dotp == curbp->b_linep) /* Hit end of buffer. */ X return (FALSE); X chunk = dotp->l_used-doto; /* Size of chunk. */ X if (chunk > (int)n) X chunk = (int)n; X if (chunk == 0) { /* End of line, merge. */ X lchange(WFHARD|WFKILLS); X /* first take out any whole lines below this one */ X nlp = lforw(dotp); X while (nlp != curbp->b_linep && llength(nlp)+1 < n) { X if (kflag) { X s = kinsert('\n'); X for (i = 0; i < llength(nlp) && X s == TRUE; i++) X s = kinsert(lgetc(nlp,i)); X if (s != TRUE) X return(FALSE); X } X lremove(curbp,nlp); X toss_to_undo(nlp); X n -= llength(nlp)+1; X nlp = lforw(dotp); X } X if ((s = ldelnewline()) != TRUE) X return (s); X if (kflag && (s = kinsert('\n')) != TRUE) X return (s); X --n; X continue; X } X lchange(WFEDIT); X copy_for_undo(dotp); X cp1 = &dotp->l_text[doto]; /* Scrunch text. */ X cp2 = cp1 + chunk; X if (kflag) { /* Kill? */ X while (cp1 != cp2) { X if ((s = kinsert(*cp1)) != TRUE) X return (s); X ++cp1; X } X cp1 = &dotp->l_text[doto]; X } X while (cp2 != &dotp->l_text[dotp->l_used]) X *cp1++ = *cp2++; X dotp->l_used -= chunk; X wp = wheadp; /* Fix windows */ X while (wp != NULL) { X if (wp->w_dotp==dotp && wp->w_doto > doto) { X wp->w_doto -= chunk; X if (wp->w_doto < doto) X wp->w_doto = doto; X } X if (wp->w_mkp==dotp && wp->w_mko > doto) { X wp->w_mko -= chunk; X if (wp->w_mko < doto) X wp->w_mko = doto; X } X if (wp->w_ldmkp==dotp && wp->w_ldmko > doto) { X wp->w_ldmko -= chunk; X if (wp->w_ldmko < doto) X wp->w_ldmko = doto; X } X wp = wp->w_wndp; X } X if (curbp->b_nmmarks != NULL) { /* fix the named marks */ X struct MARK *mp; X for (i = 0; i < 26; i++) { X mp = &(curbp->b_nmmarks[i]); X if (mp->markp==dotp && mp->marko > doto) { X mp->marko -= chunk; X if (mp->marko < doto) X mp->marko = doto; X } X } X } X n -= chunk; X } X return (TRUE); } X /* getctext: grab and return a string with the text of X the current line */ X char *getctext() X { X register LINE *lp; /* line to copy */ X register int size; /* length of line to return */ X register char *sp; /* string pointer into line */ X register char *dp; /* string pointer into returned line */ X char rline[NSTRING]; /* line to return */ X X /* find the contents of the current line and its length */ X lp = curwp->w_dotp; X sp = lp->l_text; X size = lp->l_used; X if (size >= NSTRING) X size = NSTRING - 1; X X /* copy it across */ X dp = rline; X while (size--) X *dp++ = *sp++; X *dp = 0; X return(rline); } X #if ! SMALLER /* putctext: replace the current line with the passed in text */ X putctext(iline) char *iline; /* contents of new line */ { X register int status; X X /* delete the current line */ X curwp->w_doto = 0; /* starting at the beginning of the line */ X if ((status = deltoeol(TRUE, 1)) != TRUE) X return(status); X X /* insert the new line */ X while (*iline) { X if (*iline == '\n') { X if (lnewline() != TRUE) X return(FALSE); X } else { X if (linsert(1, *iline) != TRUE) X return(FALSE); X } X ++iline; X } X status = lnewline(); X backline(TRUE, 1); X return(status); } #endif X /* X * Delete a newline. Join the current line with the next line. If the next line X * is the magic header line always return TRUE; merging the last line with the X * header line can be thought of as always being a successful operation, even X * if nothing is done, and this makes the kill buffer work "right". Easy cases X * can be done by shuffling data around. Hard cases require that lines be moved X * about in memory. Return FALSE on error and TRUE if all looks ok. Called by X * "ldelete" only. X */ ldelnewline() { X register char *cp1; X register char *cp2; X register LINE *lp1; X register LINE *lp2; X register LINE *lp3; X register WINDOW *wp; X X lp1 = curwp->w_dotp; X /* if the current line is empty, remove it */ X if (lp1->l_used == 0) { /* Blank line. */ X lremove(curbp,lp1); X toss_to_undo(lp1); X return (TRUE); X } X lp2 = lp1->l_fp; X /* if the next line is empty, that's "currline\n\n", so we X remove the second \n by deleting the next line */ X /* but never delete the newline on the last non-empty line */ X if (lp2 == curbp->b_linep) X return (TRUE); X else if (lp2->l_used == 0) { X /* next line blank? */ X lremove(curbp,lp2); X toss_to_undo(lp2); X return (TRUE); X } X /* no room in line above, make room */ X if (lp2->l_used > lp1->l_size-lp1->l_used) { X char *ntext; X int nsize; X copy_for_undo(lp1); X /* first, create the new image */ X if ((ntext=malloc(nsize = roundup(lp1->l_used + lp2->l_used))) X == NULL) X return (FALSE); X memcpy(&ntext[0], &lp1->l_text[0], lp1->l_used); X free((char *)lp1->l_text); X lp1->l_text = ntext; X } X cp1 = &lp1->l_text[lp1->l_used]; X cp2 = &lp2->l_text[0]; X while (cp2 != &lp2->l_text[lp2->l_used]) X *cp1++ = *cp2++; X /* check all windows for references to the deleted line */ X wp = wheadp; X while (wp != NULL) { X if (wp->w_linep == lp2) X wp->w_linep = lp1; X if (wp->w_dotp == lp2) { X wp->w_dotp = lp1; X wp->w_doto += lp1->l_used; X } X if (wp->w_mkp == lp2) { X wp->w_mkp = lp1; X wp->w_mko += lp1->l_used; X } X if (wp->w_ldmkp == lp2) { X wp->w_ldmkp = lp1; X wp->w_ldmko += lp1->l_used; X } X wp = wp->w_wndp; X } X if (curbp->b_nmmarks != NULL) { /* fix the named marks */ X int i; X struct MARK *mp; X for (i = 0; i < 26; i++) { X mp = &(curbp->b_nmmarks[i]); X if (mp->markp == lp2) { X mp->markp = lp1; X mp->marko += lp1->l_used; X } X } X } X lp1->l_used += lp2->l_used; X lp1->l_fp = lp2->l_fp; X lp2->l_fp->l_bp = lp1; X dumpuline(lp1); X toss_to_undo(lp2); X return (TRUE); } X /* X * Delete all of the text saved in the kill buffer. Called by commands when a X * new kill context is being created. The kill buffer array is released, just X * in case the buffer has grown to immense size. No errors. X */ kdelete() { X X if ((kregflag & KAPPEND) != 0) X kregflag = KAPPEND; X else X kregflag = KNEEDCLEAN; X } X /* X * Insert a character to the kill buffer, allocating new chunks as needed. X * Return TRUE if all is well, and FALSE on errors. X */ X kinsert(c) int c; /* character to insert in the kill buffer */ { X KILL *nchunk; /* ptr to newly malloced chunk */ X KILLREG *kbp = &kbs[ukb]; X X if ((kregflag & KNEEDCLEAN) && kbs[ukb].kbufh != NULL) { X KILL *kp; /* ptr to scan kill buffer chunk list */ X X /* first, delete all the chunks */ X kbs[ukb].kbufp = kbs[ukb].kbufh; X while (kbs[ukb].kbufp != NULL) { X kp = kbs[ukb].kbufp->d_next; X free((char *)(kbs[ukb].kbufp)); X kbs[ukb].kbufp = kp; X } X X /* and reset all the kill buffer pointers */ X kbs[ukb].kbufh = kbs[ukb].kbufp = NULL; X kbs[ukb].kused = KBLOCK; X X } X kregflag &= ~KNEEDCLEAN; X kbs[ukb].kbflag = kregflag; X X /* check to see if we need a new chunk */ X if (kbp->kused >= KBLOCK || kbp->kbufh == NULL) { X if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL) X return(FALSE); X if (kbp->kbufh == NULL) /* set head ptr if first time */ X kbp->kbufh = nchunk; X /* point the current to this new one */ X if (kbp->kbufp != NULL) X kbp->kbufp->d_next = nchunk; X kbp->kbufp = nchunk; X kbp->kbufp->d_next = NULL; X kbp->kused = 0; X } X X /* and now insert the character */ X kbp->kbufp->d_chunk[kbp->kused++] = c; X return(TRUE); } X /* select one of the named registers for use with the following command */ /* this could actually be handled as a command prefix, in kbdseq(), much X the way ^X-cmd and META-cmd are done, except that we need to be X able to accept any of X 3"adw "a3dw "ad3w X to delete 3 words into register a. So this routine gives us an X easy way to handle the second case. (The third case is handled in X operators(), the first in main()) */ usekreg(f,n) { X int c, status; X X /* take care of incrementing the buffer number, if we're replaying X a command via 'dot' */ X incr_dot_kregnum(); X X c = kbd_key(); X X if (isdigit(c)) X ukb = c - '0'; X else if (islower(c)) X ukb = c - 'a' + 10; /* named buffs are in 10 through 36 */ X else if (isupper(c)) { X ukb = c - 'A' + 10; X kregflag |= KAPPEND; X } else { X TTbeep(); X return (FALSE); X } X X /* get the next command from the keyboard */ X c = kbd_seq(); X X /* allow second chance for entering counts */ X if (f == FALSE) { X do_num_proc(&c,&f,&n); X do_rept_arg_proc(&c,&f,&n); X } X X /* and execute the command */ X status = execute(kcod2fnc(c), f, n); X X ukb = 0; X kregflag = 0; X X return(status); X } X /* buffers 0 through 9 are circulated automatically for full-line deletes */ /* we re-use one of them until the KLINES flag is on, then we advance */ /* to the next */ kregcirculate(killing) { X static lastkb; /* index of the real "0 */ X X if (ukb >= 10) /* then the user specified a lettered buffer */ X return; X X /* we only allow killing into the real "0 */ X /* ignore any other buffer spec */ X if (killing) { X if ((kbs[lastkb].kbflag & KLINES) && X ! (kbs[lastkb].kbflag & KYANK)) { X if (--lastkb < 0) lastkb = 9; X kbs[lastkb].kbflag = 0; X } X ukb = lastkb; X } else { X /* let 0 pass unmolested -- it is the default */ X if (ukb == 0) { X ukb = lastkb; X } else { X /* for the others, if the current "0 has lines in it, it X must be `"1', else "1 is `"1'. get it? */ X if (kbs[lastkb].kbflag & KLINES) X ukb = (lastkb + ukb - 1) % 10; X else X ukb = (lastkb + ukb) % 10; X } X } X } X putbefore(f,n) { X return doput(f,n,FALSE,FALSE); } X putafter(f,n) { X return doput(f,n,TRUE,FALSE); } X lineputbefore(f,n) { X return doput(f,n,FALSE,TRUE); } X lineputafter(f,n) { X return doput(f,n,TRUE,TRUE); } X X doput(f,n,after,putlines) { X int s, oukb, lining; X X if (!f) X n = 1; X X oukb = ukb; X kregcirculate(FALSE); X if (kbs[ukb].kbufh == NULL) { X if (ukb != 0) X mlwrite("Nothing in register %c", X (oukb<10)? oukb+'0' : oukb-10+'a'); X TTbeep(); X return(FALSE); X } X lining = (putlines == TRUE || (kbs[ukb].kbflag & KLINES)); X if (lining) { X if (after && curwp->w_dotp != curbp->b_linep) X curwp->w_dotp = lforw(curwp->w_dotp); X curwp->w_doto = 0; X } else { X if (after && curwp->w_doto != llength(curwp->w_dotp)) X forwchar(TRUE,1); X } X setmark(); X s = put(n,lining); X if (s == TRUE) X swapmark(); X if (curwp->w_dotp == curbp->b_linep) X curwp->w_dotp = lback(curwp->w_dotp); X if (lining) X firstnonwhite(FALSE,0); X ukb = 0; X return (s); } X /* X * Put text back from the kill register. X */ put(n,aslines) { X register int c; X register int i; X int wasnl, suppressnl; X register char *sp; /* pointer into string to insert */ X KILL *kp; /* pointer into kill register */ X X if (n < 0) X return FALSE; X X /* make sure there is something to put */ X if (kbs[ukb].kbufh == NULL) X return TRUE; /* not an error, just nothing */ X X suppressnl = FALSE; X wasnl = FALSE; X X /* for each time.... */ X while (n--) { X kp = kbs[ukb].kbufh; X while (kp != NULL) { X if (kp->d_next == NULL) X i = kbs[ukb].kused; X else X i = KBLOCK; X sp = kp->d_chunk; X while (i--) { X if ((c = *sp++) == '\n') { X if (lnewline() != TRUE) X return FALSE; X wasnl = TRUE; X } else { X if (curwp->w_dotp == curbp->b_linep) X suppressnl = TRUE; X if (linsert(1, c) != TRUE) X return FALSE; X wasnl = FALSE; X } X } X kp = kp->d_next; X } X if (wasnl) { X if (suppressnl) { X if (ldelnewline() != TRUE) X return FALSE; X } X } else { X if (aslines && !suppressnl) { X if (lnewline() != TRUE) X return FALSE; X } X } X } X curwp->w_flag |= WFHARD; X return (TRUE); } SHAR_EOF chmod 0444 line.c || echo 'restore of line.c failed' Wc_c="`wc -c < 'line.c'`" test 20714 -eq "$Wc_c" || echo 'line.c: original size 20714, current size' "$Wc_c" # ============= lock.c ============== echo 'x - extracting lock.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'lock.c' && /* LOCK: File locking command routines for MicroEMACS X written by Daniel Lawrence X */ X #include #include "estruct.h" #include "edef.h" X #if FILOCK #if BSD #include X extern int sys_nerr; /* number of system error messages defined */ extern char *sys_errlist[]; /* list of message texts */ extern int errno; /* current error */ X char *lname[NLOCKS]; /* names of all locked files */ int numlocks; /* # of current locks active */ X /* lockchk: check a file for locking and add it to the list */ X lockchk(fname) X char *fname; /* file to check for a lock */ X { X register int i; /* loop indexes */ X register int status; /* return status */ X char *undolock(); X X /* check to see if that file is already locked here */ X if (numlocks > 0) X for (i=0; i < numlocks; ++i) X if (strcmp(fname, lname[i]) == 0) X return(TRUE); X X /* if we have a full locking table, bitch and leave */ X if (numlocks == NLOCKS) { X mlwrite("LOCK ERROR: Lock table full"); X return(ABORT); X } X X /* next, try to lock it */ X status = lock(fname); X if (status == ABORT) /* file is locked, no override */ X return(ABORT); X if (status == FALSE) /* locked, overriden, dont add to table */ X return(TRUE); X X /* we have now locked it, add it to our table */ X lname[++numlocks - 1] = malloc(strlen(fname) + 1); X if (lname[numlocks - 1] == NULL) { /* malloc failure */ X undolock(fname); /* free the lock */ X mlwrite("Cannot lock, out of memory"); X --numlocks; X return(ABORT); X } X X /* everthing is cool, add it to the table */ X strcpy(lname[numlocks-1], fname); X return(TRUE); } X /* lockrel: release all the file locks so others may edit */ X lockrel() X { X register int i; /* loop index */ X register int status; /* status of locks */ X register int s; /* status of one unlock */ X X status = TRUE; X if (numlocks > 0) X for (i=0; i < numlocks; ++i) { X if ((s = unlock(lname[i])) != TRUE) X status = s; X free(lname[i]); X } X numlocks = 0; X return(status); } X /* lock: Check and lock a file from access by others X returns TRUE = files was not locked and now is X FALSE = file was locked and overridden X ABORT = file was locked, abort command */ X lock(fname) X char *fname; /* file name to lock */ X { X register char *locker; /* lock error message */ X register int status; /* return status */ X char msg[NSTRING]; /* message string */ X char *dolock(); X X /* attempt to lock the file */ X locker = dolock(fname); X if (locker == NULL) /* we win */ X return(TRUE); X X /* file failed...abort */ X if (strncmp(locker, "LOCK", 4) == 0) { X lckerror(locker); X return(ABORT); X } X X /* someone else has it....override? */ X strcpy(msg, "File in use by "); X strcat(msg, locker); X strcat(msg, ", overide?"); X status = mlyesno(msg); /* ask them */ X if (status == TRUE) X return(FALSE); X else X return(ABORT); } X /* unlock: Unlock a file X this only warns the user if it fails X */ X unlock(fname) X char *fname; /* file to unlock */ X { X register char *locker; /* undolock return string */ X char *undolock(); X X /* unclock and return */ X locker = undolock(fname); X if (locker == NULL) X return(TRUE); X X /* report the error and come back */ X lckerror(locker); X return(FALSE); } X lckerror(errstr) /* report a lock error */ X char *errstr; /* lock error string to print out */ X { X char obuf[NSTRING]; /* output buffer for error message */ X X strcpy(obuf, errstr); X strcat(obuf, " - "); X if (errno < sys_nerr) X strcat(obuf, sys_errlist[errno]); X else X strcat(obuf, "[can not get system error message]"); X mlwrite(obuf); } #endif #else lckhello() /* dummy function */ { } #endif SHAR_EOF chmod 0444 lock.c || echo 'restore of lock.c failed' Wc_c="`wc -c < 'lock.c'`" test 3557 -eq "$Wc_c" || echo 'lock.c: original size 3557, current size' "$Wc_c" # ============= main.c ============== echo 'x - extracting main.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'main.c' && /* X * This used to be MicroEMACS 3.9 X * written by Dave G. Conroy. X * substatially modified by Daniel M. Lawrence X * X * (C)opyright 1987 by Daniel M. Lawrence X * MicroEMACS 3.9 can be copied and distributed freely for any X * non-commercial purposes. MicroEMACS 3.9 can only be incorporated X * into commercial software with the permission of the current author. X * X * Turned into "VI Like Emacs", a.k.a. vile, by Paul Fox SHAR_EOF true || echo 'restore of main.c failed' echo 'End of Vile part 8' echo 'File main.c is continued in part 9' echo 9 > _shar_seq_.tmp exit 0 -- paul fox, pgf@cayman.com, (617)494-1999 Cayman Systems, 26 Landsdowne St., Cambridge, MA 02139