Commit | Line | Data |
---|---|---|
dbf02a84 WJ |
1 | /* |
2 | * | |
3 | * hist.c - history expansion | |
4 | * | |
5 | * This file is part of zsh, the Z shell. | |
6 | * | |
7 | * This software is Copyright 1992 by Paul Falstad | |
8 | * | |
9 | * Permission is hereby granted to copy, reproduce, redistribute or otherwise | |
10 | * use this software as long as: there is no monetary profit gained | |
11 | * specifically from the use or reproduction of this software, it is not | |
12 | * sold, rented, traded or otherwise marketed, and this copyright notice is | |
13 | * included prominently in any copy made. | |
14 | * | |
15 | * The author make no claims as to the fitness or correctness of this software | |
16 | * for any use whatsoever, and it is provided as is. Any use of this software | |
17 | * is at the user's own risk. | |
18 | * | |
19 | */ | |
20 | ||
21 | #include "zsh.h" | |
22 | ||
23 | #define HEAPSIZE 4096 | |
24 | ||
25 | struct hp { | |
26 | Hp next; | |
27 | char *pool,*ptr; | |
28 | int free,histno; | |
29 | }; | |
30 | ||
31 | static Hp hp_lit, hp_lex; | |
32 | static Histent curhistent; | |
33 | ||
34 | static int lastc; | |
35 | ||
36 | /* add a character to the current history word */ | |
37 | ||
38 | void hwaddc(c) /**/ | |
39 | int c; | |
40 | { | |
41 | if (hlastw && hline && (!(errflag || lexstop) || c == HISTSPACE)) { | |
42 | if (c == '!' && unset(NOBANGHIST)) hwaddc('\\'); | |
43 | *hptr++ = c; | |
44 | if (hptr-hline >= hlinesz) { | |
45 | int ll,flag = 0,oldsiz = hlinesz; | |
46 | ||
47 | ll = hptr-hlastw; | |
48 | if (curhistent->lex == hline) flag = 1; | |
49 | hline = hp_realloc(&hp_lex,hline,oldsiz,hlinesz = oldsiz+16); | |
50 | if (flag) curhistent->lex = hline; | |
51 | hptr = hline+oldsiz; | |
52 | hlastw = hptr-ll; | |
53 | } | |
54 | } | |
55 | } | |
56 | ||
57 | #define habort() { errflag = lexstop = 1; return ' '; } | |
58 | ||
59 | /* get a character after performing history substitution */ | |
60 | ||
61 | int hgetc() /**/ | |
62 | { | |
63 | int c,ev,farg,larg,argc,marg = -1,cflag = 0,bflag = 0; | |
64 | char buf[256],*ptr; | |
65 | char *sline,*eline; | |
66 | ||
67 | tailrec: | |
68 | c = hgetch(); | |
69 | if (stophist || alstackind) | |
70 | { | |
71 | hwaddc(c); | |
72 | return c; | |
73 | } | |
74 | if (isfirstch && c == hatchar) | |
75 | { | |
76 | isfirstch = 0; | |
77 | hungetch(hatchar); | |
78 | hungets(":s"); | |
79 | c = bangchar; | |
80 | goto hatskip; | |
81 | } | |
82 | if (c != ' ') | |
83 | isfirstch = 0; | |
84 | if (c == '\\') { | |
85 | int g = hgetch(); | |
86 | ||
87 | if (g != bangchar) | |
88 | hungetch(g); | |
89 | else { | |
90 | hwaddc(bangchar); | |
91 | return bangchar; | |
92 | } | |
93 | } | |
94 | if (c != bangchar) | |
95 | { | |
96 | hwaddc(c); | |
97 | return c; | |
98 | } | |
99 | hatskip: | |
100 | *hptr = '\0'; | |
101 | if ((c = hgetch()) == '{') | |
102 | { | |
103 | bflag = cflag = 1; | |
104 | c = hgetch(); | |
105 | } | |
106 | if (c == '\"') | |
107 | { | |
108 | stophist = 1; | |
109 | goto tailrec; | |
110 | } | |
111 | if (!cflag && inblank(c) || c == '=' || c == '(' || lexstop) | |
112 | { | |
113 | if (lexstop) | |
114 | lexstop = 0; | |
115 | else | |
116 | hungetch(c); | |
117 | hwaddc(bangchar); | |
118 | return bangchar; | |
119 | } | |
120 | cflag = 0; | |
121 | ptr = buf; | |
122 | ||
123 | /* get event number */ | |
124 | ||
125 | if (c == '?') | |
126 | { | |
127 | for(;;) | |
128 | { | |
129 | c = hgetch(); | |
130 | if (c == '?' || c == '\n' || lexstop) | |
131 | break; | |
132 | else | |
133 | *ptr++ = c; | |
134 | } | |
135 | if (c != '\n' && !lexstop) | |
136 | c = hgetch(); | |
137 | *ptr = '\0'; | |
138 | ev = hconsearch(hsubl = ztrdup(buf),&marg); | |
139 | if (ev == -1) | |
140 | { | |
141 | herrflush(); | |
142 | zerr("no such event: %s",buf,0); | |
143 | habort(); | |
144 | } | |
145 | } | |
146 | else | |
147 | { | |
148 | int t0; | |
149 | ||
150 | for (;;) | |
151 | { | |
152 | if (inblank(c) || c == ';' || c == ':' || c == '^' || c == '$' || | |
153 | c == '*' || c == '%' || c == '}' || lexstop) | |
154 | break; | |
155 | if (ptr != buf) { | |
156 | if (c == '-') break; | |
157 | if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c)) break; | |
158 | } | |
159 | *ptr++ = c; | |
160 | if (c == '#' || c == bangchar) | |
161 | { | |
162 | c = hgetch(); | |
163 | break; | |
164 | } | |
165 | c = hgetch(); | |
166 | } | |
167 | *ptr = 0; | |
168 | if (!*buf) | |
169 | ev = defev; | |
170 | else if (t0 = atoi(buf)) | |
171 | ev = (t0 < 0) ? curhist+t0 : t0; | |
172 | else if (*buf == bangchar) | |
173 | ev = curhist-1; | |
174 | else if (*buf == '#') | |
175 | ev = curhist; | |
176 | else if ((ev = hcomsearch(buf)) == -1) | |
177 | { | |
178 | zerr("event not found: %s",buf,0); | |
179 | while (c != '\n' && !lexstop) | |
180 | c = hgetch(); | |
181 | habort(); | |
182 | } | |
183 | } | |
184 | ||
185 | /* get the event */ | |
186 | ||
187 | if (!(eline = getevent(defev = ev))) | |
188 | habort(); | |
189 | ||
190 | /* extract the relevant arguments */ | |
191 | ||
192 | argc = getargc(eline); | |
193 | if (c == ':') | |
194 | { | |
195 | cflag = 1; | |
196 | c = hgetch(); | |
197 | } | |
198 | if (c == '*') | |
199 | { | |
200 | farg = 1; | |
201 | larg = argc; | |
202 | cflag = 0; | |
203 | } | |
204 | else | |
205 | { | |
206 | hungetch(c); | |
207 | larg = farg = getargspec(argc,marg); | |
208 | if (larg == -2) | |
209 | habort(); | |
210 | if (farg != -1) | |
211 | cflag = 0; | |
212 | c = hgetch(); | |
213 | if (c == '*') | |
214 | { | |
215 | cflag = 0; | |
216 | larg = argc; | |
217 | } | |
218 | else if (c == '-') | |
219 | { | |
220 | cflag = 0; | |
221 | larg = getargspec(argc,marg); | |
222 | if (larg == -2) | |
223 | habort(); | |
224 | if (larg == -1) | |
225 | larg = argc-1; | |
226 | } | |
227 | else | |
228 | hungetch(c); | |
229 | } | |
230 | if (farg == -1) | |
231 | farg = 0; | |
232 | if (larg == -1) | |
233 | larg = argc; | |
234 | if (!(sline = getargs(eline,farg,larg))) | |
235 | habort(); | |
236 | ||
237 | /* do the modifiers */ | |
238 | ||
239 | for(;;) | |
240 | { | |
241 | c = (cflag) ? ':' : hgetch(); | |
242 | cflag = 0; | |
243 | if (c == ':') | |
244 | { | |
245 | int gbal = 0; | |
246 | ||
247 | if ((c = hgetch()) == 'g') | |
248 | { | |
249 | gbal = 1; | |
250 | c = hgetch(); | |
251 | } | |
252 | switch(c) | |
253 | { | |
254 | case 'p': | |
255 | histdone = HISTFLAG_DONE|HISTFLAG_NOEXEC; | |
256 | break; | |
257 | case 'h': | |
258 | if (!remtpath(&sline)) | |
259 | { | |
260 | herrflush(); | |
261 | zerr("modifier failed: h",NULL,0); | |
262 | habort(); | |
263 | } | |
264 | break; | |
265 | case 'e': | |
266 | if (!rembutext(&sline)) | |
267 | { | |
268 | herrflush(); | |
269 | zerr("modifier failed: e",NULL,0); | |
270 | habort(); | |
271 | } | |
272 | break; | |
273 | case 'r': | |
274 | if (!remtext(&sline)) | |
275 | { | |
276 | herrflush(); | |
277 | zerr("modifier failed: r",NULL,0); | |
278 | habort(); | |
279 | } | |
280 | break; | |
281 | case 't': | |
282 | if (!remlpaths(&sline)) | |
283 | { | |
284 | herrflush(); | |
285 | zerr("modifier failed: t",NULL,0); | |
286 | habort(); | |
287 | } | |
288 | break; | |
289 | case 's': | |
290 | { | |
291 | int del; | |
292 | char *ptr1,*ptr2; | |
293 | ||
294 | del = hgetch(); | |
295 | ptr1 = hdynread2(del); | |
296 | if (!ptr1) | |
297 | habort(); | |
298 | ptr2 = hdynread2(del); | |
299 | if (strlen(ptr1)) | |
300 | { | |
301 | if (hsubl) | |
302 | free(hsubl); | |
303 | hsubl = ptr1; | |
304 | } | |
305 | if (hsubr) | |
306 | free(hsubr); | |
307 | hsubr = ptr2; | |
308 | } | |
309 | case '&': | |
310 | if (hsubl && hsubr) | |
311 | subst(&sline,hsubl,hsubr,gbal); | |
312 | else | |
313 | { | |
314 | herrflush(); | |
315 | zerr("no previous substitution with &",NULL,0); | |
316 | habort(); | |
317 | } | |
318 | break; | |
319 | case 'q': | |
320 | quote(&sline); | |
321 | break; | |
322 | case 'x': | |
323 | quotebreak(&sline); | |
324 | break; | |
325 | case 'l': | |
326 | downcase(&sline); | |
327 | break; | |
328 | case 'u': | |
329 | upcase(&sline); | |
330 | break; | |
331 | default: | |
332 | herrflush(); | |
333 | zerr("illegal modifier: %c",NULL,c); | |
334 | habort(); | |
335 | break; | |
336 | } | |
337 | } | |
338 | else | |
339 | { | |
340 | if (c != '}' || !bflag) | |
341 | hungetch(c); | |
342 | if (c != '}' && bflag) | |
343 | { | |
344 | zerr("'}' expected",NULL,0); | |
345 | habort(); | |
346 | } | |
347 | break; | |
348 | } | |
349 | } | |
350 | ||
351 | /* stuff the resulting string in the input queue and start over */ | |
352 | ||
353 | lexstop = 0; | |
354 | if (alstackind != MAXAL) | |
355 | { | |
356 | hungets(HISTMARK); | |
357 | alstack[alstackind++] = NULL; | |
358 | } | |
359 | for (ptr = sline; *ptr; ptr++) { | |
360 | if (ptr[0] == '\\' && ptr[1] == '!') chuck(ptr); | |
361 | } | |
362 | hungets(sline); | |
363 | histdone |= HISTFLAG_DONE; | |
364 | if (isset(HISTVERIFY)) histdone |= HISTFLAG_NOEXEC|HISTFLAG_RECALL; | |
365 | goto tailrec; | |
366 | } | |
367 | ||
368 | /* reset the alias stack for lexrestore () */ | |
369 | ||
370 | void clearalstack() /**/ | |
371 | { | |
372 | Alias ix; | |
373 | ||
374 | while (alstackind) | |
375 | { | |
376 | ix = alstack[--alstackind]; | |
377 | ix->inuse = 0; | |
378 | } | |
379 | } | |
380 | ||
381 | /* get a character without history expansion */ | |
382 | ||
383 | int hgetch() /**/ | |
384 | { | |
385 | unsigned char *line,*pmpt,*pmpt2 = NULL; | |
386 | int plen; | |
387 | ||
388 | start: | |
389 | if (inbufct) | |
390 | { | |
391 | inbufct--; | |
392 | if ((lastc = *inbufptr++) == ALPOP) | |
393 | { | |
394 | Alias ix; | |
395 | char *t; | |
396 | ||
397 | if (!alstackind) | |
398 | { | |
399 | zerr("alias stack underflow",NULL,0); | |
400 | errflag = lexstop = 1; | |
401 | return lastc = ' '; | |
402 | } | |
403 | ix = alstack[--alstackind]; | |
404 | if (ix) | |
405 | { | |
406 | ix->inuse = 0; | |
407 | t = ix->text; | |
408 | if (*t && t[strlen(t)-1] == ' ') | |
409 | alstat = ALSTAT_MORE; | |
410 | else | |
411 | alstat = ALSTAT_JUNK; | |
412 | } | |
413 | goto start; | |
414 | } | |
415 | if (itok(lastc)) | |
416 | goto start; | |
417 | return lastc; | |
418 | } | |
419 | if (strin || errflag) | |
420 | { | |
421 | lexstop = 1; | |
422 | return lastc = ' '; | |
423 | } | |
424 | if (interact && isset(SHINSTDIN)) | |
425 | if (!isfirstln) | |
426 | pmpt = (unsigned char *)putprompt(prompt2,&plen); | |
427 | else | |
428 | { | |
429 | int foo; | |
430 | ||
431 | pmpt = (unsigned char *)putprompt(prompt,&plen); | |
432 | pmpt2 = (unsigned char *)((rprompt) ? putprompt(rprompt,&foo) : NULL); | |
433 | } | |
434 | if (!(interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))) { | |
435 | char *lbuf; | |
436 | if (interact && isset(SHINSTDIN)) | |
437 | write(2,pmpt,strlen((char *) pmpt)); | |
438 | line = (unsigned char *)fgets(lbuf = zalloc(256),256,bshin); | |
439 | if (!line) free(lbuf); | |
440 | } else | |
441 | line = zleread(pmpt,pmpt2,plen); | |
442 | if (!line) { | |
443 | lexstop = 1; | |
444 | return lastc = ' '; | |
445 | } | |
446 | if (errflag) { | |
447 | free(line); | |
448 | lexstop = errflag = 1; | |
449 | return lastc = ' '; | |
450 | } | |
451 | if (interact && isset(SHINSTDIN)) { | |
452 | char *s = curhistent->lit; | |
453 | curhistent->lit = hp_concat(s,(char*)line); | |
454 | } | |
455 | if (isfirstln) spaceflag = *line == ' '; | |
456 | if (isset(VERBOSE)) { | |
457 | fputs((char *) line,stderr); | |
458 | fflush(stderr); | |
459 | } | |
460 | if (*line && line[strlen((char *) line)-1] == '\n') | |
461 | { | |
462 | lineno++; | |
463 | if (interact && isset(SUNKEYBOARDHACK) && isset(SHINSTDIN) && | |
464 | SHTTY != -1 && *line && line[1] && | |
465 | line[strlen((char *) line)-2] == '`') | |
466 | { | |
467 | int ct; | |
468 | unsigned char *ptr; | |
469 | ||
470 | for (ct = 0, ptr = line; *ptr; ptr++) | |
471 | if (*ptr == '`') | |
472 | ct++; | |
473 | if (ct & 1) | |
474 | { | |
475 | ptr[-2] = '\n'; | |
476 | ptr[-1] = '\0'; | |
477 | } | |
478 | } | |
479 | } | |
480 | isfirstch = 1; | |
481 | hungets((char*)line); | |
482 | free(line); | |
483 | goto start; | |
484 | } | |
485 | ||
486 | /* Read one line of at most n-1 chars from the input queue */ | |
487 | ||
488 | char *hgets(buf, n) /**/ | |
489 | char *buf;int n; | |
490 | { | |
491 | int l; | |
492 | ||
493 | for (l = 0; l < n-1; l++) | |
494 | if ((buf[l] = hgetch()) == '\n' || lexstop) | |
495 | break; | |
496 | buf[l+(lexstop?0:1)] = 0; | |
497 | ||
498 | return (!lexstop || l) ? buf : NULL; | |
499 | } | |
500 | ||
501 | /* put a string in the input queue */ | |
502 | ||
503 | void hungets(str) /**/ | |
504 | char *str; | |
505 | { | |
506 | int slen = strlen(str); | |
507 | ||
508 | /* shrink inbuf if it gets too big */ | |
509 | ||
510 | if (!inbufct && inbufsz > 65536) | |
511 | { | |
512 | free(inbuf); | |
513 | inbuf = zalloc(inbufsz = 256); | |
514 | inbufptr = inbuf+inbufsz; | |
515 | inbufct = 0; | |
516 | } | |
517 | if (slen+inbufct > inbufsz) | |
518 | { | |
519 | char *x; | |
520 | ||
521 | while (slen+inbufct > inbufsz) | |
522 | inbufsz *= 4; | |
523 | x = zalloc(inbufsz); | |
524 | memcpy(x+inbufsz-inbufct,inbufptr,inbufct); | |
525 | inbufptr = x+inbufsz-inbufct; | |
526 | free(inbuf); | |
527 | inbuf = x; | |
528 | } | |
529 | memcpy(inbufptr -= slen,str,slen); | |
530 | inbufct += slen; | |
531 | } | |
532 | ||
533 | /* unget a char and remove it from hline */ | |
534 | ||
535 | void hungetc(c) /**/ | |
536 | int c; | |
537 | { | |
538 | if (lexstop) | |
539 | return; | |
540 | if (hlastw) { | |
541 | if (hlastw == hptr) | |
542 | zerr("hungetc attempted at buffer start",NULL,0); | |
543 | else { | |
544 | hptr--; | |
545 | if (*hptr == '!' && unset(NOBANGHIST)) hptr--; | |
546 | } | |
547 | } | |
548 | hungetch(c); | |
549 | } | |
550 | ||
551 | void hungetch(c) /**/ | |
552 | int c; | |
553 | { | |
554 | if (lexstop) | |
555 | return; | |
556 | if (inbufct == inbufsz) | |
557 | { | |
558 | hungets(" "); | |
559 | *inbufptr = c; | |
560 | } | |
561 | else | |
562 | { | |
563 | *--inbufptr = c; | |
564 | inbufct++; | |
565 | } | |
566 | } | |
567 | ||
568 | /* begin reading a string */ | |
569 | ||
570 | void strinbeg() /**/ | |
571 | { | |
572 | strin = 1; | |
573 | hbegin(); | |
574 | lexinit(); | |
575 | } | |
576 | ||
577 | /* done reading a string */ | |
578 | ||
579 | void strinend() /**/ | |
580 | { | |
581 | strin = 0; | |
582 | isfirstch = 1; | |
583 | histdone = 0; | |
584 | hend(); | |
585 | } | |
586 | ||
587 | /* stuff a whole file into the input queue and print it */ | |
588 | ||
589 | int stuff(fn) /**/ | |
590 | char *fn; | |
591 | { | |
592 | FILE *in; | |
593 | char *buf; | |
594 | int len; | |
595 | ||
596 | if (!(in = fopen(fn,"r"))) | |
597 | { | |
598 | zerr("can't open %s",fn,0); | |
599 | return 1; | |
600 | } | |
601 | fseek(in,0,2); | |
602 | len = ftell(in); | |
603 | fseek(in,0,0); | |
604 | buf = alloc(len+1); | |
605 | if (!(fread(buf,len,1,in))) | |
606 | { | |
607 | zerr("read error on %s",fn,0); | |
608 | fclose(in); | |
609 | free(buf); | |
610 | return 1; | |
611 | } | |
612 | fclose(in); | |
613 | buf[len] = '\0'; | |
614 | fwrite(buf,len,1,stdout); | |
615 | hungets(buf); | |
616 | return 0; | |
617 | } | |
618 | ||
619 | /* flush input queue */ | |
620 | ||
621 | void hflush() /**/ | |
622 | { | |
623 | inbufptr += inbufct; | |
624 | inbufct = 0; | |
625 | } | |
626 | ||
627 | /* initialize the history mechanism */ | |
628 | ||
629 | void hbegin() /**/ | |
630 | { | |
631 | isfirstln = isfirstch = 1; | |
632 | histremmed = errflag = histdone = spaceflag = 0; | |
633 | stophist = isset(NOBANGHIST) || unset(SHINSTDIN); | |
634 | lithist = isset(HISTLIT); | |
635 | hline = hptr = hp_alloc(&hp_lex,hlinesz = 16); | |
636 | curhistent = gethistent(curhist); | |
637 | if (!curhistent->ftim) curhistent->ftim = time(NULL); | |
638 | if (interact && isset(SHINSTDIN) && !strin) { | |
639 | inittty(); | |
640 | defev = curhist++; | |
641 | if (curhist-histsiz >= 0) gethistent(curhist-histsiz)->lex = NULL; | |
642 | if (curhist-lithistsiz >= 0) gethistent(curhist-lithistsiz)->lit = NULL; | |
643 | curhistent = gethistent(curhist); | |
644 | hp_purge(hp_lex,curhist-histsiz); | |
645 | hp_purge(hp_lit,curhist-lithistsiz); | |
646 | curhistent->lex = hline; | |
647 | *(curhistent->lit = hp_alloc(&hp_lit,1)) = '\0'; | |
648 | } else | |
649 | histremmed = 1; | |
650 | } | |
651 | ||
652 | void inittty() /**/ | |
653 | { | |
654 | attachtty(mypgrp); | |
655 | } | |
656 | ||
657 | /* say we're done using the history mechanism */ | |
658 | ||
659 | int hend() /**/ | |
660 | { | |
661 | int flag,save = 1; | |
662 | Histent he; | |
663 | ||
664 | if (!hline) | |
665 | return 1; | |
666 | if (!interact || strin || unset(SHINSTDIN)) { | |
667 | hp_free(hp_lex,hline,hlinesz); | |
668 | return 1; | |
669 | } | |
670 | flag = histdone; | |
671 | histdone = 0; | |
672 | if (hptr < hline+2) | |
673 | save = 0; | |
674 | else { | |
675 | char *s,*t; | |
676 | ||
677 | s = curhistent->lit; | |
678 | if (*s && *(t = s+strlen(s)-1) == HISTSPACE) *t = '\0'; | |
679 | hptr[-1] = '\0'; | |
680 | if (hptr[-2] == '\n') | |
681 | if (hline[1]) { | |
682 | if (hptr[-3] == HISTSPACE) hptr[-3] = '\0'; | |
683 | } else save = 0; | |
684 | he = gethistent(curhist-1); | |
685 | if (!strcmp(hline,"\n") || | |
686 | (isset(HISTIGNOREDUPS) && he->lex && !strcmp(he->lex,hline)) || | |
687 | (isset(HISTIGNORESPACE) && spaceflag)) | |
688 | save = 0; | |
689 | } | |
690 | if (flag & (HISTFLAG_DONE|HISTFLAG_RECALL)) { | |
691 | char *ptr,*p; | |
692 | p = ptr = ztrdup(hline); | |
693 | for (;*p;p++) if (*p == HISTSPACE) *p = ' '; | |
694 | if ((flag & (HISTFLAG_DONE|HISTFLAG_RECALL)) == HISTFLAG_DONE) { | |
695 | fprintf(stderr,"%s\n",ptr); | |
696 | fflush(stderr); | |
697 | } | |
698 | if (flag & HISTFLAG_RECALL) { | |
699 | permalloc(); | |
700 | pushnode(bufstack,ptr); | |
701 | lastalloc(); | |
702 | save = 0; | |
703 | } else free(ptr); | |
704 | } | |
705 | curhistent->stim = time(NULL); | |
706 | curhistent->ftim = 0L; | |
707 | if (!save) remhist(); | |
708 | if (hline && !curhistent->lex) hp_free(hp_lex,hline,hlinesz); | |
709 | hline = NULL; | |
710 | return !(flag & HISTFLAG_NOEXEC || errflag); | |
711 | } | |
712 | ||
713 | /* remove the current line from the history List */ | |
714 | ||
715 | void remhist() /**/ | |
716 | { | |
717 | if (!histremmed) { histremmed = 1; curhist--; } | |
718 | } | |
719 | ||
720 | /* begin a word */ | |
721 | ||
722 | void hwbegin() /**/ | |
723 | { | |
724 | hlastw = hptr; | |
725 | } | |
726 | ||
727 | /* add a word to the history List */ | |
728 | ||
729 | char *hwadd() /**/ | |
730 | { | |
731 | char *ret = hlastw; | |
732 | ||
733 | if (hlastw && hline) | |
734 | { | |
735 | hwaddc(HISTSPACE); | |
736 | if (alstackind || strin) | |
737 | if (!(alstackind == 1 && !alstack[0])) | |
738 | hptr = hlastw; | |
739 | } | |
740 | if (alstat == ALSTAT_JUNK) | |
741 | alstat = 0; | |
742 | return ret; | |
743 | } | |
744 | ||
745 | /* get an argument specification */ | |
746 | ||
747 | int getargspec(argc,marg) /**/ | |
748 | int argc;int marg; | |
749 | { | |
750 | int c,ret = -1; | |
751 | ||
752 | if ((c = hgetch()) == '0') | |
753 | return 0; | |
754 | if (idigit(c)) | |
755 | { | |
756 | ret = 0; | |
757 | while (idigit(c)) | |
758 | { | |
759 | ret = ret*10+c-'0'; | |
760 | c = hgetch(); | |
761 | } | |
762 | hungetch(c); | |
763 | } | |
764 | else if (c == '^') | |
765 | ret = 1; | |
766 | else if (c == '$') | |
767 | ret = argc; | |
768 | else if (c == '%') | |
769 | { | |
770 | if (marg == -1) | |
771 | { | |
772 | herrflush(); | |
773 | zerr("%% with no previous word matched",NULL,0); | |
774 | return -2; | |
775 | } | |
776 | ret = marg; | |
777 | } | |
778 | else | |
779 | hungetch(c); | |
780 | return ret; | |
781 | } | |
782 | ||
783 | /* do ?foo? search */ | |
784 | ||
785 | int hconsearch(str,marg) /**/ | |
786 | char *str;int *marg; | |
787 | { | |
788 | int t0,t1 = 0; | |
789 | char *s,*hs; | |
790 | ||
791 | for (t0 = curhist-1; hs = quietgetevent(t0); t0--) | |
792 | if (s = ztrstr(hs,str)) { | |
793 | while (s != hs) if (*s-- == HISTSPACE) t1++; | |
794 | *marg = t1; | |
795 | return t0; | |
796 | } | |
797 | return -1; | |
798 | } | |
799 | ||
800 | /* do !foo search */ | |
801 | ||
802 | int hcomsearch(str) /**/ | |
803 | char *str; | |
804 | { | |
805 | int t0; | |
806 | char *hs; | |
807 | ||
808 | for (t0 = curhist-1; hs = quietgetevent(t0); t0--) | |
809 | if (!strncmp(hs,str,strlen(str))) return t0; | |
810 | return -1; | |
811 | } | |
812 | ||
813 | /* various utilities for : modifiers */ | |
814 | ||
815 | int remtpath(junkptr) /**/ | |
816 | char **junkptr; | |
817 | { | |
818 | char *str = *junkptr,*cut; | |
819 | ||
820 | if (cut = strrchr(str,'/')) { | |
821 | if (str != cut) *cut = '\0'; | |
822 | else str[1] = '\0'; | |
823 | return 1; | |
824 | } | |
825 | return 0; | |
826 | } | |
827 | ||
828 | int remtext(junkptr) /**/ | |
829 | char **junkptr; | |
830 | { | |
831 | char *str = *junkptr,*cut; | |
832 | ||
833 | if ((cut = strrchr(str,'.')) && cut != str) | |
834 | { | |
835 | *cut = '\0'; | |
836 | return 1; | |
837 | } | |
838 | return 0; | |
839 | } | |
840 | ||
841 | int rembutext(junkptr) /**/ | |
842 | char **junkptr; | |
843 | { | |
844 | char *str = *junkptr,*cut; | |
845 | ||
846 | if ((cut = strrchr(str,'.')) && cut != str) | |
847 | { | |
848 | *junkptr = strdup(cut+1); /* .xx or xx? */ | |
849 | return 1; | |
850 | } | |
851 | return 0; | |
852 | } | |
853 | ||
854 | int remlpaths(junkptr) /**/ | |
855 | char **junkptr; | |
856 | { | |
857 | char *str = *junkptr,*cut; | |
858 | ||
859 | if (cut = strrchr(str,'/')) | |
860 | { | |
861 | *cut = '\0'; | |
862 | *junkptr = strdup(cut+1); | |
863 | return 1; | |
864 | } | |
865 | return 0; | |
866 | } | |
867 | ||
868 | int makeuppercase(junkptr) /**/ | |
869 | char **junkptr; | |
870 | { | |
871 | char *str = *junkptr; | |
872 | ||
873 | for (; *str; str++) | |
874 | *str = tuupper(*str); | |
875 | return 1; | |
876 | } | |
877 | ||
878 | int makelowercase(junkptr) /**/ | |
879 | char **junkptr; | |
880 | { | |
881 | char *str = *junkptr; | |
882 | ||
883 | for (; *str; str++) | |
884 | *str = tulower(*str); | |
885 | return 1; | |
886 | } | |
887 | ||
888 | void subst(strptr,in,out,gbal) /**/ | |
889 | char **strptr;char *in;char *out;int gbal; | |
890 | { | |
891 | char *str = *strptr,*cut,*sptr; | |
892 | int off; | |
893 | ||
894 | while (cut = (char *) ztrstr(str,in)) { | |
895 | *cut = '\0'; | |
896 | sptr = convamps(out,in); | |
897 | off = cut-*strptr+strlen(sptr); | |
898 | cut += strlen(in); | |
899 | *strptr = tricat(*strptr,sptr,cut); | |
900 | if (gbal) { | |
901 | str = (char *) *strptr+off; | |
902 | continue; | |
903 | } | |
904 | break; | |
905 | } | |
906 | } | |
907 | ||
908 | char *convamps(out,in) /**/ | |
909 | char *out;char *in; | |
910 | { | |
911 | char *ptr,*ret,*pp; | |
912 | int slen,inlen = strlen(in); | |
913 | ||
914 | for (ptr = out, slen = 0; *ptr; ptr++,slen++) | |
915 | if (*ptr == '\\') | |
916 | ptr++; | |
917 | else if (*ptr == '&') | |
918 | slen += inlen-1; | |
919 | ret = pp = alloc(slen+1); | |
920 | for (ptr = out; *ptr; ptr++) | |
921 | if (*ptr == '\\') | |
922 | *pp++ = *++ptr; | |
923 | else if (*ptr == '&') | |
924 | { | |
925 | strcpy(pp,in); | |
926 | pp += inlen; | |
927 | } | |
928 | else | |
929 | *pp++ = *ptr; | |
930 | *pp = '\0'; | |
931 | return ret; | |
932 | } | |
933 | ||
934 | char *makehstr(s) /**/ | |
935 | char *s; | |
936 | { | |
937 | char *t; | |
938 | ||
939 | t = s = strdup(s); | |
940 | for (; *t; t++) | |
941 | if (*t == HISTSPACE) | |
942 | *t = ' '; | |
943 | return s; | |
944 | } | |
945 | ||
946 | char *quietgetevent(ev) /**/ | |
947 | int ev; | |
948 | { | |
949 | Histent ent; | |
950 | ||
951 | if (ev < firsthist()) return NULL; | |
952 | ent = gethistent(ev); | |
953 | return (lithist) ? ent->lit : ent->lex; | |
954 | } | |
955 | ||
956 | char *getevent(ev) /**/ | |
957 | int ev; | |
958 | { | |
959 | char *ret; | |
960 | ||
961 | ret = quietgetevent(ev); | |
962 | if (!ret) { | |
963 | herrflush(); | |
964 | zerr("no such event: %d",NULL,ev); | |
965 | } | |
966 | return ret; | |
967 | } | |
968 | ||
969 | int getargc(list) /**/ | |
970 | char *list; | |
971 | { | |
972 | int argc = 0; | |
973 | ||
974 | for (; *list; list++) if (*list == HISTSPACE) argc++; | |
975 | return argc; | |
976 | } | |
977 | ||
978 | char *getargs(elist,arg1,arg2) /**/ | |
979 | char *elist;int arg1;int arg2; | |
980 | { | |
981 | char *ret = elist,*retn; | |
982 | int acnt = arg2-arg1+1; | |
983 | ||
984 | while (arg1--) | |
985 | while (*ret && *ret++ != HISTSPACE); | |
986 | if (!*ret) | |
987 | { | |
988 | herrflush(); | |
989 | zerr("no such word in event",NULL,0); | |
990 | return NULL; | |
991 | } | |
992 | retn = ret = strdup(ret); | |
993 | while (acnt > 0) | |
994 | { | |
995 | while (*ret && *ret != HISTSPACE) | |
996 | ret++; | |
997 | if (*ret == HISTSPACE) | |
998 | *ret = ' '; | |
999 | else | |
1000 | break; | |
1001 | acnt--; | |
1002 | } | |
1003 | if (acnt > 1 && !*ret) | |
1004 | { | |
1005 | herrflush(); | |
1006 | zerr("no such word in event",NULL,0); | |
1007 | return NULL; | |
1008 | } | |
1009 | *ret = '\0'; | |
1010 | return retn; | |
1011 | } | |
1012 | ||
1013 | void upcase(x) /**/ | |
1014 | char **x; | |
1015 | { | |
1016 | char *pp = *(char **) x; | |
1017 | ||
1018 | for (; *pp; pp++) | |
1019 | *pp = tuupper(*pp); | |
1020 | } | |
1021 | ||
1022 | void downcase(x) /**/ | |
1023 | char **x; | |
1024 | { | |
1025 | char *pp = *(char **) x; | |
1026 | ||
1027 | for (; *pp; pp++) | |
1028 | *pp = tulower(*pp); | |
1029 | } | |
1030 | ||
1031 | int quote(tr) /**/ | |
1032 | char **tr; | |
1033 | { | |
1034 | char *ptr,*rptr,**str = (char **) tr; | |
1035 | int len = 3; | |
1036 | ||
1037 | for (ptr = *str; *ptr; ptr++,len++) | |
1038 | if (*ptr == '\'') len += 3; | |
1039 | ptr = *str; | |
1040 | *str = rptr = alloc(len); | |
1041 | *rptr++ = '\''; | |
1042 | for (; *ptr; ptr++) | |
1043 | if (*ptr == '\'') { | |
1044 | *rptr++ = '\''; *rptr++ = '\\'; *rptr++ = '\''; *rptr++ = '\''; | |
1045 | } else | |
1046 | *rptr++ = *ptr; | |
1047 | *rptr++ = '\''; | |
1048 | *rptr++ = 0; | |
1049 | str[1] = NULL; | |
1050 | return 0; | |
1051 | } | |
1052 | ||
1053 | int quotebreak(tr) /**/ | |
1054 | char **tr; | |
1055 | { | |
1056 | char *ptr,*rptr,**str = (char **) tr; | |
1057 | int len = 3; | |
1058 | ||
1059 | for (ptr = *str; *ptr; ptr++,len++) | |
1060 | if (*ptr == '\'') | |
1061 | len += 3; | |
1062 | else if (inblank(*ptr)) | |
1063 | len += 2; | |
1064 | ptr = *str; | |
1065 | *str = rptr = alloc(len); | |
1066 | *rptr++ = '\''; | |
1067 | for (; *ptr; ) | |
1068 | if (*ptr == '\'') { | |
1069 | *rptr++ = '\''; *rptr++ = '\\'; *rptr++ = '\''; *rptr++ = '\''; | |
1070 | ptr++; | |
1071 | } else if (inblank(*ptr)) { | |
1072 | *rptr++ = '\''; *rptr++ = *ptr++; *rptr++ = '\''; | |
1073 | } else | |
1074 | *rptr++ = *ptr++; | |
1075 | *rptr++ = '\''; | |
1076 | *rptr++ = '\0'; | |
1077 | return 0; | |
1078 | } | |
1079 | ||
1080 | void herrflush() /**/ | |
1081 | { | |
1082 | if (strin) | |
1083 | hflush(); | |
1084 | else while (lastc != '\n' && !lexstop) | |
1085 | hgetch(); | |
1086 | } | |
1087 | ||
1088 | /* read an arbitrary amount of data into a buffer until stop is found */ | |
1089 | ||
1090 | char *hdynread(stop) /**/ | |
1091 | int stop; | |
1092 | { | |
1093 | int bsiz = 256,ct = 0,c; | |
1094 | char *buf = zalloc(bsiz),*ptr; | |
1095 | ||
1096 | ptr = buf; | |
1097 | while ((c = hgetch()) != stop && c != '\n' && !lexstop) | |
1098 | { | |
1099 | if (c == '\\') | |
1100 | c = hgetch(); | |
1101 | *ptr++ = c; | |
1102 | if (++ct == bsiz) | |
1103 | { | |
1104 | buf = realloc(buf,bsiz *= 2); | |
1105 | ptr = buf+ct; | |
1106 | } | |
1107 | } | |
1108 | *ptr = 0; | |
1109 | if (c == '\n') | |
1110 | { | |
1111 | hungetch('\n'); | |
1112 | zerr("delimiter expected",NULL,0); | |
1113 | free(buf); | |
1114 | return NULL; | |
1115 | } | |
1116 | return buf; | |
1117 | } | |
1118 | ||
1119 | char *hdynread2(stop) /**/ | |
1120 | int stop; | |
1121 | { | |
1122 | int bsiz = 256,ct = 0,c; | |
1123 | char *buf = zalloc(bsiz),*ptr; | |
1124 | ||
1125 | ptr = buf; | |
1126 | while ((c = hgetch()) != stop && c != '\n' && !lexstop) | |
1127 | { | |
1128 | if (c == '\n') | |
1129 | { | |
1130 | hungetch(c); | |
1131 | break; | |
1132 | } | |
1133 | if (c == '\\') | |
1134 | c = hgetch(); | |
1135 | *ptr++ = c; | |
1136 | if (++ct == bsiz) | |
1137 | { | |
1138 | buf = realloc(buf,bsiz *= 2); | |
1139 | ptr = buf+ct; | |
1140 | } | |
1141 | } | |
1142 | *ptr = 0; | |
1143 | if (c == '\n') | |
1144 | hungetch('\n'); | |
1145 | return buf; | |
1146 | } | |
1147 | ||
1148 | void inithist() /**/ | |
1149 | { | |
1150 | hp_lit = zalloc(sizeof *hp_lit); | |
1151 | hp_lit->next = NULL; | |
1152 | hp_lit->ptr = hp_lit->pool = zalloc(HEAPSIZE); | |
1153 | hp_lit->free = HEAPSIZE; | |
1154 | hp_lex = zalloc(sizeof *hp_lex); | |
1155 | hp_lex->next = NULL; | |
1156 | hp_lex->ptr = hp_lex->pool = zalloc(HEAPSIZE); | |
1157 | hp_lex->free = HEAPSIZE; | |
1158 | histentct = (lithistsiz > histsiz) ? lithistsiz : histsiz; | |
1159 | histentarr = zcalloc(histentct*sizeof *histentarr); | |
1160 | } | |
1161 | ||
1162 | void resizehistents() /**/ | |
1163 | { | |
1164 | int newentct,t0,t1,firstlit,firstlex; | |
1165 | Histent newarr; | |
1166 | ||
1167 | newentct = (lithistsiz > histsiz) ? lithistsiz : histsiz; | |
1168 | newarr = zcalloc(newentct*sizeof *newarr); | |
1169 | firstlex = curhist-histsiz+1; | |
1170 | firstlit = curhist-lithistsiz+1; | |
1171 | t0 = firsthist(); | |
1172 | if (t0 < curhist-newentct) t0 = curhist-newentct; | |
1173 | t1 = t0 % newentct; | |
1174 | for (; t0 <= curhist; t0++) { | |
1175 | newarr[t1] = *gethistent(t0); | |
1176 | if (t0 < firstlex) newarr[t1].lex = NULL; | |
1177 | if (t0 < firstlit) newarr[t1].lit = NULL; | |
1178 | t1++; if (t1 == newentct) t1 = 0; | |
1179 | } | |
1180 | free(histentarr); | |
1181 | histentarr = newarr; | |
1182 | histentct = newentct; | |
1183 | } | |
1184 | ||
1185 | char *hp_alloc(hp,siz) /**/ | |
1186 | Hp *hp; int siz; | |
1187 | { | |
1188 | char *ret; | |
1189 | Hp h = *hp; | |
1190 | ||
1191 | if (h->free >= siz) { | |
1192 | ret = h->ptr; | |
1193 | h->ptr += siz; | |
1194 | h->free -= siz; | |
1195 | return ret; | |
1196 | } | |
1197 | #ifdef MEMDEBUG | |
1198 | fprintf(stderr,"new heap (siz = %d, curhist = %d)\n",siz,curhist); | |
1199 | #endif | |
1200 | permalloc(); | |
1201 | h = zalloc(sizeof *h); | |
1202 | h->next = *hp; | |
1203 | h->free = (siz > HEAPSIZE) ? siz : HEAPSIZE; | |
1204 | h->ptr = h->pool = zalloc(h->free); | |
1205 | h->histno = curhist; | |
1206 | *hp = h; | |
1207 | heapalloc(); | |
1208 | return hp_alloc(hp,siz); | |
1209 | } | |
1210 | ||
1211 | char *hp_realloc(hp,ptr,oldsiz,newsiz) /**/ | |
1212 | Hp *hp; char *ptr; int oldsiz; int newsiz; | |
1213 | { | |
1214 | Hp h = *hp; | |
1215 | int delta = newsiz-oldsiz; | |
1216 | char *ret; | |
1217 | ||
1218 | if (h->ptr-oldsiz == ptr && h->free >= delta) { | |
1219 | h->free -= delta; | |
1220 | h->ptr += delta; | |
1221 | return ptr; | |
1222 | } | |
1223 | #ifdef MEMDEBUG | |
1224 | fprintf(stderr,"realloc copy\n"); | |
1225 | #endif | |
1226 | memcpy(ret = hp_alloc(hp,newsiz),ptr,oldsiz); | |
1227 | return ret; | |
1228 | } | |
1229 | ||
1230 | void hp_free(h,ptr,siz) /**/ | |
1231 | Hp h; char *ptr; int siz; | |
1232 | { | |
1233 | if (h->ptr-siz == ptr) { | |
1234 | h->free += siz; | |
1235 | h->ptr -= siz; | |
1236 | } | |
1237 | } | |
1238 | ||
1239 | char *hp_concat(old,new) /**/ | |
1240 | char *old; char *new; | |
1241 | { | |
1242 | int oldlen,newlen; | |
1243 | ||
1244 | oldlen = strlen(old); newlen = strlen(new); | |
1245 | old = hp_realloc(&hp_lit,old,oldlen+1,oldlen+newlen+1); | |
1246 | strcpy(old+oldlen,new); | |
1247 | return old; | |
1248 | } | |
1249 | ||
1250 | void hp_purge(h,lim) /**/ | |
1251 | Hp h; int lim; | |
1252 | { | |
1253 | Hp hlast; | |
1254 | ||
1255 | if (!h->next) return; | |
1256 | while (h->next) { hlast = h; h = h->next; } | |
1257 | if (h->histno <= lim || h->histno == 0) { | |
1258 | #ifdef MEMDEBUG | |
1259 | fprintf(stderr,"purging %d\n",lim); | |
1260 | #endif | |
1261 | free(h->pool); | |
1262 | free(h); | |
1263 | hlast->next = NULL; | |
1264 | } | |
1265 | } | |
1266 | ||
1267 | void readhistfile(s,err) /**/ | |
1268 | char *s;int err; | |
1269 | { | |
1270 | char buf[1024]; | |
1271 | FILE *in; | |
1272 | Histent ent; | |
1273 | time_t tim = time(NULL); | |
1274 | ||
1275 | if (!s) return; | |
1276 | if (in = fopen(s,"r")) { | |
1277 | while (fgets(buf,1024,in)) { | |
1278 | int l = strlen(buf); | |
1279 | char *pt = buf; | |
1280 | ||
1281 | while (l && buf[l-1] == '\n') { | |
1282 | buf[l-1] = '\0'; | |
1283 | if (l > 1 && buf[l-2] == '\\') { | |
1284 | buf[l-2] = '\n'; | |
1285 | fgets(buf+l-1,1024-(l-1),in); | |
1286 | l = strlen(buf); | |
1287 | } else break; | |
1288 | } | |
1289 | for (;*pt;pt++) if (*pt == ' ') *pt = HISTSPACE; | |
1290 | ||
1291 | ent = gethistent(++curhist); | |
1292 | ent->lex = hp_alloc(&hp_lex,strlen(buf)+1); | |
1293 | strcpy(ent->lex,buf); | |
1294 | ent->lit = hp_alloc(&hp_lit,strlen(buf)+1); | |
1295 | strcpy(ent->lit,buf); | |
1296 | ent->ftim = ent->stim = tim; | |
1297 | } | |
1298 | fclose(in); | |
1299 | } else if (err) | |
1300 | zerr("can't read history file",s,0); | |
1301 | } | |
1302 | ||
1303 | void savehistfile(s,err,app) /**/ | |
1304 | char *s;int err;int app; | |
1305 | { | |
1306 | char *t; | |
1307 | FILE *out; | |
1308 | int ev,flag; | |
1309 | ||
1310 | if (!s || !interact) return; | |
1311 | ev = curhist-savehist+1; | |
1312 | flag = (app) ? O_APPEND : O_TRUNC; | |
1313 | if (ev < firsthist()) ev = firsthist(); | |
1314 | if (out = fdopen(open(s,O_CREAT|O_WRONLY|flag,0600),"w")) { | |
1315 | for (; ev <= curhist; ev++) { | |
1316 | t = quietgetevent(ev); | |
1317 | for (; *t; t++) | |
1318 | if (*t == HISTSPACE) fputc(' ',out); | |
1319 | else { | |
1320 | if (*t == '\n') fputc('\\',out); | |
1321 | fputc(*t,out); | |
1322 | } | |
1323 | fputc('\n',out); | |
1324 | } | |
1325 | fclose(out); | |
1326 | } else if (err) zerr("can't write history file: %s",s,0); | |
1327 | } | |
1328 | ||
1329 | int firsthist() /**/ | |
1330 | { | |
1331 | int ev; | |
1332 | Histent ent; | |
1333 | ||
1334 | ev = curhist-histentct+1; | |
1335 | if (ev < 1) ev = 1; | |
1336 | do { | |
1337 | ent = gethistent(ev); | |
1338 | if ((lithist) ? ent->lit : ent->lex) break; | |
1339 | ev++; | |
1340 | } while (ev < curhist); | |
1341 | return ev; | |
1342 | } | |
1343 |