2 * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, and/or sell copies of the Software, and to permit persons
11 * to whom the Software is furnished to do so, provided that the above
12 * copyright notice(s) and this permission notice appear in all copies of
13 * the Software and that both the above copyright notice(s) and this
14 * permission notice appear in supporting documentation.
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21 * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22 * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24 * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26 * Except as contained in this notice, the name of a copyright holder
27 * shall not be used in advertising or otherwise to promote the sale, use
28 * or other dealings in this Software without prior written authorization
29 * of the copyright holder.
33 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
34 * Use is subject to license terms.
37 #pragma ident "%Z%%M% %I% %E% SMI"
52 * History lines are split into sub-strings of GLH_SEG_SIZE
53 * characters. To avoid wasting space in the GlhLineSeg structure,
54 * this should be a multiple of the size of a pointer.
56 #define GLH_SEG_SIZE 16
59 * GlhLineSeg structures contain fixed sized segments of a larger
60 * string. These are linked into lists to record strings, with all but
61 * the last segment having GLH_SEG_SIZE characters. The last segment
62 * of a string is terminated within the GLH_SEG_SIZE characters with a
65 typedef struct GlhLineSeg GlhLineSeg
;
67 GlhLineSeg
*next
; /* The next sub-string of the history line */
68 char s
[GLH_SEG_SIZE
]; /* The sub-string. Beware that only the final */
69 /* substring of a line, as indicated by 'next' */
70 /* being NULL, is '\0' terminated. */
74 * History lines are recorded in a hash table, such that repeated
75 * lines are stored just once.
77 * Start by defining the size of the hash table. This should be a
80 #define GLH_HASH_SIZE 113
82 typedef struct GlhHashBucket GlhHashBucket
;
85 * Each history line will be represented in the hash table by a
86 * structure of the following type.
88 typedef struct GlhHashNode GlhHashNode
;
90 GlhHashBucket
*bucket
; /* The parent hash-table bucket of this node */
91 GlhHashNode
*next
; /* The next in the list of nodes within the */
92 /* parent hash-table bucket. */
93 GlhLineSeg
*head
; /* The list of sub-strings which make up a line */
94 int len
; /* The length of the line, excluding any '\0' */
95 int used
; /* The number of times this string is pointed to by */
96 /* the time-ordered list of history lines. */
97 int reported
; /* A flag that is used when searching to ensure that */
98 /* a line isn't reported redundantly. */
102 * How many new GlhHashNode elements should be allocated at a time?
104 #define GLH_HASH_INCR 50
106 static int _glh_is_line(GlhHashNode
*hash
, const char *line
, size_t n
);
107 static int _glh_line_matches_prefix(GlhHashNode
*line
, GlhHashNode
*prefix
);
108 static void _glh_return_line(GlhHashNode
*hash
, char *line
, size_t dim
);
111 * All history lines which hash to a given bucket in the hash table, are
112 * recorded in a structure of the following type.
114 struct GlhHashBucket
{
115 GlhHashNode
*lines
; /* The list of history lines which fall in this bucket */
118 static GlhHashBucket
*glh_find_bucket(GlHistory
*glh
, const char *line
,
120 static GlhHashNode
*glh_find_hash_node(GlhHashBucket
*bucket
, const char *line
,
124 FreeList
*node_mem
; /* A free-list of GlhHashNode structures */
125 GlhHashBucket bucket
[GLH_HASH_SIZE
]; /* The buckets of the hash table */
129 * GlhLineNode's are used to record history lines in time order.
131 typedef struct GlhLineNode GlhLineNode
;
133 long id
; /* The unique identifier of this history line */
134 time_t timestamp
; /* The time at which the line was archived */
135 unsigned group
; /* The identifier of the history group to which the */
136 /* the line belongs. */
137 GlhLineNode
*next
; /* The next youngest line in the list */
138 GlhLineNode
*prev
; /* The next oldest line in the list */
139 GlhHashNode
*line
; /* The hash-table entry of the history line */
143 * The number of GlhLineNode elements per freelist block.
145 #define GLH_LINE_INCR 100
148 * Encapsulate the time-ordered list of historical lines.
151 FreeList
*node_mem
; /* A freelist of GlhLineNode objects */
152 GlhLineNode
*head
; /* The oldest line in the list */
153 GlhLineNode
*tail
; /* The newest line in the list */
157 * The _glh_lookup_history() returns copies of history lines in a
158 * dynamically allocated array. This array is initially allocated
159 * GLH_LOOKUP_SIZE bytes. If subsequently this size turns out to be
160 * too small, realloc() is used to increase its size to the required
161 * size plus GLH_LOOKUP_MARGIN. The idea of the later parameter is to
162 * reduce the number of realloc() operations needed.
164 #define GLH_LBUF_SIZE 300
165 #define GLH_LBUF_MARGIN 100
168 * Encapsulate all of the resources needed to store historical input lines.
171 ErrMsg
*err
; /* The error-reporting buffer */
172 GlhLineSeg
*buffer
; /* An array of sub-line nodes to be partitioned */
173 /* into lists of sub-strings recording input lines. */
174 int nbuff
; /* The allocated dimension of buffer[] */
175 GlhLineSeg
*unused
; /* The list of free nodes in buffer[] */
176 GlhLineList list
; /* A time ordered list of history lines */
177 GlhLineNode
*recall
; /* The last line recalled, or NULL if no recall */
178 /* session is currently active. */
179 GlhLineNode
*id_node
;/* The node at which the last ID search terminated */
180 GlhLineHash hash
; /* A hash-table of reference-counted history lines */
181 GlhHashNode
*prefix
; /* A pointer to a line containing the prefix that */
182 /* is being searched for. Note that if prefix==NULL */
183 /* and prefix_len>0, this means that no line in */
184 /* the buffer starts with the requested prefix. */
185 int prefix_len
; /* The length of the prefix being searched for. */
186 char *lbuf
; /* The array in which _glh_lookup_history() returns */
188 int lbuf_dim
; /* The allocated size of lbuf[] */
189 int nbusy
; /* The number of line segments in buffer[] that are */
190 /* currently being used to record sub-lines */
191 int nfree
; /* The number of line segments in buffer that are */
192 /* not currently being used to record sub-lines */
193 unsigned long seq
; /* The next ID to assign to a line node */
194 unsigned group
; /* The identifier of the current history group */
195 int nline
; /* The number of lines currently in the history list */
196 int max_lines
; /* Either -1 or a ceiling on the number of lines */
197 int enable
; /* If false, ignore history additions and lookups */
200 #ifndef WITHOUT_FILE_SYSTEM
201 static int _glh_cant_load_history(GlHistory
*glh
, const char *filename
,
202 int lineno
, const char *message
, FILE *fp
);
203 static int _glh_cant_save_history(GlHistory
*glh
, const char *message
,
204 const char *filename
, FILE *fp
);
205 static int _glh_write_timestamp(FILE *fp
, time_t timestamp
);
206 static int _glh_decode_timestamp(char *string
, char **endp
, time_t *timestamp
);
208 static void _glh_discard_line(GlHistory
*glh
, GlhLineNode
*node
);
209 static GlhLineNode
*_glh_find_id(GlHistory
*glh
, GlhLineID id
);
210 static GlhHashNode
*_glh_acquire_copy(GlHistory
*glh
, const char *line
,
212 static GlhHashNode
*_glh_discard_copy(GlHistory
*glh
, GlhHashNode
*hnode
);
213 static int _glh_prepare_for_recall(GlHistory
*glh
, char *line
);
216 * The following structure and functions are used to iterate through
217 * the characters of a segmented history line.
220 GlhLineSeg
*seg
; /* The line segment that the next character will */
221 /* be returned from. */
222 int posn
; /* The index in the above line segment, containing */
223 /* the next unread character. */
224 char c
; /* The current character in the input line */
226 static void glh_init_stream(GlhLineStream
*str
, GlhHashNode
*line
);
227 static void glh_step_stream(GlhLineStream
*str
);
230 * See if search prefix contains any globbing characters.
232 static int glh_contains_glob(GlhHashNode
*prefix
);
234 * Match a line against a search pattern.
236 static int glh_line_matches_glob(GlhLineStream
*lstr
, GlhLineStream
*pstr
);
237 static int glh_matches_range(char c
, GlhLineStream
*pstr
);
239 /*.......................................................................
240 * Create a line history maintenance object.
243 * buflen size_t The number of bytes to allocate to the
244 * buffer that is used to record all of the
245 * most recent lines of user input that will fit.
246 * If buflen==0, no buffer will be allocated.
248 * return GlHistory * The new object, or NULL on error.
250 GlHistory
*_new_GlHistory(size_t buflen
)
252 GlHistory
*glh
; /* The object to be returned */
255 * Allocate the container.
257 glh
= (GlHistory
*) malloc(sizeof(GlHistory
));
263 * Before attempting any operation that might fail, initialize the
264 * container at least up to the point at which it can safely be passed
265 * to _del_GlHistory().
269 glh
->nbuff
= (buflen
+GLH_SEG_SIZE
-1) / GLH_SEG_SIZE
;
271 glh
->list
.node_mem
= NULL
;
272 glh
->list
.head
= glh
->list
.tail
= NULL
;
275 glh
->hash
.node_mem
= NULL
;
276 for(i
=0; i
<GLH_HASH_SIZE
; i
++)
277 glh
->hash
.bucket
[i
].lines
= NULL
;
282 glh
->nfree
= glh
->nbuff
;
289 * Allocate a place to record error messages.
291 glh
->err
= _new_ErrMsg();
293 return _del_GlHistory(glh
);
295 * Allocate the buffer, if required.
298 glh
->nbuff
= glh
->nfree
;
299 glh
->buffer
= (GlhLineSeg
*) malloc(sizeof(GlhLineSeg
) * glh
->nbuff
);
302 return _del_GlHistory(glh
);
305 * All nodes of the buffer are currently unused, so link them all into
306 * a list and make glh->unused point to the head of this list.
308 glh
->unused
= glh
->buffer
;
309 for(i
=0; i
<glh
->nbuff
-1; i
++) {
310 GlhLineSeg
*seg
= glh
->unused
+ i
;
313 glh
->unused
[i
].next
= NULL
;
316 * Allocate the GlhLineNode freelist.
318 glh
->list
.node_mem
= _new_FreeList(sizeof(GlhLineNode
), GLH_LINE_INCR
);
319 if(!glh
->list
.node_mem
)
320 return _del_GlHistory(glh
);
322 * Allocate the GlhHashNode freelist.
324 glh
->hash
.node_mem
= _new_FreeList(sizeof(GlhLineNode
), GLH_HASH_INCR
);
325 if(!glh
->hash
.node_mem
)
326 return _del_GlHistory(glh
);
328 * Allocate the array that _glh_lookup_history() uses to return a
329 * copy of a given history line. This will be resized when necessary.
331 glh
->lbuf_dim
= GLH_LBUF_SIZE
;
332 glh
->lbuf
= (char *) malloc(glh
->lbuf_dim
);
335 return _del_GlHistory(glh
);
340 /*.......................................................................
341 * Delete a GlHistory object.
344 * glh GlHistory * The object to be deleted.
346 * return GlHistory * The deleted object (always NULL).
348 GlHistory
*_del_GlHistory(GlHistory
*glh
)
352 * Delete the error-message buffer.
354 glh
->err
= _del_ErrMsg(glh
->err
);
364 * Delete the freelist of GlhLineNode's.
366 glh
->list
.node_mem
= _del_FreeList(glh
->list
.node_mem
, 1);
368 * The contents of the list were deleted by deleting the freelist.
370 glh
->list
.head
= NULL
;
371 glh
->list
.tail
= NULL
;
373 * Delete the freelist of GlhHashNode's.
375 glh
->hash
.node_mem
= _del_FreeList(glh
->hash
.node_mem
, 1);
377 * Delete the lookup buffer.
382 * Delete the container.
389 /*.......................................................................
390 * Append a new line to the history list, deleting old lines to make
394 * glh GlHistory * The input-line history maintenance object.
395 * line char * The line to be archived.
396 * force int Unless this flag is non-zero, empty lines aren't
397 * archived. This flag requests that the line be
398 * archived regardless.
403 int _glh_add_history(GlHistory
*glh
, const char *line
, int force
)
405 int slen
; /* The length of the line to be recorded (minus the '\0') */
406 int empty
; /* True if the string is empty */
407 const char *nlptr
; /* A pointer to a newline character in line[] */
408 GlhHashNode
*hnode
; /* The hash-table node of the line */
409 GlhLineNode
*lnode
; /* A node in the time-ordered list of lines */
412 * Check the arguments.
419 * Is history enabled?
421 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
424 * Cancel any ongoing search.
426 if(_glh_cancel_search(glh
))
429 * How long is the string to be recorded, being careful not to include
430 * any terminating '\n' character.
432 nlptr
= strchr(line
, '\n');
434 slen
= (nlptr
- line
);
441 for(i
=0; i
<slen
&& empty
; i
++)
442 empty
= isspace((int)(unsigned char) line
[i
]);
444 * If the line is empty, don't add it to the buffer unless explicitly
450 * Has an upper limit to the number of lines in the history list been
453 if(glh
->max_lines
>= 0) {
455 * If necessary, remove old lines until there is room to add one new
456 * line without exceeding the specified line limit.
458 while(glh
->nline
> 0 && glh
->nline
>= glh
->max_lines
)
459 _glh_discard_line(glh
, glh
->list
.head
);
461 * We can't archive the line if the maximum number of lines allowed is
464 if(glh
->max_lines
== 0)
468 * Unless already stored, store a copy of the line in the history buffer,
469 * then return a reference-counted hash-node pointer to this copy.
471 hnode
= _glh_acquire_copy(glh
, line
, slen
);
473 _err_record_msg(glh
->err
, "No room to store history line", END_ERR_MSG
);
478 * Allocate a new node in the time-ordered list of lines.
480 lnode
= (GlhLineNode
*) _new_FreeListNode(glh
->list
.node_mem
);
482 * If a new line-node couldn't be allocated, discard our copy of the
483 * stored line before reporting the error.
486 hnode
= _glh_discard_copy(glh
, hnode
);
487 _err_record_msg(glh
->err
, "No room to store history line", END_ERR_MSG
);
492 * Record a pointer to the hash-table record of the line in the new
495 lnode
->id
= glh
->seq
++;
496 lnode
->timestamp
= time(NULL
);
497 lnode
->group
= glh
->group
;
500 * Append the new node to the end of the time-ordered list.
503 glh
->list
.tail
->next
= lnode
;
505 glh
->list
.head
= lnode
;
507 lnode
->prev
= glh
->list
.tail
;
508 glh
->list
.tail
= lnode
;
510 * Record the addition of a line to the list.
516 /*.......................................................................
517 * Recall the next oldest line that has the search prefix last recorded
518 * by _glh_search_prefix().
521 * glh GlHistory * The input-line history maintenance object.
522 * line char * The input line buffer. On input this should contain
523 * the current input line, and on output, if anything
524 * was found, its contents will have been replaced
525 * with the matching line.
526 * dim size_t The allocated dimension of the line buffer.
528 * return char * A pointer to line[0], or NULL if not found.
530 char *_glh_find_backwards(GlHistory
*glh
, char *line
, size_t dim
)
532 GlhLineNode
*node
; /* The line location node being checked */
533 GlhHashNode
*old_line
; /* The previous recalled line */
535 * Check the arguments.
539 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
544 * Is history enabled?
546 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
549 * Check the line dimensions.
551 if(dim
< strlen(line
) + 1) {
552 _err_record_msg(glh
->err
, "'dim' argument inconsistent with strlen(line)",
558 * Preserve the input line if needed.
560 if(_glh_prepare_for_recall(glh
, line
))
563 * From where should we start the search?
566 node
= glh
->recall
->prev
;
567 old_line
= glh
->recall
->line
;
569 node
= glh
->list
.tail
;
573 * Search backwards through the list for the first match with the
574 * prefix string that differs from the last line that was recalled.
576 while(node
&& (node
->group
!= glh
->group
|| node
->line
== old_line
||
577 !_glh_line_matches_prefix(node
->line
, glh
->prefix
)))
580 * Was a matching line found?
584 * Recall the found node as the starting point for subsequent
589 * Copy the matching line into the provided line buffer.
591 _glh_return_line(node
->line
, line
, dim
);
598 * No match was found.
603 /*.......................................................................
604 * Recall the next newest line that has the search prefix last recorded
605 * by _glh_search_prefix().
608 * glh GlHistory * The input-line history maintenance object.
609 * line char * The input line buffer. On input this should contain
610 * the current input line, and on output, if anything
611 * was found, its contents will have been replaced
612 * with the matching line.
613 * dim size_t The allocated dimensions of the line buffer.
615 * return char * The line requested, or NULL if no matching line
618 char *_glh_find_forwards(GlHistory
*glh
, char *line
, size_t dim
)
620 GlhLineNode
*node
; /* The line location node being checked */
621 GlhHashNode
*old_line
; /* The previous recalled line */
623 * Check the arguments.
627 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
632 * Is history enabled?
634 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
637 * Check the line dimensions.
639 if(dim
< strlen(line
) + 1) {
640 _err_record_msg(glh
->err
, "'dim' argument inconsistent with strlen(line)",
646 * From where should we start the search?
649 node
= glh
->recall
->next
;
650 old_line
= glh
->recall
->line
;
655 * Search forwards through the list for the first match with the
658 while(node
&& (node
->group
!= glh
->group
|| node
->line
== old_line
||
659 !_glh_line_matches_prefix(node
->line
, glh
->prefix
)))
662 * Was a matching line found?
666 * Copy the matching line into the provided line buffer.
668 _glh_return_line(node
->line
, line
, dim
);
670 * Record the starting point of the next search.
674 * If we just returned the line that was being entered when the search
675 * session first started, cancel the search.
677 if(node
== glh
->list
.tail
)
678 _glh_cancel_search(glh
);
680 * Return the matching line to the user.
685 * No match was found.
690 /*.......................................................................
691 * If a search is in progress, cancel it.
693 * This involves discarding the line that was temporarily saved by
694 * _glh_find_backwards() when the search was originally started,
695 * and reseting the search iteration pointer to NULL.
698 * glh GlHistory * The input-line history maintenance object.
703 int _glh_cancel_search(GlHistory
*glh
)
706 * Check the arguments.
713 * If there wasn't a search in progress, do nothing.
718 * Reset the search pointers. Note that it is essential to set
719 * glh->recall to NULL before calling _glh_discard_line(), to avoid an
720 * infinite recursion.
724 * Delete the node of the preserved line.
726 _glh_discard_line(glh
, glh
->list
.tail
);
730 /*.......................................................................
731 * Set the prefix of subsequent history searches.
734 * glh GlHistory * The input-line history maintenance object.
735 * line const char * The command line who's prefix is to be used.
736 * prefix_len int The length of the prefix.
741 int _glh_search_prefix(GlHistory
*glh
, const char *line
, int prefix_len
)
744 * Check the arguments.
751 * Is history enabled?
753 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
756 * Discard any existing prefix.
758 glh
->prefix
= _glh_discard_copy(glh
, glh
->prefix
);
760 * Only store a copy of the prefix string if it isn't a zero-length string.
764 * Get a reference-counted copy of the prefix from the history cache buffer.
766 glh
->prefix
= _glh_acquire_copy(glh
, line
, prefix_len
);
768 * Was there insufficient buffer space?
771 _err_record_msg(glh
->err
, "The search prefix is too long to store",
780 /*.......................................................................
781 * Recall the oldest recorded line.
784 * glh GlHistory * The input-line history maintenance object.
785 * line char * The input line buffer. On input this should contain
786 * the current input line, and on output, its contents
787 * will have been replaced with the oldest line.
788 * dim size_t The allocated dimensions of the line buffer.
790 * return char * A pointer to line[0], or NULL if not found.
792 char *_glh_oldest_line(GlHistory
*glh
, char *line
, size_t dim
)
794 GlhLineNode
*node
; /* The line location node being checked */
796 * Check the arguments.
800 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
805 * Is history enabled?
807 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
810 * Check the line dimensions.
812 if(dim
< strlen(line
) + 1) {
813 _err_record_msg(glh
->err
, "'dim' argument inconsistent with strlen(line)",
819 * Preserve the input line if needed.
821 if(_glh_prepare_for_recall(glh
, line
))
824 * Locate the oldest line that belongs to the current group.
826 for(node
=glh
->list
.head
; node
&& node
->group
!= glh
->group
;
835 * Record the above node as the starting point for subsequent
840 * Copy the recalled line into the provided line buffer.
842 _glh_return_line(node
->line
, line
, dim
);
844 * If we just returned the line that was being entered when the search
845 * session first started, cancel the search.
847 if(node
== glh
->list
.tail
)
848 _glh_cancel_search(glh
);
852 /*.......................................................................
853 * Recall the line that was being entered when the search started.
856 * glh GlHistory * The input-line history maintenance object.
857 * line char * The input line buffer. On input this should contain
858 * the current input line, and on output, its contents
859 * will have been replaced with the line that was
860 * being entered when the search was started.
861 * dim size_t The allocated dimensions of the line buffer.
863 * return char * A pointer to line[0], or NULL if not found.
865 char *_glh_current_line(GlHistory
*glh
, char *line
, size_t dim
)
868 * Check the arguments.
872 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
877 * If history isn't enabled, or no history search has yet been started,
880 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0 || !glh
->recall
)
883 * Check the line dimensions.
885 if(dim
< strlen(line
) + 1) {
886 _err_record_msg(glh
->err
, "'dim' argument inconsistent with strlen(line)",
892 * Copy the recalled line into the provided line buffer.
894 _glh_return_line(glh
->list
.tail
->line
, line
, dim
);
896 * Since we have returned to the starting point of the search, cancel it.
898 _glh_cancel_search(glh
);
902 /*.......................................................................
903 * Query the id of a history line offset by a given number of lines from
904 * the one that is currently being recalled. If a recall session isn't
905 * in progress, or the offset points outside the history list, 0 is
909 * glh GlHistory * The input-line history maintenance object.
910 * offset int The line offset (0 for the current line, < 0
911 * for an older line, > 0 for a newer line.
913 * return GlhLineID The identifier of the line that is currently
914 * being recalled, or 0 if no recall session is
915 * currently in progress.
917 GlhLineID
_glh_line_id(GlHistory
*glh
, int offset
)
919 GlhLineNode
*node
; /* The line location node being checked */
921 * Is history enabled?
923 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
926 * Search forward 'offset' lines to find the required line.
929 for(node
=glh
->recall
; node
&& offset
!= 0; node
=node
->next
) {
930 if(node
->group
== glh
->group
)
934 for(node
=glh
->recall
; node
&& offset
!= 0; node
=node
->prev
) {
935 if(node
->group
== glh
->group
)
939 return node
? node
->id
: 0;
942 /*.......................................................................
943 * Recall a line by its history buffer ID. If the line is no longer
944 * in the buffer, or the id is zero, NULL is returned.
947 * glh GlHistory * The input-line history maintenance object.
948 * id GlhLineID The ID of the line to be returned.
949 * line char * The input line buffer. On input this should contain
950 * the current input line, and on output, its contents
951 * will have been replaced with the saved line.
952 * dim size_t The allocated dimensions of the line buffer.
954 * return char * A pointer to line[0], or NULL if not found.
956 char *_glh_recall_line(GlHistory
*glh
, GlhLineID id
, char *line
, size_t dim
)
958 GlhLineNode
*node
; /* The line location node being checked */
960 * Is history enabled?
962 if(!glh
->enable
|| !glh
->buffer
|| glh
->max_lines
== 0)
965 * Preserve the input line if needed.
967 if(_glh_prepare_for_recall(glh
, line
))
970 * Search for the specified line.
972 node
= _glh_find_id(glh
, id
);
976 if(!node
|| node
->group
!= glh
->group
)
979 * Record the node of the matching line as the starting point
980 * for subsequent searches.
984 * Copy the recalled line into the provided line buffer.
986 _glh_return_line(node
->line
, line
, dim
);
990 /*.......................................................................
991 * Save the current history in a specified file.
994 * glh GlHistory * The input-line history maintenance object.
995 * filename const char * The name of the new file to record the
997 * comment const char * Extra information such as timestamps will
998 * be recorded on a line started with this
999 * string, the idea being that the file can
1000 * double as a command file. Specify "" if
1002 * max_lines int The maximum number of lines to save, or -1
1003 * to save all of the lines in the history
1006 * return int 0 - OK.
1009 int _glh_save_history(GlHistory
*glh
, const char *filename
, const char *comment
,
1012 #ifdef WITHOUT_FILE_SYSTEM
1013 _err_record_msg(glh
->err
, "Can't save history without filesystem access",
1018 FILE *fp
; /* The output file */
1019 GlhLineNode
*node
; /* The line being saved */
1020 GlhLineNode
*head
; /* The head of the list of lines to be saved */
1021 GlhLineSeg
*seg
; /* One segment of a line being saved */
1023 * Check the arguments.
1025 if(!glh
|| !filename
|| !comment
) {
1027 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
1032 * Attempt to open the specified file.
1034 fp
= fopen(filename
, "w");
1036 return _glh_cant_save_history(glh
, "Can't open", filename
, NULL
);
1038 * If a ceiling on the number of lines to save was specified, count
1039 * that number of lines backwards, to find the first line to be saved.
1042 if(max_lines
>= 0) {
1043 for(head
=glh
->list
.tail
; head
&& --max_lines
> 0; head
=head
->prev
)
1047 head
= glh
->list
.head
;
1049 * Write the contents of the history buffer to the history file, writing
1050 * associated data such as timestamps, to a line starting with the
1051 * specified comment string.
1053 for(node
=head
; node
; node
=node
->next
) {
1055 * Write peripheral information associated with the line, as a comment.
1057 if(fprintf(fp
, "%s ", comment
) < 0 ||
1058 _glh_write_timestamp(fp
, node
->timestamp
) ||
1059 fprintf(fp
, " %u\n", node
->group
) < 0) {
1060 return _glh_cant_save_history(glh
, "Error writing", filename
, fp
);
1063 * Write the history line.
1065 for(seg
=node
->line
->head
; seg
; seg
=seg
->next
) {
1066 size_t slen
= seg
->next
? GLH_SEG_SIZE
: strlen(seg
->s
);
1067 if(fwrite(seg
->s
, sizeof(char), slen
, fp
) != slen
)
1068 return _glh_cant_save_history(glh
, "Error writing", filename
, fp
);
1073 * Close the history file.
1075 if(fclose(fp
) == EOF
)
1076 return _glh_cant_save_history(glh
, "Error writing", filename
, NULL
);
1081 #ifndef WITHOUT_FILE_SYSTEM
1082 /*.......................................................................
1083 * This is a private error return function of _glh_save_history(). It
1084 * composes an error report in the error buffer, composed using
1085 * sprintf("%s %s (%s)", message, filename, strerror(errno)). It then
1086 * closes fp and returns the error return code of _glh_save_history().
1089 * glh GlHistory * The input-line history maintenance object.
1090 * message const char * A message to be followed by the filename.
1091 * filename const char * The name of the offending output file.
1092 * fp FILE * The stream to be closed (send NULL if not
1095 * return int Always 1.
1097 static int _glh_cant_save_history(GlHistory
*glh
, const char *message
,
1098 const char *filename
, FILE *fp
)
1100 _err_record_msg(glh
->err
, message
, filename
, " (",
1101 strerror(errno
), ")", END_ERR_MSG
);
1107 /*.......................................................................
1108 * Write a timestamp to a given stdio stream, in the format
1112 * fp FILE * The stream to write to.
1113 * timestamp time_t The timestamp to be written.
1115 * return int 0 - OK.
1118 static int _glh_write_timestamp(FILE *fp
, time_t timestamp
)
1120 struct tm
*t
; /* THe broken-down calendar time */
1122 * Get the calendar components corresponding to the given timestamp.
1124 if(timestamp
< 0 || (t
= localtime(×tamp
)) == NULL
) {
1125 if(fprintf(fp
, "?") < 0)
1130 * Write the calendar time as yyyymmddhhmmss.
1132 if(fprintf(fp
, "%04d%02d%02d%02d%02d%02d", t
->tm_year
+ 1900, t
->tm_mon
+ 1,
1133 t
->tm_mday
, t
->tm_hour
, t
->tm_min
, t
->tm_sec
) < 0)
1140 /*.......................................................................
1141 * Restore previous history lines from a given file.
1144 * glh GlHistory * The input-line history maintenance object.
1145 * filename const char * The name of the file to read from.
1146 * comment const char * The same comment string that was passed to
1147 * _glh_save_history() when this file was
1149 * line char * A buffer into which lines can be read.
1150 * dim size_t The allocated dimension of line[].
1152 * return int 0 - OK.
1155 int _glh_load_history(GlHistory
*glh
, const char *filename
, const char *comment
,
1156 char *line
, size_t dim
)
1158 #ifdef WITHOUT_FILE_SYSTEM
1159 _err_record_msg(glh
->err
, "Can't load history without filesystem access",
1164 FILE *fp
; /* The output file */
1165 size_t comment_len
; /* The length of the comment string */
1166 time_t timestamp
; /* The timestamp of the history line */
1167 unsigned group
; /* The identifier of the history group to which */
1168 /* the line belongs. */
1169 int lineno
; /* The line number being read */
1171 * Check the arguments.
1173 if(!glh
|| !filename
|| !comment
|| !line
) {
1175 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
1180 * Measure the length of the comment string.
1182 comment_len
= strlen(comment
);
1184 * Clear the history list.
1186 _glh_clear_history(glh
, 1);
1188 * Attempt to open the specified file. Don't treat it as an error
1189 * if the file doesn't exist.
1191 fp
= fopen(filename
, "r");
1195 * Attempt to read each line and preceding peripheral info, and add these
1196 * to the history list.
1198 for(lineno
=1; fgets(line
, dim
, fp
) != NULL
; lineno
++) {
1199 char *lptr
; /* A pointer into the input line */
1201 * Check that the line starts with the comment string.
1203 if(strncmp(line
, comment
, comment_len
) != 0) {
1204 return _glh_cant_load_history(glh
, filename
, lineno
,
1205 "Corrupt history parameter line", fp
);
1208 * Skip spaces and tabs after the comment.
1210 for(lptr
=line
+comment_len
; *lptr
&& (*lptr
==' ' || *lptr
=='\t'); lptr
++)
1213 * The next word must be a timestamp.
1215 if(_glh_decode_timestamp(lptr
, &lptr
, ×tamp
)) {
1216 return _glh_cant_load_history(glh
, filename
, lineno
,
1217 "Corrupt timestamp", fp
);
1220 * Skip spaces and tabs.
1222 while(*lptr
==' ' || *lptr
=='\t')
1225 * The next word must be an unsigned integer group number.
1227 group
= (int) strtoul(lptr
, &lptr
, 10);
1228 if(*lptr
!= ' ' && *lptr
!= '\n') {
1229 return _glh_cant_load_history(glh
, filename
, lineno
,
1230 "Corrupt group id", fp
);
1233 * Skip spaces and tabs.
1235 while(*lptr
==' ' || *lptr
=='\t')
1238 * There shouldn't be anything left on the line.
1241 return _glh_cant_load_history(glh
, filename
, lineno
,
1242 "Corrupt parameter line", fp
);
1245 * Now read the history line itself.
1248 if(fgets(line
, dim
, fp
) == NULL
)
1249 return _glh_cant_load_history(glh
, filename
, lineno
, "Read error", fp
);
1251 * Append the line to the history buffer.
1253 if(_glh_add_history(glh
, line
, 1)) {
1254 return _glh_cant_load_history(glh
, filename
, lineno
,
1255 "Insufficient memory to record line", fp
);
1258 * Record the group and timestamp information along with the line.
1260 if(glh
->list
.tail
) {
1261 glh
->list
.tail
->timestamp
= timestamp
;
1262 glh
->list
.tail
->group
= group
;
1273 #ifndef WITHOUT_FILE_SYSTEM
1274 /*.......................................................................
1275 * This is a private error return function of _glh_load_history().
1277 static int _glh_cant_load_history(GlHistory
*glh
, const char *filename
,
1278 int lineno
, const char *message
, FILE *fp
)
1282 * Convert the line number to a string.
1284 snprintf(lnum
, sizeof(lnum
), "%d", lineno
);
1286 * Render an error message.
1288 _err_record_msg(glh
->err
, filename
, ":", lnum
, ":", message
, END_ERR_MSG
);
1297 /*.......................................................................
1298 * Read a timestamp from a string.
1301 * string char * The string to read from.
1303 * endp char ** On output *endp will point to the next unprocessed
1304 * character in string[].
1305 * timestamp time_t * The timestamp will be assigned to *t.
1307 * return int 0 - OK.
1310 static int _glh_decode_timestamp(char *string
, char **endp
, time_t *timestamp
)
1312 unsigned year
,month
,day
,hour
,min
,sec
; /* Calendar time components */
1315 * There are 14 characters in the date format yyyymmddhhmmss.
1318 char timestr
[TSLEN
+1]; /* The timestamp part of the string */
1320 * If the time wasn't available at the time that the line was recorded
1321 * it will have been written as "?". Check for this before trying
1322 * to read the timestamp.
1324 if(string
[0] == '\?') {
1330 * The timestamp is expected to be written in the form yyyymmddhhmmss.
1332 if(strlen(string
) < TSLEN
) {
1337 * Copy the timestamp out of the string.
1339 strncpy(timestr
, string
, TSLEN
);
1340 timestr
[TSLEN
] = '\0';
1342 * Decode the timestamp.
1344 if(sscanf(timestr
, "%4u%2u%2u%2u%2u%2u", &year
, &month
, &day
, &hour
, &min
,
1350 * Advance the string pointer over the successfully read timestamp.
1352 *endp
= string
+ TSLEN
;
1354 * Copy the read values into a struct tm.
1362 t
.tm_mon
= month
- 1;
1363 t
.tm_year
= year
- 1900;
1366 * Convert the contents of the struct tm to a time_t.
1368 *timestamp
= mktime(&t
);
1373 /*.......................................................................
1374 * Switch history groups.
1377 * glh GlHistory * The input-line history maintenance object.
1378 * group unsigned The new group identifier. This will be recorded
1379 * with subsequent history lines, and subsequent
1380 * history searches will only return lines with
1381 * this group identifier. This allows multiple
1382 * separate history lists to exist within
1383 * a single GlHistory object. Note that the
1384 * default group identifier is 0.
1386 * return int 0 - OK.
1389 int _glh_set_group(GlHistory
*glh
, unsigned group
)
1392 * Check the arguments.
1396 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
1401 * Is the group being changed?
1403 if(group
!= glh
->group
) {
1405 * Cancel any ongoing search.
1407 if(_glh_cancel_search(glh
))
1410 * Record the new group.
1417 /*.......................................................................
1418 * Query the current history group.
1421 * glh GlHistory * The input-line history maintenance object.
1423 * return unsigned The group identifier.
1425 int _glh_get_group(GlHistory
*glh
)
1427 return glh
? glh
->group
: 0;
1430 /*.......................................................................
1431 * Display the contents of the history list.
1434 * glh GlHistory * The input-line history maintenance object.
1435 * write_fn GlWriteFn * The function to call to write the line, or
1436 * 0 to discard the output.
1437 * data void * Anonymous data to pass to write_fn().
1438 * fmt const char * A format string. This can contain arbitrary
1439 * characters, which are written verbatim, plus
1440 * any of the following format directives:
1441 * %D - The date, like 2001-11-20
1442 * %T - The time of day, like 23:59:59
1443 * %N - The sequential entry number of the
1444 * line in the history buffer.
1445 * %G - The history group number of the line.
1446 * %% - A literal % character.
1447 * %H - The history line.
1448 * all_groups int If true, display history lines from all
1449 * history groups. Otherwise only display
1450 * those of the current history group.
1451 * max_lines int If max_lines is < 0, all available lines
1452 * are displayed. Otherwise only the most
1453 * recent max_lines lines will be displayed.
1455 * return int 0 - OK.
1458 int _glh_show_history(GlHistory
*glh
, GlWriteFn
*write_fn
, void *data
,
1459 const char *fmt
, int all_groups
, int max_lines
)
1461 GlhLineNode
*node
; /* The line being displayed */
1462 GlhLineNode
*oldest
; /* The oldest line to display */
1463 GlhLineSeg
*seg
; /* One segment of a line being displayed */
1464 enum {TSMAX
=32}; /* The maximum length of the date and time string */
1465 char buffer
[TSMAX
+1]; /* The buffer in which to write the date and time */
1466 int idlen
; /* The length of displayed ID strings */
1467 unsigned grpmax
; /* The maximum group number in the buffer */
1468 int grplen
; /* The number of characters needed to print grpmax */
1469 int len
; /* The length of a string to be written */
1471 * Check the arguments.
1473 if(!glh
|| !write_fn
|| !fmt
) {
1475 _err_record_msg(glh
->err
, "NULL argument(s)", END_ERR_MSG
);
1480 * Is history enabled?
1482 if(!glh
->enable
|| !glh
->list
.head
)
1485 * Work out the length to display ID numbers, choosing the length of
1486 * the biggest number in the buffer. Smaller numbers will be padded
1487 * with leading zeroes if needed.
1489 snprintf(buffer
, sizeof(buffer
), "%lu", (unsigned long) glh
->list
.tail
->id
);
1490 idlen
= strlen(buffer
);
1492 * Find the largest group number.
1495 for(node
=glh
->list
.head
; node
; node
=node
->next
) {
1496 if(node
->group
> grpmax
)
1497 grpmax
= node
->group
;
1500 * Find out how many characters are needed to display the group number.
1502 snprintf(buffer
, sizeof(buffer
), "%u", (unsigned) grpmax
);
1503 grplen
= strlen(buffer
);
1505 * Find the node that follows the oldest line to be displayed.
1508 oldest
= glh
->list
.head
;
1509 } else if(max_lines
==0) {
1512 for(oldest
=glh
->list
.tail
; oldest
; oldest
=oldest
->prev
) {
1513 if((all_groups
|| oldest
->group
== glh
->group
) && --max_lines
<= 0)
1517 * If the number of lines in the buffer doesn't exceed the specified
1518 * maximum, start from the oldest line in the buffer.
1521 oldest
= glh
->list
.head
;
1524 * List the history lines in increasing time order.
1526 for(node
=oldest
; node
; node
=node
->next
) {
1528 * Only display lines from the current history group, unless
1531 if(all_groups
|| node
->group
== glh
->group
) {
1532 const char *fptr
; /* A pointer into the format string */
1533 struct tm
*t
= NULL
; /* The broken time version of the timestamp */
1535 * Work out the calendar representation of the node timestamp.
1537 if(node
->timestamp
!= (time_t) -1)
1538 t
= localtime(&node
->timestamp
);
1540 * Parse the format string.
1545 * Search for the start of the next format directive or the end of the string.
1547 const char *start
= fptr
;
1548 while(*fptr
&& *fptr
!= '%')
1551 * Display any literal characters that precede the located directive.
1554 len
= (int) (fptr
- start
);
1555 if(write_fn(data
, start
, len
) != len
)
1559 * Did we hit a new directive before the end of the line?
1563 * Obey the directive. Ignore unknown directives.
1566 case 'D': /* Display the date */
1567 if(t
&& strftime(buffer
, TSMAX
, "%Y-%m-%d", t
) != 0) {
1568 len
= strlen(buffer
);
1569 if(write_fn(data
, buffer
, len
) != len
)
1573 case 'T': /* Display the time of day */
1574 if(t
&& strftime(buffer
, TSMAX
, "%H:%M:%S", t
) != 0) {
1575 len
= strlen(buffer
);
1576 if(write_fn(data
, buffer
, len
) != len
)
1580 case 'N': /* Display the sequential entry number */
1581 snprintf(buffer
, sizeof(buffer
), "%*lu", idlen
, (unsigned long) node
->id
);
1582 len
= strlen(buffer
);
1583 if(write_fn(data
, buffer
, len
) != len
)
1587 snprintf(buffer
, sizeof(buffer
), "%*u", grplen
, (unsigned) node
->group
);
1588 len
= strlen(buffer
);
1589 if(write_fn(data
, buffer
, len
) != len
)
1592 case 'H': /* Display the history line */
1593 for(seg
=node
->line
->head
; seg
; seg
=seg
->next
) {
1594 len
= seg
->next
? GLH_SEG_SIZE
: strlen(seg
->s
);
1595 if(write_fn(data
, seg
->s
, len
) != len
)
1599 case '%': /* A literal % symbol */
1600 if(write_fn(data
, "%", 1) != 1)
1605 * Skip the directive.
1616 /*.......................................................................
1617 * Change the size of the history buffer.
1620 * glh GlHistory * The input-line history maintenance object.
1621 * bufsize size_t The number of bytes in the history buffer, or 0
1622 * to delete the buffer completely.
1624 * return int 0 - OK.
1625 * 1 - Insufficient memory (the previous buffer
1626 * will have been retained). No error message
1627 * will be displayed.
1629 int _glh_resize_history(GlHistory
*glh
, size_t bufsize
)
1631 int nbuff
; /* The number of segments in the new buffer */
1634 * Check the arguments.
1641 * How many buffer segments does the requested buffer size correspond
1644 nbuff
= (bufsize
+GLH_SEG_SIZE
-1) / GLH_SEG_SIZE
;
1646 * Has a different size than the current size been requested?
1648 if(glh
->nbuff
!= nbuff
) {
1650 * Cancel any ongoing search.
1652 (void) _glh_cancel_search(glh
);
1654 * Create a wholly new buffer?
1656 if(glh
->nbuff
== 0 && nbuff
>0) {
1657 glh
->buffer
= (GlhLineSeg
*) malloc(sizeof(GlhLineSeg
) * nbuff
);
1661 glh
->nfree
= glh
->nbuff
;
1665 * Link the currently unused nodes of the buffer into a list.
1667 glh
->unused
= glh
->buffer
;
1668 for(i
=0; i
<glh
->nbuff
-1; i
++) {
1669 GlhLineSeg
*seg
= glh
->unused
+ i
;
1670 seg
->next
= seg
+ 1;
1672 glh
->unused
[i
].next
= NULL
;
1674 * Delete an existing buffer?
1676 } else if(nbuff
== 0) {
1677 _glh_clear_history(glh
, 1);
1686 * Change from one finite buffer size to another?
1689 GlhLineSeg
*buffer
; /* The resized buffer */
1690 int nbusy
; /* The number of used line segments in the new buffer */
1692 * Starting from the oldest line in the buffer, discard lines until
1693 * the buffer contains at most 'nbuff' used line segments.
1695 while(glh
->list
.head
&& glh
->nbusy
> nbuff
)
1696 _glh_discard_line(glh
, glh
->list
.head
);
1698 * Attempt to allocate a new buffer.
1700 buffer
= (GlhLineSeg
*) malloc(nbuff
* sizeof(GlhLineSeg
));
1706 * Copy the used segments of the old buffer to the start of the new buffer.
1709 for(i
=0; i
<GLH_HASH_SIZE
; i
++) {
1710 GlhHashBucket
*b
= glh
->hash
.bucket
+ i
;
1712 for(hnode
=b
->lines
; hnode
; hnode
=hnode
->next
) {
1713 GlhLineSeg
*seg
= hnode
->head
;
1714 hnode
->head
= buffer
+ nbusy
;
1715 for( ; seg
; seg
=seg
->next
) {
1716 buffer
[nbusy
] = *seg
;
1717 buffer
[nbusy
].next
= seg
->next
? &buffer
[nbusy
+1] : NULL
;
1723 * Make a list of the new buffer's unused segments.
1725 for(i
=nbusy
; i
<nbuff
-1; i
++)
1726 buffer
[i
].next
= &buffer
[i
+1];
1728 buffer
[i
].next
= NULL
;
1730 * Discard the old buffer.
1734 * Install the new buffer.
1736 glh
->buffer
= buffer
;
1739 glh
->nfree
= nbuff
- nbusy
;
1740 glh
->unused
= glh
->nfree
> 0 ? (buffer
+ nbusy
) : NULL
;
1746 /*.......................................................................
1747 * Set an upper limit to the number of lines that can be recorded in the
1748 * history list, or remove a previously specified limit.
1751 * glh GlHistory * The input-line history maintenance object.
1752 * max_lines int The maximum number of lines to allow, or -1 to
1753 * cancel a previous limit and allow as many lines
1754 * as will fit in the current history buffer size.
1756 void _glh_limit_history(GlHistory
*glh
, int max_lines
)
1761 * Apply a new limit?
1763 if(max_lines
>= 0 && max_lines
!= glh
->max_lines
) {
1765 * Count successively older lines until we reach the start of the
1766 * list, or until we have seen max_lines lines (at which point 'node'
1767 * will be line number max_lines+1).
1771 for(node
=glh
->list
.tail
; node
&& ++nline
<= max_lines
; node
=node
->prev
)
1774 * Discard any lines that exceed the limit.
1777 GlhLineNode
*oldest
= node
->next
; /* The oldest line to be kept */
1779 * Delete nodes from the head of the list until we reach the node that
1782 while(glh
->list
.head
&& glh
->list
.head
!= oldest
)
1783 _glh_discard_line(glh
, glh
->list
.head
);
1787 * Record the new limit.
1789 glh
->max_lines
= max_lines
;
1793 /*.......................................................................
1794 * Discard either all history, or the history associated with the current
1798 * glh GlHistory * The input-line history maintenance object.
1799 * all_groups int If true, clear all of the history. If false,
1800 * clear only the stored lines associated with the
1801 * currently selected history group.
1803 void _glh_clear_history(GlHistory
*glh
, int all_groups
)
1807 * Check the arguments.
1812 * Cancel any ongoing search.
1814 (void) _glh_cancel_search(glh
);
1816 * Delete all history lines regardless of group?
1820 * Claer the time-ordered list of lines.
1822 _rst_FreeList(glh
->list
.node_mem
);
1823 glh
->list
.head
= glh
->list
.tail
= NULL
;
1825 glh
->id_node
= NULL
;
1827 * Clear the hash table.
1829 for(i
=0; i
<GLH_HASH_SIZE
; i
++)
1830 glh
->hash
.bucket
[i
].lines
= NULL
;
1831 _rst_FreeList(glh
->hash
.node_mem
);
1833 * Move all line segment nodes back onto the list of unused segments.
1836 glh
->unused
= glh
->buffer
;
1837 for(i
=0; i
<glh
->nbuff
-1; i
++) {
1838 GlhLineSeg
*seg
= glh
->unused
+ i
;
1839 seg
->next
= seg
+ 1;
1841 glh
->unused
[i
].next
= NULL
;
1842 glh
->nfree
= glh
->nbuff
;
1846 glh
->nbusy
= glh
->nfree
= 0;
1849 * Just delete lines of the current group?
1852 GlhLineNode
*node
; /* The line node being checked */
1853 GlhLineNode
*next
; /* The line node that follows 'node' */
1855 * Search out and delete the line nodes of the current group.
1857 for(node
=glh
->list
.head
; node
; node
=next
) {
1859 * Keep a record of the following node before we delete the current
1864 * Discard this node?
1866 if(node
->group
== glh
->group
)
1867 _glh_discard_line(glh
, node
);
1873 /*.......................................................................
1874 * Temporarily enable or disable the history list.
1877 * glh GlHistory * The input-line history maintenance object.
1878 * enable int If true, turn on the history mechanism. If
1879 * false, disable it.
1881 void _glh_toggle_history(GlHistory
*glh
, int enable
)
1884 glh
->enable
= enable
;
1887 /*.......................................................................
1888 * Discard a given archived input line.
1891 * glh GlHistory * The history container object.
1892 * node GlhLineNode * The line to be discarded, specified via its
1893 * entry in the time-ordered list of historical
1896 static void _glh_discard_line(GlHistory
*glh
, GlhLineNode
*node
)
1899 * Remove the node from the linked list.
1902 node
->prev
->next
= node
->next
;
1904 glh
->list
.head
= node
->next
;
1906 node
->next
->prev
= node
->prev
;
1908 glh
->list
.tail
= node
->prev
;
1910 * If we are deleting the node that is marked as the start point of the
1911 * last ID search, remove the cached starting point.
1913 if(node
== glh
->id_node
)
1914 glh
->id_node
= NULL
;
1916 * If we are deleting the node that is marked as the start point of the
1917 * next prefix search, cancel the search.
1919 if(node
== glh
->recall
)
1920 _glh_cancel_search(glh
);
1922 * Delete our copy of the line.
1924 node
->line
= _glh_discard_copy(glh
, node
->line
);
1926 * Return the node to the freelist.
1928 (void) _del_FreeListNode(glh
->list
.node_mem
, node
);
1930 * Record the removal of a line from the list.
1936 /*.......................................................................
1937 * Lookup the details of a given history line, given its id.
1940 * glh GlHistory * The input-line history maintenance object.
1941 * id GlLineID The sequential number of the line.
1943 * line const char ** A pointer to a copy of the history line will be
1944 * assigned to *line. Beware that this pointer may
1945 * be invalidated by the next call to any public
1947 * group unsigned * The group membership of the line will be assigned
1949 * timestamp time_t * The timestamp of the line will be assigned to
1952 * return int 0 - The requested line wasn't found.
1953 * 1 - The line was found.
1955 int _glh_lookup_history(GlHistory
*glh
, GlhLineID id
, const char **line
,
1956 unsigned *group
, time_t *timestamp
)
1958 GlhLineNode
*node
; /* The located line location node */
1960 * Check the arguments.
1965 * Search for the line that has the specified ID.
1967 node
= _glh_find_id(glh
, id
);
1974 * Has the history line been requested?
1978 * If necessary, reallocate the lookup buffer to accomodate the size of
1979 * a copy of the located line.
1981 if(node
->line
->len
+ 1 > glh
->lbuf_dim
) {
1982 int lbuf_dim
= node
->line
->len
+ 1;
1983 char *lbuf
= realloc(glh
->lbuf
, lbuf_dim
);
1988 glh
->lbuf_dim
= lbuf_dim
;
1992 * Copy the history line into the lookup buffer.
1994 _glh_return_line(node
->line
, glh
->lbuf
, glh
->lbuf_dim
);
1996 * Assign the lookup buffer as the returned line pointer.
2001 * Does the caller want to know the group of the line?
2004 *group
= node
->group
;
2006 * Does the caller want to know the timestamp of the line?
2009 *timestamp
= node
->timestamp
;
2013 /*.......................................................................
2014 * Lookup a node in the history list by its ID.
2017 * glh GlHistory * The input-line history maintenance object.
2018 * id GlhLineID The ID of the line to be returned.
2020 * return GlhLIneNode * The located node, or NULL if not found.
2022 static GlhLineNode
*_glh_find_id(GlHistory
*glh
, GlhLineID id
)
2024 GlhLineNode
*node
; /* The node being checked */
2026 * Is history enabled?
2028 if(!glh
->enable
|| !glh
->list
.head
)
2031 * If possible, start at the end point of the last ID search.
2032 * Otherwise start from the head of the list.
2034 node
= glh
->id_node
;
2036 node
= glh
->list
.head
;
2038 * Search forwards from 'node'?
2041 while(node
&& node
->id
!= id
)
2043 glh
->id_node
= node
? node
: glh
->list
.tail
;
2045 * Search backwards from 'node'?
2048 while(node
&& node
->id
!= id
)
2050 glh
->id_node
= node
? node
: glh
->list
.head
;
2053 * Return the located node (this will be NULL if the ID wasn't found).
2058 /*.......................................................................
2059 * Query the state of the history list. Note that any of the input/output
2060 * pointers can be specified as NULL.
2063 * glh GlHistory * The input-line history maintenance object.
2065 * enabled int * If history is enabled, *enabled will be
2066 * set to 1. Otherwise it will be assigned 0.
2067 * group unsigned * The current history group ID will be assigned
2069 * max_lines int * The currently requested limit on the number
2070 * of history lines in the list, or -1 if
2073 void _glh_state_of_history(GlHistory
*glh
, int *enabled
, unsigned *group
,
2078 *enabled
= glh
->enable
;
2080 *group
= glh
->group
;
2082 *max_lines
= glh
->max_lines
;
2086 /*.......................................................................
2087 * Get the range of lines in the history buffer.
2090 * glh GlHistory * The input-line history maintenance object.
2092 * oldest unsigned long * The sequential entry number of the oldest
2093 * line in the history list will be assigned
2094 * to *oldest, unless there are no lines, in
2095 * which case 0 will be assigned.
2096 * newest unsigned long * The sequential entry number of the newest
2097 * line in the history list will be assigned
2098 * to *newest, unless there are no lines, in
2099 * which case 0 will be assigned.
2100 * nlines int * The number of lines currently in the history
2103 void _glh_range_of_history(GlHistory
*glh
, unsigned long *oldest
,
2104 unsigned long *newest
, int *nlines
)
2108 *oldest
= glh
->list
.head
? glh
->list
.head
->id
: 0;
2110 *newest
= glh
->list
.tail
? glh
->list
.tail
->id
: 0;
2112 *nlines
= glh
->nline
;
2116 /*.......................................................................
2117 * Return the size of the history buffer and the amount of the
2118 * buffer that is currently in use.
2121 * glh GlHistory * The input-line history maintenance object.
2123 * buff_size size_t * The size of the history buffer (bytes).
2124 * buff_used size_t * The amount of the history buffer that
2125 * is currently occupied (bytes).
2127 void _glh_size_of_history(GlHistory
*glh
, size_t *buff_size
, size_t *buff_used
)
2131 *buff_size
= (glh
->nbusy
+ glh
->nfree
) * GLH_SEG_SIZE
;
2133 * Determine the amount of buffer space that is currently occupied.
2136 *buff_used
= glh
->nbusy
* GLH_SEG_SIZE
;
2140 /*.......................................................................
2141 * Return extra information (ie. in addition to that provided by errno)
2142 * about the last error to occur in any of the public functions of this
2146 * glh GlHistory * The container of the history list.
2148 * return const char * A pointer to the internal buffer in which
2149 * the error message is temporarily stored.
2151 const char *_glh_last_error(GlHistory
*glh
)
2153 return glh
? _err_get_msg(glh
->err
) : "NULL GlHistory argument";
2156 /*.......................................................................
2157 * Unless already stored, store a copy of the line in the history buffer,
2158 * then return a reference-counted hash-node pointer to this copy.
2161 * glh GlHistory * The history maintenance buffer.
2162 * line const char * The history line to be recorded.
2163 * n size_t The length of the string, excluding any '\0'
2166 * return GlhHashNode * The hash-node containing the stored line, or
2169 static GlhHashNode
*_glh_acquire_copy(GlHistory
*glh
, const char *line
,
2172 GlhHashBucket
*bucket
; /* The hash-table bucket of the line */
2173 GlhHashNode
*hnode
; /* The hash-table node of the line */
2176 * In which bucket should the line be recorded?
2178 bucket
= glh_find_bucket(glh
, line
, n
);
2180 * Is the line already recorded there?
2182 hnode
= glh_find_hash_node(bucket
, line
, n
);
2184 * If the line isn't recorded in the buffer yet, make room for it.
2187 GlhLineSeg
*seg
; /* A line segment */
2188 int offset
; /* An offset into line[] */
2190 * How many string segments will be needed to record the new line,
2191 * including space for a '\0' terminator?
2193 int nseg
= ((n
+1) + GLH_SEG_SIZE
-1) / GLH_SEG_SIZE
;
2195 * Discard the oldest history lines in the buffer until at least
2196 * 'nseg' segments have been freed up, or until we run out of buffer
2199 while(glh
->nfree
< nseg
&& glh
->nbusy
> 0)
2200 _glh_discard_line(glh
, glh
->list
.head
);
2202 * If the buffer is smaller than the new line, don't attempt to truncate
2203 * it to fit. Simply don't archive it.
2205 if(glh
->nfree
< nseg
)
2208 * Record the line in the first 'nseg' segments of the list of unused segments.
2211 for(i
=0,seg
=glh
->unused
; i
<nseg
-1; i
++,seg
=seg
->next
, offset
+=GLH_SEG_SIZE
)
2212 memcpy(seg
->s
, line
+ offset
, GLH_SEG_SIZE
);
2213 memcpy(seg
->s
, line
+ offset
, n
-offset
);
2214 seg
->s
[n
-offset
] = '\0';
2216 * Create a new hash-node for the line.
2218 hnode
= (GlhHashNode
*) _new_FreeListNode(glh
->hash
.node_mem
);
2222 * Move the copy of the line from the list of unused segments to
2225 hnode
->head
= glh
->unused
;
2226 glh
->unused
= seg
->next
;
2231 * Prepend the new hash node to the list within the associated bucket.
2233 hnode
->next
= bucket
->lines
;
2234 bucket
->lines
= hnode
;
2236 * Initialize the rest of the members of the hash node.
2239 hnode
->reported
= 0;
2241 hnode
->bucket
= bucket
;
2244 * Increment the reference count of the line.
2250 /*.......................................................................
2251 * Decrement the reference count of the history line of a given hash-node,
2252 * and if the count reaches zero, delete both the hash-node and the
2253 * buffered copy of the line.
2256 * glh GlHistory * The history container object.
2257 * hnode GlhHashNode * The node to be removed.
2259 * return GlhHashNode * The deleted hash-node (ie. NULL).
2261 static GlhHashNode
*_glh_discard_copy(GlHistory
*glh
, GlhHashNode
*hnode
)
2264 GlhHashBucket
*bucket
= hnode
->bucket
;
2266 * If decrementing the reference count of the hash-node doesn't reduce
2267 * the reference count to zero, then the line is still in use in another
2268 * object, so don't delete it yet. Return NULL to indicate that the caller's
2269 * access to the hash-node copy has been deleted.
2271 if(--hnode
->used
>= 1)
2274 * Remove the hash-node from the list in its parent bucket.
2276 if(bucket
->lines
== hnode
) {
2277 bucket
->lines
= hnode
->next
;
2279 GlhHashNode
*prev
; /* The node which precedes hnode in the bucket */
2280 for(prev
=bucket
->lines
; prev
&& prev
->next
!= hnode
; prev
=prev
->next
)
2283 prev
->next
= hnode
->next
;
2287 * Return the line segments of the hash-node to the list of unused segments.
2290 GlhLineSeg
*tail
; /* The last node in the list of line segments */
2291 int nseg
; /* The number of segments being discarded */
2293 * Get the last node of the list of line segments referenced in the hash-node,
2294 * while counting the number of line segments used.
2296 for(nseg
=1,tail
=hnode
->head
; tail
->next
; nseg
++,tail
=tail
->next
)
2299 * Prepend the list of line segments used by the hash node to the
2300 * list of unused line segments.
2302 tail
->next
= glh
->unused
;
2303 glh
->unused
= hnode
->head
;
2308 * Return the container of the hash-node to the freelist.
2310 hnode
= (GlhHashNode
*) _del_FreeListNode(glh
->hash
.node_mem
, hnode
);
2315 /*.......................................................................
2316 * Private function to locate the hash bucket associated with a given
2319 * This uses a hash-function described in the dragon-book
2320 * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and
2321 * Ullman; pub. Adison Wesley) page 435.
2324 * glh GlHistory * The history container object.
2325 * line const char * The historical line to look up.
2326 * n size_t The length of the line in line[], excluding
2327 * any '\0' terminator.
2329 * return GlhHashBucket * The located hash-bucket.
2331 static GlhHashBucket
*glh_find_bucket(GlHistory
*glh
, const char *line
,
2334 unsigned long h
= 0L;
2336 for(i
=0; i
<n
; i
++) {
2337 unsigned char c
= line
[i
];
2338 h
= 65599UL * h
+ c
; /* 65599 is a prime close to 2^16 */
2340 return glh
->hash
.bucket
+ (h
% GLH_HASH_SIZE
);
2343 /*.......................................................................
2344 * Find a given history line within a given hash-table bucket.
2347 * bucket GlhHashBucket * The hash-table bucket in which to search.
2348 * line const char * The historical line to lookup.
2349 * n size_t The length of the line in line[], excluding
2350 * any '\0' terminator.
2352 * return GlhHashNode * The hash-table entry of the line, or NULL
2355 static GlhHashNode
*glh_find_hash_node(GlhHashBucket
*bucket
, const char *line
,
2358 GlhHashNode
*node
; /* A node in the list of lines in the bucket */
2360 * Compare each of the lines in the list of lines, against 'line'.
2362 for(node
=bucket
->lines
; node
; node
=node
->next
) {
2363 if(_glh_is_line(node
, line
, n
))
2369 /*.......................................................................
2370 * Return non-zero if a given string is equal to a given segmented line
2374 * hash GlhHashNode * The hash-table entry of the line.
2375 * line const char * The string to be compared to the segmented
2377 * n size_t The length of the line in line[], excluding
2378 * any '\0' terminator.
2380 * return int 0 - The lines differ.
2381 * 1 - The lines are the same.
2383 static int _glh_is_line(GlhHashNode
*hash
, const char *line
, size_t n
)
2385 GlhLineSeg
*seg
; /* A node in the list of line segments */
2388 * Do the two lines have the same length?
2393 * Compare the characters of the segmented and unsegmented versions
2396 for(seg
=hash
->head
; n
>0 && seg
; seg
=seg
->next
) {
2397 const char *s
= seg
->s
;
2398 for(i
=0; n
>0 && i
<GLH_SEG_SIZE
; i
++,n
--) {
2406 /*.......................................................................
2407 * Return non-zero if a given line has the specified segmented search
2411 * line GlhHashNode * The line to be compared against the prefix.
2412 * prefix GlhHashNode * The search prefix, or NULL to match any string.
2414 * return int 0 - The line doesn't have the specified prefix.
2415 * 1 - The line has the specified prefix.
2417 static int _glh_line_matches_prefix(GlhHashNode
*line
, GlhHashNode
*prefix
)
2419 GlhLineStream lstr
; /* The stream that is used to traverse 'line' */
2420 GlhLineStream pstr
; /* The stream that is used to traverse 'prefix' */
2422 * When prefix==NULL, this means that the nul string
2423 * is to be matched, and this matches all lines.
2428 * Wrap the two history lines that are to be compared in iterator
2431 glh_init_stream(&lstr
, line
);
2432 glh_init_stream(&pstr
, prefix
);
2434 * If the prefix contains a glob pattern, match the prefix as a glob
2437 if(glh_contains_glob(prefix
))
2438 return glh_line_matches_glob(&lstr
, &pstr
);
2440 * Is the prefix longer than the line being compared against it?
2442 if(prefix
->len
> line
->len
)
2445 * Compare the line to the prefix.
2447 while(pstr
.c
!= '\0' && pstr
.c
== lstr
.c
) {
2448 glh_step_stream(&lstr
);
2449 glh_step_stream(&pstr
);
2452 * Did we reach the end of the prefix string before finding
2455 return pstr
.c
== '\0';
2458 /*.......................................................................
2459 * Copy a given history line into a specified output string.
2462 * hash GlhHashNode The hash-table entry of the history line to
2464 * line char * A copy of the history line.
2465 * dim size_t The allocated dimension of the line buffer.
2467 static void _glh_return_line(GlhHashNode
*hash
, char *line
, size_t dim
)
2469 GlhLineSeg
*seg
; /* A node in the list of line segments */
2471 for(seg
=hash
->head
; dim
>0 && seg
; seg
=seg
->next
) {
2472 const char *s
= seg
->s
;
2473 for(i
=0; dim
>0 && i
<GLH_SEG_SIZE
; i
++,dim
--)
2477 * If the line wouldn't fit in the output buffer, replace the last character
2478 * with a '\0' terminator.
2484 /*.......................................................................
2485 * This function should be called whenever a new line recall is
2486 * attempted. It preserves a copy of the current input line in the
2487 * history list while other lines in the history list are being
2491 * glh GlHistory * The input-line history maintenance object.
2492 * line char * The current contents of the input line buffer.
2494 * return int 0 - OK.
2497 static int _glh_prepare_for_recall(GlHistory
*glh
, char *line
)
2500 * If a recall session has already been started, but we have returned
2501 * to the preserved copy of the input line, if the user has changed
2502 * this line, we should replace the preserved copy of the original
2503 * input line with the new one. To do this simply cancel the session,
2504 * so that a new session is started below.
2506 if(glh
->recall
&& glh
->recall
== glh
->list
.tail
&&
2507 !_glh_is_line(glh
->recall
->line
, line
, strlen(line
))) {
2508 _glh_cancel_search(glh
);
2511 * If this is the first line recall of a new recall session, save the
2512 * current line for potential recall later, and mark it as the last
2516 if(_glh_add_history(glh
, line
, 1))
2518 glh
->recall
= glh
->list
.tail
;
2520 * The above call to _glh_add_history() will have incremented the line
2521 * sequence number, after adding the line. Since we only want this to
2522 * to be incremented for permanently entered lines, decrement it again.
2529 /*.......................................................................
2530 * Return non-zero if a history search session is currently in progress.
2533 * glh GlHistory * The input-line history maintenance object.
2535 * return int 0 - No search is currently in progress.
2536 * 1 - A search is in progress.
2538 int _glh_search_active(GlHistory
*glh
)
2540 return glh
&& glh
->recall
;
2543 /*.......................................................................
2544 * Initialize a character iterator object to point to the start of a
2545 * given history line. The first character of the line will be placed
2546 * in str->c, and subsequent characters can be placed there by calling
2547 * glh_strep_stream().
2550 * str GlhLineStream * The iterator object to be initialized.
2551 * line GlhHashNode * The history line to be iterated over (a
2552 * NULL value here, is interpretted as an
2553 * empty string by glh_step_stream()).
2555 static void glh_init_stream(GlhLineStream
*str
, GlhHashNode
*line
)
2557 str
->seg
= line
? line
->head
: NULL
;
2559 str
->c
= str
->seg
? str
->seg
->s
[0] : '\0';
2562 /*.......................................................................
2563 * Copy the next unread character in the line being iterated, in str->c.
2564 * Once the end of the history line has been reached, all futher calls
2565 * set str->c to '\0'.
2568 * str GlhLineStream * The history-line iterator to read from.
2570 static void glh_step_stream(GlhLineStream
*str
)
2573 * Get the character from the current iterator position within the line.
2575 str
->c
= str
->seg
? str
->seg
->s
[str
->posn
] : '\0';
2577 * Unless we have reached the end of the string, move the iterator
2578 * to the position of the next character in the line.
2580 if(str
->c
!= '\0' && ++str
->posn
>= GLH_SEG_SIZE
) {
2582 str
->seg
= str
->seg
->next
;
2586 /*.......................................................................
2587 * Return non-zero if the specified search prefix contains any glob
2588 * wildcard characters.
2591 * prefix GlhHashNode * The search prefix.
2593 * return int 0 - The prefix doesn't contain any globbing
2595 * 1 - The prefix contains at least one
2596 * globbing character.
2598 static int glh_contains_glob(GlhHashNode
*prefix
)
2600 GlhLineStream pstr
; /* The stream that is used to traverse 'prefix' */
2602 * Wrap a stream iterator around the prefix, so that we can traverse it
2603 * without worrying about line-segmentation.
2605 glh_init_stream(&pstr
, prefix
);
2607 * Search for unescaped wildcard characters.
2609 while(pstr
.c
!= '\0') {
2611 case '\\': /* Skip escaped characters */
2612 glh_step_stream(&pstr
);
2614 case '*': case '?': case '[': /* A wildcard character? */
2618 glh_step_stream(&pstr
);
2621 * No wildcard characters were found.
2626 /*.......................................................................
2627 * Return non-zero if the history line matches a search prefix containing
2631 * lstr GlhLineStream * The iterator stream being used to traverse
2632 * the history line that is being matched.
2633 * pstr GlhLineStream * The iterator stream being used to traverse
2636 * return int 0 - Doesn't match.
2637 * 1 - The line matches the pattern.
2639 static int glh_line_matches_glob(GlhLineStream
*lstr
, GlhLineStream
*pstr
)
2642 * Match each character of the pattern until we reach the end of the
2645 while(pstr
->c
!= '\0') {
2647 * Handle the next character of the pattern.
2651 * A match zero-or-more characters wildcard operator.
2655 * Skip the '*' character in the pattern.
2657 glh_step_stream(pstr
);
2659 * If the pattern ends with the '*' wildcard, then the
2660 * rest of the line matches this.
2665 * Using the wildcard to match successively longer sections of
2666 * the remaining characters of the line, attempt to match
2667 * the tail of the line against the tail of the pattern.
2670 GlhLineStream old_lstr
= *lstr
;
2671 GlhLineStream old_pstr
= *pstr
;
2672 if(glh_line_matches_glob(lstr
, pstr
))
2675 * Restore the line and pattern iterators for a new try.
2680 * Prepare to try again, one character further into the line.
2682 glh_step_stream(lstr
);
2684 return 0; /* The pattern following the '*' didn't match */
2687 * A match-one-character wildcard operator.
2691 * If there is a character to be matched, skip it and advance the
2695 glh_step_stream(lstr
);
2696 glh_step_stream(pstr
);
2698 * If we hit the end of the line, there is no character
2699 * matching the operator, so the pattern doesn't match.
2706 * A character range operator, with the character ranges enclosed
2707 * in matching square brackets.
2710 glh_step_stream(pstr
); /* Skip the '[' character */
2711 if(!lstr
->c
|| !glh_matches_range(lstr
->c
, pstr
))
2713 glh_step_stream(lstr
); /* Skip the character that matched */
2716 * A backslash in the pattern prevents the following character as
2717 * being seen as a special character.
2720 glh_step_stream(pstr
); /* Skip the backslash */
2721 /* Note fallthrough to default */
2723 * A normal character to be matched explicitly.
2726 if(lstr
->c
== pstr
->c
) {
2727 glh_step_stream(lstr
);
2728 glh_step_stream(pstr
);
2736 * To get here, pattern must have been exhausted. The line only
2737 * matches the pattern if the line as also been exhausted.
2739 return pstr
->c
== '\0' && lstr
->c
== '\0';
2742 /*.......................................................................
2743 * Match a character range expression terminated by an unescaped close
2747 * c char The character to be matched with the range
2749 * pstr GlhLineStream * The iterator stream being used to traverse
2752 * return int 0 - Doesn't match.
2753 * 1 - The character matched.
2755 static int glh_matches_range(char c
, GlhLineStream
*pstr
)
2757 int invert
= 0; /* True to invert the sense of the match */
2758 int matched
= 0; /* True if the character matched the pattern */
2759 char lastc
= '\0'; /* The previous character in the pattern */
2761 * If the first character is a caret, the sense of the match is
2762 * inverted and only if the character isn't one of those in the
2763 * range, do we say that it matches.
2765 if(pstr
->c
== '^') {
2766 glh_step_stream(pstr
);
2770 * The hyphen is only a special character when it follows the first
2771 * character of the range (not including the caret).
2773 if(pstr
->c
== '-') {
2774 glh_step_stream(pstr
);
2778 * Skip other leading '-' characters since they make no sense.
2780 while(pstr
->c
== '-')
2781 glh_step_stream(pstr
);
2784 * The hyphen is only a special character when it follows the first
2785 * character of the range (not including the caret or a hyphen).
2787 if(pstr
->c
== ']') {
2788 glh_step_stream(pstr
);
2793 * Having dealt with the characters that have special meanings at
2794 * the beginning of a character range expression, see if the
2795 * character matches any of the remaining characters of the range,
2796 * up until a terminating ']' character is seen.
2798 while(!matched
&& pstr
->c
&& pstr
->c
!= ']') {
2800 * Is this a range of characters signaled by the two end characters
2801 * separated by a hyphen?
2803 if(pstr
->c
== '-') {
2804 glh_step_stream(pstr
); /* Skip the hyphen */
2805 if(pstr
->c
!= ']') {
2806 if(c
>= lastc
&& c
<= pstr
->c
)
2810 * A normal character to be compared directly.
2812 } else if(pstr
->c
== c
) {
2816 * Record and skip the character that we just processed.
2820 glh_step_stream(pstr
);
2823 * Find the terminating ']'.
2825 while(pstr
->c
&& pstr
->c
!= ']')
2826 glh_step_stream(pstr
);
2828 * Did we find a terminating ']'?
2830 if(pstr
->c
== ']') {
2832 * Skip the terminating ']'.
2834 glh_step_stream(pstr
);
2836 * If the pattern started with a caret, invert the sense of the match.
2841 * If the pattern didn't end with a ']', then it doesn't match,
2842 * regardless of the value of the required sense of the match.