1 /*
2 * sqsh_history.c - Rolling queue of command histories
3 *
4 * Copyright (C) 1995, 1996 by Scott C. Gray
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, write to the Free Software
18 * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * You may contact the author :
21 * e-mail: gray@voicenet.com
22 * grays@xtend-tech.com
23 * gray@xenotropic.com
24 */
25 #include <stdio.h>
26 #include <sys/stat.h>
27 #include "sqsh_config.h"
28 #include "sqsh_error.h"
29 #include "sqsh_expand.h"
30 #include "sqsh_global.h"
31 #include "sqsh_history.h"
32 #include "sqsh_varbuf.h"
33
34 /*-- Current Version --*/
35 #if !defined(lint) && !defined(__LINT__)
36 static char RCS_Id[] = "$Id: sqsh_history.c,v 1.10 2013/05/03 11:19:38 mwesdorp Exp $" ;
37 USE(RCS_Id)
38 #endif /* !defined(lint) */
39
40 /*-- Local Prototypes --*/
41 static hisbuf_t* hisbuf_create _ANSI_ARGS(( history_t*, char*, int )) ;
42 static hisbuf_t* hisbuf_get _ANSI_ARGS(( history_t*, int )) ;
43 static int hisbuf_destroy _ANSI_ARGS(( hisbuf_t* )) ;
44 static unsigned long adler32 _ANSI_ARGS(( char*, int )) ; /* sqsh-2.1.6 feature */
45 static int chk_buf_ifs _ANSI_ARGS(( char*, int )) ; /* sqsh-2.1.7 feature */
46 static void hist_auto_save _ANSI_ARGS(( history_t* )) ; /* sqsh-2.1.7 feature */
47 static int history_merge _ANSI_ARGS(( history_t*, history_t*)) ; /* sqsh-2.2.0 feature */
48
49
50 /*
51 * history_create():
52 *
53 * Allocates a history structure large enough to hold up to size history
54 * entries.
55 */
history_create(size)56 history_t* history_create( size )
57 int size ;
58 {
59 history_t *h ;
60
61 /*-- Always check your parameters --*/
62 if( size < 0 ) {
63 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
64 return NULL ;
65 }
66
67 /*-- Allocate a new history structure --*/
68 if( (h = (history_t*)malloc(sizeof(history_t))) == NULL ) {
69 sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
70 return NULL ;
71 }
72
73 /*-- Initialize elements of structure --*/
74 h->h_size = size ;
75 h->h_nitems = 0 ;
76 h->h_change = HISTSAVE_INIT ;
77 h->h_start = NULL ;
78 h->h_next_nbr = 1 ;
79 h->h_end = NULL ;
80
81 sqsh_set_error( SQSH_E_NONE, NULL ) ;
82 return h ;
83 }
84
85 /*
86 * history_get_size():
87 *
88 * Returns the total size of the current history.
89 */
history_get_size(h)90 int history_get_size( h )
91 history_t *h ;
92 {
93 /*-- Check parameters --*/
94 if( h == NULL ) {
95 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
96 return -1 ;
97 }
98
99 return h->h_size ;
100 }
101
102 /*
103 * history_get_nitems():
104 *
105 * Returns the total number of entries in history.
106 */
history_get_nitems(h)107 int history_get_nitems( h )
108 history_t *h ;
109 {
110 /*-- Check parameters --*/
111 if( h == NULL ) {
112 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
113 return -1 ;
114 }
115
116 return h->h_nitems ;
117 }
118
119 /*
120 * history_get_nbr():
121 *
122 * Returns the next available history entry number.
123 */
history_get_nbr(h)124 int history_get_nbr( h )
125 history_t *h ;
126 {
127 return h->h_next_nbr ;
128 }
129
history_set_size(h,size)130 int history_set_size( h, size )
131 history_t *h ;
132 int size ;
133 {
134 hisbuf_t *hb ;
135
136 /*-- Check parameters --*/
137 if( h == NULL || size < 0 ) {
138 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
139 return False ;
140 }
141
142 /*
143 * If the size was smaller than the current number of items
144 * in the history then we need to roll older items off of
145 * the history until we can fit the new size.
146 */
147 while( size < h->h_nitems ) {
148
149 /*
150 * Unlink the oldest entry from the list and destroy
151 * it.
152 */
153 hb = h->h_end ;
154 if( hb->hb_prv != NULL ) {
155 h->h_end = hb->hb_prv ;
156 h->h_end->hb_nxt = NULL ;
157 } else {
158 h->h_end = h->h_start = NULL ;
159 }
160 hisbuf_destroy( hb ) ;
161
162 --h->h_nitems ;
163 }
164 hist_auto_save ( h ) ;
165
166 h->h_size = size ;
167
168 sqsh_set_error( SQSH_E_NONE, NULL ) ;
169 return True ;
170 }
171
172 /*
173 * history_append():
174 *
175 * Adds buffer buf to history h.
176 */
history_append(h,buf)177 int history_append( h, buf )
178 history_t *h ;
179 char *buf ;
180 {
181 hisbuf_t *hisbuf, *hb_tmp ;
182 int len ;
183 /* sqsh-2.1.6 - New variables */
184 register int i;
185 unsigned long chksum;
186 char *histunique;
187
188
189 /*-- Check parameters --*/
190 if( h == NULL || buf == NULL ) {
191 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
192 return False ;
193 }
194
195 /*-- Get length of buffer --*/
196 len = strlen( buf ) ;
197
198 /*
199 * If the buffer is set up to hold no entries, or the buffer is
200 * empty, then we don't need to go any further.
201 */
202 if( h->h_size == 0 || len == 0 ) {
203 sqsh_set_error( SQSH_E_NONE, NULL ) ;
204 return True ;
205 }
206
207 /*
208 * sqsh-2.1.7 feature - Ignore 'empty' buffers with only
209 * IFS characters.
210 */
211 if (chk_buf_ifs (buf, len))
212 return True ;
213
214 /*
215 * sqsh-2.1.6 feature - Calculate a checksum on the buffer.
216 * If we do not want to store duplicate history entries, then
217 * we search the linked list for an identical entry. If found,
218 * do not store the buffer in the list, but maintain MRU-LRU
219 * order; else continue as usual.
220 */
221 chksum = adler32 (buf, len);
222 DBG(sqsh_debug(DEBUG_HISTORY, "sqsh_history: checksum of buffer: %u\n", chksum);)
223 env_get (g_env, "histunique", &histunique);
224 if (histunique != NULL && *histunique != '0')
225 {
226 for (hb_tmp = h->h_start; hb_tmp != NULL && hb_tmp->hb_chksum != chksum;
227 hb_tmp = hb_tmp->hb_nxt);
228
229 /*
230 * If hb_tmp is not NULL we just found an identical history buffer entry.
231 */
232 if (hb_tmp != NULL)
233 {
234 /*
235 * sqsh-2.1.7 - Adjust buffer access datetime and increase buffer usage count.
236 */
237 time( &hb_tmp->hb_dttm );
238 hb_tmp->hb_count++;
239
240 /*
241 * - Nothing to do when it is already the first entry
242 * (or only one).
243 */
244 if (hb_tmp == h->h_start)
245 {
246 sqsh_set_error( SQSH_E_NONE, NULL ) ;
247 return True;
248 }
249 else
250 {
251 /*
252 * Is it currently the last entry then we have to adjust h->h_end,
253 * else we have to pull the current entry out of the list.
254 */
255 if (hb_tmp == h->h_end)
256 {
257 h->h_end = hb_tmp->hb_prv;
258 h->h_end->hb_nxt = NULL;
259 }
260 else
261 {
262 hb_tmp->hb_prv->hb_nxt = hb_tmp->hb_nxt;
263 hb_tmp->hb_nxt->hb_prv = hb_tmp->hb_prv;
264 }
265 }
266 /*
267 * Now put the entry to the start of the list.
268 * (Also save the recent buffer number for renumbering the list).
269 */
270 i = h->h_start->hb_nbr;
271 h->h_start->hb_prv = hb_tmp;
272 hb_tmp->hb_nxt = h->h_start;
273 h->h_start = hb_tmp;
274 hb_tmp->hb_prv = NULL;
275 /*
276 * Adjust the buffer numbers in the list.
277 */
278 for (hb_tmp = h->h_start; hb_tmp != NULL && hb_tmp->hb_nbr != i;
279 hb_tmp->hb_nbr = i--, hb_tmp = hb_tmp->hb_nxt);
280 hist_auto_save ( h ) ;
281
282 sqsh_set_error( SQSH_E_NONE, NULL ) ;
283 return True;
284 }
285 }
286
287 /*
288 * Go ahead an try to allocate a buffer before we try to
289 * stick it into the history.
290 */
291 if( (hisbuf = hisbuf_create( h, buf, len )) == NULL ) {
292 sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
293 return False ;
294 }
295
296 /*
297 * sqsh-2.1.6 feature - Store the checksum of the new buffer in the structure.
298 */
299 hisbuf->hb_chksum = chksum;
300
301 /*
302 * sqsh-2.1.7 - Set current date and time in hb_dttm and set the usage count
303 * of the buffer to 1 in hb_count.
304 */
305 time( &hisbuf->hb_dttm );
306 hisbuf->hb_count = 1;
307
308 /*
309 * If the buffer is full, then we need to roll and entry off
310 * of the beginning of the list.
311 */
312 if( h->h_nitems == h->h_size ) {
313
314 hb_tmp = h->h_end->hb_prv ; /* Temp pointer to next-to-last */
315 hisbuf_destroy( h->h_end ) ; /* Destroy last entry */
316 h->h_end = hb_tmp ; /* Make end be the next-to-last */
317
318 if( h->h_end != NULL ) /* If there is an end */
319 h->h_end->hb_nxt = NULL ; /* Then it doesn't point anywhere */
320 else
321 h->h_start = NULL ; /* Otherwise list is empty */
322
323 --h->h_nitems ; /* One fewer... */
324 }
325
326 /*
327 * Now that we have the space, stick the new hisbuf_t into
328 * our history list.
329 */
330 if( h->h_start == NULL ) {
331 h->h_start = hisbuf ;
332 h->h_end = hisbuf ;
333 } else {
334 h->h_start->hb_prv = hisbuf ;
335 hisbuf->hb_nxt = h->h_start ;
336 h->h_start = hisbuf ;
337 }
338 ++h->h_nitems ;
339 hist_auto_save ( h ) ;
340
341 sqsh_set_error( SQSH_E_NONE, NULL ) ;
342 return True ;
343 }
344
history_find(h,idx,buf)345 int history_find( h, idx, buf )
346 history_t *h ;
347 int idx ;
348 char **buf ;
349 {
350 hisbuf_t *hb ;
351
352 /*-- Check parameters not checked by hisbuf_get() --*/
353 if( buf == NULL ) {
354 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
355 return False ;
356 }
357
358 /*-- Retrieve the appropriate history entry */
359 if( (hb = hisbuf_get( h, idx )) == NULL )
360 return False ;
361
362 *buf = hb->hb_buf ;
363
364 sqsh_set_error( SQSH_E_NONE, NULL ) ;
365 return True ;
366 }
367
368 /*
369 * history_del():
370 *
371 * Deletes entry idx from history h.
372 */
history_del(h,idx)373 int history_del( h, idx )
374 history_t *h ;
375 int idx ;
376 {
377 hisbuf_t *hb ;
378 register int i ; /* sqsh-2.1.6 - New variable */
379
380
381 /*-- Retrieve the appropriate history entry */
382 if( (hb = hisbuf_get( h, idx )) == NULL )
383 return False ;
384
385 /*-- Unlink the node --*/
386 if( hb->hb_prv != NULL )
387 hb->hb_prv->hb_nxt = hb->hb_nxt ;
388 else
389 h->h_start = hb->hb_nxt ;
390
391 if( hb->hb_nxt != NULL )
392 hb->hb_nxt->hb_prv = hb->hb_prv ;
393 else
394 h->h_end = hb->hb_prv ;
395
396 hisbuf_destroy( hb ) ;
397
398 /*
399 * sqsh-2.1.6 feature - Adjust h_nitems, h_next_nbr and renumber the list.
400 */
401 switch ( --h->h_nitems )
402 {
403 case 0 :
404 h->h_next_nbr = 1;
405 break;
406
407 case 1 :
408 h->h_start->hb_nbr = 1;
409 h->h_next_nbr = 2;
410 break;
411
412 default :
413 for (hb = h->h_start, i = hb->hb_nbr, h->h_next_nbr = i + 1;
414 hb != NULL; hb->hb_nbr = i--, hb = hb->hb_nxt);
415 break;
416 }
417 h->h_change = HISTSAVE_FORCE;
418 hist_auto_save ( h );
419
420 return True ;
421 }
422
423 /*
424 * sqsh-2.1.7 - history_range_del():
425 *
426 * Deletes entries idx_start through idx_end from history h.
427 */
history_range_del(h,idx_start,idx_end)428 int history_range_del( h, idx_start, idx_end )
429 history_t *h ;
430 int idx_start ;
431 int idx_end ;
432 {
433 hisbuf_t *hb ;
434 register int i ;
435
436 for ( i = idx_start; i <= idx_end; i++ ) {
437 /*-- Retrieve the appropriate history entry */
438 if( (hb = hisbuf_get( h, i )) == NULL )
439 continue ;
440
441 /*-- Unlink the node --*/
442 if( hb->hb_prv != NULL )
443 hb->hb_prv->hb_nxt = hb->hb_nxt ;
444 else
445 h->h_start = hb->hb_nxt ;
446
447 if( hb->hb_nxt != NULL )
448 hb->hb_nxt->hb_prv = hb->hb_prv ;
449 else
450 h->h_end = hb->hb_prv ;
451
452 hisbuf_destroy( hb ) ;
453 h->h_nitems--;
454 }
455
456 /*
457 * Adjust h_next_nbr and renumber the list.
458 */
459 switch ( h->h_nitems )
460 {
461 case 0 :
462 h->h_next_nbr = 1;
463 break;
464
465 case 1 :
466 h->h_start->hb_nbr = 1;
467 h->h_next_nbr = 2;
468 break;
469
470 default :
471 for (hb = h->h_start, i = hb->hb_nbr, h->h_next_nbr = i + 1;
472 hb != NULL; hb->hb_nbr = i--, hb = hb->hb_nxt);
473 break;
474 }
475 h->h_change = HISTSAVE_FORCE;
476 hist_auto_save ( h );
477
478 return True ;
479 }
480
history_clear(h)481 int history_clear( h )
482 history_t *h ;
483 {
484 hisbuf_t *hb, *hb_nxt ;
485
486 /*-- Check parameters --*/
487 if( h == NULL ) {
488 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
489 return False ;
490 }
491
492 hb = h->h_start ;
493 while( hb != NULL ) {
494 hb_nxt = hb->hb_nxt ;
495 hisbuf_destroy( hb ) ;
496 hb = hb_nxt ;
497 }
498
499 h->h_start = NULL ;
500 h->h_end = NULL ;
501 h->h_nitems = 0 ;
502 h->h_change = HISTSAVE_INIT ;
503
504 return True ;
505 }
506
507 /*
508 * history_save():
509 *
510 * Saves the current command history to file named save_file,
511 * returns True upon success, False upon failure.
512 */
history_save(h,save_file)513 int history_save( h, save_file )
514 history_t *h ;
515 char *save_file ;
516 {
517 hisbuf_t *hb ;
518 FILE *fptr ;
519 mode_t saved_mask;
520 char *histmerge;
521 history_t *x;
522
523
524 /*-- Check the arguments --*/
525 if( h == NULL || save_file == NULL ) {
526 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
527 return False ;
528 }
529
530 /*
531 * sqsh-2.2.0 - Merge the history file with the buffers in memory.
532 * If h_change is set to HISTSAVE_FORCE then we do not want to
533 * merge, because we may have just removed a bunch of buffers
534 * for example.
535 */
536 env_get (g_env, "histmerge", &histmerge);
537 if ( histmerge != NULL && *histmerge == '1' &&
538 h->h_change != HISTSAVE_FORCE)
539 {
540 x = history_create (history_get_size(h));
541 if (history_load (x, save_file) == True)
542 history_merge (h, x);
543 history_destroy (x);
544 }
545
546 /*-- Open the file to save to --*/
547 /* fix for 1105398 */
548 saved_mask = umask( (mode_t) 0066);
549 if( (fptr = fopen( save_file, "w" )) == NULL ) {
550 sqsh_set_error( errno, "%s: %s", save_file, strerror( errno ) ) ;
551 umask(saved_mask);
552 return False ;
553 }
554 umask(saved_mask);
555
556 /*
557 * Traverse the list of history buffers from oldest to newest, this
558 * way we can perform a regular history_append() on each buffer while
559 * loading them in and they will be in the correct order.
560 */
561 for( hb = h->h_end; hb != NULL; hb = hb->hb_prv ) {
562 /*
563 * Since buffer can contain just about anything we need to
564 * provide some sort of separator.
565 * sqsh-2.1.7 - Also store time_t of last buffer access and usage count.
566 */
567 fprintf( fptr, "--> History Entry <--\n" ) ;
568 fprintf( fptr, "--> History Info (%d:%d) <--\n", (int) hb->hb_dttm, hb->hb_count ) ;
569 fputs( hb->hb_buf, fptr ) ;
570
571 }
572
573 fclose( fptr ) ;
574 h->h_change = HISTSAVE_INIT;
575
576 sqsh_set_error( SQSH_E_NONE, NULL ) ;
577 return True ;
578 }
579
580 /*
581 * history_load():
582 *
583 * Load history contained in load file into h. If h is too small to
584 * hold all of the entries in load_file, then the oldest entries are
585 * rolled off as necessary.
586 */
history_load(h,load_file)587 int history_load( h, load_file )
588 history_t *h ;
589 char *load_file ;
590 {
591 FILE *fptr ;
592 char str[1024] ;
593 varbuf_t *history_buf ;
594 /*
595 * sqsh-2.1.7 - Keep track of buffer usage count and last access datetime.
596 */
597 int dttm = 0;
598 int count = 0;
599
600 /*-- Check the arguments --*/
601 if( h == NULL || load_file == NULL ) {
602 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
603 return False ;
604 }
605
606 /*-- Attempt to open the load file --*/
607 if( (fptr = fopen( load_file, "r" )) == NULL ) {
608 sqsh_set_error( errno, "%s: %s", load_file, strerror( errno ) ) ;
609 return False ;
610 }
611
612 /*
613 * Create a varbuf structure to hold the current buffer being
614 * loaded, this buffer will be destroyed before we return
615 * to the caller.
616 */
617 if( (history_buf = varbuf_create(1024)) == NULL ) {
618 sqsh_set_error( sqsh_get_error(), "varbuf_create: %s",
619 sqsh_get_errstr() ) ;
620 return False ;
621 }
622
623 /*
624 * sqsh-2.1.7 - Value HISTSAVE_LOAD indicates we are performing
625 * a load and we do not want to save history in hist_auto_save().
626 */
627 h->h_change = HISTSAVE_LOAD;
628 while( fgets( str, sizeof(str), fptr ) != NULL ) {
629 /*
630 * If we hit the separator for history entries then we need
631 * to save the current history buffer that we are building.
632 */
633 if( strcmp( str, "--> History Entry <--\n" ) == 0 ) {
634
635 /*
636 * However, since the first line of the load file contains
637 * the separator we don't wan't to build and empty buffer.
638 */
639 if( varbuf_getlen( history_buf ) > 0 ) {
640 /*
641 * Create a new entry.
642 */
643 if( history_append( h, varbuf_getstr( history_buf ) ) == False ) {
644 fclose( fptr ) ;
645 varbuf_destroy( history_buf ) ;
646 sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
647 h->h_change = HISTSAVE_INIT;
648 return False ;
649 }
650 /*
651 * sqsh-2.1.7 - Adjust dttm and usage count for the current buffer.
652 */
653 h->h_start->hb_dttm = (time_t) dttm;
654 h->h_start->hb_count += count;
655 dttm = 0;
656 count = 0;
657 }
658
659 varbuf_clear( history_buf ) ;
660 } else if ( strncmp( str, "--> History Info (", 18 ) == 0) {
661 /*
662 * sqsh-2.1.7 - The history file may contain a new entry to store
663 * last access date/time and buffer usage count.
664 * During buffer allocation the initial count is set to 1. We do
665 * not want to overwrite the value in the buffer, just add another
666 * occurence to keep uniqueness count correct. Therefor substract
667 * one from the value read from the history file.
668 */
669 char *p;
670
671 p = strchr (str, ')');
672 *p = '\0';
673 p = strchr (str, ':');
674 count = atoi (p+1)-1;
675 *p = '\0';
676 dttm = atoi (str+18);
677 } else {
678 /*
679 * Since this isn't an entry separator, it is a line that
680 * needs to be added to the current buffer.
681 */
682 if( varbuf_strcat( history_buf, str ) == -1 ) {
683 fclose( fptr ) ;
684 varbuf_destroy( history_buf ) ;
685 sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
686 h->h_change = HISTSAVE_INIT;
687 return False ;
688 }
689 }
690 }
691
692 fclose( fptr ) ;
693
694 /*
695 * If there is anything left in the history buffer then we need
696 * to add it to the history as well.
697 */
698 if( varbuf_getlen( history_buf ) > 0 ) {
699
700 if( history_append( h, varbuf_getstr( history_buf ) ) == False ) {
701 varbuf_destroy( history_buf ) ;
702 sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
703 h->h_change = HISTSAVE_INIT;
704 return False ;
705 }
706 /*
707 * sqsh-2.1.7 - Adjust dttm and usage count for the current buffer.
708 */
709 h->h_start->hb_dttm = (time_t) dttm;
710 h->h_start->hb_count += count;
711 }
712
713 varbuf_destroy( history_buf ) ;
714 sqsh_set_error( SQSH_E_NONE, NULL ) ;
715 h->h_change = HISTSAVE_INIT;
716 return True ;
717 }
718
history_destroy(h)719 int history_destroy( h )
720 history_t *h ;
721 {
722 if( history_clear( h ) == False )
723 return False ;
724
725 free( h ) ;
726 return True ;
727 }
728
729 /*
730 * hisbuf_get():
731 *
732 * Retrieves history entry idx from h If idx is HISTORY_HEAD then the
733 * first (most recent) entry into the history is returned and if idx
734 * is HISTORY_TAIL the last (oldest) entry is returned. Upon success
735 * True is returned, otherwise False.
736 */
hisbuf_get(h,idx)737 static hisbuf_t* hisbuf_get( h, idx )
738 history_t *h ;
739 int idx ;
740 {
741 hisbuf_t *hb ;
742
743 /*-- Check parameters --*/
744 if( h == NULL || (idx < 0 && idx != HISTORY_HEAD && idx != HISTORY_TAIL) ) {
745 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
746 return NULL ;
747 }
748
749 /*-- The most recent entry? --*/
750 if( idx == HISTORY_HEAD ) {
751 if( h->h_start == NULL ) {
752 sqsh_set_error( SQSH_E_EXIST, "History is empty" ) ;
753 return NULL ;
754 }
755
756 return h->h_start ;
757 }
758
759 /*-- The most oldest entry? --*/
760 if( idx == HISTORY_TAIL ) {
761 if( h->h_end == NULL ) {
762 sqsh_set_error( SQSH_E_EXIST, "History is empty" ) ;
763 return NULL ;
764 }
765
766 return h->h_end ;
767 }
768
769 /*
770 * Perform a rather inefficient, linear, search through
771 * our history list looking for idx.
772 */
773 for( hb = h->h_start; hb != NULL && hb->hb_nbr != idx;
774 hb = hb->hb_nxt ) ;
775
776 if( hb == NULL ) {
777 sqsh_set_error( SQSH_E_EXIST, "Invalid history number %d", idx ) ;
778 return NULL ;
779 }
780
781 return hb ;
782 }
783
784
hisbuf_create(h,buf,len)785 static hisbuf_t* hisbuf_create( h, buf, len )
786 history_t *h ;
787 char *buf ;
788 int len ;
789 {
790 hisbuf_t *hisbuf ;
791
792 /*-- Allocate --*/
793 if( (hisbuf = (hisbuf_t*)malloc(sizeof(hisbuf_t))) == NULL )
794 return NULL ;
795
796 /*-- Initialize --*/
797 hisbuf->hb_len = len ;
798 hisbuf->hb_nbr = h->h_next_nbr++ ;
799 hisbuf->hb_chksum = 0 ; /* sqsh-2.1.6 feature */
800 hisbuf->hb_dttm = 0 ; /* sqsh-2.1.7 feature */
801 hisbuf->hb_count = 0 ; /* sqsh-2.1.7 feature */
802 hisbuf->hb_nxt = NULL ;
803 hisbuf->hb_prv = NULL ;
804
805 /*-- Copy the buffer --*/
806 if( (hisbuf->hb_buf = sqsh_strdup( buf )) == NULL ) {
807 free( hisbuf ) ;
808 return NULL ;
809 }
810
811 return hisbuf ;
812 }
813
hisbuf_destroy(hb)814 static int hisbuf_destroy( hb )
815 hisbuf_t *hb ;
816 {
817 if( hb != NULL ) {
818 if( hb->hb_buf != NULL )
819 free( hb->hb_buf ) ;
820 free( hb ) ;
821 }
822 return True ;
823 }
824
825
826 /*
827 * sqsh-2.1.6 feature - Function adler32
828 *
829 * Calculate a simple checksum for the buffer pointed to by data over
830 * a length of len bytes, using the Adler algorithm.
831 */
832 #define MOD_ADLER 65521
833
adler32(data,len)834 static unsigned long adler32 (data, len)
835 char *data;
836 int len;
837 {
838 unsigned long a = 1, b = 0;
839
840 while (len-- > 0)
841 {
842 a = (a + *data++) % MOD_ADLER;
843 b = (b + a) % MOD_ADLER;
844 }
845
846 return (b << 16) | a;
847 }
848
849
850 /*
851 * sqsh-2.1.7 feature - Function chk_buf_ifs
852 *
853 * Check if the buffer only contains IFS characters.
854 * Return True if this is the case, else return False.
855 */
chk_buf_ifs(data,len)856 static int chk_buf_ifs (data, len)
857 char *data;
858 int len;
859 {
860 char *ifs = "\f\n\r\t\v ";
861 int i;
862
863 for (i = 0; i < len && strchr(ifs, data[i]) != NULL; ++i);
864 if ( i == len )
865 return True;
866 else
867 return False;
868 }
869
870
871 /*
872 * sqsh-2.1.7 feature - Autosave history after more than x
873 * configured changes (Variable hist_auto_save).
874 */
hist_auto_save(h)875 static void hist_auto_save ( h )
876 history_t *h;
877 {
878 varbuf_t *exp_buf;
879 char *history;
880 char *histsave;
881 char *hist_auto_save;
882
883
884 /*
885 * If we are currently loading the history file, then we do
886 * not want to start autosaving the history, so back off.
887 */
888 if (h->h_change == HISTSAVE_LOAD)
889 return;
890
891 /*
892 * Only try to autosave the history if we have histsave on
893 * and a history file and a hist_auto_save value configured.
894 */
895 env_get (g_env, "history", &history);
896 env_get (g_env, "hist_auto_save", &hist_auto_save);
897 env_get (g_env, "histsave", &histsave);
898 if ( history == NULL || *history == '\0' ||
899 hist_auto_save == NULL || *hist_auto_save == '0' ||
900 histsave == NULL || *histsave == '0')
901 return;
902
903 if ( h->h_change != HISTSAVE_FORCE &&
904 ++h->h_change < atoi(hist_auto_save) )
905 return;
906
907 /*
908 * Expand the history file and save the buffers to the history.
909 * history_save() will also reset the h_change counter.
910 */
911 exp_buf = varbuf_create( 256 );
912 if (exp_buf != NULL)
913 {
914 if ( sqsh_expand( history, exp_buf, 0 ) == True )
915 {
916 history_save( h, varbuf_getstr(exp_buf) );
917 DBG(sqsh_debug(DEBUG_HISTORY, "sqsh_history: History automatically saved to %s\n",
918 varbuf_getstr(exp_buf));)
919 }
920 varbuf_destroy( exp_buf );
921 }
922
923 return;
924 }
925
926
927 /*
928 * history_merge():
929 *
930 * sqsh-2.2.0 - Merge the history list of x into the history list of h.
931 */
history_merge(h,x)932 static int history_merge (h, x)
933 history_t *h;
934 history_t *x;
935 {
936 hisbuf_t *hbh;
937 hisbuf_t *hbx;
938 hisbuf_t *hbt;
939 int i;
940 char *histunique;
941 #if defined (DEBUG)
942 char hdrinfo[64];
943 char *line, *nl;
944 #endif
945
946
947 if (h == NULL || x == NULL)
948 {
949 sqsh_set_error( SQSH_E_BADPARAM, NULL ) ;
950 return False;
951 }
952
953 /*
954 * if histunique=On then remove the oldest double entries
955 * (based on hb_chksum) from h or x, otherwise only remove
956 * double entries from x, regardless the values of hb_dttm.
957 * Kind of nested loop join, h is outer table, x is inner table.
958 */
959 env_get (g_env, "histunique", &histunique);
960 hbh = h->h_start;
961 hbx = x->h_start;
962 while (hbh != NULL && hbx != NULL)
963 {
964 if (hbh->hb_chksum == hbx->hb_chksum)
965 {
966 if (hbx->hb_dttm > hbh->hb_dttm && *histunique == '1')
967 {
968 hbt = hbh->hb_nxt;
969 if (hbh->hb_prv != NULL)
970 hbh->hb_prv->hb_nxt = hbh->hb_nxt;
971 else
972 h->h_start = hbh->hb_nxt;
973 if (hbh->hb_nxt != NULL)
974 hbh->hb_nxt->hb_prv = hbh->hb_prv;
975 else
976 h->h_end = hbh->hb_prv;
977 hisbuf_destroy (hbh);
978 h->h_nitems--;
979 hbh = hbt;
980 hbx = x->h_start;
981 }
982 else
983 {
984 hbt = hbx->hb_nxt;
985 if (hbx->hb_prv != NULL)
986 hbx->hb_prv->hb_nxt = hbx->hb_nxt;
987 else
988 x->h_start = hbx->hb_nxt;
989 if (hbx->hb_nxt != NULL)
990 hbx->hb_nxt->hb_prv = hbx->hb_prv;
991 else
992 x->h_end = hbx->hb_prv;
993 hisbuf_destroy (hbx);
994 x->h_nitems--;
995 if ((hbx = hbt) == NULL)
996 {
997 hbh = hbh->hb_nxt;
998 hbx = x->h_start;
999 }
1000 }
1001 }
1002 else
1003 {
1004 if ((hbx = hbx->hb_nxt) == NULL)
1005 {
1006 hbh = hbh->hb_nxt;
1007 hbx = x->h_start;
1008 }
1009 }
1010 }
1011
1012 #if defined (DEBUG)
1013 if ( sqsh_debug_show (DEBUG_HISTORY) )
1014 {
1015 fprintf (stdout, "history_merge: Available entries in original list %d\n", h->h_nitems);
1016 fprintf (stdout, "history_merge: Available entries in merge list %d\n", x->h_nitems);
1017 for (hbx = x->h_end; hbx != NULL; hbx = hbx->hb_prv)
1018 {
1019 line = hbx->hb_buf;
1020 while ((nl = strchr (line, '\n' )) != NULL)
1021 {
1022 if (line == hbx->hb_buf) {
1023 sprintf (hdrinfo, "(%d) ", hbx->hb_nbr);
1024 fprintf (stdout, "%s%*.*s\n", hdrinfo, (int) (nl - line), (int) (nl - line), line);
1025 } else {
1026 fprintf (stdout, "%*s%*.*s\n", (int) strlen (hdrinfo), " ", (int) (nl - line), (int) (nl - line), line);
1027 }
1028 line = nl + 1;
1029 }
1030 if ( *line != '\0' )
1031 fprintf (stdout, " %s\n", line);
1032 }
1033 }
1034 #endif
1035
1036 /*
1037 * The remaining entries of x should be relinked into h
1038 * order by hb_dttm.
1039 */
1040 for (hbx = x->h_start; hbx != NULL; hbx = x->h_start)
1041 {
1042 /*
1043 * Unlink the first node from x.
1044 */
1045 x->h_start = hbx->hb_nxt;
1046 if (hbx->hb_nxt != NULL)
1047 hbx->hb_nxt->hb_prv = NULL;
1048 else
1049 x->h_end = NULL;
1050 x->h_nitems--;
1051
1052 /*
1053 * Link this node into h based on value of dttm in MRU-LRU order.
1054 */
1055 for (hbh = h->h_start; hbh != NULL && hbh->hb_dttm > hbx->hb_dttm; hbh = hbh->hb_nxt);
1056
1057 if (hbh != NULL)
1058 {
1059 if (hbh->hb_prv != NULL)
1060 hbh->hb_prv->hb_nxt = hbx;
1061 else
1062 h->h_start = hbx;
1063 hbx->hb_nxt = hbh;
1064 hbx->hb_prv = hbh->hb_prv;
1065 hbh->hb_prv = hbx;
1066 }
1067 else
1068 {
1069 if (h->h_end != NULL)
1070 h->h_end->hb_nxt = hbx;
1071 else
1072 h->h_start = hbx;
1073 hbx->hb_prv = h->h_end;
1074 h->h_end = hbx;
1075 hbx->hb_nxt = NULL;
1076 }
1077 ++h->h_nitems;
1078 }
1079
1080 /*
1081 * Renumber the list.
1082 */
1083 switch (h->h_nitems)
1084 {
1085 case 0 :
1086 h->h_next_nbr = 1;
1087 break;
1088 case 1 :
1089 h->h_start->hb_nbr = 1;
1090 h->h_next_nbr = 2;
1091 break;
1092 default :
1093 for (hbh = h->h_start, i = h->h_nitems, h->h_next_nbr = i + 1;
1094 hbh != NULL; hbh->hb_nbr = i--, hbh = hbh->hb_nxt);
1095 break;
1096 }
1097
1098 /*
1099 * If there are more entries than the allowed size of the list then
1100 * unlink the oldest entries from the list and destroy them.
1101 */
1102 while (h->h_size < h->h_nitems)
1103 {
1104 hbh = h->h_end;
1105 if (hbh->hb_prv != NULL)
1106 {
1107 h->h_end = hbh->hb_prv;
1108 h->h_end->hb_nxt = NULL;
1109 }
1110 else
1111 h->h_end = h->h_start = NULL;
1112 hisbuf_destroy (hbh);
1113 --h->h_nitems;
1114 }
1115
1116 return True;
1117 }
1118
1119