diff options
Diffstat (limited to 'libtecla-1.4.1/history.c')
-rw-r--r-- | libtecla-1.4.1/history.c | 2003 |
1 files changed, 0 insertions, 2003 deletions
diff --git a/libtecla-1.4.1/history.c b/libtecla-1.4.1/history.c deleted file mode 100644 index 5a8d94f..0000000 --- a/libtecla-1.4.1/history.c +++ /dev/null @@ -1,2003 +0,0 @@ -/* - * Copyright (c) 2000, 2001 by Martin C. Shepherd. - * - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons - * to whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT - * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL - * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Except as contained in this notice, the name of a copyright holder - * shall not be used in advertising or otherwise to promote the sale, use - * or other dealings in this Software without prior written authorization - * of the copyright holder. - */ - -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <ctype.h> -#include <time.h> -#include <errno.h> - -#include "history.h" -#include "freelist.h" - -/* - * GlLineNode's record the location and length of historical lines in - * a buffer array. - */ -typedef struct GlLineNode GlLineNode; -struct GlLineNode { - long id; /* The unique identifier of this history line */ - time_t timestamp; /* The time at which the line was archived */ - unsigned group; /* The identifier of the history group to which the */ - /* the line belongs. */ - GlLineNode *next; /* The next youngest line in the list */ - GlLineNode *prev; /* The next oldest line in the list */ - int start; /* The start index of the line in the buffer */ - int nchar; /* The total length of the line, including the '\0' */ -}; - -/* - * The number of GlLineNode elements per freelist block. - */ -#define LINE_NODE_BLK 100 - -/* - * Lines are organised in the buffer from oldest to newest. The - * positions of the lines are recorded in a doubly linked list - * of GlLineNode objects. - */ -typedef struct { - FreeList *node_mem; /* A freelist of GlLineNode objects */ - GlLineNode *head; /* The head of the list of lines */ - GlLineNode *tail; /* The tail of the list of lines */ -} GlLineList; - -/* - * All elements of the history mechanism are recorded in an object of - * the following type. - */ -struct GlHistory { - char *buffer; /* A circular buffer used to record historical input */ - /* lines. */ - size_t buflen; /* The length of the buffer array */ - GlLineList list; /* A list of the start of lines in buffer[] */ - GlLineNode *recall; /* The last line recalled, or NULL if no recall */ - /* session is currently active. */ - GlLineNode *id_node;/* The node at which the last ID search terminated */ - const char *prefix; /* A pointer to the line containing the prefix that */ - /* is being searched for. */ - int prefix_len; /* The length of the prefix */ - unsigned long seq; /* The next ID to assign to a line node */ - unsigned group; /* The identifier of the current history group */ - int nline; /* The number of lines currently in the history list */ - int max_lines; /* Either -1 or a ceiling on the number of lines */ - int enable; /* If false, ignore history additions and lookups */ -}; - -static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim); -static int _glh_cant_load_history(GlHistory *glh, const char *filename, - int lineno, const char *message, FILE *fp); -static int _glh_write_timestamp(FILE *fp, time_t timestamp); -static int _glh_decode_timestamp(char *string, char **endp, time_t *t); -static void _glh_discard_node(GlHistory *glh, GlLineNode *node); -static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id); - -/*....................................................................... - * Create a line history maintenance object. - * - * Input: - * buflen size_t The number of bytes to allocate to the circular - * buffer that is used to record all of the - * most recent lines of user input that will fit. - * If buflen==0, no buffer will be allocated. - * Output: - * return GlHistory * The new object, or NULL on error. - */ -GlHistory *_new_GlHistory(size_t buflen) -{ - GlHistory *glh; /* The object to be returned */ -/* - * Allocate the container. - */ - glh = (GlHistory *) malloc(sizeof(GlHistory)); - if(!glh) { - fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); - return NULL; - }; -/* - * Before attempting any operation that might fail, initialize the - * container at least up to the point at which it can safely be passed - * to _del_GlHistory(). - */ - glh->buffer = NULL; - glh->buflen = buflen; - glh->list.node_mem = NULL; - glh->list.head = NULL; - glh->list.tail = NULL; - glh->recall = NULL; - glh->id_node = NULL; - glh->prefix = NULL; - glh->prefix_len = 0; - glh->seq = 0; - glh->group = 0; - glh->nline = 0; - glh->max_lines = -1; - glh->enable = 1; -/* - * Allocate the buffer, if required. - */ - if(buflen > 0) { - glh->buffer = (char *) malloc(sizeof(char) * buflen); - if(!glh->buffer) { - fprintf(stderr, "_new_GlHistory: Insufficient memory.\n"); - return _del_GlHistory(glh); - }; - }; -/* - * Allocate the GlLineNode freelist. - */ - glh->list.node_mem = _new_FreeList("_new_GlHistory", sizeof(GlLineNode), - LINE_NODE_BLK); - if(!glh->list.node_mem) - return _del_GlHistory(glh); - return glh; -} - -/*....................................................................... - * Delete a GlHistory object. - * - * Input: - * glh GlHistory * The object to be deleted. - * Output: - * return GlHistory * The deleted object (always NULL). - */ -GlHistory *_del_GlHistory(GlHistory *glh) -{ - if(glh) { -/* - * Delete the buffer. - */ - if(glh->buffer) { - free(glh->buffer); - glh->buffer = NULL; - }; -/* - * Delete the freelist of GlLineNode's. - */ - glh->list.node_mem = _del_FreeList("_del_GlHistory", glh->list.node_mem, 1); -/* - * The contents of the list were deleted by deleting the freelist. - */ - glh->list.head = NULL; - glh->list.tail = NULL; -/* - * Delete the container. - */ - free(glh); - }; - return NULL; -} - -/*....................................................................... - * Add a new line to the end of the history buffer, wrapping round to the - * start of the buffer if needed. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The line to be archived. - * force int Unless this flag is non-zero, empty lines and - * lines which match the previous line in the history - * buffer, aren't archived. This flag requests that - * the line be archived regardless. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_add_history(GlHistory *glh, const char *line, int force) -{ - GlLineList *list; /* The line location list */ - int nchar; /* The number of characters needed to record the line */ - GlLineNode *node; /* The new line location list node */ - int empty; /* True if the string is empty */ - const char *nlptr;/* A pointer to a newline character in line[] */ - int i; -/* - * Check the arguments. - */ - if(!glh || !line) - return 1; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Get the line location list. - */ - list = &glh->list; -/* - * Cancel any ongoing search. - */ - if(_glh_cancel_search(glh)) - return 1; -/* - * See how much buffer space will be needed to record the line? - * - * If the string contains a terminating newline character, arrange to - * have the archived line NUL terminated at this point. - */ - nlptr = strchr(line, '\n'); - if(nlptr) - nchar = (nlptr - line) + 1; - else - nchar = strlen(line) + 1; -/* - * If the line is too big to fit in the buffer, truncate it. - */ - if(nchar > glh->buflen) - nchar = glh->buflen; -/* - * Is the line empty? - */ - empty = 1; - for(i=0; i<nchar-1 && empty; i++) - empty = isspace((int)(unsigned char) line[i]); -/* - * If the line is empty, don't add it to the buffer unless explicitly - * told to. - */ - if(empty && !force) - return 0; -/* - * If the new line is the same as the most recently added line, - * don't add it again, unless explicitly told to. - */ - if(!force && - list->tail && strlen(glh->buffer + list->tail->start) == nchar-1 && - strncmp(line, glh->buffer + list->tail->start, nchar-1)==0) - return 0; -/* - * Allocate the list node that will record the line location. - */ - node = (GlLineNode *) _new_FreeListNode(list->node_mem); - if(!node) - return 1; -/* - * Is the buffer empty? - */ - if(!list->head) { -/* - * Place the line at the beginning of the buffer. - */ - strncpy(glh->buffer, line, nchar); - glh->buffer[nchar-1] = '\0'; -/* - * Record the location of the line. - */ - node->start = 0; -/* - * The buffer has one or more lines in it. - */ - } else { -/* - * Place the start of the new line just after the most recently - * added line. - */ - int start = list->tail->start + list->tail->nchar; -/* - * If there is insufficient room between the end of the most - * recently added line and the end of the buffer, we place the - * line at the beginning of the buffer. To make as much space - * as possible for this line, we first delete any old lines - * at the end of the buffer, then shift the remaining contents - * of the buffer to the end of the buffer. - */ - if(start + nchar >= glh->buflen) { - GlLineNode *last; /* The last line in the buffer */ - GlLineNode *ln; /* A member of the list of line locations */ - int shift; /* The shift needed to move the contents of the */ - /* buffer to its end. */ -/* - * Delete any old lines between the most recent line and the end of the - * buffer. - */ - while(list->head && list->head->start > list->tail->start) - _glh_discard_node(glh, list->head); -/* - * Find the line that is nearest the end of the buffer. - */ - last = NULL; - for(ln=list->head; ln; ln=ln->next) { - if(!last || ln->start > last->start) - last = ln; - }; -/* - * How big a shift is needed to move the existing contents of the - * buffer to the end of the buffer? - */ - shift = last ? (glh->buflen - (last->start + last->nchar)) : 0; -/* - * Is any shift needed? - */ - if(shift > 0) { -/* - * Move the buffer contents to the end of the buffer. - */ - memmove(glh->buffer + shift, glh->buffer, glh->buflen - shift); -/* - * Update the listed locations to reflect the shift. - */ - for(ln=list->head; ln; ln=ln->next) - ln->start += shift; - }; -/* - * The new line should now be located at the start of the buffer. - */ - start = 0; - }; -/* - * Make space for the new line at the beginning of the buffer by - * deleting the oldest lines. This just involves removing them - * from the list of used locations. Also enforce the current - * maximum number of lines. - */ - while(list->head && - ((list->head->start >= start && list->head->start - start < nchar) || - (glh->max_lines >= 0 && glh->nline>=glh->max_lines))) { - _glh_discard_node(glh, list->head); - }; -/* - * Copy the new line into the buffer. - */ - memcpy(glh->buffer + start, line, nchar); - glh->buffer[start + nchar - 1] = '\0'; -/* - * Record its location. - */ - node->start = start; - }; -/* - * Append the line location node to the end of the list. - */ - node->id = glh->seq++; - node->timestamp = time(NULL); - node->group = glh->group; - node->nchar = nchar; - node->next = NULL; - node->prev = list->tail; - if(list->tail) - list->tail->next = node; - else - list->head = node; - list->tail = node; - glh->nline++; - return 0; -} - -/*....................................................................... - * Recall the next oldest line that has the search prefix last recorded - * by _glh_search_prefix(). - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, if anything - * was found, its contents will have been replaced - * with the matching line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ - int first; /* True if this is the start of a new search */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_find_backwards: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_find_backwards: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Is this the start of a new search? - */ - first = glh->recall==NULL; -/* - * If this is the first search backwards, save the current line - * for potential recall later, and mark it as the last line - * recalled. - */ - if(first) { - if(_glh_add_history(glh, line, 1)) - return NULL; - glh->recall = glh->list.tail; - }; -/* - * If there is no search prefix, the prefix last set by glh_search_prefix() - * doesn't exist in the history buffer. - */ - if(!glh->prefix) - return NULL; -/* - * From where should we start the search? - */ - if(glh->recall) - node = glh->recall->prev; - else - node = glh->list.tail; -/* - * Search backwards through the list for the first match with the - * prefix string. - */ - for( ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); - node = node->prev) - ; -/* - * Was a matching line found? - */ - if(node) { -/* - * Recall the found node as the starting point for subsequent - * searches. - */ - glh->recall = node; -/* - * Copy the matching line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; - }; -/* - * No match was found. - */ - return NULL; -} - -/*....................................................................... - * Recall the next newest line that has the search prefix last recorded - * by _glh_search_prefix(). - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, if anything - * was found, its contents will have been replaced - * with the matching line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * The line requested, or NULL if no matching line - * was found. - */ -char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_find_forwards: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_find_forwards: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * From where should we start the search? - */ - if(glh->recall) - node = glh->recall->next; - else - return NULL; -/* - * If there is no search prefix, the prefix last set by glh_search_prefix() - * doesn't exist in the history buffer. - */ - if(!glh->prefix) - return NULL; -/* - * Search forwards through the list for the first match with the - * prefix string. - */ - for( ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, glh->prefix, glh->prefix_len) != 0); - node = node->next) - ; -/* - * Was a matching line found? - */ - if(node) { -/* - * Did we hit the line that was originally being edited when the - * current history traversal started? - */ - if(node == glh->list.tail) - return _glh_restore_line(glh, line, dim); -/* - * Copy the matching line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; -/* - * Record the starting point of the next search. - */ - glh->recall = node; -/* - * Return the matching line to the user. - */ - return line; - }; -/* - * No match was found. - */ - return NULL; -} - -/*....................................................................... - * If a search is in progress, cancel it. - * - * This involves discarding the line that was temporarily saved by - * _glh_find_backwards() when the search was originally started, - * and reseting the search iteration pointer to NULL. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_cancel_search(GlHistory *glh) -{ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_cancel_search: NULL argument(s).\n"); - return 1; - }; -/* - * If there wasn't a search in progress, do nothing. - */ - if(!glh->recall) - return 0; -/* - * Delete the node of the preserved line. - */ - _glh_discard_node(glh, glh->list.tail); -/* - * Reset the search pointers. - */ - glh->recall = NULL; - glh->prefix = ""; - glh->prefix_len = 0; - return 0; -} - -/*....................................................................... - * Set the prefix of subsequent history searches. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The command line who's prefix is to be used. - * prefix_len int The length of the prefix. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_search_prefix: NULL argument(s).\n"); - return 1; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Record a zero length search prefix? - */ - if(prefix_len <= 0) { - glh->prefix_len = 0; - glh->prefix = ""; - return 0; - }; -/* - * Record the length of the new search prefix. - */ - glh->prefix_len = prefix_len; -/* - * If any history line starts with the specified prefix, record a - * pointer to it for comparison in subsequent searches. If the prefix - * doesn't match any of the lines, then simply record NULL to indicate - * that there is no point in searching. Note that _glh_add_history() - * clears this pointer by calling _glh_cancel_search(), so there is - * no danger of it being used after the buffer has been modified. - */ - for(node = glh->list.tail ; node && - (node->group != glh->group || - strncmp(glh->buffer + node->start, line, prefix_len) != 0); - node = node->prev) - ; -/* - * If a matching line was found record it for use as the search - * prefix. - */ - glh->prefix = node ? glh->buffer + node->start : NULL; - return 0; -} - -/*....................................................................... - * Recall the oldest recorded line. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the oldest line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ - int first; /* True if this is the start of a new search */ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_oldest_line: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_oldest_line: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Is this the start of a new search? - */ - first = glh->recall==NULL; -/* - * If this is the first search backwards, save the current line - * for potential recall later, and mark it as the last line - * recalled. - */ - if(first) { - if(_glh_add_history(glh, line, 1)) - return NULL; - glh->recall = glh->list.tail; - }; -/* - * Locate the oldest line that belongs to the current group. - */ - for(node=glh->list.head; node && node->group != glh->group; - node = node->next) - ; -/* - * No line found? - */ - if(!node) - return NULL; -/* - * Record the above node as the starting point for subsequent - * searches. - */ - glh->recall = node; -/* - * Copy the recalled line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; -} - -/*....................................................................... - * Recall the line that was being entered when the search started. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the line that was - * being entered when the search was started. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_current_line(GlHistory *glh, char *line, size_t dim) -{ -/* - * Check the arguments. - */ - if(!glh || !line) { - fprintf(stderr, "_glh_current_line: NULL argument(s).\n"); - return NULL; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * Check the line dimensions. - */ - if(dim < strlen(line) + 1) { - fprintf(stderr, - "_glh_current_line: 'dim' inconsistent with strlen(line) contents.\n"); - return NULL; - }; -/* - * Restore the original line. - */ - return _glh_restore_line(glh, line, dim); -} - -/*....................................................................... - * Remove the line that was originally being edited when the history - * traversal was started, from its saved position in the history list, - * and place it in the provided line buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the saved line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -static char *_glh_restore_line(GlHistory *glh, char *line, size_t dim) -{ - GlLineNode *tail; /* The tail node to be discarded */ -/* - * If there wasn't a search in progress, do nothing. - */ - if(!glh->recall) - return NULL; -/* - * Get the list node that is to be removed. - */ - tail = glh->list.tail; -/* - * If a pointer to the saved line is being used to record the - * current search prefix, reestablish the search prefix, to - * have it recorded by another history line if possible. - */ - if(glh->prefix == glh->buffer + tail->start) - (void) _glh_search_prefix(glh, glh->buffer + tail->start, glh->prefix_len); -/* - * Copy the recalled line into the input-line buffer. - */ - strncpy(line, glh->buffer + tail->start, dim); - line[dim-1] = '\0'; -/* - * Discard the line-location node. - */ - _glh_discard_node(glh, tail); -/* - * Mark the search as ended. - */ - glh->recall = NULL; - return line; -} - -/*....................................................................... - * Query the id of a history line offset by a given number of lines from - * the one that is currently being recalled. If a recall session isn't - * in progress, or the offset points outside the history list, 0 is - * returned. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * offset int The line offset (0 for the current line, < 0 - * for an older line, > 0 for a newer line. - * Output: - * return GlhLineID The identifier of the line that is currently - * being recalled, or 0 if no recall session is - * currently in progress. - */ -GlhLineID _glh_line_id(GlHistory *glh, int offset) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return 0; -/* - * Search forward 'offset' lines to find the required line. - */ - if(offset >= 0) { - for(node=glh->recall; node && offset != 0; node=node->next) { - if(node->group == glh->group) - offset--; - }; - } else { - for(node=glh->recall; node && offset != 0; node=node->prev) { - if(node->group == glh->group) - offset++; - }; - }; - return node ? node->id : 0; -} - -/*....................................................................... - * Recall a line by its history buffer ID. If the line is no longer - * in the buffer, or the id is zero, NULL is returned. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlhLineID The ID of the line to be returned. - * line char * The input line buffer. On input this should contain - * the current input line, and on output, its contents - * will have been replaced with the saved line. - * dim size_t The allocated dimensions of the line buffer. - * Output: - * return char * A pointer to line[0], or NULL if not found. - */ -char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim) -{ - GlLineNode *node; /* The line location node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->buffer || glh->max_lines == 0) - return NULL; -/* - * If we are starting a new recall session, save the current line - * for potential recall later. - */ - if(!glh->recall && _glh_add_history(glh, line, 1)) - return NULL; -/* - * Search for the specified line. - */ - node = _glh_find_id(glh, id); -/* - * Not found? - */ - if(!node || node->group != glh->group) - return NULL; -/* - * Record the node of the matching line as the starting point - * for subsequent searches. - */ - glh->recall = node; -/* - * Copy the recalled line into the provided line buffer. - */ - strncpy(line, glh->buffer + node->start, dim); - line[dim-1] = '\0'; - return line; -} - -/*....................................................................... - * Save the current history in a specified file. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * filename const char * The name of the new file to record the - * history in. - * comment const char * Extra information such as timestamps will - * be recorded on a line started with this - * string, the idea being that the file can - * double as a command file. Specify "" if - * you don't care. - * max_lines int The maximum number of lines to save, or -1 - * to save all of the lines in the history - * list. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_save_history(GlHistory *glh, const char *filename, const char *comment, - int max_lines) -{ - FILE *fp; /* The output file */ - GlLineNode *node; /* The line being saved */ - GlLineNode *head; /* The head of the list of lines to be saved */ -/* - * Check the arguments. - */ - if(!glh || !filename || !comment) { - fprintf(stderr, "_glh_save_history: NULL argument(s).\n"); - return 1; - }; -/* - * Attempt to open the specified file. - */ - fp = fopen(filename, "w"); - if(!fp) { - fprintf(stderr, "_glh_save_history: Can't open %s (%s).\n", - filename, strerror(errno)); - return 1; - }; -/* - * If a ceiling on the number of lines to save was specified, count - * that number of lines backwards, to find the first line to be saved. - */ - head = NULL; - if(max_lines >= 0) { - for(head=glh->list.tail; head && --max_lines > 0; head=head->prev) - ; - }; - if(!head) - head = glh->list.head; -/* - * Write the contents of the history buffer to the history file, writing - * associated data such as timestamps, to a line starting with the - * specified comment string. - */ - for(node=head; node; node=node->next) { -/* - * Write peripheral information associated with the line, as a comment. - */ - if(fprintf(fp, "%s ", comment) < 0 || - _glh_write_timestamp(fp, node->timestamp) || - fprintf(fp, " %u\n", node->group) < 0) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - (void) fclose(fp); - return 1; - }; -/* - * Write the history line. - */ - if(fprintf(fp, "%s\n", glh->buffer + node->start) < 0) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - (void) fclose(fp); - return 1; - }; - }; -/* - * Close the history file. - */ - if(fclose(fp) == EOF) { - fprintf(stderr, "Error writing %s (%s).\n", filename, strerror(errno)); - return 1; - }; - return 0; -} - -/*....................................................................... - * Restore previous history lines from a given file. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * filename const char * The name of the file to read from. - * comment const char * The same comment string that was passed to - * _glh_save_history() when this file was - * written. - * line char * A buffer into which lines can be read. - * dim size_t The allocated dimension of line[]. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_load_history(GlHistory *glh, const char *filename, const char *comment, - char *line, size_t dim) -{ - FILE *fp; /* The output file */ - size_t comment_len; /* The length of the comment string */ - time_t timestamp; /* The timestamp of the history line */ - unsigned group; /* The identifier of the history group to which */ - /* the line belongs. */ - int lineno; /* The line number being read */ -/* - * Check the arguments. - */ - if(!glh || !filename || !comment || !line) { - fprintf(stderr, "_glh_load_history: NULL argument(s).\n"); - return 1; - }; -/* - * Measure the length of the comment string. - */ - comment_len = strlen(comment); -/* - * Clear the history list. - */ - _glh_clear_history(glh, 1); -/* - * Attempt to open the specified file. Don't treat it as an error - * if the file doesn't exist. - */ - fp = fopen(filename, "r"); - if(!fp) - return 0; -/* - * Attempt to read each line and preceding peripheral info, and add these - * to the history list. - */ - for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) { - char *lptr; /* A pointer into the input line */ -/* - * Check that the line starts with the comment string. - */ - if(strncmp(line, comment, comment_len) != 0) { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt history parameter line", fp); - }; -/* - * Skip spaces and tabs after the comment. - */ - for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++) - ; -/* - * The next word must be a timestamp. - */ - if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt timestamp", fp); - }; -/* - * Skip spaces and tabs. - */ - while(*lptr==' ' || *lptr=='\t') - lptr++; -/* - * The next word must be an unsigned integer group number. - */ - group = (int) strtoul(lptr, &lptr, 10); - if(*lptr != ' ' && *lptr != '\n') { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt group id", fp); - }; -/* - * Skip spaces and tabs. - */ - while(*lptr==' ' || *lptr=='\t') - lptr++; -/* - * There shouldn't be anything left on the line. - */ - if(*lptr != '\n') { - return _glh_cant_load_history(glh, filename, lineno, - "Corrupt parameter line", fp); - }; -/* - * Now read the history line itself. - */ - lineno++; - if(fgets(line, dim, fp) == NULL) - return _glh_cant_load_history(glh, filename, lineno, "Read error", fp); -/* - * Append the line to the history buffer. - */ - if(_glh_add_history(glh, line, 1)) { - return _glh_cant_load_history(glh, filename, lineno, - "Insufficient memory to record line", fp); - }; -/* - * Record the group and timestamp information along with the line. - */ - if(glh->list.tail) { - glh->list.tail->timestamp = timestamp; - glh->list.tail->group = group; - }; - }; -/* - * Close the file. - */ - (void) fclose(fp); - return 0; -} - -/*....................................................................... - * This is a private error return function of _glh_load_history(). - */ -static int _glh_cant_load_history(GlHistory *glh, const char *filename, - int lineno, const char *message, FILE *fp) -{ - fprintf(stderr, "%s:%d: %s.\n", filename, lineno, message); - (void) fclose(fp); - return 1; -} - -/*....................................................................... - * Switch history groups. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * group unsigned The new group identifier. This will be recorded - * with subsequent history lines, and subsequent - * history searches will only return lines with - * this group identifier. This allows multiple - * separate history lists to exist within - * a single GlHistory object. Note that the - * default group identifier is 0. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_set_group(GlHistory *glh, unsigned group) -{ -/* - * Check the arguments. - */ - if(!glh) { - fprintf(stderr, "_glh_set_group: NULL argument(s).\n"); - return 1; - }; -/* - * Is the group being changed? - */ - if(group != glh->group) { -/* - * Cancel any ongoing search. - */ - if(_glh_cancel_search(glh)) - return 1; -/* - * Record the new group. - */ - glh->group = group; - }; - return 0; -} - -/*....................................................................... - * Query the current history group. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Output: - * return unsigned The group identifier. - */ -int _glh_get_group(GlHistory *glh) -{ - return glh ? glh->group : 0; -} - -/*....................................................................... - * Write a timestamp to a given stdio stream, in the format - * yyyymmddhhmmss - * - * Input: - * fp FILE * The stream to write to. - * timestamp time_t The timestamp to be written. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _glh_write_timestamp(FILE *fp, time_t timestamp) -{ - struct tm *t; /* THe broken-down calendar time */ -/* - * Get the calendar components corresponding to the given timestamp. - */ - if(timestamp < 0 || (t = localtime(×tamp)) == NULL) { - if(fprintf(fp, "?") < 0) - return 1; - return 0; - }; -/* - * Write the calendar time as yyyymmddhhmmss. - */ - if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1, - t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0) - return 1; - return 0; -} - -/*....................................................................... - * Read a timestamp from a string. - * - * Input: - * string char * The string to read from. - * Input/Output: - * endp char ** On output *endp will point to the next unprocessed - * character in string[]. - * timestamp time_t * The timestamp will be assigned to *t. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp) -{ - unsigned year,month,day,hour,min,sec; /* Calendar time components */ - struct tm t; -/* - * There are 14 characters in the date format yyyymmddhhmmss. - */ - enum {TSLEN=14}; - char timestr[TSLEN+1]; /* The timestamp part of the string */ -/* - * If the time wasn't available at the time that the line was recorded - * it will have been written as "?". Check for this before trying - * to read the timestamp. - */ - if(string[0] == '\?') { - *endp = string+1; - *timestamp = -1; - return 0; - }; -/* - * The timestamp is expected to be written in the form yyyymmddhhmmss. - */ - if(strlen(string) < TSLEN) { - *endp = string; - return 1; - }; -/* - * Copy the timestamp out of the string. - */ - strncpy(timestr, string, TSLEN); - timestr[TSLEN] = '\0'; -/* - * Decode the timestamp. - */ - if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min, - &sec) != 6) { - *endp = string; - return 1; - }; -/* - * Advance the string pointer over the successfully read timestamp. - */ - *endp = string + TSLEN; -/* - * Copy the read values into a struct tm. - */ - t.tm_sec = sec; - t.tm_min = min; - t.tm_hour = hour; - t.tm_mday = day; - t.tm_wday = 0; - t.tm_yday = 0; - t.tm_mon = month - 1; - t.tm_year = year - 1900; - t.tm_isdst = -1; -/* - * Convert the contents of the struct tm to a time_t. - */ - *timestamp = mktime(&t); - return 0; -} - -/*....................................................................... - * Display the contents of the history list. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * fp FILE * The stdio stream to write to. - * fmt const char * A format string. This can contain arbitrary - * characters, which are written verbatim, plus - * any of the following format directives: - * %D - The date, like 2001-11-20 - * %T - The time of day, like 23:59:59 - * %N - The sequential entry number of the - * line in the history buffer. - * %G - The history group number of the line. - * %% - A literal % character. - * %H - The history line. - * all_groups int If true, display history lines from all - * history groups. Otherwise only display - * those of the current history group. - * max_lines int If max_lines is < 0, all available lines - * are displayed. Otherwise only the most - * recent max_lines lines will be displayed. - * Output: - * return int 0 - OK. - * 1 - Error. - */ -int _glh_show_history(GlHistory *glh, FILE *fp, const char *fmt, - int all_groups, int max_lines) -{ - GlLineNode *node; /* The line being displayed */ - GlLineNode *oldest; /* The oldest line to display */ - enum {TSMAX=32}; /* The maximum length of the date and time string */ - char buffer[TSMAX+1]; /* The buffer in which to write the date and time */ - int idlen; /* The length of displayed ID strings */ - unsigned grpmax; /* The maximum group number in the buffer */ - int grplen; /* The number of characters needed to print grpmax */ -/* - * Check the arguments. - */ - if(!glh || !fp || !fmt) { - fprintf(stderr, "_glh_show_history: NULL argument(s).\n"); - return 1; - }; -/* - * Is history enabled? - */ - if(!glh->enable || !glh->list.head) - return 0; -/* - * Work out the length to display ID numbers, choosing the length of - * the biggest number in the buffer. Smaller numbers will be padded - * with leading zeroes if needed. - */ - sprintf(buffer, "%lu", (unsigned long) glh->list.tail->id); - idlen = strlen(buffer); -/* - * Find the largest group number. - */ - grpmax = 0; - for(node=glh->list.head; node; node=node->next) { - if(node->group > grpmax) - grpmax = node->group; - }; -/* - * Find out how many characters are needed to display the group number. - */ - sprintf(buffer, "%u", (unsigned) grpmax); - grplen = strlen(buffer); -/* - * Find the node that follows the oldest line to be displayed. - */ - if(max_lines < 0) { - oldest = glh->list.head; - } else if(max_lines==0) { - return 0; - } else { - for(oldest=glh->list.tail; oldest; oldest=oldest->prev) { - if((all_groups || oldest->group == glh->group) && --max_lines <= 0) - break; - }; -/* - * If the number of lines in the buffer doesn't exceed the specified - * maximum, start from the oldest line in the buffer. - */ - if(!oldest) - oldest = glh->list.head; - }; -/* - * List the history lines in increasing time order. - */ - for(node=oldest; node; node=node->next) { -/* - * Only display lines from the current history group, unless - * told otherwise. - */ - if(all_groups || node->group == glh->group) { - const char *fptr; /* A pointer into the format string */ - struct tm *t = NULL; /* The broken time version of the timestamp */ -/* - * Work out the calendar representation of the node timestamp. - */ - if(node->timestamp != (time_t) -1) - t = localtime(&node->timestamp); -/* - * Parse the format string. - */ - fptr = fmt; - while(*fptr) { -/* - * Search for the start of the next format directive or the end of the string. - */ - const char *start = fptr; - while(*fptr && *fptr != '%') - fptr++; -/* - * Display any literal characters that precede the located directive. - */ - if(fptr > start && fprintf(fp, "%.*s", (int) (fptr - start), start) < 0) - return 1; -/* - * Did we hit a new directive before the end of the line? - */ - if(*fptr) { -/* - * Obey the directive. Ignore unknown directives. - */ - switch(*++fptr) { - case 'D': /* Display the date */ - if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0 && - fprintf(fp, "%s", buffer) < 0) - return 1; - break; - case 'T': /* Display the time of day */ - if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0 && - fprintf(fp, "%s", buffer) < 0) - return 1; - break; - case 'N': /* Display the sequential entry number */ - if(fprintf(fp, "%*lu", idlen, (unsigned long) node->id) < 0) - return 1; - break; - case 'G': - if(fprintf(fp, "%*u", grplen, (unsigned) node->group) < 0) - return 1; - break; - case 'H': /* Display the history line */ - if(fprintf(fp, "%s", glh->buffer + node->start) < 0) - return 1; - break; - case '%': /* A literal % symbol */ - if(fputc('%', fp) == EOF) - return 1; - break; - }; -/* - * Skip the directive. - */ - if(*fptr) - fptr++; - }; - }; - }; - }; - return 0; -} - -/*....................................................................... - * Change the size of the history buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * bufsize size_t The number of bytes in the history buffer, or 0 - * to delete the buffer completely. - * Output: - * return int 0 - OK. - * 1 - Insufficient memory (the previous buffer - * will have been retained). No error message - * will be displayed. - */ -int _glh_resize_history(GlHistory *glh, size_t bufsize) -{ - GlLineNode *node; /* A line location node in the list of lines */ - GlLineNode *prev; /* The line location node preceding 'node' */ -/* - * Check the arguments. - */ - if(!glh) - return bufsize > 0; -/* - * If the new size doesn't differ from the existing size, do nothing. - */ - if(glh->buflen == bufsize) - return 0; -/* - * Cancel any ongoing search. - */ - (void) _glh_cancel_search(glh); -/* - * Create a wholly new buffer? - */ - if(glh->buflen == 0) { - glh->buffer = (char *) malloc(bufsize); - if(!glh->buffer) - return 1; - glh->buflen = bufsize; -/* - * Delete an existing buffer? - */ - } else if(bufsize == 0) { - _glh_clear_history(glh, 1); - free(glh->buffer); - glh->buffer = NULL; - glh->buflen = 0; -/* - * To get here, we must be shrinking or expanding from one - * finite size to another. - */ - } else { -/* - * If we are shrinking the size of the buffer, then we first need - * to discard the oldest lines that won't fit in the new buffer. - */ - if(bufsize < glh->buflen) { - size_t nbytes = 0; /* The number of bytes used in the new buffer */ - GlLineNode *oldest; /* The oldest node to be kept */ -/* - * Searching backwards from the youngest line, find the oldest - * line for which there will be sufficient room in the new buffer. - */ - for(oldest = glh->list.tail; - oldest && (nbytes += oldest->nchar) <= bufsize; - oldest = oldest->prev) - ; -/* - * We will have gone one node too far, unless we reached the oldest line - * without exceeding the target length. - */ - if(oldest) { - nbytes -= oldest->nchar; - oldest = oldest->next; - }; -/* - * Discard the nodes that can't be retained. - */ - while(glh->list.head && glh->list.head != oldest) - _glh_discard_node(glh, glh->list.head); -/* - * If we are increasing the size of the buffer, we need to reallocate - * the buffer before shifting the lines into their new positions. - */ - } else { - char *new_buffer = (char *) realloc(glh->buffer, bufsize); - if(!new_buffer) - return 1; - glh->buffer = new_buffer; - glh->buflen = bufsize; - }; -/* - * If there are any lines to be preserved, copy the block of lines - * that precedes the end of the existing buffer to what will be - * the end of the new buffer. - */ - if(glh->list.head) { - int shift; /* The number of bytes to shift lines in the buffer */ -/* - * Get the oldest line to be kept. - */ - GlLineNode *oldest = glh->list.head; -/* - * Count the number of characters that are used in the lines that - * precede the end of the current buffer (ie. not including those - * lines that have been wrapped to the start of the buffer). - */ - int n = 0; - for(node=oldest,prev=oldest->prev; node && node->start >= oldest->start; - prev=node, node=node->next) - n += node->nchar; -/* - * Move these bytes to the end of the resized buffer. - */ - memmove(glh->buffer + bufsize - n, glh->buffer + oldest->start, n); -/* - * Adjust the buffer pointers to reflect the new locations of the moved - * lines. - */ - shift = bufsize - n - oldest->start; - for(node=prev; node && node->start >= oldest->start; node=node->prev) - node->start += shift; - }; -/* - * Shrink the buffer? - */ - if(bufsize < glh->buflen) { - char *new_buffer = (char *) realloc(glh->buffer, bufsize); - if(new_buffer) - glh->buffer = new_buffer; - glh->buflen = bufsize; /* Mark it as shrunk, regardless of success */ - }; - }; - return 0; -} - -/*....................................................................... - * Set an upper limit to the number of lines that can be recorded in the - * history list, or remove a previously specified limit. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * max_lines int The maximum number of lines to allow, or -1 to - * cancel a previous limit and allow as many lines - * as will fit in the current history buffer size. - */ -void _glh_limit_history(GlHistory *glh, int max_lines) -{ - if(!glh) - return; -/* - * Apply a new limit? - */ - if(max_lines >= 0 && max_lines != glh->max_lines) { -/* - * Count successively older lines until we reach the start of the - * list, or until we have seen max_lines lines (at which point 'node' - * will be line number max_lines+1). - */ - int nline = 0; - GlLineNode *node; - for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev) - ; -/* - * Discard any lines that exceed the limit. - */ - if(node) { - GlLineNode *oldest = node->next; /* The oldest line to be kept */ -/* - * Delete nodes from the head of the list until we reach the node that - * is to be kept. - */ - while(glh->list.head && glh->list.head != oldest) - _glh_discard_node(glh, glh->list.head); - }; - }; -/* - * Record the new limit. - */ - glh->max_lines = max_lines; - return; -} - -/*....................................................................... - * Discard either all history, or the history associated with the current - * history group. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * all_groups int If true, clear all of the history. If false, - * clear only the stored lines associated with the - * currently selected history group. - */ -void _glh_clear_history(GlHistory *glh, int all_groups) -{ -/* - * Check the arguments. - */ - if(!glh) - return; -/* - * Cancel any ongoing search. - */ - (void) _glh_cancel_search(glh); -/* - * Delete all history lines regardless of group? - */ - if(all_groups) { - _rst_FreeList(glh->list.node_mem); - glh->list.head = glh->list.tail = NULL; - glh->nline = 0; - glh->id_node = NULL; -/* - * Just delete lines of the current group? - */ - } else { - GlLineNode *node; /* The line node being checked */ - GlLineNode *prev; /* The line node that precedes 'node' */ - GlLineNode *next; /* The line node that follows 'node' */ -/* - * Search out and delete the line nodes of the current group. - */ - for(node=glh->list.head; node; node=next) { -/* - * Keep a record of the following node before we delete the current - * node. - */ - next = node->next; -/* - * Discard this node? - */ - if(node->group == glh->group) - _glh_discard_node(glh, node); - }; -/* - * If there are any lines left, and we deleted any lines, there will - * be gaps in the buffer. These need to be removed. - */ - if(glh->list.head) { - int epos; /* The index of the last used element in the buffer */ -/* - * Find the line nearest the end of the buffer. - */ - GlLineNode *enode; - for(node=glh->list.head, prev=NULL; - node && node->start >= glh->list.head->start; - prev=node, node = node->next) - ; - enode = prev; -/* - * Move the end line to abutt the end of the buffer, and remove gaps - * between the lines that precede it. - */ - epos = glh->buflen; - for(node=enode; node; node=node->prev) { - int shift = epos - (node->start + node->nchar); - if(shift) { - memmove(glh->buffer + node->start + shift, - glh->buffer + node->start, node->nchar); - node->start += shift; - }; - epos = node->start; - }; -/* - * Move the first line in the buffer to the start of the buffer, and - * remove gaps between the lines that follow it. - */ - epos = 0; - for(node=enode ? enode->next : NULL; node; node=node->next) { - int shift = epos - node->start; - if(shift) { - memmove(glh->buffer + node->start + shift, - glh->buffer + node->start, node->nchar); - node->start += shift; - }; - epos = node->start + node->nchar; - }; - }; - }; - return; -} - -/*....................................................................... - * Temporarily enable or disable the history list. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * enable int If true, turn on the history mechanism. If - * false, disable it. - */ -void _glh_toggle_history(GlHistory *glh, int enable) -{ - if(glh) - glh->enable = enable; -} - -/*....................................................................... - * Remove a given line location node from the history list, and return - * it to the freelist. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * node GlLineNode * The node to be removed. This must be currently - * in the list who's head is glh->list.head, or - * be NULL. - */ -static void _glh_discard_node(GlHistory *glh, GlLineNode *node) -{ - if(node) { -/* - * Make the node that precedes the node being removed point - * to the one that follows it. - */ - if(node->prev) - node->prev->next = node->next; - else - glh->list.head = node->next; -/* - * Make the node that follows the node being removed point - * to the one that precedes it. - */ - if(node->next) - node->next->prev = node->prev; - else - glh->list.tail = node->prev; -/* - * If we are deleting the node that is marked as the start point of the - * last ID search, remove the cached starting point. - */ - if(node == glh->id_node) - glh->id_node = NULL; -/* - * Return the node to the free list. - */ - node = (GlLineNode *) _del_FreeListNode(glh->list.node_mem, node); -/* - * Decrement the count of the number of lines in the buffer. - */ - glh->nline--; - }; -} - -/*....................................................................... - * Lookup the details of a given history line, given its id. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlLineID The sequential number of the line. - * Input/Output: - * line const char ** A pointer to the history line will be assigned - * to *line. - * group unsigned * The group membership of the line will be assigned - * to *group. - * timestamp time_t * The timestamp of the line will be assigned to - * *timestamp. - * Output: - * return int 0 - The requested line wasn't found. - * 1 - The line was found. - */ -int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line, - unsigned *group, time_t *timestamp) -{ - GlLineNode *node; /* The located line location node */ -/* - * Check the arguments. - */ - if(!glh) - return 0; -/* - * Search for the line that has the specified ID. - */ - node = _glh_find_id(glh, (GlhLineID) id); -/* - * Not found? - */ - if(!node) - return 0; -/* - * Return the details of the line. - */ - if(line) - *line = glh->buffer + node->start; - if(group) - *group = node->group; - if(timestamp) - *timestamp = node->timestamp; - return 1; -} - -/*....................................................................... - * Lookup a node in the history list by its ID. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * id GlhLineID The ID of the line to be returned. - * Output: - * return GlLIneNode * The located node, or NULL if not found. - */ -static GlLineNode *_glh_find_id(GlHistory *glh, GlhLineID id) -{ - GlLineNode *node; /* The node being checked */ -/* - * Is history enabled? - */ - if(!glh->enable || !glh->list.head) - return NULL; -/* - * If possible, start at the end point of the last ID search. - * Otherwise start from the head of the list. - */ - node = glh->id_node; - if(!node) - node = glh->list.head; -/* - * Search forwards from 'node'? - */ - if(node->id < id) { - while(node && node->id != id) - node = node->next; - glh->id_node = node ? node : glh->list.tail; -/* - * Search backwards from 'node'? - */ - } else { - while(node && node->id != id) - node = node->prev; - glh->id_node = node ? node : glh->list.head; - }; -/* - * Return the located node (this will be NULL if the ID wasn't found). - */ - return node; -} - -/*....................................................................... - * Query the state of the history list. Note that any of the input/output - * pointers can be specified as NULL. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * enabled int * If history is enabled, *enabled will be - * set to 1. Otherwise it will be assigned 0. - * group unsigned * The current history group ID will be assigned - * to *group. - * max_lines int * The currently requested limit on the number - * of history lines in the list, or -1 if - * unlimited. - */ -void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group, - int *max_lines) -{ - if(glh) { - if(enabled) - *enabled = glh->enable; - if(group) - *group = glh->group; - if(max_lines) - *max_lines = glh->max_lines; - }; -} - -/*....................................................................... - * Get the range of lines in the history buffer. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * oldest unsigned long * The sequential entry number of the oldest - * line in the history list will be assigned - * to *oldest, unless there are no lines, in - * which case 0 will be assigned. - * newest unsigned long * The sequential entry number of the newest - * line in the history list will be assigned - * to *newest, unless there are no lines, in - * which case 0 will be assigned. - * nlines int * The number of lines currently in the history - * list. - */ -void _glh_range_of_history(GlHistory *glh, unsigned long *oldest, - unsigned long *newest, int *nlines) -{ - if(glh) { - if(oldest) - *oldest = glh->list.head ? glh->list.head->id : 0; - if(newest) - *newest = glh->list.tail ? glh->list.tail->id : 0; - if(nlines) - *nlines = glh->nline; - }; -} - -/*....................................................................... - * Return the size of the history buffer and the amount of the - * buffer that is currently in use. - * - * Input: - * glh GlHistory * The input-line history maintenance object. - * Input/Output: - * buff_size size_t * The size of the history buffer (bytes). - * buff_used size_t * The amount of the history buffer that - * is currently occupied (bytes). - */ -void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used) -{ - if(glh) { - if(buff_size) - *buff_size = glh->buflen; -/* - * Determine the amount of buffer space that is currently occupied. - */ - if(buff_used) { - size_t used = 0; - GlLineNode *node; - for(node=glh->list.head; node; node=node->next) - used += node->nchar; - *buff_used = used; - }; - }; -} |