Annotation of GNUtools/debug/gdb/readline/history.c, revision 1.1

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: */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.