Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!apple!voder!wlbr!roger.imsd.contel.com!mh From: mh@roger.imsd.contel.com (Mike H.) Newsgroups: comp.windows.news Subject: NeWS version of elvis (the vi clone) part 4 of 9 Message-ID: <1991Jan11.020153.27223@wlbr.imsd.contel.com> Date: 11 Jan 91 02:01:53 GMT Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 1546 Nntp-Posting-Host: roger.imsd.contel.com #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # date.c # ex.c # input.c # This archive created: Thu Jan 10 17:21:08 1991 export PATH; PATH=/bin:$PATH if test -f 'date.c' then echo shar: will not over-write existing file "'date.c'" else cat << \SHAR_EOF > 'date.c' SHAR_EOF fi # end of overwriting check if test -f 'ex.c' then echo shar: will not over-write existing file "'ex.c'" else cat << \SHAR_EOF > 'ex.c' /* ex.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the code for reading ex commands. */ #include "config.h" #include #include "vi.h" #ifndef isascii # define isascii(c) !((c)&~0x7f) #endif /* This data type is used to describe the possible argument combinations */ typedef short ARGT; #define FROM 1 /* allow a linespec */ #define TO 2 /* allow a second linespec */ #define BANG 4 /* allow a ! after the command name */ #define EXTRA 8 /* allow extra args after command name */ #define XFILE 16 /* expand wildcards in extra part */ #define NOSPC 32 /* no spaces allowed in the extra part */ #define DFLALL 64 /* default file range is 1,$ */ #define DFLNONE 128 /* no default file range */ #define NODFL 256 /* do not default to the current file name */ #define EXRCOK 512 /* can be in a .exrc file */ #define NL 1024 /* if mode!=MODE_EX, then write a newline first */ #define PLUS 2048 /* allow a line number, as in ":e +32 foo" */ #define ZERO 4096 /* allow 0 to be given as a line number */ #define FILES (XFILE + EXTRA) /* multiple extra files allowed */ #define WORD1 (EXTRA + NOSPC) /* one extra word allowed */ #define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */ #define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */ #define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */ #define RANGE (FROM + TO) /* range of linespecs allowed */ #define NONE 0 /* no args allowed at all */ /* This array maps ex command names to command codes. The order in which * command names are listed below is significant -- ambiguous abbreviations * are always resolved to be the first possible match. (e.g. "r" is taken * to mean "read", not "rewind", because "read" comes before "rewind") */ static struct { char *name; /* name of the command */ CMD code; /* enum code of the command */ void (*fn)();/* function which executes the command */ ARGT argt; /* command line arguments permitted/needed/used */ } cmdnames[] = { /* cmd name cmd code function arguments */ {"append", CMD_APPEND, cmd_append, FROM+ZERO }, #ifdef DEBUG {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, #endif {"change", CMD_CHANGE, cmd_append, RANGE }, {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 }, {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS }, {"file", CMD_FILE, cmd_file, NAMEDF }, {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL}, {"insert", CMD_INSERT, cmd_append, FROM }, {"join", CMD_INSERT, cmd_join, RANGE }, {"k", CMD_MARK, cmd_mark, FROM+WORD1 }, {"list", CMD_LIST, cmd_print, RANGE+NL }, {"move", CMD_MOVE, cmd_move, RANGE+EXTRA }, {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS }, {"Next", CMD_PREVIOUS, cmd_next, BANG }, {"print", CMD_PRINT, cmd_print, RANGE+NL }, {"quit", CMD_QUIT, cmd_xit, BANG }, {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF}, {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA }, {"to", CMD_COPY, cmd_move, RANGE+EXTRA }, {"undo", CMD_UNDO, cmd_undo, NONE }, {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL}, {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL}, {"xit", CMD_XIT, cmd_xit, BANG+NL }, {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 }, {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL}, {"<", CMD_SHIFTL, cmd_shift, RANGE }, {">", CMD_SHIFTR, cmd_shift, RANGE }, {"=", CMD_EQUAL, cmd_file, RANGE }, {"&", CMD_SUBAGAIN, cmd_substitute, RANGE }, #ifndef NO_AT {"@", CMD_AT, cmd_at, EXTRA }, #endif #ifndef NO_ABBR {"abbreviate", CMD_ABBR, cmd_abbr, EXRCOK+EXTRA }, #endif {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS }, #ifndef NO_ERRLIST {"cc", CMD_CC, cmd_make, BANG+FILES }, #endif {"cd", CMD_CD, cmd_cd, EXRCOK+NAMEDF }, {"copy", CMD_COPY, cmd_move, RANGE+EXTRA }, #ifndef NO_DIGRAPH {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA}, #endif #ifndef NO_ERRLIST {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF }, #endif {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 }, {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA}, #ifndef NO_MKEXRC {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, #endif {"number", CMD_NUMBER, cmd_print, RANGE+NL }, {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 }, {"set", CMD_SET, cmd_set, EXRCOK+EXTRA }, {"shell", CMD_SHELL, cmd_shell, NL }, {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF }, {"tag", CMD_TAG, cmd_tag, BANG+WORD1 }, {"version", CMD_VERSION, cmd_version, EXRCOK+NONE }, {"visual", CMD_VISUAL, cmd_visual, NONE }, {"wq", CMD_WQUIT, cmd_xit, NL }, #ifdef DEBUG {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, {"validate", CMD_VALIDATE, cmd_validate, BANG+NL }, #endif {"chdir", CMD_CD, cmd_cd, EXRCOK+NAMEDF }, #ifndef NO_ERRLIST {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS }, #endif {"mark", CMD_MARK, cmd_mark, FROM+WORD1 }, {"previous", CMD_PREVIOUS, cmd_next, BANG }, {"rewind", CMD_REWIND, cmd_next, BANG }, {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA}, #ifndef NO_ABBR {"unabbreviate",CMD_UNABBR, cmd_abbr, EXRCOK+WORD1 }, #endif {(char *)0} }; /* This function parses a search pattern - given a pointer to a / or ?, * it replaces the ending / or ? with a \0, and returns a pointer to the * stuff that came after the pattern. */ char *parseptrn(ptrn) REG char *ptrn; { REG char *scan; for (scan = ptrn + 1; *scan && *scan != *ptrn; scan++) { /* allow backslashed versions of / and ? in the pattern */ if (*scan == '\\' && scan[1] != '\0') { scan++; } } if (*scan) { *scan++ = '\0'; } return scan; } /* This function parses a line specifier for ex commands */ char *linespec(s, markptr) REG char *s; /* start of the line specifier */ MARK *markptr; /* where to store the mark's value */ { long num; REG char *t; /* parse each ;-delimited clause of this linespec */ do { /* skip an initial ';', if any */ if (*s == ';') { s++; } /* skip leading spaces */ while (isascii(*s) && isspace(*s)) { s++; } /* dot means current position */ if (*s == '.') { s++; *markptr = cursor; } /* '$' means the last line */ else if (*s == '$') { s++; *markptr = MARK_LAST; } /* digit means an absolute line number */ else if (isascii(*s) && isdigit(*s)) { for (num = 0; isascii(*s) && isdigit(*s); s++) { num = num * 10 + *s - '0'; } *markptr = MARK_AT_LINE(num); } /* appostrophe means go to a set mark */ else if (*s == '\'') { s++; *markptr = m_tomark(cursor, 1L, (int)*s); s++; } /* slash means do a search */ else if (*s == '/' || *s == '?') { /* put a '\0' at the end of the search pattern */ t = parseptrn(s); /* search for the pattern */ *markptr &= ~(BLKSIZE - 1); if (*s == '/') { pfetch(markline(*markptr)); if (plen > 0) *markptr += plen - 1; *markptr = m_fsrch(*markptr, s); } else { *markptr = m_bsrch(*markptr, s); } /* adjust command string pointer */ s = t; } /* if linespec was faulty, quit now */ if (!*markptr) { return s; } /* maybe add an offset */ t = s; if (*t == '-' || *t == '+') { s++; for (num = 0; *s >= '0' && *s <= '9'; s++) { num = num * 10 + *s - '0'; } if (num == 0) { num = 1; } *markptr = m_updnto(*markptr, num, *t); } } while (*s == ';' || *s == '+' || *s == '-'); return s; } /* This function reads an ex command and executes it. */ void ex() { char cmdbuf[80]; REG int cmdlen; static long oldline; significant = FALSE; oldline = markline(cursor); while (mode == MODE_EX) { /* read a line */ cmdlen = vgets(':', cmdbuf, sizeof cmdbuf); if (cmdlen < 0) { return; } /* if empty line, assume ".+1" */ if (cmdlen == 0) { strcpy(cmdbuf, ".+1"); qaddch('\r'); clrtoeol(); } else { addch('\n'); } refresh(); /* parse & execute the command */ doexcmd(cmdbuf); /* handle autoprint */ if (significant || markline(cursor) != oldline) { significant = FALSE; oldline = markline(cursor); if (*o_autoprint && mode == MODE_EX) { cmd_print(cursor, cursor, CMD_PRINT, FALSE, ""); } } } } void doexcmd(cmdbuf) char *cmdbuf; /* string containing an ex command */ { REG char *scan; /* used to scan thru cmdbuf */ MARK frommark; /* first linespec */ MARK tomark; /* second linespec */ REG int cmdlen; /* length of the command name given */ CMD cmd; /* what command is this? */ ARGT argt; /* argument types for this command */ short forceit; /* bang version of a command? */ REG int cmdidx; /* index of command */ REG char *build; /* used while copying filenames */ int iswild; /* boolean: filenames use wildcards? */ int isdfl; /* using default line ranges? */ int didsub; /* did we substitute file names for % or # */ /* ex commands can't be undone via the shift-U command */ U_line = 0L; /* ignore command lines that start with a double-quote */ if (*cmdbuf == '"') { return; } /* permit extra colons at the start of the line */ while (*cmdbuf == ':') { cmdbuf++; } /* parse the line specifier */ scan = cmdbuf; if (nlines < 1) { /* no file, so don't allow addresses */ } else if (*scan == '%') { /* '%' means all lines */ frommark = MARK_FIRST; tomark = MARK_LAST; scan++; } else if (*scan == '0') { frommark = tomark = MARK_UNSET; scan++; } else { frommark = cursor; scan = linespec(scan, &frommark); tomark = frommark; if (frommark && *scan == ',') { scan++; scan = linespec(scan, &tomark); } if (!tomark) { /* faulty line spec -- fault already described */ return; } if (frommark > tomark) { msg("first address exceeds the second"); return; } } isdfl = (scan == cmdbuf); /* skip whitespace */ while (isascii(*scan) && isspace(*scan)) { scan++; } /* if no command, then just move the cursor to the mark */ if (!*scan) { cursor = tomark; return; } /* figure out how long the command name is */ if (isascii(*scan) && !isalpha(*scan)) { cmdlen = 1; } else { for (cmdlen = 1; !isascii(scan[cmdlen]) || isalpha(scan[cmdlen]); cmdlen++) { } } /* lookup the command code */ for (cmdidx = 0; cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen); cmdidx++) { } argt = cmdnames[cmdidx].argt; cmd = cmdnames[cmdidx].code; if (cmd == CMD_NULL) { #if OSK msg("Unknown command \"%s\"", scan); #else msg("Unknown command \"%.*s\"", cmdlen, scan); #endif return; } /* if the command ended with a bang, set the forceit flag */ scan += cmdlen; if ((argt & BANG) && *scan == '!') { scan++; forceit = 1; } else { forceit = 0; } /* skip any more whitespace, to leave scan pointing to arguments */ while (isascii(*scan) && isspace(*scan)) { scan++; } /* a couple of special cases for filenames */ if (argt & XFILE) { /* if names were given, process them */ if (*scan) { for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++) { switch (*scan) { case '%': if (!*origname) { msg("No filename to substitute for %%"); return; } strcpy(build, origname); while (*build) { build++; } didsub = TRUE; break; case '#': if (!*prevorig) { msg("No filename to substitute for #"); return; } strcpy(build, prevorig); while (*build) { build++; } didsub = TRUE; break; case '*': case '?': #if !(MSDOS || TOS) case '[': case '`': case '{': /* } */ case '$': case '~': #endif *build++ = *scan; iswild = TRUE; break; default: *build++ = *scan; } } *build = '\0'; if (cmd == CMD_BANG || cmd == CMD_READ && tmpblk.c[0] == '!' || cmd == CMD_WRITE && tmpblk.c[0] == '!') { if (didsub) { if (mode != MODE_EX) { addch('\n'); } addstr(tmpblk.c); addch('\n'); exrefresh(); } } else { if (iswild && tmpblk.c[0] != '>') { scan = wildcard(tmpblk.c); } } } else /* no names given, maybe assume origname */ { if (!(argt & NODFL)) { strcpy(tmpblk.c, origname); } else { *tmpblk.c = '\0'; } } scan = tmpblk.c; } /* bad arguments? */ if (!(argt & EXRCOK) && nlines < 1L) { msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC); return; } if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET) { msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name); return; } if (!(argt & FROM) && frommark != cursor && nlines >= 1L) { msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name); return; } if (!(argt & TO) && tomark != frommark && nlines >= 1L) { msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name); return; } if (!(argt & EXTRA) && *scan) { msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name); return; } if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) { build = scan; #ifndef CRUNCH if ((argt & PLUS) && *build == '+') { while (*build && !(isascii(*build) && isspace(*build))) { build++; } while (*build && isascii(*build) && isspace(*build)) { build++; } } #endif /* not CRUNCH */ for (; *build; build++) { if (isspace(*build)) { msg("Too many %s to \"%s\" command.", (argt & XFILE) ? "filenames" : "arguments", cmdnames[cmdidx].name); return; } } } /* some commands have special default ranges */ if (isdfl && (argt & DFLALL)) { frommark = MARK_FIRST; tomark = MARK_LAST; } else if (isdfl && (argt & DFLNONE)) { frommark = tomark = 0L; } /* write a newline if called from visual mode */ if ((argt & NL) && mode != MODE_EX && !exwrote) { addch('\n'); exrefresh(); } /* act on the command */ (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan); } /* This function executes EX commands from a file. It returns 1 normally, or * 0 if the file could not be opened for reading. */ int doexrc(filename) char *filename; /* name of a ".exrc" file */ { int fd; /* file descriptor */ int len; /* length of the ".exrc" file */ char buf[MAXRCLEN]; /* buffer, holds the entire .exrc file */ /* open the file, read it, and close */ fd = open(filename, O_RDONLY); if (fd < 0) { return 0; } len = tread(fd, buf, MAXRCLEN); close(fd); /* execute the string */ exstring(buf, len); return 1; } void exstring(buf, len) char *buf; /* the commands to execute */ int len; /* the length of the string */ { char *cmd; /* start of a command */ char *end; /* used to search for the end of cmd */ /* find & do each command */ for (cmd = buf; cmd < &buf[len]; cmd = end + 1) { /* find the end of the command */ for (end = cmd; end < &buf[len] && *end != '\n' && *end != '|'; end++) { } *end = '\0'; /* do it */ doexcmd(cmd); } } SHAR_EOF fi # end of overwriting check if test -f 'input.c' then echo shar: will not over-write existing file "'input.c'" else cat << \SHAR_EOF > 'input.c' /* input.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the input() function, which implements vi's INPUT mode. * It also contains the code that supports digraphs. */ #include #include "config.h" #include "vi.h" #ifndef NO_DIGRAPH static struct _DIG { struct _DIG *next; char key1; char key2; char dig; char save; } *digs; char digraph(key1, key2) char key1; /* the underlying character */ char key2; /* the second character */ { int newkey; REG struct _DIG *dp; /* if digraphs are disabled, then just return the new char */ if (!*o_digraph) { return key2; } /* remember the new key, so we can return it if this isn't a digraph */ newkey = key2; /* sort key1 and key2, so that their original order won't matter */ if (key1 > key2) { key2 = key1; key1 = newkey; } /* scan through the digraph chart */ for (dp = digs; dp && (dp->key1 != key1 || dp->key2 != key2); dp = dp->next) { } /* if this combination isn't in there, just use the new key */ if (!dp) { return newkey; } /* else use the digraph key */ return dp->dig; } /* this function lists or defines digraphs */ void do_digraph(bang, extra) int bang; char extra[]; { int dig; REG struct _DIG *dp; struct _DIG *prev; static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */ char listbuf[8]; /* if "extra" is NULL, then we've reached the end of the built-ins */ if (!extra) { user_defined = TRUE; return; } /* if no args, then display the existing digraphs */ if (*extra < ' ') { listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' '; listbuf[7] = '\0'; for (dig = 0, dp = digs; dp; dp = dp->next) { if (dp->save || bang) { dig += 7; if (dig >= COLS) { addch('\n'); exrefresh(); dig = 7; } listbuf[3] = dp->key1; listbuf[4] = dp->key2; listbuf[6] = dp->dig; qaddstr(listbuf); } } addch('\n'); exrefresh(); return; } /* make sure we have at least two characters */ if (!extra[1]) { msg("Digraphs must be composed of two characters"); return; } /* sort key1 and key2, so that their original order won't matter */ if (extra[0] > extra[1]) { dig = extra[0]; extra[0] = extra[1]; extra[1] = dig; } /* locate the new digraph character */ for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++) { } dig = extra[dig]; if (!bang && dig) { dig |= 0x80; } /* search for the digraph */ for (prev = (struct _DIG *)0, dp = digs; dp && (dp->key1 != extra[0] || dp->key2 != extra[1]); prev = dp, dp = dp->next) { } /* deleting the digraph? */ if (!dig) { if (!dp) { #ifndef CRUNCH msg("%c%c not a digraph", extra[0], extra[1]); #endif return; } if (prev) prev->next = dp->next; else digs = dp->next; free(dp); return; } /* if necessary, create a new digraph struct for the new digraph */ if (dig && !dp) { dp = (struct _DIG *)malloc(sizeof *dp); if (!dp) { msg("Out of space in the digraph table"); return; } if (prev) prev->next = dp; else digs = dp; dp->next = (struct _DIG *)0; } /* assign it the new digraph value */ dp->key1 = extra[0]; dp->key2 = extra[1]; dp->dig = dig; dp->save = user_defined; } # ifndef NO_MKEXRC void savedigs(fd) int fd; { static char buf[] = "digraph! XX Y\n"; REG struct _DIG *dp; for (dp = digs; dp; dp = dp->next) { if (dp->save) { buf[9] = dp->key1; buf[10] = dp->key2; buf[12] = dp->dig; write(fd, buf, (unsigned)14); } } } # endif #endif #ifndef NO_ABBR static struct _AB { struct _AB *next; char *large; /* the expanded form */ char small[1]; /* the abbreviated form (appended to struct) */ } *abbrev; /* This functions lists or defines abbreviations */ void do_abbr(extra) char *extra; { int smlen; /* length of the small form */ int lrg; /* index of the start of the large form */ REG struct _AB *ab; /* used to move through the abbrev list */ struct _AB *prev; /* no arguments? */ if (!*extra) { /* list all current abbreviations */ for (ab = abbrev; ab; ab = ab->next) { qaddstr("abbr "); qaddstr(ab->small); qaddch(' '); qaddstr(ab->large); addch('\n'); exrefresh(); } return; } /* else one or more arguments. Parse the first & look up in abbrev[] */ for (smlen = 0; extra[smlen] && isalnum(extra[smlen]); smlen++) { } for (prev = (struct _AB *)0, ab = abbrev; ab; prev = ab, ab = ab->next) { if (!strncmp(extra, ab->small, smlen) && !ab->small[smlen]) { break; } } /* locate the start of the large form, if any */ for (lrg = smlen; extra[lrg] && isascii(extra[lrg]) && isspace(extra[lrg]); lrg++) { } /* only one arg? */ if (!extra[lrg]) { /* trying to undo an abbreviation which doesn't exist? */ if (!ab) { #ifndef CRUNCH msg("\"%s\" not an abbreviation", extra); #endif return; } /* undo the abbreviation */ if (prev) prev->next = ab->next; else abbrev = ab->next; free(ab->large); free(ab); return; } /* multiple args - [re]define an abbreviation */ if (ab) { /* redefining - free the old large form */ free(ab->large); } else { /* adding a new definition - make a new struct */ ab = (struct _AB *)malloc((unsigned)(smlen + sizeof *ab)); #ifndef CRUNCH if (!ab) { msg("Out of memory -- Sorry"); return; } #endif strncpy(ab->small, extra, smlen); ab->small[smlen] = '\0'; ab->next = (struct _AB *)0; if (prev) prev->next = ab; else abbrev = ab; } /* store the new form */ ab->large = (char *)malloc((unsigned)(strlen(&extra[lrg]) + 1)); strcpy(ab->large, &extra[lrg]); } # ifndef NO_MKEXRC /* This function is called from cmd_mkexrc() to save the abbreviations */ void saveabbr(fd) int fd; /* fd to which the :abbr commands should be written */ { REG struct _AB *ab; for (ab = abbrev; ab; ab = ab->next) { twrite(fd, "abbr ", 5); twrite(fd, ab->small, strlen(ab->small)); twrite(fd, " ", 1); twrite(fd, ab->large, strlen(ab->large)); twrite(fd, "\n", 1); } } # endif /* This function should be called before each char is inserted. If the next * char is non-alphanumeric and we're at the end of a word, then that word * is checked against the abbrev[] array and expanded, if appropriate. Upon * returning from this function, the new char still must be inserted. */ static MARK expandabbr(m, ch) MARK m; /* the cursor position */ int ch; /* the character to insert */ { char *word; /* where the word starts */ int len; /* length of the word */ REG struct _AB *ab; /* if no abbreviations are in effect, or ch is aphanumeric, then * don't do anything */ if (!abbrev || !isascii(ch) || isalnum(ch)) { return m; } /* see where the preceding word starts */ pfetch(markline(m)); for (word = ptext + markidx(m), len = 0; --word >= ptext && (!isascii(*word) || isalnum(*word)); len++) { } word++; /* if zero-length, then it isn't a word, really -- so nothing */ if (len == 0) { return m; } /* look it up in the abbrev list */ for (ab = abbrev; ab; ab = ab->next) { if (!strncmp(ab->small, word, len) && !ab->small[len]) { break; } } /* not an abbreviation? then do nothing */ if (!ab) { return m; } /* else replace the small form with the large form */ add(m, ab->large); delete(m - len, m); /* return with the cursor after the end of the large form */ return m - len + strlen(ab->large); } #endif /* This function allows the user to replace an existing (possibly zero-length) * chunk of text with typed-in text. It returns the MARK of the last character * that the user typed in. */ MARK input(from, to, when, indentref) MARK from; /* where to start inserting text */ MARK to; /* extent of text to delete */ int when; /* either WHEN_VIINP or WHEN_VIREP */ long indentref; /* line rel to current mark to use for autoindent reference - MCH */ { char key[2]; /* key char followed by '\0' char */ char *build; /* used in building a newline+indent string */ char *scan; /* used while looking at the indent chars of a line */ MARK m; /* some place in the text */ #ifndef NO_EXTENSIONS int quit = FALSE; /* boolean: are we exiting after this? */ #endif #ifdef DEBUG /* if "from" and "to" are reversed, complain */ if (from > to) { msg("ERROR: input(%ld:%d, %ld:%d)", markline(from), markidx(from), markline(to), markidx(to)); return MARK_UNSET; } #endif key[1] = 0; /* if we're replacing text with new text, save the old stuff */ /* (Alas, there is no easy way to save text for replace mode) */ if (from != to) { cut(from, to); } ChangeText { /* if doing a dot command, then reuse the previous text */ if (doingdot) { /* delete the text that's there now */ if (from != to) { delete(from, to); } /* insert the previous text */ cutname('.'); cursor = paste(from, FALSE, TRUE) + 1L; } else /* interactive version */ { /* if doing a change within the line... */ if (from != to && markline(from) == markline(to)) { /* mark the end of the text with a "$" */ change(to - 1, to, "$"); } else { /* delete the old text right off */ if (from != to) { delete(from, to); } to = from; } /* handle autoindent of the first line, maybe */ cursor = from; if (*o_autoindent && markline(cursor) > 1L && markidx(cursor) == 0) { /* Only autoindent blank lines. */ pfetch(markline(cursor)); if (plen == 0) { /* Okay, we really want to autoindent */ pfetch(markline(cursor) + indentref); for (scan = ptext, build = tmpblk.c; *scan == ' ' || *scan == '\t'; ) { *build++ = *scan++; } if (build > tmpblk.c) { *build = '\0'; add(cursor, tmpblk.c); cursor += (build - tmpblk.c); } } } /* repeatedly add characters from the user */ for (;;) { /* Get a character */ redraw(cursor, TRUE); #ifdef DEBUG msg("cursor=%ld.%d, to=%ld.%d", markline(cursor), markidx(cursor), markline(to), markidx(to)); #endif key[0] = getkey(when); /* if whitespace & wrapmargin is set & we're * past the warpmargin, then change the * whitespace character into a newline */ if ((*key == ' ' || *key == '\t') && *o_wrapmargin != 0) { pfetch(markline(cursor)); if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff)) { *key = '\n'; } } /* process it */ switch (*key) { #ifndef NO_EXTENSIONS case 0: /* special movement mapped keys */ *key = getkey(0); switch (*key) { case 'h': m = m_left(cursor, 0L); break; case 'j': case 'k': m = m_updnto(cursor, 0L, *key); break; case 'l': m = cursor + 1; break; case 'b': m = m_bword(cursor, 0L); break; case 'w': m = m_fword(cursor, 0L); break; case '^': m = m_front(cursor, 0L); break; case '$': m = m_rear(cursor, 0L); break; case ctrl('B'): case ctrl('F'): m = m_scroll(cursor, 0L, *key); break; case 'x': m = v_xchar(cursor, 0L); break; case 'i': m = to = from = cursor; break; default: m = MARK_UNSET; break; } /* adjust the moved cursor */ m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0)); if (*key == '$' || (*key == 'l' && m <= cursor)) { m++; } /* if the cursor is reasonable, use it */ if (m == MARK_UNSET) { Beep(); } else { if (to > cursor) { delete(cursor, to); redraw(cursor, TRUE); } from = to = cursor = m; } break; case ctrl('Z'): if (getkey(0) == ctrl('Z')) { quit = TRUE; goto BreakBreak; } break; #endif case ctrl('['): #ifndef NO_ABBR cursor = expandabbr(cursor, ctrl('[')); #endif goto BreakBreak; case ctrl('U'): if (markline(cursor) == markline(from)) { cursor = from; } else { cursor &= ~(BLKSIZE - 1); } break; case ctrl('D'): case ctrl('T'): if (to > cursor) { delete(cursor, to); } mark[27] = cursor; cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, ""); if (mark[27]) { cursor = mark[27]; } else { cursor = m_front(cursor, 0L); } to = cursor; break; case '\b': if (cursor <= from) { Beep(); } else if (markidx(cursor) == 0) { cursor -= BLKSIZE; pfetch(markline(cursor)); cursor += plen; } else { cursor--; } break; case ctrl('W'): m = m_bword(cursor, 1L); if (markline(m) == markline(cursor) && m >= from) { cursor = m; if (from > cursor) { from = cursor; } } else { Beep(); } break; case '\n': #if OSK case '\l': #else case '\r': #endif #ifndef NO_ABBR cursor = expandabbr(cursor, '\n'); #endif build = tmpblk.c; *build++ = '\n'; if (*o_autoindent) { /* figure out indent for next line */ pfetch(markline(cursor)); for (scan = ptext; *scan == ' ' || *scan == '\t'; ) { *build++ = *scan++; } /* remove indent from this line, if blank */ if (!*scan && plen > 0) { to = cursor &= ~(BLKSIZE - 1); delete(cursor, cursor + plen); } } *build = 0; if (cursor >= to && when != WHEN_VIREP) { add(cursor, tmpblk.c); } else { change(cursor, to, tmpblk.c); } redraw(cursor, TRUE); to = cursor = (cursor & ~(BLKSIZE - 1)) + BLKSIZE + (int)(build - tmpblk.c) - 1; break; case ctrl('A'): case ctrl('P'): if (cursor < to) { delete(cursor, to); } if (*key == ctrl('A')) { cutname('.'); } to = cursor = paste(cursor, FALSE, TRUE) + 1L; break; case ctrl('V'): if (cursor >= to && when != WHEN_VIREP) { add(cursor, "^"); } else { change(cursor, to, "^"); to = cursor + 1; } redraw(cursor, TRUE); *key = getkey(0); if (*key == '\n') { /* '\n' too hard to handle */ #if OSK *key = '\l'; #else *key = '\r'; #endif } change(cursor, cursor + 1, key); cursor++; if (cursor > to) { to = cursor; } break; case ctrl('L'): case ctrl('R'): redraw(MARK_UNSET, FALSE); break; default: if (cursor >= to && when != WHEN_VIREP) { #ifndef NO_ABBR cursor = expandabbr(cursor, *key); #endif add(cursor, key); cursor++; to = cursor; } else { pfetch(markline(cursor)); if (markidx(cursor) == plen) { #ifndef NO_ABBR cursor = expandabbr(cursor, *key); #endif add(cursor, key); } else { #ifndef NO_DIGRAPH *key = digraph(ptext[markidx(cursor)], *key); #endif #ifndef NO_ABBR cursor = expandabbr(cursor, *key); #endif change(cursor, cursor + 1, key); } cursor++; } #ifndef NO_SHOWMATCH /* show matching "({[" if neceesary */ if (*o_showmatch && strchr(")}]", *key)) { redraw(cursor, TRUE); m = m_match(cursor - 1, 0L); if (markline(m) >= topline && markline(m) <= botline) { redraw(m, TRUE); refresh(); sleep(1); } } #endif } /* end switch(*key) */ } /* end for(;;) */ BreakBreak:; /* delete any excess characters */ /* this includes any whitespace on a whitespace only line if we are in autoindent mode - MCH */ if (*o_autoindent) { /* remove indent from this line, if blank */ pfetch(markline(cursor)); for (scan = ptext; *scan == ' ' || *scan == '\t'; ) *scan++; if (!*scan && plen > 0) { to = cursor &= ~(BLKSIZE - 1); delete(cursor, cursor + plen); } } if (cursor < to) { delete(cursor, to); } } /* end if doingdot else */ } /* end ChangeText */ /* put the new text into a cut buffer for possible reuse */ if (!doingdot) { blksync(); cutname('.'); cut(from, cursor); } /* move to last char that we inputted, unless it was newline */ if (markidx(cursor) != 0) { cursor--; } redraw(cursor, FALSE); #ifndef NO_EXTENSIONS if (quit) { /* if this is a nested "do", then cut it short */ abortdo(); /* exit, unless we can't write out the file */ cursor = v_xit(cursor, 0L, 'Z'); } #endif rptlines = 0L; return cursor; } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0