1 /*
2  * sqsh_readline.c - Retrieve a line of input from user
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 <ctype.h>
27 #include <regex.h>
28 #include "sqsh_config.h"
29 #include "sqsh_env.h"
30 #include "sqsh_error.h"
31 #include "sqsh_expand.h"   /* sqsh-2.1.6 */
32 #include "sqsh_global.h"
33 #include "sqsh_init.h"
34 #include "sqsh_readline.h"
35 #include "sqsh_stdin.h"
36 #include "sqsh_varbuf.h"
37 #include "sqsh_parser/sqsh_parser.h"
38 
39 /*-- Current Version --*/
40 #if !defined(lint) && !defined(__LINT__)
41 static char RCS_Id[] = "$Id: sqsh_readline.c,v 1.14 2013/12/12 11:36:52 mwesdorp Exp $" ;
42 USE(RCS_Id)
43 #endif /* !defined(lint) */
44 
45 /*-- Prototypes needed by readline --*/
46 #if defined(USE_READLINE)
47 static char**  sqsh_completion     _ANSI_ARGS(( char*, int, int )) ;
48 static char*   sqsh_generator      _ANSI_ARGS(( char*, int )) ;
49 
50 #if defined(OLD_READLINE)
51 #define   rl_compentry_func_t   Function
52 #define   rl_completion_matches completion_matches
53 #endif
54 
55 /*
56  * sqsh-2.1.8 - Function prototypes for new feature column name completion.
57  */
58 static int sqsh_readline_addcol   _ANSI_ARGS(( char* )) ;
59 static int sqsh_readline_clearcol _ANSI_ARGS(( void  )) ;
60 static int DynColnameLoad         _ANSI_ARGS(( char*, char* )) ;
61 
62 /*
63  * sqsh-2.2.0 - Function prototypes for new feature readline_histignore.
64  */
65 static int regex_match            _ANSI_ARGS(( char*, char* )) ;
66 
67 /*
68  * If GNU Readline support is compiled in, this data structure is
69  * used to contain the active list of keywords.
70  */
71 typedef struct keyword_st {
72     char              *k_word ;          /* The actual word */
73     struct keyword_st *k_nxt ;           /* Keep 'em in a list */
74 } keyword_t ;
75 
76 /*-- Linked list of keywords --*/
77 static keyword_t     *sg_keyword_start = NULL ;
78 static keyword_t     *sg_keyword_end   = NULL ;
79 static keyword_t     *sg_colname_start = NULL ;
80 static keyword_t     *sg_colname_end   = NULL ;
81 
82 /*
83  * sqsh-2.3 - Variable sg_rl_string points to varbuf buffer
84  *            to prevent data overflows.
85 */
86 static varbuf_t      *sg_rl_string     = NULL ;
87 
88 #endif /* USE_READLINE */
89 
90 
91 /*
92  * sqsh_readline_init():
93  *
94  * This internal function is used to initialize any variables that
95  * may be required by the readline library.  It doesn't really do
96  * much if readline support isn't compiled in.
97  */
sqsh_readline_init()98 int sqsh_readline_init()
99 {
100 #if defined(USE_READLINE)
101     int   stifle_value;
102     char *readline_histsize;
103     char *readline_history;
104     /* sqsh-2.1.6 - New variables */
105     varbuf_t *exp_buf;
106 
107     /*
108      * In non-interactive mode we don't need to bother with
109      * any of the readline crap.
110      */
111     if (!sqsh_stdin_isatty())
112     {
113         return True;
114     }
115 
116     DBG(sqsh_debug( DEBUG_READLINE, "sqsh_readline_init: Initializing\n" );)
117 
118     /*
119      * Set up the limit on the size of the readline history
120      * buffer according to the value of $readline_history.
121      */
122     env_get( g_env, "readline_histsize", &readline_histsize );
123     if (readline_histsize != NULL)
124     {
125         stifle_value = atoi(readline_histsize);
126 
127         if (stifle_value > 0)
128         {
129             stifle_history( stifle_value );
130         }
131     }
132 
133     /*
134      * Read in the readline history file if necessary.
135      * sqsh-2.1.6 feature - Expand readline_history variable
136      */
137     env_get( g_env, "readline_history", &readline_history );
138     if (readline_history != NULL && *readline_history != '\0')
139     {
140         exp_buf = varbuf_create (512);
141         if (exp_buf == NULL)
142         {
143           fprintf (stderr, "sqsh: %s\n", sqsh_get_errstr());
144           sqsh_exit (255);
145         }
146         if (sqsh_expand (readline_history, exp_buf, 0) != False)
147           read_history( varbuf_getstr(exp_buf) );
148         else
149             fprintf( stderr, "sqsh: Error expanding $readline_history: %s\n", sqsh_get_errstr() );
150         varbuf_destroy (exp_buf);
151     }
152 
153     /*
154      * sqsh-2.1.8: Moved loading keyword_file from sqsh_main.c to here.
155      * If the user has requested some form of keyword completion
156      * then attempt to read the contents of the keywords file.
157     */
158     (void) sqsh_readline_load ();
159 
160     rl_readline_name                 = "sqsh" ;
161     rl_completion_entry_function     = (rl_compentry_func_t  *) sqsh_completion ;
162     rl_attempted_completion_function = (rl_completion_func_t *) sqsh_completion ;
163 
164     /*
165      * sqsh-2.1.8 - Remove '@' and '$' from the readline default list of word break
166      * characters by assigning a new list of word break characters to the variable
167      * rl_completer_word_break_characters. The @ and $ characters may be part
168      * of object/column names and would otherwise lead to problems with TAB completion
169      * when keyword_dynamic is enabled.
170      */
171     rl_completer_word_break_characters = " \t\n\"\\'`><=;|&{(";
172 
173     /*
174      * sqsh-2.3 - Initialize variable sg_rl_string.
175      */
176     if( (sg_rl_string = varbuf_create (16384)) == NULL ) {
177         sqsh_set_error( sqsh_get_error(), "sg_rl_string: %s", sqsh_get_errstr() ) ;
178         return False ;
179     }
180 
181 #endif /* USE_READLINE */
182 
183     return True ;
184 }
185 
sqsh_readline_exit()186 int sqsh_readline_exit()
187 {
188 #if defined(USE_READLINE)
189     char *readline_history;
190     /* sqsh-2.1.6 - New variables */
191     varbuf_t *exp_buf;
192 
193     /*
194      * In non-interactive mode we don't need to bother with
195      * any of the readline crap.
196      */
197     if (!sqsh_stdin_isatty())
198     {
199         return True;
200     }
201 
202     /*
203      * sqsh-2.1.6 feature - Expand readline_history variable
204     */
205     env_get( g_env, "readline_history", &readline_history );
206     if (readline_history != NULL && *readline_history != '\0')
207     {
208         exp_buf = varbuf_create (512);
209         if (exp_buf == NULL)
210         {
211           fprintf (stderr, "sqsh: %s\n", sqsh_get_errstr());
212           sqsh_exit (255);
213         }
214         if (sqsh_expand (readline_history, exp_buf, 0) != False)
215           write_history( varbuf_getstr(exp_buf) );
216         else
217             fprintf( stderr, "sqsh: Error expanding $readline_history: %s\n",
218                 sqsh_get_errstr() );
219         varbuf_destroy (exp_buf);
220     }
221 
222     /*
223      * sqsh-2.3 - Cleanup sg_rl_string.
224      */
225     if( sg_rl_string != NULL) {
226         varbuf_destroy ( sg_rl_string );
227         sg_rl_string = NULL;
228     }
229 
230 #endif /* USE_READLINE */
231 
232     return True;
233 }
234 
235 /*
236  * sqsh_readline():
237  *
238  * Reads a line of input from the user, providing prompt (if it is
239  * non-null.
240  */
sqsh_readline(prompt)241 char* sqsh_readline( prompt )
242     char *prompt;
243 {
244     static char str[16384];
245 
246 #if defined(USE_READLINE)
247     char *line;
248     char *cp;
249     /* sqsh-2.1.6 - New variable */
250     char *ignoreeof = NULL;
251     /* sqsh-2.2.0 - New variables */
252     char *readline_histignore;
253     char *p1, *p2;
254     int  match;
255 
256 
257     sqsh_set_error( SQSH_E_NONE, NULL );
258     if (prompt == NULL)
259     {
260         return ( sqsh_stdin_fgets( str, sizeof( str ) ) );
261     }
262 
263     /* sqsh-2.1.6 feature - Expand color prompt */
264     prompt = expand_color_prompt (prompt, True);
265 
266     /*
267      * sqsh-2.1.6 feature - Obtain environment variable ignoreeof. This will
268      * indicate if we have to ignore ^D yes or no.
269     */
270     env_get( g_env, "ignoreeof", &ignoreeof );
271     if (ignoreeof == NULL || *ignoreeof == '0')
272     {
273         /*
274          *               Standard behaviour:
275          * Since we have no way of capturing any real error conditions
276          * from readline, if it returns NULL we just have to assume
277          * that we have hit EOF, and not some error condition.  From what
278          * we can tell from the readline library, there is no way to
279          * differentiate the two.
280         */
281         if ((line = readline( prompt )) == NULL)
282         {
283             return NULL;
284         }
285     }
286     else
287     {
288         /*
289         ** If ignoreeof is defined True, continue with readline
290         ** as long as NULL is returned (accidentally C-d pressed).
291         */
292         while ((line = readline( prompt )) == NULL) {
293             fprintf (stdout, "\nUse \"exit\" or \"quit\" to leave the sqsh shell.\n");
294             fflush  (stdout);
295         }
296     }
297 
298     /*
299      * Attempt to find out if there is anything in this line except
300      * for white-space.  If there isn't then don't save it to the
301      * history.
302      */
303     for (cp = line; *cp != '\0' && isspace((int)*cp); ++cp);
304 
305     if (*cp != '\0')
306     {
307         /*
308          * sqsh-2.2.0 - If $readline_histignore is set, then do not add the line to
309          * the readline history if it matches the provided regular expression or
310          * equals one of the colon separated list of keywords.
311          * if $readline_histignore starts with RE: then it is considered a regular
312          * expression that is evaluated with function regex_match.
313          * Rationale is to filter out the 'go', 'lo', 'mo', quit, etc.
314          * statements from the readline history.
315         */
316         match = False;
317         env_get( g_env, "readline_histignore", &readline_histignore );
318         if (readline_histignore != NULL && *readline_histignore != '\0')
319         {
320             /*
321              * Duplicate the variable to a work buffer so we
322              * can modify it. What we want to do is strip of begin
323              * and end double quotes, if they exists.
324             */
325             readline_histignore = sqsh_strdup (readline_histignore);
326             cp = readline_histignore;
327             if ( cp != NULL && *cp == '"' && *(cp+strlen(cp)-1) == '"' )
328             {
329                 *(cp+strlen(cp)-1) = '\0';
330                 cp = readline_histignore + 1;
331             }
332 
333             if ( cp != NULL && strncmp (cp, "RE:", 3) == 0 )
334             {
335                 /*
336                  * readline_histignore contains an extended regular expression
337                  * if the string starts with RE:
338                 */
339                 cp = cp + 3;
340                 if (regex_match (cp, line) == 0)
341                     match = True;
342             }
343             else
344             {
345                 for (p1 = cp; p1 != NULL && match == False; p1 = p2)
346                 {
347                     if ( (p2 = strchr(p1, ':')) != NULL )
348                         *p2++ = '\0';
349                     if ( strcmp (line, p1) == 0 )
350                         match = True;
351                 }
352             }
353             if ( readline_histignore != NULL )
354                 free (readline_histignore);
355         }
356         if ( match == False )
357             add_history( line );
358     }
359 
360     /*
361      * Since readline mallocs line every time and doesn't append a
362      * newline, we make a copy of the current line, adding the newline
363      * and free the readline copy.
364      * sqsh-2.3 - Use a flexible buffer instead of an array to store
365      * the line. This is to prevent data overflows.
366      */
367     varbuf_strcpy ( sg_rl_string, line );
368     varbuf_strcat ( sg_rl_string, "\n" );
369     free( line );
370     return (varbuf_getstr( sg_rl_string ));
371 
372 #else /* USE_READLINE */
373 
374     /*
375      * If the user supplied a prompt, then print it out. Otherwise
376      * they get no stinking prompt.
377      */
378     if (prompt != NULL)
379     {
380         fputs( prompt, stdout );
381         fflush( stdout );
382     }
383 
384     /*
385      * Keep trying to read a line until we hit feof(stdin), or while
386      * we are getting interrupted by signal handlers.
387      */
388     return ( sqsh_stdin_fgets( str, sizeof( str )));
389 
390 #endif
391 
392 }
393 
394 /*
395  * sqsh_readline_load ():
396  *
397  * sqsh-2.1.8: Check and expand keyword_file variable and call sqsh_readline_read
398  * to suck in the keyword list.
399  */
sqsh_readline_load(void)400 int sqsh_readline_load ( void )
401 {
402 #if !defined(USE_READLINE)
403     sqsh_set_error(SQSH_E_EXIST, "sqsh compiled without readline support" ) ;
404     return False ;
405 #else
406     char          *keyword_file;
407     varbuf_t      *exp_buf;
408     int           ret = False ;
409 
410     env_get( g_env, "keyword_file", &keyword_file );
411     if ( keyword_file != NULL && *keyword_file != '\0')
412     {
413         exp_buf = varbuf_create( 512 );
414         if (exp_buf == NULL)
415         {
416             fprintf( stderr, "sqsh: %s\n", sqsh_get_errstr() );
417             sqsh_exit( 255 );
418         }
419         if (sqsh_expand( keyword_file, exp_buf, 0 ) != False)
420             ret = sqsh_readline_read( varbuf_getstr( exp_buf) );
421         else
422             fprintf( stderr, "sqsh: Error expanding $keyword_file: %s\n",
423                    sqsh_get_errstr() );
424         varbuf_destroy( exp_buf );
425     }
426     return ret;
427 #endif
428 }
429 
430 /*
431  * sqsh_readline_read():
432  *
433  * Reads the contents of filename into the readline keyword completion
434  * list.
435  */
sqsh_readline_read(filename)436 int sqsh_readline_read( filename )
437     char *filename ;
438 {
439 #if !defined(USE_READLINE)
440     sqsh_set_error(SQSH_E_EXIST, "sqsh compiled without readline support" ) ;
441     return False ;
442 #else
443     FILE      *infile ;
444     char       keyword[512] ;
445     int        idx ;
446     int        ch ;
447 
448     DBG(sqsh_debug(DEBUG_READLINE,"sqsh_readline_read: Reading %s\n",filename);)
449 
450     if( (infile = fopen( filename, "r")) == NULL ) {
451 
452         DBG(sqsh_debug( DEBUG_READLINE, "sqsh_readline_read: %s: %s\n", filename,
453                        strerror( errno ) ) ;)
454 
455         sqsh_set_error( errno, "%s: %s", filename, strerror( errno ) ) ;
456         return False ;
457     }
458 
459     while( (ch = sqsh_getc(infile)) != EOF ) {
460 
461         /*-- Skip whitespace --*/
462         while( ch != EOF && isspace((int)ch) )
463             ch = sqsh_getc(infile) ;
464 
465         idx = 0 ;
466         while( ch != EOF && !(isspace((int)ch)) ) {
467             keyword[idx++] = ch ;
468             ch = sqsh_getc(infile) ;
469         }
470 
471         if( idx != 0 ) {
472             keyword[idx] = '\0' ;
473 
474             if( sqsh_readline_add( keyword ) == False ) {
475                 fclose( infile ) ;
476                 return False ;
477             }
478         }
479     }
480     fclose(infile) ;
481     return True ;
482 #endif
483 }
484 
485 /*
486  * sqsh_readline_add()
487  *
488  * Adds a new keyword to the readline keyword completion list.
489  */
sqsh_readline_add(keyword)490 int sqsh_readline_add( keyword )
491     char *keyword ;
492 {
493 #if !defined(USE_READLINE)
494     sqsh_set_error(SQSH_E_EXIST, "sqsh compiled without readline support" ) ;
495     return False ;
496 #else
497     keyword_t *k ;
498 
499     /*-- Allocate a new structure --*/
500     if( (k = (keyword_t*)malloc(sizeof(keyword_t))) == NULL ) {
501         sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
502         return False ;
503     }
504 
505     /*-- Create the keyword --*/
506     if( (k->k_word = sqsh_strdup( keyword )) == NULL ) {
507         free( k ) ;
508         sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
509         return False ;
510     }
511     k->k_nxt = NULL ;
512 
513     DBG(sqsh_debug( DEBUG_READLINE, "sqsh_readline_add: Adding '%s'\n",
514                     k->k_word ) ;)
515 
516     if( sg_keyword_end == NULL )
517         sg_keyword_start = sg_keyword_end = k ;
518     else {
519         sg_keyword_end->k_nxt = k ;
520         sg_keyword_end = k ;
521     }
522 
523     return True ;
524 #endif
525 }
526 
527 /*
528  * sqsh_readline_clear():
529  *
530  * Clears the contents of the readline keyword completion buffer.
531  */
sqsh_readline_clear()532 int sqsh_readline_clear()
533 {
534 #if !defined(USE_READLINE)
535     sqsh_set_error(SQSH_E_EXIST, "sqsh compiled without readline support" ) ;
536     return False ;
537 #else
538     keyword_t  *k, *n ;
539 
540     k = sg_keyword_start ;
541     while( k != NULL ) {
542         n = k->k_nxt ;
543         free( k->k_word ) ;
544         free( k ) ;
545         k = n ;
546     }
547 
548     sg_keyword_start = sg_keyword_end = NULL ;
549     return True ;
550 #endif
551 }
552 
553 
554 #if defined(USE_READLINE)
555 
556 /*
557  * This giant list is the complete list of key words avialable in
558  * in Sybase System 10.  It is used by the readline completion
559  * generator function to do keyword completion. Note, this list
560  * must remain in sorted order (case insensitive).
561  *
562  * It should also be noted that I really don't need to keep all
563  * of these words in the list.  For example, 'as' and 'at' are
564  * relatively useless for completion.  Also, there are several
565  * undocumented key words in here that aren't very useful to most
566  * humans.
567  * sqsh-2.1.8 - Added all missing reserved words of ASE 15.7.
568  */
569 static char *sqsh_statements[] = {
570     "abs",
571     "acos",
572     "add",
573     "all",
574     "alter",
575     "and",
576     "any",
577     "arith_overflow",
578     "as",
579     "asc",
580     "ascii",
581     "asin",
582     "at",
583     "atan",
584     "atn2",
585     "authorization",
586     "avg",
587     "begin",
588     "between",
589     "break",
590     "browse",
591     "bulk",
592     "by",
593     "cascade",
594     "case",
595     "ceiling",
596     "char",
597     "char_convert",
598     "char_length",
599     "charindex",
600     "check",
601     "checkpoint",
602     "close",
603     "clustered",
604     "coalesce",
605     "col_length",
606     "col_name",
607     "commit",
608     "compressed",
609     "compute",
610     "confirm",
611     "connect",
612     "constraint",
613     "continue",
614     "controlrow",
615     "convert",
616     "cos",
617     "cot",
618     "count",
619     "count_big",
620     "create",
621     "current",
622     "cursor",
623     "curunreservedpgs",
624     "data_pgs",
625     "database",
626     "dateadd",
627     "datediff",
628     "datename",
629     "datepart",
630     "db_id",
631     "db_name",
632     "dbcc",
633     "deallocate",
634     "declare",
635     "decrypt",
636     "decrypt_default",
637     "default",
638     "define",
639     "degrees",
640     "delete",
641     "desc",
642     "deterministic",
643     "difference",
644     "disk",
645     "distinct",
646     "double",
647     "drop",
648     "dual_control",
649     "dummy",
650     "dump",
651     "else",
652     "encrypt",
653     "end",
654     "endtran",
655     "errlvl",
656     "errordata",
657     "errorexit",
658     "escape",
659     "except",
660     "exclusive",
661     "exec",
662     "execute",
663     "exists",
664     "exit",
665     "exp",
666     "exp_row_size",
667     "external",
668     "fetch",
669     "fillfactor",
670     "floor",
671     "for",
672     "foreign",
673     "from",
674     "getdate",
675     "goto",
676     "grant",
677     "group",
678     "having",
679     "hextoint",
680     "holdlock",
681     "host_name",
682     "identity",
683     "identity_gap",
684     "identity_insert",
685     "identity_start",
686     "if",
687     "in",
688     "index",
689     "index_col",
690     "inout",
691     "insensitive",
692     "insert",
693     "install",
694     "intersect",
695     "into",
696     "inttohex",
697     "is",
698     "isnull",
699     "isolation",
700     "jar",
701     "join",
702     "key",
703     "kill",
704     "lct_admin",
705     "level",
706     "like",
707     "lineno",
708     "load",
709     "lob_compression",
710     "lock",
711     "log",
712     "log10",
713     "lower",
714     "ltrim",
715     "materialized",
716     "max",
717     "max_rows_per_page",
718     "min",
719     "mirror",
720     "mirrorexit",
721     "modify",
722     "national",
723     "noholdlock",
724     "nonclustered",
725     "not",
726     "null",
727     "nullif",
728     "numeric_truncation",
729     "object_id",
730     "object_name",
731     "of",
732     "off",
733     "offsets",
734     "on",
735     "once",
736     "online",
737     "only",
738     "open",
739     "option",
740     "or",
741     "order",
742     "out",
743     "output",
744     "over",
745     "param",
746     "partition",
747     "patindex",
748     "perm",
749     "permanent",
750     "pi",
751     "plan",
752     "power",
753     "precision",
754     "prepare",
755     "primary",
756     "print",
757     "privileges",
758     "proc",
759     "proc_role",
760     "procedure",
761     "processexit",
762     "proxy_table",
763     "public",
764     "quiesce",
765     "radians",
766     "raiserror",
767     "rand",
768     "read",
769     "readpast",
770     "readtext",
771     "reconfigure",
772     "references",
773     "release_locks_on_close",
774     "remove",
775     "reorg",
776     "replace",
777     "replicate",
778     "replication",
779     "reserved_pgs",
780     "reservepagegap",
781     "restree",
782     "return",
783     "returns",
784     "reverse",
785     "revoke",
786     "right",
787     "role",
788     "rollback",
789     "round",
790     "rowcnt",
791     "rowcount",
792     "rows",
793     "rtrim",
794     "rule",
795     "save",
796     "schema",
797     "scroll",
798     "select",
799     "semi_sensitive",
800     "set",
801     "setuser",
802     "shared",
803     "show_role",
804     "shutdown",
805     "sign",
806     "sin",
807     "some",
808     "soundex",
809     "space",
810     "sqrt",
811     "statement",
812     "statistics",
813     "str",
814     "stringsize",
815     "stripe",
816     "stuff",
817     "substring",
818     "sum",
819     "suser_id",
820     "suser_name",
821     "syb_identity",
822     "syb_restree",
823     "syb_terminate",
824     "table",
825     "tan",
826     "temp",
827     "temporary",
828     "terminate",
829     "textsize",
830     "to",
831     "tracefile",
832     "tran",
833     "transaction",
834     "trigger",
835     "truncate",
836     "tsequal",
837     "union",
838     "unique",
839     "unpartition",
840     "update",
841     "upper",
842     "use",
843     "used_pgs",
844     "user",
845     "user_id",
846     "user_name",
847     "user_option",
848     "using",
849     "valid_name",
850     "values",
851     "varying",
852     "view",
853     "waitfor",
854     "when",
855     "where",
856     "while",
857     "with",
858     "work",
859     "writetext",
860     "xmlextract",
861     "xmlparse",
862     "xmltable",
863     "xmltest"
864 } ;
865 
866 /*
867  * sqsh_generator():
868  *
869  * This function is used by readline.  It is called repeatededly
870  * with the currently chunk of word that the user is sitting on (text)
871  * and is expected to return each possible match for that word.
872  */
sqsh_generator(text,state)873 static char* sqsh_generator( text, state )
874     char *text;
875     int   state;
876 {
877     static      int        idx = 0;
878     static      keyword_t *cur = NULL;
879     static      char      *keyword_completion;
880     int         low, high, middle;
881     int         len;
882     int         nitems;
883     int         r;
884     char        *str;
885     char        *cptr;
886     char        *word;
887     char        *keyword_dynamic;
888     char        objname[256];
889     varbuf_t   *vb;
890     char       *tn;
891 
892     len    = strlen(text);
893     nitems = sizeof(sqsh_statements) / sizeof(char*);
894 
895 
896     /*
897      * If this is the initial state, then we need to check to see if
898      * the user even wants statement completion.  If he doesn't, then
899      * don't do anything.
900      */
901     if (state == 0)
902     {
903         env_get( g_env, "keyword_completion", &keyword_completion );
904 
905         if (keyword_completion == NULL || *keyword_completion == '0')
906             return NULL;
907 
908         /*
909          * sqsh-2.1.8: In case keyword_dynamic is enabled, check if the user
910          * wants to autocomplete a column/parameter name.
911          */
912         env_get( g_env, "keyword_dynamic", &keyword_dynamic );
913         if (keyword_dynamic != NULL && *keyword_dynamic != '0')
914         {
915             /*
916              * sqsh-2.1.8: If the text string contains a dot, then assume the
917              * first part is an object name and the second part is the column
918              * name we want to autocomplete. Obtain the objname from the text
919              * and query all the column/parameter names for this object and
920              * store them in a linked list.
921              * sqsh-2.4 : Added code to generate a completion list for aliased
922              * objectnames as well. Code contributed by K.-M. Hansche.
923              */
924             for ( idx = strlen(text)-1; idx >= 0 && text[idx] != '.'; idx-- );
925             if ( idx >= 0 && text[idx] == '.' )
926             {
927                 strncpy ( objname, text, idx );
928                 objname[idx] = '\0';
929                 vb = varbuf_create ( varbuf_getlen(g_sqlbuf) + strlen(rl_line_buffer) );
930                 if (vb == NULL)
931                 {
932                     fprintf (stderr, "sqsh: %s\n", sqsh_get_errstr());
933                     sqsh_exit (255);
934                 }
935                 varbuf_strcpy ( vb, varbuf_getstr(g_sqlbuf) );
936                 varbuf_strcat ( vb, rl_line_buffer );
937 
938                 parseSql ( varbuf_getstr(vb) );
939                 tn = getTableForAlias ( objname );
940                 (void) DynColnameLoad ( (tn != NULL) ? tn : objname, objname );
941 
942                 varbuf_destroy(vb);
943                 delTableDefs();
944             }
945             else if (sg_colname_start != NULL)
946                 sqsh_readline_clearcol ();
947         }
948 
949         /*
950          * If the user has supplied their own keyword completion list
951          * then we want to ignore the built-in one.
952          */
953         if ((sg_keyword_start != NULL) || (sg_colname_start != NULL))
954         {
955             DBG(sqsh_debug(DEBUG_READLINE, "sqsh_generator: Using user list\n" );)
956 
957             /*
958              * How's this for efficiency? A nice O(n) search of our list
959              * of keywords.  Ok, so it ain't so elegant.  Sue me.
960              */
961             cur = sg_colname_start != NULL ? sg_colname_start : sg_keyword_start;
962             if (*keyword_completion == '4') /* Exact */
963             {
964                 for (; cur != NULL && strncmp( cur->k_word, text, len ) != 0;
965                     cur = cur->k_nxt );
966             }
967             else
968             {
969                 for (; cur != NULL && strncasecmp( cur->k_word, text, len ) != 0;
970                     cur = cur->k_nxt );
971             }
972 
973             /*
974              * If we failed to find anything, then give up and return
975              * NULL to the caller indicating that we are all done or
976              * have not found a match.
977              * sqsh-2.5 : No results? Then try filename completion.
978              */
979             if (cur == NULL)
980             {
981                 if (sg_colname_start != NULL)
982                     sqsh_readline_clearcol();
983                 return ( rl_filename_completion_function (text, state) );
984             }
985 
986             /*
987              * Otherwise, save a pointer to the word that matched
988              * so that we can figure out what to do next.
989              */
990             word = cur->k_word;
991         }
992         else
993         {
994             DBG(sqsh_debug(DEBUG_READLINE,
995                            "sqsh_generator: Using internal list\n" );)
996 
997             /*
998              * In the case of our built-in list we know that it is in
999              * sorted order, so we will perform a binary search of the
1000              * array to save a little time.
1001              */
1002             low    = 0;
1003             middle = 0;
1004             high   = nitems - 1;
1005             while (low <= high)
1006             {
1007                 middle = low + (high - low) / 2;
1008                 if ((r = strncasecmp( sqsh_statements[middle], text, len )) == 0)
1009                     break;
1010 
1011                 if( r < 0 )
1012                     low = middle + 1;
1013                 else
1014                     high = middle - 1;
1015             }
1016 
1017             /*
1018              * If we couldn't even find a partial match, then give up
1019              * and return NULL.
1020              * sqsh-2.5 - Try filename completion instead.
1021              */
1022             if (low > high)
1023                 return( rl_filename_completion_function (text, state) );
1024 
1025             /*
1026              * Now, we have found an entry which matches (at least partially)
1027              * the value of text.  However, we haven't necessarily found the
1028              * first on in our sorted list, so we need to back up until we
1029              * find it.
1030              */
1031             for (idx = middle;
1032                   idx > 0 && strncasecmp( sqsh_statements[idx-1], text, len ) == 0;
1033                   --idx);
1034 
1035             word = sqsh_statements[idx];
1036         }
1037 
1038     }
1039     else   /* state != 0 */
1040     {
1041 
1042         /*
1043          * If the user has a keyword list defined of their own, then
1044          * we want to traverse to the next item in the list and see
1045          * if it matches the partial text that was passed in.
1046          */
1047         if ((sg_keyword_start != NULL) || (sg_colname_start != NULL))
1048         {
1049             /*
1050              * If we are already at the end of the list then don't
1051              * bother to return anything.
1052              * sqsh-2.5 : Try filename completion instead.
1053              */
1054             if (cur == NULL)
1055             {
1056                 if (sg_colname_start != NULL)
1057                     sqsh_readline_clearcol();
1058                 return( rl_filename_completion_function (text, state) );
1059             }
1060 
1061             /*
1062              * Traverse on through the list until we find another
1063              * match or reach the end of the list.
1064              */
1065             if (*keyword_completion == '4') /* Exact */
1066             {
1067                 for (cur = cur->k_nxt;
1068                      cur != NULL && strncmp( cur->k_word, text, len ) != 0;
1069                      cur = cur->k_nxt );
1070             }
1071             else
1072             {
1073                 for (cur = cur->k_nxt;
1074                      cur != NULL && strncasecmp( cur->k_word, text, len ) != 0;
1075                      cur = cur->k_nxt );
1076             }
1077 
1078             /*
1079              * If we hit the end, then we let the caller know that
1080              * we are all done.
1081              * sqsh-2.5 : Try filename completion instead.
1082              */
1083             if (cur == NULL)
1084             {
1085                 if (sg_colname_start != NULL)
1086                     sqsh_readline_clearcol();
1087                 return( rl_filename_completion_function (text, state) );
1088             }
1089 
1090             word = cur->k_word;
1091 
1092         }
1093         else
1094         {
1095             /*
1096              * If the user doesn't have a keyword list, then we want
1097              * to move to the next item in our sqsh_statements array.
1098              */
1099             ++idx;
1100             if (idx >= nitems || strncasecmp(sqsh_statements[idx],text,len) != 0)
1101                 return( rl_filename_completion_function (text, state) );
1102             word = sqsh_statements[idx];
1103         }
1104     }
1105 
1106     /*
1107      * At this point one of the sections of code above has set the
1108      * 'word' pointer to the word that is returned to the caller.
1109      * All we have to do now is to convert the word in the manner
1110      * requested the 'keyword_completion' variable and return
1111      * it.
1112      */
1113     if ((str = (char*) malloc( (strlen(word) + 1) * sizeof(char) )) == NULL)
1114     {
1115         if (sg_colname_start != NULL)
1116             sqsh_readline_clearcol();
1117         return NULL;
1118     }
1119 
1120     switch (*keyword_completion)
1121     {
1122         case '1':        /* Lower case */
1123 
1124             for (cptr = str; *word != '\0'; ++word, ++cptr)
1125             {
1126                 *cptr = tolower( (int) *word);
1127             }
1128             *cptr = '\0';
1129             break;
1130 
1131         case '2':        /* Upper case */
1132 
1133             for (cptr = str; *word != '\0'; ++word, ++cptr)
1134             {
1135                 *cptr = toupper( (int) *word);
1136             }
1137             *cptr = '\0';
1138             break;
1139 
1140         case '3':       /* Smart */
1141 
1142             for (cptr = str; *word != '\0'; ++word, ++cptr)
1143             {
1144                 if (isupper((int)*text))
1145                 {
1146                     *cptr = toupper( (int) *word);
1147                 }
1148                 else
1149                 {
1150                     *cptr = tolower( (int) *word);
1151                 }
1152             }
1153             *cptr = '\0';
1154             break;
1155 
1156         case '4':       /* Exact */
1157         default:
1158             strcpy( str, word );
1159     }
1160 
1161     return str ;
1162 }
1163 
sqsh_completion(text,start,end)1164 static char** sqsh_completion( text, start, end )
1165     char *text ;
1166     int   start ;
1167     int   end ;
1168 {
1169     return (char **)rl_completion_matches( text, (rl_compentry_func_t*)sqsh_generator ) ;
1170 }
1171 
1172 
1173 /*
1174  * sqsh-2.1.8 - New feature: dynamic column name completion.
1175  *
1176  * The next local functions implement the dynamic column name completion feature.
1177  * To keep it simple, I just used my own copies of some of the above functions to
1178  * distinguish them from the existing functions that are being used by the keyword
1179  * completion function, instead of adapting these functions for generic use.
1180  */
1181 
1182 
1183 /*
1184  * sqsh_readline_addcol()
1185  *
1186  * sqsh-2.1.8: Add a new column name to the readline keyword completion list.
1187  */
sqsh_readline_addcol(keyword)1188 static int sqsh_readline_addcol( keyword )
1189     char *keyword ;
1190 {
1191     keyword_t *k ;
1192 
1193     /*-- Allocate a new structure --*/
1194     if( (k = (keyword_t*)malloc(sizeof(keyword_t))) == NULL ) {
1195         sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
1196         return False ;
1197     }
1198 
1199     /*-- Create the keyword --*/
1200     if( (k->k_word = sqsh_strdup( keyword )) == NULL ) {
1201         free( k ) ;
1202         sqsh_set_error( SQSH_E_NOMEM, NULL ) ;
1203         return False ;
1204     }
1205     k->k_nxt = NULL ;
1206 
1207     DBG(sqsh_debug( DEBUG_READLINE, "sqsh_readline_add: Adding '%s'\n",
1208                     k->k_word ) ;)
1209 
1210     if( sg_colname_end == NULL )
1211         sg_colname_start = sg_colname_end = k ;
1212     else {
1213         sg_colname_end->k_nxt = k ;
1214         sg_colname_end        = k ;
1215     }
1216 
1217     return True ;
1218 }
1219 
1220 
1221 /*
1222  * sqsh_readline_clearcol():
1223  *
1224  * sqsh-2.1.8: Clears the contents of the object specific column name linked list.
1225  */
sqsh_readline_clearcol()1226 static int sqsh_readline_clearcol()
1227 {
1228     keyword_t  *k, *n ;
1229 
1230     k = sg_colname_start ;
1231     while( k != NULL ) {
1232         n = k->k_nxt ;
1233         free( k->k_word ) ;
1234         free( k ) ;
1235         k = n ;
1236     }
1237 
1238     sg_colname_start = sg_colname_end = NULL ;
1239     return True ;
1240 }
1241 
1242 
1243 /*
1244  * Function: DynColnameLoad()
1245  *
1246  * sqsh-2.1.8 - Dynamically execute a query to obtain the column names of
1247  * an existing table, view or stored procedure.
1248  * sqsh-2.4   - Also take the database into account. An object may be
1249  *              specified as master.dbo.sysdatabases, or dbo.sysusages
1250  *              or syscolumns for example, the last two expected to
1251  *              exist in the current database.
1252  *
1253  */
DynColnameLoad(objname,alias)1254 static int DynColnameLoad (objname, alias)
1255     char *objname;
1256     char *alias;
1257 {
1258     CS_COMMAND *cmd;
1259     CS_DATAFMT  columns[1];
1260     CS_RETCODE  ret;
1261     CS_RETCODE  results_ret;
1262     CS_INT      result_type;
1263     CS_INT      count;
1264     CS_INT      idx;
1265     CS_INT      datalength[1];
1266     CS_SMALLINT indicator [1];
1267     CS_CHAR     name   [256];
1268     CS_CHAR     query  [768];
1269     CS_CHAR     dbname [256];
1270     char       *cptr;
1271 
1272 
1273     if (sg_colname_start != NULL)
1274     {
1275         sqsh_readline_clearcol();
1276     }
1277     if ( g_connection == NULL )
1278     {
1279         DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: g_connection is not initialized.\n"));
1280         return (CS_FAIL);
1281     }
1282     if (ct_cmd_alloc( g_connection, &cmd ) != CS_SUCCEED)
1283     {
1284         DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Call to ct_cmd_alloc failed.\n"));
1285         return (CS_FAIL);
1286     }
1287     /*
1288      * sqsh-2.4 - If the objectname contains two dots, then the first part specifies
1289      * the database name, the second part the owner name (may be empty) and the last
1290      * part the objectname itself.
1291      */
1292     if ((cptr = strchr( objname, '.' )) != NULL && strchr( cptr + 1, '.' ) != NULL)
1293     {
1294         idx = (CS_INT) (cptr - objname);
1295         strncpy (dbname, objname, idx);
1296         dbname[idx] = '\0';
1297         sprintf (query, "select \'%s.\' + name from %s..syscolumns where id=object_id(\'%s\') order by name"
1298                       ,alias, dbname, objname);
1299     }
1300     else
1301         sprintf (query, "select \'%s.\' + name from syscolumns where id=object_id(\'%s\') order by name"
1302                       ,alias, objname);
1303 
1304     if (ct_command( cmd,                /* Command Structure */
1305                     CS_LANG_CMD,        /* Command Type      */
1306                     query,              /* Query Buffer      */
1307                     CS_NULLTERM,        /* Buffer Length     */
1308                     CS_UNUSED           /* Options           */
1309                   ) != CS_SUCCEED)
1310     {
1311         ct_cmd_drop( cmd );
1312         DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Call to ct_command failed.\n"));
1313         return (CS_FAIL);
1314     }
1315     if (ct_send( cmd ) != CS_SUCCEED)
1316     {
1317         ct_cmd_drop( cmd );
1318         DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Call to ct_send failed.\n"));
1319         return (CS_FAIL);
1320     }
1321 
1322     while ((results_ret = ct_results(cmd, &result_type)) == CS_SUCCEED)
1323     {
1324         switch ((int) result_type)
1325         {
1326             case CS_ROW_RESULT:
1327                 columns[0].datatype  = CS_CHAR_TYPE;
1328                 columns[0].format    = CS_FMT_NULLTERM;
1329                 columns[0].maxlength = 255;
1330                 columns[0].count     = 1;
1331                 columns[0].locale    = NULL;
1332                 ct_bind(cmd, 1, &columns[0], name, &datalength[0], &indicator[0]);
1333 
1334                 while (ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count) == CS_SUCCEED)
1335                 {
1336                     /* Remove trailing blanks, tabs and newlines, just in case */
1337                     for ( idx = strlen(name) - 1;
1338                           idx >= 0 && (name[idx] == ' ' || name[idx] == '\t' || name[idx] == '\n');
1339                           name[idx--] = '\0');
1340 
1341                     sqsh_readline_addcol ( name );    /* Add name to linked list of colnames */
1342                 }
1343                 break;
1344 
1345             case CS_CMD_SUCCEED:
1346                 DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: No rows returned from query.\n"));
1347                 ret = CS_FAIL;
1348                 break;
1349 
1350             case CS_CMD_FAIL:
1351                 DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Error encountered during query processing.\n"));
1352                 ret = CS_FAIL;
1353                 break;
1354 
1355             case CS_CMD_DONE:
1356                 break;
1357 
1358             default:
1359                 DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Unexpected error encountered. (1)\n"));
1360                 ret = CS_FAIL;
1361                 break;
1362         }
1363     }
1364 
1365     switch ((int) results_ret)
1366     {
1367         case CS_END_RESULTS:
1368             ret = CS_SUCCEED;
1369             break;
1370 
1371         case CS_FAIL:
1372             DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Unexpected error encountered. (2)\n"));
1373             ret = CS_FAIL;
1374             break;
1375 
1376         default:
1377             DBG(sqsh_debug(DEBUG_ERROR, "DynColnameLoad: Unexpected error encountered. (3)\n"));
1378             ret = CS_FAIL;
1379             break;
1380     }
1381     ct_cmd_drop( cmd );
1382     if (ret == CS_FAIL)
1383         sqsh_readline_clearcol();
1384 
1385     return ( ret );
1386 }
1387 
1388 
1389 /*
1390  * sqsh-2.2.0 - Function regex_match.
1391  *
1392  * Match string against the extended regular expression in
1393  * pattern, treating errors as no match.
1394  *
1395  * Return 0 for match, not 0 for no match.
1396 */
regex_match(pattern,string)1397 static int regex_match (pattern, string)
1398     char *pattern;
1399     char *string;
1400 {
1401     regex_t  re;
1402     int      status;
1403 
1404 
1405     if ((status = regcomp (&re, pattern, REG_EXTENDED|REG_NOSUB|REG_ICASE) != 0))
1406         return (status);
1407     status = regexec (&re, string, (size_t) 0, NULL, 0);
1408     regfree (&re);
1409     return (status);
1410 }
1411 
1412 #endif  /* USE_READLINE */
1413