/* * =[=][p] [ pattern ] [ old=new ... ] * * = - command redo * = - qed style line edit * p - print don't execute * * Boyd Roberts * June '90. * * $Log: :.c,v $ * Revision 1.2 92/02/22 19:01:40 boyd * Fixed bug with typing $ past the end of the line. * Previously the $ would be ignored. Now, anything * after the end of the line is automatically added. * * Revision 1.1 92/02/22 18:58:33 boyd * Initial revision * */ #include #include #include #include /* * `=' is `:' for `rc(1)' */ #define EQ ':' #define HISTORY "history" #define SHELL "SHELL" #define SH "/bin/sh" #define NULLSTR ((char *)0) #define SYSERROR (-1) #define BLKZ 512 #define FLEXZ 128 #define VECZ 64 #define flex_char(f, c) (*((f)->f_ptr == (f)->f_end ? flex_fill(f) : (f)->f_ptr++) = (c)) typedef struct { char *f_str; char *f_end; char *f_ptr; } flex; typedef struct { int v_count; int v_size; int v_incr; char **v_list; } vec; char *my_name; char *seps = " \t;&<>|"; int edit; int print; extern char *strrchr(); extern char *strchr(); extern char *strcpy(); extern char *getenv(); extern long lseek(); char * salloc(n) int n; { register char *s; extern char *malloc(); if ((s = malloc((unsigned)n)) == NULLSTR) { fprintf(stderr, "%s: Ran out of memory.\n", my_name); exit(1); /* NOTREACHED */ } return s; } char * srealloc(p, n) char *p; int n; { register char *s; extern char *realloc(); if ((s = realloc(p, (unsigned)n)) == NULLSTR) { fprintf(stderr, "%s: Ran out of memory.\n", my_name); exit(1); /* NOTREACHED */ } return s; } void flex_init(f) register flex *f; { f->f_str = f->f_ptr = salloc(FLEXZ); f->f_end = f->f_ptr + FLEXZ; } char * flex_fill(f) register flex *f; { register int s; s = f->f_end - f->f_str + FLEXZ; f->f_str = srealloc(f->f_str, s); f->f_end = f->f_str + s; f->f_ptr = f->f_end - FLEXZ; return f->f_ptr++; } void flex_end(f) register flex *f; { f->f_ptr = f->f_str; } char * flex_str(f, s) register flex *f; register char *s; { register char *p; p = s; while (*p != '\0') { flex_char(f, *p); p++; } return s; } void vec_init(v, n) register vec *v; register int n; { v->v_count = 0; v->v_size = v->v_incr = n; v->v_list = (char **)salloc(n * sizeof(char *)); } char * vec_str(v, s) register vec *v; register char *s; { if (v->v_count == v->v_size) v->v_list = (char **)srealloc((char *)v->v_list, (int)(v->v_size += v->v_incr) * sizeof(char *)); v->v_list[v->v_count++] = s; return s; } char * sysmess() { extern int errno; extern int sys_nerr; extern char *sys_errlist[]; if (errno < 0 || errno >= sys_nerr) return "Unknown error"; return sys_errlist[errno]; } int match(l, s) char *l; register char *s; { register char *p; register char *q; register int c; for (p = l; (c = *p) == ' ' || c == '\t' || c == '('; p++) ; if (*p == '\0') return 0; q = p; while ((c = *p) != '\0' && strchr(seps, c) == NULLSTR) { if (c == '/') q = p; p++; } if (*q == '/') q++; while (q <= p) { if (*q != (c = *s++)) { if (c == '\0') return 1; return 0; } if (*q++ == '\0') return 1; } return 0; } int ok(l) char *l; { register char *p; register char c; static char eq[2] = { EQ, '\0' }; for (p = l; (c = *p) == ' ' || c == '\t'; p++) ; return c != '\0' && !match(p, eq); } void could_not(what, with) register char *what; register char *with; { fprintf(stderr, "%s: Could not %s \"%s\". %s\n", my_name, what, with, sysmess()); exit(1); /* NOTREACHED*/ } void sub(pp, subs) char **pp; char *subs[]; { register char *p; register int i; int l; l = strlen(*pp); for (i = 0; subs[i] != NULLSTR; i++) l += strlen(subs[i]); p = strcpy(salloc(l + 1), *pp); for (i = 0; subs[i] != NULLSTR; i++) { register char *q; register char *s; register char *r; int ls; int lr; r = strchr(s = subs[i], EQ); *r++ = '\0'; l = strlen(p); ls = strlen(s); lr = strlen(r); for (q = p; q <= &p[l - ls]; q++) { if (strncmp(q, s, ls) == 0) { int x; int y; if (lr > ls) { y = lr - ls; for (x = l; &p[x] >= &q[ls]; x--) p[x + y] = p[x]; } else if (ls > lr) { for (x = 0; (q[lr + x] = q[ls + x]) != '\0'; x++) ; } for (x = 0; x < lr; x++) q[x] = r[x]; break; } } *--r = EQ; } *pp = p; } int qed(pp) char **pp; { register int c; register char *p; register int x; register int col; int l; int add; static flex f = { NULLSTR }; if (f.f_str == NULLSTR) flex_init(&f); x = 0; col = 0; add = 0; l = strlen(p = *pp); fprintf(stderr, "%s\n", p); while ((c = getchar()) != EOF) { register int i; switch ((add && c != '\n') ? '^' : c) { case '#': break; case '%': flex_char(&f, ' '); break; case '$': if (!add) { if (l > x) l = x; } case '^': if (add) flex_char(&f, c); else add = 1; col++; continue; case ' ': if (l <= x) flex_char(&f, ' '); else flex_char(&f, p[x]); break; case '\t': do { col++; if (x < l) { flex_char(&f, p[x]); if (p[x++] == '\t') col = (col + 8) & ~7; } else { flex_char(&f, ' '); add = 1; } } while ((col % 8) != 0); continue; case EQ: if (col == 0) { if ((c = getchar()) == '\n') { flex_end(&f); return 0; } ungetc(c, stdin); c = EQ; } flex_char(&f, c); break; case '\n': if (col == 0) { *pp = p; flex_end(&f); return 1; } for (i = x; i < l; i++) flex_char(&f, p[i]); p = f.f_str; l = f.f_ptr - f.f_str; flex_char(&f, '\0'); fprintf(stderr, "%s\n", p); flex_init(&f); x = 0; col = 0; add = 0; continue; default: flex_char(&f, c); break; } if (++x >= l) add = 1; col++; } exit(1); /* NOTREACHED */ } long fill(fd, f, blkp, szp, pos, vp) int fd; char *f; char **blkp; int *szp; long pos; vec *vp; { register int n; register char *p; register int nl; for (;;) { if (pos < 0) { *szp += pos; pos = 0; } if (lseek(fd, pos, 0) == (long)SYSERROR) { could_not("seek on", f); /* NOTREACHED */ } if ((n = read(fd, *blkp, *szp)) == SYSERROR) { could_not("read", f); /* NOTREACHED */ } if (n == 0) return 0; vp->v_count = 0; nl = 0; for (p = *blkp + n - 1; p >= *blkp; p--) { if (*p == '\n') { *p = '\0'; if (nl && ok(p + 1)) vec_str(vp, p + 1); nl++; } } if (pos == 0 && nl && ok(*blkp)) vec_str(vp, *blkp); if (pos == 0) return pos; if (vp->v_count) { pos -= *szp - (vp->v_list[vp->v_count - 1] - *blkp); return pos; } *szp += BLKZ; *blkp = srealloc(*blkp, *szp); pos -= BLKZ; } } char * find(fd, f, patt, subs) int fd; char *f; char *patt; char *subs[]; { register int i; long pos; char *blk; int sz; struct stat statb; vec v; static char *last = NULLSTR; if (fstat(fd, &statb) == SYSERROR) { could_not("stat", f); /* NOTREACHED */ } pos = statb.st_size - BLKZ; blk = salloc(sz = BLKZ); vec_init(&v, VECZ); do { pos = fill(fd, f, &blk, &sz, pos, &v); for (i = 0; i < v.v_count; i++) { if (patt == NULLSTR || match(v.v_list[i], patt)) { sub(&v.v_list[i], subs); last = v.v_list[i]; if (!edit || qed(&v.v_list[i])) return v.v_list[i]; } } } while (pos != 0); if (edit && last != NULLSTR) { register char *p; i = strlen(p = last) + 1; do { last = strcpy(salloc(i), p); } while (!qed(&last)); return last; } return NULLSTR; } void usage() { fprintf(stderr, "usage: %s [ pattern ] [ old%cnew ... ]\n", my_name, EQ); exit(1); /* NOTREACHED */ } main(argc, argv) int argc; char *argv[]; { register int i; register char *p; register char *q; int fd; char *sh; char *patt; if ((my_name = strrchr(argv[0], '/')) == NULLSTR || *++my_name == '\0') my_name = argv[0]; print = strchr(my_name, 'p') != NULLSTR; edit = my_name[0] == EQ && my_name[1] == EQ; if ((p = getenv(HISTORY)) == NULLSTR) { fprintf(stderr, "%s: No environment $%s.\n", my_name, HISTORY); exit(1); } if ((fd = open(p, O_RDWR|O_APPEND)) == SYSERROR) { could_not("open", p); /* NOTREACHED */ } patt = NULLSTR; i = 1; if (argc > 1) { if (strchr(argv[i], EQ) == NULLSTR) patt = argv[i++]; while (--argc >= i) { if (strchr(argv[argc], EQ) == NULLSTR) { usage(); /* NOTREACHED */ } } } if ((p = find(fd, p, patt, &argv[i])) == NULLSTR) { fprintf(stderr, patt == NULLSTR ? "%s: No last command.\n" : "%s: No match for \"%s\".\n", my_name, patt); exit(1); /* NOTREACHED */ } if (print || !edit) fprintf(print ? stdout : stderr, "%s\n", p); if (print) { exit(0); /* NOTREACHED */ } if ((sh = getenv(SHELL)) == NULLSTR) sh = SH; p[i = strlen(p)] = '\n'; (void)write(fd, p, i + 1); (void)close(fd); p[i] = '\0'; if ((q = strrchr(sh, '/')) == NULLSTR || *q++ == '\0') q = sh; execl(sh, q, "-c", p, (char *)0); could_not("exec", sh); }