|
1 //hist.c - history expansion |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1992-1997 Paul Falstad |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 |
|
32 #include "zsh.mdh" |
|
33 #include "hist.pro" |
|
34 |
|
35 #ifdef __SYMBIAN32__ |
|
36 #ifdef __WINSCW__ |
|
37 #pragma warn_unusedarg off |
|
38 #pragma warn_possunwant off |
|
39 #endif//__WINSCW__ |
|
40 #endif//__SYMBIAN32__ |
|
41 |
|
42 |
|
43 /* Functions to call for getting/ungetting a character and for history |
|
44 * word control. */ |
|
45 |
|
46 /**/ |
|
47 mod_export int (*hgetc) _((void)); |
|
48 |
|
49 /**/ |
|
50 void (*hungetc) _((int)); |
|
51 |
|
52 /**/ |
|
53 void (*hwaddc) _((int)); |
|
54 |
|
55 /**/ |
|
56 void (*hwbegin) _((int)); |
|
57 |
|
58 /**/ |
|
59 void (*hwend) _((void)); |
|
60 |
|
61 /**/ |
|
62 void (*addtoline) _((int)); |
|
63 |
|
64 /* != 0 means history substitution is turned off */ |
|
65 |
|
66 /**/ |
|
67 mod_export int stophist; |
|
68 |
|
69 /* if != 0, we are expanding the current line */ |
|
70 |
|
71 /**/ |
|
72 mod_export int expanding; |
|
73 |
|
74 /* these are used to modify the cursor position during expansion */ |
|
75 |
|
76 /**/ |
|
77 mod_export int excs, exlast; |
|
78 |
|
79 /* |
|
80 * Current history event number |
|
81 * |
|
82 * Note on curhist: with history inactive, this points to the |
|
83 * last line actually added to the history list. With history active, |
|
84 * the line does not get added to the list until hend(), if at all. |
|
85 * However, curhist is incremented to reflect the current line anyway |
|
86 * and a temporary history entry is inserted while the user is editing. |
|
87 * If the resulting line was not added to the list, a flag is set so |
|
88 * that curhist will be decremented in hbegin(). |
|
89 */ |
|
90 |
|
91 /**/ |
|
92 mod_export zlong curhist; |
|
93 |
|
94 /**/ |
|
95 struct histent curline; |
|
96 |
|
97 /* current line count of allocated history entries */ |
|
98 |
|
99 /**/ |
|
100 zlong histlinect; |
|
101 |
|
102 /* The history lines are kept in a hash, and also doubly-linked in a ring */ |
|
103 |
|
104 /**/ |
|
105 HashTable histtab; |
|
106 /**/ |
|
107 mod_export Histent hist_ring; |
|
108 |
|
109 /* capacity of history lists */ |
|
110 |
|
111 /**/ |
|
112 zlong histsiz; |
|
113 |
|
114 /* desired history-file size (in lines) */ |
|
115 |
|
116 /**/ |
|
117 zlong savehistsiz; |
|
118 |
|
119 /* if = 1, we have performed history substitution on the current line * |
|
120 * if = 2, we have used the 'p' modifier */ |
|
121 |
|
122 /**/ |
|
123 int histdone; |
|
124 |
|
125 /* state of the history mechanism */ |
|
126 |
|
127 /**/ |
|
128 int histactive; |
|
129 |
|
130 /* Current setting of the associated option, but sometimes also includes |
|
131 * the setting of the HIST_SAVE_NO_DUPS option. */ |
|
132 |
|
133 /**/ |
|
134 int hist_ignore_all_dups; |
|
135 |
|
136 /* What flags (if any) we should skip when moving through the history */ |
|
137 |
|
138 /**/ |
|
139 mod_export int hist_skip_flags; |
|
140 |
|
141 /* Bits of histactive variable */ |
|
142 #define HA_ACTIVE (1<<0) /* History mechanism is active */ |
|
143 #define HA_NOSTORE (1<<1) /* Don't store the line when finished */ |
|
144 #define HA_NOINC (1<<2) /* Don't store, curhist not incremented */ |
|
145 |
|
146 /* Array of word beginnings and endings in current history line. */ |
|
147 |
|
148 /**/ |
|
149 short *chwords; |
|
150 |
|
151 /* Max, actual position in chwords. |
|
152 * nwords = chwordpos/2 because we record beginning and end of words. |
|
153 */ |
|
154 |
|
155 /**/ |
|
156 int chwordlen, chwordpos; |
|
157 |
|
158 /* the last l for s/l/r/ history substitution */ |
|
159 |
|
160 /**/ |
|
161 char *hsubl; |
|
162 |
|
163 /* the last r for s/l/r/ history substitution */ |
|
164 |
|
165 /**/ |
|
166 char *hsubr; |
|
167 |
|
168 /* pointer into the history line */ |
|
169 |
|
170 /**/ |
|
171 mod_export char *hptr; |
|
172 |
|
173 /* the current history line */ |
|
174 |
|
175 /**/ |
|
176 mod_export char *chline; |
|
177 |
|
178 /* true if the last character returned by hgetc was an escaped bangchar * |
|
179 * if it is set and NOBANGHIST is unset hwaddc escapes bangchars */ |
|
180 |
|
181 /**/ |
|
182 int qbang; |
|
183 |
|
184 /* max size of histline */ |
|
185 |
|
186 /**/ |
|
187 int hlinesz; |
|
188 |
|
189 /* default event (usually curhist-1, that is, "!!") */ |
|
190 |
|
191 static zlong defev; |
|
192 |
|
193 /* add a character to the current history word */ |
|
194 |
|
195 static void |
|
196 ihwaddc(int c) |
|
197 { |
|
198 /* Only if history line exists and lexing has not finished. */ |
|
199 if (chline && !(errflag || lexstop)) { |
|
200 /* Quote un-expanded bangs in the history line. */ |
|
201 if (c == bangchar && stophist < 2 && qbang) |
|
202 /* If qbang is not set, we do not escape this bangchar as it's * |
|
203 * not mecessary (e.g. it's a bang in !=, or it is followed * |
|
204 * by a space). Roughly speaking, qbang is zero only if the * |
|
205 * history interpreter has already digested this bang and * |
|
206 * found that it is not necessary to escape it. */ |
|
207 hwaddc('\\'); |
|
208 *hptr++ = c; |
|
209 |
|
210 /* Resize history line if necessary */ |
|
211 if (hptr - chline >= hlinesz) { |
|
212 int oldsiz = hlinesz; |
|
213 |
|
214 chline = realloc(chline, hlinesz = oldsiz + 64); |
|
215 hptr = chline + oldsiz; |
|
216 } |
|
217 } |
|
218 } |
|
219 |
|
220 /* This function adds a character to the zle input line. It is used when * |
|
221 * zsh expands history (see doexpandhist() in zle_tricky.c). It also * |
|
222 * calculates the new cursor position after the expansion. It is called * |
|
223 * from hgetc() and from gettok() in lex.c for characters in comments. */ |
|
224 |
|
225 /**/ |
|
226 void |
|
227 iaddtoline(int c) |
|
228 { |
|
229 if (!expanding || lexstop) |
|
230 return; |
|
231 if (qbang && c == bangchar && stophist < 2) { |
|
232 exlast--; |
|
233 spaceinline(1); |
|
234 line[cs++] = '\\'; |
|
235 } |
|
236 if (excs > cs) { |
|
237 excs += 1 + inbufct - exlast; |
|
238 if (excs < cs) |
|
239 /* this case could be handled better but it is * |
|
240 * so rare that it does not worth it */ |
|
241 excs = cs; |
|
242 } |
|
243 exlast = inbufct; |
|
244 spaceinline(1); |
|
245 line[cs++] = itok(c) ? ztokens[c - Pound] : c; |
|
246 } |
|
247 |
|
248 static int |
|
249 ihgetc(void) |
|
250 { |
|
251 int c = ingetc(); |
|
252 |
|
253 qbang = 0; |
|
254 if (!stophist && !(inbufflags & INP_ALIAS)) { |
|
255 /* If necessary, expand history characters. */ |
|
256 c = histsubchar(c); |
|
257 if (c < 0) { |
|
258 /* bad expansion */ |
|
259 errflag = lexstop = 1; |
|
260 return ' '; |
|
261 } |
|
262 } |
|
263 if ((inbufflags & INP_HIST) && !stophist) { |
|
264 /* the current character c came from a history expansion * |
|
265 * (inbufflags & INP_HIST) and history is not disabled * |
|
266 * (e.g. we are not inside single quotes). In that case, \! * |
|
267 * should be treated as ! (since this \! came from a previous * |
|
268 * history line where \ was used to escape the bang). So if * |
|
269 * c == '\\' we fetch one more character to see if it's a bang, * |
|
270 * and if it is not, we unget it and reset c back to '\\' */ |
|
271 qbang = 0; |
|
272 if (c == '\\' && !(qbang = (c = ingetc()) == bangchar)) |
|
273 safeinungetc(c), c = '\\'; |
|
274 } else if (stophist || (inbufflags & INP_ALIAS)) |
|
275 /* If the result is a bangchar which came from history or alias * |
|
276 * expansion, we treat it as an escaped bangchar, unless history * |
|
277 * is disabled. If stophist == 1 it only means that history is * |
|
278 * temporarily disabled by a !" which won't appear in in the * |
|
279 * history, so we still have an escaped bang. stophist > 1 if * |
|
280 * history is disabled with NOBANGHIST or by someone else (e.g. * |
|
281 * when the lexer scans single quoted text). */ |
|
282 qbang = c == bangchar && (stophist < 2); |
|
283 hwaddc(c); |
|
284 addtoline(c); |
|
285 |
|
286 return c; |
|
287 } |
|
288 |
|
289 /**/ |
|
290 static void |
|
291 safeinungetc(int c) |
|
292 { |
|
293 if (lexstop) |
|
294 lexstop = 0; |
|
295 else |
|
296 inungetc(c); |
|
297 } |
|
298 |
|
299 /**/ |
|
300 void |
|
301 herrflush(void) |
|
302 { |
|
303 inpopalias(); |
|
304 |
|
305 while (!lexstop && inbufct && !strin) |
|
306 hwaddc(ingetc()); |
|
307 } |
|
308 |
|
309 /* extract :s/foo/bar/ delimiters and arguments */ |
|
310 |
|
311 /**/ |
|
312 static int |
|
313 getsubsargs(char *subline) |
|
314 { |
|
315 int del; |
|
316 char *ptr1, *ptr2; |
|
317 |
|
318 del = ingetc(); |
|
319 ptr1 = hdynread2(del); |
|
320 if (!ptr1) |
|
321 return 1; |
|
322 ptr2 = hdynread2(del); |
|
323 if (strlen(ptr1)) { |
|
324 zsfree(hsubl); |
|
325 hsubl = ptr1; |
|
326 } |
|
327 zsfree(hsubr); |
|
328 hsubr = ptr2; |
|
329 if (hsubl && !strstr(subline, hsubl)) { |
|
330 herrflush(); |
|
331 zerr("substitution failed", NULL, 0); |
|
332 return 1; |
|
333 } |
|
334 return 0; |
|
335 } |
|
336 |
|
337 /* Get the maximum no. of words for a history entry. */ |
|
338 |
|
339 /**/ |
|
340 static int |
|
341 getargc(Histent ehist) |
|
342 { |
|
343 return ehist->nwords ? ehist->nwords-1 : 0; |
|
344 } |
|
345 |
|
346 /* Perform history substitution, returning the next character afterwards. */ |
|
347 |
|
348 /**/ |
|
349 static int |
|
350 histsubchar(int c) |
|
351 { |
|
352 int farg, evset = -1, larg, argc, cflag = 0, bflag = 0; |
|
353 zlong ev; |
|
354 static int marg = -1; |
|
355 static zlong mev = -1; |
|
356 char buf[256], *ptr; |
|
357 char *sline; |
|
358 Histent ehist; |
|
359 |
|
360 /* look, no goto's */ |
|
361 if (isfirstch && c == hatchar) { |
|
362 /* Line begins ^foo^bar */ |
|
363 isfirstch = 0; |
|
364 inungetc(hatchar); |
|
365 if (!(ehist = gethist(defev)) |
|
366 || !(sline = getargs(ehist, 0, getargc(ehist))) |
|
367 || getsubsargs(sline) || !hsubl) |
|
368 return -1; |
|
369 subst(&sline, hsubl, hsubr, 0); |
|
370 } else { |
|
371 /* Line doesn't begin ^foo^bar */ |
|
372 if (c != ' ') |
|
373 isfirstch = 0; |
|
374 #ifndef __SYMBIAN32__ |
|
375 if (c == '\\') { |
|
376 int g = ingetc(); |
|
377 |
|
378 if (g != bangchar) |
|
379 safeinungetc(g); |
|
380 else { |
|
381 qbang = 1; |
|
382 return bangchar; |
|
383 } |
|
384 } |
|
385 #endif //__SYMBIAN32__ |
|
386 if (c != bangchar) |
|
387 return c; |
|
388 *hptr = '\0'; |
|
389 if ((c = ingetc()) == '{') { |
|
390 bflag = cflag = 1; |
|
391 c = ingetc(); |
|
392 } |
|
393 if (c == '\"') { |
|
394 stophist = 1; |
|
395 return ingetc(); |
|
396 } |
|
397 if ((!cflag && inblank(c)) || c == '=' || c == '(' || lexstop) { |
|
398 safeinungetc(c); |
|
399 return bangchar; |
|
400 } |
|
401 cflag = 0; |
|
402 ptr = buf; |
|
403 |
|
404 /* get event number */ |
|
405 |
|
406 queue_signals(); |
|
407 if (c == '?') { |
|
408 for (;;) { |
|
409 c = ingetc(); |
|
410 if (c == '?' || c == '\n' || lexstop) |
|
411 break; |
|
412 else |
|
413 *ptr++ = c; |
|
414 } |
|
415 if (c != '\n' && !lexstop) |
|
416 c = ingetc(); |
|
417 *ptr = '\0'; |
|
418 mev = ev = hconsearch(hsubl = ztrdup(buf), &marg); |
|
419 evset = 0; |
|
420 if (ev == -1) { |
|
421 herrflush(); |
|
422 unqueue_signals(); |
|
423 zerr("no such event: %s", buf, 0); |
|
424 return -1; |
|
425 } |
|
426 } else { |
|
427 zlong t0; |
|
428 |
|
429 for (;;) { |
|
430 if (inblank(c) || c == ';' || c == ':' || c == '^' || |
|
431 c == '$' || c == '*' || c == '%' || c == '}' || |
|
432 c == '\'' || c == '"' || c == '`' || lexstop) |
|
433 break; |
|
434 if (ptr != buf) { |
|
435 if (c == '-') |
|
436 break; |
|
437 if ((idigit(buf[0]) || buf[0] == '-') && !idigit(c)) |
|
438 break; |
|
439 } |
|
440 *ptr++ = c; |
|
441 if (c == '#' || c == bangchar) { |
|
442 c = ingetc(); |
|
443 break; |
|
444 } |
|
445 c = ingetc(); |
|
446 } |
|
447 *ptr = 0; |
|
448 if (!*buf) { |
|
449 if (c != '%') { |
|
450 if (isset(CSHJUNKIEHISTORY)) |
|
451 ev = addhistnum(curhist,-1,HIST_FOREIGN); |
|
452 else |
|
453 ev = defev; |
|
454 if (c == ':' && evset == -1) |
|
455 evset = 0; |
|
456 else |
|
457 evset = 1; |
|
458 } else { |
|
459 if (marg != -1) |
|
460 ev = mev; |
|
461 else |
|
462 ev = defev; |
|
463 evset = 0; |
|
464 } |
|
465 } else if ((t0 = zstrtol(buf, NULL, 10))) { |
|
466 ev = (t0 < 0) ? addhistnum(curhist,t0,HIST_FOREIGN) : t0; |
|
467 evset = 1; |
|
468 } else if ((unsigned)*buf == bangchar) { |
|
469 ev = addhistnum(curhist,-1,HIST_FOREIGN); |
|
470 evset = 1; |
|
471 } else if (*buf == '#') { |
|
472 ev = curhist; |
|
473 evset = 1; |
|
474 } else if ((ev = hcomsearch(buf)) == -1) { |
|
475 herrflush(); |
|
476 unqueue_signals(); |
|
477 zerr("event not found: %s", buf, 0); |
|
478 return -1; |
|
479 } else |
|
480 evset = 1; |
|
481 } |
|
482 |
|
483 /* get the event */ |
|
484 |
|
485 if (!(ehist = gethist(defev = ev))) { |
|
486 unqueue_signals(); |
|
487 return -1; |
|
488 } |
|
489 /* extract the relevant arguments */ |
|
490 |
|
491 argc = getargc(ehist); |
|
492 if (c == ':') { |
|
493 cflag = 1; |
|
494 c = ingetc(); |
|
495 if (c == '%' && marg != -1) { |
|
496 if (!evset) { |
|
497 ehist = gethist(defev = mev); |
|
498 argc = getargc(ehist); |
|
499 } else { |
|
500 herrflush(); |
|
501 unqueue_signals(); |
|
502 zerr("Ambiguous history reference", NULL, 0); |
|
503 return -1; |
|
504 } |
|
505 |
|
506 } |
|
507 } |
|
508 if (c == '*') { |
|
509 farg = 1; |
|
510 larg = argc; |
|
511 cflag = 0; |
|
512 } else { |
|
513 inungetc(c); |
|
514 larg = farg = getargspec(argc, marg, evset); |
|
515 if (larg == -2) { |
|
516 unqueue_signals(); |
|
517 return -1; |
|
518 } |
|
519 if (farg != -1) |
|
520 cflag = 0; |
|
521 c = ingetc(); |
|
522 if (c == '*') { |
|
523 cflag = 0; |
|
524 larg = argc; |
|
525 } else if (c == '-') { |
|
526 cflag = 0; |
|
527 larg = getargspec(argc, marg, evset); |
|
528 if (larg == -2) { |
|
529 unqueue_signals(); |
|
530 return -1; |
|
531 } |
|
532 if (larg == -1) |
|
533 larg = argc - 1; |
|
534 } else |
|
535 inungetc(c); |
|
536 } |
|
537 if (farg == -1) |
|
538 farg = 0; |
|
539 if (larg == -1) |
|
540 larg = argc; |
|
541 if (!(sline = getargs(ehist, farg, larg))) { |
|
542 unqueue_signals(); |
|
543 return -1; |
|
544 } |
|
545 unqueue_signals(); |
|
546 } |
|
547 |
|
548 /* do the modifiers */ |
|
549 |
|
550 for (;;) { |
|
551 c = (cflag) ? ':' : ingetc(); |
|
552 cflag = 0; |
|
553 if (c == ':') { |
|
554 int gbal = 0; |
|
555 |
|
556 if ((c = ingetc()) == 'g') { |
|
557 gbal = 1; |
|
558 c = ingetc(); |
|
559 } |
|
560 switch (c) { |
|
561 case 'p': |
|
562 histdone = HISTFLAG_DONE | HISTFLAG_NOEXEC; |
|
563 break; |
|
564 case 'h': |
|
565 if (!remtpath(&sline)) { |
|
566 herrflush(); |
|
567 zerr("modifier failed: h", NULL, 0); |
|
568 return -1; |
|
569 } |
|
570 break; |
|
571 case 'e': |
|
572 if (!rembutext(&sline)) { |
|
573 herrflush(); |
|
574 zerr("modifier failed: e", NULL, 0); |
|
575 return -1; |
|
576 } |
|
577 break; |
|
578 case 'r': |
|
579 if (!remtext(&sline)) { |
|
580 herrflush(); |
|
581 zerr("modifier failed: r", NULL, 0); |
|
582 return -1; |
|
583 } |
|
584 break; |
|
585 case 't': |
|
586 if (!remlpaths(&sline)) { |
|
587 herrflush(); |
|
588 zerr("modifier failed: t", NULL, 0); |
|
589 return -1; |
|
590 } |
|
591 break; |
|
592 case 's': |
|
593 if (getsubsargs(sline)) |
|
594 return -1; /* fall through */ |
|
595 case '&': |
|
596 if (hsubl && hsubr) |
|
597 subst(&sline, hsubl, hsubr, gbal); |
|
598 else { |
|
599 herrflush(); |
|
600 zerr("no previous substitution", NULL, 0); |
|
601 return -1; |
|
602 } |
|
603 break; |
|
604 case 'q': |
|
605 quote(&sline); |
|
606 break; |
|
607 case 'Q': |
|
608 { |
|
609 int one = noerrs, oef = errflag; |
|
610 |
|
611 noerrs = 1; |
|
612 parse_subst_string(sline); |
|
613 noerrs = one; |
|
614 errflag = oef; |
|
615 remnulargs(sline); |
|
616 untokenize(sline); |
|
617 } |
|
618 break; |
|
619 case 'x': |
|
620 quotebreak(&sline); |
|
621 break; |
|
622 case 'l': |
|
623 downcase(&sline); |
|
624 break; |
|
625 case 'u': |
|
626 upcase(&sline); |
|
627 break; |
|
628 default: |
|
629 herrflush(); |
|
630 zerr("illegal modifier: %c", NULL, c); |
|
631 return -1; |
|
632 } |
|
633 } else { |
|
634 if (c != '}' || !bflag) |
|
635 inungetc(c); |
|
636 if (c != '}' && bflag) { |
|
637 zerr("'}' expected", NULL, 0); |
|
638 return -1; |
|
639 } |
|
640 break; |
|
641 } |
|
642 } |
|
643 |
|
644 /* |
|
645 * Push the expanded value onto the input stack, |
|
646 * marking this as a history word for purposes of the alias stack. |
|
647 */ |
|
648 |
|
649 lexstop = 0; |
|
650 /* this function is called only called from hgetc and only if * |
|
651 * !(inbufflags & INP_ALIAS). History expansion should never be * |
|
652 * done with INP_ALIAS (to prevent recursive history expansion and * |
|
653 * histoty expansion of aliases). Escapes are not removed here. * |
|
654 * This is now handled in hgetc. */ |
|
655 inpush(sline, INP_HIST, NULL); /* sline from heap, don't free */ |
|
656 histdone |= HISTFLAG_DONE; |
|
657 if (isset(HISTVERIFY)) |
|
658 histdone |= HISTFLAG_NOEXEC | HISTFLAG_RECALL; |
|
659 |
|
660 /* Don't try and re-expand line. */ |
|
661 return ingetc(); |
|
662 } |
|
663 |
|
664 /* unget a char and remove it from chline. It can only be used * |
|
665 * to unget a character returned by hgetc. */ |
|
666 |
|
667 static void |
|
668 ihungetc(int c) |
|
669 { |
|
670 int doit = 1; |
|
671 |
|
672 while (!lexstop && !errflag) { |
|
673 if (hptr[-1] != (char) c && stophist < 4 && |
|
674 hptr > chline + 1 && hptr[-1] == '\n' && hptr[-2] == '\\') |
|
675 hungetc('\n'), hungetc('\\'); |
|
676 |
|
677 if (expanding) { |
|
678 cs--; |
|
679 ll--; |
|
680 exlast++; |
|
681 } |
|
682 DPUTS(hptr <= chline, "BUG: hungetc attempted at buffer start"); |
|
683 hptr--; |
|
684 DPUTS(*hptr != (char) c, "BUG: wrong character in hungetc() "); |
|
685 qbang = (c == bangchar && stophist < 2 && |
|
686 hptr > chline && hptr[-1] == '\\'); |
|
687 if (doit) |
|
688 inungetc(c); |
|
689 if (!qbang) |
|
690 return; |
|
691 doit = !stophist && ((inbufflags & INP_HIST) || |
|
692 !(inbufflags & INP_ALIAS)); |
|
693 c = '\\'; |
|
694 } |
|
695 } |
|
696 |
|
697 /* begin reading a string */ |
|
698 |
|
699 /**/ |
|
700 mod_export void |
|
701 strinbeg(int dohist) |
|
702 { |
|
703 strin++; |
|
704 hbegin(dohist); |
|
705 lexinit(); |
|
706 } |
|
707 |
|
708 /* done reading a string */ |
|
709 |
|
710 /**/ |
|
711 mod_export void |
|
712 strinend(void) |
|
713 { |
|
714 hend(NULL); |
|
715 DPUTS(!strin, "BUG: strinend() called without strinbeg()"); |
|
716 strin--; |
|
717 isfirstch = 1; |
|
718 histdone = 0; |
|
719 } |
|
720 |
|
721 /* dummy functions to use instead of hwaddc(), hwbegin(), and hwend() when |
|
722 * they aren't needed */ |
|
723 |
|
724 static void |
|
725 nohw(UNUSED(int c)) |
|
726 { |
|
727 } |
|
728 |
|
729 static void |
|
730 nohwe(void) |
|
731 { |
|
732 } |
|
733 |
|
734 /* these functions handle adding/removing curline to/from the hist_ring */ |
|
735 |
|
736 static void |
|
737 linkcurline(void) |
|
738 { |
|
739 if (!hist_ring) |
|
740 hist_ring = curline.up = curline.down = &curline; |
|
741 else { |
|
742 curline.up = hist_ring; |
|
743 curline.down = hist_ring->down; |
|
744 hist_ring->down = hist_ring->down->up = &curline; |
|
745 hist_ring = &curline; |
|
746 } |
|
747 curline.histnum = ++curhist; |
|
748 } |
|
749 |
|
750 static void |
|
751 unlinkcurline(void) |
|
752 { |
|
753 curline.up->down = curline.down; |
|
754 curline.down->up = curline.up; |
|
755 if (hist_ring == &curline) { |
|
756 if (!histlinect) |
|
757 hist_ring = NULL; |
|
758 else |
|
759 hist_ring = curline.up; |
|
760 } |
|
761 curhist--; |
|
762 } |
|
763 |
|
764 /* initialize the history mechanism */ |
|
765 |
|
766 /**/ |
|
767 mod_export void |
|
768 hbegin(int dohist) |
|
769 { |
|
770 isfirstln = isfirstch = 1; |
|
771 errflag = histdone = 0; |
|
772 if (!dohist) |
|
773 stophist = 2; |
|
774 else if (dohist != 2) |
|
775 stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0; |
|
776 else |
|
777 stophist = 0; |
|
778 if (stophist == 2 || (inbufflags & INP_ALIAS)) { |
|
779 chline = hptr = NULL; |
|
780 hlinesz = 0; |
|
781 chwords = NULL; |
|
782 chwordlen = 0; |
|
783 hgetc = ingetc; |
|
784 hungetc = inungetc; |
|
785 hwaddc = nohw; |
|
786 hwbegin = nohw; |
|
787 hwend = nohwe; |
|
788 addtoline = nohw; |
|
789 } else { |
|
790 chline = hptr = zshcalloc(hlinesz = 64); |
|
791 chwords = zalloc((chwordlen = 64) * sizeof(short)); |
|
792 hgetc = ihgetc; |
|
793 hungetc = ihungetc; |
|
794 hwaddc = ihwaddc; |
|
795 hwbegin = ihwbegin; |
|
796 hwend = ihwend; |
|
797 addtoline = iaddtoline; |
|
798 if (!isset(BANGHIST)) |
|
799 stophist = 4; |
|
800 } |
|
801 chwordpos = 0; |
|
802 |
|
803 if (hist_ring && !hist_ring->ftim) |
|
804 hist_ring->ftim = time(NULL); |
|
805 if ((dohist == 2 || (interact && isset(SHINSTDIN))) && !strin) { |
|
806 histactive = HA_ACTIVE; |
|
807 attachtty(mypgrp); |
|
808 linkcurline(); |
|
809 defev = addhistnum(curhist, -1, HIST_FOREIGN); |
|
810 } else |
|
811 histactive = HA_ACTIVE | HA_NOINC; |
|
812 } |
|
813 |
|
814 /**/ |
|
815 void |
|
816 histreduceblanks(void) |
|
817 { |
|
818 int i, len, pos, needblank, spacecount = 0; |
|
819 |
|
820 if (isset(HISTIGNORESPACE)) |
|
821 while (chline[spacecount] == ' ') spacecount++; |
|
822 |
|
823 for (i = 0, len = spacecount; i < chwordpos; i += 2) { |
|
824 len += chwords[i+1] - chwords[i] |
|
825 + (i > 0 && chwords[i] > chwords[i-1]); |
|
826 } |
|
827 if (chline[len] == '\0') |
|
828 return; |
|
829 |
|
830 for (i = 0, pos = spacecount; i < chwordpos; i += 2) { |
|
831 len = chwords[i+1] - chwords[i]; |
|
832 needblank = (i < chwordpos-2 && chwords[i+2] > chwords[i+1]); |
|
833 if (pos != chwords[i]) { |
|
834 memcpy(chline + pos, chline + chwords[i], len + needblank); |
|
835 chwords[i] = pos; |
|
836 chwords[i+1] = chwords[i] + len; |
|
837 } |
|
838 pos += len + needblank; |
|
839 } |
|
840 chline[pos] = '\0'; |
|
841 } |
|
842 |
|
843 /**/ |
|
844 void |
|
845 histremovedups(void) |
|
846 { |
|
847 Histent he, next; |
|
848 for (he = hist_ring; he; he = next) { |
|
849 next = up_histent(he); |
|
850 if (he->flags & HIST_DUP) |
|
851 freehistnode((HashNode)he); |
|
852 } |
|
853 } |
|
854 |
|
855 /**/ |
|
856 mod_export zlong |
|
857 addhistnum(zlong hl, int n, int xflags) |
|
858 { |
|
859 int dir = n < 0? -1 : n > 0? 1 : 0; |
|
860 Histent he = gethistent(hl, dir); |
|
861 |
|
862 if (!he) |
|
863 return 0; |
|
864 if (he->histnum != hl) |
|
865 n -= dir; |
|
866 if (n) |
|
867 he = movehistent(he, n, xflags); |
|
868 if (!he) |
|
869 return dir < 0? firsthist() - 1 : curhist + 1; |
|
870 return he->histnum; |
|
871 } |
|
872 |
|
873 /**/ |
|
874 mod_export Histent |
|
875 movehistent(Histent he, int n, int xflags) |
|
876 { |
|
877 while (n < 0) { |
|
878 if (!(he = up_histent(he))) |
|
879 return NULL; |
|
880 if (!(he->flags & xflags)) |
|
881 n++; |
|
882 } |
|
883 while (n > 0) { |
|
884 if (!(he = down_histent(he))) |
|
885 return NULL; |
|
886 if (!(he->flags & xflags)) |
|
887 n--; |
|
888 } |
|
889 checkcurline(he); |
|
890 return he; |
|
891 } |
|
892 |
|
893 /**/ |
|
894 mod_export Histent |
|
895 up_histent(Histent he) |
|
896 { |
|
897 return he->up == hist_ring? NULL : he->up; |
|
898 } |
|
899 |
|
900 /**/ |
|
901 mod_export Histent |
|
902 down_histent(Histent he) |
|
903 { |
|
904 return he == hist_ring? NULL : he->down; |
|
905 } |
|
906 |
|
907 /**/ |
|
908 mod_export Histent |
|
909 gethistent(zlong ev, int nearmatch) |
|
910 { |
|
911 Histent he; |
|
912 |
|
913 if (!hist_ring) |
|
914 return NULL; |
|
915 |
|
916 if (ev - hist_ring->down->histnum < hist_ring->histnum - ev) { |
|
917 for (he = hist_ring->down; he->histnum < ev; he = he->down) ; |
|
918 if (he->histnum != ev) { |
|
919 if (nearmatch == 0 |
|
920 || (nearmatch < 0 && (he = up_histent(he)) == NULL)) |
|
921 return NULL; |
|
922 } |
|
923 } |
|
924 else { |
|
925 for (he = hist_ring; he->histnum > ev; he = he->up) ; |
|
926 if (he->histnum != ev) { |
|
927 if (nearmatch == 0 |
|
928 || (nearmatch > 0 && (he = down_histent(he)) == NULL)) |
|
929 return NULL; |
|
930 } |
|
931 } |
|
932 |
|
933 checkcurline(he); |
|
934 return he; |
|
935 } |
|
936 |
|
937 static void |
|
938 putoldhistentryontop(short keep_going) |
|
939 { |
|
940 static Histent next = NULL; |
|
941 Histent he = keep_going? next : hist_ring->down; |
|
942 next = he->down; |
|
943 if (isset(HISTEXPIREDUPSFIRST) && !(he->flags & HIST_DUP)) { |
|
944 static zlong max_unique_ct = 0; |
|
945 if (!keep_going) |
|
946 max_unique_ct = savehistsiz; |
|
947 do { |
|
948 if (max_unique_ct-- <= 0 || he == hist_ring) { |
|
949 max_unique_ct = 0; |
|
950 he = hist_ring->down; |
|
951 next = hist_ring; |
|
952 break; |
|
953 } |
|
954 he = next; |
|
955 next = he->down; |
|
956 } while (!(he->flags & HIST_DUP)); |
|
957 } |
|
958 if (he != hist_ring->down) { |
|
959 he->up->down = he->down; |
|
960 he->down->up = he->up; |
|
961 he->up = hist_ring; |
|
962 he->down = hist_ring->down; |
|
963 hist_ring->down = he->down->up = he; |
|
964 } |
|
965 hist_ring = he; |
|
966 } |
|
967 |
|
968 /**/ |
|
969 Histent |
|
970 prepnexthistent(void) |
|
971 { |
|
972 Histent he; |
|
973 int curline_in_ring = hist_ring == &curline; |
|
974 |
|
975 if (curline_in_ring) |
|
976 unlinkcurline(); |
|
977 if (hist_ring && hist_ring->flags & HIST_TMPSTORE) { |
|
978 curhist--; |
|
979 freehistnode((HashNode)hist_ring); |
|
980 } |
|
981 |
|
982 if (histlinect < histsiz) { |
|
983 he = (Histent)zshcalloc(sizeof *he); |
|
984 if (!hist_ring) |
|
985 hist_ring = he->up = he->down = he; |
|
986 else { |
|
987 he->up = hist_ring; |
|
988 he->down = hist_ring->down; |
|
989 hist_ring->down = he->down->up = he; |
|
990 hist_ring = he; |
|
991 } |
|
992 histlinect++; |
|
993 } |
|
994 else { |
|
995 putoldhistentryontop(0); |
|
996 freehistdata(hist_ring, 0); |
|
997 he = hist_ring; |
|
998 } |
|
999 he->histnum = ++curhist; |
|
1000 if (curline_in_ring) |
|
1001 linkcurline(); |
|
1002 return he; |
|
1003 } |
|
1004 |
|
1005 /* A helper function for hend() */ |
|
1006 |
|
1007 static int |
|
1008 should_ignore_line(Eprog prog) |
|
1009 { |
|
1010 if (isset(HISTIGNORESPACE)) { |
|
1011 if (*chline == ' ' || aliasspaceflag) |
|
1012 return 1; |
|
1013 } |
|
1014 |
|
1015 if (!prog) |
|
1016 return 0; |
|
1017 |
|
1018 if (isset(HISTNOFUNCTIONS)) { |
|
1019 Wordcode pc = prog->prog; |
|
1020 wordcode code = *pc; |
|
1021 if (wc_code(code) == WC_LIST && WC_LIST_TYPE(code) & Z_SIMPLE |
|
1022 && wc_code(pc[2]) == WC_FUNCDEF) |
|
1023 return 1; |
|
1024 } |
|
1025 |
|
1026 if (isset(HISTNOSTORE)) { |
|
1027 char *b = getjobtext(prog, NULL); |
|
1028 int saw_builtin; |
|
1029 if (*b == 'b' && strncmp(b,"builtin ",8) == 0) { |
|
1030 b += 8; |
|
1031 saw_builtin = 1; |
|
1032 } else |
|
1033 saw_builtin = 0; |
|
1034 if (*b == 'h' && strncmp(b,"history",7) == 0 && (!b[7] || b[7] == ' ') |
|
1035 && (saw_builtin || !shfunctab->getnode(shfunctab,"history"))) |
|
1036 return 1; |
|
1037 if (*b == 'r' && (!b[1] || b[1] == ' ') |
|
1038 && (saw_builtin || !shfunctab->getnode(shfunctab,"r"))) |
|
1039 return 1; |
|
1040 if (*b == 'f' && b[1] == 'c' && b[2] == ' ' && b[3] == '-' |
|
1041 && (saw_builtin || !shfunctab->getnode(shfunctab,"fc"))) { |
|
1042 b += 3; |
|
1043 do { |
|
1044 if (*++b == 'l') |
|
1045 return 1; |
|
1046 } while (ialpha(*b)); |
|
1047 } |
|
1048 } |
|
1049 |
|
1050 return 0; |
|
1051 } |
|
1052 |
|
1053 /* say we're done using the history mechanism */ |
|
1054 |
|
1055 /**/ |
|
1056 mod_export int |
|
1057 hend(Eprog prog) |
|
1058 { |
|
1059 int flag, save = 1; |
|
1060 char *hf; |
|
1061 |
|
1062 DPUTS(stophist != 2 && !(inbufflags & INP_ALIAS) && !chline, |
|
1063 "BUG: chline is NULL in hend()"); |
|
1064 queue_signals(); |
|
1065 if (histdone & HISTFLAG_SETTY) |
|
1066 settyinfo(&shttyinfo); |
|
1067 if (!(histactive & HA_NOINC)) |
|
1068 unlinkcurline(); |
|
1069 if (histactive & (HA_NOSTORE|HA_NOINC)) { |
|
1070 zfree(chline, hlinesz); |
|
1071 zfree(chwords, chwordlen*sizeof(short)); |
|
1072 chline = NULL; |
|
1073 histactive = 0; |
|
1074 unqueue_signals(); |
|
1075 return 1; |
|
1076 } |
|
1077 if (hist_ignore_all_dups != isset(HISTIGNOREALLDUPS) |
|
1078 && (hist_ignore_all_dups = isset(HISTIGNOREALLDUPS)) != 0) |
|
1079 histremovedups(); |
|
1080 /* For history sharing, lock history file once for both read and write */ |
|
1081 hf = getsparam("HISTFILE"); |
|
1082 if (isset(SHAREHISTORY) && lockhistfile(hf, 0)) { |
|
1083 readhistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST); |
|
1084 curline.histnum = curhist+1; |
|
1085 } |
|
1086 flag = histdone; |
|
1087 histdone = 0; |
|
1088 if (hptr < chline + 1) |
|
1089 save = 0; |
|
1090 else { |
|
1091 *hptr = '\0'; |
|
1092 if (hptr[-1] == '\n') { |
|
1093 if (chline[1]) { |
|
1094 *--hptr = '\0'; |
|
1095 } else |
|
1096 save = 0; |
|
1097 } |
|
1098 if (chwordpos <= 2) |
|
1099 save = 0; |
|
1100 else if (should_ignore_line(prog)) |
|
1101 save = -1; |
|
1102 } |
|
1103 if (flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) { |
|
1104 char *ptr; |
|
1105 |
|
1106 ptr = ztrdup(chline); |
|
1107 if ((flag & (HISTFLAG_DONE | HISTFLAG_RECALL)) == HISTFLAG_DONE) { |
|
1108 zputs(ptr, shout); |
|
1109 fputc('\n', shout); |
|
1110 fflush(shout); |
|
1111 } |
|
1112 if (flag & HISTFLAG_RECALL) { |
|
1113 zpushnode(bufstack, ptr); |
|
1114 save = 0; |
|
1115 } else |
|
1116 zsfree(ptr); |
|
1117 } |
|
1118 if (save || *chline == ' ') { |
|
1119 Histent he; |
|
1120 for (he = hist_ring; he && he->flags & HIST_FOREIGN; |
|
1121 he = up_histent(he)) ; |
|
1122 if (he && he->flags & HIST_TMPSTORE) { |
|
1123 if (he == hist_ring) |
|
1124 curline.histnum = curhist--; |
|
1125 freehistnode((HashNode)he); |
|
1126 } |
|
1127 } |
|
1128 if (save) { |
|
1129 Histent he; |
|
1130 int newflags; |
|
1131 |
|
1132 #ifdef DEBUG |
|
1133 /* debugging only */ |
|
1134 if (chwordpos%2) { |
|
1135 hwend(); |
|
1136 DPUTS(1, "BUG: uncompleted line in history"); |
|
1137 } |
|
1138 #endif |
|
1139 /* get rid of pesky \n which we've already nulled out */ |
|
1140 if (chwordpos > 1 && !chline[chwords[chwordpos-2]]) { |
|
1141 chwordpos -= 2; |
|
1142 /* strip superfluous blanks, if desired */ |
|
1143 if (isset(HISTREDUCEBLANKS)) |
|
1144 histreduceblanks(); |
|
1145 } |
|
1146 newflags = save > 0? 0 : HIST_TMPSTORE; |
|
1147 if ((isset(HISTIGNOREDUPS) || isset(HISTIGNOREALLDUPS)) && save > 0 |
|
1148 && hist_ring && histstrcmp(chline, hist_ring->text) == 0) { |
|
1149 /* This history entry compares the same as the previous. |
|
1150 * In case minor changes were made, we overwrite the |
|
1151 * previous one with the current one. This also gets the |
|
1152 * timestamp right. Perhaps, preserve the HIST_OLD flag. |
|
1153 */ |
|
1154 he = hist_ring; |
|
1155 newflags |= he->flags & HIST_OLD; /* Avoid re-saving */ |
|
1156 freehistdata(he, 0); |
|
1157 curline.histnum = curhist; |
|
1158 } else |
|
1159 he = prepnexthistent(); |
|
1160 |
|
1161 he->text = ztrdup(chline); |
|
1162 he->stim = time(NULL); |
|
1163 he->ftim = 0L; |
|
1164 he->flags = newflags; |
|
1165 |
|
1166 if ((he->nwords = chwordpos/2)) { |
|
1167 he->words = (short *)zalloc(chwordpos * sizeof(short)); |
|
1168 memcpy(he->words, chwords, chwordpos * sizeof(short)); |
|
1169 } |
|
1170 if (!(newflags & HIST_TMPSTORE)) |
|
1171 addhistnode(histtab, he->text, he); |
|
1172 } |
|
1173 zfree(chline, hlinesz); |
|
1174 zfree(chwords, chwordlen*sizeof(short)); |
|
1175 chline = NULL; |
|
1176 histactive = 0; |
|
1177 if (isset(SHAREHISTORY)? histfileIsLocked() : isset(INCAPPENDHISTORY)) |
|
1178 savehistfile(hf, 0, HFILE_USE_OPTIONS | HFILE_FAST); |
|
1179 unlockhistfile(hf); /* It's OK to call this even if we aren't locked */ |
|
1180 unqueue_signals(); |
|
1181 return !(flag & HISTFLAG_NOEXEC || errflag); |
|
1182 } |
|
1183 |
|
1184 /* Gives current expansion word if not last word before chwordpos. */ |
|
1185 |
|
1186 /**/ |
|
1187 int hwgetword = -1; |
|
1188 |
|
1189 /* begin a word */ |
|
1190 |
|
1191 /**/ |
|
1192 void |
|
1193 ihwbegin(int offset) |
|
1194 { |
|
1195 if (stophist == 2) |
|
1196 return; |
|
1197 if (chwordpos%2) |
|
1198 chwordpos--; /* make sure we're on a word start, not end */ |
|
1199 /* If we're expanding an alias, we should overwrite the expansion |
|
1200 * in the history. |
|
1201 */ |
|
1202 if ((inbufflags & INP_ALIAS) && !(inbufflags & INP_HIST)) |
|
1203 hwgetword = chwordpos; |
|
1204 else |
|
1205 hwgetword = -1; |
|
1206 chwords[chwordpos++] = hptr - chline + offset; |
|
1207 } |
|
1208 |
|
1209 /* add a word to the history List */ |
|
1210 |
|
1211 /**/ |
|
1212 void |
|
1213 ihwend(void) |
|
1214 { |
|
1215 if (stophist == 2) |
|
1216 return; |
|
1217 if (chwordpos%2 && chline) { |
|
1218 /* end of word reached and we've already begun a word */ |
|
1219 if (hptr > chline + chwords[chwordpos-1]) { |
|
1220 chwords[chwordpos++] = hptr - chline; |
|
1221 if (chwordpos >= chwordlen) { |
|
1222 chwords = (short *) realloc(chwords, |
|
1223 (chwordlen += 32) * |
|
1224 sizeof(short)); |
|
1225 } |
|
1226 if (hwgetword > -1) { |
|
1227 /* We want to reuse the current word position */ |
|
1228 chwordpos = hwgetword; |
|
1229 /* Start from where previous word ended, if possible */ |
|
1230 hptr = chline + chwords[chwordpos ? chwordpos - 1 : 0]; |
|
1231 } |
|
1232 } else { |
|
1233 /* scrub that last word, it doesn't exist */ |
|
1234 chwordpos--; |
|
1235 } |
|
1236 } |
|
1237 } |
|
1238 |
|
1239 /* Go back to immediately after the last word, skipping space. */ |
|
1240 |
|
1241 /**/ |
|
1242 void |
|
1243 histbackword(void) |
|
1244 { |
|
1245 if (!(chwordpos%2) && chwordpos) |
|
1246 hptr = chline + chwords[chwordpos-1]; |
|
1247 } |
|
1248 |
|
1249 /* Get the start and end point of the current history word */ |
|
1250 |
|
1251 /**/ |
|
1252 static void |
|
1253 hwget(char **startptr) |
|
1254 { |
|
1255 int pos = hwgetword > -1 ? hwgetword : chwordpos - 2; |
|
1256 |
|
1257 #ifdef DEBUG |
|
1258 /* debugging only */ |
|
1259 if (hwgetword == -1 && !chwordpos) { |
|
1260 /* no words available */ |
|
1261 DPUTS(1, "BUG: hwget() called with no words"); |
|
1262 *startptr = ""; |
|
1263 return; |
|
1264 } |
|
1265 else if (hwgetword == -1 && chwordpos%2) { |
|
1266 DPUTS(1, "BUG: hwget() called in middle of word"); |
|
1267 *startptr = ""; |
|
1268 return; |
|
1269 } |
|
1270 #endif |
|
1271 |
|
1272 *startptr = chline + chwords[pos]; |
|
1273 chline[chwords[++pos]] = '\0'; |
|
1274 } |
|
1275 |
|
1276 /* Replace the current history word with rep, if different */ |
|
1277 |
|
1278 /**/ |
|
1279 void |
|
1280 hwrep(char *rep) |
|
1281 { |
|
1282 char *start; |
|
1283 hwget(&start); |
|
1284 |
|
1285 if (!strcmp(rep, start)) |
|
1286 return; |
|
1287 |
|
1288 hptr = start; |
|
1289 chwordpos = (hwgetword > -1) ? hwgetword : chwordpos - 2; |
|
1290 hwbegin(0); |
|
1291 qbang = 1; |
|
1292 while (*rep) |
|
1293 hwaddc(*rep++); |
|
1294 hwend(); |
|
1295 } |
|
1296 |
|
1297 /* Get the entire current line, deleting it in the history. */ |
|
1298 |
|
1299 /**/ |
|
1300 mod_export char * |
|
1301 hgetline(void) |
|
1302 { |
|
1303 /* Currently only used by pushlineoredit(). |
|
1304 * It's necessary to prevent that from getting too pally with |
|
1305 * the history code. |
|
1306 */ |
|
1307 char *ret; |
|
1308 |
|
1309 if (!chline || hptr == chline) |
|
1310 return NULL; |
|
1311 *hptr = '\0'; |
|
1312 ret = dupstring(chline); |
|
1313 |
|
1314 /* reset line */ |
|
1315 hptr = chline; |
|
1316 chwordpos = 0; |
|
1317 hwgetword = -1; |
|
1318 |
|
1319 return ret; |
|
1320 } |
|
1321 |
|
1322 /* get an argument specification */ |
|
1323 |
|
1324 /**/ |
|
1325 static int |
|
1326 getargspec(int argc, int marg, int evset) |
|
1327 { |
|
1328 int c, ret = -1; |
|
1329 |
|
1330 if ((c = ingetc()) == '0') |
|
1331 return 0; |
|
1332 if (idigit(c)) { |
|
1333 ret = 0; |
|
1334 while (idigit(c)) { |
|
1335 ret = ret * 10 + c - '0'; |
|
1336 c = ingetc(); |
|
1337 } |
|
1338 inungetc(c); |
|
1339 } else if (c == '^') |
|
1340 ret = 1; |
|
1341 else if (c == '$') |
|
1342 ret = argc; |
|
1343 else if (c == '%') { |
|
1344 if (evset) { |
|
1345 herrflush(); |
|
1346 zerr("Ambiguous history reference", NULL, 0); |
|
1347 return -2; |
|
1348 } |
|
1349 if (marg == -1) { |
|
1350 herrflush(); |
|
1351 zerr("%% with no previous word matched", NULL, 0); |
|
1352 return -2; |
|
1353 } |
|
1354 ret = marg; |
|
1355 } else |
|
1356 inungetc(c); |
|
1357 return ret; |
|
1358 } |
|
1359 |
|
1360 /* do ?foo? search */ |
|
1361 |
|
1362 /**/ |
|
1363 static zlong |
|
1364 hconsearch(char *str, int *marg) |
|
1365 { |
|
1366 int t1 = 0; |
|
1367 char *s; |
|
1368 Histent he; |
|
1369 |
|
1370 for (he = up_histent(hist_ring); he; he = up_histent(he)) { |
|
1371 if (he->flags & HIST_FOREIGN) |
|
1372 continue; |
|
1373 if ((s = strstr(he->text, str))) { |
|
1374 int pos = s - he->text; |
|
1375 while (t1 < he->nwords && he->words[2*t1] <= pos) |
|
1376 t1++; |
|
1377 *marg = t1 - 1; |
|
1378 return he->histnum; |
|
1379 } |
|
1380 } |
|
1381 return -1; |
|
1382 } |
|
1383 |
|
1384 /* do !foo search */ |
|
1385 |
|
1386 /**/ |
|
1387 zlong |
|
1388 hcomsearch(char *str) |
|
1389 { |
|
1390 Histent he; |
|
1391 int len = strlen(str); |
|
1392 |
|
1393 for (he = up_histent(hist_ring); he; he = up_histent(he)) { |
|
1394 if (he->flags & HIST_FOREIGN) |
|
1395 continue; |
|
1396 if (strncmp(he->text, str, len) == 0) |
|
1397 return he->histnum; |
|
1398 } |
|
1399 return -1; |
|
1400 } |
|
1401 |
|
1402 /* various utilities for : modifiers */ |
|
1403 |
|
1404 /**/ |
|
1405 int |
|
1406 remtpath(char **junkptr) |
|
1407 { |
|
1408 char *str = strend(*junkptr); |
|
1409 |
|
1410 /* ignore trailing slashes */ |
|
1411 while (str >= *junkptr && IS_DIRSEP(*str)) |
|
1412 --str; |
|
1413 /* skip filename */ |
|
1414 while (str >= *junkptr && !IS_DIRSEP(*str)) |
|
1415 --str; |
|
1416 if (str < *junkptr) { |
|
1417 if (IS_DIRSEP(**junkptr)) |
|
1418 *junkptr = dupstring ("/"); |
|
1419 else |
|
1420 *junkptr = dupstring ("."); |
|
1421 |
|
1422 return 0; |
|
1423 } |
|
1424 /* repeated slashes are considered like a single slash */ |
|
1425 while (str > *junkptr && IS_DIRSEP(str[-1])) |
|
1426 --str; |
|
1427 /* never erase the root slash */ |
|
1428 if (str == *junkptr) { |
|
1429 ++str; |
|
1430 /* Leading doubled slashes (`//') have a special meaning on cygwin |
|
1431 and some old flavor of UNIX, so we do not assimilate them to |
|
1432 a single slash. However a greater number is ok to squeeze. */ |
|
1433 if (IS_DIRSEP(*str) && !IS_DIRSEP(str[1])) |
|
1434 ++str; |
|
1435 } |
|
1436 *str = '\0'; |
|
1437 return 1; |
|
1438 } |
|
1439 |
|
1440 /**/ |
|
1441 int |
|
1442 remtext(char **junkptr) |
|
1443 { |
|
1444 char *str; |
|
1445 |
|
1446 for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str) |
|
1447 if (*str == '.') { |
|
1448 *str = '\0'; |
|
1449 return 1; |
|
1450 } |
|
1451 return 0; |
|
1452 } |
|
1453 |
|
1454 /**/ |
|
1455 int |
|
1456 rembutext(char **junkptr) |
|
1457 { |
|
1458 char *str; |
|
1459 |
|
1460 for (str = strend(*junkptr); str >= *junkptr && !IS_DIRSEP(*str); --str) |
|
1461 if (*str == '.') { |
|
1462 *junkptr = dupstring(str + 1); /* .xx or xx? */ |
|
1463 return 1; |
|
1464 } |
|
1465 /* no extension */ |
|
1466 *junkptr = dupstring (""); |
|
1467 return 0; |
|
1468 } |
|
1469 |
|
1470 /**/ |
|
1471 mod_export int |
|
1472 remlpaths(char **junkptr) |
|
1473 { |
|
1474 char *str = strend(*junkptr); |
|
1475 |
|
1476 if (IS_DIRSEP(*str)) { |
|
1477 /* remove trailing slashes */ |
|
1478 while (str >= *junkptr && IS_DIRSEP(*str)) |
|
1479 --str; |
|
1480 str[1] = '\0'; |
|
1481 } |
|
1482 for (; str >= *junkptr; --str) |
|
1483 if (IS_DIRSEP(*str)) { |
|
1484 *str = '\0'; |
|
1485 *junkptr = dupstring(str + 1); |
|
1486 return 1; |
|
1487 } |
|
1488 return 0; |
|
1489 } |
|
1490 |
|
1491 /**/ |
|
1492 int |
|
1493 makeuppercase(char **junkptr) |
|
1494 { |
|
1495 char *str = *junkptr; |
|
1496 |
|
1497 for (; *str; str++) |
|
1498 *str = tuupper(*str); |
|
1499 return 1; |
|
1500 } |
|
1501 |
|
1502 /**/ |
|
1503 int |
|
1504 makelowercase(char **junkptr) |
|
1505 { |
|
1506 char *str = *junkptr; |
|
1507 |
|
1508 for (; *str; str++) |
|
1509 *str = tulower(*str); |
|
1510 return 1; |
|
1511 } |
|
1512 |
|
1513 /**/ |
|
1514 int |
|
1515 makecapitals(char **junkptr) |
|
1516 { |
|
1517 char *str = *junkptr; |
|
1518 |
|
1519 for (; *str;) { |
|
1520 for (; *str && !ialnum(*str); str++); |
|
1521 if (*str) |
|
1522 *str = tuupper(*str), str++; |
|
1523 for (; *str && ialnum(*str); str++) |
|
1524 *str = tulower(*str); |
|
1525 } |
|
1526 return 1; |
|
1527 } |
|
1528 |
|
1529 /**/ |
|
1530 void |
|
1531 subst(char **strptr, char *in, char *out, int gbal) |
|
1532 { |
|
1533 char *str = *strptr, *instr = *strptr, *substcut, *sptr, *oldstr; |
|
1534 int off, inlen, outlen; |
|
1535 |
|
1536 if (!*in) |
|
1537 in = str, gbal = 0; |
|
1538 if (!(substcut = (char *)strstr(str, in))) |
|
1539 return; |
|
1540 inlen = strlen(in); |
|
1541 sptr = convamps(out, in, inlen); |
|
1542 outlen = strlen(sptr); |
|
1543 |
|
1544 do { |
|
1545 *substcut = '\0'; |
|
1546 off = substcut - *strptr + outlen; |
|
1547 substcut += inlen; |
|
1548 *strptr = tricat(oldstr = *strptr, sptr, substcut); |
|
1549 if (oldstr != instr) |
|
1550 zsfree(oldstr); |
|
1551 str = (char *)*strptr + off; |
|
1552 } while (gbal && (substcut = (char *)strstr(str, in))); |
|
1553 } |
|
1554 |
|
1555 /**/ |
|
1556 static char * |
|
1557 convamps(char *out, char *in, int inlen) |
|
1558 { |
|
1559 char *ptr, *ret, *pp; |
|
1560 int slen, sdup = 0; |
|
1561 |
|
1562 for (ptr = out, slen = 0; *ptr; ptr++, slen++) |
|
1563 if (*ptr == '\\') |
|
1564 ptr++, sdup = 1; |
|
1565 else if (*ptr == '&') |
|
1566 slen += inlen - 1, sdup = 1; |
|
1567 if (!sdup) |
|
1568 return out; |
|
1569 ret = pp = (char *) zhalloc(slen + 1); |
|
1570 for (ptr = out; *ptr; ptr++) |
|
1571 if (*ptr == '\\') |
|
1572 *pp++ = *++ptr; |
|
1573 else if (*ptr == '&') { |
|
1574 strcpy(pp, in); |
|
1575 pp += inlen; |
|
1576 } else |
|
1577 *pp++ = *ptr; |
|
1578 *pp = '\0'; |
|
1579 return ret; |
|
1580 } |
|
1581 |
|
1582 /**/ |
|
1583 mod_export void |
|
1584 checkcurline(Histent he) |
|
1585 { |
|
1586 if (he->histnum == curhist && (histactive & HA_ACTIVE)) { |
|
1587 curline.text = chline; |
|
1588 curline.nwords = chwordpos/2; |
|
1589 curline.words = chwords; |
|
1590 } |
|
1591 } |
|
1592 |
|
1593 /**/ |
|
1594 mod_export Histent |
|
1595 quietgethist(int ev) |
|
1596 { |
|
1597 return gethistent(ev, GETHIST_EXACT); |
|
1598 } |
|
1599 |
|
1600 /**/ |
|
1601 static Histent |
|
1602 gethist(int ev) |
|
1603 { |
|
1604 Histent ret; |
|
1605 |
|
1606 ret = quietgethist(ev); |
|
1607 if (!ret) { |
|
1608 herrflush(); |
|
1609 zerr("no such event: %d", NULL, ev); |
|
1610 } |
|
1611 return ret; |
|
1612 } |
|
1613 |
|
1614 /**/ |
|
1615 static char * |
|
1616 getargs(Histent elist, int arg1, int arg2) |
|
1617 { |
|
1618 short *words = elist->words; |
|
1619 int pos1, nwords = elist->nwords; |
|
1620 |
|
1621 if (arg2 < arg1 || arg1 >= nwords || arg2 >= nwords) { |
|
1622 /* remember, argN is indexed from 0, nwords is total no. of words */ |
|
1623 herrflush(); |
|
1624 zerr("no such word in event", NULL, 0); |
|
1625 return NULL; |
|
1626 } |
|
1627 |
|
1628 pos1 = words[2*arg1]; |
|
1629 return dupstrpfx(elist->text + pos1, words[2*arg2+1] - pos1); |
|
1630 } |
|
1631 |
|
1632 /**/ |
|
1633 void |
|
1634 upcase(char **x) |
|
1635 { |
|
1636 char *pp = *(char **)x; |
|
1637 |
|
1638 for (; *pp; pp++) |
|
1639 *pp = tuupper(*pp); |
|
1640 } |
|
1641 |
|
1642 /**/ |
|
1643 void |
|
1644 downcase(char **x) |
|
1645 { |
|
1646 char *pp = *(char **)x; |
|
1647 |
|
1648 for (; *pp; pp++) |
|
1649 *pp = tulower(*pp); |
|
1650 } |
|
1651 |
|
1652 /**/ |
|
1653 int |
|
1654 quote(char **tr) |
|
1655 { |
|
1656 char *ptr, *rptr, **str = (char **)tr; |
|
1657 int len = 3; |
|
1658 int inquotes = 0; |
|
1659 |
|
1660 for (ptr = *str; *ptr; ptr++, len++) |
|
1661 if (*ptr == '\'') { |
|
1662 len += 3; |
|
1663 if (!inquotes) |
|
1664 inquotes = 1; |
|
1665 else |
|
1666 inquotes = 0; |
|
1667 } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') |
|
1668 len += 2; |
|
1669 ptr = *str; |
|
1670 *str = rptr = (char *) zhalloc(len); |
|
1671 *rptr++ = '\''; |
|
1672 for (; *ptr; ptr++) |
|
1673 if (*ptr == '\'') { |
|
1674 if (!inquotes) |
|
1675 inquotes = 1; |
|
1676 else |
|
1677 inquotes = 0; |
|
1678 *rptr++ = '\''; |
|
1679 *rptr++ = '\\'; |
|
1680 *rptr++ = '\''; |
|
1681 *rptr++ = '\''; |
|
1682 } else if (inblank(*ptr) && !inquotes && ptr[-1] != '\\') { |
|
1683 *rptr++ = '\''; |
|
1684 *rptr++ = *ptr; |
|
1685 *rptr++ = '\''; |
|
1686 } else |
|
1687 *rptr++ = *ptr; |
|
1688 *rptr++ = '\''; |
|
1689 *rptr++ = 0; |
|
1690 str[1] = NULL; |
|
1691 return 0; |
|
1692 } |
|
1693 |
|
1694 /**/ |
|
1695 static int |
|
1696 quotebreak(char **tr) |
|
1697 { |
|
1698 char *ptr, *rptr, **str = (char **)tr; |
|
1699 int len = 3; |
|
1700 |
|
1701 for (ptr = *str; *ptr; ptr++, len++) |
|
1702 if (*ptr == '\'') |
|
1703 len += 3; |
|
1704 else if (inblank(*ptr)) |
|
1705 len += 2; |
|
1706 ptr = *str; |
|
1707 *str = rptr = (char *) zhalloc(len); |
|
1708 *rptr++ = '\''; |
|
1709 for (; *ptr;) |
|
1710 if (*ptr == '\'') { |
|
1711 *rptr++ = '\''; |
|
1712 *rptr++ = '\\'; |
|
1713 *rptr++ = '\''; |
|
1714 *rptr++ = '\''; |
|
1715 ptr++; |
|
1716 } else if (inblank(*ptr)) { |
|
1717 *rptr++ = '\''; |
|
1718 *rptr++ = *ptr++; |
|
1719 *rptr++ = '\''; |
|
1720 } else |
|
1721 *rptr++ = *ptr++; |
|
1722 *rptr++ = '\''; |
|
1723 *rptr++ = '\0'; |
|
1724 return 0; |
|
1725 } |
|
1726 |
|
1727 /* read an arbitrary amount of data into a buffer until stop is found */ |
|
1728 |
|
1729 #if 0 /**/ |
|
1730 char * |
|
1731 hdynread(int stop) |
|
1732 { |
|
1733 int bsiz = 256, ct = 0, c; |
|
1734 char *buf = (char *)zalloc(bsiz), *ptr; |
|
1735 |
|
1736 ptr = buf; |
|
1737 while ((c = ingetc()) != stop && c != '\n' && !lexstop) { |
|
1738 if (c == '\\') |
|
1739 c = ingetc(); |
|
1740 *ptr++ = c; |
|
1741 if (++ct == bsiz) { |
|
1742 buf = realloc(buf, bsiz *= 2); |
|
1743 ptr = buf + ct; |
|
1744 } |
|
1745 } |
|
1746 *ptr = 0; |
|
1747 if (c == '\n') { |
|
1748 inungetc('\n'); |
|
1749 zerr("delimiter expected", NULL, 0); |
|
1750 zfree(buf, bsiz); |
|
1751 return NULL; |
|
1752 } |
|
1753 return buf; |
|
1754 } |
|
1755 #endif |
|
1756 |
|
1757 /**/ |
|
1758 static char * |
|
1759 hdynread2(int stop) |
|
1760 { |
|
1761 int bsiz = 256, ct = 0, c; |
|
1762 char *buf = (char *)zalloc(bsiz), *ptr; |
|
1763 |
|
1764 ptr = buf; |
|
1765 while ((c = ingetc()) != stop && c != '\n' && !lexstop) { |
|
1766 if (c == '\n') { |
|
1767 inungetc(c); |
|
1768 break; |
|
1769 } |
|
1770 if (c == '\\') |
|
1771 c = ingetc(); |
|
1772 *ptr++ = c; |
|
1773 if (++ct == bsiz) { |
|
1774 buf = realloc(buf, bsiz *= 2); |
|
1775 ptr = buf + ct; |
|
1776 } |
|
1777 } |
|
1778 *ptr = 0; |
|
1779 if (c == '\n') |
|
1780 inungetc('\n'); |
|
1781 return buf; |
|
1782 } |
|
1783 |
|
1784 /**/ |
|
1785 void |
|
1786 inithist(void) |
|
1787 { |
|
1788 createhisttable(); |
|
1789 } |
|
1790 |
|
1791 /**/ |
|
1792 void |
|
1793 resizehistents(void) |
|
1794 { |
|
1795 if (histlinect > histsiz) { |
|
1796 /* The reason we don't just call freehistnode(hist_ring->down) is |
|
1797 * so that we can honor the HISTEXPIREDUPSFIRST setting. */ |
|
1798 putoldhistentryontop(0); |
|
1799 freehistnode((HashNode)hist_ring); |
|
1800 while (histlinect > histsiz) { |
|
1801 putoldhistentryontop(1); |
|
1802 freehistnode((HashNode)hist_ring); |
|
1803 } |
|
1804 } |
|
1805 } |
|
1806 |
|
1807 /* Remember the last line in the history file so we can find it again. */ |
|
1808 static struct histfile_stats { |
|
1809 char *text; |
|
1810 time_t stim, mtim; |
|
1811 off_t fpos, fsiz; |
|
1812 zlong next_write_ev; |
|
1813 } lasthist; |
|
1814 |
|
1815 static struct histsave { |
|
1816 struct histfile_stats lasthist; |
|
1817 char *histfile; |
|
1818 HashTable histtab; |
|
1819 Histent hist_ring; |
|
1820 zlong curhist; |
|
1821 zlong histlinect; |
|
1822 zlong histsiz; |
|
1823 zlong savehistsiz; |
|
1824 int locallevel; |
|
1825 } *histsave_stack; |
|
1826 static int histsave_stack_size = 0; |
|
1827 static int histsave_stack_pos = 0; |
|
1828 |
|
1829 static zlong histfile_linect; |
|
1830 |
|
1831 static int |
|
1832 readhistline(int start, char **bufp, int *bufsiz, FILE *in) |
|
1833 { |
|
1834 char *buf = *bufp; |
|
1835 if (fgets(buf + start, *bufsiz - start, in)) { |
|
1836 int len = start + strlen(buf + start); |
|
1837 if (len == start) |
|
1838 return -1; |
|
1839 if (buf[len - 1] != '\n') { |
|
1840 if (!feof(in)) { |
|
1841 if (len < (*bufsiz) - 1) |
|
1842 return -1; |
|
1843 *bufp = zrealloc(buf, 2 * (*bufsiz)); |
|
1844 *bufsiz = 2 * (*bufsiz); |
|
1845 return readhistline(len, bufp, bufsiz, in); |
|
1846 } |
|
1847 } |
|
1848 else { |
|
1849 buf[len - 1] = '\0'; |
|
1850 if (len > 1 && buf[len - 2] == '\\') { |
|
1851 buf[--len - 1] = '\n'; |
|
1852 if (!feof(in)) |
|
1853 return readhistline(len, bufp, bufsiz, in); |
|
1854 } |
|
1855 } |
|
1856 return len; |
|
1857 } |
|
1858 return 0; |
|
1859 } |
|
1860 |
|
1861 /**/ |
|
1862 void |
|
1863 readhistfile(char *fn, int err, int readflags) |
|
1864 { |
|
1865 char *buf, *start = NULL; |
|
1866 FILE *in; |
|
1867 Histent he; |
|
1868 time_t stim, ftim, tim = time(NULL); |
|
1869 off_t fpos; |
|
1870 short *wordlist; |
|
1871 struct stat sb; |
|
1872 int nwordpos, nwordlist, bufsiz; |
|
1873 int searching, newflags, l; |
|
1874 |
|
1875 if (!fn && !(fn = getsparam("HISTFILE"))) |
|
1876 return; |
|
1877 if (readflags & HFILE_FAST) { |
|
1878 if (stat(unmeta(fn), &sb) < 0 |
|
1879 || (lasthist.fsiz == sb.st_size && lasthist.mtim == sb.st_mtime) |
|
1880 || !lockhistfile(fn, 0)) |
|
1881 return; |
|
1882 lasthist.fsiz = sb.st_size; |
|
1883 lasthist.mtim = sb.st_mtime; |
|
1884 } |
|
1885 else if (!lockhistfile(fn, 1)) |
|
1886 return; |
|
1887 if ((in = fopen(unmeta(fn), "r"))) { |
|
1888 nwordlist = 64; |
|
1889 wordlist = (short *)zalloc(nwordlist*sizeof(short)); |
|
1890 bufsiz = 1024; |
|
1891 buf = zalloc(bufsiz); |
|
1892 |
|
1893 if (readflags & HFILE_FAST && lasthist.text) { |
|
1894 if (lasthist.fpos < lasthist.fsiz) { |
|
1895 fseek(in, lasthist.fpos, 0); |
|
1896 searching = 1; |
|
1897 } |
|
1898 else { |
|
1899 histfile_linect = 0; |
|
1900 searching = -1; |
|
1901 } |
|
1902 } else |
|
1903 searching = 0; |
|
1904 |
|
1905 newflags = HIST_OLD | HIST_READ; |
|
1906 if (readflags & HFILE_FAST) |
|
1907 newflags |= HIST_FOREIGN; |
|
1908 if (readflags & HFILE_SKIPOLD |
|
1909 || (hist_ignore_all_dups && newflags & hist_skip_flags)) |
|
1910 newflags |= HIST_MAKEUNIQUE; |
|
1911 while (fpos = ftell(in), (l = readhistline(0, &buf, &bufsiz, in))) { |
|
1912 char *pt = buf; |
|
1913 |
|
1914 if (l < 0) { |
|
1915 zerr("corrupt history file %s", fn, 0); |
|
1916 break; |
|
1917 } |
|
1918 if (*pt == ':') { |
|
1919 pt++; |
|
1920 stim = zstrtol(pt, NULL, 0); |
|
1921 for (; *pt != ':' && *pt; pt++); |
|
1922 if (*pt) { |
|
1923 pt++; |
|
1924 ftim = zstrtol(pt, NULL, 0); |
|
1925 for (; *pt != ';' && *pt; pt++); |
|
1926 if (*pt) |
|
1927 pt++; |
|
1928 } else |
|
1929 ftim = stim; |
|
1930 } else { |
|
1931 if (*pt == '\\' && pt[1] == ':') |
|
1932 pt++; |
|
1933 stim = ftim = 0; |
|
1934 } |
|
1935 |
|
1936 if (searching) { |
|
1937 if (searching > 0) { |
|
1938 if (stim == lasthist.stim |
|
1939 && histstrcmp(pt, lasthist.text) == 0) |
|
1940 searching = 0; |
|
1941 else { |
|
1942 fseek(in, 0, 0); |
|
1943 histfile_linect = 0; |
|
1944 searching = -1; |
|
1945 } |
|
1946 continue; |
|
1947 } |
|
1948 else if (stim < lasthist.stim) { |
|
1949 histfile_linect++; |
|
1950 continue; |
|
1951 } |
|
1952 searching = 0; |
|
1953 } |
|
1954 |
|
1955 if (readflags & HFILE_USE_OPTIONS) { |
|
1956 histfile_linect++; |
|
1957 lasthist.fpos = fpos; |
|
1958 lasthist.stim = stim; |
|
1959 } |
|
1960 |
|
1961 he = prepnexthistent(); |
|
1962 he->text = ztrdup(pt); |
|
1963 he->flags = newflags; |
|
1964 if ((he->stim = stim) == 0) |
|
1965 he->stim = he->ftim = tim; |
|
1966 else if (ftim < stim) |
|
1967 he->ftim = stim + ftim; |
|
1968 else |
|
1969 he->ftim = ftim; |
|
1970 |
|
1971 /* Divide up the words. We don't know how it lexes, |
|
1972 so just look for white-space. |
|
1973 */ |
|
1974 nwordpos = 0; |
|
1975 start = pt; |
|
1976 do { |
|
1977 while (inblank(*pt)) |
|
1978 pt++; |
|
1979 if (*pt) { |
|
1980 if (nwordpos >= nwordlist) |
|
1981 wordlist = (short *) realloc(wordlist, |
|
1982 (nwordlist += 64)*sizeof(short)); |
|
1983 wordlist[nwordpos++] = pt - start; |
|
1984 while (*pt && !inblank(*pt)) |
|
1985 pt++; |
|
1986 wordlist[nwordpos++] = pt - start; |
|
1987 } |
|
1988 } while (*pt); |
|
1989 |
|
1990 he->nwords = nwordpos/2; |
|
1991 if (he->nwords) { |
|
1992 he->words = (short *)zalloc(nwordpos*sizeof(short)); |
|
1993 memcpy(he->words, wordlist, nwordpos*sizeof(short)); |
|
1994 } else |
|
1995 he->words = (short *)NULL; |
|
1996 addhistnode(histtab, he->text, he); |
|
1997 if (he->flags & HIST_DUP) { |
|
1998 freehistnode((HashNode)he); |
|
1999 curhist--; |
|
2000 } |
|
2001 } |
|
2002 if (start && readflags & HFILE_USE_OPTIONS) { |
|
2003 zsfree(lasthist.text); |
|
2004 lasthist.text = ztrdup(start); |
|
2005 } |
|
2006 zfree(wordlist, nwordlist*sizeof(short)); |
|
2007 zfree(buf, bufsiz); |
|
2008 |
|
2009 fclose(in); |
|
2010 } else if (err) |
|
2011 zerr("can't read history file %s", fn, 0); |
|
2012 |
|
2013 unlockhistfile(fn); |
|
2014 } |
|
2015 |
|
2016 /**/ |
|
2017 void |
|
2018 savehistfile(char *fn, int err, int writeflags) |
|
2019 { |
|
2020 char *t, *start = NULL; |
|
2021 FILE *out; |
|
2022 Histent he; |
|
2023 zlong xcurhist = curhist - !!(histactive & HA_ACTIVE); |
|
2024 int extended_history = isset(EXTENDEDHISTORY); |
|
2025 |
|
2026 if (!interact || savehistsiz <= 0 || !hist_ring |
|
2027 || (!fn && !(fn = getsparam("HISTFILE")))) |
|
2028 return; |
|
2029 if (writeflags & HFILE_FAST) { |
|
2030 he = gethistent(lasthist.next_write_ev, GETHIST_DOWNWARD); |
|
2031 while (he && he->flags & HIST_OLD) { |
|
2032 lasthist.next_write_ev = he->histnum + 1; |
|
2033 he = down_histent(he); |
|
2034 } |
|
2035 if (!he || !lockhistfile(fn, 0)) |
|
2036 return; |
|
2037 if (histfile_linect > savehistsiz + savehistsiz / 5) |
|
2038 writeflags &= ~HFILE_FAST; |
|
2039 } |
|
2040 else { |
|
2041 if (!lockhistfile(fn, 1)) |
|
2042 return; |
|
2043 he = hist_ring->down; |
|
2044 } |
|
2045 if (writeflags & HFILE_USE_OPTIONS) { |
|
2046 if (isset(APPENDHISTORY) || isset(INCAPPENDHISTORY) |
|
2047 || isset(SHAREHISTORY)) |
|
2048 writeflags |= HFILE_APPEND | HFILE_SKIPOLD; |
|
2049 else |
|
2050 histfile_linect = 0; |
|
2051 if (isset(HISTSAVENODUPS)) |
|
2052 writeflags |= HFILE_SKIPDUPS; |
|
2053 if (isset(SHAREHISTORY)) |
|
2054 extended_history = 1; |
|
2055 } |
|
2056 if (writeflags & HFILE_APPEND) { |
|
2057 out = fdopen(open(unmeta(fn), |
|
2058 O_CREAT | O_WRONLY | O_APPEND | O_NOCTTY, 0600), "a"); |
|
2059 } |
|
2060 else { |
|
2061 out = fdopen(open(unmeta(fn), |
|
2062 O_CREAT | O_WRONLY | O_TRUNC | O_NOCTTY, 0600), "w"); |
|
2063 } |
|
2064 if (out) { |
|
2065 for (; he && he->histnum <= xcurhist; he = down_histent(he)) { |
|
2066 if ((writeflags & HFILE_SKIPDUPS && he->flags & HIST_DUP) |
|
2067 || (writeflags & HFILE_SKIPFOREIGN && he->flags & HIST_FOREIGN) |
|
2068 || he->flags & HIST_TMPSTORE) |
|
2069 continue; |
|
2070 if (writeflags & HFILE_SKIPOLD) { |
|
2071 if (he->flags & HIST_OLD) |
|
2072 continue; |
|
2073 he->flags |= HIST_OLD; |
|
2074 if (writeflags & HFILE_USE_OPTIONS) |
|
2075 lasthist.next_write_ev = he->histnum + 1; |
|
2076 } |
|
2077 if (writeflags & HFILE_USE_OPTIONS) { |
|
2078 lasthist.fpos = ftell(out); |
|
2079 lasthist.stim = he->stim; |
|
2080 histfile_linect++; |
|
2081 } |
|
2082 t = start = he->text; |
|
2083 if (extended_history) { |
|
2084 fprintf(out, ": %ld:%ld;", (long)he->stim, |
|
2085 he->ftim? (long)(he->ftim - he->stim) : 0L); |
|
2086 } else if (*t == ':') |
|
2087 fputc('\\', out); |
|
2088 |
|
2089 for (; *t; t++) { |
|
2090 if (*t == '\n') |
|
2091 fputc('\\', out); |
|
2092 fputc(*t, out); |
|
2093 } |
|
2094 fputc('\n', out); |
|
2095 } |
|
2096 if (start && writeflags & HFILE_USE_OPTIONS) { |
|
2097 struct stat sb; |
|
2098 fflush(out); |
|
2099 if (fstat(fileno(out), &sb) == 0) { |
|
2100 lasthist.fsiz = sb.st_size; |
|
2101 lasthist.mtim = sb.st_mtime; |
|
2102 } |
|
2103 zsfree(lasthist.text); |
|
2104 lasthist.text = ztrdup(start); |
|
2105 } |
|
2106 fclose(out); |
|
2107 |
|
2108 if (writeflags & HFILE_SKIPOLD |
|
2109 && !(writeflags & (HFILE_FAST | HFILE_NO_REWRITE))) { |
|
2110 int remember_histactive = histactive; |
|
2111 |
|
2112 /* Zeroing histactive avoids unnecessary munging of curline. */ |
|
2113 histactive = 0; |
|
2114 /* The NULL leaves HISTFILE alone, preserving fn's value. */ |
|
2115 pushhiststack(NULL, savehistsiz, savehistsiz, -1); |
|
2116 |
|
2117 hist_ignore_all_dups |= isset(HISTSAVENODUPS); |
|
2118 readhistfile(fn, err, 0); |
|
2119 hist_ignore_all_dups = isset(HISTIGNOREALLDUPS); |
|
2120 if (histlinect) |
|
2121 savehistfile(fn, err, 0); |
|
2122 |
|
2123 pophiststack(); |
|
2124 histactive = remember_histactive; |
|
2125 } |
|
2126 } else if (err) |
|
2127 zerr("can't write history file %s", fn, 0); |
|
2128 |
|
2129 unlockhistfile(fn); |
|
2130 } |
|
2131 |
|
2132 static int lockhistct; |
|
2133 |
|
2134 /**/ |
|
2135 int |
|
2136 lockhistfile(char *fn, int keep_trying) |
|
2137 { |
|
2138 int ct = lockhistct; |
|
2139 |
|
2140 if (!fn && !(fn = getsparam("HISTFILE"))) |
|
2141 return 0; |
|
2142 if (!lockhistct++) { |
|
2143 struct stat sb; |
|
2144 int fd; |
|
2145 char *lockfile; |
|
2146 #ifdef HAVE_LINK |
|
2147 char *tmpfile; |
|
2148 #endif |
|
2149 |
|
2150 lockfile = bicat(unmeta(fn), ".LOCK"); |
|
2151 #ifdef HAVE_LINK |
|
2152 if ((fd = gettempfile(fn, 0, &tmpfile)) >= 0) { |
|
2153 FILE *out = fdopen(fd, "w"); |
|
2154 if (out) { |
|
2155 fprintf(out, "%ld %s\n", (long)getpid(), getsparam("HOST")); |
|
2156 fclose(out); |
|
2157 } else |
|
2158 close(fd); |
|
2159 while (link(tmpfile, lockfile) < 0) { |
|
2160 if (errno != EEXIST || !keep_trying) |
|
2161 ; |
|
2162 else if (stat(lockfile, &sb) < 0) { |
|
2163 if (errno == ENOENT) |
|
2164 continue; |
|
2165 } |
|
2166 else { |
|
2167 if (time(NULL) - sb.st_mtime < 10) |
|
2168 sleep(1); |
|
2169 else |
|
2170 unlink(lockfile); |
|
2171 continue; |
|
2172 } |
|
2173 lockhistct--; |
|
2174 break; |
|
2175 } |
|
2176 unlink(tmpfile); |
|
2177 free(tmpfile); |
|
2178 } |
|
2179 #else /* not HAVE_LINK */ |
|
2180 while ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0644)) < 0) { |
|
2181 if (errno != EEXIST || !keep_trying) |
|
2182 break; |
|
2183 if (stat(lockfile, &sb) < 0) { |
|
2184 if (errno == ENOENT) |
|
2185 continue; |
|
2186 break; |
|
2187 } |
|
2188 if (time(NULL) - sb.st_mtime < 10) |
|
2189 sleep(1); |
|
2190 else |
|
2191 unlink(lockfile); |
|
2192 } |
|
2193 if (fd < 0) |
|
2194 lockhistct--; |
|
2195 else { |
|
2196 FILE *out = fdopen(fd, "w"); |
|
2197 if (out) { |
|
2198 fprintf(out, "%ld %s\n", (long)mypid, getsparam("HOST")); |
|
2199 fclose(out); |
|
2200 } else |
|
2201 close(fd); |
|
2202 } |
|
2203 #endif /* not HAVE_LINK */ |
|
2204 free(lockfile); |
|
2205 } |
|
2206 return ct != lockhistct; |
|
2207 } |
|
2208 |
|
2209 /* Unlock the history file if this corresponds to the last nested lock |
|
2210 * request. If we don't have the file locked, just return. |
|
2211 */ |
|
2212 |
|
2213 /**/ |
|
2214 void |
|
2215 unlockhistfile(char *fn) |
|
2216 { |
|
2217 if (!fn && !(fn = getsparam("HISTFILE"))) |
|
2218 return; |
|
2219 if (--lockhistct) { |
|
2220 if (lockhistct < 0) |
|
2221 lockhistct = 0; |
|
2222 } |
|
2223 else { |
|
2224 char *lockfile; |
|
2225 fn = unmeta(fn); |
|
2226 lockfile = zalloc(strlen(fn) + 5 + 1); |
|
2227 sprintf(lockfile, "%s.LOCK", fn); |
|
2228 unlink(lockfile); |
|
2229 free(lockfile); |
|
2230 } |
|
2231 } |
|
2232 |
|
2233 /**/ |
|
2234 int |
|
2235 histfileIsLocked(void) |
|
2236 { |
|
2237 return lockhistct > 0; |
|
2238 } |
|
2239 |
|
2240 /* Get the words in the current buffer. Using the lexer. */ |
|
2241 |
|
2242 /**/ |
|
2243 mod_export LinkList |
|
2244 bufferwords(LinkList list, char *buf, int *index) |
|
2245 { |
|
2246 int num = 0, cur = -1, got = 0, ne = noerrs, ocs = cs, oll = ll; |
|
2247 int owb = wb, owe = we, oadx = addedx, ozp = zleparse, onc = nocomments; |
|
2248 int ona = noaliases; |
|
2249 char *p; |
|
2250 |
|
2251 if (!list) |
|
2252 list = newlinklist(); |
|
2253 |
|
2254 zleparse = 1; |
|
2255 addedx = 0; |
|
2256 noerrs = 1; |
|
2257 lexsave(); |
|
2258 if (buf) { |
|
2259 int l = strlen(buf); |
|
2260 |
|
2261 p = (char *) zhalloc(l + 2); |
|
2262 memcpy(p, buf, l); |
|
2263 p[l] = ' '; |
|
2264 p[l + 1] = '\0'; |
|
2265 inpush(p, 0, NULL); |
|
2266 cs = strlen(p) + 1; |
|
2267 nocomments = 1; |
|
2268 } else if (!isfirstln && chline) { |
|
2269 p = (char *) zhalloc(hptr - chline + ll + 2); |
|
2270 memcpy(p, chline, hptr - chline); |
|
2271 memcpy(p + (hptr - chline), line, ll); |
|
2272 p[(hptr - chline) + ll] = ' '; |
|
2273 p[(hptr - chline) + ll + 1] = '\0'; |
|
2274 inpush(p, 0, NULL); |
|
2275 cs += hptr - chline; |
|
2276 } else { |
|
2277 p = (char *) zhalloc(ll + 2); |
|
2278 memcpy(p, line, ll); |
|
2279 p[ll] = ' '; |
|
2280 p[ll + 1] = '\0'; |
|
2281 inpush(p, 0, NULL); |
|
2282 } |
|
2283 ll = strlen(p); |
|
2284 if (cs) |
|
2285 cs--; |
|
2286 strinbeg(0); |
|
2287 noaliases = 1; |
|
2288 do { |
|
2289 if (incond) |
|
2290 incond = 1 + (tok != DINBRACK && tok != INPAR && |
|
2291 tok != DBAR && tok != DAMPER && |
|
2292 tok != BANG); |
|
2293 ctxtlex(); |
|
2294 if (tok == ENDINPUT || tok == LEXERR) |
|
2295 break; |
|
2296 if (tokstr && *tokstr) { |
|
2297 untokenize((p = dupstring(tokstr))); |
|
2298 addlinknode(list, p); |
|
2299 num++; |
|
2300 } else if (buf) { |
|
2301 if (IS_REDIROP(tok) && tokfd >= 0) { |
|
2302 char b[20]; |
|
2303 |
|
2304 sprintf(b, "%d%s", tokfd, tokstrings[tok]); |
|
2305 addlinknode(list, dupstring(b)); |
|
2306 num++; |
|
2307 } else if (tok != NEWLIN) { |
|
2308 addlinknode(list, dupstring(tokstrings[tok])); |
|
2309 num++; |
|
2310 } |
|
2311 } |
|
2312 if (!got && !zleparse) { |
|
2313 got = 1; |
|
2314 cur = num - 1; |
|
2315 } |
|
2316 } while (tok != ENDINPUT && tok != LEXERR); |
|
2317 if (buf && tok == LEXERR && tokstr && *tokstr) { |
|
2318 int plen; |
|
2319 untokenize((p = dupstring(tokstr))); |
|
2320 plen = strlen(p); |
|
2321 /* |
|
2322 * Strip the space we added for lexing but which won't have |
|
2323 * been swallowed by the lexer because we aborted early. |
|
2324 * The test is paranoia. |
|
2325 */ |
|
2326 if (plen && p[plen-1] == ' ' && (plen == 1 || p[plen-2] != Meta)) |
|
2327 p[plen - 1] = '\0'; |
|
2328 addlinknode(list, p); |
|
2329 num++; |
|
2330 } |
|
2331 if (cur < 0 && num) |
|
2332 cur = num - 1; |
|
2333 noaliases = ona; |
|
2334 strinend(); |
|
2335 inpop(); |
|
2336 errflag = 0; |
|
2337 zleparse = ozp; |
|
2338 nocomments = onc; |
|
2339 noerrs = ne; |
|
2340 lexrestore(); |
|
2341 cs = ocs; |
|
2342 ll = oll; |
|
2343 wb = owb; |
|
2344 we = owe; |
|
2345 addedx = oadx; |
|
2346 |
|
2347 if (index) |
|
2348 *index = cur; |
|
2349 |
|
2350 return list; |
|
2351 } |
|
2352 |
|
2353 /* Move the current history list out of the way and prepare a fresh history |
|
2354 * list using hf for HISTFILE, hs for HISTSIZE, and shs for SAVEHIST. If |
|
2355 * the hf value is an empty string, HISTFILE will be unset from the new |
|
2356 * environment; if it is NULL, HISTFILE will not be changed, not even by the |
|
2357 * pop function (this functionality is used internally to rewrite the current |
|
2358 * history file without affecting pointers into the environment). |
|
2359 */ |
|
2360 |
|
2361 /**/ |
|
2362 int |
|
2363 pushhiststack(char *hf, zlong hs, zlong shs, int level) |
|
2364 { |
|
2365 struct histsave *h; |
|
2366 int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline; |
|
2367 |
|
2368 if (histsave_stack_pos == histsave_stack_size) { |
|
2369 histsave_stack_size += 5; |
|
2370 histsave_stack = zrealloc(histsave_stack, |
|
2371 histsave_stack_size * sizeof (struct histsave)); |
|
2372 } |
|
2373 |
|
2374 if (curline_in_ring) |
|
2375 unlinkcurline(); |
|
2376 |
|
2377 h = &histsave_stack[histsave_stack_pos++]; |
|
2378 |
|
2379 h->lasthist = lasthist; |
|
2380 if (hf) { |
|
2381 if ((h->histfile = getsparam("HISTFILE")) != NULL && *h->histfile) |
|
2382 h->histfile = ztrdup(h->histfile); |
|
2383 else |
|
2384 h->histfile = ""; |
|
2385 } else |
|
2386 h->histfile = NULL; |
|
2387 h->histtab = histtab; |
|
2388 h->hist_ring = hist_ring; |
|
2389 h->curhist = curhist; |
|
2390 h->histlinect = histlinect; |
|
2391 h->histsiz = histsiz; |
|
2392 h->savehistsiz = savehistsiz; |
|
2393 h->locallevel = level; |
|
2394 |
|
2395 memset(&lasthist, 0, sizeof lasthist); |
|
2396 if (hf) { |
|
2397 if (*hf) |
|
2398 setsparam("HISTFILE", ztrdup(hf)); |
|
2399 else |
|
2400 unsetparam("HISTFILE"); |
|
2401 } |
|
2402 hist_ring = NULL; |
|
2403 curhist = histlinect = 0; |
|
2404 histsiz = hs; |
|
2405 savehistsiz = shs; |
|
2406 inithist(); /* sets histtab */ |
|
2407 |
|
2408 if (curline_in_ring) |
|
2409 linkcurline(); |
|
2410 |
|
2411 return histsave_stack_pos; |
|
2412 } |
|
2413 |
|
2414 |
|
2415 /**/ |
|
2416 int |
|
2417 pophiststack(void) |
|
2418 { |
|
2419 struct histsave *h; |
|
2420 int curline_in_ring = (histactive & HA_ACTIVE) && hist_ring == &curline; |
|
2421 |
|
2422 if (histsave_stack_pos == 0) |
|
2423 return 0; |
|
2424 |
|
2425 if (curline_in_ring) |
|
2426 unlinkcurline(); |
|
2427 |
|
2428 deletehashtable(histtab); |
|
2429 zsfree(lasthist.text); |
|
2430 |
|
2431 h = &histsave_stack[--histsave_stack_pos]; |
|
2432 |
|
2433 lasthist = h->lasthist; |
|
2434 if (h->histfile) { |
|
2435 if (*h->histfile) |
|
2436 setsparam("HISTFILE", h->histfile); |
|
2437 else |
|
2438 unsetparam("HISTFILE"); |
|
2439 } |
|
2440 histtab = h->histtab; |
|
2441 hist_ring = h->hist_ring; |
|
2442 curhist = h->curhist; |
|
2443 histlinect = h->histlinect; |
|
2444 histsiz = h->histsiz; |
|
2445 savehistsiz = h->savehistsiz; |
|
2446 |
|
2447 if (curline_in_ring) |
|
2448 linkcurline(); |
|
2449 |
|
2450 return histsave_stack_pos + 1; |
|
2451 } |
|
2452 |
|
2453 /* If pop_through > 0, pop all array items >= the 1-relative index value. |
|
2454 * If pop_through <= 0, pop (-1)*pop_through levels off the stack. |
|
2455 * If the (new) top of stack is from a higher locallevel, auto-pop until |
|
2456 * it is not. |
|
2457 */ |
|
2458 |
|
2459 /**/ |
|
2460 int |
|
2461 saveandpophiststack(int pop_through, int writeflags) |
|
2462 { |
|
2463 if (pop_through <= 0) { |
|
2464 pop_through += histsave_stack_pos + 1; |
|
2465 if (pop_through <= 0) |
|
2466 pop_through = 1; |
|
2467 } |
|
2468 while (pop_through > 1 |
|
2469 && histsave_stack[pop_through-2].locallevel > locallevel) |
|
2470 pop_through--; |
|
2471 if (histsave_stack_pos < pop_through) |
|
2472 return 0; |
|
2473 do { |
|
2474 if (!nohistsave) |
|
2475 savehistfile(NULL, 1, writeflags); |
|
2476 pophiststack(); |
|
2477 } while (histsave_stack_pos >= pop_through); |
|
2478 return 1; |
|
2479 } |