|
|
1.1 root 1: /* History.c -- standalone history library */
2:
3: /* Copyright (C) 1989, 1991 Free Software Foundation, Inc.
4:
5: This file contains the GNU History Library (the Library), a set of
6: routines for managing the text of previously typed lines.
7:
8: The Library is free software; you can redistribute it and/or modify
9: it under the terms of the GNU General Public License as published by
10: the Free Software Foundation; either version 2 of the License, or
11: (at your option) any later version.
12:
13: The Library is distributed in the hope that it will be useful,
14: but WITHOUT ANY WARRANTY; without even the implied warranty of
15: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: GNU General Public License for more details.
17:
18: You should have received a copy of the GNU General Public License
19: along with this program; if not, write to the Free Software
20: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
21:
22: /* The goal is to make the implementation transparent, so that you
23: don't have to know what data types are used, just what functions
24: you can call. I think I have done that. */
25:
26: /* Remove these declarations when we have a complete libgnu.a. */
27: #ifndef xmalloc
28: #if !defined (STATIC_MALLOC)
29: extern char *xmalloc (), *xrealloc ();
30: #else
31: static char *xmalloc (), *xrealloc ();
32: #endif
33: #endif /* !xmalloc */
34:
35: #include "sysdep.h"
36: #include <stdio.h>
37: #include <errno.h>
38: #include <sys/types.h>
39: #ifndef NO_SYS_FILE
40: #include <sys/file.h>
41: #endif
42: #include <sys/stat.h>
43: #include <fcntl.h>
44:
45: #include "history.h"
46:
47: #ifndef savestring
48: #define savestring(x) (char *)strcpy ((char *)xmalloc (1 + strlen (x)), (x))
49: #endif
50:
51: #ifndef whitespace
52: #define whitespace(c) (((c) == ' ') || ((c) == '\t'))
53: #endif
54:
55: #ifndef digit
56: #define digit(c) ((c) >= '0' && (c) <= '9')
57: #endif
58:
59: #ifndef member
60: #define member(c, s) ((c) ? index ((s), (c)) : 0)
61: #endif
62:
63: /* **************************************************************** */
64: /* */
65: /* History Functions */
66: /* */
67: /* **************************************************************** */
68:
69: /* An array of HIST_ENTRY. This is where we store the history. */
70: static HIST_ENTRY **the_history = (HIST_ENTRY **)NULL;
71:
72: /* Non-zero means that we have enforced a limit on the amount of
73: history that we save. */
74: int history_stifled = 0;
75:
76: /* If HISTORY_STIFLED is non-zero, then this is the maximum number of
77: entries to remember. */
78: int max_input_history;
79:
80: /* The current location of the interactive history pointer. Just makes
81: life easier for outside callers. */
82: static int history_offset = 0;
83:
84: /* The number of strings currently stored in the input_history list. */
85: int history_length = 0;
86:
87: /* The current number of slots allocated to the input_history. */
88: static int history_size = 0;
89:
90: /* The number of slots to increase the_history by. */
91: #define DEFAULT_HISTORY_GROW_SIZE 50
92:
93: /* The character that represents the start of a history expansion
94: request. This is usually `!'. */
95: char history_expansion_char = '!';
96:
97: /* The character that invokes word substitution if found at the start of
98: a line. This is usually `^'. */
99: char history_subst_char = '^';
100:
101: /* During tokenization, if this character is seen as the first character
102: of a word, then it, and all subsequent characters upto a newline are
103: ignored. For a Bourne shell, this should be '#'. Bash special cases
104: the interactive comment character to not be a comment delimiter. */
105: char history_comment_char = '\0';
106:
107: /* The list of characters which inhibit the expansion of text if found
108: immediately following history_expansion_char. */
109: char *history_no_expand_chars = " \t\n\r=";
110:
111: /* The logical `base' of the history array. It defaults to 1. */
112: int history_base = 1;
113:
114: /* Begin a session in which the history functions might be used. This
115: initializes interactive variables. */
116: void
117: using_history ()
118: {
119: history_offset = history_length;
120: }
121:
122: /* Return the number of bytes that the primary history entries are using.
123: This just adds up the lengths of the_history->lines. */
124: int
125: history_total_bytes ()
126: {
127: register int i, result;
128:
129: result = 0;
130:
131: for (i = 0; the_history && the_history[i]; i++)
132: result += strlen (the_history[i]->line);
133:
134: return (result);
135: }
136:
137: /* Place STRING at the end of the history list. The data field
138: is set to NULL. */
139: void
140: add_history (string)
141: char *string;
142: {
143: HIST_ENTRY *temp;
144:
145: if (history_stifled && (history_length == max_input_history))
146: {
147: register int i;
148:
149: /* If the history is stifled, and history_length is zero,
150: and it equals max_input_history, we don't save items. */
151: if (!history_length)
152: return;
153:
154: /* If there is something in the slot, then remove it. */
155: if (the_history[0])
156: {
157: free (the_history[0]->line);
158: free (the_history[0]);
159: }
160:
161: for (i = 0; i < history_length; i++)
162: the_history[i] = the_history[i + 1];
163:
164: history_base++;
165:
166: }
167: else
168: {
169: if (!history_size)
170: {
171: the_history = (HIST_ENTRY **)
172: xmalloc ((history_size = DEFAULT_HISTORY_GROW_SIZE)
173: * sizeof (HIST_ENTRY *));
174: history_length = 1;
175:
176: }
177: else
178: {
179: if (history_length == (history_size - 1))
180: {
181: the_history = (HIST_ENTRY **)
182: xrealloc (the_history,
183: ((history_size += DEFAULT_HISTORY_GROW_SIZE)
184: * sizeof (HIST_ENTRY *)));
185: }
186: history_length++;
187: }
188: }
189:
190: temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
191: temp->line = savestring (string);
192: temp->data = (char *)NULL;
193:
194: the_history[history_length] = (HIST_ENTRY *)NULL;
195: the_history[history_length - 1] = temp;
196: }
197:
198: /* Make the history entry at WHICH have LINE and DATA. This returns
199: the old entry so you can dispose of the data. In the case of an
200: invalid WHICH, a NULL pointer is returned. */
201: HIST_ENTRY *
202: replace_history_entry (which, line, data)
203: int which;
204: char *line;
205: char *data;
206: {
207: HIST_ENTRY *temp = (HIST_ENTRY *)xmalloc (sizeof (HIST_ENTRY));
208: HIST_ENTRY *old_value;
209:
210: if (which >= history_length)
211: return ((HIST_ENTRY *)NULL);
212:
213: old_value = the_history[which];
214:
215: temp->line = savestring (line);
216: temp->data = data;
217: the_history[which] = temp;
218:
219: return (old_value);
220: }
221:
222: /* Returns the magic number which says what history element we are
223: looking at now. In this implementation, it returns history_offset. */
224: int
225: where_history ()
226: {
227: return (history_offset);
228: }
229:
230: /* Search the history for STRING, starting at history_offset.
231: If DIRECTION < 0, then the search is through previous entries, else
232: through subsequent. If ANCHORED is non-zero, the string must
233: appear at the beginning of a history line, otherwise, the string
234: may appear anywhere in the line. If the string is found, then
235: current_history () is the history entry, and the value of this
236: function is the offset in the line of that history entry that the
237: string was found in. Otherwise, nothing is changed, and a -1 is
238: returned. */
239:
240: #define ANCHORED_SEARCH 1
241: #define NON_ANCHORED_SEARCH 0
242:
243: static int
244: history_search_internal (string, direction, anchored)
245: char *string;
246: int direction, anchored;
247: {
248: register int i = history_offset;
249: register int reverse = (direction < 0);
250: register char *line;
251: register int index;
252: int string_len = strlen (string);
253:
254: /* Take care of trivial cases first. */
255:
256: if (!history_length || ((i == history_length) && !reverse))
257: return (-1);
258:
259: if (reverse && (i == history_length))
260: i--;
261:
262: while (1)
263: {
264: /* Search each line in the history list for STRING. */
265:
266: /* At limit for direction? */
267: if ((reverse && i < 0) ||
268: (!reverse && i == history_length))
269: return (-1);
270:
271: line = the_history[i]->line;
272: index = strlen (line);
273:
274: /* If STRING is longer than line, no match. */
275: if (string_len > index)
276: goto next_line;
277:
278: /* Handle anchored searches first. */
279: if (anchored == ANCHORED_SEARCH)
280: {
281: if (strncmp (string, line, string_len) == 0)
282: {
283: history_offset = i;
284: return (0);
285: }
286:
287: goto next_line;
288: }
289:
290: /* Do substring search. */
291: if (reverse)
292: {
293: index -= string_len;
294:
295: while (index >= 0)
296: {
297: if (strncmp (string, line + index, string_len) == 0)
298: {
299: history_offset = i;
300: return (index);
301: }
302: index--;
303: }
304: }
305: else
306: {
307: register int limit = index - string_len + 1;
308: index = 0;
309:
310: while (index < limit)
311: {
312: if (strncmp (string, line + index, string_len) == 0)
313: {
314: history_offset = i;
315: return (index);
316: }
317: index++;
318: }
319: }
320: next_line:
321: if (reverse)
322: i--;
323: else
324: i++;
325: }
326: }
327:
328: /* Do a non-anchored search for STRING through the history in DIRECTION. */
329: int
330: history_search (string, direction)
331: char *string;
332: int direction;
333: {
334: return (history_search_internal (string, direction, NON_ANCHORED_SEARCH));
335: }
336:
337: /* Do an anchored search for string through the history in DIRECTION. */
338: int
339: history_search_prefix (string, direction)
340: char *string;
341: int direction;
342: {
343: return (history_search_internal (string, direction, ANCHORED_SEARCH));
344: }
345:
346: /* Remove history element WHICH from the history. The removed
347: element is returned to you so you can free the line, data,
348: and containing structure. */
349: HIST_ENTRY *
350: remove_history (which)
351: int which;
352: {
353: HIST_ENTRY *return_value;
354:
355: if (which >= history_length || !history_length)
356: return_value = (HIST_ENTRY *)NULL;
357: else
358: {
359: register int i;
360: return_value = the_history[which];
361:
362: for (i = which; i < history_length; i++)
363: the_history[i] = the_history[i + 1];
364:
365: history_length--;
366: }
367:
368: return (return_value);
369: }
370:
371: /* Stifle the history list, remembering only MAX number of lines. */
372: void
373: stifle_history (max)
374: int max;
375: {
376: if (max < 0)
377: max = 0;
378: if (history_length > max)
379: {
380: register int i, j;
381:
382: /* This loses because we cannot free the data. */
383: for (i = 0; i < (history_length - max); i++)
384: {
385: free (the_history[i]->line);
386: free (the_history[i]);
387: }
388: history_base = i;
389: for (j = 0, i = history_length - max; j < max; i++, j++)
390: the_history[j] = the_history[i];
391: the_history[j] = (HIST_ENTRY *)NULL;
392: history_length = j;
393: }
394: history_stifled = 1;
395: max_input_history = max;
396: }
397:
398: /* Stop stifling the history. This returns the previous amount the history
399: was stifled by. The value is positive if the history was stifled, negative
400: if it wasn't. */
401: int
402: unstifle_history ()
403: {
404: int result = max_input_history;
405: if (history_stifled)
406: {
407: result = - result;
408: history_stifled = 0;
409: }
410: return (result);
411: }
412:
413: /* Return the string that should be used in the place of this
414: filename. This only matters when you don't specify the
415: filename to read_history (), or write_history (). */
416: static char *
417: history_filename (filename)
418: char *filename;
419: {
420: char *return_val = filename ? savestring (filename) : (char *)NULL;
421:
422: if (!return_val)
423: {
424: char *home = (char *)getenv ("HOME");
425: if (!home) home = ".";
426: return_val = (char *)xmalloc (2 + strlen (home) + strlen (".history"));
427: sprintf (return_val, "%s/.history", home);
428: }
429: return (return_val);
430: }
431:
432: /* Add the contents of FILENAME to the history list, a line at a time.
433: If FILENAME is NULL, then read from ~/.history. Returns 0 if
434: successful, or errno if not. */
435: int
436: read_history (filename)
437: char *filename;
438: {
439: return (read_history_range (filename, 0, -1));
440: }
441:
442: /* Read a range of lines from FILENAME, adding them to the history list.
443: Start reading at the FROM'th line and end at the TO'th. If FROM
444: is zero, start at the beginning. If TO is less than FROM, read
445: until the end of the file. If FILENAME is NULL, then read from
446: ~/.history. Returns 0 if successful, or errno if not. */
447: int
448: read_history_range (filename, from, to)
449: char *filename;
450: int from, to;
451: {
452: register int line_start, line_end;
453: char *input, *buffer = (char *)NULL;
454: int file, current_line;
455: struct stat finfo;
456: extern int errno;
457:
458: input = history_filename (filename);
459: file = open (input, O_RDONLY, 0666);
460:
461: if ((file < 0) ||
462: (stat (input, &finfo) == -1))
463: goto error_and_exit;
464:
465: buffer = (char *)xmalloc (finfo.st_size + 1);
466:
467: if (read (file, buffer, finfo.st_size) != finfo.st_size)
468: error_and_exit:
469: {
470: if (file >= 0)
471: close (file);
472:
473: if (buffer)
474: free (buffer);
475:
476: return (errno);
477: }
478:
479: close (file);
480:
481: /* Set TO to larger than end of file if negative. */
482: if (to < 0)
483: to = finfo.st_size;
484:
485: /* Start at beginning of file, work to end. */
486: line_start = line_end = current_line = 0;
487:
488: /* Skip lines until we are at FROM. */
489: while (line_start < finfo.st_size && current_line < from)
490: {
491: for (line_end = line_start; line_end < finfo.st_size; line_end++)
492: if (buffer[line_end] == '\n')
493: {
494: current_line++;
495: line_start = line_end + 1;
496: if (current_line == from)
497: break;
498: }
499: }
500:
501: /* If there are lines left to gobble, then gobble them now. */
502: for (line_end = line_start; line_end < finfo.st_size; line_end++)
503: if (buffer[line_end] == '\n')
504: {
505: buffer[line_end] = '\0';
506:
507: if (buffer[line_start])
508: add_history (buffer + line_start);
509:
510: current_line++;
511:
512: if (current_line >= to)
513: break;
514:
515: line_start = line_end + 1;
516: }
517: return (0);
518: }
519:
520: /* Truncate the history file FNAME, leaving only LINES trailing lines.
521: If FNAME is NULL, then use ~/.history. */
522: history_truncate_file (fname, lines)
523: char *fname;
524: register int lines;
525: {
526: register int i;
527: int file;
528: char *buffer = (char *)NULL, *filename;
529: struct stat finfo;
530:
531: filename = history_filename (fname);
532: if (stat (filename, &finfo) == -1)
533: goto truncate_exit;
534:
535: file = open (filename, O_RDONLY, 0666);
536:
537: if (file == -1)
538: goto truncate_exit;
539:
540: buffer = (char *)xmalloc (finfo.st_size + 1);
541: read (file, buffer, finfo.st_size);
542: close (file);
543:
544: /* Count backwards from the end of buffer until we have passed
545: LINES lines. */
546: for (i = finfo.st_size; lines && i; i--)
547: {
548: if (buffer[i] == '\n')
549: lines--;
550: }
551:
552: /* If there are fewer lines in the file than we want to truncate to,
553: then we are all done. */
554: if (!i)
555: goto truncate_exit;
556:
557: /* Otherwise, write from the start of this line until the end of the
558: buffer. */
559: for (--i; i; i--)
560: if (buffer[i] == '\n')
561: {
562: i++;
563: break;
564: }
565:
566: file = open (filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
567: if (file == -1)
568: goto truncate_exit;
569:
570: write (file, buffer + i, finfo.st_size - i);
571: close (file);
572:
573: truncate_exit:
574: if (buffer)
575: free (buffer);
576:
577: free (filename);
578: }
579:
580: #define HISTORY_APPEND 0
581: #define HISTORY_OVERWRITE 1
582:
583: /* Workhorse function for writing history. Writes NELEMENT entries
584: from the history list to FILENAME. OVERWRITE is non-zero if you
585: wish to replace FILENAME with the entries. */
586: static int
587: history_do_write (filename, nelements, overwrite)
588: char *filename;
589: int nelements, overwrite;
590: {
591: extern int errno;
592: register int i, j;
593: char *output = history_filename (filename);
594: int file, mode;
595:
596: if (overwrite)
597: mode = O_WRONLY | O_CREAT | O_TRUNC;
598: else
599: mode = O_WRONLY | O_APPEND;
600:
601: if ((file = open (output, mode, 0666)) == -1)
602: return (errno);
603:
604: if (nelements > history_length)
605: nelements = history_length;
606:
607: /* Build a buffer of all the lines to write, and write them in one syscall.
608: Suggested by Peter Ho ([email protected]). */
609: {
610: register int j = 0;
611: int buffer_size = 0;
612: char *buffer;
613:
614: /* Calculate the total number of bytes to write. */
615: for (i = history_length - nelements; i < history_length; i++)
616: buffer_size += 1 + strlen (the_history[i]->line);
617:
618: /* Allocate the buffer, and fill it. */
619: buffer = (char *)xmalloc (buffer_size);
620:
621: for (i = history_length - nelements; i < history_length; i++)
622: {
623: strcpy (buffer + j, the_history[i]->line);
624: j += strlen (the_history[i]->line);
625: buffer[j++] = '\n';
626: }
627:
628: write (file, buffer, buffer_size);
629: free (buffer);
630: }
631:
632: close (file);
633: return (0);
634: }
635:
636: /* Append NELEMENT entries to FILENAME. The entries appended are from
637: the end of the list minus NELEMENTs up to the end of the list. */
638: int
639: append_history (nelements, filename)
640: int nelements;
641: char *filename;
642: {
643: return (history_do_write (filename, nelements, HISTORY_APPEND));
644: }
645:
646: /* Overwrite FILENAME with the current history. If FILENAME is NULL,
647: then write the history list to ~/.history. Values returned
648: are as in read_history ().*/
649: int
650: write_history (filename)
651: char *filename;
652: {
653: return (history_do_write (filename, history_length, HISTORY_OVERWRITE));
654: }
655:
656: /* Return the history entry at the current position, as determined by
657: history_offset. If there is no entry there, return a NULL pointer. */
658: HIST_ENTRY *
659: current_history ()
660: {
661: if ((history_offset == history_length) || !the_history)
662: return ((HIST_ENTRY *)NULL);
663: else
664: return (the_history[history_offset]);
665: }
666:
667: /* Back up history_offset to the previous history entry, and return
668: a pointer to that entry. If there is no previous entry then return
669: a NULL pointer. */
670: HIST_ENTRY *
671: previous_history ()
672: {
673: if (!history_offset)
674: return ((HIST_ENTRY *)NULL);
675: else
676: return (the_history[--history_offset]);
677: }
678:
679: /* Move history_offset forward to the next history entry, and return
680: a pointer to that entry. If there is no next entry then return a
681: NULL pointer. */
682: HIST_ENTRY *
683: next_history ()
684: {
685: if (history_offset == history_length)
686: return ((HIST_ENTRY *)NULL);
687: else
688: return (the_history[++history_offset]);
689: }
690:
691: /* Return the current history array. The caller has to be carefull, since this
692: is the actual array of data, and could be bashed or made corrupt easily.
693: The array is terminated with a NULL pointer. */
694: HIST_ENTRY **
695: history_list ()
696: {
697: return (the_history);
698: }
699:
700: /* Return the history entry which is logically at OFFSET in the history array.
701: OFFSET is relative to history_base. */
702: HIST_ENTRY *
703: history_get (offset)
704: int offset;
705: {
706: int index = offset - history_base;
707:
708: if (index >= history_length ||
709: index < 0 ||
710: !the_history)
711: return ((HIST_ENTRY *)NULL);
712: return (the_history[index]);
713: }
714:
715: /* Search for STRING in the history list. DIR is < 0 for searching
716: backwards. POS is an absolute index into the history list at
717: which point to begin searching. */
718: int
719: history_search_pos (string, dir, pos)
720: char *string;
721: int dir, pos;
722: {
723: int ret, old = where_history ();
724: history_set_pos (pos);
725: if (history_search (string, dir) == -1)
726: {
727: history_set_pos (old);
728: return (-1);
729: }
730: ret = where_history ();
731: history_set_pos (old);
732: return ret;
733: }
734:
735: /* Make the current history item be the one at POS, an absolute index.
736: Returns zero if POS is out of range, else non-zero. */
737: int
738: history_set_pos (pos)
739: int pos;
740: {
741: if (pos > history_length || pos < 0 || !the_history)
742: return (0);
743: history_offset = pos;
744: return (1);
745: }
746:
747:
748: /* **************************************************************** */
749: /* */
750: /* History Expansion */
751: /* */
752: /* **************************************************************** */
753:
754: /* Hairy history expansion on text, not tokens. This is of general
755: use, and thus belongs in this library. */
756:
757: /* The last string searched for in a !?string? search. */
758: static char *search_string = (char *)NULL;
759:
760: /* Return the event specified at TEXT + OFFSET modifying OFFSET to
761: point to after the event specifier. Just a pointer to the history
762: line is returned; NULL is returned in the event of a bad specifier.
763: You pass STRING with *INDEX equal to the history_expansion_char that
764: begins this specification.
765: DELIMITING_QUOTE is a character that is allowed to end the string
766: specification for what to search for in addition to the normal
767: characters `:', ` ', `\t', `\n', and sometimes `?'.
768: So you might call this function like:
769: line = get_history_event ("!echo:p", &index, 0); */
770: char *
771: get_history_event (string, caller_index, delimiting_quote)
772: char *string;
773: int *caller_index;
774: int delimiting_quote;
775: {
776: register int i = *caller_index;
777: int which, sign = 1;
778: HIST_ENTRY *entry;
779:
780: /* The event can be specified in a number of ways.
781:
782: !! the previous command
783: !n command line N
784: !-n current command-line minus N
785: !str the most recent command starting with STR
786: !?str[?]
787: the most recent command containing STR
788:
789: All values N are determined via HISTORY_BASE. */
790:
791: if (string[i] != history_expansion_char)
792: return ((char *)NULL);
793:
794: /* Move on to the specification. */
795: i++;
796:
797: /* Handle !! case. */
798: if (string[i] == history_expansion_char)
799: {
800: i++;
801: which = history_base + (history_length - 1);
802: *caller_index = i;
803: goto get_which;
804: }
805:
806: /* Hack case of numeric line specification. */
807: read_which:
808: if (string[i] == '-')
809: {
810: sign = -1;
811: i++;
812: }
813:
814: if (digit (string[i]))
815: {
816: int start = i;
817:
818: /* Get the extent of the digits. */
819: for (; digit (string[i]); i++);
820:
821: /* Get the digit value. */
822: sscanf (string + start, "%d", &which);
823:
824: *caller_index = i;
825:
826: if (sign < 0)
827: which = (history_length + history_base) - which;
828:
829: get_which:
830: if (entry = history_get (which))
831: return (entry->line);
832:
833: return ((char *)NULL);
834: }
835:
836: /* This must be something to search for. If the spec begins with
837: a '?', then the string may be anywhere on the line. Otherwise,
838: the string must be found at the start of a line. */
839: {
840: int index;
841: char *temp;
842: int substring_okay = 0;
843:
844: if (string[i] == '?')
845: {
846: substring_okay++;
847: i++;
848: }
849:
850: for (index = i; string[i]; i++)
851: if (whitespace (string[i]) ||
852: string[i] == '\n' ||
853: string[i] == ':' ||
854: (substring_okay && string[i] == '?') ||
855: string[i] == delimiting_quote)
856: break;
857:
858: temp = (char *)alloca (1 + (i - index));
859: strncpy (temp, &string[index], (i - index));
860: temp[i - index] = '\0';
861:
862: if (string[i] == '?')
863: i++;
864:
865: *caller_index = i;
866:
867: search_again:
868:
869: index = history_search_internal
870: (temp, -1, substring_okay ? NON_ANCHORED_SEARCH : ANCHORED_SEARCH);
871:
872: if (index < 0)
873: search_lost:
874: {
875: history_offset = history_length;
876: return ((char *)NULL);
877: }
878:
879: if (index == 0)
880: {
881: search_won:
882: entry = current_history ();
883: history_offset = history_length;
884:
885: /* If this was a substring search, then remember the string that
886: we matched for word substitution. */
887: if (substring_okay)
888: {
889: if (search_string)
890: free (search_string);
891: search_string = savestring (temp);
892: }
893:
894: return (entry->line);
895: }
896:
897: if (history_offset)
898: history_offset--;
899: else
900: goto search_lost;
901:
902: goto search_again;
903: }
904: }
905:
906: /* Expand the string STRING, placing the result into OUTPUT, a pointer
907: to a string. Returns:
908:
909: 0) If no expansions took place (or, if the only change in
910: the text was the de-slashifying of the history expansion
911: character)
912: 1) If expansions did take place
913: -1) If there was an error in expansion.
914:
915: If an error ocurred in expansion, then OUTPUT contains a descriptive
916: error message. */
917: int
918: history_expand (string, output)
919: char *string;
920: char **output;
921: {
922: register int j, l = strlen (string);
923: int i, word_spec_error = 0;
924: int cc, modified = 0;
925: char *word_spec, *event;
926: int starting_index, only_printing = 0, substitute_globally = 0;
927:
928: char *get_history_word_specifier (), *rindex ();
929:
930: /* The output string, and its length. */
931: int len = 0;
932: char *result = (char *)NULL;
933:
934: /* Used in add_string; */
935: char *temp, tt[2], tbl[3];
936:
937: /* Prepare the buffer for printing error messages. */
938: result = (char *)xmalloc (len = 255);
939:
940: result[0] = tt[1] = tbl[2] = '\0';
941: tbl[0] = '\\';
942: tbl[1] = history_expansion_char;
943:
944: /* Grovel the string. Only backslash can quote the history escape
945: character. We also handle arg specifiers. */
946:
947: /* Before we grovel forever, see if the history_expansion_char appears
948: anywhere within the text. */
949:
950: /* The quick substitution character is a history expansion all right. That
951: is to say, "^this^that^" is equivalent to "!!:s^this^that^", and in fact,
952: that is the substitution that we do. */
953: if (string[0] == history_subst_char)
954: {
955: char *format_string = (char *)alloca (10 + strlen (string));
956:
957: sprintf (format_string, "%c%c:s%s",
958: history_expansion_char, history_expansion_char,
959: string);
960: string = format_string;
961: l += 4;
962: goto grovel;
963: }
964:
965: /* If not quick substitution, still maybe have to do expansion. */
966:
967: /* `!' followed by one of the characters in history_no_expand_chars
968: is NOT an expansion. */
969: for (i = 0; string[i]; i++)
970: if (string[i] == history_expansion_char)
971: if (!string[i + 1] || member (string[i + 1], history_no_expand_chars))
972: continue;
973: else
974: goto grovel;
975:
976: free (result);
977: *output = savestring (string);
978: return (0);
979:
980: grovel:
981:
982: for (i = j = 0; i < l; i++)
983: {
984: int tchar = string[i];
985: if (tchar == history_expansion_char)
986: tchar = -3;
987:
988: switch (tchar)
989: {
990: case '\\':
991: if (string[i + 1] == history_expansion_char)
992: {
993: i++;
994: temp = tbl;
995: goto do_add;
996: }
997: else
998: goto add_char;
999:
1000: /* case history_expansion_char: */
1001: case -3:
1002: starting_index = i + 1;
1003: cc = string[i + 1];
1004:
1005: /* If the history_expansion_char is followed by one of the
1006: characters in history_no_expand_chars, then it is not a
1007: candidate for expansion of any kind. */
1008: if (member (cc, history_no_expand_chars))
1009: goto add_char;
1010:
1011: /* There is something that is listed as a `word specifier' in csh
1012: documentation which means `the expanded text to this point'.
1013: That is not a word specifier, it is an event specifier. */
1014:
1015: if (cc == '#')
1016: goto hack_pound_sign;
1017:
1018: /* If it is followed by something that starts a word specifier,
1019: then !! is implied as the event specifier. */
1020:
1021: if (member (cc, ":$*%^"))
1022: {
1023: char fake_s[3];
1024: int fake_i = 0;
1025: i++;
1026: fake_s[0] = fake_s[1] = history_expansion_char;
1027: fake_s[2] = '\0';
1028: event = get_history_event (fake_s, &fake_i, 0);
1029: }
1030: else
1031: {
1032: int quoted_search_delimiter = 0;
1033:
1034: /* If the character before this `!' is a double or single
1035: quote, then this expansion takes place inside of the
1036: quoted string. If we have to search for some text ("!foo"),
1037: allow the delimiter to end the search string. */
1038: if (i && (string[i - 1] == '\'' || string[i - 1] == '"'))
1039: quoted_search_delimiter = string[i - 1];
1040:
1041: event = get_history_event (string, &i, quoted_search_delimiter);
1042: }
1043:
1044: if (!event)
1045: event_not_found:
1046: {
1047: int l = 1 + (i - starting_index);
1048:
1049: temp = (char *)alloca (1 + l);
1050: strncpy (temp, string + starting_index, l);
1051: temp[l - 1] = 0;
1052: sprintf (result, "%s: %s.", temp,
1053: word_spec_error ? "Bad word specifier" : "Event not found");
1054: error_exit:
1055: *output = result;
1056: return (-1);
1057: }
1058:
1059: /* If a word specifier is found, then do what that requires. */
1060: starting_index = i;
1061:
1062: word_spec = get_history_word_specifier (string, event, &i);
1063:
1064: /* There is no such thing as a `malformed word specifier'. However,
1065: it is possible for a specifier that has no match. In that case,
1066: we complain. */
1067: if (word_spec == (char *)-1)
1068: bad_word_spec:
1069: {
1070: word_spec_error++;
1071: goto event_not_found;
1072: }
1073:
1074: /* If no word specifier, than the thing of interest was the event. */
1075: if (!word_spec)
1076: temp = event;
1077: else
1078: {
1079: temp = (char *)alloca (1 + strlen (word_spec));
1080: strcpy (temp, word_spec);
1081: free (word_spec);
1082: }
1083:
1084: /* Perhaps there are other modifiers involved. Do what they say. */
1085:
1086: hack_specials:
1087:
1088: if (string[i] == ':')
1089: {
1090: char *tstr;
1091:
1092: switch (string[i + 1])
1093: {
1094: /* :p means make this the last executed line. So we
1095: return an error state after adding this line to the
1096: history. */
1097: case 'p':
1098: only_printing++;
1099: goto next_special;
1100:
1101: /* :t discards all but the last part of the pathname. */
1102: case 't':
1103: tstr = rindex (temp, '/');
1104: if (tstr)
1105: temp = ++tstr;
1106: goto next_special;
1107:
1108: /* :h discards the last part of a pathname. */
1109: case 'h':
1110: tstr = rindex (temp, '/');
1111: if (tstr)
1112: *tstr = '\0';
1113: goto next_special;
1114:
1115: /* :r discards the suffix. */
1116: case 'r':
1117: tstr = rindex (temp, '.');
1118: if (tstr)
1119: *tstr = '\0';
1120: goto next_special;
1121:
1122: /* :e discards everything but the suffix. */
1123: case 'e':
1124: tstr = rindex (temp, '.');
1125: if (tstr)
1126: temp = tstr;
1127: goto next_special;
1128:
1129: /* :s/this/that substitutes `this' for `that'. */
1130: /* :gs/this/that substitutes `this' for `that' globally. */
1131: case 'g':
1132: if (string[i + 2] == 's')
1133: {
1134: i++;
1135: substitute_globally = 1;
1136: goto substitute;
1137: }
1138: else
1139:
1140: case 's':
1141: substitute:
1142: {
1143: char *this, *that, *new_event;
1144: int delimiter = 0;
1145: int si, l_this, l_that, l_temp = strlen (temp);
1146:
1147: if (i + 2 < strlen (string))
1148: delimiter = string[i + 2];
1149:
1150: if (!delimiter)
1151: break;
1152:
1153: i += 3;
1154:
1155: /* Get THIS. */
1156: for (si = i; string[si] && string[si] != delimiter; si++);
1157: l_this = (si - i);
1158: this = (char *)alloca (1 + l_this);
1159: strncpy (this, string + i, l_this);
1160: this[l_this] = '\0';
1161:
1162: i = si;
1163: if (string[si])
1164: i++;
1165:
1166: /* Get THAT. */
1167: for (si = i; string[si] && string[si] != delimiter; si++);
1168: l_that = (si - i);
1169: that = (char *)alloca (1 + l_that);
1170: strncpy (that, string + i, l_that);
1171: that[l_that] = '\0';
1172:
1173: i = si;
1174: if (string[si]) i++;
1175:
1176: /* Ignore impossible cases. */
1177: if (l_this > l_temp)
1178: goto cant_substitute;
1179:
1180: /* Find the first occurrence of THIS in TEMP. */
1181: si = 0;
1182: for (; (si + l_this) <= l_temp; si++)
1183: if (strncmp (temp + si, this, l_this) == 0)
1184: {
1185: new_event =
1186: (char *)alloca (1 + (l_that - l_this) + l_temp);
1187: strncpy (new_event, temp, si);
1188: strncpy (new_event + si, that, l_that);
1189: strncpy (new_event + si + l_that,
1190: temp + si + l_this,
1191: l_temp - (si + l_this));
1192: new_event[(l_that - l_this) + l_temp] = '\0';
1193: temp = new_event;
1194:
1195: if (substitute_globally)
1196: {
1197: si += l_that;
1198: l_temp = strlen (temp);
1199: substitute_globally++;
1200: continue;
1201: }
1202:
1203: goto hack_specials;
1204: }
1205:
1206: cant_substitute:
1207:
1208: if (substitute_globally > 1)
1209: {
1210: substitute_globally = 0;
1211: goto hack_specials;
1212: }
1213:
1214: goto event_not_found;
1215: }
1216:
1217: /* :# is the line so far. Note that we have to
1218: alloca () it since RESULT could be realloc ()'ed
1219: below in add_string. */
1220: case '#':
1221: hack_pound_sign:
1222: if (result)
1223: {
1224: temp = (char *)alloca (1 + strlen (result));
1225: strcpy (temp, result);
1226: }
1227: else
1228: temp = "";
1229:
1230: next_special:
1231: i += 2;
1232: goto hack_specials;
1233: }
1234:
1235: }
1236: /* Believe it or not, we have to back the pointer up by one. */
1237: --i;
1238: goto add_string;
1239:
1240: /* A regular character. Just add it to the output string. */
1241: default:
1242: add_char:
1243: tt[0] = string[i];
1244: temp = tt;
1245: goto do_add;
1246:
1247: add_string:
1248: modified++;
1249:
1250: do_add:
1251: j += strlen (temp);
1252: while (j > len)
1253: result = (char *)xrealloc (result, (len += 255));
1254:
1255: strcpy (result + (j - strlen (temp)), temp);
1256: }
1257: }
1258:
1259: *output = result;
1260:
1261: if (only_printing)
1262: {
1263: add_history (result);
1264: return (-1);
1265: }
1266:
1267: return (modified != 0);
1268: }
1269:
1270: /* Return a consed string which is the word specified in SPEC, and found
1271: in FROM. NULL is returned if there is no spec. -1 is returned if
1272: the word specified cannot be found. CALLER_INDEX is the offset in
1273: SPEC to start looking; it is updated to point to just after the last
1274: character parsed. */
1275: char *
1276: get_history_word_specifier (spec, from, caller_index)
1277: char *spec, *from;
1278: int *caller_index;
1279: {
1280: register int i = *caller_index;
1281: int first, last;
1282: int expecting_word_spec = 0;
1283: char *history_arg_extract ();
1284:
1285: /* The range of words to return doesn't exist yet. */
1286: first = last = 0;
1287:
1288: /* If we found a colon, then this *must* be a word specification. If
1289: it isn't, then it is an error. */
1290: if (spec[i] == ':')
1291: i++, expecting_word_spec++;
1292:
1293: /* Handle special cases first. */
1294:
1295: /* `%' is the word last searched for. */
1296: if (spec[i] == '%')
1297: {
1298: *caller_index = i + 1;
1299: if (search_string)
1300: return (savestring (search_string));
1301: else
1302: return (savestring (""));
1303: }
1304:
1305: /* `*' matches all of the arguments, but not the command. */
1306: if (spec[i] == '*')
1307: {
1308: char *star_result;
1309:
1310: *caller_index = i + 1;
1311: star_result = history_arg_extract (1, '$', from);
1312:
1313: if (!star_result)
1314: star_result = savestring ("");
1315:
1316: return (star_result);
1317: }
1318:
1319: /* `$' is last arg. */
1320: if (spec[i] == '$')
1321: {
1322: *caller_index = i + 1;
1323: return (history_arg_extract ('$', '$', from));
1324: }
1325:
1326: /* Try to get FIRST and LAST figured out. */
1327: if (spec[i] == '-' || spec[i] == '^')
1328: {
1329: first = 1;
1330: goto get_last;
1331: }
1332:
1333: get_first:
1334: if (digit (spec[i]) && expecting_word_spec)
1335: {
1336: sscanf (spec + i, "%d", &first);
1337: for (; digit (spec[i]); i++);
1338: }
1339: else
1340: return ((char *)NULL);
1341:
1342: get_last:
1343: if (spec[i] == '^')
1344: {
1345: i++;
1346: last = 1;
1347: goto get_args;
1348: }
1349:
1350: if (spec[i] != '-')
1351: {
1352: last = first;
1353: goto get_args;
1354: }
1355:
1356: i++;
1357:
1358: if (digit (spec[i]))
1359: {
1360: sscanf (spec + i, "%d", &last);
1361: for (; digit (spec[i]); i++);
1362: }
1363: else
1364: if (spec[i] == '$')
1365: {
1366: i++;
1367: last = '$';
1368: }
1369:
1370: get_args:
1371: {
1372: char *result = (char *)NULL;
1373:
1374: *caller_index = i;
1375:
1376: if (last >= first)
1377: result = history_arg_extract (first, last, from);
1378:
1379: if (result)
1380: return (result);
1381: else
1382: return ((char *)-1);
1383: }
1384: }
1385:
1386: /* Extract the args specified, starting at FIRST, and ending at LAST.
1387: The args are taken from STRING. If either FIRST or LAST is < 0,
1388: then make that arg count from the right (subtract from the number of
1389: tokens, so that FIRST = -1 means the next to last token on the line). */
1390: char *
1391: history_arg_extract (first, last, string)
1392: int first, last;
1393: char *string;
1394: {
1395: register int i, len;
1396: char *result = (char *)NULL;
1397: int size = 0, offset = 0;
1398:
1399: char **history_tokenize (), **list;
1400:
1401: if (!(list = history_tokenize (string)))
1402: return ((char *)NULL);
1403:
1404: for (len = 0; list[len]; len++);
1405:
1406: if (last < 0)
1407: last = len + last - 1;
1408:
1409: if (first < 0)
1410: first = len + first - 1;
1411:
1412: if (last == '$')
1413: last = len - 1;
1414:
1415: if (first == '$')
1416: first = len - 1;
1417:
1418: last++;
1419:
1420: if (first > len || last > len || first < 0 || last < 0)
1421: result = ((char *)NULL);
1422: else
1423: {
1424: for (i = first; i < last; i++)
1425: {
1426: int l = strlen (list[i]);
1427:
1428: if (!result)
1429: result = (char *)xmalloc ((size = (2 + l)));
1430: else
1431: result = (char *)xrealloc (result, (size += (2 + l)));
1432: strcpy (result + offset, list[i]);
1433: offset += l;
1434: if (i + 1 < last)
1435: {
1436: strcpy (result + offset, " ");
1437: offset++;
1438: }
1439: }
1440: }
1441:
1442: for (i = 0; i < len; i++)
1443: free (list[i]);
1444:
1445: free (list);
1446:
1447: return (result);
1448: }
1449:
1450: #define slashify_in_quotes "\\`\"$"
1451:
1452: /* Return an array of tokens, much as the shell might. The tokens are
1453: parsed out of STRING. */
1454: char **
1455: history_tokenize (string)
1456: char *string;
1457: {
1458: char **result = (char **)NULL;
1459: register int i, start, result_index, size;
1460: int len;
1461:
1462: i = result_index = size = 0;
1463:
1464: /* Get a token, and stuff it into RESULT. The tokens are split
1465: exactly where the shell would split them. */
1466: get_token:
1467:
1468: /* Skip leading whitespace. */
1469: for (; string[i] && whitespace(string[i]); i++);
1470:
1471: start = i;
1472:
1473: if (!string[i] || string[i] == history_comment_char)
1474: return (result);
1475:
1476: if (member (string[i], "()\n")) {
1477: i++;
1478: goto got_token;
1479: }
1480:
1481: if (member (string[i], "<>;&|")) {
1482: int peek = string[i + 1];
1483:
1484: if (peek == string[i]) {
1485: if (peek == '<') {
1486: if (string[1 + 2] == '-')
1487: i++;
1488: i += 2;
1489: goto got_token;
1490: }
1491:
1492: if (member (peek, ">:&|")) {
1493: i += 2;
1494: goto got_token;
1495: }
1496: } else {
1497: if ((peek == '&' &&
1498: (string[i] == '>' || string[i] == '<')) ||
1499: ((peek == '>') &&
1500: (string[i] == '&'))) {
1501: i += 2;
1502: goto got_token;
1503: }
1504: }
1505: i++;
1506: goto got_token;
1507: }
1508:
1509: /* Get word from string + i; */
1510: {
1511: int delimiter = 0;
1512:
1513: if (member (string[i], "\"'`"))
1514: delimiter = string[i++];
1515:
1516: for (;string[i]; i++) {
1517:
1518: if (string[i] == '\\') {
1519:
1520: if (string[i + 1] == '\n') {
1521: i++;
1522: continue;
1523: } else {
1524: if (delimiter != '\'')
1525: if ((delimiter != '"') ||
1526: (member (string[i], slashify_in_quotes))) {
1527: i++;
1528: continue;
1529: }
1530: }
1531: }
1532:
1533: if (delimiter && string[i] == delimiter) {
1534: delimiter = 0;
1535: continue;
1536: }
1537:
1538: if (!delimiter && (member (string[i], " \t\n;&()|<>")))
1539: goto got_token;
1540:
1541: if (!delimiter && member (string[i], "\"'`")) {
1542: delimiter = string[i];
1543: continue;
1544: }
1545: }
1546: got_token:
1547:
1548: len = i - start;
1549: if (result_index + 2 >= size) {
1550: if (!size)
1551: result = (char **)xmalloc ((size = 10) * (sizeof (char *)));
1552: else
1553: result =
1554: (char **)xrealloc (result, ((size += 10) * (sizeof (char *))));
1555: }
1556: result[result_index] = (char *)xmalloc (1 + len);
1557: strncpy (result[result_index], string + start, len);
1558: result[result_index][len] = '\0';
1559: result_index++;
1560: result[result_index] = (char *)NULL;
1561: }
1562: if (string[i])
1563: goto get_token;
1564:
1565: return (result);
1566: }
1567:
1568: #if defined (STATIC_MALLOC)
1569:
1570: /* **************************************************************** */
1571: /* */
1572: /* xmalloc and xrealloc () */
1573: /* */
1574: /* **************************************************************** */
1575:
1576: static void memory_error_and_abort ();
1577:
1578: static char *
1579: xmalloc (bytes)
1580: int bytes;
1581: {
1582: char *temp = (char *)malloc (bytes);
1583:
1584: if (!temp)
1585: memory_error_and_abort ();
1586: return (temp);
1587: }
1588:
1589: static char *
1590: xrealloc (pointer, bytes)
1591: char *pointer;
1592: int bytes;
1593: {
1594: char *temp;
1595:
1596: if (!pointer)
1597: temp = (char *)xmalloc (bytes);
1598: else
1599: temp = (char *)realloc (pointer, bytes);
1600:
1601: if (!temp)
1602: memory_error_and_abort ();
1603:
1604: return (temp);
1605: }
1606:
1607: static void
1608: memory_error_and_abort ()
1609: {
1610: fprintf (stderr, "history: Out of virtual memory!\n");
1611: abort ();
1612: }
1613: #endif /* STATIC_MALLOC */
1614:
1615:
1616: /* **************************************************************** */
1617: /* */
1618: /* Test Code */
1619: /* */
1620: /* **************************************************************** */
1621: #ifdef TEST
1622: main ()
1623: {
1624: char line[1024], *t;
1625: int done = 0;
1626:
1627: line[0] = 0;
1628:
1629: while (!done)
1630: {
1631: fprintf (stdout, "history%% ");
1632: t = gets (line);
1633:
1634: if (!t)
1635: strcpy (line, "quit");
1636:
1637: if (line[0])
1638: {
1639: char *expansion;
1640: int result;
1641:
1642: using_history ();
1643:
1644: result = history_expand (line, &expansion);
1645: strcpy (line, expansion);
1646: free (expansion);
1647: if (result)
1648: fprintf (stderr, "%s\n", line);
1649:
1650: if (result < 0)
1651: continue;
1652:
1653: add_history (line);
1654: }
1655:
1656: if (strcmp (line, "quit") == 0) done = 1;
1657: if (strcmp (line, "save") == 0) write_history (0);
1658: if (strcmp (line, "read") == 0) read_history (0);
1659: if (strcmp (line, "list") == 0)
1660: {
1661: register HIST_ENTRY **the_list = history_list ();
1662: register int i;
1663:
1664: if (the_list)
1665: for (i = 0; the_list[i]; i++)
1666: fprintf (stdout, "%d: %s\n", i + history_base, the_list[i]->line);
1667: }
1668: if (strncmp (line, "delete", strlen ("delete")) == 0)
1669: {
1670: int which;
1671: if ((sscanf (line + strlen ("delete"), "%d", &which)) == 1)
1672: {
1673: HIST_ENTRY *entry = remove_history (which);
1674: if (!entry)
1675: fprintf (stderr, "No such entry %d\n", which);
1676: else
1677: {
1678: free (entry->line);
1679: free (entry);
1680: }
1681: }
1682: else
1683: {
1684: fprintf (stderr, "non-numeric arg given to `delete'\n");
1685: }
1686: }
1687: }
1688: }
1689:
1690: #endif /* TEST */
1691:
1692: /*
1693: * Local variables:
1694: * compile-command: "gcc -g -DTEST -o history history.c"
1695: * end:
1696: */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.