1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000, 2001, 2002, 2003, 2004 by Martin C. Shepherd.
3*7c478bd9Sstevel@tonic-gate *
4*7c478bd9Sstevel@tonic-gate * All rights reserved.
5*7c478bd9Sstevel@tonic-gate *
6*7c478bd9Sstevel@tonic-gate * Permission is hereby granted, free of charge, to any person obtaining a
7*7c478bd9Sstevel@tonic-gate * copy of this software and associated documentation files (the
8*7c478bd9Sstevel@tonic-gate * "Software"), to deal in the Software without restriction, including
9*7c478bd9Sstevel@tonic-gate * without limitation the rights to use, copy, modify, merge, publish,
10*7c478bd9Sstevel@tonic-gate * distribute, and/or sell copies of the Software, and to permit persons
11*7c478bd9Sstevel@tonic-gate * to whom the Software is furnished to do so, provided that the above
12*7c478bd9Sstevel@tonic-gate * copyright notice(s) and this permission notice appear in all copies of
13*7c478bd9Sstevel@tonic-gate * the Software and that both the above copyright notice(s) and this
14*7c478bd9Sstevel@tonic-gate * permission notice appear in supporting documentation.
15*7c478bd9Sstevel@tonic-gate *
16*7c478bd9Sstevel@tonic-gate * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17*7c478bd9Sstevel@tonic-gate * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18*7c478bd9Sstevel@tonic-gate * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
19*7c478bd9Sstevel@tonic-gate * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
20*7c478bd9Sstevel@tonic-gate * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
21*7c478bd9Sstevel@tonic-gate * INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
22*7c478bd9Sstevel@tonic-gate * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
23*7c478bd9Sstevel@tonic-gate * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
24*7c478bd9Sstevel@tonic-gate * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
25*7c478bd9Sstevel@tonic-gate *
26*7c478bd9Sstevel@tonic-gate * Except as contained in this notice, the name of a copyright holder
27*7c478bd9Sstevel@tonic-gate * shall not be used in advertising or otherwise to promote the sale, use
28*7c478bd9Sstevel@tonic-gate * or other dealings in this Software without prior written authorization
29*7c478bd9Sstevel@tonic-gate * of the copyright holder.
30*7c478bd9Sstevel@tonic-gate */
31*7c478bd9Sstevel@tonic-gate
32*7c478bd9Sstevel@tonic-gate /*
33*7c478bd9Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
34*7c478bd9Sstevel@tonic-gate * Use is subject to license terms.
35*7c478bd9Sstevel@tonic-gate */
36*7c478bd9Sstevel@tonic-gate
37*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI"
38*7c478bd9Sstevel@tonic-gate
39*7c478bd9Sstevel@tonic-gate #include <stdlib.h>
40*7c478bd9Sstevel@tonic-gate #include <stdio.h>
41*7c478bd9Sstevel@tonic-gate #include <string.h>
42*7c478bd9Sstevel@tonic-gate #include <ctype.h>
43*7c478bd9Sstevel@tonic-gate #include <time.h>
44*7c478bd9Sstevel@tonic-gate #include <errno.h>
45*7c478bd9Sstevel@tonic-gate
46*7c478bd9Sstevel@tonic-gate #include "ioutil.h"
47*7c478bd9Sstevel@tonic-gate #include "history.h"
48*7c478bd9Sstevel@tonic-gate #include "freelist.h"
49*7c478bd9Sstevel@tonic-gate #include "errmsg.h"
50*7c478bd9Sstevel@tonic-gate
51*7c478bd9Sstevel@tonic-gate /*
52*7c478bd9Sstevel@tonic-gate * History lines are split into sub-strings of GLH_SEG_SIZE
53*7c478bd9Sstevel@tonic-gate * characters. To avoid wasting space in the GlhLineSeg structure,
54*7c478bd9Sstevel@tonic-gate * this should be a multiple of the size of a pointer.
55*7c478bd9Sstevel@tonic-gate */
56*7c478bd9Sstevel@tonic-gate #define GLH_SEG_SIZE 16
57*7c478bd9Sstevel@tonic-gate
58*7c478bd9Sstevel@tonic-gate /*
59*7c478bd9Sstevel@tonic-gate * GlhLineSeg structures contain fixed sized segments of a larger
60*7c478bd9Sstevel@tonic-gate * string. These are linked into lists to record strings, with all but
61*7c478bd9Sstevel@tonic-gate * the last segment having GLH_SEG_SIZE characters. The last segment
62*7c478bd9Sstevel@tonic-gate * of a string is terminated within the GLH_SEG_SIZE characters with a
63*7c478bd9Sstevel@tonic-gate * '\0'.
64*7c478bd9Sstevel@tonic-gate */
65*7c478bd9Sstevel@tonic-gate typedef struct GlhLineSeg GlhLineSeg;
66*7c478bd9Sstevel@tonic-gate struct GlhLineSeg {
67*7c478bd9Sstevel@tonic-gate GlhLineSeg *next; /* The next sub-string of the history line */
68*7c478bd9Sstevel@tonic-gate char s[GLH_SEG_SIZE]; /* The sub-string. Beware that only the final */
69*7c478bd9Sstevel@tonic-gate /* substring of a line, as indicated by 'next' */
70*7c478bd9Sstevel@tonic-gate /* being NULL, is '\0' terminated. */
71*7c478bd9Sstevel@tonic-gate };
72*7c478bd9Sstevel@tonic-gate
73*7c478bd9Sstevel@tonic-gate /*
74*7c478bd9Sstevel@tonic-gate * History lines are recorded in a hash table, such that repeated
75*7c478bd9Sstevel@tonic-gate * lines are stored just once.
76*7c478bd9Sstevel@tonic-gate *
77*7c478bd9Sstevel@tonic-gate * Start by defining the size of the hash table. This should be a
78*7c478bd9Sstevel@tonic-gate * prime number.
79*7c478bd9Sstevel@tonic-gate */
80*7c478bd9Sstevel@tonic-gate #define GLH_HASH_SIZE 113
81*7c478bd9Sstevel@tonic-gate
82*7c478bd9Sstevel@tonic-gate typedef struct GlhHashBucket GlhHashBucket;
83*7c478bd9Sstevel@tonic-gate
84*7c478bd9Sstevel@tonic-gate /*
85*7c478bd9Sstevel@tonic-gate * Each history line will be represented in the hash table by a
86*7c478bd9Sstevel@tonic-gate * structure of the following type.
87*7c478bd9Sstevel@tonic-gate */
88*7c478bd9Sstevel@tonic-gate typedef struct GlhHashNode GlhHashNode;
89*7c478bd9Sstevel@tonic-gate struct GlhHashNode {
90*7c478bd9Sstevel@tonic-gate GlhHashBucket *bucket; /* The parent hash-table bucket of this node */
91*7c478bd9Sstevel@tonic-gate GlhHashNode *next; /* The next in the list of nodes within the */
92*7c478bd9Sstevel@tonic-gate /* parent hash-table bucket. */
93*7c478bd9Sstevel@tonic-gate GlhLineSeg *head; /* The list of sub-strings which make up a line */
94*7c478bd9Sstevel@tonic-gate int len; /* The length of the line, excluding any '\0' */
95*7c478bd9Sstevel@tonic-gate int used; /* The number of times this string is pointed to by */
96*7c478bd9Sstevel@tonic-gate /* the time-ordered list of history lines. */
97*7c478bd9Sstevel@tonic-gate int reported; /* A flag that is used when searching to ensure that */
98*7c478bd9Sstevel@tonic-gate /* a line isn't reported redundantly. */
99*7c478bd9Sstevel@tonic-gate };
100*7c478bd9Sstevel@tonic-gate
101*7c478bd9Sstevel@tonic-gate /*
102*7c478bd9Sstevel@tonic-gate * How many new GlhHashNode elements should be allocated at a time?
103*7c478bd9Sstevel@tonic-gate */
104*7c478bd9Sstevel@tonic-gate #define GLH_HASH_INCR 50
105*7c478bd9Sstevel@tonic-gate
106*7c478bd9Sstevel@tonic-gate static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n);
107*7c478bd9Sstevel@tonic-gate static int _glh_line_matches_prefix(GlhHashNode *line, GlhHashNode *prefix);
108*7c478bd9Sstevel@tonic-gate static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim);
109*7c478bd9Sstevel@tonic-gate
110*7c478bd9Sstevel@tonic-gate /*
111*7c478bd9Sstevel@tonic-gate * All history lines which hash to a given bucket in the hash table, are
112*7c478bd9Sstevel@tonic-gate * recorded in a structure of the following type.
113*7c478bd9Sstevel@tonic-gate */
114*7c478bd9Sstevel@tonic-gate struct GlhHashBucket {
115*7c478bd9Sstevel@tonic-gate GlhHashNode *lines; /* The list of history lines which fall in this bucket */
116*7c478bd9Sstevel@tonic-gate };
117*7c478bd9Sstevel@tonic-gate
118*7c478bd9Sstevel@tonic-gate static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line,
119*7c478bd9Sstevel@tonic-gate size_t n);
120*7c478bd9Sstevel@tonic-gate static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line,
121*7c478bd9Sstevel@tonic-gate size_t n);
122*7c478bd9Sstevel@tonic-gate
123*7c478bd9Sstevel@tonic-gate typedef struct {
124*7c478bd9Sstevel@tonic-gate FreeList *node_mem; /* A free-list of GlhHashNode structures */
125*7c478bd9Sstevel@tonic-gate GlhHashBucket bucket[GLH_HASH_SIZE]; /* The buckets of the hash table */
126*7c478bd9Sstevel@tonic-gate } GlhLineHash;
127*7c478bd9Sstevel@tonic-gate
128*7c478bd9Sstevel@tonic-gate /*
129*7c478bd9Sstevel@tonic-gate * GlhLineNode's are used to record history lines in time order.
130*7c478bd9Sstevel@tonic-gate */
131*7c478bd9Sstevel@tonic-gate typedef struct GlhLineNode GlhLineNode;
132*7c478bd9Sstevel@tonic-gate struct GlhLineNode {
133*7c478bd9Sstevel@tonic-gate long id; /* The unique identifier of this history line */
134*7c478bd9Sstevel@tonic-gate time_t timestamp; /* The time at which the line was archived */
135*7c478bd9Sstevel@tonic-gate unsigned group; /* The identifier of the history group to which the */
136*7c478bd9Sstevel@tonic-gate /* the line belongs. */
137*7c478bd9Sstevel@tonic-gate GlhLineNode *next; /* The next youngest line in the list */
138*7c478bd9Sstevel@tonic-gate GlhLineNode *prev; /* The next oldest line in the list */
139*7c478bd9Sstevel@tonic-gate GlhHashNode *line; /* The hash-table entry of the history line */
140*7c478bd9Sstevel@tonic-gate };
141*7c478bd9Sstevel@tonic-gate
142*7c478bd9Sstevel@tonic-gate /*
143*7c478bd9Sstevel@tonic-gate * The number of GlhLineNode elements per freelist block.
144*7c478bd9Sstevel@tonic-gate */
145*7c478bd9Sstevel@tonic-gate #define GLH_LINE_INCR 100
146*7c478bd9Sstevel@tonic-gate
147*7c478bd9Sstevel@tonic-gate /*
148*7c478bd9Sstevel@tonic-gate * Encapsulate the time-ordered list of historical lines.
149*7c478bd9Sstevel@tonic-gate */
150*7c478bd9Sstevel@tonic-gate typedef struct {
151*7c478bd9Sstevel@tonic-gate FreeList *node_mem; /* A freelist of GlhLineNode objects */
152*7c478bd9Sstevel@tonic-gate GlhLineNode *head; /* The oldest line in the list */
153*7c478bd9Sstevel@tonic-gate GlhLineNode *tail; /* The newest line in the list */
154*7c478bd9Sstevel@tonic-gate } GlhLineList;
155*7c478bd9Sstevel@tonic-gate
156*7c478bd9Sstevel@tonic-gate /*
157*7c478bd9Sstevel@tonic-gate * The _glh_lookup_history() returns copies of history lines in a
158*7c478bd9Sstevel@tonic-gate * dynamically allocated array. This array is initially allocated
159*7c478bd9Sstevel@tonic-gate * GLH_LOOKUP_SIZE bytes. If subsequently this size turns out to be
160*7c478bd9Sstevel@tonic-gate * too small, realloc() is used to increase its size to the required
161*7c478bd9Sstevel@tonic-gate * size plus GLH_LOOKUP_MARGIN. The idea of the later parameter is to
162*7c478bd9Sstevel@tonic-gate * reduce the number of realloc() operations needed.
163*7c478bd9Sstevel@tonic-gate */
164*7c478bd9Sstevel@tonic-gate #define GLH_LBUF_SIZE 300
165*7c478bd9Sstevel@tonic-gate #define GLH_LBUF_MARGIN 100
166*7c478bd9Sstevel@tonic-gate
167*7c478bd9Sstevel@tonic-gate /*
168*7c478bd9Sstevel@tonic-gate * Encapsulate all of the resources needed to store historical input lines.
169*7c478bd9Sstevel@tonic-gate */
170*7c478bd9Sstevel@tonic-gate struct GlHistory {
171*7c478bd9Sstevel@tonic-gate ErrMsg *err; /* The error-reporting buffer */
172*7c478bd9Sstevel@tonic-gate GlhLineSeg *buffer; /* An array of sub-line nodes to be partitioned */
173*7c478bd9Sstevel@tonic-gate /* into lists of sub-strings recording input lines. */
174*7c478bd9Sstevel@tonic-gate int nbuff; /* The allocated dimension of buffer[] */
175*7c478bd9Sstevel@tonic-gate GlhLineSeg *unused; /* The list of free nodes in buffer[] */
176*7c478bd9Sstevel@tonic-gate GlhLineList list; /* A time ordered list of history lines */
177*7c478bd9Sstevel@tonic-gate GlhLineNode *recall; /* The last line recalled, or NULL if no recall */
178*7c478bd9Sstevel@tonic-gate /* session is currently active. */
179*7c478bd9Sstevel@tonic-gate GlhLineNode *id_node;/* The node at which the last ID search terminated */
180*7c478bd9Sstevel@tonic-gate GlhLineHash hash; /* A hash-table of reference-counted history lines */
181*7c478bd9Sstevel@tonic-gate GlhHashNode *prefix; /* A pointer to a line containing the prefix that */
182*7c478bd9Sstevel@tonic-gate /* is being searched for. Note that if prefix==NULL */
183*7c478bd9Sstevel@tonic-gate /* and prefix_len>0, this means that no line in */
184*7c478bd9Sstevel@tonic-gate /* the buffer starts with the requested prefix. */
185*7c478bd9Sstevel@tonic-gate int prefix_len; /* The length of the prefix being searched for. */
186*7c478bd9Sstevel@tonic-gate char *lbuf; /* The array in which _glh_lookup_history() returns */
187*7c478bd9Sstevel@tonic-gate /* history lines */
188*7c478bd9Sstevel@tonic-gate int lbuf_dim; /* The allocated size of lbuf[] */
189*7c478bd9Sstevel@tonic-gate int nbusy; /* The number of line segments in buffer[] that are */
190*7c478bd9Sstevel@tonic-gate /* currently being used to record sub-lines */
191*7c478bd9Sstevel@tonic-gate int nfree; /* The number of line segments in buffer that are */
192*7c478bd9Sstevel@tonic-gate /* not currently being used to record sub-lines */
193*7c478bd9Sstevel@tonic-gate unsigned long seq; /* The next ID to assign to a line node */
194*7c478bd9Sstevel@tonic-gate unsigned group; /* The identifier of the current history group */
195*7c478bd9Sstevel@tonic-gate int nline; /* The number of lines currently in the history list */
196*7c478bd9Sstevel@tonic-gate int max_lines; /* Either -1 or a ceiling on the number of lines */
197*7c478bd9Sstevel@tonic-gate int enable; /* If false, ignore history additions and lookups */
198*7c478bd9Sstevel@tonic-gate };
199*7c478bd9Sstevel@tonic-gate
200*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
201*7c478bd9Sstevel@tonic-gate static int _glh_cant_load_history(GlHistory *glh, const char *filename,
202*7c478bd9Sstevel@tonic-gate int lineno, const char *message, FILE *fp);
203*7c478bd9Sstevel@tonic-gate static int _glh_cant_save_history(GlHistory *glh, const char *message,
204*7c478bd9Sstevel@tonic-gate const char *filename, FILE *fp);
205*7c478bd9Sstevel@tonic-gate static int _glh_write_timestamp(FILE *fp, time_t timestamp);
206*7c478bd9Sstevel@tonic-gate static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp);
207*7c478bd9Sstevel@tonic-gate #endif
208*7c478bd9Sstevel@tonic-gate static void _glh_discard_line(GlHistory *glh, GlhLineNode *node);
209*7c478bd9Sstevel@tonic-gate static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id);
210*7c478bd9Sstevel@tonic-gate static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line,
211*7c478bd9Sstevel@tonic-gate size_t n);
212*7c478bd9Sstevel@tonic-gate static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode);
213*7c478bd9Sstevel@tonic-gate static int _glh_prepare_for_recall(GlHistory *glh, char *line);
214*7c478bd9Sstevel@tonic-gate
215*7c478bd9Sstevel@tonic-gate /*
216*7c478bd9Sstevel@tonic-gate * The following structure and functions are used to iterate through
217*7c478bd9Sstevel@tonic-gate * the characters of a segmented history line.
218*7c478bd9Sstevel@tonic-gate */
219*7c478bd9Sstevel@tonic-gate typedef struct {
220*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* The line segment that the next character will */
221*7c478bd9Sstevel@tonic-gate /* be returned from. */
222*7c478bd9Sstevel@tonic-gate int posn; /* The index in the above line segment, containing */
223*7c478bd9Sstevel@tonic-gate /* the next unread character. */
224*7c478bd9Sstevel@tonic-gate char c; /* The current character in the input line */
225*7c478bd9Sstevel@tonic-gate } GlhLineStream;
226*7c478bd9Sstevel@tonic-gate static void glh_init_stream(GlhLineStream *str, GlhHashNode *line);
227*7c478bd9Sstevel@tonic-gate static void glh_step_stream(GlhLineStream *str);
228*7c478bd9Sstevel@tonic-gate
229*7c478bd9Sstevel@tonic-gate /*
230*7c478bd9Sstevel@tonic-gate * See if search prefix contains any globbing characters.
231*7c478bd9Sstevel@tonic-gate */
232*7c478bd9Sstevel@tonic-gate static int glh_contains_glob(GlhHashNode *prefix);
233*7c478bd9Sstevel@tonic-gate /*
234*7c478bd9Sstevel@tonic-gate * Match a line against a search pattern.
235*7c478bd9Sstevel@tonic-gate */
236*7c478bd9Sstevel@tonic-gate static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr);
237*7c478bd9Sstevel@tonic-gate static int glh_matches_range(char c, GlhLineStream *pstr);
238*7c478bd9Sstevel@tonic-gate
239*7c478bd9Sstevel@tonic-gate /*.......................................................................
240*7c478bd9Sstevel@tonic-gate * Create a line history maintenance object.
241*7c478bd9Sstevel@tonic-gate *
242*7c478bd9Sstevel@tonic-gate * Input:
243*7c478bd9Sstevel@tonic-gate * buflen size_t The number of bytes to allocate to the
244*7c478bd9Sstevel@tonic-gate * buffer that is used to record all of the
245*7c478bd9Sstevel@tonic-gate * most recent lines of user input that will fit.
246*7c478bd9Sstevel@tonic-gate * If buflen==0, no buffer will be allocated.
247*7c478bd9Sstevel@tonic-gate * Output:
248*7c478bd9Sstevel@tonic-gate * return GlHistory * The new object, or NULL on error.
249*7c478bd9Sstevel@tonic-gate */
_new_GlHistory(size_t buflen)250*7c478bd9Sstevel@tonic-gate GlHistory *_new_GlHistory(size_t buflen)
251*7c478bd9Sstevel@tonic-gate {
252*7c478bd9Sstevel@tonic-gate GlHistory *glh; /* The object to be returned */
253*7c478bd9Sstevel@tonic-gate int i;
254*7c478bd9Sstevel@tonic-gate /*
255*7c478bd9Sstevel@tonic-gate * Allocate the container.
256*7c478bd9Sstevel@tonic-gate */
257*7c478bd9Sstevel@tonic-gate glh = (GlHistory *) malloc(sizeof(GlHistory));
258*7c478bd9Sstevel@tonic-gate if(!glh) {
259*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
260*7c478bd9Sstevel@tonic-gate return NULL;
261*7c478bd9Sstevel@tonic-gate };
262*7c478bd9Sstevel@tonic-gate /*
263*7c478bd9Sstevel@tonic-gate * Before attempting any operation that might fail, initialize the
264*7c478bd9Sstevel@tonic-gate * container at least up to the point at which it can safely be passed
265*7c478bd9Sstevel@tonic-gate * to _del_GlHistory().
266*7c478bd9Sstevel@tonic-gate */
267*7c478bd9Sstevel@tonic-gate glh->err = NULL;
268*7c478bd9Sstevel@tonic-gate glh->buffer = NULL;
269*7c478bd9Sstevel@tonic-gate glh->nbuff = (buflen+GLH_SEG_SIZE-1) / GLH_SEG_SIZE;
270*7c478bd9Sstevel@tonic-gate glh->unused = NULL;
271*7c478bd9Sstevel@tonic-gate glh->list.node_mem = NULL;
272*7c478bd9Sstevel@tonic-gate glh->list.head = glh->list.tail = NULL;
273*7c478bd9Sstevel@tonic-gate glh->recall = NULL;
274*7c478bd9Sstevel@tonic-gate glh->id_node = NULL;
275*7c478bd9Sstevel@tonic-gate glh->hash.node_mem = NULL;
276*7c478bd9Sstevel@tonic-gate for(i=0; i<GLH_HASH_SIZE; i++)
277*7c478bd9Sstevel@tonic-gate glh->hash.bucket[i].lines = NULL;
278*7c478bd9Sstevel@tonic-gate glh->prefix = NULL;
279*7c478bd9Sstevel@tonic-gate glh->lbuf = NULL;
280*7c478bd9Sstevel@tonic-gate glh->lbuf_dim = 0;
281*7c478bd9Sstevel@tonic-gate glh->nbusy = 0;
282*7c478bd9Sstevel@tonic-gate glh->nfree = glh->nbuff;
283*7c478bd9Sstevel@tonic-gate glh->seq = 0;
284*7c478bd9Sstevel@tonic-gate glh->group = 0;
285*7c478bd9Sstevel@tonic-gate glh->nline = 0;
286*7c478bd9Sstevel@tonic-gate glh->max_lines = -1;
287*7c478bd9Sstevel@tonic-gate glh->enable = 1;
288*7c478bd9Sstevel@tonic-gate /*
289*7c478bd9Sstevel@tonic-gate * Allocate a place to record error messages.
290*7c478bd9Sstevel@tonic-gate */
291*7c478bd9Sstevel@tonic-gate glh->err = _new_ErrMsg();
292*7c478bd9Sstevel@tonic-gate if(!glh->err)
293*7c478bd9Sstevel@tonic-gate return _del_GlHistory(glh);
294*7c478bd9Sstevel@tonic-gate /*
295*7c478bd9Sstevel@tonic-gate * Allocate the buffer, if required.
296*7c478bd9Sstevel@tonic-gate */
297*7c478bd9Sstevel@tonic-gate if(glh->nbuff > 0) {
298*7c478bd9Sstevel@tonic-gate glh->nbuff = glh->nfree;
299*7c478bd9Sstevel@tonic-gate glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * glh->nbuff);
300*7c478bd9Sstevel@tonic-gate if(!glh->buffer) {
301*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
302*7c478bd9Sstevel@tonic-gate return _del_GlHistory(glh);
303*7c478bd9Sstevel@tonic-gate };
304*7c478bd9Sstevel@tonic-gate /*
305*7c478bd9Sstevel@tonic-gate * All nodes of the buffer are currently unused, so link them all into
306*7c478bd9Sstevel@tonic-gate * a list and make glh->unused point to the head of this list.
307*7c478bd9Sstevel@tonic-gate */
308*7c478bd9Sstevel@tonic-gate glh->unused = glh->buffer;
309*7c478bd9Sstevel@tonic-gate for(i=0; i<glh->nbuff-1; i++) {
310*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg = glh->unused + i;
311*7c478bd9Sstevel@tonic-gate seg->next = seg + 1;
312*7c478bd9Sstevel@tonic-gate };
313*7c478bd9Sstevel@tonic-gate glh->unused[i].next = NULL;
314*7c478bd9Sstevel@tonic-gate };
315*7c478bd9Sstevel@tonic-gate /*
316*7c478bd9Sstevel@tonic-gate * Allocate the GlhLineNode freelist.
317*7c478bd9Sstevel@tonic-gate */
318*7c478bd9Sstevel@tonic-gate glh->list.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_LINE_INCR);
319*7c478bd9Sstevel@tonic-gate if(!glh->list.node_mem)
320*7c478bd9Sstevel@tonic-gate return _del_GlHistory(glh);
321*7c478bd9Sstevel@tonic-gate /*
322*7c478bd9Sstevel@tonic-gate * Allocate the GlhHashNode freelist.
323*7c478bd9Sstevel@tonic-gate */
324*7c478bd9Sstevel@tonic-gate glh->hash.node_mem = _new_FreeList(sizeof(GlhLineNode), GLH_HASH_INCR);
325*7c478bd9Sstevel@tonic-gate if(!glh->hash.node_mem)
326*7c478bd9Sstevel@tonic-gate return _del_GlHistory(glh);
327*7c478bd9Sstevel@tonic-gate /*
328*7c478bd9Sstevel@tonic-gate * Allocate the array that _glh_lookup_history() uses to return a
329*7c478bd9Sstevel@tonic-gate * copy of a given history line. This will be resized when necessary.
330*7c478bd9Sstevel@tonic-gate */
331*7c478bd9Sstevel@tonic-gate glh->lbuf_dim = GLH_LBUF_SIZE;
332*7c478bd9Sstevel@tonic-gate glh->lbuf = (char *) malloc(glh->lbuf_dim);
333*7c478bd9Sstevel@tonic-gate if(!glh->lbuf) {
334*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
335*7c478bd9Sstevel@tonic-gate return _del_GlHistory(glh);
336*7c478bd9Sstevel@tonic-gate };
337*7c478bd9Sstevel@tonic-gate return glh;
338*7c478bd9Sstevel@tonic-gate }
339*7c478bd9Sstevel@tonic-gate
340*7c478bd9Sstevel@tonic-gate /*.......................................................................
341*7c478bd9Sstevel@tonic-gate * Delete a GlHistory object.
342*7c478bd9Sstevel@tonic-gate *
343*7c478bd9Sstevel@tonic-gate * Input:
344*7c478bd9Sstevel@tonic-gate * glh GlHistory * The object to be deleted.
345*7c478bd9Sstevel@tonic-gate * Output:
346*7c478bd9Sstevel@tonic-gate * return GlHistory * The deleted object (always NULL).
347*7c478bd9Sstevel@tonic-gate */
_del_GlHistory(GlHistory * glh)348*7c478bd9Sstevel@tonic-gate GlHistory *_del_GlHistory(GlHistory *glh)
349*7c478bd9Sstevel@tonic-gate {
350*7c478bd9Sstevel@tonic-gate if(glh) {
351*7c478bd9Sstevel@tonic-gate /*
352*7c478bd9Sstevel@tonic-gate * Delete the error-message buffer.
353*7c478bd9Sstevel@tonic-gate */
354*7c478bd9Sstevel@tonic-gate glh->err = _del_ErrMsg(glh->err);
355*7c478bd9Sstevel@tonic-gate /*
356*7c478bd9Sstevel@tonic-gate * Delete the buffer.
357*7c478bd9Sstevel@tonic-gate */
358*7c478bd9Sstevel@tonic-gate if(glh->buffer) {
359*7c478bd9Sstevel@tonic-gate free(glh->buffer);
360*7c478bd9Sstevel@tonic-gate glh->buffer = NULL;
361*7c478bd9Sstevel@tonic-gate glh->unused = NULL;
362*7c478bd9Sstevel@tonic-gate };
363*7c478bd9Sstevel@tonic-gate /*
364*7c478bd9Sstevel@tonic-gate * Delete the freelist of GlhLineNode's.
365*7c478bd9Sstevel@tonic-gate */
366*7c478bd9Sstevel@tonic-gate glh->list.node_mem = _del_FreeList(glh->list.node_mem, 1);
367*7c478bd9Sstevel@tonic-gate /*
368*7c478bd9Sstevel@tonic-gate * The contents of the list were deleted by deleting the freelist.
369*7c478bd9Sstevel@tonic-gate */
370*7c478bd9Sstevel@tonic-gate glh->list.head = NULL;
371*7c478bd9Sstevel@tonic-gate glh->list.tail = NULL;
372*7c478bd9Sstevel@tonic-gate /*
373*7c478bd9Sstevel@tonic-gate * Delete the freelist of GlhHashNode's.
374*7c478bd9Sstevel@tonic-gate */
375*7c478bd9Sstevel@tonic-gate glh->hash.node_mem = _del_FreeList(glh->hash.node_mem, 1);
376*7c478bd9Sstevel@tonic-gate /*
377*7c478bd9Sstevel@tonic-gate * Delete the lookup buffer.
378*7c478bd9Sstevel@tonic-gate */
379*7c478bd9Sstevel@tonic-gate if(glh->lbuf)
380*7c478bd9Sstevel@tonic-gate free(glh->lbuf);
381*7c478bd9Sstevel@tonic-gate /*
382*7c478bd9Sstevel@tonic-gate * Delete the container.
383*7c478bd9Sstevel@tonic-gate */
384*7c478bd9Sstevel@tonic-gate free(glh);
385*7c478bd9Sstevel@tonic-gate };
386*7c478bd9Sstevel@tonic-gate return NULL;
387*7c478bd9Sstevel@tonic-gate }
388*7c478bd9Sstevel@tonic-gate
389*7c478bd9Sstevel@tonic-gate /*.......................................................................
390*7c478bd9Sstevel@tonic-gate * Append a new line to the history list, deleting old lines to make
391*7c478bd9Sstevel@tonic-gate * room, if needed.
392*7c478bd9Sstevel@tonic-gate *
393*7c478bd9Sstevel@tonic-gate * Input:
394*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
395*7c478bd9Sstevel@tonic-gate * line char * The line to be archived.
396*7c478bd9Sstevel@tonic-gate * force int Unless this flag is non-zero, empty lines aren't
397*7c478bd9Sstevel@tonic-gate * archived. This flag requests that the line be
398*7c478bd9Sstevel@tonic-gate * archived regardless.
399*7c478bd9Sstevel@tonic-gate * Output:
400*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
401*7c478bd9Sstevel@tonic-gate * 1 - Error.
402*7c478bd9Sstevel@tonic-gate */
_glh_add_history(GlHistory * glh,const char * line,int force)403*7c478bd9Sstevel@tonic-gate int _glh_add_history(GlHistory *glh, const char *line, int force)
404*7c478bd9Sstevel@tonic-gate {
405*7c478bd9Sstevel@tonic-gate int slen; /* The length of the line to be recorded (minus the '\0') */
406*7c478bd9Sstevel@tonic-gate int empty; /* True if the string is empty */
407*7c478bd9Sstevel@tonic-gate const char *nlptr; /* A pointer to a newline character in line[] */
408*7c478bd9Sstevel@tonic-gate GlhHashNode *hnode; /* The hash-table node of the line */
409*7c478bd9Sstevel@tonic-gate GlhLineNode *lnode; /* A node in the time-ordered list of lines */
410*7c478bd9Sstevel@tonic-gate int i;
411*7c478bd9Sstevel@tonic-gate /*
412*7c478bd9Sstevel@tonic-gate * Check the arguments.
413*7c478bd9Sstevel@tonic-gate */
414*7c478bd9Sstevel@tonic-gate if(!glh || !line) {
415*7c478bd9Sstevel@tonic-gate errno = EINVAL;
416*7c478bd9Sstevel@tonic-gate return 1;
417*7c478bd9Sstevel@tonic-gate };
418*7c478bd9Sstevel@tonic-gate /*
419*7c478bd9Sstevel@tonic-gate * Is history enabled?
420*7c478bd9Sstevel@tonic-gate */
421*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
422*7c478bd9Sstevel@tonic-gate return 0;
423*7c478bd9Sstevel@tonic-gate /*
424*7c478bd9Sstevel@tonic-gate * Cancel any ongoing search.
425*7c478bd9Sstevel@tonic-gate */
426*7c478bd9Sstevel@tonic-gate if(_glh_cancel_search(glh))
427*7c478bd9Sstevel@tonic-gate return 1;
428*7c478bd9Sstevel@tonic-gate /*
429*7c478bd9Sstevel@tonic-gate * How long is the string to be recorded, being careful not to include
430*7c478bd9Sstevel@tonic-gate * any terminating '\n' character.
431*7c478bd9Sstevel@tonic-gate */
432*7c478bd9Sstevel@tonic-gate nlptr = strchr(line, '\n');
433*7c478bd9Sstevel@tonic-gate if(nlptr)
434*7c478bd9Sstevel@tonic-gate slen = (nlptr - line);
435*7c478bd9Sstevel@tonic-gate else
436*7c478bd9Sstevel@tonic-gate slen = strlen(line);
437*7c478bd9Sstevel@tonic-gate /*
438*7c478bd9Sstevel@tonic-gate * Is the line empty?
439*7c478bd9Sstevel@tonic-gate */
440*7c478bd9Sstevel@tonic-gate empty = 1;
441*7c478bd9Sstevel@tonic-gate for(i=0; i<slen && empty; i++)
442*7c478bd9Sstevel@tonic-gate empty = isspace((int)(unsigned char) line[i]);
443*7c478bd9Sstevel@tonic-gate /*
444*7c478bd9Sstevel@tonic-gate * If the line is empty, don't add it to the buffer unless explicitly
445*7c478bd9Sstevel@tonic-gate * told to.
446*7c478bd9Sstevel@tonic-gate */
447*7c478bd9Sstevel@tonic-gate if(empty && !force)
448*7c478bd9Sstevel@tonic-gate return 0;
449*7c478bd9Sstevel@tonic-gate /*
450*7c478bd9Sstevel@tonic-gate * Has an upper limit to the number of lines in the history list been
451*7c478bd9Sstevel@tonic-gate * specified?
452*7c478bd9Sstevel@tonic-gate */
453*7c478bd9Sstevel@tonic-gate if(glh->max_lines >= 0) {
454*7c478bd9Sstevel@tonic-gate /*
455*7c478bd9Sstevel@tonic-gate * If necessary, remove old lines until there is room to add one new
456*7c478bd9Sstevel@tonic-gate * line without exceeding the specified line limit.
457*7c478bd9Sstevel@tonic-gate */
458*7c478bd9Sstevel@tonic-gate while(glh->nline > 0 && glh->nline >= glh->max_lines)
459*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, glh->list.head);
460*7c478bd9Sstevel@tonic-gate /*
461*7c478bd9Sstevel@tonic-gate * We can't archive the line if the maximum number of lines allowed is
462*7c478bd9Sstevel@tonic-gate * zero.
463*7c478bd9Sstevel@tonic-gate */
464*7c478bd9Sstevel@tonic-gate if(glh->max_lines == 0)
465*7c478bd9Sstevel@tonic-gate return 0;
466*7c478bd9Sstevel@tonic-gate };
467*7c478bd9Sstevel@tonic-gate /*
468*7c478bd9Sstevel@tonic-gate * Unless already stored, store a copy of the line in the history buffer,
469*7c478bd9Sstevel@tonic-gate * then return a reference-counted hash-node pointer to this copy.
470*7c478bd9Sstevel@tonic-gate */
471*7c478bd9Sstevel@tonic-gate hnode = _glh_acquire_copy(glh, line, slen);
472*7c478bd9Sstevel@tonic-gate if(!hnode) {
473*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG);
474*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
475*7c478bd9Sstevel@tonic-gate return 1;
476*7c478bd9Sstevel@tonic-gate };
477*7c478bd9Sstevel@tonic-gate /*
478*7c478bd9Sstevel@tonic-gate * Allocate a new node in the time-ordered list of lines.
479*7c478bd9Sstevel@tonic-gate */
480*7c478bd9Sstevel@tonic-gate lnode = (GlhLineNode *) _new_FreeListNode(glh->list.node_mem);
481*7c478bd9Sstevel@tonic-gate /*
482*7c478bd9Sstevel@tonic-gate * If a new line-node couldn't be allocated, discard our copy of the
483*7c478bd9Sstevel@tonic-gate * stored line before reporting the error.
484*7c478bd9Sstevel@tonic-gate */
485*7c478bd9Sstevel@tonic-gate if(!lnode) {
486*7c478bd9Sstevel@tonic-gate hnode = _glh_discard_copy(glh, hnode);
487*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "No room to store history line", END_ERR_MSG);
488*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
489*7c478bd9Sstevel@tonic-gate return 1;
490*7c478bd9Sstevel@tonic-gate };
491*7c478bd9Sstevel@tonic-gate /*
492*7c478bd9Sstevel@tonic-gate * Record a pointer to the hash-table record of the line in the new
493*7c478bd9Sstevel@tonic-gate * list node.
494*7c478bd9Sstevel@tonic-gate */
495*7c478bd9Sstevel@tonic-gate lnode->id = glh->seq++;
496*7c478bd9Sstevel@tonic-gate lnode->timestamp = time(NULL);
497*7c478bd9Sstevel@tonic-gate lnode->group = glh->group;
498*7c478bd9Sstevel@tonic-gate lnode->line = hnode;
499*7c478bd9Sstevel@tonic-gate /*
500*7c478bd9Sstevel@tonic-gate * Append the new node to the end of the time-ordered list.
501*7c478bd9Sstevel@tonic-gate */
502*7c478bd9Sstevel@tonic-gate if(glh->list.head)
503*7c478bd9Sstevel@tonic-gate glh->list.tail->next = lnode;
504*7c478bd9Sstevel@tonic-gate else
505*7c478bd9Sstevel@tonic-gate glh->list.head = lnode;
506*7c478bd9Sstevel@tonic-gate lnode->next = NULL;
507*7c478bd9Sstevel@tonic-gate lnode->prev = glh->list.tail;
508*7c478bd9Sstevel@tonic-gate glh->list.tail = lnode;
509*7c478bd9Sstevel@tonic-gate /*
510*7c478bd9Sstevel@tonic-gate * Record the addition of a line to the list.
511*7c478bd9Sstevel@tonic-gate */
512*7c478bd9Sstevel@tonic-gate glh->nline++;
513*7c478bd9Sstevel@tonic-gate return 0;
514*7c478bd9Sstevel@tonic-gate }
515*7c478bd9Sstevel@tonic-gate
516*7c478bd9Sstevel@tonic-gate /*.......................................................................
517*7c478bd9Sstevel@tonic-gate * Recall the next oldest line that has the search prefix last recorded
518*7c478bd9Sstevel@tonic-gate * by _glh_search_prefix().
519*7c478bd9Sstevel@tonic-gate *
520*7c478bd9Sstevel@tonic-gate * Input:
521*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
522*7c478bd9Sstevel@tonic-gate * line char * The input line buffer. On input this should contain
523*7c478bd9Sstevel@tonic-gate * the current input line, and on output, if anything
524*7c478bd9Sstevel@tonic-gate * was found, its contents will have been replaced
525*7c478bd9Sstevel@tonic-gate * with the matching line.
526*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimension of the line buffer.
527*7c478bd9Sstevel@tonic-gate * Output:
528*7c478bd9Sstevel@tonic-gate * return char * A pointer to line[0], or NULL if not found.
529*7c478bd9Sstevel@tonic-gate */
_glh_find_backwards(GlHistory * glh,char * line,size_t dim)530*7c478bd9Sstevel@tonic-gate char *_glh_find_backwards(GlHistory *glh, char *line, size_t dim)
531*7c478bd9Sstevel@tonic-gate {
532*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line location node being checked */
533*7c478bd9Sstevel@tonic-gate GlhHashNode *old_line; /* The previous recalled line */
534*7c478bd9Sstevel@tonic-gate /*
535*7c478bd9Sstevel@tonic-gate * Check the arguments.
536*7c478bd9Sstevel@tonic-gate */
537*7c478bd9Sstevel@tonic-gate if(!glh || !line) {
538*7c478bd9Sstevel@tonic-gate if(glh)
539*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
540*7c478bd9Sstevel@tonic-gate errno = EINVAL;
541*7c478bd9Sstevel@tonic-gate return NULL;
542*7c478bd9Sstevel@tonic-gate };
543*7c478bd9Sstevel@tonic-gate /*
544*7c478bd9Sstevel@tonic-gate * Is history enabled?
545*7c478bd9Sstevel@tonic-gate */
546*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
547*7c478bd9Sstevel@tonic-gate return NULL;
548*7c478bd9Sstevel@tonic-gate /*
549*7c478bd9Sstevel@tonic-gate * Check the line dimensions.
550*7c478bd9Sstevel@tonic-gate */
551*7c478bd9Sstevel@tonic-gate if(dim < strlen(line) + 1) {
552*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)",
553*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
554*7c478bd9Sstevel@tonic-gate errno = EINVAL;
555*7c478bd9Sstevel@tonic-gate return NULL;
556*7c478bd9Sstevel@tonic-gate };
557*7c478bd9Sstevel@tonic-gate /*
558*7c478bd9Sstevel@tonic-gate * Preserve the input line if needed.
559*7c478bd9Sstevel@tonic-gate */
560*7c478bd9Sstevel@tonic-gate if(_glh_prepare_for_recall(glh, line))
561*7c478bd9Sstevel@tonic-gate return NULL;
562*7c478bd9Sstevel@tonic-gate /*
563*7c478bd9Sstevel@tonic-gate * From where should we start the search?
564*7c478bd9Sstevel@tonic-gate */
565*7c478bd9Sstevel@tonic-gate if(glh->recall) {
566*7c478bd9Sstevel@tonic-gate node = glh->recall->prev;
567*7c478bd9Sstevel@tonic-gate old_line = glh->recall->line;
568*7c478bd9Sstevel@tonic-gate } else {
569*7c478bd9Sstevel@tonic-gate node = glh->list.tail;
570*7c478bd9Sstevel@tonic-gate old_line = NULL;
571*7c478bd9Sstevel@tonic-gate };
572*7c478bd9Sstevel@tonic-gate /*
573*7c478bd9Sstevel@tonic-gate * Search backwards through the list for the first match with the
574*7c478bd9Sstevel@tonic-gate * prefix string that differs from the last line that was recalled.
575*7c478bd9Sstevel@tonic-gate */
576*7c478bd9Sstevel@tonic-gate while(node && (node->group != glh->group || node->line == old_line ||
577*7c478bd9Sstevel@tonic-gate !_glh_line_matches_prefix(node->line, glh->prefix)))
578*7c478bd9Sstevel@tonic-gate node = node->prev;
579*7c478bd9Sstevel@tonic-gate /*
580*7c478bd9Sstevel@tonic-gate * Was a matching line found?
581*7c478bd9Sstevel@tonic-gate */
582*7c478bd9Sstevel@tonic-gate if(node) {
583*7c478bd9Sstevel@tonic-gate /*
584*7c478bd9Sstevel@tonic-gate * Recall the found node as the starting point for subsequent
585*7c478bd9Sstevel@tonic-gate * searches.
586*7c478bd9Sstevel@tonic-gate */
587*7c478bd9Sstevel@tonic-gate glh->recall = node;
588*7c478bd9Sstevel@tonic-gate /*
589*7c478bd9Sstevel@tonic-gate * Copy the matching line into the provided line buffer.
590*7c478bd9Sstevel@tonic-gate */
591*7c478bd9Sstevel@tonic-gate _glh_return_line(node->line, line, dim);
592*7c478bd9Sstevel@tonic-gate /*
593*7c478bd9Sstevel@tonic-gate * Return it.
594*7c478bd9Sstevel@tonic-gate */
595*7c478bd9Sstevel@tonic-gate return line;
596*7c478bd9Sstevel@tonic-gate };
597*7c478bd9Sstevel@tonic-gate /*
598*7c478bd9Sstevel@tonic-gate * No match was found.
599*7c478bd9Sstevel@tonic-gate */
600*7c478bd9Sstevel@tonic-gate return NULL;
601*7c478bd9Sstevel@tonic-gate }
602*7c478bd9Sstevel@tonic-gate
603*7c478bd9Sstevel@tonic-gate /*.......................................................................
604*7c478bd9Sstevel@tonic-gate * Recall the next newest line that has the search prefix last recorded
605*7c478bd9Sstevel@tonic-gate * by _glh_search_prefix().
606*7c478bd9Sstevel@tonic-gate *
607*7c478bd9Sstevel@tonic-gate * Input:
608*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
609*7c478bd9Sstevel@tonic-gate * line char * The input line buffer. On input this should contain
610*7c478bd9Sstevel@tonic-gate * the current input line, and on output, if anything
611*7c478bd9Sstevel@tonic-gate * was found, its contents will have been replaced
612*7c478bd9Sstevel@tonic-gate * with the matching line.
613*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimensions of the line buffer.
614*7c478bd9Sstevel@tonic-gate * Output:
615*7c478bd9Sstevel@tonic-gate * return char * The line requested, or NULL if no matching line
616*7c478bd9Sstevel@tonic-gate * was found.
617*7c478bd9Sstevel@tonic-gate */
_glh_find_forwards(GlHistory * glh,char * line,size_t dim)618*7c478bd9Sstevel@tonic-gate char *_glh_find_forwards(GlHistory *glh, char *line, size_t dim)
619*7c478bd9Sstevel@tonic-gate {
620*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line location node being checked */
621*7c478bd9Sstevel@tonic-gate GlhHashNode *old_line; /* The previous recalled line */
622*7c478bd9Sstevel@tonic-gate /*
623*7c478bd9Sstevel@tonic-gate * Check the arguments.
624*7c478bd9Sstevel@tonic-gate */
625*7c478bd9Sstevel@tonic-gate if(!glh || !line) {
626*7c478bd9Sstevel@tonic-gate if(glh)
627*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
628*7c478bd9Sstevel@tonic-gate errno = EINVAL;
629*7c478bd9Sstevel@tonic-gate return NULL;
630*7c478bd9Sstevel@tonic-gate };
631*7c478bd9Sstevel@tonic-gate /*
632*7c478bd9Sstevel@tonic-gate * Is history enabled?
633*7c478bd9Sstevel@tonic-gate */
634*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
635*7c478bd9Sstevel@tonic-gate return NULL;
636*7c478bd9Sstevel@tonic-gate /*
637*7c478bd9Sstevel@tonic-gate * Check the line dimensions.
638*7c478bd9Sstevel@tonic-gate */
639*7c478bd9Sstevel@tonic-gate if(dim < strlen(line) + 1) {
640*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)",
641*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
642*7c478bd9Sstevel@tonic-gate errno = EINVAL;
643*7c478bd9Sstevel@tonic-gate return NULL;
644*7c478bd9Sstevel@tonic-gate };
645*7c478bd9Sstevel@tonic-gate /*
646*7c478bd9Sstevel@tonic-gate * From where should we start the search?
647*7c478bd9Sstevel@tonic-gate */
648*7c478bd9Sstevel@tonic-gate if(glh->recall) {
649*7c478bd9Sstevel@tonic-gate node = glh->recall->next;
650*7c478bd9Sstevel@tonic-gate old_line = glh->recall->line;
651*7c478bd9Sstevel@tonic-gate } else {
652*7c478bd9Sstevel@tonic-gate return NULL;
653*7c478bd9Sstevel@tonic-gate };
654*7c478bd9Sstevel@tonic-gate /*
655*7c478bd9Sstevel@tonic-gate * Search forwards through the list for the first match with the
656*7c478bd9Sstevel@tonic-gate * prefix string.
657*7c478bd9Sstevel@tonic-gate */
658*7c478bd9Sstevel@tonic-gate while(node && (node->group != glh->group || node->line == old_line ||
659*7c478bd9Sstevel@tonic-gate !_glh_line_matches_prefix(node->line, glh->prefix)))
660*7c478bd9Sstevel@tonic-gate node = node->next;
661*7c478bd9Sstevel@tonic-gate /*
662*7c478bd9Sstevel@tonic-gate * Was a matching line found?
663*7c478bd9Sstevel@tonic-gate */
664*7c478bd9Sstevel@tonic-gate if(node) {
665*7c478bd9Sstevel@tonic-gate /*
666*7c478bd9Sstevel@tonic-gate * Copy the matching line into the provided line buffer.
667*7c478bd9Sstevel@tonic-gate */
668*7c478bd9Sstevel@tonic-gate _glh_return_line(node->line, line, dim);
669*7c478bd9Sstevel@tonic-gate /*
670*7c478bd9Sstevel@tonic-gate * Record the starting point of the next search.
671*7c478bd9Sstevel@tonic-gate */
672*7c478bd9Sstevel@tonic-gate glh->recall = node;
673*7c478bd9Sstevel@tonic-gate /*
674*7c478bd9Sstevel@tonic-gate * If we just returned the line that was being entered when the search
675*7c478bd9Sstevel@tonic-gate * session first started, cancel the search.
676*7c478bd9Sstevel@tonic-gate */
677*7c478bd9Sstevel@tonic-gate if(node == glh->list.tail)
678*7c478bd9Sstevel@tonic-gate _glh_cancel_search(glh);
679*7c478bd9Sstevel@tonic-gate /*
680*7c478bd9Sstevel@tonic-gate * Return the matching line to the user.
681*7c478bd9Sstevel@tonic-gate */
682*7c478bd9Sstevel@tonic-gate return line;
683*7c478bd9Sstevel@tonic-gate };
684*7c478bd9Sstevel@tonic-gate /*
685*7c478bd9Sstevel@tonic-gate * No match was found.
686*7c478bd9Sstevel@tonic-gate */
687*7c478bd9Sstevel@tonic-gate return NULL;
688*7c478bd9Sstevel@tonic-gate }
689*7c478bd9Sstevel@tonic-gate
690*7c478bd9Sstevel@tonic-gate /*.......................................................................
691*7c478bd9Sstevel@tonic-gate * If a search is in progress, cancel it.
692*7c478bd9Sstevel@tonic-gate *
693*7c478bd9Sstevel@tonic-gate * This involves discarding the line that was temporarily saved by
694*7c478bd9Sstevel@tonic-gate * _glh_find_backwards() when the search was originally started,
695*7c478bd9Sstevel@tonic-gate * and reseting the search iteration pointer to NULL.
696*7c478bd9Sstevel@tonic-gate *
697*7c478bd9Sstevel@tonic-gate * Input:
698*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
699*7c478bd9Sstevel@tonic-gate * Output:
700*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
701*7c478bd9Sstevel@tonic-gate * 1 - Error.
702*7c478bd9Sstevel@tonic-gate */
_glh_cancel_search(GlHistory * glh)703*7c478bd9Sstevel@tonic-gate int _glh_cancel_search(GlHistory *glh)
704*7c478bd9Sstevel@tonic-gate {
705*7c478bd9Sstevel@tonic-gate /*
706*7c478bd9Sstevel@tonic-gate * Check the arguments.
707*7c478bd9Sstevel@tonic-gate */
708*7c478bd9Sstevel@tonic-gate if(!glh) {
709*7c478bd9Sstevel@tonic-gate errno = EINVAL;
710*7c478bd9Sstevel@tonic-gate return 1;
711*7c478bd9Sstevel@tonic-gate };
712*7c478bd9Sstevel@tonic-gate /*
713*7c478bd9Sstevel@tonic-gate * If there wasn't a search in progress, do nothing.
714*7c478bd9Sstevel@tonic-gate */
715*7c478bd9Sstevel@tonic-gate if(!glh->recall)
716*7c478bd9Sstevel@tonic-gate return 0;
717*7c478bd9Sstevel@tonic-gate /*
718*7c478bd9Sstevel@tonic-gate * Reset the search pointers. Note that it is essential to set
719*7c478bd9Sstevel@tonic-gate * glh->recall to NULL before calling _glh_discard_line(), to avoid an
720*7c478bd9Sstevel@tonic-gate * infinite recursion.
721*7c478bd9Sstevel@tonic-gate */
722*7c478bd9Sstevel@tonic-gate glh->recall = NULL;
723*7c478bd9Sstevel@tonic-gate /*
724*7c478bd9Sstevel@tonic-gate * Delete the node of the preserved line.
725*7c478bd9Sstevel@tonic-gate */
726*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, glh->list.tail);
727*7c478bd9Sstevel@tonic-gate return 0;
728*7c478bd9Sstevel@tonic-gate }
729*7c478bd9Sstevel@tonic-gate
730*7c478bd9Sstevel@tonic-gate /*.......................................................................
731*7c478bd9Sstevel@tonic-gate * Set the prefix of subsequent history searches.
732*7c478bd9Sstevel@tonic-gate *
733*7c478bd9Sstevel@tonic-gate * Input:
734*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
735*7c478bd9Sstevel@tonic-gate * line const char * The command line who's prefix is to be used.
736*7c478bd9Sstevel@tonic-gate * prefix_len int The length of the prefix.
737*7c478bd9Sstevel@tonic-gate * Output:
738*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
739*7c478bd9Sstevel@tonic-gate * 1 - Error.
740*7c478bd9Sstevel@tonic-gate */
_glh_search_prefix(GlHistory * glh,const char * line,int prefix_len)741*7c478bd9Sstevel@tonic-gate int _glh_search_prefix(GlHistory *glh, const char *line, int prefix_len)
742*7c478bd9Sstevel@tonic-gate {
743*7c478bd9Sstevel@tonic-gate /*
744*7c478bd9Sstevel@tonic-gate * Check the arguments.
745*7c478bd9Sstevel@tonic-gate */
746*7c478bd9Sstevel@tonic-gate if(!glh) {
747*7c478bd9Sstevel@tonic-gate errno = EINVAL;
748*7c478bd9Sstevel@tonic-gate return 1;
749*7c478bd9Sstevel@tonic-gate };
750*7c478bd9Sstevel@tonic-gate /*
751*7c478bd9Sstevel@tonic-gate * Is history enabled?
752*7c478bd9Sstevel@tonic-gate */
753*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
754*7c478bd9Sstevel@tonic-gate return 0;
755*7c478bd9Sstevel@tonic-gate /*
756*7c478bd9Sstevel@tonic-gate * Discard any existing prefix.
757*7c478bd9Sstevel@tonic-gate */
758*7c478bd9Sstevel@tonic-gate glh->prefix = _glh_discard_copy(glh, glh->prefix);
759*7c478bd9Sstevel@tonic-gate /*
760*7c478bd9Sstevel@tonic-gate * Only store a copy of the prefix string if it isn't a zero-length string.
761*7c478bd9Sstevel@tonic-gate */
762*7c478bd9Sstevel@tonic-gate if(prefix_len > 0) {
763*7c478bd9Sstevel@tonic-gate /*
764*7c478bd9Sstevel@tonic-gate * Get a reference-counted copy of the prefix from the history cache buffer.
765*7c478bd9Sstevel@tonic-gate */
766*7c478bd9Sstevel@tonic-gate glh->prefix = _glh_acquire_copy(glh, line, prefix_len);
767*7c478bd9Sstevel@tonic-gate /*
768*7c478bd9Sstevel@tonic-gate * Was there insufficient buffer space?
769*7c478bd9Sstevel@tonic-gate */
770*7c478bd9Sstevel@tonic-gate if(!glh->prefix) {
771*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "The search prefix is too long to store",
772*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
773*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
774*7c478bd9Sstevel@tonic-gate return 1;
775*7c478bd9Sstevel@tonic-gate };
776*7c478bd9Sstevel@tonic-gate };
777*7c478bd9Sstevel@tonic-gate return 0;
778*7c478bd9Sstevel@tonic-gate }
779*7c478bd9Sstevel@tonic-gate
780*7c478bd9Sstevel@tonic-gate /*.......................................................................
781*7c478bd9Sstevel@tonic-gate * Recall the oldest recorded line.
782*7c478bd9Sstevel@tonic-gate *
783*7c478bd9Sstevel@tonic-gate * Input:
784*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
785*7c478bd9Sstevel@tonic-gate * line char * The input line buffer. On input this should contain
786*7c478bd9Sstevel@tonic-gate * the current input line, and on output, its contents
787*7c478bd9Sstevel@tonic-gate * will have been replaced with the oldest line.
788*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimensions of the line buffer.
789*7c478bd9Sstevel@tonic-gate * Output:
790*7c478bd9Sstevel@tonic-gate * return char * A pointer to line[0], or NULL if not found.
791*7c478bd9Sstevel@tonic-gate */
_glh_oldest_line(GlHistory * glh,char * line,size_t dim)792*7c478bd9Sstevel@tonic-gate char *_glh_oldest_line(GlHistory *glh, char *line, size_t dim)
793*7c478bd9Sstevel@tonic-gate {
794*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line location node being checked */
795*7c478bd9Sstevel@tonic-gate /*
796*7c478bd9Sstevel@tonic-gate * Check the arguments.
797*7c478bd9Sstevel@tonic-gate */
798*7c478bd9Sstevel@tonic-gate if(!glh || !line) {
799*7c478bd9Sstevel@tonic-gate if(glh)
800*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
801*7c478bd9Sstevel@tonic-gate errno = EINVAL;
802*7c478bd9Sstevel@tonic-gate return NULL;
803*7c478bd9Sstevel@tonic-gate };
804*7c478bd9Sstevel@tonic-gate /*
805*7c478bd9Sstevel@tonic-gate * Is history enabled?
806*7c478bd9Sstevel@tonic-gate */
807*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
808*7c478bd9Sstevel@tonic-gate return NULL;
809*7c478bd9Sstevel@tonic-gate /*
810*7c478bd9Sstevel@tonic-gate * Check the line dimensions.
811*7c478bd9Sstevel@tonic-gate */
812*7c478bd9Sstevel@tonic-gate if(dim < strlen(line) + 1) {
813*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)",
814*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
815*7c478bd9Sstevel@tonic-gate errno = EINVAL;
816*7c478bd9Sstevel@tonic-gate return NULL;
817*7c478bd9Sstevel@tonic-gate };
818*7c478bd9Sstevel@tonic-gate /*
819*7c478bd9Sstevel@tonic-gate * Preserve the input line if needed.
820*7c478bd9Sstevel@tonic-gate */
821*7c478bd9Sstevel@tonic-gate if(_glh_prepare_for_recall(glh, line))
822*7c478bd9Sstevel@tonic-gate return NULL;
823*7c478bd9Sstevel@tonic-gate /*
824*7c478bd9Sstevel@tonic-gate * Locate the oldest line that belongs to the current group.
825*7c478bd9Sstevel@tonic-gate */
826*7c478bd9Sstevel@tonic-gate for(node=glh->list.head; node && node->group != glh->group;
827*7c478bd9Sstevel@tonic-gate node = node->next)
828*7c478bd9Sstevel@tonic-gate ;
829*7c478bd9Sstevel@tonic-gate /*
830*7c478bd9Sstevel@tonic-gate * No line found?
831*7c478bd9Sstevel@tonic-gate */
832*7c478bd9Sstevel@tonic-gate if(!node)
833*7c478bd9Sstevel@tonic-gate return NULL;
834*7c478bd9Sstevel@tonic-gate /*
835*7c478bd9Sstevel@tonic-gate * Record the above node as the starting point for subsequent
836*7c478bd9Sstevel@tonic-gate * searches.
837*7c478bd9Sstevel@tonic-gate */
838*7c478bd9Sstevel@tonic-gate glh->recall = node;
839*7c478bd9Sstevel@tonic-gate /*
840*7c478bd9Sstevel@tonic-gate * Copy the recalled line into the provided line buffer.
841*7c478bd9Sstevel@tonic-gate */
842*7c478bd9Sstevel@tonic-gate _glh_return_line(node->line, line, dim);
843*7c478bd9Sstevel@tonic-gate /*
844*7c478bd9Sstevel@tonic-gate * If we just returned the line that was being entered when the search
845*7c478bd9Sstevel@tonic-gate * session first started, cancel the search.
846*7c478bd9Sstevel@tonic-gate */
847*7c478bd9Sstevel@tonic-gate if(node == glh->list.tail)
848*7c478bd9Sstevel@tonic-gate _glh_cancel_search(glh);
849*7c478bd9Sstevel@tonic-gate return line;
850*7c478bd9Sstevel@tonic-gate }
851*7c478bd9Sstevel@tonic-gate
852*7c478bd9Sstevel@tonic-gate /*.......................................................................
853*7c478bd9Sstevel@tonic-gate * Recall the line that was being entered when the search started.
854*7c478bd9Sstevel@tonic-gate *
855*7c478bd9Sstevel@tonic-gate * Input:
856*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
857*7c478bd9Sstevel@tonic-gate * line char * The input line buffer. On input this should contain
858*7c478bd9Sstevel@tonic-gate * the current input line, and on output, its contents
859*7c478bd9Sstevel@tonic-gate * will have been replaced with the line that was
860*7c478bd9Sstevel@tonic-gate * being entered when the search was started.
861*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimensions of the line buffer.
862*7c478bd9Sstevel@tonic-gate * Output:
863*7c478bd9Sstevel@tonic-gate * return char * A pointer to line[0], or NULL if not found.
864*7c478bd9Sstevel@tonic-gate */
_glh_current_line(GlHistory * glh,char * line,size_t dim)865*7c478bd9Sstevel@tonic-gate char *_glh_current_line(GlHistory *glh, char *line, size_t dim)
866*7c478bd9Sstevel@tonic-gate {
867*7c478bd9Sstevel@tonic-gate /*
868*7c478bd9Sstevel@tonic-gate * Check the arguments.
869*7c478bd9Sstevel@tonic-gate */
870*7c478bd9Sstevel@tonic-gate if(!glh || !line) {
871*7c478bd9Sstevel@tonic-gate if(glh)
872*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
873*7c478bd9Sstevel@tonic-gate errno = EINVAL;
874*7c478bd9Sstevel@tonic-gate return NULL;
875*7c478bd9Sstevel@tonic-gate };
876*7c478bd9Sstevel@tonic-gate /*
877*7c478bd9Sstevel@tonic-gate * If history isn't enabled, or no history search has yet been started,
878*7c478bd9Sstevel@tonic-gate * ignore the call.
879*7c478bd9Sstevel@tonic-gate */
880*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0 || !glh->recall)
881*7c478bd9Sstevel@tonic-gate return NULL;
882*7c478bd9Sstevel@tonic-gate /*
883*7c478bd9Sstevel@tonic-gate * Check the line dimensions.
884*7c478bd9Sstevel@tonic-gate */
885*7c478bd9Sstevel@tonic-gate if(dim < strlen(line) + 1) {
886*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "'dim' argument inconsistent with strlen(line)",
887*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
888*7c478bd9Sstevel@tonic-gate errno = EINVAL;
889*7c478bd9Sstevel@tonic-gate return NULL;
890*7c478bd9Sstevel@tonic-gate };
891*7c478bd9Sstevel@tonic-gate /*
892*7c478bd9Sstevel@tonic-gate * Copy the recalled line into the provided line buffer.
893*7c478bd9Sstevel@tonic-gate */
894*7c478bd9Sstevel@tonic-gate _glh_return_line(glh->list.tail->line, line, dim);
895*7c478bd9Sstevel@tonic-gate /*
896*7c478bd9Sstevel@tonic-gate * Since we have returned to the starting point of the search, cancel it.
897*7c478bd9Sstevel@tonic-gate */
898*7c478bd9Sstevel@tonic-gate _glh_cancel_search(glh);
899*7c478bd9Sstevel@tonic-gate return line;
900*7c478bd9Sstevel@tonic-gate }
901*7c478bd9Sstevel@tonic-gate
902*7c478bd9Sstevel@tonic-gate /*.......................................................................
903*7c478bd9Sstevel@tonic-gate * Query the id of a history line offset by a given number of lines from
904*7c478bd9Sstevel@tonic-gate * the one that is currently being recalled. If a recall session isn't
905*7c478bd9Sstevel@tonic-gate * in progress, or the offset points outside the history list, 0 is
906*7c478bd9Sstevel@tonic-gate * returned.
907*7c478bd9Sstevel@tonic-gate *
908*7c478bd9Sstevel@tonic-gate * Input:
909*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
910*7c478bd9Sstevel@tonic-gate * offset int The line offset (0 for the current line, < 0
911*7c478bd9Sstevel@tonic-gate * for an older line, > 0 for a newer line.
912*7c478bd9Sstevel@tonic-gate * Output:
913*7c478bd9Sstevel@tonic-gate * return GlhLineID The identifier of the line that is currently
914*7c478bd9Sstevel@tonic-gate * being recalled, or 0 if no recall session is
915*7c478bd9Sstevel@tonic-gate * currently in progress.
916*7c478bd9Sstevel@tonic-gate */
_glh_line_id(GlHistory * glh,int offset)917*7c478bd9Sstevel@tonic-gate GlhLineID _glh_line_id(GlHistory *glh, int offset)
918*7c478bd9Sstevel@tonic-gate {
919*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line location node being checked */
920*7c478bd9Sstevel@tonic-gate /*
921*7c478bd9Sstevel@tonic-gate * Is history enabled?
922*7c478bd9Sstevel@tonic-gate */
923*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
924*7c478bd9Sstevel@tonic-gate return 0;
925*7c478bd9Sstevel@tonic-gate /*
926*7c478bd9Sstevel@tonic-gate * Search forward 'offset' lines to find the required line.
927*7c478bd9Sstevel@tonic-gate */
928*7c478bd9Sstevel@tonic-gate if(offset >= 0) {
929*7c478bd9Sstevel@tonic-gate for(node=glh->recall; node && offset != 0; node=node->next) {
930*7c478bd9Sstevel@tonic-gate if(node->group == glh->group)
931*7c478bd9Sstevel@tonic-gate offset--;
932*7c478bd9Sstevel@tonic-gate };
933*7c478bd9Sstevel@tonic-gate } else {
934*7c478bd9Sstevel@tonic-gate for(node=glh->recall; node && offset != 0; node=node->prev) {
935*7c478bd9Sstevel@tonic-gate if(node->group == glh->group)
936*7c478bd9Sstevel@tonic-gate offset++;
937*7c478bd9Sstevel@tonic-gate };
938*7c478bd9Sstevel@tonic-gate };
939*7c478bd9Sstevel@tonic-gate return node ? node->id : 0;
940*7c478bd9Sstevel@tonic-gate }
941*7c478bd9Sstevel@tonic-gate
942*7c478bd9Sstevel@tonic-gate /*.......................................................................
943*7c478bd9Sstevel@tonic-gate * Recall a line by its history buffer ID. If the line is no longer
944*7c478bd9Sstevel@tonic-gate * in the buffer, or the id is zero, NULL is returned.
945*7c478bd9Sstevel@tonic-gate *
946*7c478bd9Sstevel@tonic-gate * Input:
947*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
948*7c478bd9Sstevel@tonic-gate * id GlhLineID The ID of the line to be returned.
949*7c478bd9Sstevel@tonic-gate * line char * The input line buffer. On input this should contain
950*7c478bd9Sstevel@tonic-gate * the current input line, and on output, its contents
951*7c478bd9Sstevel@tonic-gate * will have been replaced with the saved line.
952*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimensions of the line buffer.
953*7c478bd9Sstevel@tonic-gate * Output:
954*7c478bd9Sstevel@tonic-gate * return char * A pointer to line[0], or NULL if not found.
955*7c478bd9Sstevel@tonic-gate */
_glh_recall_line(GlHistory * glh,GlhLineID id,char * line,size_t dim)956*7c478bd9Sstevel@tonic-gate char *_glh_recall_line(GlHistory *glh, GlhLineID id, char *line, size_t dim)
957*7c478bd9Sstevel@tonic-gate {
958*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line location node being checked */
959*7c478bd9Sstevel@tonic-gate /*
960*7c478bd9Sstevel@tonic-gate * Is history enabled?
961*7c478bd9Sstevel@tonic-gate */
962*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->buffer || glh->max_lines == 0)
963*7c478bd9Sstevel@tonic-gate return NULL;
964*7c478bd9Sstevel@tonic-gate /*
965*7c478bd9Sstevel@tonic-gate * Preserve the input line if needed.
966*7c478bd9Sstevel@tonic-gate */
967*7c478bd9Sstevel@tonic-gate if(_glh_prepare_for_recall(glh, line))
968*7c478bd9Sstevel@tonic-gate return NULL;
969*7c478bd9Sstevel@tonic-gate /*
970*7c478bd9Sstevel@tonic-gate * Search for the specified line.
971*7c478bd9Sstevel@tonic-gate */
972*7c478bd9Sstevel@tonic-gate node = _glh_find_id(glh, id);
973*7c478bd9Sstevel@tonic-gate /*
974*7c478bd9Sstevel@tonic-gate * Not found?
975*7c478bd9Sstevel@tonic-gate */
976*7c478bd9Sstevel@tonic-gate if(!node || node->group != glh->group)
977*7c478bd9Sstevel@tonic-gate return NULL;
978*7c478bd9Sstevel@tonic-gate /*
979*7c478bd9Sstevel@tonic-gate * Record the node of the matching line as the starting point
980*7c478bd9Sstevel@tonic-gate * for subsequent searches.
981*7c478bd9Sstevel@tonic-gate */
982*7c478bd9Sstevel@tonic-gate glh->recall = node;
983*7c478bd9Sstevel@tonic-gate /*
984*7c478bd9Sstevel@tonic-gate * Copy the recalled line into the provided line buffer.
985*7c478bd9Sstevel@tonic-gate */
986*7c478bd9Sstevel@tonic-gate _glh_return_line(node->line, line, dim);
987*7c478bd9Sstevel@tonic-gate return line;
988*7c478bd9Sstevel@tonic-gate }
989*7c478bd9Sstevel@tonic-gate
990*7c478bd9Sstevel@tonic-gate /*.......................................................................
991*7c478bd9Sstevel@tonic-gate * Save the current history in a specified file.
992*7c478bd9Sstevel@tonic-gate *
993*7c478bd9Sstevel@tonic-gate * Input:
994*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
995*7c478bd9Sstevel@tonic-gate * filename const char * The name of the new file to record the
996*7c478bd9Sstevel@tonic-gate * history in.
997*7c478bd9Sstevel@tonic-gate * comment const char * Extra information such as timestamps will
998*7c478bd9Sstevel@tonic-gate * be recorded on a line started with this
999*7c478bd9Sstevel@tonic-gate * string, the idea being that the file can
1000*7c478bd9Sstevel@tonic-gate * double as a command file. Specify "" if
1001*7c478bd9Sstevel@tonic-gate * you don't care.
1002*7c478bd9Sstevel@tonic-gate * max_lines int The maximum number of lines to save, or -1
1003*7c478bd9Sstevel@tonic-gate * to save all of the lines in the history
1004*7c478bd9Sstevel@tonic-gate * list.
1005*7c478bd9Sstevel@tonic-gate * Output:
1006*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1007*7c478bd9Sstevel@tonic-gate * 1 - Error.
1008*7c478bd9Sstevel@tonic-gate */
_glh_save_history(GlHistory * glh,const char * filename,const char * comment,int max_lines)1009*7c478bd9Sstevel@tonic-gate int _glh_save_history(GlHistory *glh, const char *filename, const char *comment,
1010*7c478bd9Sstevel@tonic-gate int max_lines)
1011*7c478bd9Sstevel@tonic-gate {
1012*7c478bd9Sstevel@tonic-gate #ifdef WITHOUT_FILE_SYSTEM
1013*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "Can't save history without filesystem access",
1014*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
1015*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1016*7c478bd9Sstevel@tonic-gate return 1;
1017*7c478bd9Sstevel@tonic-gate #else
1018*7c478bd9Sstevel@tonic-gate FILE *fp; /* The output file */
1019*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line being saved */
1020*7c478bd9Sstevel@tonic-gate GlhLineNode *head; /* The head of the list of lines to be saved */
1021*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* One segment of a line being saved */
1022*7c478bd9Sstevel@tonic-gate /*
1023*7c478bd9Sstevel@tonic-gate * Check the arguments.
1024*7c478bd9Sstevel@tonic-gate */
1025*7c478bd9Sstevel@tonic-gate if(!glh || !filename || !comment) {
1026*7c478bd9Sstevel@tonic-gate if(glh)
1027*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
1028*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1029*7c478bd9Sstevel@tonic-gate return 1;
1030*7c478bd9Sstevel@tonic-gate };
1031*7c478bd9Sstevel@tonic-gate /*
1032*7c478bd9Sstevel@tonic-gate * Attempt to open the specified file.
1033*7c478bd9Sstevel@tonic-gate */
1034*7c478bd9Sstevel@tonic-gate fp = fopen(filename, "w");
1035*7c478bd9Sstevel@tonic-gate if(!fp)
1036*7c478bd9Sstevel@tonic-gate return _glh_cant_save_history(glh, "Can't open", filename, NULL);
1037*7c478bd9Sstevel@tonic-gate /*
1038*7c478bd9Sstevel@tonic-gate * If a ceiling on the number of lines to save was specified, count
1039*7c478bd9Sstevel@tonic-gate * that number of lines backwards, to find the first line to be saved.
1040*7c478bd9Sstevel@tonic-gate */
1041*7c478bd9Sstevel@tonic-gate head = NULL;
1042*7c478bd9Sstevel@tonic-gate if(max_lines >= 0) {
1043*7c478bd9Sstevel@tonic-gate for(head=glh->list.tail; head && --max_lines > 0; head=head->prev)
1044*7c478bd9Sstevel@tonic-gate ;
1045*7c478bd9Sstevel@tonic-gate };
1046*7c478bd9Sstevel@tonic-gate if(!head)
1047*7c478bd9Sstevel@tonic-gate head = glh->list.head;
1048*7c478bd9Sstevel@tonic-gate /*
1049*7c478bd9Sstevel@tonic-gate * Write the contents of the history buffer to the history file, writing
1050*7c478bd9Sstevel@tonic-gate * associated data such as timestamps, to a line starting with the
1051*7c478bd9Sstevel@tonic-gate * specified comment string.
1052*7c478bd9Sstevel@tonic-gate */
1053*7c478bd9Sstevel@tonic-gate for(node=head; node; node=node->next) {
1054*7c478bd9Sstevel@tonic-gate /*
1055*7c478bd9Sstevel@tonic-gate * Write peripheral information associated with the line, as a comment.
1056*7c478bd9Sstevel@tonic-gate */
1057*7c478bd9Sstevel@tonic-gate if(fprintf(fp, "%s ", comment) < 0 ||
1058*7c478bd9Sstevel@tonic-gate _glh_write_timestamp(fp, node->timestamp) ||
1059*7c478bd9Sstevel@tonic-gate fprintf(fp, " %u\n", node->group) < 0) {
1060*7c478bd9Sstevel@tonic-gate return _glh_cant_save_history(glh, "Error writing", filename, fp);
1061*7c478bd9Sstevel@tonic-gate };
1062*7c478bd9Sstevel@tonic-gate /*
1063*7c478bd9Sstevel@tonic-gate * Write the history line.
1064*7c478bd9Sstevel@tonic-gate */
1065*7c478bd9Sstevel@tonic-gate for(seg=node->line->head; seg; seg=seg->next) {
1066*7c478bd9Sstevel@tonic-gate size_t slen = seg->next ? GLH_SEG_SIZE : strlen(seg->s);
1067*7c478bd9Sstevel@tonic-gate if(fwrite(seg->s, sizeof(char), slen, fp) != slen)
1068*7c478bd9Sstevel@tonic-gate return _glh_cant_save_history(glh, "Error writing", filename, fp);
1069*7c478bd9Sstevel@tonic-gate };
1070*7c478bd9Sstevel@tonic-gate fputc('\n', fp);
1071*7c478bd9Sstevel@tonic-gate };
1072*7c478bd9Sstevel@tonic-gate /*
1073*7c478bd9Sstevel@tonic-gate * Close the history file.
1074*7c478bd9Sstevel@tonic-gate */
1075*7c478bd9Sstevel@tonic-gate if(fclose(fp) == EOF)
1076*7c478bd9Sstevel@tonic-gate return _glh_cant_save_history(glh, "Error writing", filename, NULL);
1077*7c478bd9Sstevel@tonic-gate return 0;
1078*7c478bd9Sstevel@tonic-gate #endif
1079*7c478bd9Sstevel@tonic-gate }
1080*7c478bd9Sstevel@tonic-gate
1081*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
1082*7c478bd9Sstevel@tonic-gate /*.......................................................................
1083*7c478bd9Sstevel@tonic-gate * This is a private error return function of _glh_save_history(). It
1084*7c478bd9Sstevel@tonic-gate * composes an error report in the error buffer, composed using
1085*7c478bd9Sstevel@tonic-gate * sprintf("%s %s (%s)", message, filename, strerror(errno)). It then
1086*7c478bd9Sstevel@tonic-gate * closes fp and returns the error return code of _glh_save_history().
1087*7c478bd9Sstevel@tonic-gate *
1088*7c478bd9Sstevel@tonic-gate * Input:
1089*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1090*7c478bd9Sstevel@tonic-gate * message const char * A message to be followed by the filename.
1091*7c478bd9Sstevel@tonic-gate * filename const char * The name of the offending output file.
1092*7c478bd9Sstevel@tonic-gate * fp FILE * The stream to be closed (send NULL if not
1093*7c478bd9Sstevel@tonic-gate * open).
1094*7c478bd9Sstevel@tonic-gate * Output:
1095*7c478bd9Sstevel@tonic-gate * return int Always 1.
1096*7c478bd9Sstevel@tonic-gate */
_glh_cant_save_history(GlHistory * glh,const char * message,const char * filename,FILE * fp)1097*7c478bd9Sstevel@tonic-gate static int _glh_cant_save_history(GlHistory *glh, const char *message,
1098*7c478bd9Sstevel@tonic-gate const char *filename, FILE *fp)
1099*7c478bd9Sstevel@tonic-gate {
1100*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, message, filename, " (",
1101*7c478bd9Sstevel@tonic-gate strerror(errno), ")", END_ERR_MSG);
1102*7c478bd9Sstevel@tonic-gate if(fp)
1103*7c478bd9Sstevel@tonic-gate (void) fclose(fp);
1104*7c478bd9Sstevel@tonic-gate return 1;
1105*7c478bd9Sstevel@tonic-gate }
1106*7c478bd9Sstevel@tonic-gate
1107*7c478bd9Sstevel@tonic-gate /*.......................................................................
1108*7c478bd9Sstevel@tonic-gate * Write a timestamp to a given stdio stream, in the format
1109*7c478bd9Sstevel@tonic-gate * yyyymmddhhmmss
1110*7c478bd9Sstevel@tonic-gate *
1111*7c478bd9Sstevel@tonic-gate * Input:
1112*7c478bd9Sstevel@tonic-gate * fp FILE * The stream to write to.
1113*7c478bd9Sstevel@tonic-gate * timestamp time_t The timestamp to be written.
1114*7c478bd9Sstevel@tonic-gate * Output:
1115*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1116*7c478bd9Sstevel@tonic-gate * 1 - Error.
1117*7c478bd9Sstevel@tonic-gate */
_glh_write_timestamp(FILE * fp,time_t timestamp)1118*7c478bd9Sstevel@tonic-gate static int _glh_write_timestamp(FILE *fp, time_t timestamp)
1119*7c478bd9Sstevel@tonic-gate {
1120*7c478bd9Sstevel@tonic-gate struct tm *t; /* THe broken-down calendar time */
1121*7c478bd9Sstevel@tonic-gate /*
1122*7c478bd9Sstevel@tonic-gate * Get the calendar components corresponding to the given timestamp.
1123*7c478bd9Sstevel@tonic-gate */
1124*7c478bd9Sstevel@tonic-gate if(timestamp < 0 || (t = localtime(×tamp)) == NULL) {
1125*7c478bd9Sstevel@tonic-gate if(fprintf(fp, "?") < 0)
1126*7c478bd9Sstevel@tonic-gate return 1;
1127*7c478bd9Sstevel@tonic-gate return 0;
1128*7c478bd9Sstevel@tonic-gate };
1129*7c478bd9Sstevel@tonic-gate /*
1130*7c478bd9Sstevel@tonic-gate * Write the calendar time as yyyymmddhhmmss.
1131*7c478bd9Sstevel@tonic-gate */
1132*7c478bd9Sstevel@tonic-gate if(fprintf(fp, "%04d%02d%02d%02d%02d%02d", t->tm_year + 1900, t->tm_mon + 1,
1133*7c478bd9Sstevel@tonic-gate t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec) < 0)
1134*7c478bd9Sstevel@tonic-gate return 1;
1135*7c478bd9Sstevel@tonic-gate return 0;
1136*7c478bd9Sstevel@tonic-gate }
1137*7c478bd9Sstevel@tonic-gate
1138*7c478bd9Sstevel@tonic-gate #endif
1139*7c478bd9Sstevel@tonic-gate
1140*7c478bd9Sstevel@tonic-gate /*.......................................................................
1141*7c478bd9Sstevel@tonic-gate * Restore previous history lines from a given file.
1142*7c478bd9Sstevel@tonic-gate *
1143*7c478bd9Sstevel@tonic-gate * Input:
1144*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1145*7c478bd9Sstevel@tonic-gate * filename const char * The name of the file to read from.
1146*7c478bd9Sstevel@tonic-gate * comment const char * The same comment string that was passed to
1147*7c478bd9Sstevel@tonic-gate * _glh_save_history() when this file was
1148*7c478bd9Sstevel@tonic-gate * written.
1149*7c478bd9Sstevel@tonic-gate * line char * A buffer into which lines can be read.
1150*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimension of line[].
1151*7c478bd9Sstevel@tonic-gate * Output:
1152*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1153*7c478bd9Sstevel@tonic-gate * 1 - Error.
1154*7c478bd9Sstevel@tonic-gate */
_glh_load_history(GlHistory * glh,const char * filename,const char * comment,char * line,size_t dim)1155*7c478bd9Sstevel@tonic-gate int _glh_load_history(GlHistory *glh, const char *filename, const char *comment,
1156*7c478bd9Sstevel@tonic-gate char *line, size_t dim)
1157*7c478bd9Sstevel@tonic-gate {
1158*7c478bd9Sstevel@tonic-gate #ifdef WITHOUT_FILE_SYSTEM
1159*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "Can't load history without filesystem access",
1160*7c478bd9Sstevel@tonic-gate END_ERR_MSG);
1161*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1162*7c478bd9Sstevel@tonic-gate return 1;
1163*7c478bd9Sstevel@tonic-gate #else
1164*7c478bd9Sstevel@tonic-gate FILE *fp; /* The output file */
1165*7c478bd9Sstevel@tonic-gate size_t comment_len; /* The length of the comment string */
1166*7c478bd9Sstevel@tonic-gate time_t timestamp; /* The timestamp of the history line */
1167*7c478bd9Sstevel@tonic-gate unsigned group; /* The identifier of the history group to which */
1168*7c478bd9Sstevel@tonic-gate /* the line belongs. */
1169*7c478bd9Sstevel@tonic-gate int lineno; /* The line number being read */
1170*7c478bd9Sstevel@tonic-gate /*
1171*7c478bd9Sstevel@tonic-gate * Check the arguments.
1172*7c478bd9Sstevel@tonic-gate */
1173*7c478bd9Sstevel@tonic-gate if(!glh || !filename || !comment || !line) {
1174*7c478bd9Sstevel@tonic-gate if(glh)
1175*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
1176*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1177*7c478bd9Sstevel@tonic-gate return 1;
1178*7c478bd9Sstevel@tonic-gate };
1179*7c478bd9Sstevel@tonic-gate /*
1180*7c478bd9Sstevel@tonic-gate * Measure the length of the comment string.
1181*7c478bd9Sstevel@tonic-gate */
1182*7c478bd9Sstevel@tonic-gate comment_len = strlen(comment);
1183*7c478bd9Sstevel@tonic-gate /*
1184*7c478bd9Sstevel@tonic-gate * Clear the history list.
1185*7c478bd9Sstevel@tonic-gate */
1186*7c478bd9Sstevel@tonic-gate _glh_clear_history(glh, 1);
1187*7c478bd9Sstevel@tonic-gate /*
1188*7c478bd9Sstevel@tonic-gate * Attempt to open the specified file. Don't treat it as an error
1189*7c478bd9Sstevel@tonic-gate * if the file doesn't exist.
1190*7c478bd9Sstevel@tonic-gate */
1191*7c478bd9Sstevel@tonic-gate fp = fopen(filename, "r");
1192*7c478bd9Sstevel@tonic-gate if(!fp)
1193*7c478bd9Sstevel@tonic-gate return 0;
1194*7c478bd9Sstevel@tonic-gate /*
1195*7c478bd9Sstevel@tonic-gate * Attempt to read each line and preceding peripheral info, and add these
1196*7c478bd9Sstevel@tonic-gate * to the history list.
1197*7c478bd9Sstevel@tonic-gate */
1198*7c478bd9Sstevel@tonic-gate for(lineno=1; fgets(line, dim, fp) != NULL; lineno++) {
1199*7c478bd9Sstevel@tonic-gate char *lptr; /* A pointer into the input line */
1200*7c478bd9Sstevel@tonic-gate /*
1201*7c478bd9Sstevel@tonic-gate * Check that the line starts with the comment string.
1202*7c478bd9Sstevel@tonic-gate */
1203*7c478bd9Sstevel@tonic-gate if(strncmp(line, comment, comment_len) != 0) {
1204*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno,
1205*7c478bd9Sstevel@tonic-gate "Corrupt history parameter line", fp);
1206*7c478bd9Sstevel@tonic-gate };
1207*7c478bd9Sstevel@tonic-gate /*
1208*7c478bd9Sstevel@tonic-gate * Skip spaces and tabs after the comment.
1209*7c478bd9Sstevel@tonic-gate */
1210*7c478bd9Sstevel@tonic-gate for(lptr=line+comment_len; *lptr && (*lptr==' ' || *lptr=='\t'); lptr++)
1211*7c478bd9Sstevel@tonic-gate ;
1212*7c478bd9Sstevel@tonic-gate /*
1213*7c478bd9Sstevel@tonic-gate * The next word must be a timestamp.
1214*7c478bd9Sstevel@tonic-gate */
1215*7c478bd9Sstevel@tonic-gate if(_glh_decode_timestamp(lptr, &lptr, ×tamp)) {
1216*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno,
1217*7c478bd9Sstevel@tonic-gate "Corrupt timestamp", fp);
1218*7c478bd9Sstevel@tonic-gate };
1219*7c478bd9Sstevel@tonic-gate /*
1220*7c478bd9Sstevel@tonic-gate * Skip spaces and tabs.
1221*7c478bd9Sstevel@tonic-gate */
1222*7c478bd9Sstevel@tonic-gate while(*lptr==' ' || *lptr=='\t')
1223*7c478bd9Sstevel@tonic-gate lptr++;
1224*7c478bd9Sstevel@tonic-gate /*
1225*7c478bd9Sstevel@tonic-gate * The next word must be an unsigned integer group number.
1226*7c478bd9Sstevel@tonic-gate */
1227*7c478bd9Sstevel@tonic-gate group = (int) strtoul(lptr, &lptr, 10);
1228*7c478bd9Sstevel@tonic-gate if(*lptr != ' ' && *lptr != '\n') {
1229*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno,
1230*7c478bd9Sstevel@tonic-gate "Corrupt group id", fp);
1231*7c478bd9Sstevel@tonic-gate };
1232*7c478bd9Sstevel@tonic-gate /*
1233*7c478bd9Sstevel@tonic-gate * Skip spaces and tabs.
1234*7c478bd9Sstevel@tonic-gate */
1235*7c478bd9Sstevel@tonic-gate while(*lptr==' ' || *lptr=='\t')
1236*7c478bd9Sstevel@tonic-gate lptr++;
1237*7c478bd9Sstevel@tonic-gate /*
1238*7c478bd9Sstevel@tonic-gate * There shouldn't be anything left on the line.
1239*7c478bd9Sstevel@tonic-gate */
1240*7c478bd9Sstevel@tonic-gate if(*lptr != '\n') {
1241*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno,
1242*7c478bd9Sstevel@tonic-gate "Corrupt parameter line", fp);
1243*7c478bd9Sstevel@tonic-gate };
1244*7c478bd9Sstevel@tonic-gate /*
1245*7c478bd9Sstevel@tonic-gate * Now read the history line itself.
1246*7c478bd9Sstevel@tonic-gate */
1247*7c478bd9Sstevel@tonic-gate lineno++;
1248*7c478bd9Sstevel@tonic-gate if(fgets(line, dim, fp) == NULL)
1249*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno, "Read error", fp);
1250*7c478bd9Sstevel@tonic-gate /*
1251*7c478bd9Sstevel@tonic-gate * Append the line to the history buffer.
1252*7c478bd9Sstevel@tonic-gate */
1253*7c478bd9Sstevel@tonic-gate if(_glh_add_history(glh, line, 1)) {
1254*7c478bd9Sstevel@tonic-gate return _glh_cant_load_history(glh, filename, lineno,
1255*7c478bd9Sstevel@tonic-gate "Insufficient memory to record line", fp);
1256*7c478bd9Sstevel@tonic-gate };
1257*7c478bd9Sstevel@tonic-gate /*
1258*7c478bd9Sstevel@tonic-gate * Record the group and timestamp information along with the line.
1259*7c478bd9Sstevel@tonic-gate */
1260*7c478bd9Sstevel@tonic-gate if(glh->list.tail) {
1261*7c478bd9Sstevel@tonic-gate glh->list.tail->timestamp = timestamp;
1262*7c478bd9Sstevel@tonic-gate glh->list.tail->group = group;
1263*7c478bd9Sstevel@tonic-gate };
1264*7c478bd9Sstevel@tonic-gate };
1265*7c478bd9Sstevel@tonic-gate /*
1266*7c478bd9Sstevel@tonic-gate * Close the file.
1267*7c478bd9Sstevel@tonic-gate */
1268*7c478bd9Sstevel@tonic-gate (void) fclose(fp);
1269*7c478bd9Sstevel@tonic-gate return 0;
1270*7c478bd9Sstevel@tonic-gate #endif
1271*7c478bd9Sstevel@tonic-gate }
1272*7c478bd9Sstevel@tonic-gate
1273*7c478bd9Sstevel@tonic-gate #ifndef WITHOUT_FILE_SYSTEM
1274*7c478bd9Sstevel@tonic-gate /*.......................................................................
1275*7c478bd9Sstevel@tonic-gate * This is a private error return function of _glh_load_history().
1276*7c478bd9Sstevel@tonic-gate */
_glh_cant_load_history(GlHistory * glh,const char * filename,int lineno,const char * message,FILE * fp)1277*7c478bd9Sstevel@tonic-gate static int _glh_cant_load_history(GlHistory *glh, const char *filename,
1278*7c478bd9Sstevel@tonic-gate int lineno, const char *message, FILE *fp)
1279*7c478bd9Sstevel@tonic-gate {
1280*7c478bd9Sstevel@tonic-gate char lnum[20];
1281*7c478bd9Sstevel@tonic-gate /*
1282*7c478bd9Sstevel@tonic-gate * Convert the line number to a string.
1283*7c478bd9Sstevel@tonic-gate */
1284*7c478bd9Sstevel@tonic-gate snprintf(lnum, sizeof(lnum), "%d", lineno);
1285*7c478bd9Sstevel@tonic-gate /*
1286*7c478bd9Sstevel@tonic-gate * Render an error message.
1287*7c478bd9Sstevel@tonic-gate */
1288*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, filename, ":", lnum, ":", message, END_ERR_MSG);
1289*7c478bd9Sstevel@tonic-gate /*
1290*7c478bd9Sstevel@tonic-gate * Close the file.
1291*7c478bd9Sstevel@tonic-gate */
1292*7c478bd9Sstevel@tonic-gate if(fp)
1293*7c478bd9Sstevel@tonic-gate (void) fclose(fp);
1294*7c478bd9Sstevel@tonic-gate return 1;
1295*7c478bd9Sstevel@tonic-gate }
1296*7c478bd9Sstevel@tonic-gate
1297*7c478bd9Sstevel@tonic-gate /*.......................................................................
1298*7c478bd9Sstevel@tonic-gate * Read a timestamp from a string.
1299*7c478bd9Sstevel@tonic-gate *
1300*7c478bd9Sstevel@tonic-gate * Input:
1301*7c478bd9Sstevel@tonic-gate * string char * The string to read from.
1302*7c478bd9Sstevel@tonic-gate * Input/Output:
1303*7c478bd9Sstevel@tonic-gate * endp char ** On output *endp will point to the next unprocessed
1304*7c478bd9Sstevel@tonic-gate * character in string[].
1305*7c478bd9Sstevel@tonic-gate * timestamp time_t * The timestamp will be assigned to *t.
1306*7c478bd9Sstevel@tonic-gate * Output:
1307*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1308*7c478bd9Sstevel@tonic-gate * 1 - Error.
1309*7c478bd9Sstevel@tonic-gate */
_glh_decode_timestamp(char * string,char ** endp,time_t * timestamp)1310*7c478bd9Sstevel@tonic-gate static int _glh_decode_timestamp(char *string, char **endp, time_t *timestamp)
1311*7c478bd9Sstevel@tonic-gate {
1312*7c478bd9Sstevel@tonic-gate unsigned year,month,day,hour,min,sec; /* Calendar time components */
1313*7c478bd9Sstevel@tonic-gate struct tm t;
1314*7c478bd9Sstevel@tonic-gate /*
1315*7c478bd9Sstevel@tonic-gate * There are 14 characters in the date format yyyymmddhhmmss.
1316*7c478bd9Sstevel@tonic-gate */
1317*7c478bd9Sstevel@tonic-gate enum {TSLEN=14};
1318*7c478bd9Sstevel@tonic-gate char timestr[TSLEN+1]; /* The timestamp part of the string */
1319*7c478bd9Sstevel@tonic-gate /*
1320*7c478bd9Sstevel@tonic-gate * If the time wasn't available at the time that the line was recorded
1321*7c478bd9Sstevel@tonic-gate * it will have been written as "?". Check for this before trying
1322*7c478bd9Sstevel@tonic-gate * to read the timestamp.
1323*7c478bd9Sstevel@tonic-gate */
1324*7c478bd9Sstevel@tonic-gate if(string[0] == '\?') {
1325*7c478bd9Sstevel@tonic-gate *endp = string+1;
1326*7c478bd9Sstevel@tonic-gate *timestamp = -1;
1327*7c478bd9Sstevel@tonic-gate return 0;
1328*7c478bd9Sstevel@tonic-gate };
1329*7c478bd9Sstevel@tonic-gate /*
1330*7c478bd9Sstevel@tonic-gate * The timestamp is expected to be written in the form yyyymmddhhmmss.
1331*7c478bd9Sstevel@tonic-gate */
1332*7c478bd9Sstevel@tonic-gate if(strlen(string) < TSLEN) {
1333*7c478bd9Sstevel@tonic-gate *endp = string;
1334*7c478bd9Sstevel@tonic-gate return 1;
1335*7c478bd9Sstevel@tonic-gate };
1336*7c478bd9Sstevel@tonic-gate /*
1337*7c478bd9Sstevel@tonic-gate * Copy the timestamp out of the string.
1338*7c478bd9Sstevel@tonic-gate */
1339*7c478bd9Sstevel@tonic-gate strncpy(timestr, string, TSLEN);
1340*7c478bd9Sstevel@tonic-gate timestr[TSLEN] = '\0';
1341*7c478bd9Sstevel@tonic-gate /*
1342*7c478bd9Sstevel@tonic-gate * Decode the timestamp.
1343*7c478bd9Sstevel@tonic-gate */
1344*7c478bd9Sstevel@tonic-gate if(sscanf(timestr, "%4u%2u%2u%2u%2u%2u", &year, &month, &day, &hour, &min,
1345*7c478bd9Sstevel@tonic-gate &sec) != 6) {
1346*7c478bd9Sstevel@tonic-gate *endp = string;
1347*7c478bd9Sstevel@tonic-gate return 1;
1348*7c478bd9Sstevel@tonic-gate };
1349*7c478bd9Sstevel@tonic-gate /*
1350*7c478bd9Sstevel@tonic-gate * Advance the string pointer over the successfully read timestamp.
1351*7c478bd9Sstevel@tonic-gate */
1352*7c478bd9Sstevel@tonic-gate *endp = string + TSLEN;
1353*7c478bd9Sstevel@tonic-gate /*
1354*7c478bd9Sstevel@tonic-gate * Copy the read values into a struct tm.
1355*7c478bd9Sstevel@tonic-gate */
1356*7c478bd9Sstevel@tonic-gate t.tm_sec = sec;
1357*7c478bd9Sstevel@tonic-gate t.tm_min = min;
1358*7c478bd9Sstevel@tonic-gate t.tm_hour = hour;
1359*7c478bd9Sstevel@tonic-gate t.tm_mday = day;
1360*7c478bd9Sstevel@tonic-gate t.tm_wday = 0;
1361*7c478bd9Sstevel@tonic-gate t.tm_yday = 0;
1362*7c478bd9Sstevel@tonic-gate t.tm_mon = month - 1;
1363*7c478bd9Sstevel@tonic-gate t.tm_year = year - 1900;
1364*7c478bd9Sstevel@tonic-gate t.tm_isdst = -1;
1365*7c478bd9Sstevel@tonic-gate /*
1366*7c478bd9Sstevel@tonic-gate * Convert the contents of the struct tm to a time_t.
1367*7c478bd9Sstevel@tonic-gate */
1368*7c478bd9Sstevel@tonic-gate *timestamp = mktime(&t);
1369*7c478bd9Sstevel@tonic-gate return 0;
1370*7c478bd9Sstevel@tonic-gate }
1371*7c478bd9Sstevel@tonic-gate #endif
1372*7c478bd9Sstevel@tonic-gate
1373*7c478bd9Sstevel@tonic-gate /*.......................................................................
1374*7c478bd9Sstevel@tonic-gate * Switch history groups.
1375*7c478bd9Sstevel@tonic-gate *
1376*7c478bd9Sstevel@tonic-gate * Input:
1377*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1378*7c478bd9Sstevel@tonic-gate * group unsigned The new group identifier. This will be recorded
1379*7c478bd9Sstevel@tonic-gate * with subsequent history lines, and subsequent
1380*7c478bd9Sstevel@tonic-gate * history searches will only return lines with
1381*7c478bd9Sstevel@tonic-gate * this group identifier. This allows multiple
1382*7c478bd9Sstevel@tonic-gate * separate history lists to exist within
1383*7c478bd9Sstevel@tonic-gate * a single GlHistory object. Note that the
1384*7c478bd9Sstevel@tonic-gate * default group identifier is 0.
1385*7c478bd9Sstevel@tonic-gate * Output:
1386*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1387*7c478bd9Sstevel@tonic-gate * 1 - Error.
1388*7c478bd9Sstevel@tonic-gate */
_glh_set_group(GlHistory * glh,unsigned group)1389*7c478bd9Sstevel@tonic-gate int _glh_set_group(GlHistory *glh, unsigned group)
1390*7c478bd9Sstevel@tonic-gate {
1391*7c478bd9Sstevel@tonic-gate /*
1392*7c478bd9Sstevel@tonic-gate * Check the arguments.
1393*7c478bd9Sstevel@tonic-gate */
1394*7c478bd9Sstevel@tonic-gate if(!glh) {
1395*7c478bd9Sstevel@tonic-gate if(glh)
1396*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
1397*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1398*7c478bd9Sstevel@tonic-gate return 1;
1399*7c478bd9Sstevel@tonic-gate };
1400*7c478bd9Sstevel@tonic-gate /*
1401*7c478bd9Sstevel@tonic-gate * Is the group being changed?
1402*7c478bd9Sstevel@tonic-gate */
1403*7c478bd9Sstevel@tonic-gate if(group != glh->group) {
1404*7c478bd9Sstevel@tonic-gate /*
1405*7c478bd9Sstevel@tonic-gate * Cancel any ongoing search.
1406*7c478bd9Sstevel@tonic-gate */
1407*7c478bd9Sstevel@tonic-gate if(_glh_cancel_search(glh))
1408*7c478bd9Sstevel@tonic-gate return 1;
1409*7c478bd9Sstevel@tonic-gate /*
1410*7c478bd9Sstevel@tonic-gate * Record the new group.
1411*7c478bd9Sstevel@tonic-gate */
1412*7c478bd9Sstevel@tonic-gate glh->group = group;
1413*7c478bd9Sstevel@tonic-gate };
1414*7c478bd9Sstevel@tonic-gate return 0;
1415*7c478bd9Sstevel@tonic-gate }
1416*7c478bd9Sstevel@tonic-gate
1417*7c478bd9Sstevel@tonic-gate /*.......................................................................
1418*7c478bd9Sstevel@tonic-gate * Query the current history group.
1419*7c478bd9Sstevel@tonic-gate *
1420*7c478bd9Sstevel@tonic-gate * Input:
1421*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1422*7c478bd9Sstevel@tonic-gate * Output:
1423*7c478bd9Sstevel@tonic-gate * return unsigned The group identifier.
1424*7c478bd9Sstevel@tonic-gate */
_glh_get_group(GlHistory * glh)1425*7c478bd9Sstevel@tonic-gate int _glh_get_group(GlHistory *glh)
1426*7c478bd9Sstevel@tonic-gate {
1427*7c478bd9Sstevel@tonic-gate return glh ? glh->group : 0;
1428*7c478bd9Sstevel@tonic-gate }
1429*7c478bd9Sstevel@tonic-gate
1430*7c478bd9Sstevel@tonic-gate /*.......................................................................
1431*7c478bd9Sstevel@tonic-gate * Display the contents of the history list.
1432*7c478bd9Sstevel@tonic-gate *
1433*7c478bd9Sstevel@tonic-gate * Input:
1434*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1435*7c478bd9Sstevel@tonic-gate * write_fn GlWriteFn * The function to call to write the line, or
1436*7c478bd9Sstevel@tonic-gate * 0 to discard the output.
1437*7c478bd9Sstevel@tonic-gate * data void * Anonymous data to pass to write_fn().
1438*7c478bd9Sstevel@tonic-gate * fmt const char * A format string. This can contain arbitrary
1439*7c478bd9Sstevel@tonic-gate * characters, which are written verbatim, plus
1440*7c478bd9Sstevel@tonic-gate * any of the following format directives:
1441*7c478bd9Sstevel@tonic-gate * %D - The date, like 2001-11-20
1442*7c478bd9Sstevel@tonic-gate * %T - The time of day, like 23:59:59
1443*7c478bd9Sstevel@tonic-gate * %N - The sequential entry number of the
1444*7c478bd9Sstevel@tonic-gate * line in the history buffer.
1445*7c478bd9Sstevel@tonic-gate * %G - The history group number of the line.
1446*7c478bd9Sstevel@tonic-gate * %% - A literal % character.
1447*7c478bd9Sstevel@tonic-gate * %H - The history line.
1448*7c478bd9Sstevel@tonic-gate * all_groups int If true, display history lines from all
1449*7c478bd9Sstevel@tonic-gate * history groups. Otherwise only display
1450*7c478bd9Sstevel@tonic-gate * those of the current history group.
1451*7c478bd9Sstevel@tonic-gate * max_lines int If max_lines is < 0, all available lines
1452*7c478bd9Sstevel@tonic-gate * are displayed. Otherwise only the most
1453*7c478bd9Sstevel@tonic-gate * recent max_lines lines will be displayed.
1454*7c478bd9Sstevel@tonic-gate * Output:
1455*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1456*7c478bd9Sstevel@tonic-gate * 1 - Error.
1457*7c478bd9Sstevel@tonic-gate */
_glh_show_history(GlHistory * glh,GlWriteFn * write_fn,void * data,const char * fmt,int all_groups,int max_lines)1458*7c478bd9Sstevel@tonic-gate int _glh_show_history(GlHistory *glh, GlWriteFn *write_fn, void *data,
1459*7c478bd9Sstevel@tonic-gate const char *fmt, int all_groups, int max_lines)
1460*7c478bd9Sstevel@tonic-gate {
1461*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line being displayed */
1462*7c478bd9Sstevel@tonic-gate GlhLineNode *oldest; /* The oldest line to display */
1463*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* One segment of a line being displayed */
1464*7c478bd9Sstevel@tonic-gate enum {TSMAX=32}; /* The maximum length of the date and time string */
1465*7c478bd9Sstevel@tonic-gate char buffer[TSMAX+1]; /* The buffer in which to write the date and time */
1466*7c478bd9Sstevel@tonic-gate int idlen; /* The length of displayed ID strings */
1467*7c478bd9Sstevel@tonic-gate unsigned grpmax; /* The maximum group number in the buffer */
1468*7c478bd9Sstevel@tonic-gate int grplen; /* The number of characters needed to print grpmax */
1469*7c478bd9Sstevel@tonic-gate int len; /* The length of a string to be written */
1470*7c478bd9Sstevel@tonic-gate /*
1471*7c478bd9Sstevel@tonic-gate * Check the arguments.
1472*7c478bd9Sstevel@tonic-gate */
1473*7c478bd9Sstevel@tonic-gate if(!glh || !write_fn || !fmt) {
1474*7c478bd9Sstevel@tonic-gate if(glh)
1475*7c478bd9Sstevel@tonic-gate _err_record_msg(glh->err, "NULL argument(s)", END_ERR_MSG);
1476*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1477*7c478bd9Sstevel@tonic-gate return 1;
1478*7c478bd9Sstevel@tonic-gate };
1479*7c478bd9Sstevel@tonic-gate /*
1480*7c478bd9Sstevel@tonic-gate * Is history enabled?
1481*7c478bd9Sstevel@tonic-gate */
1482*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->list.head)
1483*7c478bd9Sstevel@tonic-gate return 0;
1484*7c478bd9Sstevel@tonic-gate /*
1485*7c478bd9Sstevel@tonic-gate * Work out the length to display ID numbers, choosing the length of
1486*7c478bd9Sstevel@tonic-gate * the biggest number in the buffer. Smaller numbers will be padded
1487*7c478bd9Sstevel@tonic-gate * with leading zeroes if needed.
1488*7c478bd9Sstevel@tonic-gate */
1489*7c478bd9Sstevel@tonic-gate snprintf(buffer, sizeof(buffer), "%lu", (unsigned long) glh->list.tail->id);
1490*7c478bd9Sstevel@tonic-gate idlen = strlen(buffer);
1491*7c478bd9Sstevel@tonic-gate /*
1492*7c478bd9Sstevel@tonic-gate * Find the largest group number.
1493*7c478bd9Sstevel@tonic-gate */
1494*7c478bd9Sstevel@tonic-gate grpmax = 0;
1495*7c478bd9Sstevel@tonic-gate for(node=glh->list.head; node; node=node->next) {
1496*7c478bd9Sstevel@tonic-gate if(node->group > grpmax)
1497*7c478bd9Sstevel@tonic-gate grpmax = node->group;
1498*7c478bd9Sstevel@tonic-gate };
1499*7c478bd9Sstevel@tonic-gate /*
1500*7c478bd9Sstevel@tonic-gate * Find out how many characters are needed to display the group number.
1501*7c478bd9Sstevel@tonic-gate */
1502*7c478bd9Sstevel@tonic-gate snprintf(buffer, sizeof(buffer), "%u", (unsigned) grpmax);
1503*7c478bd9Sstevel@tonic-gate grplen = strlen(buffer);
1504*7c478bd9Sstevel@tonic-gate /*
1505*7c478bd9Sstevel@tonic-gate * Find the node that follows the oldest line to be displayed.
1506*7c478bd9Sstevel@tonic-gate */
1507*7c478bd9Sstevel@tonic-gate if(max_lines < 0) {
1508*7c478bd9Sstevel@tonic-gate oldest = glh->list.head;
1509*7c478bd9Sstevel@tonic-gate } else if(max_lines==0) {
1510*7c478bd9Sstevel@tonic-gate return 0;
1511*7c478bd9Sstevel@tonic-gate } else {
1512*7c478bd9Sstevel@tonic-gate for(oldest=glh->list.tail; oldest; oldest=oldest->prev) {
1513*7c478bd9Sstevel@tonic-gate if((all_groups || oldest->group == glh->group) && --max_lines <= 0)
1514*7c478bd9Sstevel@tonic-gate break;
1515*7c478bd9Sstevel@tonic-gate };
1516*7c478bd9Sstevel@tonic-gate /*
1517*7c478bd9Sstevel@tonic-gate * If the number of lines in the buffer doesn't exceed the specified
1518*7c478bd9Sstevel@tonic-gate * maximum, start from the oldest line in the buffer.
1519*7c478bd9Sstevel@tonic-gate */
1520*7c478bd9Sstevel@tonic-gate if(!oldest)
1521*7c478bd9Sstevel@tonic-gate oldest = glh->list.head;
1522*7c478bd9Sstevel@tonic-gate };
1523*7c478bd9Sstevel@tonic-gate /*
1524*7c478bd9Sstevel@tonic-gate * List the history lines in increasing time order.
1525*7c478bd9Sstevel@tonic-gate */
1526*7c478bd9Sstevel@tonic-gate for(node=oldest; node; node=node->next) {
1527*7c478bd9Sstevel@tonic-gate /*
1528*7c478bd9Sstevel@tonic-gate * Only display lines from the current history group, unless
1529*7c478bd9Sstevel@tonic-gate * told otherwise.
1530*7c478bd9Sstevel@tonic-gate */
1531*7c478bd9Sstevel@tonic-gate if(all_groups || node->group == glh->group) {
1532*7c478bd9Sstevel@tonic-gate const char *fptr; /* A pointer into the format string */
1533*7c478bd9Sstevel@tonic-gate struct tm *t = NULL; /* The broken time version of the timestamp */
1534*7c478bd9Sstevel@tonic-gate /*
1535*7c478bd9Sstevel@tonic-gate * Work out the calendar representation of the node timestamp.
1536*7c478bd9Sstevel@tonic-gate */
1537*7c478bd9Sstevel@tonic-gate if(node->timestamp != (time_t) -1)
1538*7c478bd9Sstevel@tonic-gate t = localtime(&node->timestamp);
1539*7c478bd9Sstevel@tonic-gate /*
1540*7c478bd9Sstevel@tonic-gate * Parse the format string.
1541*7c478bd9Sstevel@tonic-gate */
1542*7c478bd9Sstevel@tonic-gate fptr = fmt;
1543*7c478bd9Sstevel@tonic-gate while(*fptr) {
1544*7c478bd9Sstevel@tonic-gate /*
1545*7c478bd9Sstevel@tonic-gate * Search for the start of the next format directive or the end of the string.
1546*7c478bd9Sstevel@tonic-gate */
1547*7c478bd9Sstevel@tonic-gate const char *start = fptr;
1548*7c478bd9Sstevel@tonic-gate while(*fptr && *fptr != '%')
1549*7c478bd9Sstevel@tonic-gate fptr++;
1550*7c478bd9Sstevel@tonic-gate /*
1551*7c478bd9Sstevel@tonic-gate * Display any literal characters that precede the located directive.
1552*7c478bd9Sstevel@tonic-gate */
1553*7c478bd9Sstevel@tonic-gate if(fptr > start) {
1554*7c478bd9Sstevel@tonic-gate len = (int) (fptr - start);
1555*7c478bd9Sstevel@tonic-gate if(write_fn(data, start, len) != len)
1556*7c478bd9Sstevel@tonic-gate return 1;
1557*7c478bd9Sstevel@tonic-gate };
1558*7c478bd9Sstevel@tonic-gate /*
1559*7c478bd9Sstevel@tonic-gate * Did we hit a new directive before the end of the line?
1560*7c478bd9Sstevel@tonic-gate */
1561*7c478bd9Sstevel@tonic-gate if(*fptr) {
1562*7c478bd9Sstevel@tonic-gate /*
1563*7c478bd9Sstevel@tonic-gate * Obey the directive. Ignore unknown directives.
1564*7c478bd9Sstevel@tonic-gate */
1565*7c478bd9Sstevel@tonic-gate switch(*++fptr) {
1566*7c478bd9Sstevel@tonic-gate case 'D': /* Display the date */
1567*7c478bd9Sstevel@tonic-gate if(t && strftime(buffer, TSMAX, "%Y-%m-%d", t) != 0) {
1568*7c478bd9Sstevel@tonic-gate len = strlen(buffer);
1569*7c478bd9Sstevel@tonic-gate if(write_fn(data, buffer, len) != len)
1570*7c478bd9Sstevel@tonic-gate return 1;
1571*7c478bd9Sstevel@tonic-gate };
1572*7c478bd9Sstevel@tonic-gate break;
1573*7c478bd9Sstevel@tonic-gate case 'T': /* Display the time of day */
1574*7c478bd9Sstevel@tonic-gate if(t && strftime(buffer, TSMAX, "%H:%M:%S", t) != 0) {
1575*7c478bd9Sstevel@tonic-gate len = strlen(buffer);
1576*7c478bd9Sstevel@tonic-gate if(write_fn(data, buffer, len) != len)
1577*7c478bd9Sstevel@tonic-gate return 1;
1578*7c478bd9Sstevel@tonic-gate };
1579*7c478bd9Sstevel@tonic-gate break;
1580*7c478bd9Sstevel@tonic-gate case 'N': /* Display the sequential entry number */
1581*7c478bd9Sstevel@tonic-gate snprintf(buffer, sizeof(buffer), "%*lu", idlen, (unsigned long) node->id);
1582*7c478bd9Sstevel@tonic-gate len = strlen(buffer);
1583*7c478bd9Sstevel@tonic-gate if(write_fn(data, buffer, len) != len)
1584*7c478bd9Sstevel@tonic-gate return 1;
1585*7c478bd9Sstevel@tonic-gate break;
1586*7c478bd9Sstevel@tonic-gate case 'G':
1587*7c478bd9Sstevel@tonic-gate snprintf(buffer, sizeof(buffer), "%*u", grplen, (unsigned) node->group);
1588*7c478bd9Sstevel@tonic-gate len = strlen(buffer);
1589*7c478bd9Sstevel@tonic-gate if(write_fn(data, buffer, len) != len)
1590*7c478bd9Sstevel@tonic-gate return 1;
1591*7c478bd9Sstevel@tonic-gate break;
1592*7c478bd9Sstevel@tonic-gate case 'H': /* Display the history line */
1593*7c478bd9Sstevel@tonic-gate for(seg=node->line->head; seg; seg=seg->next) {
1594*7c478bd9Sstevel@tonic-gate len = seg->next ? GLH_SEG_SIZE : strlen(seg->s);
1595*7c478bd9Sstevel@tonic-gate if(write_fn(data, seg->s, len) != len)
1596*7c478bd9Sstevel@tonic-gate return 1;
1597*7c478bd9Sstevel@tonic-gate };
1598*7c478bd9Sstevel@tonic-gate break;
1599*7c478bd9Sstevel@tonic-gate case '%': /* A literal % symbol */
1600*7c478bd9Sstevel@tonic-gate if(write_fn(data, "%", 1) != 1)
1601*7c478bd9Sstevel@tonic-gate return 1;
1602*7c478bd9Sstevel@tonic-gate break;
1603*7c478bd9Sstevel@tonic-gate };
1604*7c478bd9Sstevel@tonic-gate /*
1605*7c478bd9Sstevel@tonic-gate * Skip the directive.
1606*7c478bd9Sstevel@tonic-gate */
1607*7c478bd9Sstevel@tonic-gate if(*fptr)
1608*7c478bd9Sstevel@tonic-gate fptr++;
1609*7c478bd9Sstevel@tonic-gate };
1610*7c478bd9Sstevel@tonic-gate };
1611*7c478bd9Sstevel@tonic-gate };
1612*7c478bd9Sstevel@tonic-gate };
1613*7c478bd9Sstevel@tonic-gate return 0;
1614*7c478bd9Sstevel@tonic-gate }
1615*7c478bd9Sstevel@tonic-gate
1616*7c478bd9Sstevel@tonic-gate /*.......................................................................
1617*7c478bd9Sstevel@tonic-gate * Change the size of the history buffer.
1618*7c478bd9Sstevel@tonic-gate *
1619*7c478bd9Sstevel@tonic-gate * Input:
1620*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1621*7c478bd9Sstevel@tonic-gate * bufsize size_t The number of bytes in the history buffer, or 0
1622*7c478bd9Sstevel@tonic-gate * to delete the buffer completely.
1623*7c478bd9Sstevel@tonic-gate * Output:
1624*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
1625*7c478bd9Sstevel@tonic-gate * 1 - Insufficient memory (the previous buffer
1626*7c478bd9Sstevel@tonic-gate * will have been retained). No error message
1627*7c478bd9Sstevel@tonic-gate * will be displayed.
1628*7c478bd9Sstevel@tonic-gate */
_glh_resize_history(GlHistory * glh,size_t bufsize)1629*7c478bd9Sstevel@tonic-gate int _glh_resize_history(GlHistory *glh, size_t bufsize)
1630*7c478bd9Sstevel@tonic-gate {
1631*7c478bd9Sstevel@tonic-gate int nbuff; /* The number of segments in the new buffer */
1632*7c478bd9Sstevel@tonic-gate int i;
1633*7c478bd9Sstevel@tonic-gate /*
1634*7c478bd9Sstevel@tonic-gate * Check the arguments.
1635*7c478bd9Sstevel@tonic-gate */
1636*7c478bd9Sstevel@tonic-gate if(!glh) {
1637*7c478bd9Sstevel@tonic-gate errno = EINVAL;
1638*7c478bd9Sstevel@tonic-gate return 1;
1639*7c478bd9Sstevel@tonic-gate };
1640*7c478bd9Sstevel@tonic-gate /*
1641*7c478bd9Sstevel@tonic-gate * How many buffer segments does the requested buffer size correspond
1642*7c478bd9Sstevel@tonic-gate * to?
1643*7c478bd9Sstevel@tonic-gate */
1644*7c478bd9Sstevel@tonic-gate nbuff = (bufsize+GLH_SEG_SIZE-1) / GLH_SEG_SIZE;
1645*7c478bd9Sstevel@tonic-gate /*
1646*7c478bd9Sstevel@tonic-gate * Has a different size than the current size been requested?
1647*7c478bd9Sstevel@tonic-gate */
1648*7c478bd9Sstevel@tonic-gate if(glh->nbuff != nbuff) {
1649*7c478bd9Sstevel@tonic-gate /*
1650*7c478bd9Sstevel@tonic-gate * Cancel any ongoing search.
1651*7c478bd9Sstevel@tonic-gate */
1652*7c478bd9Sstevel@tonic-gate (void) _glh_cancel_search(glh);
1653*7c478bd9Sstevel@tonic-gate /*
1654*7c478bd9Sstevel@tonic-gate * Create a wholly new buffer?
1655*7c478bd9Sstevel@tonic-gate */
1656*7c478bd9Sstevel@tonic-gate if(glh->nbuff == 0 && nbuff>0) {
1657*7c478bd9Sstevel@tonic-gate glh->buffer = (GlhLineSeg *) malloc(sizeof(GlhLineSeg) * nbuff);
1658*7c478bd9Sstevel@tonic-gate if(!glh->buffer)
1659*7c478bd9Sstevel@tonic-gate return 1;
1660*7c478bd9Sstevel@tonic-gate glh->nbuff = nbuff;
1661*7c478bd9Sstevel@tonic-gate glh->nfree = glh->nbuff;
1662*7c478bd9Sstevel@tonic-gate glh->nbusy = 0;
1663*7c478bd9Sstevel@tonic-gate glh->nline = 0;
1664*7c478bd9Sstevel@tonic-gate /*
1665*7c478bd9Sstevel@tonic-gate * Link the currently unused nodes of the buffer into a list.
1666*7c478bd9Sstevel@tonic-gate */
1667*7c478bd9Sstevel@tonic-gate glh->unused = glh->buffer;
1668*7c478bd9Sstevel@tonic-gate for(i=0; i<glh->nbuff-1; i++) {
1669*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg = glh->unused + i;
1670*7c478bd9Sstevel@tonic-gate seg->next = seg + 1;
1671*7c478bd9Sstevel@tonic-gate };
1672*7c478bd9Sstevel@tonic-gate glh->unused[i].next = NULL;
1673*7c478bd9Sstevel@tonic-gate /*
1674*7c478bd9Sstevel@tonic-gate * Delete an existing buffer?
1675*7c478bd9Sstevel@tonic-gate */
1676*7c478bd9Sstevel@tonic-gate } else if(nbuff == 0) {
1677*7c478bd9Sstevel@tonic-gate _glh_clear_history(glh, 1);
1678*7c478bd9Sstevel@tonic-gate free(glh->buffer);
1679*7c478bd9Sstevel@tonic-gate glh->buffer = NULL;
1680*7c478bd9Sstevel@tonic-gate glh->unused = NULL;
1681*7c478bd9Sstevel@tonic-gate glh->nbuff = 0;
1682*7c478bd9Sstevel@tonic-gate glh->nfree = 0;
1683*7c478bd9Sstevel@tonic-gate glh->nbusy = 0;
1684*7c478bd9Sstevel@tonic-gate glh->nline = 0;
1685*7c478bd9Sstevel@tonic-gate /*
1686*7c478bd9Sstevel@tonic-gate * Change from one finite buffer size to another?
1687*7c478bd9Sstevel@tonic-gate */
1688*7c478bd9Sstevel@tonic-gate } else {
1689*7c478bd9Sstevel@tonic-gate GlhLineSeg *buffer; /* The resized buffer */
1690*7c478bd9Sstevel@tonic-gate int nbusy; /* The number of used line segments in the new buffer */
1691*7c478bd9Sstevel@tonic-gate /*
1692*7c478bd9Sstevel@tonic-gate * Starting from the oldest line in the buffer, discard lines until
1693*7c478bd9Sstevel@tonic-gate * the buffer contains at most 'nbuff' used line segments.
1694*7c478bd9Sstevel@tonic-gate */
1695*7c478bd9Sstevel@tonic-gate while(glh->list.head && glh->nbusy > nbuff)
1696*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, glh->list.head);
1697*7c478bd9Sstevel@tonic-gate /*
1698*7c478bd9Sstevel@tonic-gate * Attempt to allocate a new buffer.
1699*7c478bd9Sstevel@tonic-gate */
1700*7c478bd9Sstevel@tonic-gate buffer = (GlhLineSeg *) malloc(nbuff * sizeof(GlhLineSeg));
1701*7c478bd9Sstevel@tonic-gate if(!buffer) {
1702*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
1703*7c478bd9Sstevel@tonic-gate return 1;
1704*7c478bd9Sstevel@tonic-gate };
1705*7c478bd9Sstevel@tonic-gate /*
1706*7c478bd9Sstevel@tonic-gate * Copy the used segments of the old buffer to the start of the new buffer.
1707*7c478bd9Sstevel@tonic-gate */
1708*7c478bd9Sstevel@tonic-gate nbusy = 0;
1709*7c478bd9Sstevel@tonic-gate for(i=0; i<GLH_HASH_SIZE; i++) {
1710*7c478bd9Sstevel@tonic-gate GlhHashBucket *b = glh->hash.bucket + i;
1711*7c478bd9Sstevel@tonic-gate GlhHashNode *hnode;
1712*7c478bd9Sstevel@tonic-gate for(hnode=b->lines; hnode; hnode=hnode->next) {
1713*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg = hnode->head;
1714*7c478bd9Sstevel@tonic-gate hnode->head = buffer + nbusy;
1715*7c478bd9Sstevel@tonic-gate for( ; seg; seg=seg->next) {
1716*7c478bd9Sstevel@tonic-gate buffer[nbusy] = *seg;
1717*7c478bd9Sstevel@tonic-gate buffer[nbusy].next = seg->next ? &buffer[nbusy+1] : NULL;
1718*7c478bd9Sstevel@tonic-gate nbusy++;
1719*7c478bd9Sstevel@tonic-gate };
1720*7c478bd9Sstevel@tonic-gate };
1721*7c478bd9Sstevel@tonic-gate };
1722*7c478bd9Sstevel@tonic-gate /*
1723*7c478bd9Sstevel@tonic-gate * Make a list of the new buffer's unused segments.
1724*7c478bd9Sstevel@tonic-gate */
1725*7c478bd9Sstevel@tonic-gate for(i=nbusy; i<nbuff-1; i++)
1726*7c478bd9Sstevel@tonic-gate buffer[i].next = &buffer[i+1];
1727*7c478bd9Sstevel@tonic-gate if(i < nbuff)
1728*7c478bd9Sstevel@tonic-gate buffer[i].next = NULL;
1729*7c478bd9Sstevel@tonic-gate /*
1730*7c478bd9Sstevel@tonic-gate * Discard the old buffer.
1731*7c478bd9Sstevel@tonic-gate */
1732*7c478bd9Sstevel@tonic-gate free(glh->buffer);
1733*7c478bd9Sstevel@tonic-gate /*
1734*7c478bd9Sstevel@tonic-gate * Install the new buffer.
1735*7c478bd9Sstevel@tonic-gate */
1736*7c478bd9Sstevel@tonic-gate glh->buffer = buffer;
1737*7c478bd9Sstevel@tonic-gate glh->nbuff = nbuff;
1738*7c478bd9Sstevel@tonic-gate glh->nbusy = nbusy;
1739*7c478bd9Sstevel@tonic-gate glh->nfree = nbuff - nbusy;
1740*7c478bd9Sstevel@tonic-gate glh->unused = glh->nfree > 0 ? (buffer + nbusy) : NULL;
1741*7c478bd9Sstevel@tonic-gate };
1742*7c478bd9Sstevel@tonic-gate };
1743*7c478bd9Sstevel@tonic-gate return 0;
1744*7c478bd9Sstevel@tonic-gate }
1745*7c478bd9Sstevel@tonic-gate
1746*7c478bd9Sstevel@tonic-gate /*.......................................................................
1747*7c478bd9Sstevel@tonic-gate * Set an upper limit to the number of lines that can be recorded in the
1748*7c478bd9Sstevel@tonic-gate * history list, or remove a previously specified limit.
1749*7c478bd9Sstevel@tonic-gate *
1750*7c478bd9Sstevel@tonic-gate * Input:
1751*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1752*7c478bd9Sstevel@tonic-gate * max_lines int The maximum number of lines to allow, or -1 to
1753*7c478bd9Sstevel@tonic-gate * cancel a previous limit and allow as many lines
1754*7c478bd9Sstevel@tonic-gate * as will fit in the current history buffer size.
1755*7c478bd9Sstevel@tonic-gate */
_glh_limit_history(GlHistory * glh,int max_lines)1756*7c478bd9Sstevel@tonic-gate void _glh_limit_history(GlHistory *glh, int max_lines)
1757*7c478bd9Sstevel@tonic-gate {
1758*7c478bd9Sstevel@tonic-gate if(!glh)
1759*7c478bd9Sstevel@tonic-gate return;
1760*7c478bd9Sstevel@tonic-gate /*
1761*7c478bd9Sstevel@tonic-gate * Apply a new limit?
1762*7c478bd9Sstevel@tonic-gate */
1763*7c478bd9Sstevel@tonic-gate if(max_lines >= 0 && max_lines != glh->max_lines) {
1764*7c478bd9Sstevel@tonic-gate /*
1765*7c478bd9Sstevel@tonic-gate * Count successively older lines until we reach the start of the
1766*7c478bd9Sstevel@tonic-gate * list, or until we have seen max_lines lines (at which point 'node'
1767*7c478bd9Sstevel@tonic-gate * will be line number max_lines+1).
1768*7c478bd9Sstevel@tonic-gate */
1769*7c478bd9Sstevel@tonic-gate int nline = 0;
1770*7c478bd9Sstevel@tonic-gate GlhLineNode *node;
1771*7c478bd9Sstevel@tonic-gate for(node=glh->list.tail; node && ++nline <= max_lines; node=node->prev)
1772*7c478bd9Sstevel@tonic-gate ;
1773*7c478bd9Sstevel@tonic-gate /*
1774*7c478bd9Sstevel@tonic-gate * Discard any lines that exceed the limit.
1775*7c478bd9Sstevel@tonic-gate */
1776*7c478bd9Sstevel@tonic-gate if(node) {
1777*7c478bd9Sstevel@tonic-gate GlhLineNode *oldest = node->next; /* The oldest line to be kept */
1778*7c478bd9Sstevel@tonic-gate /*
1779*7c478bd9Sstevel@tonic-gate * Delete nodes from the head of the list until we reach the node that
1780*7c478bd9Sstevel@tonic-gate * is to be kept.
1781*7c478bd9Sstevel@tonic-gate */
1782*7c478bd9Sstevel@tonic-gate while(glh->list.head && glh->list.head != oldest)
1783*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, glh->list.head);
1784*7c478bd9Sstevel@tonic-gate };
1785*7c478bd9Sstevel@tonic-gate };
1786*7c478bd9Sstevel@tonic-gate /*
1787*7c478bd9Sstevel@tonic-gate * Record the new limit.
1788*7c478bd9Sstevel@tonic-gate */
1789*7c478bd9Sstevel@tonic-gate glh->max_lines = max_lines;
1790*7c478bd9Sstevel@tonic-gate return;
1791*7c478bd9Sstevel@tonic-gate }
1792*7c478bd9Sstevel@tonic-gate
1793*7c478bd9Sstevel@tonic-gate /*.......................................................................
1794*7c478bd9Sstevel@tonic-gate * Discard either all history, or the history associated with the current
1795*7c478bd9Sstevel@tonic-gate * history group.
1796*7c478bd9Sstevel@tonic-gate *
1797*7c478bd9Sstevel@tonic-gate * Input:
1798*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1799*7c478bd9Sstevel@tonic-gate * all_groups int If true, clear all of the history. If false,
1800*7c478bd9Sstevel@tonic-gate * clear only the stored lines associated with the
1801*7c478bd9Sstevel@tonic-gate * currently selected history group.
1802*7c478bd9Sstevel@tonic-gate */
_glh_clear_history(GlHistory * glh,int all_groups)1803*7c478bd9Sstevel@tonic-gate void _glh_clear_history(GlHistory *glh, int all_groups)
1804*7c478bd9Sstevel@tonic-gate {
1805*7c478bd9Sstevel@tonic-gate int i;
1806*7c478bd9Sstevel@tonic-gate /*
1807*7c478bd9Sstevel@tonic-gate * Check the arguments.
1808*7c478bd9Sstevel@tonic-gate */
1809*7c478bd9Sstevel@tonic-gate if(!glh)
1810*7c478bd9Sstevel@tonic-gate return;
1811*7c478bd9Sstevel@tonic-gate /*
1812*7c478bd9Sstevel@tonic-gate * Cancel any ongoing search.
1813*7c478bd9Sstevel@tonic-gate */
1814*7c478bd9Sstevel@tonic-gate (void) _glh_cancel_search(glh);
1815*7c478bd9Sstevel@tonic-gate /*
1816*7c478bd9Sstevel@tonic-gate * Delete all history lines regardless of group?
1817*7c478bd9Sstevel@tonic-gate */
1818*7c478bd9Sstevel@tonic-gate if(all_groups) {
1819*7c478bd9Sstevel@tonic-gate /*
1820*7c478bd9Sstevel@tonic-gate * Claer the time-ordered list of lines.
1821*7c478bd9Sstevel@tonic-gate */
1822*7c478bd9Sstevel@tonic-gate _rst_FreeList(glh->list.node_mem);
1823*7c478bd9Sstevel@tonic-gate glh->list.head = glh->list.tail = NULL;
1824*7c478bd9Sstevel@tonic-gate glh->nline = 0;
1825*7c478bd9Sstevel@tonic-gate glh->id_node = NULL;
1826*7c478bd9Sstevel@tonic-gate /*
1827*7c478bd9Sstevel@tonic-gate * Clear the hash table.
1828*7c478bd9Sstevel@tonic-gate */
1829*7c478bd9Sstevel@tonic-gate for(i=0; i<GLH_HASH_SIZE; i++)
1830*7c478bd9Sstevel@tonic-gate glh->hash.bucket[i].lines = NULL;
1831*7c478bd9Sstevel@tonic-gate _rst_FreeList(glh->hash.node_mem);
1832*7c478bd9Sstevel@tonic-gate /*
1833*7c478bd9Sstevel@tonic-gate * Move all line segment nodes back onto the list of unused segments.
1834*7c478bd9Sstevel@tonic-gate */
1835*7c478bd9Sstevel@tonic-gate if(glh->buffer) {
1836*7c478bd9Sstevel@tonic-gate glh->unused = glh->buffer;
1837*7c478bd9Sstevel@tonic-gate for(i=0; i<glh->nbuff-1; i++) {
1838*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg = glh->unused + i;
1839*7c478bd9Sstevel@tonic-gate seg->next = seg + 1;
1840*7c478bd9Sstevel@tonic-gate };
1841*7c478bd9Sstevel@tonic-gate glh->unused[i].next = NULL;
1842*7c478bd9Sstevel@tonic-gate glh->nfree = glh->nbuff;
1843*7c478bd9Sstevel@tonic-gate glh->nbusy = 0;
1844*7c478bd9Sstevel@tonic-gate } else {
1845*7c478bd9Sstevel@tonic-gate glh->unused = NULL;
1846*7c478bd9Sstevel@tonic-gate glh->nbusy = glh->nfree = 0;
1847*7c478bd9Sstevel@tonic-gate };
1848*7c478bd9Sstevel@tonic-gate /*
1849*7c478bd9Sstevel@tonic-gate * Just delete lines of the current group?
1850*7c478bd9Sstevel@tonic-gate */
1851*7c478bd9Sstevel@tonic-gate } else {
1852*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The line node being checked */
1853*7c478bd9Sstevel@tonic-gate GlhLineNode *next; /* The line node that follows 'node' */
1854*7c478bd9Sstevel@tonic-gate /*
1855*7c478bd9Sstevel@tonic-gate * Search out and delete the line nodes of the current group.
1856*7c478bd9Sstevel@tonic-gate */
1857*7c478bd9Sstevel@tonic-gate for(node=glh->list.head; node; node=next) {
1858*7c478bd9Sstevel@tonic-gate /*
1859*7c478bd9Sstevel@tonic-gate * Keep a record of the following node before we delete the current
1860*7c478bd9Sstevel@tonic-gate * node.
1861*7c478bd9Sstevel@tonic-gate */
1862*7c478bd9Sstevel@tonic-gate next = node->next;
1863*7c478bd9Sstevel@tonic-gate /*
1864*7c478bd9Sstevel@tonic-gate * Discard this node?
1865*7c478bd9Sstevel@tonic-gate */
1866*7c478bd9Sstevel@tonic-gate if(node->group == glh->group)
1867*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, node);
1868*7c478bd9Sstevel@tonic-gate };
1869*7c478bd9Sstevel@tonic-gate };
1870*7c478bd9Sstevel@tonic-gate return;
1871*7c478bd9Sstevel@tonic-gate }
1872*7c478bd9Sstevel@tonic-gate
1873*7c478bd9Sstevel@tonic-gate /*.......................................................................
1874*7c478bd9Sstevel@tonic-gate * Temporarily enable or disable the history list.
1875*7c478bd9Sstevel@tonic-gate *
1876*7c478bd9Sstevel@tonic-gate * Input:
1877*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1878*7c478bd9Sstevel@tonic-gate * enable int If true, turn on the history mechanism. If
1879*7c478bd9Sstevel@tonic-gate * false, disable it.
1880*7c478bd9Sstevel@tonic-gate */
_glh_toggle_history(GlHistory * glh,int enable)1881*7c478bd9Sstevel@tonic-gate void _glh_toggle_history(GlHistory *glh, int enable)
1882*7c478bd9Sstevel@tonic-gate {
1883*7c478bd9Sstevel@tonic-gate if(glh)
1884*7c478bd9Sstevel@tonic-gate glh->enable = enable;
1885*7c478bd9Sstevel@tonic-gate }
1886*7c478bd9Sstevel@tonic-gate
1887*7c478bd9Sstevel@tonic-gate /*.......................................................................
1888*7c478bd9Sstevel@tonic-gate * Discard a given archived input line.
1889*7c478bd9Sstevel@tonic-gate *
1890*7c478bd9Sstevel@tonic-gate * Input:
1891*7c478bd9Sstevel@tonic-gate * glh GlHistory * The history container object.
1892*7c478bd9Sstevel@tonic-gate * node GlhLineNode * The line to be discarded, specified via its
1893*7c478bd9Sstevel@tonic-gate * entry in the time-ordered list of historical
1894*7c478bd9Sstevel@tonic-gate * input lines.
1895*7c478bd9Sstevel@tonic-gate */
_glh_discard_line(GlHistory * glh,GlhLineNode * node)1896*7c478bd9Sstevel@tonic-gate static void _glh_discard_line(GlHistory *glh, GlhLineNode *node)
1897*7c478bd9Sstevel@tonic-gate {
1898*7c478bd9Sstevel@tonic-gate /*
1899*7c478bd9Sstevel@tonic-gate * Remove the node from the linked list.
1900*7c478bd9Sstevel@tonic-gate */
1901*7c478bd9Sstevel@tonic-gate if(node->prev)
1902*7c478bd9Sstevel@tonic-gate node->prev->next = node->next;
1903*7c478bd9Sstevel@tonic-gate else
1904*7c478bd9Sstevel@tonic-gate glh->list.head = node->next;
1905*7c478bd9Sstevel@tonic-gate if(node->next)
1906*7c478bd9Sstevel@tonic-gate node->next->prev = node->prev;
1907*7c478bd9Sstevel@tonic-gate else
1908*7c478bd9Sstevel@tonic-gate glh->list.tail = node->prev;
1909*7c478bd9Sstevel@tonic-gate /*
1910*7c478bd9Sstevel@tonic-gate * If we are deleting the node that is marked as the start point of the
1911*7c478bd9Sstevel@tonic-gate * last ID search, remove the cached starting point.
1912*7c478bd9Sstevel@tonic-gate */
1913*7c478bd9Sstevel@tonic-gate if(node == glh->id_node)
1914*7c478bd9Sstevel@tonic-gate glh->id_node = NULL;
1915*7c478bd9Sstevel@tonic-gate /*
1916*7c478bd9Sstevel@tonic-gate * If we are deleting the node that is marked as the start point of the
1917*7c478bd9Sstevel@tonic-gate * next prefix search, cancel the search.
1918*7c478bd9Sstevel@tonic-gate */
1919*7c478bd9Sstevel@tonic-gate if(node == glh->recall)
1920*7c478bd9Sstevel@tonic-gate _glh_cancel_search(glh);
1921*7c478bd9Sstevel@tonic-gate /*
1922*7c478bd9Sstevel@tonic-gate * Delete our copy of the line.
1923*7c478bd9Sstevel@tonic-gate */
1924*7c478bd9Sstevel@tonic-gate node->line = _glh_discard_copy(glh, node->line);
1925*7c478bd9Sstevel@tonic-gate /*
1926*7c478bd9Sstevel@tonic-gate * Return the node to the freelist.
1927*7c478bd9Sstevel@tonic-gate */
1928*7c478bd9Sstevel@tonic-gate (void) _del_FreeListNode(glh->list.node_mem, node);
1929*7c478bd9Sstevel@tonic-gate /*
1930*7c478bd9Sstevel@tonic-gate * Record the removal of a line from the list.
1931*7c478bd9Sstevel@tonic-gate */
1932*7c478bd9Sstevel@tonic-gate glh->nline--;
1933*7c478bd9Sstevel@tonic-gate return;
1934*7c478bd9Sstevel@tonic-gate }
1935*7c478bd9Sstevel@tonic-gate
1936*7c478bd9Sstevel@tonic-gate /*.......................................................................
1937*7c478bd9Sstevel@tonic-gate * Lookup the details of a given history line, given its id.
1938*7c478bd9Sstevel@tonic-gate *
1939*7c478bd9Sstevel@tonic-gate * Input:
1940*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
1941*7c478bd9Sstevel@tonic-gate * id GlLineID The sequential number of the line.
1942*7c478bd9Sstevel@tonic-gate * Input/Output:
1943*7c478bd9Sstevel@tonic-gate * line const char ** A pointer to a copy of the history line will be
1944*7c478bd9Sstevel@tonic-gate * assigned to *line. Beware that this pointer may
1945*7c478bd9Sstevel@tonic-gate * be invalidated by the next call to any public
1946*7c478bd9Sstevel@tonic-gate * history function.
1947*7c478bd9Sstevel@tonic-gate * group unsigned * The group membership of the line will be assigned
1948*7c478bd9Sstevel@tonic-gate * to *group.
1949*7c478bd9Sstevel@tonic-gate * timestamp time_t * The timestamp of the line will be assigned to
1950*7c478bd9Sstevel@tonic-gate * *timestamp.
1951*7c478bd9Sstevel@tonic-gate * Output:
1952*7c478bd9Sstevel@tonic-gate * return int 0 - The requested line wasn't found.
1953*7c478bd9Sstevel@tonic-gate * 1 - The line was found.
1954*7c478bd9Sstevel@tonic-gate */
_glh_lookup_history(GlHistory * glh,GlhLineID id,const char ** line,unsigned * group,time_t * timestamp)1955*7c478bd9Sstevel@tonic-gate int _glh_lookup_history(GlHistory *glh, GlhLineID id, const char **line,
1956*7c478bd9Sstevel@tonic-gate unsigned *group, time_t *timestamp)
1957*7c478bd9Sstevel@tonic-gate {
1958*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The located line location node */
1959*7c478bd9Sstevel@tonic-gate /*
1960*7c478bd9Sstevel@tonic-gate * Check the arguments.
1961*7c478bd9Sstevel@tonic-gate */
1962*7c478bd9Sstevel@tonic-gate if(!glh)
1963*7c478bd9Sstevel@tonic-gate return 0;
1964*7c478bd9Sstevel@tonic-gate /*
1965*7c478bd9Sstevel@tonic-gate * Search for the line that has the specified ID.
1966*7c478bd9Sstevel@tonic-gate */
1967*7c478bd9Sstevel@tonic-gate node = _glh_find_id(glh, id);
1968*7c478bd9Sstevel@tonic-gate /*
1969*7c478bd9Sstevel@tonic-gate * Not found?
1970*7c478bd9Sstevel@tonic-gate */
1971*7c478bd9Sstevel@tonic-gate if(!node)
1972*7c478bd9Sstevel@tonic-gate return 0;
1973*7c478bd9Sstevel@tonic-gate /*
1974*7c478bd9Sstevel@tonic-gate * Has the history line been requested?
1975*7c478bd9Sstevel@tonic-gate */
1976*7c478bd9Sstevel@tonic-gate if(line) {
1977*7c478bd9Sstevel@tonic-gate /*
1978*7c478bd9Sstevel@tonic-gate * If necessary, reallocate the lookup buffer to accomodate the size of
1979*7c478bd9Sstevel@tonic-gate * a copy of the located line.
1980*7c478bd9Sstevel@tonic-gate */
1981*7c478bd9Sstevel@tonic-gate if(node->line->len + 1 > glh->lbuf_dim) {
1982*7c478bd9Sstevel@tonic-gate int lbuf_dim = node->line->len + 1;
1983*7c478bd9Sstevel@tonic-gate char *lbuf = realloc(glh->lbuf, lbuf_dim);
1984*7c478bd9Sstevel@tonic-gate if(!lbuf) {
1985*7c478bd9Sstevel@tonic-gate errno = ENOMEM;
1986*7c478bd9Sstevel@tonic-gate return 0;
1987*7c478bd9Sstevel@tonic-gate };
1988*7c478bd9Sstevel@tonic-gate glh->lbuf_dim = lbuf_dim;
1989*7c478bd9Sstevel@tonic-gate glh->lbuf = lbuf;
1990*7c478bd9Sstevel@tonic-gate };
1991*7c478bd9Sstevel@tonic-gate /*
1992*7c478bd9Sstevel@tonic-gate * Copy the history line into the lookup buffer.
1993*7c478bd9Sstevel@tonic-gate */
1994*7c478bd9Sstevel@tonic-gate _glh_return_line(node->line, glh->lbuf, glh->lbuf_dim);
1995*7c478bd9Sstevel@tonic-gate /*
1996*7c478bd9Sstevel@tonic-gate * Assign the lookup buffer as the returned line pointer.
1997*7c478bd9Sstevel@tonic-gate */
1998*7c478bd9Sstevel@tonic-gate *line = glh->lbuf;
1999*7c478bd9Sstevel@tonic-gate };
2000*7c478bd9Sstevel@tonic-gate /*
2001*7c478bd9Sstevel@tonic-gate * Does the caller want to know the group of the line?
2002*7c478bd9Sstevel@tonic-gate */
2003*7c478bd9Sstevel@tonic-gate if(group)
2004*7c478bd9Sstevel@tonic-gate *group = node->group;
2005*7c478bd9Sstevel@tonic-gate /*
2006*7c478bd9Sstevel@tonic-gate * Does the caller want to know the timestamp of the line?
2007*7c478bd9Sstevel@tonic-gate */
2008*7c478bd9Sstevel@tonic-gate if(timestamp)
2009*7c478bd9Sstevel@tonic-gate *timestamp = node->timestamp;
2010*7c478bd9Sstevel@tonic-gate return 1;
2011*7c478bd9Sstevel@tonic-gate }
2012*7c478bd9Sstevel@tonic-gate
2013*7c478bd9Sstevel@tonic-gate /*.......................................................................
2014*7c478bd9Sstevel@tonic-gate * Lookup a node in the history list by its ID.
2015*7c478bd9Sstevel@tonic-gate *
2016*7c478bd9Sstevel@tonic-gate * Input:
2017*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2018*7c478bd9Sstevel@tonic-gate * id GlhLineID The ID of the line to be returned.
2019*7c478bd9Sstevel@tonic-gate * Output:
2020*7c478bd9Sstevel@tonic-gate * return GlhLIneNode * The located node, or NULL if not found.
2021*7c478bd9Sstevel@tonic-gate */
_glh_find_id(GlHistory * glh,GlhLineID id)2022*7c478bd9Sstevel@tonic-gate static GlhLineNode *_glh_find_id(GlHistory *glh, GlhLineID id)
2023*7c478bd9Sstevel@tonic-gate {
2024*7c478bd9Sstevel@tonic-gate GlhLineNode *node; /* The node being checked */
2025*7c478bd9Sstevel@tonic-gate /*
2026*7c478bd9Sstevel@tonic-gate * Is history enabled?
2027*7c478bd9Sstevel@tonic-gate */
2028*7c478bd9Sstevel@tonic-gate if(!glh->enable || !glh->list.head)
2029*7c478bd9Sstevel@tonic-gate return NULL;
2030*7c478bd9Sstevel@tonic-gate /*
2031*7c478bd9Sstevel@tonic-gate * If possible, start at the end point of the last ID search.
2032*7c478bd9Sstevel@tonic-gate * Otherwise start from the head of the list.
2033*7c478bd9Sstevel@tonic-gate */
2034*7c478bd9Sstevel@tonic-gate node = glh->id_node;
2035*7c478bd9Sstevel@tonic-gate if(!node)
2036*7c478bd9Sstevel@tonic-gate node = glh->list.head;
2037*7c478bd9Sstevel@tonic-gate /*
2038*7c478bd9Sstevel@tonic-gate * Search forwards from 'node'?
2039*7c478bd9Sstevel@tonic-gate */
2040*7c478bd9Sstevel@tonic-gate if(node->id < id) {
2041*7c478bd9Sstevel@tonic-gate while(node && node->id != id)
2042*7c478bd9Sstevel@tonic-gate node = node->next;
2043*7c478bd9Sstevel@tonic-gate glh->id_node = node ? node : glh->list.tail;
2044*7c478bd9Sstevel@tonic-gate /*
2045*7c478bd9Sstevel@tonic-gate * Search backwards from 'node'?
2046*7c478bd9Sstevel@tonic-gate */
2047*7c478bd9Sstevel@tonic-gate } else {
2048*7c478bd9Sstevel@tonic-gate while(node && node->id != id)
2049*7c478bd9Sstevel@tonic-gate node = node->prev;
2050*7c478bd9Sstevel@tonic-gate glh->id_node = node ? node : glh->list.head;
2051*7c478bd9Sstevel@tonic-gate };
2052*7c478bd9Sstevel@tonic-gate /*
2053*7c478bd9Sstevel@tonic-gate * Return the located node (this will be NULL if the ID wasn't found).
2054*7c478bd9Sstevel@tonic-gate */
2055*7c478bd9Sstevel@tonic-gate return node;
2056*7c478bd9Sstevel@tonic-gate }
2057*7c478bd9Sstevel@tonic-gate
2058*7c478bd9Sstevel@tonic-gate /*.......................................................................
2059*7c478bd9Sstevel@tonic-gate * Query the state of the history list. Note that any of the input/output
2060*7c478bd9Sstevel@tonic-gate * pointers can be specified as NULL.
2061*7c478bd9Sstevel@tonic-gate *
2062*7c478bd9Sstevel@tonic-gate * Input:
2063*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2064*7c478bd9Sstevel@tonic-gate * Input/Output:
2065*7c478bd9Sstevel@tonic-gate * enabled int * If history is enabled, *enabled will be
2066*7c478bd9Sstevel@tonic-gate * set to 1. Otherwise it will be assigned 0.
2067*7c478bd9Sstevel@tonic-gate * group unsigned * The current history group ID will be assigned
2068*7c478bd9Sstevel@tonic-gate * to *group.
2069*7c478bd9Sstevel@tonic-gate * max_lines int * The currently requested limit on the number
2070*7c478bd9Sstevel@tonic-gate * of history lines in the list, or -1 if
2071*7c478bd9Sstevel@tonic-gate * unlimited.
2072*7c478bd9Sstevel@tonic-gate */
_glh_state_of_history(GlHistory * glh,int * enabled,unsigned * group,int * max_lines)2073*7c478bd9Sstevel@tonic-gate void _glh_state_of_history(GlHistory *glh, int *enabled, unsigned *group,
2074*7c478bd9Sstevel@tonic-gate int *max_lines)
2075*7c478bd9Sstevel@tonic-gate {
2076*7c478bd9Sstevel@tonic-gate if(glh) {
2077*7c478bd9Sstevel@tonic-gate if(enabled)
2078*7c478bd9Sstevel@tonic-gate *enabled = glh->enable;
2079*7c478bd9Sstevel@tonic-gate if(group)
2080*7c478bd9Sstevel@tonic-gate *group = glh->group;
2081*7c478bd9Sstevel@tonic-gate if(max_lines)
2082*7c478bd9Sstevel@tonic-gate *max_lines = glh->max_lines;
2083*7c478bd9Sstevel@tonic-gate };
2084*7c478bd9Sstevel@tonic-gate }
2085*7c478bd9Sstevel@tonic-gate
2086*7c478bd9Sstevel@tonic-gate /*.......................................................................
2087*7c478bd9Sstevel@tonic-gate * Get the range of lines in the history buffer.
2088*7c478bd9Sstevel@tonic-gate *
2089*7c478bd9Sstevel@tonic-gate * Input:
2090*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2091*7c478bd9Sstevel@tonic-gate * Input/Output:
2092*7c478bd9Sstevel@tonic-gate * oldest unsigned long * The sequential entry number of the oldest
2093*7c478bd9Sstevel@tonic-gate * line in the history list will be assigned
2094*7c478bd9Sstevel@tonic-gate * to *oldest, unless there are no lines, in
2095*7c478bd9Sstevel@tonic-gate * which case 0 will be assigned.
2096*7c478bd9Sstevel@tonic-gate * newest unsigned long * The sequential entry number of the newest
2097*7c478bd9Sstevel@tonic-gate * line in the history list will be assigned
2098*7c478bd9Sstevel@tonic-gate * to *newest, unless there are no lines, in
2099*7c478bd9Sstevel@tonic-gate * which case 0 will be assigned.
2100*7c478bd9Sstevel@tonic-gate * nlines int * The number of lines currently in the history
2101*7c478bd9Sstevel@tonic-gate * list.
2102*7c478bd9Sstevel@tonic-gate */
_glh_range_of_history(GlHistory * glh,unsigned long * oldest,unsigned long * newest,int * nlines)2103*7c478bd9Sstevel@tonic-gate void _glh_range_of_history(GlHistory *glh, unsigned long *oldest,
2104*7c478bd9Sstevel@tonic-gate unsigned long *newest, int *nlines)
2105*7c478bd9Sstevel@tonic-gate {
2106*7c478bd9Sstevel@tonic-gate if(glh) {
2107*7c478bd9Sstevel@tonic-gate if(oldest)
2108*7c478bd9Sstevel@tonic-gate *oldest = glh->list.head ? glh->list.head->id : 0;
2109*7c478bd9Sstevel@tonic-gate if(newest)
2110*7c478bd9Sstevel@tonic-gate *newest = glh->list.tail ? glh->list.tail->id : 0;
2111*7c478bd9Sstevel@tonic-gate if(nlines)
2112*7c478bd9Sstevel@tonic-gate *nlines = glh->nline;
2113*7c478bd9Sstevel@tonic-gate };
2114*7c478bd9Sstevel@tonic-gate }
2115*7c478bd9Sstevel@tonic-gate
2116*7c478bd9Sstevel@tonic-gate /*.......................................................................
2117*7c478bd9Sstevel@tonic-gate * Return the size of the history buffer and the amount of the
2118*7c478bd9Sstevel@tonic-gate * buffer that is currently in use.
2119*7c478bd9Sstevel@tonic-gate *
2120*7c478bd9Sstevel@tonic-gate * Input:
2121*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2122*7c478bd9Sstevel@tonic-gate * Input/Output:
2123*7c478bd9Sstevel@tonic-gate * buff_size size_t * The size of the history buffer (bytes).
2124*7c478bd9Sstevel@tonic-gate * buff_used size_t * The amount of the history buffer that
2125*7c478bd9Sstevel@tonic-gate * is currently occupied (bytes).
2126*7c478bd9Sstevel@tonic-gate */
_glh_size_of_history(GlHistory * glh,size_t * buff_size,size_t * buff_used)2127*7c478bd9Sstevel@tonic-gate void _glh_size_of_history(GlHistory *glh, size_t *buff_size, size_t *buff_used)
2128*7c478bd9Sstevel@tonic-gate {
2129*7c478bd9Sstevel@tonic-gate if(glh) {
2130*7c478bd9Sstevel@tonic-gate if(buff_size)
2131*7c478bd9Sstevel@tonic-gate *buff_size = (glh->nbusy + glh->nfree) * GLH_SEG_SIZE;
2132*7c478bd9Sstevel@tonic-gate /*
2133*7c478bd9Sstevel@tonic-gate * Determine the amount of buffer space that is currently occupied.
2134*7c478bd9Sstevel@tonic-gate */
2135*7c478bd9Sstevel@tonic-gate if(buff_used)
2136*7c478bd9Sstevel@tonic-gate *buff_used = glh->nbusy * GLH_SEG_SIZE;
2137*7c478bd9Sstevel@tonic-gate };
2138*7c478bd9Sstevel@tonic-gate }
2139*7c478bd9Sstevel@tonic-gate
2140*7c478bd9Sstevel@tonic-gate /*.......................................................................
2141*7c478bd9Sstevel@tonic-gate * Return extra information (ie. in addition to that provided by errno)
2142*7c478bd9Sstevel@tonic-gate * about the last error to occur in any of the public functions of this
2143*7c478bd9Sstevel@tonic-gate * module.
2144*7c478bd9Sstevel@tonic-gate *
2145*7c478bd9Sstevel@tonic-gate * Input:
2146*7c478bd9Sstevel@tonic-gate * glh GlHistory * The container of the history list.
2147*7c478bd9Sstevel@tonic-gate * Output:
2148*7c478bd9Sstevel@tonic-gate * return const char * A pointer to the internal buffer in which
2149*7c478bd9Sstevel@tonic-gate * the error message is temporarily stored.
2150*7c478bd9Sstevel@tonic-gate */
_glh_last_error(GlHistory * glh)2151*7c478bd9Sstevel@tonic-gate const char *_glh_last_error(GlHistory *glh)
2152*7c478bd9Sstevel@tonic-gate {
2153*7c478bd9Sstevel@tonic-gate return glh ? _err_get_msg(glh->err) : "NULL GlHistory argument";
2154*7c478bd9Sstevel@tonic-gate }
2155*7c478bd9Sstevel@tonic-gate
2156*7c478bd9Sstevel@tonic-gate /*.......................................................................
2157*7c478bd9Sstevel@tonic-gate * Unless already stored, store a copy of the line in the history buffer,
2158*7c478bd9Sstevel@tonic-gate * then return a reference-counted hash-node pointer to this copy.
2159*7c478bd9Sstevel@tonic-gate *
2160*7c478bd9Sstevel@tonic-gate * Input:
2161*7c478bd9Sstevel@tonic-gate * glh GlHistory * The history maintenance buffer.
2162*7c478bd9Sstevel@tonic-gate * line const char * The history line to be recorded.
2163*7c478bd9Sstevel@tonic-gate * n size_t The length of the string, excluding any '\0'
2164*7c478bd9Sstevel@tonic-gate * terminator.
2165*7c478bd9Sstevel@tonic-gate * Output:
2166*7c478bd9Sstevel@tonic-gate * return GlhHashNode * The hash-node containing the stored line, or
2167*7c478bd9Sstevel@tonic-gate * NULL on error.
2168*7c478bd9Sstevel@tonic-gate */
_glh_acquire_copy(GlHistory * glh,const char * line,size_t n)2169*7c478bd9Sstevel@tonic-gate static GlhHashNode *_glh_acquire_copy(GlHistory *glh, const char *line,
2170*7c478bd9Sstevel@tonic-gate size_t n)
2171*7c478bd9Sstevel@tonic-gate {
2172*7c478bd9Sstevel@tonic-gate GlhHashBucket *bucket; /* The hash-table bucket of the line */
2173*7c478bd9Sstevel@tonic-gate GlhHashNode *hnode; /* The hash-table node of the line */
2174*7c478bd9Sstevel@tonic-gate int i;
2175*7c478bd9Sstevel@tonic-gate /*
2176*7c478bd9Sstevel@tonic-gate * In which bucket should the line be recorded?
2177*7c478bd9Sstevel@tonic-gate */
2178*7c478bd9Sstevel@tonic-gate bucket = glh_find_bucket(glh, line, n);
2179*7c478bd9Sstevel@tonic-gate /*
2180*7c478bd9Sstevel@tonic-gate * Is the line already recorded there?
2181*7c478bd9Sstevel@tonic-gate */
2182*7c478bd9Sstevel@tonic-gate hnode = glh_find_hash_node(bucket, line, n);
2183*7c478bd9Sstevel@tonic-gate /*
2184*7c478bd9Sstevel@tonic-gate * If the line isn't recorded in the buffer yet, make room for it.
2185*7c478bd9Sstevel@tonic-gate */
2186*7c478bd9Sstevel@tonic-gate if(!hnode) {
2187*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* A line segment */
2188*7c478bd9Sstevel@tonic-gate int offset; /* An offset into line[] */
2189*7c478bd9Sstevel@tonic-gate /*
2190*7c478bd9Sstevel@tonic-gate * How many string segments will be needed to record the new line,
2191*7c478bd9Sstevel@tonic-gate * including space for a '\0' terminator?
2192*7c478bd9Sstevel@tonic-gate */
2193*7c478bd9Sstevel@tonic-gate int nseg = ((n+1) + GLH_SEG_SIZE-1) / GLH_SEG_SIZE;
2194*7c478bd9Sstevel@tonic-gate /*
2195*7c478bd9Sstevel@tonic-gate * Discard the oldest history lines in the buffer until at least
2196*7c478bd9Sstevel@tonic-gate * 'nseg' segments have been freed up, or until we run out of buffer
2197*7c478bd9Sstevel@tonic-gate * space.
2198*7c478bd9Sstevel@tonic-gate */
2199*7c478bd9Sstevel@tonic-gate while(glh->nfree < nseg && glh->nbusy > 0)
2200*7c478bd9Sstevel@tonic-gate _glh_discard_line(glh, glh->list.head);
2201*7c478bd9Sstevel@tonic-gate /*
2202*7c478bd9Sstevel@tonic-gate * If the buffer is smaller than the new line, don't attempt to truncate
2203*7c478bd9Sstevel@tonic-gate * it to fit. Simply don't archive it.
2204*7c478bd9Sstevel@tonic-gate */
2205*7c478bd9Sstevel@tonic-gate if(glh->nfree < nseg)
2206*7c478bd9Sstevel@tonic-gate return NULL;
2207*7c478bd9Sstevel@tonic-gate /*
2208*7c478bd9Sstevel@tonic-gate * Record the line in the first 'nseg' segments of the list of unused segments.
2209*7c478bd9Sstevel@tonic-gate */
2210*7c478bd9Sstevel@tonic-gate offset = 0;
2211*7c478bd9Sstevel@tonic-gate for(i=0,seg=glh->unused; i<nseg-1; i++,seg=seg->next, offset+=GLH_SEG_SIZE)
2212*7c478bd9Sstevel@tonic-gate memcpy(seg->s, line + offset, GLH_SEG_SIZE);
2213*7c478bd9Sstevel@tonic-gate memcpy(seg->s, line + offset, n-offset);
2214*7c478bd9Sstevel@tonic-gate seg->s[n-offset] = '\0';
2215*7c478bd9Sstevel@tonic-gate /*
2216*7c478bd9Sstevel@tonic-gate * Create a new hash-node for the line.
2217*7c478bd9Sstevel@tonic-gate */
2218*7c478bd9Sstevel@tonic-gate hnode = (GlhHashNode *) _new_FreeListNode(glh->hash.node_mem);
2219*7c478bd9Sstevel@tonic-gate if(!hnode)
2220*7c478bd9Sstevel@tonic-gate return NULL;
2221*7c478bd9Sstevel@tonic-gate /*
2222*7c478bd9Sstevel@tonic-gate * Move the copy of the line from the list of unused segments to
2223*7c478bd9Sstevel@tonic-gate * the hash node.
2224*7c478bd9Sstevel@tonic-gate */
2225*7c478bd9Sstevel@tonic-gate hnode->head = glh->unused;
2226*7c478bd9Sstevel@tonic-gate glh->unused = seg->next;
2227*7c478bd9Sstevel@tonic-gate seg->next = NULL;
2228*7c478bd9Sstevel@tonic-gate glh->nbusy += nseg;
2229*7c478bd9Sstevel@tonic-gate glh->nfree -= nseg;
2230*7c478bd9Sstevel@tonic-gate /*
2231*7c478bd9Sstevel@tonic-gate * Prepend the new hash node to the list within the associated bucket.
2232*7c478bd9Sstevel@tonic-gate */
2233*7c478bd9Sstevel@tonic-gate hnode->next = bucket->lines;
2234*7c478bd9Sstevel@tonic-gate bucket->lines = hnode;
2235*7c478bd9Sstevel@tonic-gate /*
2236*7c478bd9Sstevel@tonic-gate * Initialize the rest of the members of the hash node.
2237*7c478bd9Sstevel@tonic-gate */
2238*7c478bd9Sstevel@tonic-gate hnode->len = n;
2239*7c478bd9Sstevel@tonic-gate hnode->reported = 0;
2240*7c478bd9Sstevel@tonic-gate hnode->used = 0;
2241*7c478bd9Sstevel@tonic-gate hnode->bucket = bucket;
2242*7c478bd9Sstevel@tonic-gate };
2243*7c478bd9Sstevel@tonic-gate /*
2244*7c478bd9Sstevel@tonic-gate * Increment the reference count of the line.
2245*7c478bd9Sstevel@tonic-gate */
2246*7c478bd9Sstevel@tonic-gate hnode->used++;
2247*7c478bd9Sstevel@tonic-gate return hnode;
2248*7c478bd9Sstevel@tonic-gate }
2249*7c478bd9Sstevel@tonic-gate
2250*7c478bd9Sstevel@tonic-gate /*.......................................................................
2251*7c478bd9Sstevel@tonic-gate * Decrement the reference count of the history line of a given hash-node,
2252*7c478bd9Sstevel@tonic-gate * and if the count reaches zero, delete both the hash-node and the
2253*7c478bd9Sstevel@tonic-gate * buffered copy of the line.
2254*7c478bd9Sstevel@tonic-gate *
2255*7c478bd9Sstevel@tonic-gate * Input:
2256*7c478bd9Sstevel@tonic-gate * glh GlHistory * The history container object.
2257*7c478bd9Sstevel@tonic-gate * hnode GlhHashNode * The node to be removed.
2258*7c478bd9Sstevel@tonic-gate * Output:
2259*7c478bd9Sstevel@tonic-gate * return GlhHashNode * The deleted hash-node (ie. NULL).
2260*7c478bd9Sstevel@tonic-gate */
_glh_discard_copy(GlHistory * glh,GlhHashNode * hnode)2261*7c478bd9Sstevel@tonic-gate static GlhHashNode *_glh_discard_copy(GlHistory *glh, GlhHashNode *hnode)
2262*7c478bd9Sstevel@tonic-gate {
2263*7c478bd9Sstevel@tonic-gate if(hnode) {
2264*7c478bd9Sstevel@tonic-gate GlhHashBucket *bucket = hnode->bucket;
2265*7c478bd9Sstevel@tonic-gate /*
2266*7c478bd9Sstevel@tonic-gate * If decrementing the reference count of the hash-node doesn't reduce
2267*7c478bd9Sstevel@tonic-gate * the reference count to zero, then the line is still in use in another
2268*7c478bd9Sstevel@tonic-gate * object, so don't delete it yet. Return NULL to indicate that the caller's
2269*7c478bd9Sstevel@tonic-gate * access to the hash-node copy has been deleted.
2270*7c478bd9Sstevel@tonic-gate */
2271*7c478bd9Sstevel@tonic-gate if(--hnode->used >= 1)
2272*7c478bd9Sstevel@tonic-gate return NULL;
2273*7c478bd9Sstevel@tonic-gate /*
2274*7c478bd9Sstevel@tonic-gate * Remove the hash-node from the list in its parent bucket.
2275*7c478bd9Sstevel@tonic-gate */
2276*7c478bd9Sstevel@tonic-gate if(bucket->lines == hnode) {
2277*7c478bd9Sstevel@tonic-gate bucket->lines = hnode->next;
2278*7c478bd9Sstevel@tonic-gate } else {
2279*7c478bd9Sstevel@tonic-gate GlhHashNode *prev; /* The node which precedes hnode in the bucket */
2280*7c478bd9Sstevel@tonic-gate for(prev=bucket->lines; prev && prev->next != hnode; prev=prev->next)
2281*7c478bd9Sstevel@tonic-gate ;
2282*7c478bd9Sstevel@tonic-gate if(prev)
2283*7c478bd9Sstevel@tonic-gate prev->next = hnode->next;
2284*7c478bd9Sstevel@tonic-gate };
2285*7c478bd9Sstevel@tonic-gate hnode->next = NULL;
2286*7c478bd9Sstevel@tonic-gate /*
2287*7c478bd9Sstevel@tonic-gate * Return the line segments of the hash-node to the list of unused segments.
2288*7c478bd9Sstevel@tonic-gate */
2289*7c478bd9Sstevel@tonic-gate if(hnode->head) {
2290*7c478bd9Sstevel@tonic-gate GlhLineSeg *tail; /* The last node in the list of line segments */
2291*7c478bd9Sstevel@tonic-gate int nseg; /* The number of segments being discarded */
2292*7c478bd9Sstevel@tonic-gate /*
2293*7c478bd9Sstevel@tonic-gate * Get the last node of the list of line segments referenced in the hash-node,
2294*7c478bd9Sstevel@tonic-gate * while counting the number of line segments used.
2295*7c478bd9Sstevel@tonic-gate */
2296*7c478bd9Sstevel@tonic-gate for(nseg=1,tail=hnode->head; tail->next; nseg++,tail=tail->next)
2297*7c478bd9Sstevel@tonic-gate ;
2298*7c478bd9Sstevel@tonic-gate /*
2299*7c478bd9Sstevel@tonic-gate * Prepend the list of line segments used by the hash node to the
2300*7c478bd9Sstevel@tonic-gate * list of unused line segments.
2301*7c478bd9Sstevel@tonic-gate */
2302*7c478bd9Sstevel@tonic-gate tail->next = glh->unused;
2303*7c478bd9Sstevel@tonic-gate glh->unused = hnode->head;
2304*7c478bd9Sstevel@tonic-gate glh->nbusy -= nseg;
2305*7c478bd9Sstevel@tonic-gate glh->nfree += nseg;
2306*7c478bd9Sstevel@tonic-gate };
2307*7c478bd9Sstevel@tonic-gate /*
2308*7c478bd9Sstevel@tonic-gate * Return the container of the hash-node to the freelist.
2309*7c478bd9Sstevel@tonic-gate */
2310*7c478bd9Sstevel@tonic-gate hnode = (GlhHashNode *) _del_FreeListNode(glh->hash.node_mem, hnode);
2311*7c478bd9Sstevel@tonic-gate };
2312*7c478bd9Sstevel@tonic-gate return NULL;
2313*7c478bd9Sstevel@tonic-gate }
2314*7c478bd9Sstevel@tonic-gate
2315*7c478bd9Sstevel@tonic-gate /*.......................................................................
2316*7c478bd9Sstevel@tonic-gate * Private function to locate the hash bucket associated with a given
2317*7c478bd9Sstevel@tonic-gate * history line.
2318*7c478bd9Sstevel@tonic-gate *
2319*7c478bd9Sstevel@tonic-gate * This uses a hash-function described in the dragon-book
2320*7c478bd9Sstevel@tonic-gate * ("Compilers - Principles, Techniques and Tools", by Aho, Sethi and
2321*7c478bd9Sstevel@tonic-gate * Ullman; pub. Adison Wesley) page 435.
2322*7c478bd9Sstevel@tonic-gate *
2323*7c478bd9Sstevel@tonic-gate * Input:
2324*7c478bd9Sstevel@tonic-gate * glh GlHistory * The history container object.
2325*7c478bd9Sstevel@tonic-gate * line const char * The historical line to look up.
2326*7c478bd9Sstevel@tonic-gate * n size_t The length of the line in line[], excluding
2327*7c478bd9Sstevel@tonic-gate * any '\0' terminator.
2328*7c478bd9Sstevel@tonic-gate * Output:
2329*7c478bd9Sstevel@tonic-gate * return GlhHashBucket * The located hash-bucket.
2330*7c478bd9Sstevel@tonic-gate */
glh_find_bucket(GlHistory * glh,const char * line,size_t n)2331*7c478bd9Sstevel@tonic-gate static GlhHashBucket *glh_find_bucket(GlHistory *glh, const char *line,
2332*7c478bd9Sstevel@tonic-gate size_t n)
2333*7c478bd9Sstevel@tonic-gate {
2334*7c478bd9Sstevel@tonic-gate unsigned long h = 0L;
2335*7c478bd9Sstevel@tonic-gate int i;
2336*7c478bd9Sstevel@tonic-gate for(i=0; i<n; i++) {
2337*7c478bd9Sstevel@tonic-gate unsigned char c = line[i];
2338*7c478bd9Sstevel@tonic-gate h = 65599UL * h + c; /* 65599 is a prime close to 2^16 */
2339*7c478bd9Sstevel@tonic-gate };
2340*7c478bd9Sstevel@tonic-gate return glh->hash.bucket + (h % GLH_HASH_SIZE);
2341*7c478bd9Sstevel@tonic-gate }
2342*7c478bd9Sstevel@tonic-gate
2343*7c478bd9Sstevel@tonic-gate /*.......................................................................
2344*7c478bd9Sstevel@tonic-gate * Find a given history line within a given hash-table bucket.
2345*7c478bd9Sstevel@tonic-gate *
2346*7c478bd9Sstevel@tonic-gate * Input:
2347*7c478bd9Sstevel@tonic-gate * bucket GlhHashBucket * The hash-table bucket in which to search.
2348*7c478bd9Sstevel@tonic-gate * line const char * The historical line to lookup.
2349*7c478bd9Sstevel@tonic-gate * n size_t The length of the line in line[], excluding
2350*7c478bd9Sstevel@tonic-gate * any '\0' terminator.
2351*7c478bd9Sstevel@tonic-gate * Output:
2352*7c478bd9Sstevel@tonic-gate * return GlhHashNode * The hash-table entry of the line, or NULL
2353*7c478bd9Sstevel@tonic-gate * if not found.
2354*7c478bd9Sstevel@tonic-gate */
glh_find_hash_node(GlhHashBucket * bucket,const char * line,size_t n)2355*7c478bd9Sstevel@tonic-gate static GlhHashNode *glh_find_hash_node(GlhHashBucket *bucket, const char *line,
2356*7c478bd9Sstevel@tonic-gate size_t n)
2357*7c478bd9Sstevel@tonic-gate {
2358*7c478bd9Sstevel@tonic-gate GlhHashNode *node; /* A node in the list of lines in the bucket */
2359*7c478bd9Sstevel@tonic-gate /*
2360*7c478bd9Sstevel@tonic-gate * Compare each of the lines in the list of lines, against 'line'.
2361*7c478bd9Sstevel@tonic-gate */
2362*7c478bd9Sstevel@tonic-gate for(node=bucket->lines; node; node=node->next) {
2363*7c478bd9Sstevel@tonic-gate if(_glh_is_line(node, line, n))
2364*7c478bd9Sstevel@tonic-gate return node;
2365*7c478bd9Sstevel@tonic-gate };
2366*7c478bd9Sstevel@tonic-gate return NULL;
2367*7c478bd9Sstevel@tonic-gate }
2368*7c478bd9Sstevel@tonic-gate
2369*7c478bd9Sstevel@tonic-gate /*.......................................................................
2370*7c478bd9Sstevel@tonic-gate * Return non-zero if a given string is equal to a given segmented line
2371*7c478bd9Sstevel@tonic-gate * node.
2372*7c478bd9Sstevel@tonic-gate *
2373*7c478bd9Sstevel@tonic-gate * Input:
2374*7c478bd9Sstevel@tonic-gate * hash GlhHashNode * The hash-table entry of the line.
2375*7c478bd9Sstevel@tonic-gate * line const char * The string to be compared to the segmented
2376*7c478bd9Sstevel@tonic-gate * line.
2377*7c478bd9Sstevel@tonic-gate * n size_t The length of the line in line[], excluding
2378*7c478bd9Sstevel@tonic-gate * any '\0' terminator.
2379*7c478bd9Sstevel@tonic-gate * Output:
2380*7c478bd9Sstevel@tonic-gate * return int 0 - The lines differ.
2381*7c478bd9Sstevel@tonic-gate * 1 - The lines are the same.
2382*7c478bd9Sstevel@tonic-gate */
_glh_is_line(GlhHashNode * hash,const char * line,size_t n)2383*7c478bd9Sstevel@tonic-gate static int _glh_is_line(GlhHashNode *hash, const char *line, size_t n)
2384*7c478bd9Sstevel@tonic-gate {
2385*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* A node in the list of line segments */
2386*7c478bd9Sstevel@tonic-gate int i;
2387*7c478bd9Sstevel@tonic-gate /*
2388*7c478bd9Sstevel@tonic-gate * Do the two lines have the same length?
2389*7c478bd9Sstevel@tonic-gate */
2390*7c478bd9Sstevel@tonic-gate if(n != hash->len)
2391*7c478bd9Sstevel@tonic-gate return 0;
2392*7c478bd9Sstevel@tonic-gate /*
2393*7c478bd9Sstevel@tonic-gate * Compare the characters of the segmented and unsegmented versions
2394*7c478bd9Sstevel@tonic-gate * of the line.
2395*7c478bd9Sstevel@tonic-gate */
2396*7c478bd9Sstevel@tonic-gate for(seg=hash->head; n>0 && seg; seg=seg->next) {
2397*7c478bd9Sstevel@tonic-gate const char *s = seg->s;
2398*7c478bd9Sstevel@tonic-gate for(i=0; n>0 && i<GLH_SEG_SIZE; i++,n--) {
2399*7c478bd9Sstevel@tonic-gate if(*line++ != *s++)
2400*7c478bd9Sstevel@tonic-gate return 0;
2401*7c478bd9Sstevel@tonic-gate };
2402*7c478bd9Sstevel@tonic-gate };
2403*7c478bd9Sstevel@tonic-gate return 1;
2404*7c478bd9Sstevel@tonic-gate }
2405*7c478bd9Sstevel@tonic-gate
2406*7c478bd9Sstevel@tonic-gate /*.......................................................................
2407*7c478bd9Sstevel@tonic-gate * Return non-zero if a given line has the specified segmented search
2408*7c478bd9Sstevel@tonic-gate * prefix.
2409*7c478bd9Sstevel@tonic-gate *
2410*7c478bd9Sstevel@tonic-gate * Input:
2411*7c478bd9Sstevel@tonic-gate * line GlhHashNode * The line to be compared against the prefix.
2412*7c478bd9Sstevel@tonic-gate * prefix GlhHashNode * The search prefix, or NULL to match any string.
2413*7c478bd9Sstevel@tonic-gate * Output:
2414*7c478bd9Sstevel@tonic-gate * return int 0 - The line doesn't have the specified prefix.
2415*7c478bd9Sstevel@tonic-gate * 1 - The line has the specified prefix.
2416*7c478bd9Sstevel@tonic-gate */
_glh_line_matches_prefix(GlhHashNode * line,GlhHashNode * prefix)2417*7c478bd9Sstevel@tonic-gate static int _glh_line_matches_prefix(GlhHashNode *line, GlhHashNode *prefix)
2418*7c478bd9Sstevel@tonic-gate {
2419*7c478bd9Sstevel@tonic-gate GlhLineStream lstr; /* The stream that is used to traverse 'line' */
2420*7c478bd9Sstevel@tonic-gate GlhLineStream pstr; /* The stream that is used to traverse 'prefix' */
2421*7c478bd9Sstevel@tonic-gate /*
2422*7c478bd9Sstevel@tonic-gate * When prefix==NULL, this means that the nul string
2423*7c478bd9Sstevel@tonic-gate * is to be matched, and this matches all lines.
2424*7c478bd9Sstevel@tonic-gate */
2425*7c478bd9Sstevel@tonic-gate if(!prefix)
2426*7c478bd9Sstevel@tonic-gate return 1;
2427*7c478bd9Sstevel@tonic-gate /*
2428*7c478bd9Sstevel@tonic-gate * Wrap the two history lines that are to be compared in iterator
2429*7c478bd9Sstevel@tonic-gate * stream objects.
2430*7c478bd9Sstevel@tonic-gate */
2431*7c478bd9Sstevel@tonic-gate glh_init_stream(&lstr, line);
2432*7c478bd9Sstevel@tonic-gate glh_init_stream(&pstr, prefix);
2433*7c478bd9Sstevel@tonic-gate /*
2434*7c478bd9Sstevel@tonic-gate * If the prefix contains a glob pattern, match the prefix as a glob
2435*7c478bd9Sstevel@tonic-gate * pattern.
2436*7c478bd9Sstevel@tonic-gate */
2437*7c478bd9Sstevel@tonic-gate if(glh_contains_glob(prefix))
2438*7c478bd9Sstevel@tonic-gate return glh_line_matches_glob(&lstr, &pstr);
2439*7c478bd9Sstevel@tonic-gate /*
2440*7c478bd9Sstevel@tonic-gate * Is the prefix longer than the line being compared against it?
2441*7c478bd9Sstevel@tonic-gate */
2442*7c478bd9Sstevel@tonic-gate if(prefix->len > line->len)
2443*7c478bd9Sstevel@tonic-gate return 0;
2444*7c478bd9Sstevel@tonic-gate /*
2445*7c478bd9Sstevel@tonic-gate * Compare the line to the prefix.
2446*7c478bd9Sstevel@tonic-gate */
2447*7c478bd9Sstevel@tonic-gate while(pstr.c != '\0' && pstr.c == lstr.c) {
2448*7c478bd9Sstevel@tonic-gate glh_step_stream(&lstr);
2449*7c478bd9Sstevel@tonic-gate glh_step_stream(&pstr);
2450*7c478bd9Sstevel@tonic-gate };
2451*7c478bd9Sstevel@tonic-gate /*
2452*7c478bd9Sstevel@tonic-gate * Did we reach the end of the prefix string before finding
2453*7c478bd9Sstevel@tonic-gate * any differences?
2454*7c478bd9Sstevel@tonic-gate */
2455*7c478bd9Sstevel@tonic-gate return pstr.c == '\0';
2456*7c478bd9Sstevel@tonic-gate }
2457*7c478bd9Sstevel@tonic-gate
2458*7c478bd9Sstevel@tonic-gate /*.......................................................................
2459*7c478bd9Sstevel@tonic-gate * Copy a given history line into a specified output string.
2460*7c478bd9Sstevel@tonic-gate *
2461*7c478bd9Sstevel@tonic-gate * Input:
2462*7c478bd9Sstevel@tonic-gate * hash GlhHashNode The hash-table entry of the history line to
2463*7c478bd9Sstevel@tonic-gate * be copied.
2464*7c478bd9Sstevel@tonic-gate * line char * A copy of the history line.
2465*7c478bd9Sstevel@tonic-gate * dim size_t The allocated dimension of the line buffer.
2466*7c478bd9Sstevel@tonic-gate */
_glh_return_line(GlhHashNode * hash,char * line,size_t dim)2467*7c478bd9Sstevel@tonic-gate static void _glh_return_line(GlhHashNode *hash, char *line, size_t dim)
2468*7c478bd9Sstevel@tonic-gate {
2469*7c478bd9Sstevel@tonic-gate GlhLineSeg *seg; /* A node in the list of line segments */
2470*7c478bd9Sstevel@tonic-gate int i;
2471*7c478bd9Sstevel@tonic-gate for(seg=hash->head; dim>0 && seg; seg=seg->next) {
2472*7c478bd9Sstevel@tonic-gate const char *s = seg->s;
2473*7c478bd9Sstevel@tonic-gate for(i=0; dim>0 && i<GLH_SEG_SIZE; i++,dim--)
2474*7c478bd9Sstevel@tonic-gate *line++ = *s++;
2475*7c478bd9Sstevel@tonic-gate };
2476*7c478bd9Sstevel@tonic-gate /*
2477*7c478bd9Sstevel@tonic-gate * If the line wouldn't fit in the output buffer, replace the last character
2478*7c478bd9Sstevel@tonic-gate * with a '\0' terminator.
2479*7c478bd9Sstevel@tonic-gate */
2480*7c478bd9Sstevel@tonic-gate if(dim==0)
2481*7c478bd9Sstevel@tonic-gate line[-1] = '\0';
2482*7c478bd9Sstevel@tonic-gate }
2483*7c478bd9Sstevel@tonic-gate
2484*7c478bd9Sstevel@tonic-gate /*.......................................................................
2485*7c478bd9Sstevel@tonic-gate * This function should be called whenever a new line recall is
2486*7c478bd9Sstevel@tonic-gate * attempted. It preserves a copy of the current input line in the
2487*7c478bd9Sstevel@tonic-gate * history list while other lines in the history list are being
2488*7c478bd9Sstevel@tonic-gate * returned.
2489*7c478bd9Sstevel@tonic-gate *
2490*7c478bd9Sstevel@tonic-gate * Input:
2491*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2492*7c478bd9Sstevel@tonic-gate * line char * The current contents of the input line buffer.
2493*7c478bd9Sstevel@tonic-gate * Output:
2494*7c478bd9Sstevel@tonic-gate * return int 0 - OK.
2495*7c478bd9Sstevel@tonic-gate * 1 - Error.
2496*7c478bd9Sstevel@tonic-gate */
_glh_prepare_for_recall(GlHistory * glh,char * line)2497*7c478bd9Sstevel@tonic-gate static int _glh_prepare_for_recall(GlHistory *glh, char *line)
2498*7c478bd9Sstevel@tonic-gate {
2499*7c478bd9Sstevel@tonic-gate /*
2500*7c478bd9Sstevel@tonic-gate * If a recall session has already been started, but we have returned
2501*7c478bd9Sstevel@tonic-gate * to the preserved copy of the input line, if the user has changed
2502*7c478bd9Sstevel@tonic-gate * this line, we should replace the preserved copy of the original
2503*7c478bd9Sstevel@tonic-gate * input line with the new one. To do this simply cancel the session,
2504*7c478bd9Sstevel@tonic-gate * so that a new session is started below.
2505*7c478bd9Sstevel@tonic-gate */
2506*7c478bd9Sstevel@tonic-gate if(glh->recall && glh->recall == glh->list.tail &&
2507*7c478bd9Sstevel@tonic-gate !_glh_is_line(glh->recall->line, line, strlen(line))) {
2508*7c478bd9Sstevel@tonic-gate _glh_cancel_search(glh);
2509*7c478bd9Sstevel@tonic-gate };
2510*7c478bd9Sstevel@tonic-gate /*
2511*7c478bd9Sstevel@tonic-gate * If this is the first line recall of a new recall session, save the
2512*7c478bd9Sstevel@tonic-gate * current line for potential recall later, and mark it as the last
2513*7c478bd9Sstevel@tonic-gate * line recalled.
2514*7c478bd9Sstevel@tonic-gate */
2515*7c478bd9Sstevel@tonic-gate if(!glh->recall) {
2516*7c478bd9Sstevel@tonic-gate if(_glh_add_history(glh, line, 1))
2517*7c478bd9Sstevel@tonic-gate return 1;
2518*7c478bd9Sstevel@tonic-gate glh->recall = glh->list.tail;
2519*7c478bd9Sstevel@tonic-gate /*
2520*7c478bd9Sstevel@tonic-gate * The above call to _glh_add_history() will have incremented the line
2521*7c478bd9Sstevel@tonic-gate * sequence number, after adding the line. Since we only want this to
2522*7c478bd9Sstevel@tonic-gate * to be incremented for permanently entered lines, decrement it again.
2523*7c478bd9Sstevel@tonic-gate */
2524*7c478bd9Sstevel@tonic-gate glh->seq--;
2525*7c478bd9Sstevel@tonic-gate };
2526*7c478bd9Sstevel@tonic-gate return 0;
2527*7c478bd9Sstevel@tonic-gate }
2528*7c478bd9Sstevel@tonic-gate
2529*7c478bd9Sstevel@tonic-gate /*.......................................................................
2530*7c478bd9Sstevel@tonic-gate * Return non-zero if a history search session is currently in progress.
2531*7c478bd9Sstevel@tonic-gate *
2532*7c478bd9Sstevel@tonic-gate * Input:
2533*7c478bd9Sstevel@tonic-gate * glh GlHistory * The input-line history maintenance object.
2534*7c478bd9Sstevel@tonic-gate * Output:
2535*7c478bd9Sstevel@tonic-gate * return int 0 - No search is currently in progress.
2536*7c478bd9Sstevel@tonic-gate * 1 - A search is in progress.
2537*7c478bd9Sstevel@tonic-gate */
_glh_search_active(GlHistory * glh)2538*7c478bd9Sstevel@tonic-gate int _glh_search_active(GlHistory *glh)
2539*7c478bd9Sstevel@tonic-gate {
2540*7c478bd9Sstevel@tonic-gate return glh && glh->recall;
2541*7c478bd9Sstevel@tonic-gate }
2542*7c478bd9Sstevel@tonic-gate
2543*7c478bd9Sstevel@tonic-gate /*.......................................................................
2544*7c478bd9Sstevel@tonic-gate * Initialize a character iterator object to point to the start of a
2545*7c478bd9Sstevel@tonic-gate * given history line. The first character of the line will be placed
2546*7c478bd9Sstevel@tonic-gate * in str->c, and subsequent characters can be placed there by calling
2547*7c478bd9Sstevel@tonic-gate * glh_strep_stream().
2548*7c478bd9Sstevel@tonic-gate *
2549*7c478bd9Sstevel@tonic-gate * Input:
2550*7c478bd9Sstevel@tonic-gate * str GlhLineStream * The iterator object to be initialized.
2551*7c478bd9Sstevel@tonic-gate * line GlhHashNode * The history line to be iterated over (a
2552*7c478bd9Sstevel@tonic-gate * NULL value here, is interpretted as an
2553*7c478bd9Sstevel@tonic-gate * empty string by glh_step_stream()).
2554*7c478bd9Sstevel@tonic-gate */
glh_init_stream(GlhLineStream * str,GlhHashNode * line)2555*7c478bd9Sstevel@tonic-gate static void glh_init_stream(GlhLineStream *str, GlhHashNode *line)
2556*7c478bd9Sstevel@tonic-gate {
2557*7c478bd9Sstevel@tonic-gate str->seg = line ? line->head : NULL;
2558*7c478bd9Sstevel@tonic-gate str->posn = 0;
2559*7c478bd9Sstevel@tonic-gate str->c = str->seg ? str->seg->s[0] : '\0';
2560*7c478bd9Sstevel@tonic-gate }
2561*7c478bd9Sstevel@tonic-gate
2562*7c478bd9Sstevel@tonic-gate /*.......................................................................
2563*7c478bd9Sstevel@tonic-gate * Copy the next unread character in the line being iterated, in str->c.
2564*7c478bd9Sstevel@tonic-gate * Once the end of the history line has been reached, all futher calls
2565*7c478bd9Sstevel@tonic-gate * set str->c to '\0'.
2566*7c478bd9Sstevel@tonic-gate *
2567*7c478bd9Sstevel@tonic-gate * Input:
2568*7c478bd9Sstevel@tonic-gate * str GlhLineStream * The history-line iterator to read from.
2569*7c478bd9Sstevel@tonic-gate */
glh_step_stream(GlhLineStream * str)2570*7c478bd9Sstevel@tonic-gate static void glh_step_stream(GlhLineStream *str)
2571*7c478bd9Sstevel@tonic-gate {
2572*7c478bd9Sstevel@tonic-gate /*
2573*7c478bd9Sstevel@tonic-gate * Get the character from the current iterator position within the line.
2574*7c478bd9Sstevel@tonic-gate */
2575*7c478bd9Sstevel@tonic-gate str->c = str->seg ? str->seg->s[str->posn] : '\0';
2576*7c478bd9Sstevel@tonic-gate /*
2577*7c478bd9Sstevel@tonic-gate * Unless we have reached the end of the string, move the iterator
2578*7c478bd9Sstevel@tonic-gate * to the position of the next character in the line.
2579*7c478bd9Sstevel@tonic-gate */
2580*7c478bd9Sstevel@tonic-gate if(str->c != '\0' && ++str->posn >= GLH_SEG_SIZE) {
2581*7c478bd9Sstevel@tonic-gate str->posn = 0;
2582*7c478bd9Sstevel@tonic-gate str->seg = str->seg->next;
2583*7c478bd9Sstevel@tonic-gate };
2584*7c478bd9Sstevel@tonic-gate }
2585*7c478bd9Sstevel@tonic-gate
2586*7c478bd9Sstevel@tonic-gate /*.......................................................................
2587*7c478bd9Sstevel@tonic-gate * Return non-zero if the specified search prefix contains any glob
2588*7c478bd9Sstevel@tonic-gate * wildcard characters.
2589*7c478bd9Sstevel@tonic-gate *
2590*7c478bd9Sstevel@tonic-gate * Input:
2591*7c478bd9Sstevel@tonic-gate * prefix GlhHashNode * The search prefix.
2592*7c478bd9Sstevel@tonic-gate * Output:
2593*7c478bd9Sstevel@tonic-gate * return int 0 - The prefix doesn't contain any globbing
2594*7c478bd9Sstevel@tonic-gate * characters.
2595*7c478bd9Sstevel@tonic-gate * 1 - The prefix contains at least one
2596*7c478bd9Sstevel@tonic-gate * globbing character.
2597*7c478bd9Sstevel@tonic-gate */
glh_contains_glob(GlhHashNode * prefix)2598*7c478bd9Sstevel@tonic-gate static int glh_contains_glob(GlhHashNode *prefix)
2599*7c478bd9Sstevel@tonic-gate {
2600*7c478bd9Sstevel@tonic-gate GlhLineStream pstr; /* The stream that is used to traverse 'prefix' */
2601*7c478bd9Sstevel@tonic-gate /*
2602*7c478bd9Sstevel@tonic-gate * Wrap a stream iterator around the prefix, so that we can traverse it
2603*7c478bd9Sstevel@tonic-gate * without worrying about line-segmentation.
2604*7c478bd9Sstevel@tonic-gate */
2605*7c478bd9Sstevel@tonic-gate glh_init_stream(&pstr, prefix);
2606*7c478bd9Sstevel@tonic-gate /*
2607*7c478bd9Sstevel@tonic-gate * Search for unescaped wildcard characters.
2608*7c478bd9Sstevel@tonic-gate */
2609*7c478bd9Sstevel@tonic-gate while(pstr.c != '\0') {
2610*7c478bd9Sstevel@tonic-gate switch(pstr.c) {
2611*7c478bd9Sstevel@tonic-gate case '\\': /* Skip escaped characters */
2612*7c478bd9Sstevel@tonic-gate glh_step_stream(&pstr);
2613*7c478bd9Sstevel@tonic-gate break;
2614*7c478bd9Sstevel@tonic-gate case '*': case '?': case '[': /* A wildcard character? */
2615*7c478bd9Sstevel@tonic-gate return 1;
2616*7c478bd9Sstevel@tonic-gate break;
2617*7c478bd9Sstevel@tonic-gate };
2618*7c478bd9Sstevel@tonic-gate glh_step_stream(&pstr);
2619*7c478bd9Sstevel@tonic-gate };
2620*7c478bd9Sstevel@tonic-gate /*
2621*7c478bd9Sstevel@tonic-gate * No wildcard characters were found.
2622*7c478bd9Sstevel@tonic-gate */
2623*7c478bd9Sstevel@tonic-gate return 0;
2624*7c478bd9Sstevel@tonic-gate }
2625*7c478bd9Sstevel@tonic-gate
2626*7c478bd9Sstevel@tonic-gate /*.......................................................................
2627*7c478bd9Sstevel@tonic-gate * Return non-zero if the history line matches a search prefix containing
2628*7c478bd9Sstevel@tonic-gate * a glob pattern.
2629*7c478bd9Sstevel@tonic-gate *
2630*7c478bd9Sstevel@tonic-gate * Input:
2631*7c478bd9Sstevel@tonic-gate * lstr GlhLineStream * The iterator stream being used to traverse
2632*7c478bd9Sstevel@tonic-gate * the history line that is being matched.
2633*7c478bd9Sstevel@tonic-gate * pstr GlhLineStream * The iterator stream being used to traverse
2634*7c478bd9Sstevel@tonic-gate * the pattern.
2635*7c478bd9Sstevel@tonic-gate * Output:
2636*7c478bd9Sstevel@tonic-gate * return int 0 - Doesn't match.
2637*7c478bd9Sstevel@tonic-gate * 1 - The line matches the pattern.
2638*7c478bd9Sstevel@tonic-gate */
glh_line_matches_glob(GlhLineStream * lstr,GlhLineStream * pstr)2639*7c478bd9Sstevel@tonic-gate static int glh_line_matches_glob(GlhLineStream *lstr, GlhLineStream *pstr)
2640*7c478bd9Sstevel@tonic-gate {
2641*7c478bd9Sstevel@tonic-gate /*
2642*7c478bd9Sstevel@tonic-gate * Match each character of the pattern until we reach the end of the
2643*7c478bd9Sstevel@tonic-gate * pattern.
2644*7c478bd9Sstevel@tonic-gate */
2645*7c478bd9Sstevel@tonic-gate while(pstr->c != '\0') {
2646*7c478bd9Sstevel@tonic-gate /*
2647*7c478bd9Sstevel@tonic-gate * Handle the next character of the pattern.
2648*7c478bd9Sstevel@tonic-gate */
2649*7c478bd9Sstevel@tonic-gate switch(pstr->c) {
2650*7c478bd9Sstevel@tonic-gate /*
2651*7c478bd9Sstevel@tonic-gate * A match zero-or-more characters wildcard operator.
2652*7c478bd9Sstevel@tonic-gate */
2653*7c478bd9Sstevel@tonic-gate case '*':
2654*7c478bd9Sstevel@tonic-gate /*
2655*7c478bd9Sstevel@tonic-gate * Skip the '*' character in the pattern.
2656*7c478bd9Sstevel@tonic-gate */
2657*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2658*7c478bd9Sstevel@tonic-gate /*
2659*7c478bd9Sstevel@tonic-gate * If the pattern ends with the '*' wildcard, then the
2660*7c478bd9Sstevel@tonic-gate * rest of the line matches this.
2661*7c478bd9Sstevel@tonic-gate */
2662*7c478bd9Sstevel@tonic-gate if(pstr->c == '\0')
2663*7c478bd9Sstevel@tonic-gate return 1;
2664*7c478bd9Sstevel@tonic-gate /*
2665*7c478bd9Sstevel@tonic-gate * Using the wildcard to match successively longer sections of
2666*7c478bd9Sstevel@tonic-gate * the remaining characters of the line, attempt to match
2667*7c478bd9Sstevel@tonic-gate * the tail of the line against the tail of the pattern.
2668*7c478bd9Sstevel@tonic-gate */
2669*7c478bd9Sstevel@tonic-gate while(lstr->c) {
2670*7c478bd9Sstevel@tonic-gate GlhLineStream old_lstr = *lstr;
2671*7c478bd9Sstevel@tonic-gate GlhLineStream old_pstr = *pstr;
2672*7c478bd9Sstevel@tonic-gate if(glh_line_matches_glob(lstr, pstr))
2673*7c478bd9Sstevel@tonic-gate return 1;
2674*7c478bd9Sstevel@tonic-gate /*
2675*7c478bd9Sstevel@tonic-gate * Restore the line and pattern iterators for a new try.
2676*7c478bd9Sstevel@tonic-gate */
2677*7c478bd9Sstevel@tonic-gate *lstr = old_lstr;
2678*7c478bd9Sstevel@tonic-gate *pstr = old_pstr;
2679*7c478bd9Sstevel@tonic-gate /*
2680*7c478bd9Sstevel@tonic-gate * Prepare to try again, one character further into the line.
2681*7c478bd9Sstevel@tonic-gate */
2682*7c478bd9Sstevel@tonic-gate glh_step_stream(lstr);
2683*7c478bd9Sstevel@tonic-gate };
2684*7c478bd9Sstevel@tonic-gate return 0; /* The pattern following the '*' didn't match */
2685*7c478bd9Sstevel@tonic-gate break;
2686*7c478bd9Sstevel@tonic-gate /*
2687*7c478bd9Sstevel@tonic-gate * A match-one-character wildcard operator.
2688*7c478bd9Sstevel@tonic-gate */
2689*7c478bd9Sstevel@tonic-gate case '?':
2690*7c478bd9Sstevel@tonic-gate /*
2691*7c478bd9Sstevel@tonic-gate * If there is a character to be matched, skip it and advance the
2692*7c478bd9Sstevel@tonic-gate * pattern pointer.
2693*7c478bd9Sstevel@tonic-gate */
2694*7c478bd9Sstevel@tonic-gate if(lstr->c) {
2695*7c478bd9Sstevel@tonic-gate glh_step_stream(lstr);
2696*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2697*7c478bd9Sstevel@tonic-gate /*
2698*7c478bd9Sstevel@tonic-gate * If we hit the end of the line, there is no character
2699*7c478bd9Sstevel@tonic-gate * matching the operator, so the pattern doesn't match.
2700*7c478bd9Sstevel@tonic-gate */
2701*7c478bd9Sstevel@tonic-gate } else {
2702*7c478bd9Sstevel@tonic-gate return 0;
2703*7c478bd9Sstevel@tonic-gate };
2704*7c478bd9Sstevel@tonic-gate break;
2705*7c478bd9Sstevel@tonic-gate /*
2706*7c478bd9Sstevel@tonic-gate * A character range operator, with the character ranges enclosed
2707*7c478bd9Sstevel@tonic-gate * in matching square brackets.
2708*7c478bd9Sstevel@tonic-gate */
2709*7c478bd9Sstevel@tonic-gate case '[':
2710*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr); /* Skip the '[' character */
2711*7c478bd9Sstevel@tonic-gate if(!lstr->c || !glh_matches_range(lstr->c, pstr))
2712*7c478bd9Sstevel@tonic-gate return 0;
2713*7c478bd9Sstevel@tonic-gate glh_step_stream(lstr); /* Skip the character that matched */
2714*7c478bd9Sstevel@tonic-gate break;
2715*7c478bd9Sstevel@tonic-gate /*
2716*7c478bd9Sstevel@tonic-gate * A backslash in the pattern prevents the following character as
2717*7c478bd9Sstevel@tonic-gate * being seen as a special character.
2718*7c478bd9Sstevel@tonic-gate */
2719*7c478bd9Sstevel@tonic-gate case '\\':
2720*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr); /* Skip the backslash */
2721*7c478bd9Sstevel@tonic-gate /* Note fallthrough to default */
2722*7c478bd9Sstevel@tonic-gate /*
2723*7c478bd9Sstevel@tonic-gate * A normal character to be matched explicitly.
2724*7c478bd9Sstevel@tonic-gate */
2725*7c478bd9Sstevel@tonic-gate default:
2726*7c478bd9Sstevel@tonic-gate if(lstr->c == pstr->c) {
2727*7c478bd9Sstevel@tonic-gate glh_step_stream(lstr);
2728*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2729*7c478bd9Sstevel@tonic-gate } else {
2730*7c478bd9Sstevel@tonic-gate return 0;
2731*7c478bd9Sstevel@tonic-gate };
2732*7c478bd9Sstevel@tonic-gate break;
2733*7c478bd9Sstevel@tonic-gate };
2734*7c478bd9Sstevel@tonic-gate };
2735*7c478bd9Sstevel@tonic-gate /*
2736*7c478bd9Sstevel@tonic-gate * To get here, pattern must have been exhausted. The line only
2737*7c478bd9Sstevel@tonic-gate * matches the pattern if the line as also been exhausted.
2738*7c478bd9Sstevel@tonic-gate */
2739*7c478bd9Sstevel@tonic-gate return pstr->c == '\0' && lstr->c == '\0';
2740*7c478bd9Sstevel@tonic-gate }
2741*7c478bd9Sstevel@tonic-gate
2742*7c478bd9Sstevel@tonic-gate /*.......................................................................
2743*7c478bd9Sstevel@tonic-gate * Match a character range expression terminated by an unescaped close
2744*7c478bd9Sstevel@tonic-gate * square bracket.
2745*7c478bd9Sstevel@tonic-gate *
2746*7c478bd9Sstevel@tonic-gate * Input:
2747*7c478bd9Sstevel@tonic-gate * c char The character to be matched with the range
2748*7c478bd9Sstevel@tonic-gate * pattern.
2749*7c478bd9Sstevel@tonic-gate * pstr GlhLineStream * The iterator stream being used to traverse
2750*7c478bd9Sstevel@tonic-gate * the pattern.
2751*7c478bd9Sstevel@tonic-gate * Output:
2752*7c478bd9Sstevel@tonic-gate * return int 0 - Doesn't match.
2753*7c478bd9Sstevel@tonic-gate * 1 - The character matched.
2754*7c478bd9Sstevel@tonic-gate */
glh_matches_range(char c,GlhLineStream * pstr)2755*7c478bd9Sstevel@tonic-gate static int glh_matches_range(char c, GlhLineStream *pstr)
2756*7c478bd9Sstevel@tonic-gate {
2757*7c478bd9Sstevel@tonic-gate int invert = 0; /* True to invert the sense of the match */
2758*7c478bd9Sstevel@tonic-gate int matched = 0; /* True if the character matched the pattern */
2759*7c478bd9Sstevel@tonic-gate char lastc = '\0'; /* The previous character in the pattern */
2760*7c478bd9Sstevel@tonic-gate /*
2761*7c478bd9Sstevel@tonic-gate * If the first character is a caret, the sense of the match is
2762*7c478bd9Sstevel@tonic-gate * inverted and only if the character isn't one of those in the
2763*7c478bd9Sstevel@tonic-gate * range, do we say that it matches.
2764*7c478bd9Sstevel@tonic-gate */
2765*7c478bd9Sstevel@tonic-gate if(pstr->c == '^') {
2766*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2767*7c478bd9Sstevel@tonic-gate invert = 1;
2768*7c478bd9Sstevel@tonic-gate };
2769*7c478bd9Sstevel@tonic-gate /*
2770*7c478bd9Sstevel@tonic-gate * The hyphen is only a special character when it follows the first
2771*7c478bd9Sstevel@tonic-gate * character of the range (not including the caret).
2772*7c478bd9Sstevel@tonic-gate */
2773*7c478bd9Sstevel@tonic-gate if(pstr->c == '-') {
2774*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2775*7c478bd9Sstevel@tonic-gate if(c == '-')
2776*7c478bd9Sstevel@tonic-gate matched = 1;
2777*7c478bd9Sstevel@tonic-gate /*
2778*7c478bd9Sstevel@tonic-gate * Skip other leading '-' characters since they make no sense.
2779*7c478bd9Sstevel@tonic-gate */
2780*7c478bd9Sstevel@tonic-gate while(pstr->c == '-')
2781*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2782*7c478bd9Sstevel@tonic-gate };
2783*7c478bd9Sstevel@tonic-gate /*
2784*7c478bd9Sstevel@tonic-gate * The hyphen is only a special character when it follows the first
2785*7c478bd9Sstevel@tonic-gate * character of the range (not including the caret or a hyphen).
2786*7c478bd9Sstevel@tonic-gate */
2787*7c478bd9Sstevel@tonic-gate if(pstr->c == ']') {
2788*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2789*7c478bd9Sstevel@tonic-gate if(c == ']')
2790*7c478bd9Sstevel@tonic-gate matched = 1;
2791*7c478bd9Sstevel@tonic-gate };
2792*7c478bd9Sstevel@tonic-gate /*
2793*7c478bd9Sstevel@tonic-gate * Having dealt with the characters that have special meanings at
2794*7c478bd9Sstevel@tonic-gate * the beginning of a character range expression, see if the
2795*7c478bd9Sstevel@tonic-gate * character matches any of the remaining characters of the range,
2796*7c478bd9Sstevel@tonic-gate * up until a terminating ']' character is seen.
2797*7c478bd9Sstevel@tonic-gate */
2798*7c478bd9Sstevel@tonic-gate while(!matched && pstr->c && pstr->c != ']') {
2799*7c478bd9Sstevel@tonic-gate /*
2800*7c478bd9Sstevel@tonic-gate * Is this a range of characters signaled by the two end characters
2801*7c478bd9Sstevel@tonic-gate * separated by a hyphen?
2802*7c478bd9Sstevel@tonic-gate */
2803*7c478bd9Sstevel@tonic-gate if(pstr->c == '-') {
2804*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr); /* Skip the hyphen */
2805*7c478bd9Sstevel@tonic-gate if(pstr->c != ']') {
2806*7c478bd9Sstevel@tonic-gate if(c >= lastc && c <= pstr->c)
2807*7c478bd9Sstevel@tonic-gate matched = 1;
2808*7c478bd9Sstevel@tonic-gate };
2809*7c478bd9Sstevel@tonic-gate /*
2810*7c478bd9Sstevel@tonic-gate * A normal character to be compared directly.
2811*7c478bd9Sstevel@tonic-gate */
2812*7c478bd9Sstevel@tonic-gate } else if(pstr->c == c) {
2813*7c478bd9Sstevel@tonic-gate matched = 1;
2814*7c478bd9Sstevel@tonic-gate };
2815*7c478bd9Sstevel@tonic-gate /*
2816*7c478bd9Sstevel@tonic-gate * Record and skip the character that we just processed.
2817*7c478bd9Sstevel@tonic-gate */
2818*7c478bd9Sstevel@tonic-gate lastc = pstr->c;
2819*7c478bd9Sstevel@tonic-gate if(pstr->c != ']')
2820*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2821*7c478bd9Sstevel@tonic-gate };
2822*7c478bd9Sstevel@tonic-gate /*
2823*7c478bd9Sstevel@tonic-gate * Find the terminating ']'.
2824*7c478bd9Sstevel@tonic-gate */
2825*7c478bd9Sstevel@tonic-gate while(pstr->c && pstr->c != ']')
2826*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2827*7c478bd9Sstevel@tonic-gate /*
2828*7c478bd9Sstevel@tonic-gate * Did we find a terminating ']'?
2829*7c478bd9Sstevel@tonic-gate */
2830*7c478bd9Sstevel@tonic-gate if(pstr->c == ']') {
2831*7c478bd9Sstevel@tonic-gate /*
2832*7c478bd9Sstevel@tonic-gate * Skip the terminating ']'.
2833*7c478bd9Sstevel@tonic-gate */
2834*7c478bd9Sstevel@tonic-gate glh_step_stream(pstr);
2835*7c478bd9Sstevel@tonic-gate /*
2836*7c478bd9Sstevel@tonic-gate * If the pattern started with a caret, invert the sense of the match.
2837*7c478bd9Sstevel@tonic-gate */
2838*7c478bd9Sstevel@tonic-gate if(invert)
2839*7c478bd9Sstevel@tonic-gate matched = !matched;
2840*7c478bd9Sstevel@tonic-gate /*
2841*7c478bd9Sstevel@tonic-gate * If the pattern didn't end with a ']', then it doesn't match,
2842*7c478bd9Sstevel@tonic-gate * regardless of the value of the required sense of the match.
2843*7c478bd9Sstevel@tonic-gate */
2844*7c478bd9Sstevel@tonic-gate } else {
2845*7c478bd9Sstevel@tonic-gate matched = 0;
2846*7c478bd9Sstevel@tonic-gate };
2847*7c478bd9Sstevel@tonic-gate return matched;
2848*7c478bd9Sstevel@tonic-gate }
2849*7c478bd9Sstevel@tonic-gate
2850