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