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