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