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 TDS_UNUSED)
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 information\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"
489 : "%35s: %s\n"
490 : "%35s: %" tdsPRIdir "\n"
491 : "%35s: %s\n"
492 : "%35s: %s\n"
493 : "%35s: %s\n"
494 : "%35s: %s\n"
495 : "%35s: %s\n"
496 : "%35s: %s\n"
497 : "%35s: %s\n"
498 : "%35s: %s\n"
499 : "%35s: %s\n"
500 : "%35s: %s\n"
501 : "%35s: %s\n"
502 : "%35s: %s\n",
503 : "Compile-time settings (established with the \"configure\" script)",
504 : "Version", settings->freetds_version,
505 : "freetds.conf directory", settings->sysconfdir,
506 : /* settings->last_update */
507 0 : "MS db-lib source compatibility", yes_no(settings->msdblib),
508 0 : "Sybase binary compatibility", yes_no(settings->sybase_compat),
509 0 : "Thread safety", yes_no(settings->threadsafe),
510 0 : "iconv library", yes_no(settings->libiconv),
511 : "TDS version", settings->tdsver,
512 0 : "iODBC", yes_no(settings->iodbc),
513 0 : "unixodbc", yes_no(settings->unixodbc),
514 0 : "SSPI \"trusted\" logins", yes_no(settings->sspi),
515 0 : "Kerberos", yes_no(settings->kerberos),
516 0 : "OpenSSL", yes_no(settings->openssl),
517 0 : "GnuTLS", yes_no(settings->gnutls),
518 0 : "MARS", yes_no(settings->mars));
519 0 : tds_free_login(login);
520 0 : exit(0);
521 : break;
522 0 : default:
523 0 : tsql_print_usage(basename(argv[0]));
524 0 : exit(1);
525 : break;
526 : }
527 : }
528 :
529 132 : if (opt_flags_str != NULL) {
530 120 : char *minus_flags = tds_new(char, strlen(opt_flags_str) + 5);
531 120 : if (minus_flags != NULL) {
532 120 : strcpy(minus_flags, "go -");
533 120 : strcat(minus_flags, opt_flags_str);
534 120 : get_opt_flags(minus_flags, &global_opt_flags);
535 120 : free(minus_flags);
536 : }
537 : }
538 :
539 132 : if ((global_opt_flags & OPT_INSTANCES) && hostname) {
540 : struct addrinfo *addr;
541 0 : tds_dir_char *filename = tds_dir_getenv(TDS_DIR("TDSDUMP"));
542 :
543 0 : if (filename) {
544 0 : size_t len = tds_dir_len(filename) + 12;
545 0 : tds_dir_char *path = tds_new(tds_dir_char, len);
546 0 : if (!path)
547 0 : exit(1);
548 0 : tds_dir_snprintf(path, len, TDS_DIR("%s.instances"), filename);
549 0 : tdsdump_open(path);
550 0 : free(path);
551 : }
552 0 : if ((addr = tds_lookup_host(hostname)) != NULL) {
553 0 : tds7_get_instance_ports(stderr, addr);
554 0 : freeaddrinfo(addr);
555 : }
556 0 : tdsdump_close();
557 0 : exit(0);
558 : }
559 :
560 : /* validate parameters */
561 132 : if (!servername && !hostname) {
562 0 : fprintf(stderr, "%s: error: Missing argument -S or -H\n", argv[0]);
563 0 : exit(1);
564 : }
565 132 : if (hostname && !port) {
566 : /*
567 : * TODO: It would be convenient to have a function that looks up a reasonable port based on:
568 : * - TDSPORT environment variable
569 : * - config files
570 : * - get_default_instance_port
571 : * - TDS version
572 : * in that order.
573 : */
574 0 : if (!QUIET) {
575 0 : printf("Missing argument -p, looking for default instance ... ");
576 : }
577 0 : if (0 == (port = get_default_instance_port(hostname))) {
578 0 : fprintf(stderr, "%s: no default port provided by host %s\n", argv[0], hostname);
579 0 : exit(1);
580 : }
581 0 : if (!QUIET)
582 0 : printf("found default instance, port %d\n", port);
583 :
584 : }
585 : /* A NULL username indicates a domain (trusted) login */
586 132 : if (!username) {
587 0 : username = tds_new0(char, 1);
588 0 : if (!username) {
589 0 : fprintf(stderr, "Could not allocate memory for username\n");
590 0 : exit(1);
591 : }
592 : use_domain_login = 1;
593 : }
594 132 : if (!password) {
595 0 : password = tds_new0(char, 128);
596 0 : if (!password) {
597 0 : fprintf(stderr, "Could not allocate memory for password\n");
598 0 : exit(1);
599 : }
600 0 : if (!use_domain_login)
601 0 : readpassphrase("Password: ", password, 128, RPP_ECHO_OFF);
602 : }
603 132 : if (!opt_col_term) {
604 0 : fprintf(stderr, "%s: missing delimiter for -t (check escaping)\n", argv[0]);
605 0 : exit(1);
606 : }
607 132 : if (!opt_row_term) {
608 0 : fprintf(stderr, "%s: missing delimiter for -r (check escaping)\n", argv[0]);
609 0 : exit(1);
610 : }
611 :
612 : /* all validated, let's do it */
613 132 : if (!tds_set_user(login, username)
614 132 : || !tds_set_app(login, appname)
615 132 : || !tds_set_library(login, "TDS-Library")
616 132 : || !tds_set_language(login, "us_english")
617 132 : || !tds_set_passwd(login, password))
618 : goto out_of_memory;
619 132 : if (charset && !tds_set_client_charset(login, charset))
620 : goto out_of_memory;
621 :
622 : /* if it's a servername */
623 132 : if (servername) {
624 132 : if (!tds_set_server(login, servername))
625 : goto out_of_memory;
626 132 : if (confile) {
627 0 : tds_set_interfaces_file_loc(confile);
628 : }
629 : /* else we specified hostname/port */
630 : } else {
631 0 : if (!tds_set_server(login, hostname))
632 : goto out_of_memory;
633 0 : tds_set_port(login, port);
634 : }
635 :
636 : memset(password, 0, strlen(password));
637 :
638 : /* free up all the memory */
639 132 : free(confile);
640 132 : free(hostname);
641 132 : free(username);
642 132 : free(password);
643 132 : free(servername);
644 132 : free(charset);
645 132 : return;
646 :
647 0 : out_of_memory:
648 0 : fprintf(stderr, "%s: out of memory\n", argv[0]);
649 0 : exit(1);
650 : }
651 :
652 : static int
653 296 : tsql_handle_message(const TDSCONTEXT * context TDS_UNUSED, TDSSOCKET * tds TDS_UNUSED, TDSMESSAGE * msg)
654 : {
655 296 : if (msg->msgno == 0) {
656 0 : fprintf(stderr, "%s\n", msg->message);
657 0 : return 0;
658 : }
659 :
660 296 : switch (msg->msgno) {
661 258 : case 5701: /* changed_database */
662 : case 5703: /* changed_language */
663 : case 20018: /* The @optional_command_line is too long */
664 258 : if (VERBOSE)
665 0 : fprintf(stderr, "%s\n", msg->message);
666 : break;
667 38 : default:
668 114 : fprintf(stderr, "Msg %d (severity %d, state %d) from %s",
669 76 : msg->msgno, msg->severity, msg->state, msg->server);
670 38 : if (msg->proc_name && strlen(msg->proc_name))
671 0 : fprintf(stderr, ", Procedure %s", msg->proc_name);
672 38 : if (msg->line_number > 0)
673 4 : fprintf(stderr, " Line %d", msg->line_number);
674 38 : fprintf(stderr, ":\n\t\"%s\"\n", msg->message);
675 38 : break;
676 : }
677 :
678 : return 0;
679 : }
680 :
681 : static int /* error from library, not message from server */
682 2 : tsql_handle_error(const TDSCONTEXT * context TDS_UNUSED, TDSSOCKET * tds TDS_UNUSED, TDSMESSAGE * msg)
683 : {
684 2 : fprintf(stderr, "Error %d (severity %d):\n\t%s\n", msg->msgno, msg->severity, msg->message);
685 2 : if (0 != msg->oserr) {
686 0 : fprintf(stderr, "\tOS error %d, \"%s\"\n", msg->oserr, strerror(msg->oserr));
687 : }
688 2 : return TDS_INT_CANCEL;
689 : }
690 :
691 : static void
692 0 : slurp_input_file(char *fname, char **mybuf, size_t *bufsz, size_t *buflen, int *line)
693 : {
694 0 : FILE *fp = NULL;
695 : register char *n;
696 : char linebuf[1024];
697 0 : char *s = NULL;
698 :
699 0 : if ((fp = fopen(fname, "r")) == NULL) {
700 0 : fprintf(stderr, "Unable to open input file '%s': %s\n", fname, strerror(errno));
701 0 : return;
702 : }
703 0 : while ((s = fgets(linebuf, sizeof(linebuf), fp)) != NULL) {
704 0 : while (*buflen + strlen(s) + 2 > *bufsz) {
705 0 : *bufsz *= 2;
706 0 : if (!TDS_RESIZE(*mybuf, *bufsz)) {
707 0 : perror("tsql: ");
708 0 : exit(1);
709 : }
710 : }
711 0 : strcpy(*mybuf + *buflen, s);
712 0 : *buflen += strlen(*mybuf + *buflen);
713 0 : n = strrchr(s, '\n');
714 0 : if (n != NULL)
715 0 : *n = '\0';
716 0 : tsql_add_history(s);
717 0 : (*line)++;
718 : }
719 0 : fclose(fp);
720 : }
721 :
722 : static void
723 0 : print_instance_data(TDSLOGIN *login)
724 : {
725 0 : if (!login)
726 : return;
727 :
728 0 : if (!tds_dstr_isempty(&login->instance_name))
729 0 : printf("connecting to instance %s on port %d\n", tds_dstr_cstr(&login->instance_name), login->port);
730 : }
731 :
732 : #if defined(HAVE_ALARM) && !defined(_WIN32)
733 : static void
734 0 : count_alarm(int s TDS_UNUSED)
735 : {
736 : static int count = 0;
737 : char buf[64];
738 :
739 : /* print the counter, do not use stderr as may be locked! */
740 0 : sprintf(buf, "\r%2d", ++count);
741 0 : write(STDERR_FILENO, buf, strlen(buf));
742 :
743 0 : alarm(1);
744 0 : }
745 : #endif
746 :
747 : int
748 132 : main(int argc, char **argv)
749 : {
750 132 : char *s = NULL, *s2 = NULL, *cmd = NULL;
751 : char prompt[20];
752 132 : int line = 0;
753 : char *mybuf;
754 132 : size_t bufsz = 4096;
755 132 : size_t buflen = 0;
756 : TDSSOCKET *tds;
757 : TDSLOGIN *login;
758 : TDSCONTEXT *context;
759 : TDSLOGIN *connection;
760 132 : int opt_flags = 0;
761 132 : const char *locale = NULL;
762 132 : const char *charset = NULL;
763 :
764 132 : istty = isatty(0);
765 :
766 : if (tds_socket_init()) {
767 : fprintf(stderr, "Unable to initialize sockets\n");
768 : return 1;
769 : }
770 :
771 132 : setlocale(LC_ALL, "");
772 :
773 : /* grab a login structure */
774 132 : login = tds_alloc_login(true);
775 132 : if (!login) {
776 0 : fprintf(stderr, "login cannot be null\n");
777 0 : return 1;
778 : }
779 :
780 : /* process all the command line args into the login structure */
781 132 : populate_login(login, argc, argv);
782 :
783 132 : context = tds_alloc_context(NULL);
784 132 : if (!context) {
785 0 : tds_free_login(login);
786 0 : fprintf(stderr, "context cannot be null\n");
787 0 : return 1;
788 : }
789 132 : if (context->locale && !context->locale->datetime_fmt) {
790 : /* set default in case there's no locale file */
791 0 : context->locale->datetime_fmt = strdup(STD_DATETIME_FMT);
792 : }
793 :
794 132 : context->msg_handler = tsql_handle_message;
795 132 : context->err_handler = tsql_handle_error;
796 :
797 : /* Try to open a connection */
798 132 : tds = tds_alloc_socket(context, 512);
799 132 : assert(tds);
800 132 : tds_set_parent(tds, NULL);
801 132 : connection = tds_read_config_info(tds, login, context->locale);
802 132 : if (!connection)
803 : return 1;
804 :
805 132 : locale = setlocale(LC_ALL, NULL);
806 :
807 : #if HAVE_LOCALE_CHARSET
808 : charset = locale_charset();
809 : #endif
810 : #if HAVE_NL_LANGINFO && defined(CODESET)
811 : if (!charset)
812 132 : charset = nl_langinfo(CODESET);
813 : #endif
814 :
815 132 : if (locale)
816 132 : if (!QUIET) printf("locale is \"%s\"\n", locale);
817 132 : if (charset) {
818 132 : if (!QUIET) printf("locale charset is \"%s\"\n", charset);
819 : }
820 :
821 264 : if (tds_dstr_isempty(&connection->client_charset)) {
822 0 : if (!charset)
823 0 : charset = "ISO-8859-1";
824 :
825 0 : if (!tds_set_client_charset(login, charset))
826 : return 1;
827 0 : if (!tds_dstr_dup(&connection->client_charset, &login->client_charset))
828 : return 1;
829 : }
830 144 : if (!QUIET) printf("using default charset \"%s\"\n", tds_dstr_cstr(&connection->client_charset));
831 :
832 132 : if (opt_default_db) {
833 120 : if (!tds_dstr_copy(&connection->database, opt_default_db))
834 : return 1;
835 120 : if (!QUIET) fprintf(stderr, "Setting %s as default database in login packet\n", opt_default_db);
836 : }
837 :
838 : /*
839 : * If we're able to establish an ip address for the server, we'll try to connect to it.
840 : * If that machine is currently unreachable, show a timer connecting to the server.
841 : */
842 : #if defined(HAVE_ALARM) && !defined(_WIN32)
843 132 : if (connection && !QUIET) {
844 12 : signal(SIGALRM, count_alarm);
845 12 : fflush(stderr);
846 12 : alarm(1);
847 : }
848 : #endif
849 132 : if (!connection || TDS_FAILED(tds_connect_and_login(tds, connection))) {
850 2 : if (VERBOSE)
851 0 : print_instance_data(connection);
852 2 : tds_free_socket(tds);
853 2 : tds_free_login(login);
854 2 : tds_free_context(context);
855 2 : fprintf(stderr, "There was a problem connecting to the server\n");
856 2 : exit(1);
857 : }
858 :
859 : #if defined(HAVE_ALARM) && !defined(_WIN32)
860 130 : if (!QUIET) {
861 10 : alarm(0);
862 10 : signal(SIGALRM, SIG_DFL);
863 10 : fprintf(stderr, "\r");
864 : }
865 : #endif
866 :
867 130 : if (VERBOSE)
868 0 : print_instance_data(connection);
869 130 : tds_free_login(connection);
870 : /* give the buffer an initial size */
871 130 : bufsz = 4096;
872 130 : mybuf = tds_new(char, bufsz);
873 130 : if (!mybuf) {
874 0 : fprintf(stderr, "Could not allocate memory for mybuf\n");
875 0 : return 1;
876 : }
877 130 : mybuf[0] = '\0';
878 130 : buflen = 0;
879 :
880 : #if defined(HAVE_READLINE) && HAVE_RL_INHIBIT_COMPLETION
881 130 : rl_inhibit_completion = 1;
882 : #endif
883 :
884 542 : for (s=NULL, s2=NULL; ; free(s), free(s2), s2=NULL) {
885 954 : sprintf(prompt, "%d> ", ++line);
886 542 : s = tsql_readline(QUIET ? NULL : prompt);
887 542 : if (s == NULL) {
888 120 : if (buflen)
889 112 : do_query(tds, mybuf, global_opt_flags);
890 : break;
891 : }
892 :
893 : /*
894 : * 'GO' is special only at the start of a line
895 : * The rest of the line may include options that apply to the batch,
896 : * and perhaps whitespace.
897 : */
898 422 : if (0 == strncasecmp(s, "go", 2) && (strlen(s) == 2 || TDS_ISSPACE(s[2]))) {
899 42 : char *go_line = strdup(s);
900 42 : assert(go_line);
901 42 : line = 0;
902 42 : if (get_opt_flags(go_line, &opt_flags)) {
903 42 : free(go_line);
904 42 : opt_flags ^= global_opt_flags;
905 42 : do_query(tds, mybuf, opt_flags);
906 42 : mybuf[0] = '\0';
907 42 : buflen = 0;
908 42 : continue;
909 : }
910 0 : free(go_line);
911 : }
912 :
913 : /* skip leading whitespace */
914 380 : if ((s2 = strdup(s)) == NULL) { /* copy to mangle with strtok() */
915 0 : perror("tsql: ");
916 0 : exit(1);
917 : }
918 :
919 380 : if ((cmd = strtok(s2, " \t")) == NULL)
920 16 : cmd = "";
921 :
922 380 : if (!strcasecmp(cmd, "exit") || !strcasecmp(cmd, "quit") || !strcasecmp(cmd, "bye"))
923 : break;
924 370 : if (!strcasecmp(cmd, "version")) {
925 0 : tds_version(tds->conn, mybuf);
926 0 : printf("using TDS version %s\n", mybuf);
927 0 : line = 0;
928 0 : mybuf[0] = '\0';
929 0 : buflen = 0;
930 0 : continue;
931 : }
932 370 : if (!strcasecmp(cmd, "reset")) {
933 0 : line = 0;
934 0 : mybuf[0] = '\0';
935 0 : buflen = 0;
936 370 : } else if (!strcasecmp(cmd, ":r")) {
937 0 : slurp_input_file(strtok(NULL, " \t"), &mybuf, &bufsz, &buflen, &line);
938 : } else {
939 370 : while (buflen + strlen(s) + 2 > bufsz) {
940 0 : bufsz *= 2;
941 0 : if (!TDS_RESIZE(mybuf, bufsz)) {
942 0 : perror("tsql: ");
943 0 : exit(1);
944 : }
945 : }
946 370 : tsql_add_history(s);
947 370 : strcpy(mybuf + buflen, s);
948 : /* preserve line numbering for the parser */
949 370 : strcat(mybuf + buflen, "\n");
950 370 : buflen += strlen(mybuf + buflen);
951 : }
952 : }
953 :
954 : /* close up shop */
955 130 : free(mybuf);
956 130 : tds_close_socket(tds);
957 130 : tds_free_socket(tds);
958 130 : tds_free_login(login);
959 130 : tds_free_context(context);
960 : tds_socket_done();
961 :
962 130 : return 0;
963 : }
|