rc

[fork] interactive rc shell
Log | Files | Refs | README | LICENSE

commit edf6d5059cbb06ad0cd1c5b126cd9359e4ea32e8
parent 5e91362042e4c1b7023ce18ef7e81ab144fd6928
Author: tim <tim>
Date:   Wed,  4 Jun 1997 15:02:34 +0000

Initial revision

Diffstat:
Ahistory.c | 341+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 341 insertions(+), 0 deletions(-)

diff --git a/history.c b/history.c @@ -0,0 +1,341 @@ +/* + history.c -- primitive history mechanism + + Paul Haahr & Byron Rakitzis, July 1991. + + This program mimics the att v8 = and == history programs. + The edit() algorithm was adapted from a similar program + that Boyd Roberts wrote, but otherwise all the code has + been written from scratch. + + edit() was subsequently redone by Hugh Redelmeier in order + to correctly deal with tab characters in the source line. + + BUGS: + There is an implicit assumption that commands are no + more than 1k characters long. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static char *id = "@(#) history.c 8/91"; + +#undef FALSE +#undef TRUE +typedef enum { FALSE, TRUE } bool; + +#define CHUNKSIZE 65536 + +static struct { + char *old, *new; +} *replace; + +static char **search, *progname, *history; +static char me; /* typically ':' or '-' */ +static bool editit = FALSE, printit = FALSE; +static int nreplace = 0, nsearch = 0; +static FILE *fp; + +static void *ealloc(size_t n) { + void *p = (void *) malloc(n); + if (p == NULL) { + perror("malloc"); + exit(1); + } + return p; +} + +static void *erealloc(void *p, size_t n) { + p = (void *) realloc(p, n); + if (p == NULL) { + perror("realloc"); + exit(1); + } + return p; +} + +static char *newstr() { + return ealloc((size_t)1024); +} + +static char *basename(char *s) { + char *t = strrchr(s, '/'); + return (t == NULL) ? s : t + 1; +} + +/* stupid O(n^2) substring matching routine */ + +static char *isin(char *target, char *pattern) { + size_t plen = strlen(pattern); + size_t tlen = strlen(target); + for (; tlen >= plen; target++, --tlen) + if (strncmp(target, pattern, plen) == 0) + return target; + return NULL; +} + +/* replace the first match in the string with "new" */ +static char *sub(char *s, char *old, char *new) { + char *t, *u; + + t = isin(s, old); + u = newstr(); + + *t = '\0'; + while (*old != '\0') + old++, t++; + strcpy(u, s); + strcat(u, new); + strcat(u, t); + return u; +} + +static char *edit(char *s) { + char *final, *f, *end; + int col; + bool ins; + +start: + fprintf(stderr, "%s\n", s); + f = final = newstr(); + end = s + strlen(s); + col = 0; + ins = FALSE; + + for (;; col++) { + int c = getchar(); + + if (c == me && col == 0) { + int peekc = getchar(); + if (peekc == '\n') + return NULL; + ungetc(peekc, stdin); + } + if (c == '\n') { + if (col == 0) + return s; + + while (s < end) /* copy remainder of string */ + *f++ = *s++; + *f = '\0'; + s = final; + goto start; + } else if (ins || s>=end) { + /* col need not be accurate -- tabs need not be interpreted */ + *f++ = c; + } else { + switch (c) { + case '+': + while (s < end) + *f++ = *s++; + *f = '\0'; + continue; + case '%': + c = ' '; + /* FALLTHROUGH */ + default: + *f++ = c; + break; + case EOF: + exit(1); + /* NOTREACHED */ + case ' ': + if (*s == '\t') { + int oldcol = col; + + for (;; col++) { + int peekc; + + if ((col&07) == 07) { + *f++ = '\t'; /* we spaced past a tab */ + break; + } + peekc = getchar(); + if (peekc != ' ') { + ungetc(peekc, stdin); + if (peekc != '\n') { + /* we spaced partially into a tab */ + do { + *f++ = ' '; + oldcol++; + } while (oldcol <= col); + } + break; + } + } + } else { + *f++ = *s; + } + break; + case '#': + break; + case '$': + end = s; /* truncate s */ + continue; /* skip incrementing s */ + case '^': + ins = TRUE; + continue; /* skip incrementing s */ + case '\t': + for (;; col++) { + if ((*f++ = s<end? *s++ : '\t') == '\t') { + col = col | 07; /* advance to before next tabstop */ + } + if ((col&07) == 07) /* stop before tabstop */ + break; + } + continue; /* skip incrementing s */ + } + if (s<end && (*s!='\t' || (col&07)==07)) + s++; + } + } +} + +static char *readhistoryfile(char **last) { + char *buf; + size_t count, size; + long nread; + + if ((history = getenv("history")) == NULL) { + fprintf(stderr, "$history not set\n"); + exit(1); + } + fp = fopen(history, "r+"); + if (fp == NULL) { + perror(history); + exit(1); + } + + size = 0; + count = 0; + buf = ealloc(size = CHUNKSIZE); + while ((nread = fread(buf + count, sizeof (char), size - count, fp)) > 0) { + count += nread; + if (size - count == 0) + buf = erealloc(buf, size *= 4); + } + if (nread == -1) { + perror(history); + exit(1); + } + *last = buf + count; + return buf; +} + +static char *getcommand() { + char *s, *t; + static char *hist = NULL, *last; + + if (hist == NULL) { + hist = readhistoryfile(&last); + *--last = '\0'; /* trim final newline */ + } + +again: s = last; + if (s < hist) + return NULL; + while (*--s != '\n') + if (s <= hist) { + last = hist - 1; + return hist; + } + *s = '\0'; + last = s++; + + /* + * if the command contains the "me" character at the start of the line + * or after any of [`{|()@] then try again + */ + + for (t = s; *t != '\0'; t++) + if (*t == me) { + char *u = t - 1; + while (u >= s && (*u == ' ' || *u == '\t')) + --u; + if (u < s) + goto again; + switch (*u) { + case '`': case '@': + case '(': case ')': + case '{': case '|': + goto again; + default: + break; + } + } + return s; +} + +int main(int argc, char **argv) { + int i; + char *s; + + s = progname = basename(argv[0]); + me = *s++; + if (*s == me) { + s++; + editit = TRUE; + } + if (*s == 'p') { + s++; + printit = TRUE; + } +/* Nahh... + if (*s != '\0') { + fprintf(stderr, "\"%s\": bad name for history program\n", progname); + exit(1); + } +*/ + + if (argc > 1) { + replace = ealloc((argc - 1) * sizeof *replace); + search = ealloc((argc - 1) * sizeof *search); + } + for (i = 1; i < argc; i++) + if ((s = strchr(argv[i], ':')) == NULL) + search[nsearch++] = argv[i]; + else { + *(char *)s = '\0'; /* do we confuse ps too much? */ + replace[nreplace].old = argv[i]; + replace[nreplace].new = s + 1; + nreplace++; + } + +next: s = getcommand(); + if (s == NULL) { + fprintf(stderr, "command not matched\n"); + return 1; + } + for (i = 0; i < nsearch; i++) + if (!isin(s, search[i])) + goto next; + for (i = 0; i < nreplace; i++) + if (!isin(s, replace[i].old)) + goto next; + else + s = sub(s, replace[i].old, replace[i].new); + if (editit) { + s = edit(s); + if (s == NULL) + goto next; + } + fseek(fp, 0, 2); /* 2 == end of file. i.e., append command to $history */ + fprintf(fp, "%s\n", s); + fclose(fp); + if (printit) + printf("%s\n", s); + else { + char *shell = getenv("SHELL"); + + if (!editit) + fprintf(stderr, "%s\n", s); + if (shell == NULL) + shell = "/bin/sh"; + execl(shell, basename(shell), "-c", s, NULL); + perror(shell); + exit(1); + } + return 0; +}