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