1 /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 Brian Bruns
3 * Copyright (C) 2006-2015 Frediano Ziglio
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
19 */
20
21 #include <config.h>
22
23 #include <freetds/time.h>
24
25 #include <stdio.h>
26 #include <assert.h>
27 #include <ctype.h>
28 #if HAVE_FORK
29 #include <sys/wait.h>
30 #endif
31 #include <signal.h>
32
33 #ifdef HAVE_READLINE
34 #include <readline/readline.h>
35 #include <readline/history.h>
36 #endif /* HAVE_READLINE */
37
38 #if HAVE_ERRNO_H
39 #include <errno.h>
40 #endif /* HAVE_ERRNO_H */
41
42 #if HAVE_STDLIB_H
43 #include <stdlib.h>
44 #endif /* HAVE_STDLIB_H */
45
46 #if HAVE_STRING_H
47 #include <string.h>
48 #endif /* HAVE_STRING_H */
49
50 #if HAVE_UNISTD_H
51 # include <unistd.h>
52 #elif defined(_WIN32)
53 # include <io.h>
54 # undef isatty
55 # define isatty(fd) _isatty(fd)
56 #endif /* HAVE_UNISTD_H */
57
58 /* HP-UX require some constants defined by limits.h */
59 #ifdef HAVE_LIMITS_H
60 #include <limits.h>
61 #endif /* HAVE_LIMITS_H */
62
63 #if defined(__hpux__) && !defined(_POSIX_PATH_MAX)
64 #define _POSIX_PATH_MAX 255
65 #endif
66
67 #ifdef HAVE_LOCALE_H
68 #include <locale.h>
69 #endif /* HAVE_LOCALE_H */
70
71 #ifdef HAVE_LANGINFO_H
72 #include <langinfo.h>
73 #endif /* HAVE_LANGINFO_H */
74
75 #ifdef HAVE_LOCALCHARSET_H
76 #include <localcharset.h>
77 #endif /* HAVE_LOCALCHARSET_H */
78
79 #include <freetds/tds.h>
80 #include <freetds/iconv.h>
81 #include <freetds/utils/string.h>
82 #include <freetds/convert.h>
83 #include <freetds/data.h>
84 #include <freetds/utils.h>
85 #include <freetds/replacements.h>
86
87 #define TDS_ISSPACE(c) isspace((unsigned char) (c))
88
89 enum
90 {
91 OPT_VERSION = 0x01,
92 OPT_TIMER = 0x02,
93 OPT_NOFOOTER = 0x04,
94 OPT_NOHEADER = 0x08,
95 OPT_QUIET = 0x10,
96 OPT_VERBOSE = 0x20,
97 OPT_INSTANCES= 0x40
98 };
99
100 static int istty = 0;
101 static int global_opt_flags = 0;
102
103 #define QUIET (global_opt_flags & OPT_QUIET)
104 #define VERBOSE (global_opt_flags & OPT_VERBOSE)
105
106 static const char *opt_col_term = "\t";
107 static const char *opt_row_term = "\n";
108 static const char *opt_default_db = NULL;
109
110 static int do_query(TDSSOCKET * tds, char *buf, int opt_flags);
111 static int get_opt_flags(char *s, int *opt_flags);
112 static void populate_login(TDSLOGIN * login, int argc, char **argv);
113 static int tsql_handle_message(const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg);
114 static int tsql_handle_error (const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg);
115 static void slurp_input_file(char *fname, char **mybuf, size_t *bufsz, size_t *buflen, int *line);
116
117 static char *
tsql_readline(char * prompt)118 tsql_readline(char *prompt)
119 {
120 size_t sz, pos;
121 char *line, *p;
122
123 #ifdef HAVE_READLINE
124 if (istty)
125 return readline(prompt);
126 #endif
127
128 sz = 1024;
129 pos = 0;
130 line = tds_new(char, sz);
131 if (!line)
132 return NULL;
133
134 if (prompt && prompt[0])
135 printf("%s", prompt);
136 for (;;) {
137 /* read a piece */
138 if (fgets(line + pos, (int)(sz - pos), stdin) == NULL) {
139 if (pos)
140 return line;
141 break;
142 }
143
144 /* got end-of-line ? */
145 p = strchr(line + pos, '\n');
146 if (p) {
147 *p = 0;
148 return line;
149 }
150
151 /* allocate space if needed */
152 pos += strlen(line + pos);
153 if (pos + 1024 >= sz) {
154 sz += 1024;
155 if (!TDS_RESIZE(line, sz))
156 break;
157 }
158 }
159 free(line);
160 return NULL;
161 }
162
163 static void
tsql_add_history(const char * s)164 tsql_add_history(const char *s)
165 {
166 #ifdef HAVE_READLINE
167 if (istty)
168 add_history(s);
169 #endif
170 }
171
172 /**
173 * Returns the version of the TDS protocol in effect for the link
174 * as a decimal integer.
175 * Typical returned values are 42, 50, 70, 80.
176 * Also fills pversion_string unless it is null.
177 * Typical pversion_string values are "4.2" and "7.0".
178 */
179 static int
tds_version(TDSCONNECTION * conn,char * pversion_string)180 tds_version(TDSCONNECTION * conn, char *pversion_string)
181 {
182 int iversion = 0;
183
184 iversion = 10 * TDS_MAJOR(conn) + TDS_MINOR(conn);
185
186 sprintf(pversion_string, "%d.%d", TDS_MAJOR(conn), TDS_MINOR(conn));
187
188 return iversion;
189 }
190
191 static int
do_query(TDSSOCKET * tds,char * buf,int opt_flags)192 do_query(TDSSOCKET * tds, char *buf, int opt_flags)
193 {
194 int rows = 0;
195 TDSRET rc;
196 int i;
197 TDSCOLUMN *col;
198 int ctype;
199 CONV_RESULT dres;
200 unsigned char *src;
201 TDS_INT srclen;
202 TDS_INT resulttype;
203 struct timeval start, stop;
204 int print_rows = 1;
205 char message[128];
206
207 rc = tds_submit_query(tds, buf);
208 if (TDS_FAILED(rc)) {
209 fprintf(stderr, "tds_submit_query() failed\n");
210 return 1;
211 }
212
213 while ((rc = tds_process_tokens(tds, &resulttype, NULL, TDS_TOKEN_RESULTS)) == TDS_SUCCESS) {
214 const int stop_mask = TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
215 if (opt_flags & OPT_TIMER) {
216 gettimeofday(&start, NULL);
217 print_rows = 0;
218 }
219 switch (resulttype) {
220 case TDS_ROWFMT_RESULT:
221 if ((!(opt_flags & OPT_NOHEADER)) && tds->current_results) {
222 for (i = 0; i < tds->current_results->num_cols; i++) {
223 if (i) fputs(opt_col_term, stdout);
224 fputs(tds_dstr_cstr(&tds->current_results->columns[i]->column_name), stdout);
225 }
226 fputs(opt_row_term, stdout);
227 }
228 break;
229 case TDS_COMPUTE_RESULT:
230 case TDS_ROW_RESULT:
231 rows = 0;
232 while ((rc = tds_process_tokens(tds, &resulttype, NULL, stop_mask)) == TDS_SUCCESS) {
233 if (resulttype != TDS_ROW_RESULT && resulttype != TDS_COMPUTE_RESULT)
234 break;
235
236 rows++;
237
238 if (!tds->current_results)
239 continue;
240
241 for (i = 0; i < tds->current_results->num_cols; i++) {
242 col = tds->current_results->columns[i];
243 if (col->column_cur_size < 0) {
244 if (print_rows) {
245 if (i) fputs(opt_col_term, stdout);
246 fputs("NULL", stdout);
247 }
248 continue;
249 }
250 ctype = tds_get_conversion_type(col->column_type, col->column_size);
251
252 src = col->column_data;
253 if (is_blob_col(col) && col->column_type != SYBVARIANT)
254 src = (unsigned char *) ((TDSBLOB *) src)->textvalue;
255 srclen = col->column_cur_size;
256
257
258 if (tds_convert(tds_get_ctx(tds), ctype, src, srclen, SYBVARCHAR, &dres) < 0)
259 continue;
260 if (print_rows) {
261 if (i) fputs(opt_col_term, stdout);
262 fputs(dres.c, stdout);
263 }
264 free(dres.c);
265 }
266 if (print_rows)
267 fputs(opt_row_term, stdout);
268
269 }
270 if (!QUIET) printf("(%d row%s affected)\n", rows, rows == 1 ? "" : "s");
271 break;
272 case TDS_STATUS_RESULT:
273 if (!QUIET)
274 printf("(return status = %d)\n", tds->ret_status);
275 break;
276 default:
277 break;
278 }
279
280 if (opt_flags & OPT_VERSION) {
281 char version[64];
282 int line = 0;
283
284 line = tds_version(tds->conn, version);
285 if (line) {
286 TDSMESSAGE msg;
287 memset(&msg, 0, sizeof(TDSMESSAGE));
288 msg.server = "tsql";
289 sprintf(message, "using TDS version %s", version);
290 msg.message = message;
291 tsql_handle_message(tds_get_ctx(tds), tds, &msg);
292 }
293 }
294 if (opt_flags & OPT_TIMER) {
295 TDSMESSAGE msg;
296 gettimeofday(&stop, NULL);
297 sprintf(message, "Total time for processing %d rows: %ld msecs\n",
298 rows, (long) ((stop.tv_sec - start.tv_sec) * 1000) + ((stop.tv_usec - start.tv_usec) / 1000));
299
300 memset(&msg, 0, sizeof(TDSMESSAGE));
301 msg.server = "tsql";
302 msg.message = message;
303 tsql_handle_message(tds_get_ctx(tds), tds, &msg);
304 }
305 }
306 return 0;
307 }
308
309 static void
tsql_print_usage(const char * progname)310 tsql_print_usage(const char *progname)
311 {
312 fprintf(stderr,
313 "Usage: %s [-a <appname>] [-S <server> | -H <hostname> -p <port>] -U <username> [-P <password>] [-I <config file>] [-o <options>] [-t delim] [-r delim] [-D database]\n"
314 " or: %s -C\n"
315 " or: %s -L -H <hostname>\n"
316 "If -C is specified just print configuration and exit.\n"
317 "If -L is specified with a host name (-H) instances found are printed.\n"
318 " -a specify application name\n"
319 " -S specify server entry in freetds.conf to connect\n"
320 " -H specify hostname to connect\n"
321 " -p specify port to connect to\n"
322 " -U specify username to use\n"
323 " -P specify password to use\n"
324 " -D specify database name to use\n"
325 " -I specify old configuration file (called interface) to use\n"
326 " -J specify character set to use\n"
327 " -v verbose mode\n"
328 "-o options:\n"
329 "\tf\tDo not print footer\n"
330 "\th\tDo not print header\n"
331 "\tt\tPrint time informations\n"
332 "\tv\tPrint TDS version\n"
333 "\tq\tQuiet\n\n"
334 "\tDelimiters can be multi-char strings appropriately escaped for your shell.\n"
335 "\tDefault column delimitor is <tab>; default row delimiter is <newline>\n",
336 progname, progname, progname);
337 }
338
339 static void
reset_getopt(void)340 reset_getopt(void)
341 {
342 #ifdef HAVE_GETOPT_OPTRESET
343 optreset = 1;
344 optind = 1;
345 #else
346 optind = 0;
347 #endif
348 }
349
350 /*
351 * The 'GO' command may be followed by options that apply to the batch.
352 * If they don't appear to be right, assume the letters "go" are part of the
353 * SQL, not a batch separator.
354 */
355 static int
get_opt_flags(char * s,int * opt_flags)356 get_opt_flags(char *s, int *opt_flags)
357 {
358 char **argv;
359 int argc;
360 int opt;
361
362 /* make sure we have enough elements */
363 assert(s && opt_flags);
364 argv = tds_new0(char*, strlen(s) + 2);
365 if (!argv)
366 return 0;
367
368 /* parse the command line and assign to argv */
369 for (argc=0; (argv[argc] = strtok(s, " ")) != NULL; argc++)
370 s = NULL;
371
372 *opt_flags = 0;
373 reset_getopt();
374 opterr = 0; /* suppress error messages */
375 while ((opt = getopt(argc, argv, "fhLqtv")) != -1) {
376 switch (opt) {
377 case 'f':
378 *opt_flags |= OPT_NOFOOTER;
379 break;
380 case 'h':
381 *opt_flags |= OPT_NOHEADER;
382 break;
383 case 't':
384 *opt_flags |= OPT_TIMER;
385 break;
386 case 'v':
387 *opt_flags |= OPT_VERSION;
388 break;
389 case 'q':
390 *opt_flags |= OPT_QUIET;
391 break;
392 default:
393 fprintf(stderr, "Warning: invalid option '%s' found: \"go\" treated as simple SQL\n", argv[optind-1]);
394 free(argv);
395 return 0;
396 }
397 }
398
399 free(argv);
400 return 1;
401 }
402
403 static int
get_default_instance_port(const char hostname[])404 get_default_instance_port(const char hostname[])
405 {
406 int port;
407 struct addrinfo *addr;
408
409 if ((addr = tds_lookup_host(hostname)) == NULL)
410 return 0;
411
412 port = tds7_get_instance_port(addr, "MSSQLSERVER");
413
414 freeaddrinfo(addr);
415
416 return port;
417 }
418
419 #if !defined(LC_ALL)
420 # define LC_ALL 0
421 #endif
422
423 static const char *
yes_no(bool value)424 yes_no(bool value)
425 {
426 return value ? "yes" : "no";
427 }
428
429 static void
populate_login(TDSLOGIN * login,int argc,char ** argv)430 populate_login(TDSLOGIN * login, int argc, char **argv)
431 {
432 const TDS_COMPILETIME_SETTINGS *settings;
433 char *hostname = NULL, *servername = NULL;
434 char *username = NULL, *password = NULL;
435 char *confile = NULL;
436 const char *appname = "TSQL";
437 int opt, port=0, use_domain_login=0;
438 char *charset = NULL;
439 char *opt_flags_str = NULL;
440
441 while ((opt = getopt(argc, argv, "a:H:S:I:J:P:U:p:Co:t:r:D:Lv")) != -1) {
442 switch (opt) {
443 case 'a':
444 appname = optarg;
445 break;
446 case 't':
447 opt_col_term = optarg;
448 break;
449 case 'r':
450 opt_row_term = optarg;
451 break;
452 case 'D':
453 opt_default_db = optarg;
454 break;
455 case 'o':
456 opt_flags_str = optarg;
457 break;
458 case 'H':
459 free(hostname);
460 hostname = strdup(optarg);
461 break;
462 case 'S':
463 free(servername);
464 servername = strdup(optarg);
465 break;
466 case 'U':
467 free(username);
468 username = strdup(optarg);
469 break;
470 case 'P':
471 free(password);
472 password = tds_getpassarg(optarg);
473 break;
474 case 'I':
475 free(confile);
476 confile = strdup(optarg);
477 break;
478 case 'J':
479 free(charset);
480 charset = strdup(optarg);
481 break;
482 case 'p':
483 port = atoi(optarg);
484 break;
485 case 'L':
486 global_opt_flags |= OPT_INSTANCES;
487 break;
488 case 'v':
489 global_opt_flags |= OPT_VERBOSE;
490 break;
491 case 'C':
492 settings = tds_get_compiletime_settings();
493 printf("%s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n%35s: %s\n",
494 "Compile-time settings (established with the \"configure\" script)",
495 "Version", settings->freetds_version,
496 "freetds.conf directory", settings->sysconfdir,
497 /* settings->last_update */
498 "MS db-lib source compatibility", yes_no(settings->msdblib),
499 "Sybase binary compatibility", yes_no(settings->sybase_compat),
500 "Thread safety", yes_no(settings->threadsafe),
501 "iconv library", yes_no(settings->libiconv),
502 "TDS version", settings->tdsver,
503 "iODBC", yes_no(settings->iodbc),
504 "unixodbc", yes_no(settings->unixodbc),
505 "SSPI \"trusted\" logins", yes_no(settings->sspi),
506 "Kerberos", yes_no(settings->kerberos),
507 "OpenSSL", yes_no(settings->openssl),
508 "GnuTLS", yes_no(settings->gnutls),
509 "MARS", yes_no(settings->mars));
510 exit(0);
511 break;
512 default:
513 tsql_print_usage(basename(argv[0]));
514 exit(1);
515 break;
516 }
517 }
518
519 if (opt_flags_str != NULL) {
520 char *minus_flags = tds_new(char, strlen(opt_flags_str) + 5);
521 if (minus_flags != NULL) {
522 strcpy(minus_flags, "go -");
523 strcat(minus_flags, opt_flags_str);
524 get_opt_flags(minus_flags, &global_opt_flags);
525 free(minus_flags);
526 }
527 }
528
529 if ((global_opt_flags & OPT_INSTANCES) && hostname) {
530 struct addrinfo *addr;
531 char *filename = getenv("TDSDUMP");
532
533 if (filename) {
534 if (asprintf(&filename, "%s.instances", filename) < 0)
535 exit(1);
536 tdsdump_open(filename);
537 free(filename);
538 }
539 if ((addr = tds_lookup_host(hostname)) != NULL) {
540 tds7_get_instance_ports(stderr, addr);
541 freeaddrinfo(addr);
542 }
543 tdsdump_close();
544 exit(0);
545 }
546
547 /* validate parameters */
548 if (!servername && !hostname) {
549 fprintf(stderr, "%s: error: Missing argument -S or -H\n", argv[0]);
550 exit(1);
551 }
552 if (hostname && !port) {
553 /*
554 * TODO: It would be convenient to have a function that looks up a reasonable port based on:
555 * - TDSPORT environment variable
556 * - config files
557 * - get_default_instance_port
558 * - TDS version
559 * in that order.
560 */
561 if (!QUIET) {
562 printf("Missing argument -p, looking for default instance ... ");
563 }
564 if (0 == (port = get_default_instance_port(hostname))) {
565 fprintf(stderr, "%s: no default port provided by host %s\n", argv[0], hostname);
566 exit(1);
567 }
568 if (!QUIET)
569 printf("found default instance, port %d\n", port);
570
571 }
572 /* A NULL username indicates a domain (trusted) login */
573 if (!username) {
574 username = tds_new0(char, 1);
575 if (!username) {
576 fprintf(stderr, "Could not allocate memory for username\n");
577 exit(1);
578 }
579 use_domain_login = 1;
580 }
581 if (!password) {
582 password = tds_new0(char, 128);
583 if (!password) {
584 fprintf(stderr, "Could not allocate memory for password\n");
585 exit(1);
586 }
587 if (!use_domain_login)
588 readpassphrase("Password: ", password, 128, RPP_ECHO_OFF);
589 }
590 if (!opt_col_term) {
591 fprintf(stderr, "%s: missing delimiter for -t (check escaping)\n", argv[0]);
592 exit(1);
593 }
594 if (!opt_row_term) {
595 fprintf(stderr, "%s: missing delimiter for -r (check escaping)\n", argv[0]);
596 exit(1);
597 }
598
599 /* all validated, let's do it */
600 if (!tds_set_user(login, username)
601 || !tds_set_app(login, appname)
602 || !tds_set_library(login, "TDS-Library")
603 || !tds_set_language(login, "us_english")
604 || !tds_set_passwd(login, password))
605 goto out_of_memory;
606 if (charset && !tds_set_client_charset(login, charset))
607 goto out_of_memory;
608
609 /* if it's a servername */
610 if (servername) {
611 if (!tds_set_server(login, servername))
612 goto out_of_memory;
613 if (confile) {
614 tds_set_interfaces_file_loc(confile);
615 }
616 /* else we specified hostname/port */
617 } else {
618 if (!tds_set_server(login, hostname))
619 goto out_of_memory;
620 tds_set_port(login, port);
621 }
622
623 memset(password, 0, strlen(password));
624
625 /* free up all the memory */
626 free(confile);
627 free(hostname);
628 free(username);
629 free(password);
630 free(servername);
631 free(charset);
632 return;
633
634 out_of_memory:
635 fprintf(stderr, "%s: out of memory\n", argv[0]);
636 exit(1);
637 }
638
639 static int
tsql_handle_message(const TDSCONTEXT * context,TDSSOCKET * tds,TDSMESSAGE * msg)640 tsql_handle_message(const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg)
641 {
642 if (msg->msgno == 0) {
643 fprintf(stderr, "%s\n", msg->message);
644 return 0;
645 }
646
647 switch (msg->msgno) {
648 case 5701: /* changed_database */
649 case 5703: /* changed_language */
650 case 20018: /* The @optional_command_line is too long */
651 if (VERBOSE)
652 fprintf(stderr, "%s\n", msg->message);
653 break;
654 default:
655 fprintf(stderr, "Msg %d (severity %d, state %d) from %s",
656 msg->msgno, msg->severity, msg->state, msg->server);
657 if (msg->proc_name && strlen(msg->proc_name))
658 fprintf(stderr, ", Procedure %s", msg->proc_name);
659 if (msg->line_number > 0)
660 fprintf(stderr, " Line %d", msg->line_number);
661 fprintf(stderr, ":\n\t\"%s\"\n", msg->message);
662 break;
663 }
664
665 return 0;
666 }
667
668 static int /* error from library, not message from server */
tsql_handle_error(const TDSCONTEXT * context,TDSSOCKET * tds,TDSMESSAGE * msg)669 tsql_handle_error(const TDSCONTEXT * context, TDSSOCKET * tds, TDSMESSAGE * msg)
670 {
671 fprintf(stderr, "Error %d (severity %d):\n\t%s\n", msg->msgno, msg->severity, msg->message);
672 if (0 != msg->oserr) {
673 fprintf(stderr, "\tOS error %d, \"%s\"\n", msg->oserr, strerror(msg->oserr));
674 }
675 return TDS_INT_CANCEL;
676 }
677
678 static void
slurp_input_file(char * fname,char ** mybuf,size_t * bufsz,size_t * buflen,int * line)679 slurp_input_file(char *fname, char **mybuf, size_t *bufsz, size_t *buflen, int *line)
680 {
681 FILE *fp = NULL;
682 register char *n;
683 char linebuf[1024];
684 char *s = NULL;
685
686 if ((fp = fopen(fname, "r")) == NULL) {
687 fprintf(stderr, "Unable to open input file '%s': %s\n", fname, strerror(errno));
688 return;
689 }
690 while ((s = fgets(linebuf, sizeof(linebuf), fp)) != NULL) {
691 while (*buflen + strlen(s) + 2 > *bufsz) {
692 *bufsz *= 2;
693 if (!TDS_RESIZE(*mybuf, *bufsz)) {
694 perror("tsql: ");
695 exit(1);
696 }
697 }
698 strcpy(*mybuf + *buflen, s);
699 *buflen += strlen(*mybuf + *buflen);
700 n = strrchr(s, '\n');
701 if (n != NULL)
702 *n = '\0';
703 tsql_add_history(s);
704 (*line)++;
705 }
706 fclose(fp);
707 }
708
709 static void
print_instance_data(TDSLOGIN * login)710 print_instance_data(TDSLOGIN *login)
711 {
712 if (!login)
713 return;
714
715 if (!tds_dstr_isempty(&login->instance_name))
716 printf("connecting to instance %s on port %d\n", tds_dstr_cstr(&login->instance_name), login->port);
717 }
718
719 #if defined(HAVE_ALARM) && !defined(_WIN32)
720 static void
count_alarm(int s)721 count_alarm(int s)
722 {
723 static int count = 0;
724 char buf[64];
725
726 /* print the counter, do not use stderr as may be locked! */
727 sprintf(buf, "\r%2d", ++count);
728 write(STDERR_FILENO, buf, strlen(buf));
729
730 alarm(1);
731 }
732 #endif
733
734 int
main(int argc,char ** argv)735 main(int argc, char **argv)
736 {
737 char *s = NULL, *s2 = NULL, *cmd = NULL;
738 char prompt[20];
739 int line = 0;
740 char *mybuf;
741 size_t bufsz = 4096;
742 size_t buflen = 0;
743 TDSSOCKET *tds;
744 TDSLOGIN *login;
745 TDSCONTEXT *context;
746 TDSLOGIN *connection;
747 int opt_flags = 0;
748 const char *locale = NULL;
749 const char *charset = NULL;
750
751 istty = isatty(0);
752
753 if (INITSOCKET()) {
754 fprintf(stderr, "Unable to initialize sockets\n");
755 return 1;
756 }
757
758 setlocale(LC_ALL, "");
759
760 /* grab a login structure */
761 login = tds_alloc_login(1);
762 if (!login) {
763 fprintf(stderr, "login cannot be null\n");
764 return 1;
765 }
766
767 context = tds_alloc_context(NULL);
768 if (!context) {
769 fprintf(stderr, "context cannot be null\n");
770 return 1;
771 }
772 if (context->locale && !context->locale->date_fmt) {
773 /* set default in case there's no locale file */
774 context->locale->date_fmt = strdup(STD_DATETIME_FMT);
775 }
776
777 context->msg_handler = tsql_handle_message;
778 context->err_handler = tsql_handle_error;
779
780 /* process all the command line args into the login structure */
781 populate_login(login, argc, argv);
782
783 /* Try to open a connection */
784 tds = tds_alloc_socket(context, 512);
785 assert(tds);
786 tds_set_parent(tds, NULL);
787 connection = tds_read_config_info(tds, login, context->locale);
788 if (!connection)
789 return 1;
790
791 locale = setlocale(LC_ALL, NULL);
792
793 #if HAVE_LOCALE_CHARSET
794 charset = locale_charset();
795 #endif
796 #if HAVE_NL_LANGINFO && defined(CODESET)
797 if (!charset)
798 charset = nl_langinfo(CODESET);
799 #endif
800
801 if (locale)
802 if (!QUIET) printf("locale is \"%s\"\n", locale);
803 if (charset) {
804 if (!QUIET) printf("locale charset is \"%s\"\n", charset);
805 }
806
807 if (tds_dstr_isempty(&connection->client_charset)) {
808 if (!charset)
809 charset = "ISO-8859-1";
810
811 if (!tds_set_client_charset(login, charset))
812 return 1;
813 if (!tds_dstr_dup(&connection->client_charset, &login->client_charset))
814 return 1;
815 }
816 if (!QUIET) printf("using default charset \"%s\"\n", tds_dstr_cstr(&connection->client_charset));
817
818 if (opt_default_db) {
819 if (!tds_dstr_copy(&connection->database, opt_default_db))
820 return 1;
821 if (!QUIET) fprintf(stderr, "Setting %s as default database in login packet\n", opt_default_db);
822 }
823
824 /*
825 * If we're able to establish an ip address for the server, we'll try to connect to it.
826 * If that machine is currently unreachable, show a timer connecting to the server.
827 */
828 #if defined(HAVE_ALARM) && !defined(_WIN32)
829 if (connection && !QUIET) {
830 signal(SIGALRM, count_alarm);
831 fflush(stderr);
832 alarm(1);
833 }
834 #endif
835 if (!connection || TDS_FAILED(tds_connect_and_login(tds, connection))) {
836 if (VERBOSE)
837 print_instance_data(connection);
838 tds_free_socket(tds);
839 tds_free_login(login);
840 tds_free_context(context);
841 fprintf(stderr, "There was a problem connecting to the server\n");
842 exit(1);
843 }
844
845 #if defined(HAVE_ALARM) && !defined(_WIN32)
846 if (!QUIET) {
847 alarm(0);
848 signal(SIGALRM, SIG_DFL);
849 fprintf(stderr, "\r");
850 }
851 #endif
852
853 if (VERBOSE)
854 print_instance_data(connection);
855 tds_free_login(connection);
856 /* give the buffer an initial size */
857 bufsz = 4096;
858 mybuf = tds_new(char, bufsz);
859 if (!mybuf) {
860 fprintf(stderr, "Could not allocate memory for mybuf\n");
861 return 1;
862 }
863 mybuf[0] = '\0';
864 buflen = 0;
865
866 #if defined(HAVE_READLINE) && HAVE_RL_INHIBIT_COMPLETION
867 rl_inhibit_completion = 1;
868 #endif
869
870 for (s=NULL, s2=NULL; ; free(s), free(s2), s2=NULL) {
871 sprintf(prompt, "%d> ", ++line);
872 s = tsql_readline(QUIET ? NULL : prompt);
873 if (s == NULL) {
874 if (buflen)
875 do_query(tds, mybuf, global_opt_flags);
876 break;
877 }
878
879 /*
880 * 'GO' is special only at the start of a line
881 * The rest of the line may include options that apply to the batch,
882 * and perhaps whitespace.
883 */
884 if (0 == strncasecmp(s, "go", 2) && (strlen(s) == 2 || TDS_ISSPACE(s[2]))) {
885 char *go_line = strdup(s);
886 assert(go_line);
887 line = 0;
888 if (get_opt_flags(go_line, &opt_flags)) {
889 free(go_line);
890 opt_flags ^= global_opt_flags;
891 do_query(tds, mybuf, opt_flags);
892 mybuf[0] = '\0';
893 buflen = 0;
894 continue;
895 }
896 free(go_line);
897 }
898
899 /* skip leading whitespace */
900 if ((s2 = strdup(s)) == NULL) { /* copy to mangle with strtok() */
901 perror("tsql: ");
902 exit(1);
903 }
904
905 if ((cmd = strtok(s2, " \t")) == NULL)
906 cmd = "";
907
908 if (!strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit") || !strcasecmp(cmd, "bye"))
909 break;
910 if (!strcasecmp(cmd, "version")) {
911 tds_version(tds->conn, mybuf);
912 printf("using TDS version %s\n", mybuf);
913 line = 0;
914 mybuf[0] = '\0';
915 buflen = 0;
916 continue;
917 }
918 if (!strcasecmp(cmd, "reset")) {
919 line = 0;
920 mybuf[0] = '\0';
921 buflen = 0;
922 } else if (!strcasecmp(cmd, ":r")) {
923 slurp_input_file(strtok(NULL, " \t"), &mybuf, &bufsz, &buflen, &line);
924 } else {
925 while (buflen + strlen(s) + 2 > bufsz) {
926 bufsz *= 2;
927 if (!TDS_RESIZE(mybuf, bufsz)) {
928 perror("tsql: ");
929 exit(1);
930 }
931 }
932 tsql_add_history(s);
933 strcpy(mybuf + buflen, s);
934 /* preserve line numbering for the parser */
935 strcat(mybuf + buflen, "\n");
936 buflen += strlen(mybuf + buflen);
937 }
938 }
939
940 /* close up shop */
941 free(mybuf);
942 tds_close_socket(tds);
943 tds_free_socket(tds);
944 tds_free_login(login);
945 tds_free_context(context);
946 DONESOCKET();
947
948 return 0;
949 }
950