|
|
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: main(argc, argv)
151: char *argv[];
152: {
153: register FILE *f; FILE *getline();
154: register nsubst, i;
155: char *strchr();
156: int edit=0;
157: int print=0;
158: cmd=argv[0];
159: if(cmd[0]!='\0'){
160: if(cmd[1]==cmd[0])
161: edit++;
162: if(cmd[1]=='p' || cmd[1]!='\0' && cmd[2]=='p')
163: print++;
164: }
165: for(nsubst=0;argc>1 && strchr(argv[argc-1], cmd[0])!=NULL;nsubst++){
166: --argc;
167: edit=0;
168: }
169: histname=getenv("HISTORY");
170: f = getline(argc, argv);
171: for(i=0;i!=nsubst;i++)
172: alter(argv[argc+i]);
173: if(edit){
174: if((tty=fopen("/dev/tty", "r"))==0)
175: tty=stdin;
176: xform(f);
177: }
178: fclose(f);
179: if((f=fopen(histname, "a"))!=NULL){
180: fprintf(f, "%s\n", linebuf);
181: fclose(f);
182: }
183: if(print)
184: dumpline(1);
185: else{
186: if(!edit)
187: dumpline(2);
188: execl("/bin/sh", "sh", "-c", linebuf, (char *)0);
189: error("No shell!\n");
190: }
191: }
192: dumpline(fd){
193: /* write on fd directly for speed */
194: write(fd, linebuf, strlen(linebuf));
195: write(2, "\n", 1);
196: }
197: /*
198: * Look at $HISTORY. If argc==1 get the last non-blank line in the file.
199: * Otherwise, argv[1] is a pattern to match against the lines of the history
200: * file. The last matching line wins.
201: */
202: FILE *
203: getline(argc, argv)
204: char *argv[];
205: {
206: char history[LBSIZE];
207: char pat[LBSIZE];
208: register char *hp;
209: register FILE *f;
210: register i;
211: int nmatch=0, ntell;
212: if(histname==NULL)
213: error("Environment lacks HISTORY\n");
214: if((f=fopen(histname, "r"))==NULL){
215: perror(histname);
216: exit(1);
217: }
218: pat[0]='\0';
219: for(i=1;i!=argc && argv[i][0]!=cmd[0];i++){
220: strcat(pat, argv[i]);
221: strcat(pat, " ");
222: }
223: pat[strlen(pat)-1]='\0'; /* annul the extra space */
224: while(ntell=ftell(f), fgets(history, LBSIZE, f)){
225: /*
226: * Skip leading blanks or tabs
227: */
228: for(hp=history;*hp==' '||*hp=='\t';hp++)
229: ;
230: if(anyequals(hp))
231: continue;
232: /*
233: * Welcome to the land of snakey logic
234: */
235: if(pat[0]=='\0'?!empty(hp):match(hp, pat)){
236: linesave(ntell, nmatch++);
237: strncpy(linebuf, hp, LBSIZE);
238: }
239: }
240: if(nmatch==0)
241: error("Can't find a line to redo");
242: linebuf[strlen(linebuf)-1]='\0'; /* delete a newline */
243: return f;
244: }
245:
246: #define VECSIZE 512
247: int *matchvec, *matchend, *lastmatch, *lastuniq, vecsize;
248:
249: linesave(adr, i)
250: {
251: if (matchvec == 0)
252: matchend = matchvec = Malloc(int, vecsize = VECSIZE);
253: if (i >= vecsize)
254: matchvec = Realloc(int, matchvec, vecsize += VECSIZE);
255: if (matchvec == 0)
256: return;
257: *matchend++ = adr;
258: }
259:
260: prevline(f)
261: FILE *f;
262: {
263: char history[LBSIZE]; char *strchr(), *savestr();
264: register char *hp; register int *ip;
265: if (matchvec == 0)
266: return;
267: if (lastmatch == 0)
268: lastuniq = matchend, lastmatch = --matchend;
269: *--lastuniq = (int)savestr(linebuf);
270: do {
271: if (--lastmatch < matchvec) {
272: matchvec = 0;
273: return;
274: }
275: fseek(f, *lastmatch, 0);
276: fgets(history, LBSIZE, f);
277: for(hp=history;*hp==' '||*hp=='\t';hp++)
278: ;
279: *strchr(hp,'\n') = '\0';
280: for (ip=lastuniq; ip <= matchend; ip++)
281: if (strcmp((char *)(*ip), hp) == 0) break;
282: } while (ip <= matchend);
283: strncpy(linebuf, hp, LBSIZE);
284: }
285:
286: char *
287: savestr(str) /* Place string into permanent storage. */
288: register char *str;
289: {
290: static int nchleft; static char *strpt;
291: register int len; char *strcpy();
292:
293: if ((len = strlen(str)+1) > nchleft) {
294: strpt = malloc(nchleft = 2*LBSIZE);
295: }
296: if (strpt == 0)
297: return 0;
298: str = strcpy(strpt, str);
299: strpt += len; nchleft -= len;
300: return str;
301: }
302:
303: /*
304: * Throw away any command with an = or == word
305: */
306: anyequals(line)
307: register char *line;
308: {
309: register char *p, *q;
310: for(p=line; *p; p++)
311: if(*p=='=' && (p==line || p[-1]==' ' || p[-1]=='\t')){
312: if(p[1]=='=')
313: p++;
314: if(p[1]=='\n' || p[1]==' ' || p[1]=='\t')
315: return 1;
316: }
317: return 0;
318: }
319: match(s, p)
320: register char *s, *p;
321: {
322: register char *h;
323: for(h=s;*s!='\n' && *s!=' ' && *s!='\t' && *s!='\0';s++)
324: if(*s=='/')
325: h=s+1;
326: return(startis(h, p));
327: }
328: startis(s, p)
329: register char *s, *p;
330: {
331: while(*p!='\0')
332: if(*p==' '){
333: if(*s!=' ' && *s!='\t')
334: return(0);
335: while(*s==' ' || *s=='\t')
336: s++;
337: p++;
338: }
339: else if(*s++ != *p++)
340: return(0);
341: return(1);
342: }
343: empty(s)
344: register char *s;
345: {
346: if(*s==cmd[0])
347: return(1);
348: while(*s!='\0'){
349: if(*s!=' ' && *s!='\t' && *s!='\n')
350: return(0);
351: s++;
352: }
353: return(1);
354: }
355: alter(pat)
356: register char *pat;
357: {
358: register char *sub, *s=linebuf, *t=genbuf;
359: int patlen, sublen, matched=0;
360: for(sub=pat;*sub!=cmd[0] && *sub!='\0';sub++);
361: if(*sub!=cmd[0])
362: error("Bad Substitution");
363: patlen=sub-pat;
364: if(*sub==cmd[0])
365: *sub++='\0';
366: sublen=strlen(sub);
367: while(*s)
368: if(!matched && startis(s, pat)){
369: matched++;
370: s+=patlen;
371: strcpy(t, sub);
372: t+=sublen;
373: }
374: else
375: *t++ = *s++;
376: if(!matched)
377: error("No pattern match");
378: *t='\0';
379: strcpy(linebuf, genbuf);
380: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.