|
|
1.1 root 1: /*% cc -O %
2: * =, == -- redo commands from history
3: */
4: #include <stdio.h>
5: #define TRUE 1
6: #define FALSE 0
7: #define LBSIZE 512
8:
9: char *malloc(), *realloc();
10:
11: #define Malloc(type,size) ((type *)malloc((size)*sizeof(type)))
12: #define Realloc(type,ptr,size) ((type *)realloc(ptr,(size)*sizeof(type)))
13:
14: char linebuf[LBSIZE];
15: char *linebp;
16: char *getenv();
17: char *cmd;
18: char genbuf[LBSIZE];
19: #define RHSIZE 512
20: char rhsbuf[RHSIZE];
21: FILE *tty;
22: int nextcol(col,cp,input)
23: register col;
24: register char *cp;
25: int input;
26: {
27: register char c;
28: c = *cp;
29: if (c=='\t')
30: col |= 07;
31: else if (c<' ' || c=='\177')
32: error("Invalid character in command");
33: return (++col);
34: }
35: xform(hfile)
36: FILE *hfile;
37: {
38: register char *i, *m, *o;
39: int *line, insert, ic, mc, c;
40: char *tf, *tl;
41: for(;;){
42: dumpline(2);
43: m=rhsbuf;
44: while ((c = getc(tty))!='\n') {
45: if (c == EOF)
46: exit(0);
47: *m++ = c;
48: if (m==rhsbuf+RHSIZE-1)
49: error("Out of space");
50: }
51: *m='\0';
52: if (m==rhsbuf)
53: break;
54: if (rhsbuf[0] == '=' && rhsbuf[1] == 0) {
55: prevline(hfile);
56: continue;
57: }
58: i=linebuf;
59: o=genbuf;
60: do ; while (*o++ = *i++);
61: if (i+(m-rhsbuf) > linebuf+LBSIZE)
62: error("Out of space");
63: i=genbuf;
64: o=linebuf;
65: m=rhsbuf;
66: insert=FALSE;
67: ic=0;
68: mc=0;
69: while (*i && *m && !insert) {
70: if(*i=='\t' && *m!='#' && *m!='^' && *m!='$') {
71: ic=nextcol(ic,i,FALSE);
72: tf=m;
73: tl=m;
74: do {
75: if (*m!=' ' && *m!='\t') {
76: if(*m=='%')
77: *m=' ';
78: tl=m+1;
79: }
80: mc=nextcol(mc,m++,TRUE);
81: } while (ic>mc && *m && *m!='#' &&
82: *m!='^' && *m!='$');
83: if (ic>mc) {
84: ic=mc;
85: if (*m)
86: tl=m;
87: } else {
88: if (tl==m)
89: i++;
90: else
91: ic--;
92: }
93: while (tf!=tl)
94: *o++ = *tf++;
95: } else {
96: mc=nextcol(mc,m,TRUE);
97: *o = *m;
98: switch (*m++) {
99: case ' ':
100: case '\t':
101: break;
102: case '^':
103: mc=ic;
104: insert++;
105: break;
106: case '$':
107: i="";
108: break;
109: case '#':
110: ic=nextcol(ic,i++,FALSE);
111: while(*m=='#' && ic>mc)
112: mc=nextcol(mc,m++,TRUE);
113: if (ic!=mc)
114: error("Partly deleted tab");
115: break;
116: case '%':
117: *o = ' ';
118: /* fall through */
119: default:
120: o++;
121: ic=nextcol(ic,i++,FALSE);
122: }
123: }
124: for (;;) {
125: if (ic>mc && *m) {
126: if (*m!=' ' && *m!='\t')
127: error("Skipped non-blank");
128: mc=nextcol(mc,m++,TRUE);
129: } else if (mc>ic && *i) {
130: ic=nextcol(ic,i,FALSE);
131: *o++ = *i++;
132: } else
133: break;
134: }
135: }
136: if (mc>ic && m[-1]=='\t')
137: *o++ = '\t';
138: while (*m)
139: *o++ = *m++;
140: do ; while (*o++ = *i++);
141: }
142: }
143: error(s)
144: char *s;
145: {
146: fprintf(stderr, "%s: %s\n", cmd, s);
147: exit(1);
148: }
149: char *histname;
150: char *shellname;
151: main(argc, argv)
152: char *argv[];
153: {
154: register FILE *f; FILE *getline();
155: register nsubst, i;
156: char *strchr();
157: int edit=0;
158: int print=0;
159: cmd=argv[0];
160: if(cmd[0]!='\0'){
161: if(cmd[1]==cmd[0])
162: edit++;
163: if(cmd[1]=='p' || cmd[1]!='\0' && cmd[2]=='p')
164: print++;
165: }
166: for(nsubst=0;argc>1 && strchr(argv[argc-1], cmd[0])!=NULL;nsubst++)
167: --argc;
168: histname=getenv("HISTORY");
169: shellname=getenv("SHELL");
170: if (shellname == 0 || *shellname == 0)
171: shellname = "/bin/sh";
172: f = getline(argc, argv);
173: for(i=0;i!=nsubst;i++)
174: alter(argv[argc+i]);
175: if(edit){
176: if((tty=fopen("/dev/tty", "r"))==0)
177: tty=stdin;
178: xform(f);
179: }
180: fclose(f);
181: if((f=fopen(histname, "a"))!=NULL){
182: fprintf(f, "%s\n", linebuf);
183: fclose(f);
184: }
185: if(print)
186: dumpline(1);
187: else{
188: if(!edit)
189: dumpline(2);
190: execl(shellname, "sh", "-c", linebuf, (char *)0);
191: error("No shell!\n");
192: }
193: }
194: dumpline(fd){
195: /* write on fd directly for speed */
196: write(fd, linebuf, strlen(linebuf));
197: write(2, "\n", 1);
198: }
199: /*
200: * Look at $HISTORY. If argc==1 get the last non-blank line in the file.
201: * Otherwise, argv[1] is a pattern to match against the lines of the history
202: * file. The last matching line wins.
203: */
204: FILE *
205: getline(argc, argv)
206: char *argv[];
207: {
208: char history[LBSIZE];
209: char pat[LBSIZE];
210: register char *hp;
211: register FILE *f;
212: register i;
213: int nmatch=0, ntell;
214: if(histname==NULL)
215: error("Environment lacks HISTORY\n");
216: if((f=fopen(histname, "r"))==NULL){
217: perror(histname);
218: exit(1);
219: }
220: pat[0]='\0';
221: for(i=1;i!=argc && argv[i][0]!=cmd[0];i++){
222: strcat(pat, argv[i]);
223: strcat(pat, " ");
224: }
225: pat[strlen(pat)-1]='\0'; /* annul the extra space */
226: while(ntell=ftell(f), fgets(history, LBSIZE, f)){
227: /*
228: * Skip leading blanks or tabs
229: */
230: for(hp=history;*hp==' '||*hp=='\t';hp++)
231: ;
232: if(anyequals(hp))
233: continue;
234: /*
235: * Welcome to the land of snakey logic
236: */
237: if(pat[0]=='\0'?!empty(hp):match(hp, pat)){
238: linesave(ntell, nmatch++);
239: strncpy(linebuf, hp, LBSIZE);
240: }
241: }
242: if(nmatch==0)
243: error("Can't find a line to redo");
244: linebuf[strlen(linebuf)-1]='\0'; /* delete a newline */
245: return f;
246: }
247:
248: #define VECSIZE 512
249: int *matchvec, *matchend, *lastmatch, *lastuniq, vecsize;
250:
251: linesave(adr, i)
252: {
253: if (matchvec == 0)
254: matchend = matchvec = Malloc(int, vecsize = VECSIZE);
255: if (i >= vecsize)
256: matchvec = Realloc(int, matchvec, vecsize += VECSIZE);
257: if (matchvec == 0)
258: return;
259: *matchend++ = adr;
260: }
261:
262: prevline(f)
263: FILE *f;
264: {
265: char history[LBSIZE]; char *strchr(), *savestr();
266: register char *hp; register int *ip;
267: if (matchvec == 0)
268: return;
269: if (lastmatch == 0)
270: lastuniq = matchend, lastmatch = --matchend;
271: *--lastuniq = (int)savestr(linebuf);
272: do {
273: if (--lastmatch < matchvec) {
274: matchvec = 0;
275: return;
276: }
277: fseek(f, *lastmatch, 0);
278: fgets(history, LBSIZE, f);
279: for(hp=history;*hp==' '||*hp=='\t';hp++)
280: ;
281: *strchr(hp,'\n') = '\0';
282: for (ip=lastuniq; ip <= matchend; ip++)
283: if (strcmp((char *)(*ip), hp) == 0) break;
284: } while (ip <= matchend);
285: strncpy(linebuf, hp, LBSIZE);
286: }
287:
288: char *
289: savestr(str) /* Place string into permanent storage. */
290: register char *str;
291: {
292: static int nchleft; static char *strpt;
293: register int len; char *strcpy();
294:
295: if ((len = strlen(str)+1) > nchleft) {
296: strpt = malloc(nchleft = 2*LBSIZE);
297: }
298: if (strpt == 0)
299: return 0;
300: str = strcpy(strpt, str);
301: strpt += len; nchleft -= len;
302: return str;
303: }
304:
305: /*
306: * Throw away any command with an = or == word
307: */
308: anyequals(line)
309: register char *line;
310: {
311: register char *p, *q;
312: for(p=line; *p; p++)
313: if(*p=='=' && (p==line || p[-1]==' ' || p[-1]=='\t')){
314: if(p[1]=='=')
315: p++;
316: if(p[1]=='\n' || p[1]==' ' || p[1]=='\t')
317: return 1;
318: }
319: return 0;
320: }
321: match(s, p)
322: register char *s, *p;
323: {
324: register char *h;
325: for(h=s;*s!='\n' && *s!=' ' && *s!='\t' && *s!='\0';s++)
326: if(*s=='/')
327: h=s+1;
328: return(startis(h, p));
329: }
330: startis(s, p)
331: register char *s, *p;
332: {
333: while(*p!='\0')
334: if(*p==' '){
335: if(*s!=' ' && *s!='\t')
336: return(0);
337: while(*s==' ' || *s=='\t')
338: s++;
339: p++;
340: }
341: else if(*s++ != *p++)
342: return(0);
343: return(1);
344: }
345: empty(s)
346: register char *s;
347: {
348: if(*s==cmd[0])
349: return(1);
350: while(*s!='\0'){
351: if(*s!=' ' && *s!='\t' && *s!='\n')
352: return(0);
353: s++;
354: }
355: return(1);
356: }
357: alter(pat)
358: register char *pat;
359: {
360: register char *sub, *s=linebuf, *t=genbuf;
361: int patlen, sublen, matched=0;
362: for(sub=pat;*sub!=cmd[0] && *sub!='\0';sub++);
363: if(*sub!=cmd[0])
364: error("Bad Substitution");
365: patlen=sub-pat;
366: if(*sub==cmd[0])
367: *sub++='\0';
368: sublen=strlen(sub);
369: while(*s)
370: if(!matched && startis(s, pat)){
371: matched++;
372: s+=patlen;
373: strcpy(t, sub);
374: t+=sublen;
375: }
376: else
377: *t++ = *s++;
378: if(!matched)
379: error("No pattern match");
380: *t='\0';
381: strcpy(linebuf, genbuf);
382: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.