LCOV - code coverage report
Current view: top level - src/apps - defncopy.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 362 483 74.9 %
Date: 2026-11-07 23:58:45 Functions: 20 21 95.2 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2004-2011  James K. Lowden
       3             :  *
       4             :  * This program  is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2 of the License, or (at your option) any later version.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Library General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU General Public
      15             :  * License along with this library; if not, write to the
      16             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      17             :  * Boston, MA 02111-1307, USA.
      18             :  */
      19             : 
      20             : #ifdef MicrosoftsDbLib
      21             : #ifdef _WIN32
      22             : # pragma warning( disable : 4142 )
      23             : # include "win32.microsoft/have.h"
      24             : # include "../../include/replacements.win32.hacked.h"
      25             : int getopt(int argc, const char *argv[], char *optstring);
      26             : 
      27             : # ifndef DBNTWIN32
      28             : #  define DBNTWIN32
      29             : 
      30             : /*
      31             :  * As of Visual Studio .NET 2003, define WIN32_LEAN_AND_MEAN to avoid redefining LPCBYTE in sqlfront.h
      32             :  * Unless it was already defined, undefine it after windows.h has been included.
      33             :  * (windows.h includes a bunch of stuff needed by sqlfront.h.  Bleh.)
      34             :  */
      35             : #  ifndef WIN32_LEAN_AND_MEAN
      36             : #   define WIN32_LEAN_AND_MEAN
      37             : #   define WIN32_LEAN_AND_MEAN_DEFINED_HERE
      38             : #  endif
      39             : #  include <freetds/windows.h>
      40             : #  ifdef WIN32_LEAN_AND_MEAN_DEFINED_HERE
      41             : #   undef WIN32_LEAN_AND_MEAN_DEFINED_HERE
      42             : #   undef WIN32_LEAN_AND_MEAN
      43             : #  endif
      44             : #  include <process.h>
      45             : #  include <sqlfront.h>
      46             : #  include <sqldb.h>
      47             : 
      48             : #endif /* DBNTWIN32 */
      49             : # include "win32.microsoft/syb2ms.h"
      50             : #endif
      51             : #endif /* MicrosoftsDbLib */
      52             : 
      53             : #include <config.h>
      54             : 
      55             : #include <stdio.h>
      56             : #include <assert.h>
      57             : 
      58             : #if HAVE_ERRNO_H
      59             : #include <errno.h>
      60             : #endif
      61             : 
      62             : #if HAVE_UNISTD_H
      63             : #include <unistd.h>
      64             : #endif
      65             : 
      66             : #if HAVE_STDLIB_H
      67             : #include <stdlib.h>
      68             : #endif
      69             : 
      70             : #if HAVE_STRING_H
      71             : #include <string.h>
      72             : #endif
      73             : 
      74             : #if HAVE_LIBGEN_H
      75             : #include <libgen.h>
      76             : #endif
      77             : 
      78             : #if HAVE_LOCALE_H
      79             : #include <locale.h>
      80             : #endif
      81             : 
      82             : #include <sybfront.h>
      83             : #include <sybdb.h>
      84             : #ifndef MicrosoftsDbLib
      85             : #include <freetds/replacements.h>
      86             : #else
      87             : 
      88             : #ifndef _WIN32
      89             : # include <freetds/replacements.h>
      90             : #endif
      91             : #endif /* MicrosoftsDbLib */
      92             : 
      93             : #include <freetds/sysdep_private.h>
      94             : #include <freetds/utils.h>
      95             : #include <freetds/bool.h>
      96             : #include <freetds/macros.h>
      97             : 
      98             : #ifndef MicrosoftsDbLib
      99             : static int err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, char *dberrstr, char *oserrstr);
     100             : static int msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, char *msgtext,
     101             :                 char *srvname, char *procname, int line);
     102             : #else
     103             : static int err_handler(DBPROCESS * dbproc, int severity, int dberr, int oserr, const char dberrstr[], const char oserrstr[]);
     104             : static int msg_handler(DBPROCESS * dbproc, DBINT msgno, int msgstate, int severity, const char msgtext[],
     105             :                 const char srvname[], const char procname[], unsigned short int line);
     106             : #endif /* MicrosoftsDbLib */
     107             : 
     108             : typedef struct _options
     109             : {
     110             :         int     optind;
     111             :         bool sybase_syntax;
     112             :         char    *servername,
     113             :                 *database,
     114             :                 *appname,
     115             :                  hostname[128],
     116             :                 *input_filename,
     117             :                 *output_filename;
     118             : } OPTIONS;
     119             : 
     120             : static void
     121         120 : free_options(OPTIONS *options)
     122             : {
     123             :         /* Memory was allocated with tds_xstrdup() which resolves to strdup(),
     124             :          * (not with tds_new) */
     125         120 :         free(options->servername);
     126         120 :         free(options->database);
     127         120 :         free(options->input_filename);
     128         120 :         free(options->output_filename);
     129             :         /* note: options->appname points into argv[0] */
     130         120 : }
     131             : 
     132             : typedef struct _procedure
     133             : {
     134             :         char name[512], owner[512];
     135             : } PROCEDURE;
     136             : 
     137             : typedef struct DDL {
     138             :         char *name;
     139             :         char *type;
     140             :         char *length;
     141             :         char *precision;
     142             :         char *scale;
     143             :         char *nullable;
     144             : } DDL;
     145             : 
     146             : static int print_ddl(DBPROCESS *dbproc, PROCEDURE *procedure);
     147             : static int print_results(DBPROCESS *dbproc);
     148             : static LOGINREC* get_login(char *argv0, OPTIONS *poptions);
     149             : static void parse_argument(const char argument[], PROCEDURE* procedure);
     150             : static void usage(const char invoked_as[]);
     151             : static char *rtrim(char *s);
     152             : static char *ltrim(char *s);
     153             : 
     154             : typedef struct tmp_buf {
     155             :         struct tmp_buf *next;
     156             :         char buf[1];
     157             : } tmp_buf;
     158             : 
     159             : static tmp_buf *tmp_list = NULL;
     160             : 
     161             : static void*
     162         494 : tmp_malloc(size_t len)
     163             : {
     164         494 :         tmp_buf *tmp = malloc(sizeof(tmp_buf*) + len);
     165         494 :         if (!tmp) {
     166           0 :                 fprintf(stderr, "Out of memory\n");
     167           0 :                 exit(1);
     168             :         }
     169         494 :         tmp->next = tmp_list;
     170         494 :         tmp_list = tmp;
     171         494 :         return tmp->buf;
     172             : }
     173             : 
     174             : static void
     175             : tmp_free(void)
     176             : {
     177        1028 :         while (tmp_list) {
     178         494 :                 tmp_buf *next = tmp_list->next;
     179         494 :                 free(tmp_list);
     180         494 :                 tmp_list = next;
     181             :         }
     182             : }
     183             : 
     184             : static size_t
     185             : count_chars(const char *s, char c)
     186             : {
     187         834 :         size_t num = 0;
     188             :         if (c != 0) {
     189         834 :                 --s;
     190         894 :                 while ((s = strchr(s + 1, c)) != NULL)
     191          40 :                         ++num;
     192             :         }
     193             :         return num;
     194             : }
     195             : 
     196             : static char *
     197             : sql_quote(char *dest, const char *src, char quote_char)
     198             : {
     199        4356 :         for (; *src; ++src) {
     200        4356 :                 if (*src == quote_char)
     201           0 :                         *dest++ = *src;
     202        4356 :                 *dest++ = *src;
     203             :         }
     204             :         return dest;
     205             : }
     206             : 
     207             : static const char *
     208         494 : quote_id(const char *id)
     209             : {
     210             :         size_t n, len;
     211             :         char *s, *p;
     212             : 
     213         494 :         n = count_chars(id, ']');
     214         494 :         len = 1 + strlen(id) + n + 1 + 1;
     215         494 :         p = s = tmp_malloc(len);
     216         494 :         *p++ = '[';
     217         494 :         p = sql_quote(p, id, ']');
     218         494 :         *p++ = ']';
     219         494 :         *p = 0;
     220         494 :         return s;
     221             : }
     222             : 
     223             : static const char *
     224         320 : quote_str(const char *str)
     225             : {
     226             :         size_t n, len;
     227             :         char *s, *p;
     228             : 
     229         320 :         n = count_chars(str, '\'');
     230         320 :         if (!n)
     231             :                 return str;
     232           0 :         len = strlen(str) + n + 1;
     233           0 :         s = tmp_malloc(len);
     234           0 :         p = sql_quote(s, str, '\'');
     235           0 :         *p = 0;
     236           0 :         return s;
     237             : }
     238             : 
     239             : /* global variables */
     240             : static OPTIONS options;
     241             : static char use_statement[512];
     242             : static DBPROCESS *dbproc;
     243             : 
     244             : /* end global variables */
     245             : 
     246             : 
     247             : static int process_args(LOGINREC * login, int argc, char **argv, OPTIONS * options);
     248             : static int main_processing(LOGINREC * login, int argc, char **argv);
     249             : 
     250             : /**
     251             :  * The purpose of this program is to load or extract the text of a stored procedure.
     252             :  * This first cut does extract only.
     253             :  * TODO: support loading procedures onto the server.
     254             :  */
     255             : int
     256         120 : main(int argc, char *argv[])
     257             : {
     258             :         LOGINREC *login;
     259             :         RETCODE erc;
     260             :         int result;
     261             : 
     262         120 :         setlocale(LC_ALL, "");
     263             : 
     264             : #ifdef __VMS
     265             :         /* Convert VMS-style arguments to Unix-style */
     266             :         parse_vms_args(&argc, &argv);
     267             : #endif
     268             : 
     269             :         /* Initialize db-lib */
     270             : #if _WIN32 && defined(MicrosoftsDbLib)
     271             :         LPCSTR msg = dbinit();
     272             :         if (msg == NULL) {
     273             : #else
     274         120 :         erc = dbinit();
     275         120 :         if (erc == FAIL) {
     276             : #endif /* MicrosoftsDbLib */
     277           0 :                 fprintf(stderr, "%s:%d: dbinit() failed\n", options.appname, __LINE__);
     278           0 :                 exit(EXIT_FAILURE);
     279             :         }
     280             : 
     281         120 :         memset(&options, 0, sizeof(options));
     282             :         /* get command-line parameters and call dblogin() */
     283         120 :         login = get_login(argv[0], &options);
     284         120 :         result = process_args(login, argc, argv, &options);
     285         120 :         if (result == 0)
     286         120 :                 result = main_processing(login, argc, argv);
     287             :         else
     288           0 :                 usage(options.appname);
     289             : 
     290         120 :         if (dbproc)
     291         120 :                 bcp_done(dbproc);
     292         120 :         if (login)
     293         120 :                 dbloginfree(login);
     294         120 :         dbexit();
     295         120 :         free_options(&options);
     296             :         return result;
     297             : }
     298             : 
     299             : static int
     300         120 : main_processing(LOGINREC *login, int argc, char **argv)
     301             : {
     302         120 :         int i, nrows = 0;
     303             :         RETCODE erc;
     304             :         PROCEDURE procedure;
     305             : 
     306         120 :         if (!login)
     307             :                 return EXIT_FAILURE;
     308             : 
     309             :         /* Install our error and message handlers */
     310         120 :         dberrhandle(err_handler);
     311         120 :         dbmsghandle(msg_handler);
     312             : 
     313             :         /*
     314             :          * Override stdin, stdout, and stderr, as required
     315             :          */
     316         120 :         if (options.input_filename) {
     317          30 :                 if (freopen(options.input_filename, "r", stdin) == NULL) {
     318           0 :                         fprintf(stderr, "%s: unable to open %s: %s\n", options.appname, options.input_filename, strerror(errno));
     319           0 :                         return EXIT_FAILURE;
     320             :                 }
     321             :         }
     322             : 
     323         120 :         if (options.output_filename) {
     324          40 :                 if (freopen(options.output_filename, "w", stdout) == NULL) {
     325           0 :                         fprintf(stderr, "%s: unable to open %s: %s\n", options.appname, options.output_filename, strerror(errno));
     326           0 :                         return EXIT_FAILURE;
     327             :                 }
     328             :         }
     329             : 
     330             :         /* Select the specified database, if any */
     331         120 :         if (options.database)
     332         120 :                 DBSETLDBNAME(login, options.database);
     333             : 
     334             :         /*
     335             :          * Connect to the server
     336             :          */
     337         120 :         dbproc = dbopen(login, options.servername);
     338         120 :         if (!dbproc) {
     339           0 :                 fprintf(stderr, "There was a problem connecting to the server.\n");
     340           0 :                 return EXIT_FAILURE;
     341             :         }
     342             : 
     343             :         /* We're in input mode if no output objects were specified. */
     344         120 :         if (options.optind == argc) {
     345             :                 /* This code is only designed to support the syntax output by DEFNCOPY.
     346             :                  * Refer to fisql.c for a more general input implementation.
     347             :                  */
     348             : 
     349             :                 char buf[5000];
     350             : 
     351             :                 i = 0;
     352         200 :                 while (fgets(buf, sizeof buf, stdin)) {
     353         160 :                         ++i;
     354             :                         /* dbcmd does not recognize directive "GO" */
     355         160 :                         if (buf[0] == 'G' && buf[1] == 'O')
     356          20 :                                 continue;
     357             : 
     358         140 :                         erc = dbcmd(dbproc, buf);
     359         140 :                         if (erc != SUCCEED) {
     360           0 :                                 fprintf(stderr, "%s: input failed on line %d\n", options.appname, i);
     361           0 :                                 return EXIT_FAILURE;
     362             :                         }
     363             :                 }
     364          40 :                 erc = dbsqlexec(dbproc);
     365          40 :                 if (erc != SUCCEED)
     366             :                         return EXIT_FAILURE;    /* Error handler would output error messages */
     367          40 :                 if (dbhasretstat(dbproc)) {
     368           0 :                         erc = dbretstatus(dbproc);
     369           0 :                         printf("(return status = %d)\n", erc);
     370             :                 }
     371             : 
     372             :                 return 0;
     373             :         }
     374             : 
     375             :         /*
     376             :          * Read the procedure names and move their texts.
     377             :          */
     378          80 :         for (i=options.optind; i < argc; i++) {
     379             : #ifndef MicrosoftsDbLib
     380             :                 static const char query[] = " select       c.text"
     381             : #else
     382             :                 static const char query[] = " select       cast(c.text as text)"
     383             : #endif /* MicrosoftsDbLib */
     384             :                                          ", number "
     385             :                                          " from syscomments c,"
     386             :                                          "      sysobjects o"
     387             :                                          " where   o.id = c.id"
     388             :                                          " and             o.name = '%s'"
     389             :                                          " and             o.uid = user_id('%s')"
     390             :                                          " and             o.type not in ('U', 'S')" /* no user or system tables */
     391             :                                          " order by        c.number, %sc.colid"
     392             :                                         ;
     393             :                 static const char query_table[] = " execute sp_help '%s.%s' ";
     394             : 
     395          80 :                 parse_argument(argv[i], &procedure);
     396             : 
     397          80 :                 erc = dbfcmd(dbproc, query, quote_str(procedure.name), quote_str(procedure.owner),
     398          80 :                              (DBTDS(dbproc) == DBTDS_5_0) ? "c.colid2, ":"");
     399          80 :                 tmp_free();
     400             : 
     401             :                 /* Send the query to the server (we could use dbsqlexec(), instead) */
     402          80 :                 erc = dbsqlsend(dbproc);
     403          80 :                 if (erc == FAIL) {
     404           0 :                         fprintf(stderr, "%s:%d: dbsqlsend() failed\n", options.appname, __LINE__);
     405           0 :                         return EXIT_FAILURE;
     406             :                 }
     407             : 
     408             :                 /* Wait for it to execute */
     409          80 :                 erc = dbsqlok(dbproc);
     410          80 :                 if (erc == FAIL) {
     411           0 :                         fprintf(stderr, "%s:%d: dbsqlok() failed\n", options.appname, __LINE__);
     412           0 :                         return EXIT_FAILURE;
     413             :                 }
     414             : 
     415             :                 /* Write the output */
     416          80 :                 nrows = print_results(dbproc);
     417             : 
     418             :                 /* If the query to retrieve a non-table object failed, try retrieving
     419             :                  * a table instead. Note: Sybase DEFNCOPY does not support table
     420             :                  * definitions; this code is peculiar to FreeTDS DEFNCOPY.
     421             :                  */
     422          80 :                 if (0 == nrows) {
     423          80 :                         erc = dbfcmd(dbproc, query_table, quote_str(procedure.owner), quote_str(procedure.name));
     424          80 :                         tmp_free();
     425          80 :                         assert(SUCCEED == erc);
     426          80 :                         erc = dbsqlexec(dbproc);
     427          80 :                         if (erc == FAIL) {
     428           0 :                                 fprintf(stderr, "%s:%d: dbsqlexec() failed\n", options.appname, __LINE__);
     429           0 :                                 return EXIT_FAILURE;
     430             :                         }
     431          80 :                         nrows = print_ddl(dbproc, &procedure);
     432             :                 }
     433             : 
     434          80 :                 switch (nrows) {
     435             :                 case -1:
     436             :                         return EXIT_FAILURE;
     437           0 :                 case 0:
     438           0 :                         fprintf(stderr, "%s: error: %s.%s.%s.%s not found\n", options.appname,
     439             :                                 options.servername, options.database, procedure.owner, procedure.name);
     440           0 :                         return 2;
     441             :                 default:
     442             :                         break;
     443             :                 }
     444             :         }
     445             : 
     446             :         return EXIT_SUCCESS;
     447             : }
     448             : 
     449             : static void
     450          80 : parse_argument(const char argument[], PROCEDURE* procedure)
     451             : {
     452          80 :         const char *s = strchr(argument, '.');
     453             : 
     454          80 :         if (s) {
     455          80 :                 size_t len = s - argument;
     456          80 :                 if (len > sizeof(procedure->owner) - 1)
     457           0 :                         len = sizeof(procedure->owner) - 1;
     458          80 :                 memcpy(procedure->owner, argument, len);
     459          80 :                 procedure->owner[len] = '\0';
     460             : 
     461          80 :                 strlcpy(procedure->name, s+1, sizeof(procedure->name));
     462             :         } else {
     463           0 :                 strcpy(procedure->owner, "dbo");
     464           0 :                 strlcpy(procedure->name, argument, sizeof(procedure->name));
     465             :         }
     466          80 : }
     467             : 
     468             : static char *
     469         240 : rtrim(char *s)
     470             : {
     471         240 :         char *p = strchr(s, '\0');
     472             : 
     473         240 :         while (--p >= s && *p == ' ')
     474             :                 ;
     475         240 :         *(p + 1) = '\0';
     476             : 
     477         240 :         return s;
     478             : }
     479             : 
     480             : static char *
     481         240 : ltrim(char *s)
     482             : {
     483         240 :         char *p = s;
     484             : 
     485         480 :         while (*p == ' ')
     486           0 :                 ++p;
     487         240 :         memmove(s, p, strlen(p) + 1);
     488             : 
     489         240 :         return s;
     490             : }
     491             : 
     492             : static bool
     493        6664 : is_in(const char *item, const char *list)
     494             : {
     495        6664 :         const size_t item_len = strlen(item);
     496        7034 :         for (;;) {
     497       13698 :                 size_t len = strlen(list);
     498       13698 :                 if (len == 0)
     499             :                         return false;
     500        8122 :                 if (len == item_len && strcasecmp(item, list) == 0)
     501             :                         return true;
     502        7034 :                 list += len + 1;
     503             :         }
     504             : }
     505             : 
     506             : static void
     507         150 : search_columns(DBPROCESS *dbproc, int *colmap, const char *const *colmap_names, int num_cols)
     508             : {
     509             :         int i;
     510             : 
     511         150 :         assert(dbproc && colmap && colmap_names);
     512         150 :         assert(num_cols > 0);
     513             : 
     514             :         /* Find the columns we need */
     515         840 :         for (i = 0; i < num_cols; ++i)
     516         840 :                 colmap[i] = -1;
     517        1406 :         for (i = 1; i <= dbnumcols(dbproc); ++i) {
     518        1406 :                 const char *name = dbcolname(dbproc, i);
     519             :                 int j;
     520             : 
     521        6752 :                 for (j = 0; j < num_cols; ++j) {
     522        6186 :                         if (is_in(name, colmap_names[j])) {
     523         840 :                                 colmap[j] = i;
     524         840 :                                 break;
     525             :                         }
     526             :                 }
     527             :         }
     528         840 :         for (i = 0; i < num_cols; ++i) {
     529         840 :                 if (colmap[i] == -1) {
     530           0 :                         fprintf(stderr, "Expected column name %s not found\n", colmap_names[i]);
     531           0 :                         exit(1);
     532             :                 }
     533             :         }
     534         150 : }
     535             : 
     536             : static int
     537          34 : find_column_name(const char *start, const DDL *columns, int num_columns)
     538             : {
     539          34 :         size_t start_len = strlen(start);
     540          34 :         size_t found_len = 0;
     541          34 :         int i, found = -1;
     542             : 
     543         158 :         for (i = 0; i < num_columns; ++i) {
     544         124 :                 const char *const name = columns[i].name;
     545         124 :                 const size_t name_len = strlen(name);
     546         124 :                 if (name_len <= start_len && name_len > found_len
     547          86 :                     && (start[name_len] == 0 || strncmp(start+name_len, ", ", 2) == 0)
     548          60 :                     && memcmp(name, start, name_len) == 0) {
     549          40 :                         found_len = name_len;
     550          40 :                         found = i;
     551             :                 }
     552             :         }
     553          34 :         return found;
     554             : }
     555             : 
     556             : static void
     557          80 : definition_delimiter(void)
     558             : {
     559          80 :         if (options.sybase_syntax)
     560          30 :                 printf("/* ### DEFNCOPY: END OF DEFINITION */\n");
     561             :         else
     562          50 :                 printf("GO\n\n");
     563          80 : }
     564             : 
     565             : /* This function split and quote index keys.
     566             :  * Index keys are separate by a command and a space (", ") however the
     567             :  * separator (very unlikely but possible can contain that separator)
     568             :  * so we use search for column names taking the longer we can.
     569             :  */
     570             : static char *
     571          20 : quote_index_keys(char *index_keys, const DDL *columns, int num_columns)
     572             : {
     573             :         size_t num_commas = count_chars(index_keys, ',');
     574          20 :         size_t num_quotes = count_chars(index_keys, ']');
     575          20 :         size_t max_len = strlen(index_keys) + num_quotes + (num_commas + 1) * 2 + 1;
     576          20 :         char *const new_index_keys = malloc(max_len);
     577             :         char *dest;
     578          20 :         bool first = true;
     579          20 :         assert(new_index_keys);
     580             :         dest = new_index_keys;
     581          50 :         while (*index_keys) {
     582          30 :                 int icol = find_column_name(index_keys, columns, num_columns);
     583             :                 /* Sybase put a space at the beginning, handle it */
     584          30 :                 if (icol < 0 && index_keys[0] == ' ') {
     585           4 :                         icol = find_column_name(index_keys + 1, columns, num_columns);
     586           4 :                         if (icol >= 0)
     587           4 :                                 ++index_keys;
     588             :                 }
     589          30 :                 if (!first)
     590          10 :                         *dest++ = ',';
     591          30 :                 *dest++ = '[';
     592          30 :                 if (icol >= 0) {
     593             :                         /* found a column matching, use the name */
     594          60 :                         dest = sql_quote(dest, columns[icol].name, ']');
     595          30 :                         index_keys += strlen(columns[icol].name);
     596             :                 } else {
     597             :                         /* not found, fallback looking for terminator */
     598             :                         char save;
     599           0 :                         char *end = strstr(index_keys, ", ");
     600           0 :                         if (!end)
     601           0 :                                 end = strchr(index_keys, 0);
     602           0 :                         save = *end;
     603           0 :                         *end = 0;
     604           0 :                         dest = sql_quote(dest, index_keys, ']');
     605           0 :                         *end = save;
     606           0 :                         index_keys = end;
     607             :                 }
     608          30 :                 *dest++ = ']';
     609          30 :                 if (strncmp(index_keys, ", ", 2) == 0)
     610          10 :                         index_keys += 2;
     611             :                 first = false;
     612             :         }
     613          20 :         *dest = 0;
     614          20 :         return new_index_keys;
     615             : }
     616             : 
     617             : static char *
     618          20 : parse_index_row(DBPROCESS *dbproc, PROCEDURE *procedure, char *create_index, const DDL *columns, int num_columns)
     619             : {
     620             :         static const char *const colmap_names[3] = {
     621             :                 "index_name\0",
     622             :                 "index_description\0",
     623             :                 "index_keys\0",
     624             :         };
     625             :         int colmap[TDS_VECTOR_SIZE(colmap_names)];
     626          20 :         char *index_name, *index_description, *index_keys, *p, fprimary=0;
     627             :         char *tmp_str;
     628             :         DBINT datlen;
     629             :         int ret, i;
     630             : 
     631          20 :         assert(dbnumcols(dbproc) >=3 );      /* column had better be in range */
     632             : 
     633          20 :         search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
     634             : 
     635             :         /* name */
     636          20 :         datlen = dbdatlen(dbproc, 1);
     637          20 :         index_name = (char *) tds_strndup(dbdata(dbproc, 1), datlen);
     638          20 :         assert(index_name);
     639             : 
     640             :         /* kind */
     641          20 :         i = colmap[1];
     642          20 :         datlen = dbdatlen(dbproc, i);
     643          20 :         index_description = (char *) tds_strndup(dbdata(dbproc, i), datlen);
     644          20 :         assert(index_description);
     645             : 
     646             :         /* columns */
     647          20 :         i = colmap[2];
     648          20 :         datlen = dbdatlen(dbproc, i);
     649          20 :         index_keys = (char *) tds_strndup(dbdata(dbproc, i), datlen);
     650          20 :         assert(index_keys);
     651             : 
     652          20 :         tmp_str = quote_index_keys(index_keys, columns, num_columns);
     653          20 :         free(index_keys);
     654          20 :         index_keys = tmp_str;
     655             : 
     656             :         /* fix up the index attributes; we're going to use the string verbatim (almost). */
     657          20 :         p = strstr(index_description, "located");
     658          20 :         if (p) {
     659          16 :                 *p = '\0'; /* we don't care where it's located */
     660             :         }
     661             :         /* Microsoft version: [non]clustered[, unique][, primary key] located on PRIMARY */
     662          20 :         p = strstr(index_description, "primary key");
     663          20 :         if (p) {
     664           0 :                 fprimary = 1;
     665           0 :                 *p = '\0'; /* we don't care where it's located */
     666           0 :                 if ((p = strchr(index_description, ',')) != NULL)
     667           0 :                         *p = '\0'; /* we use only the first term (clustered/nonclustered) */
     668             :         } else {
     669             :                 /* reorder "unique" and "clustered" */
     670          20 :                 char nonclustered[] = "nonclustered", unique[] = "unique";
     671          20 :                 char *pclustering = nonclustered;
     672          20 :                 if (NULL == strstr(index_description, pclustering)) {
     673           0 :                         pclustering += 3;
     674           0 :                         if (NULL == strstr(index_description, pclustering))
     675           0 :                                 *pclustering = '\0';
     676             :                 }
     677          20 :                 if (NULL == strstr(index_description, unique))
     678          20 :                         unique[0] = '\0';
     679          20 :                 sprintf(index_description, "%s %s", unique, pclustering);
     680             :         }
     681             :         /* Put it to a temporary variable; we'll print it after the CREATE TABLE statement. */
     682          20 :         tmp_str = create_index;
     683          20 :         create_index = create_index ? create_index : "";
     684          20 :         if (fprimary) {
     685           0 :                 ret = asprintf(&create_index,
     686             :                                "%sALTER TABLE %s.%s ADD CONSTRAINT %s PRIMARY KEY %s (%s)\n\n",
     687             :                                create_index,
     688           0 :                                quote_id(procedure->owner), quote_id(procedure->name),
     689             :                                quote_id(index_name), index_description, index_keys);
     690             :         } else {
     691          40 :                 ret = asprintf(&create_index,
     692             :                                "%sCREATE %s INDEX %s on %s.%s(%s)\n\n",
     693             :                                create_index,
     694             :                                index_description, quote_id(index_name),
     695          40 :                                quote_id(procedure->owner), quote_id(procedure->name), index_keys);
     696             :         }
     697          20 :         assert(ret >= 0);
     698          20 :         free(tmp_str);
     699          20 :         tmp_free();
     700             : 
     701          20 :         free(index_name);
     702          20 :         free(index_description);
     703          20 :         free(index_keys);
     704             : 
     705          20 :         return create_index;
     706             : }
     707             : 
     708             : /*
     709             :  * Get the table information from sp_help, because it's easier to get the index information (eventually).
     710             :  * The column descriptions are in resultset #2, which is where we start.
     711             :  * As shown below, sp_help returns different columns for resultset #2, so we build a map.
     712             :  *          Sybase                 Microsoft
     713             :  *          -----------------      ----------------
     714             :  *       1. Column_name            Column_name
     715             :  *       2. Type                   Type
     716             :  *       3.                        Computed
     717             :  *       4. Length                 Length
     718             :  *       5. Prec                   Prec
     719             :  *       6. Scale                  Scale
     720             :  *       7. Nulls                  Nullable
     721             :  *       8. Default_name           TrimTrail
     722             :  *       9. Rule_name              FixedLenNullIn
     723             :  *      10. Access_Rule_name       Collation
     724             :  *      11. Identity
     725             :  */
     726             : static int
     727          80 : print_ddl(DBPROCESS *dbproc, PROCEDURE *procedure)
     728             : {
     729             :         static const char *const colmap_names[6] = {
     730             :                 "column_name\0",
     731             :                 "type\0",
     732             :                 "length\0",
     733             :                 "prec\0",
     734             :                 "scale\0",
     735             :                 "nulls\0nullable\0",
     736             :         };
     737             : 
     738          80 :         DDL *ddl = NULL;
     739          80 :         char *create_index = NULL;
     740             :         RETCODE erc;
     741             :         int iresultset, i;
     742          80 :         int maxnamelen = 0, nrows = 0;
     743             :         char **p_str;
     744          80 :         bool is_ms = false;
     745             : 
     746          80 :         assert(dbproc);
     747          80 :         assert(procedure);
     748             : 
     749             :         /* sp_help returns several result sets.  We want just the second one, for now */
     750         488 :         for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
     751             :                 int row_code;
     752             : 
     753         488 :                 if (erc == FAIL) {
     754           0 :                         fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
     755           0 :                         goto cleanup;
     756             :                 }
     757             : 
     758             :                 /* Get the data */
     759        1010 :                 while ((row_code = dbnextrow(dbproc)) != NO_MORE_ROWS) {
     760             :                         DDL *p;
     761             :                         char **coldesc[sizeof(DDL)/sizeof(char*)];      /* an array of pointers to the DDL elements */
     762             :                         int colmap[TDS_VECTOR_SIZE(colmap_names)];
     763             : 
     764         522 :                         assert(row_code == REG_ROW);
     765             : 
     766             :                         /* Look for index data */
     767         522 :                         if (0 == strcmp("index_name", dbcolname(dbproc, 1))) {
     768          20 :                                 create_index = parse_index_row(dbproc, procedure, create_index, ddl, nrows);
     769         412 :                                 continue;
     770             :                         }
     771             : 
     772             :                         /* skip other resultsets that don't describe the table's columns */
     773         502 :                         if (0 != strcasecmp("Column_name", dbcolname(dbproc, 1)))
     774         372 :                                 continue;
     775             : 
     776             :                         /* Find the columns we need */
     777         130 :                         search_columns(dbproc, colmap, colmap_names, TDS_VECTOR_SIZE(colmap));
     778             : 
     779             :                         /* check if server is Microsoft */
     780        1352 :                         for (i = 1; i <= dbnumcols(dbproc); ++i)
     781        1326 :                                 if (strcasecmp(dbcolname(dbproc, i), "Collation") == 0) {
     782             :                                         is_ms = true;
     783             :                                         break;
     784             :                                 }
     785             : 
     786             :                         /* Make room for the next row */
     787         130 :                         p = (DDL *) realloc(ddl, ++nrows * sizeof(DDL));
     788         130 :                         if (p == NULL) {
     789           0 :                                 perror("error: insufficient memory for row DDL");
     790           0 :                                 assert(p !=  NULL);
     791             :                                 exit(1);
     792             :                         }
     793         130 :                         ddl = p;
     794             : 
     795             :                         /* take the address of each member, so we can loop through them */
     796         130 :                         coldesc[0] = &ddl[nrows-1].name;
     797         130 :                         coldesc[1] = &ddl[nrows-1].type;
     798         130 :                         coldesc[2] = &ddl[nrows-1].length;
     799         130 :                         coldesc[3] = &ddl[nrows-1].precision;
     800         130 :                         coldesc[4] = &ddl[nrows-1].scale;
     801         130 :                         coldesc[5] = &ddl[nrows-1].nullable;
     802             : 
     803         910 :                         for( i=0; i < sizeof(DDL)/sizeof(char*); i++) {
     804         780 :                                 const int col_index = colmap[i];
     805         780 :                                 const DBINT datlen = dbdatlen(dbproc, col_index);
     806         780 :                                 const int type = dbcoltype(dbproc, col_index);
     807             : 
     808         780 :                                 assert(datlen >= 0); /* column had better be in range */
     809             : 
     810         780 :                                 if (datlen == 0) {
     811          52 :                                         *coldesc[i] = NULL;
     812          52 :                                         continue;
     813             :                                 }
     814             : 
     815         728 :                                 if (type == SYBCHAR || type == SYBVARCHAR) {
     816         572 :                                         *coldesc[i] = (char *) tds_strndup(dbdata(dbproc, col_index), datlen);
     817         572 :                                         if (!*coldesc[i]) {
     818           0 :                                                 perror("error: insufficient memory for row detail");
     819           0 :                                                 exit(1);
     820             :                                         }
     821             :                                 } else {
     822             :                                         char buf[256];
     823         156 :                                         DBINT len = dbconvert(dbproc, type, dbdata(dbproc, col_index), datlen,
     824             :                                                 SYBVARCHAR, (BYTE *) buf, -1);
     825         156 :                                         if (len < 0) {
     826           0 :                                                 fprintf(stderr, "Error converting column to char");
     827           0 :                                                 exit(1);
     828             :                                         }
     829         156 :                                         *coldesc[i] = strdup(buf);
     830         156 :                                         if (!*coldesc[i]) {
     831           0 :                                                 perror("error: insufficient memory for row detail");
     832           0 :                                                 exit(1);
     833             :                                         }
     834             :                                 }
     835             : 
     836             :                                 /*
     837             :                                  * maxnamelen will determine how much room we allow for column names
     838             :                                  * in the CREATE TABLE statement
     839             :                                  */
     840         728 :                                 if (i == 0)
     841         130 :                                         maxnamelen = (maxnamelen > datlen)? maxnamelen : datlen;
     842             :                         }
     843             :                 } /* wend */
     844             :         }
     845             : 
     846             :         /*
     847             :          * We've collected the description for the columns in the 'ddl' array.
     848             :          * Now we'll print the CREATE TABLE statement in jkl's preferred format.
     849             :          */
     850          80 :         if (nrows == 0)
     851             :                 goto cleanup;
     852             : 
     853          80 :         printf("CREATE TABLE %s.%s\n", quote_id(procedure->owner), quote_id(procedure->name));
     854             :         tmp_free();
     855         130 :         for (i=0; i < nrows; i++) {
     856             :                 static const char varytypenames[] =     "char\0"
     857             :                                                         "nchar\0"
     858             :                                                         "varchar\0"
     859             :                                                         "nvarchar\0"
     860             :                                                         "unichar\0"
     861             :                                                         "univarchar\0"
     862             :                                                         "binary\0"
     863             :                                                         "varbinary\0"
     864             :                                                         ;
     865         130 :                 char *type = NULL;
     866             :                 bool is_null;
     867             :                 int ret;
     868             : 
     869             :                 /* get size of decimal, numeric, char, and image types */
     870         130 :                 ret = 0;
     871         130 :                 if (is_in(ddl[i].type, "decimal\0numeric\0")) {
     872           0 :                         if (ddl[i].precision && 0 != strcasecmp("NULL", ddl[i].precision)) {
     873           0 :                                 rtrim(ddl[i].precision);
     874           0 :                                 rtrim(ddl[i].scale);
     875           0 :                                 ret = asprintf(&type, "%s(%s,%s)", ddl[i].type, ddl[i].precision, ddl[i].scale);
     876             :                         }
     877         130 :                 } else if (is_in(ddl[i].type, varytypenames)) {
     878         110 :                         ltrim(rtrim(ddl[i].length));
     879         110 :                         if (strcmp(ddl[i].length, "-1") == 0)
     880           0 :                                 ret = asprintf(&type, "%s(max)", ddl[i].type);
     881         110 :                         else if (is_ms && is_in(ddl[i].type, "nchar\0nvarchar\0"))
     882         176 :                                 ret = asprintf(&type, "%s(%d)", ddl[i].type, atoi(ddl[i].length)/2);
     883             :                         else
     884          22 :                                 ret = asprintf(&type, "%s(%s)", ddl[i].type, ddl[i].length);
     885             :                 }
     886         110 :                 assert(ret >= 0);
     887             : 
     888         130 :                 ltrim(rtrim(ddl[i].nullable));
     889         130 :                 is_null = is_in(ddl[i].nullable, "1\0yes\0");
     890             : 
     891             :                 /*      {(|,} name type [NOT] NULL */
     892         260 :                 printf("\t%c %-*s %-15s %3s NULL\n", (i==0? '(' : ','), maxnamelen+2, quote_id(ddl[i].name),
     893         130 :                                                 (type? type : ddl[i].type), (is_null? "" : "NOT"));
     894         130 :                 tmp_free();
     895             : 
     896         130 :                 free(type);
     897             :         }
     898          80 :         printf("\t)\n");
     899             : 
     900             :         /* print the CREATE INDEX statements */
     901          80 :         if (create_index != NULL)
     902          20 :                 fputs(create_index, stdout);
     903             : 
     904          80 :         definition_delimiter();
     905             : 
     906          80 : cleanup:
     907          80 :         p_str = (char **) ddl;
     908         860 :         for (i = 0; i < nrows * (sizeof(DDL) / sizeof(char *)); ++i)
     909         780 :                 free(p_str[i]);
     910          80 :         free(ddl);
     911          80 :         free(create_index);
     912          80 :         return nrows;
     913             : }
     914             : 
     915             : static int /* return count of SQL text rows */
     916          80 : print_results(DBPROCESS *dbproc)
     917             : {
     918             :         RETCODE erc;
     919             :         int row_code;
     920             :         int iresultset;
     921          80 :         int nrows = 0;
     922             : 
     923             :         /* bound variables */
     924             :         enum column_id { ctext=1, number=2 };
     925             :         char sql_text[16002];
     926             :         int      sql_text_status;
     927             :         int      procedure_number; /* for create proc abc;2 */
     928             :         int      procedure_number_status;
     929             : 
     930             :         /*
     931             :          * Set up each result set with dbresults()
     932             :          */
     933          80 :         for (iresultset=1; (erc = dbresults(dbproc)) != NO_MORE_RESULTS; iresultset++) {
     934          80 :                 if (erc == FAIL) {
     935           0 :                         fprintf(stderr, "%s:%d: dbresults(), result set %d failed\n", options.appname, __LINE__, iresultset);
     936           0 :                         return -1;
     937             :                 }
     938             : 
     939          80 :                 if (SUCCEED != DBROWS(dbproc)) {
     940             :                         return 0;
     941             :                 }
     942             : 
     943             :                 /*
     944             :                  * Bind the columns to our variables.
     945             :                  */
     946           0 :                 if (sizeof(sql_text) <= dbcollen(dbproc, ctext) ) {
     947           0 :                         assert(sizeof(sql_text) > dbcollen(dbproc, ctext));
     948             :                         return 0;
     949             :                 }
     950           0 :                 erc = dbbind(dbproc, ctext, STRINGBIND, 0, (BYTE *) sql_text);
     951           0 :                 if (erc == FAIL) {
     952           0 :                         fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, ctext);
     953           0 :                         return -1;
     954             :                 }
     955           0 :                 erc = dbnullbind(dbproc, ctext, &sql_text_status);
     956           0 :                 if (erc == FAIL) {
     957           0 :                         fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, ctext);
     958           0 :                         return -1;
     959             :                 }
     960             : 
     961           0 :                 erc = dbbind(dbproc, number, INTBIND, -1, (BYTE *) &procedure_number);
     962           0 :                 if (erc == FAIL) {
     963           0 :                         fprintf(stderr, "%s:%d: dbbind(), column %d failed\n", options.appname, __LINE__, number);
     964           0 :                         return -1;
     965             :                 }
     966           0 :                 erc = dbnullbind(dbproc, number, &procedure_number_status);
     967           0 :                 if (erc == FAIL) {
     968           0 :                         fprintf(stderr, "%s:%d: dbnullbind(), column %d failed\n", options.appname, __LINE__, number);
     969           0 :                         return -1;
     970             :                 }
     971             : 
     972             :                 /*
     973             :                  * Print the data to stdout.
     974             :                  */
     975           0 :                 for (; (row_code = dbnextrow(dbproc)) != NO_MORE_ROWS; nrows++) {
     976           0 :                         switch (row_code) {
     977           0 :                         case REG_ROW:
     978           0 :                                 if (-1 == sql_text_status) {
     979           0 :                                         fprintf(stderr, "defncopy: error: unexpected NULL row in SQL text\n");
     980             :                                 } else {
     981           0 :                                         printf("%s", sql_text);
     982             :                                 }
     983             :                                 break;
     984           0 :                         case BUF_FULL:
     985             :                         default:
     986           0 :                                 fprintf(stderr, "defncopy: error: expected REG_ROW (%d), got %d instead\n", REG_ROW, row_code);
     987           0 :                                 assert(row_code == REG_ROW);
     988             :                                 break;
     989             :                         }       /* row_code */
     990             : 
     991             :                 }               /* wend dbnextrow */
     992             :                 /* Sybase DEFNCOPY marker between definitions */
     993           0 :                 definition_delimiter();
     994             : 
     995             :         }                       /* wend dbresults */
     996             : 
     997             :         return nrows;
     998             : }
     999             : 
    1000             : static void
    1001             : usage(const char invoked_as[])
    1002             : {
    1003             : 
    1004           0 :         fprintf(stderr, "usage:  %s \n"
    1005             : /*
    1006             :                 "        [-X]\n"
    1007             :                 "        [-a <display_charset>]\n"
    1008             :                 "        [-I <interfaces_file>]\n"
    1009             :                 "        [-J [<client_charset>]]\n"
    1010             :                 "        [-K <keytab_file>]\n"
    1011             : */
    1012             :                 "        [-P <password>]\n"
    1013             : /*
    1014             :                 "        [-R <remote_server_principal>]\n"
    1015             : */
    1016             :                 "        [-S [<server_name>]]\n"
    1017             :                 "        [-U <user_name>]\n"
    1018             : /*
    1019             :                 "        [-V <security_options>]\n"
    1020             :                 "        [-Z <security_mechanism>]\n"
    1021             :                 "        [-z <language>]\n"
    1022             : */
    1023             :                 "        { in <file_name> <database_name> |\n"
    1024             :                 "          out <file_name> <database_name> [<owner>.]<object_name>\n"
    1025             :                 "              [[<owner>.]<object_name>...] }\n"
    1026             :                 "\n            OR\n\n"
    1027             :                 "        %s [-U username] [-P password]\n"
    1028             :                 "        [-S servername] [-D database]\n"
    1029             :                 "        [-i input filename] [-o output filename]\n"
    1030             :                 "        [owner.]object_name [[owner.]object_name...]\n",
    1031             :                 invoked_as, invoked_as);
    1032             : }
    1033             : 
    1034             : static LOGINREC *
    1035         120 : get_login(char *argv0, OPTIONS *options)
    1036             : {
    1037             :         LOGINREC *login;
    1038             : 
    1039         120 :         assert(options);
    1040             : 
    1041         120 :         options->appname = basename(argv0);
    1042             : 
    1043         120 :         login = dblogin();
    1044             : 
    1045         120 :         if (!login) {
    1046           0 :                 fprintf(stderr, "%s: unable to allocate login structure\n", options->appname);
    1047           0 :                 return NULL;
    1048             :         }
    1049             : 
    1050         120 :         DBSETLAPP(login, options->appname);
    1051             : 
    1052         120 :         if (-1 != gethostname(options->hostname, sizeof(options->hostname))) {
    1053         120 :                 DBSETLHOST(login, options->hostname);
    1054             :         }
    1055             :         return login;
    1056             : }
    1057             : 
    1058             : static int
    1059         120 : process_args(LOGINREC *login, int argc, char **argv, OPTIONS *options)
    1060             : {
    1061             :         char *password;
    1062             :         int ch;
    1063         120 :         int fdomain = TRUE;
    1064             : 
    1065             :         extern char *optarg;
    1066             :         extern int optind;
    1067             : 
    1068         120 :         if (!login)
    1069             :                 return EXIT_FAILURE;
    1070             : 
    1071         570 :         while ((ch = getopt(argc, argv, "U:P:S:d:D:i:o:v")) != -1) {
    1072         450 :                 switch (ch) {
    1073         120 :                 case 'U':
    1074         120 :                         DBSETLUSER(login, optarg);
    1075         120 :                         fdomain = FALSE;
    1076         120 :                         break;
    1077         120 :                 case 'P':
    1078         120 :                         password = tds_getpassarg(optarg);
    1079         120 :                         if (!password) {
    1080           0 :                                 fprintf(stderr, "Error getting password\n");
    1081           0 :                                 return EXIT_FAILURE;
    1082             :                         }
    1083         120 :                         DBSETLPWD(login, password);
    1084             :                         memset(password, 0, strlen(password));
    1085         120 :                         free(password);
    1086         120 :                         password = NULL;
    1087         120 :                         fdomain = FALSE;
    1088         120 :                         break;
    1089         120 :                 case 'S':
    1090         120 :                         options->servername = xstrdup(optarg);
    1091         120 :                         break;
    1092          70 :                 case 'd':
    1093             :                 case 'D':
    1094          70 :                         options->database = xstrdup(optarg);
    1095          70 :                         break;
    1096          10 :                 case 'i':
    1097          10 :                         options->input_filename = xstrdup(optarg);
    1098          10 :                         break;
    1099          10 :                 case 'o':
    1100          10 :                         options->output_filename = xstrdup(optarg);
    1101          10 :                         break;
    1102           0 :                 case 'v':
    1103           0 :                         printf("%s\n\n%s", argv[0],
    1104             :                                 "Copyright (C) 2004-2011  James K. Lowden\n\n"
    1105             :                                 "This program  is free software; you can redistribute it and/or\n"
    1106             :                                 "modify it under the terms of the GNU General Public\n"
    1107             :                                 "License as published by the Free Software Foundation; either\n"
    1108             :                                 "version 2 of the License, or (at your option) any later version.\n\n"
    1109             :                                 "This library is distributed in the hope that it will be useful,\n"
    1110             :                                 "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
    1111             :                                 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
    1112             :                                 "Library General Public License for more details.\n\n"
    1113             :                                 "You should have received a copy of the GNU General Public\n"
    1114             :                                 "License along with this library; if not, write to the\n"
    1115             :                                 "Free Software Foundation, Inc., 59 Temple Place - Suite 330,\n"
    1116             :                                 "Boston, MA 02111-1307, USA.\n");
    1117           0 :                         return EXIT_FAILURE;
    1118             :                 case '?':
    1119             :                 default:
    1120             :                         return EXIT_FAILURE;
    1121             :                 }
    1122             :         }
    1123             : 
    1124             : #if defined(MicrosoftsDbLib) && defined(_WIN32)
    1125             :         if (fdomain)
    1126             :                 DBSETLSECURE(login);
    1127             : #else
    1128         120 :         if (fdomain)
    1129           0 :                 DBSETLNETWORKAUTH(login, TRUE);
    1130             : #endif /* MicrosoftsDbLib */
    1131             : 
    1132             :         /* Server name must be present */
    1133         120 :         if (!options->servername)
    1134             :                 return EXIT_FAILURE;
    1135             : 
    1136             :         /* Does not make sense to specify both input and output filenames */
    1137         120 :         if (options->input_filename && options->output_filename) {
    1138           0 :                 fprintf(stderr, "%s: Cannot specify both input and output files.\n", options->appname);
    1139           0 :                 return EXIT_FAILURE;
    1140             :         }
    1141             : 
    1142             :         /* We support two different syntaxes: Sybase, and FreeTDS traditional.
    1143             :          * We will take the presence of -D , -i, or -o as meaning FreeTDS syntax;
    1144             :          * "in" or "out" as Sybase syntax; and if all are absent, treat it as
    1145             :          * FreeTDS syntax which allows -i and -o to be omitted, meaning that
    1146             :          * stdin/stdout should be used.
    1147             :          */
    1148         120 :         if (!(options->database || options->input_filename || options->output_filename)
    1149          50 :             && optind + 3 <= argc) {
    1150             :                 /* If we are using Sybase syntax, then we're going to consume 3
    1151             :                  * non-switch arguments: direction, filename, and database name.
    1152             :                  */
    1153             : 
    1154          50 :                 if (strncasecmp(argv[optind], "in", 2) == 0) {
    1155          20 :                         optind++;       /* consume direction */
    1156          20 :                         options->input_filename = xstrdup(argv[optind]);
    1157          20 :                         optind++;       /* consume filename */
    1158          20 :                         options->sybase_syntax = true;
    1159          30 :                 } else if (strncasecmp(argv[optind], "out", 3) == 0) {
    1160          30 :                         optind++;       /* consume direction */
    1161          30 :                         options->output_filename = xstrdup(argv[optind]);
    1162          30 :                         optind++;       /* consume filename */
    1163          30 :                         options->sybase_syntax = true;
    1164             :                 }
    1165          50 :                 if (options->sybase_syntax) {
    1166          50 :                         options->database = xstrdup(argv[optind]);
    1167          50 :                         optind++;       /* consume database name */
    1168             :                 }
    1169             :         }
    1170             : 
    1171             :         /* Does not make sense to request input but provide objects;
    1172             :          * or request output but not provide them */
    1173         120 :         if (options->input_filename && optind < argc) {
    1174           0 :                 fprintf(stderr, "%s: Cannot specify both input file and objects to output.\n", options->appname);
    1175           0 :                 return EXIT_FAILURE;
    1176             :         }
    1177         120 :         if (options->output_filename && optind == argc) {
    1178           0 :                 fprintf(stderr, "%s: Output mode specified, but no object names given.\n", options->appname);
    1179           0 :                 return EXIT_FAILURE;
    1180             :         }
    1181             : 
    1182             :         /* optind should now point at the objects, if any, to read. */
    1183         120 :         options->optind = optind;
    1184         120 :         return 0;
    1185             : }
    1186             : 
    1187             : static int
    1188             : #ifndef MicrosoftsDbLib
    1189           0 : err_handler(DBPROCESS * dbproc TDS_UNUSED, int severity, int dberr, int oserr TDS_UNUSED,
    1190             :             char *dberrstr, char *oserrstr TDS_UNUSED)
    1191             : #else
    1192             : err_handler(DBPROCESS * dbproc TDS_UNUSED, int severity, int dberr, int oserr TDS_UNUSED,
    1193             :             const char dberrstr[], const char oserrstr[] TDS_UNUSED)
    1194             : #endif /* MicrosoftsDbLib */
    1195             : {
    1196             : 
    1197           0 :         if (dberr) {
    1198           0 :                 fprintf(stderr, "%s: Msg %d, Level %d\n", options.appname, dberr, severity);
    1199           0 :                 fprintf(stderr, "%s\n\n", dberrstr);
    1200             :         } else {
    1201           0 :                 fprintf(stderr, "%s: DB-LIBRARY error:\n\t", options.appname);
    1202           0 :                 fprintf(stderr, "%s\n", dberrstr);
    1203             :         }
    1204             : 
    1205           0 :         return INT_CANCEL;
    1206             : }
    1207             : 
    1208             : static int
    1209             : #ifndef MicrosoftsDbLib
    1210        1164 : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, char *msgtext,
    1211             :             char *srvname TDS_UNUSED, char *procname TDS_UNUSED, int line TDS_UNUSED)
    1212             : #else
    1213             : msg_handler(DBPROCESS * dbproc TDS_UNUSED, DBINT msgno, int msgstate TDS_UNUSED, int severity TDS_UNUSED, const char msgtext[],
    1214             :             const char srvname[] TDS_UNUSED, const char procname[] TDS_UNUSED, unsigned short int line TDS_UNUSED)
    1215             : #endif /* MicrosoftsDbLib */
    1216             : {
    1217             :         char *dbname, *endquote;
    1218             : 
    1219        1164 :         switch (msgno) {
    1220         144 :         case 5701: /* Print "USE dbname" for "Changed database context to 'dbname'" */
    1221         144 :                 dbname = strchr(msgtext, '\'');
    1222         144 :                 if (!dbname)
    1223             :                         break;
    1224         144 :                 endquote = strchr(++dbname, '\'');
    1225         144 :                 if (!endquote)
    1226             :                         break;
    1227         144 :                 *endquote = '\0';
    1228         144 :                 sprintf(use_statement, "USE %s\nGO\n\n", quote_id(dbname));
    1229             :                 tmp_free();
    1230             :                 return 0;
    1231             : 
    1232             :         case 0: /* Ignore print messages */
    1233             :         case 5703:      /* Ignore "Changed language setting to <language>". */
    1234             :                 return 0;
    1235             : 
    1236             :         default:
    1237             :                 break;
    1238             :         }
    1239             : 
    1240             : #if 0
    1241             :         printf("Msg %ld, Severity %d, State %d\n", (long) msgno, severity, msgstate);
    1242             : 
    1243             :         if (strlen(srvname) > 0)
    1244             :                 printf("Server '%s', ", srvname);
    1245             :         if (strlen(procname) > 0)
    1246             :                 printf("Procedure '%s', ", procname);
    1247             :         if (line > 0)
    1248             :                 printf("Line %d", line);
    1249             : #endif
    1250           0 :         printf("\t/* %s */\n", msgtext);
    1251             : 
    1252         248 :         return 0;
    1253             : }

Generated by: LCOV version 1.13