LCOV - code coverage report
Current view: top level - clients/mapiclient - mclient.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 948 1902 49.8 %
Date: 2021-10-13 02:24:04 Functions: 28 40 70.0 %

          Line data    Source code
       1             : /*
       2             :  * This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       5             :  *
       6             :  * Copyright 1997 - July 2008 CWI, August 2008 - 2021 MonetDB B.V.
       7             :  */
       8             : 
       9             : /* The Mapi Client Interface
      10             :  * A textual interface to the Monet server using the Mapi library,
      11             :  * providing command-line access for its users. It is the preferred
      12             :  * interface for non-DBAs.
      13             :  * See mclient.1 for usage information.
      14             :  */
      15             : 
      16             : #include "monetdb_config.h"
      17             : #ifndef HAVE_GETOPT_LONG
      18             : #  include "monet_getopt.h"
      19             : #else
      20             : # ifdef HAVE_GETOPT_H
      21             : #  include "getopt.h"
      22             : # endif
      23             : #endif
      24             : #include "mapi.h"
      25             : #include <unistd.h>
      26             : #include <string.h>
      27             : #ifdef HAVE_STRINGS_H
      28             : #include <strings.h>              /* strcasecmp */
      29             : #endif
      30             : #include <sys/stat.h>
      31             : 
      32             : #ifdef HAVE_LIBREADLINE
      33             : #include <readline/readline.h>
      34             : #include <readline/history.h>
      35             : #include "ReadlineTools.h"
      36             : #endif
      37             : #include "stream.h"
      38             : #include "msqldump.h"
      39             : #define LIBMUTILS 1
      40             : #include "mprompt.h"
      41             : #include "mutils.h"           /* mercurial_revision */
      42             : #include "dotmonetdb.h"
      43             : 
      44             : #include <locale.h>
      45             : 
      46             : #ifdef HAVE_ICONV
      47             : #include <iconv.h>
      48             : #ifdef HAVE_NL_LANGINFO
      49             : #include <langinfo.h>
      50             : #endif
      51             : #endif
      52             : 
      53             : #ifndef S_ISCHR
      54             : #define S_ISCHR(m)      (((m) & S_IFMT) == S_IFCHR)
      55             : #endif
      56             : #ifndef S_ISREG
      57             : #define S_ISREG(m)      (((m) & S_IFMT) == S_IFREG)
      58             : #endif
      59             : 
      60             : enum modes {
      61             :         MAL,
      62             :         SQL
      63             : };
      64             : 
      65             : static enum modes mode = SQL;
      66             : static stream *toConsole;
      67             : static stream *stdout_stream;
      68             : static stream *stderr_stream;
      69             : static stream *fromConsole = NULL;
      70             : static char *language = NULL;
      71             : static char *logfile = NULL;
      72             : static char promptbuf[16];
      73             : static bool echoquery = false;
      74             : #ifdef HAVE_ICONV
      75             : static char *encoding;
      76             : #endif
      77             : static bool errseen = false;
      78             : static bool allow_remote = false;
      79             : 
      80             : #define setPrompt() snprintf(promptbuf, sizeof(promptbuf), "%.*s>", (int) sizeof(promptbuf) - 2, language)
      81             : #define debugMode() (strncmp(promptbuf, "mdb", 3) == 0)
      82             : 
      83             : /* the internal result set formatters */
      84             : enum formatters {
      85             :         NOformatter,
      86             :         RAWformatter,           // as the data is received
      87             :         TABLEformatter,         // render as a bordered table
      88             :         CSVformatter,           // render as a comma or tab separated values list
      89             :         XMLformatter,           // render as a valid XML document
      90             :         TESTformatter,          // for testing, escape characters
      91             :         TRASHformatter,         // remove the result set
      92             :         ROWCOUNTformatter,      // only print the number of rows returned
      93             :         EXPANDEDformatter       // render as multi-row single record
      94             : };
      95             : static enum formatters formatter = NOformatter;
      96             : char *separator = NULL;         /* column separator for CSV/TAB format */
      97             : bool csvheader = false;         /* include header line in CSV format */
      98             : bool noquote = false;           /* don't use quotes in CSV format */
      99             : 
     100             : #define DEFWIDTH 80
     101             : 
     102             : /* use a 64 bit integer for the timer */
     103             : typedef int64_t timertype;
     104             : 
     105             : static timertype t0, t1;        /* used for timing */
     106             : 
     107             : #define UTF8BOM         "\xEF\xBB\xBF"        /* UTF-8 encoding of Unicode BOM */
     108             : #define UTF8BOMLENGTH   3       /* length of above */
     109             : 
     110             : /* Pagination and simple ASCII-based rendering is provided for SQL
     111             :  * sessions. The result set size is limited by the cache size of the
     112             :  * Mapi Library. It is sufficiently large to accommodate most result
     113             :  * to be browsed manually.
     114             :  *
     115             :  * The pagewidth determines the maximum space allocated for a single
     116             :  * row. If the total space required is larger, then a heuristic
     117             :  * routine is called to distribute the available space. Attribute
     118             :  * values may then span multiple lines. Setting the pagewidth to 0
     119             :  * turns off row size control. */
     120             : 
     121             : #ifdef HAVE_POPEN
     122             : static char *pager = 0;         /* use external pager */
     123             : #endif
     124             : #ifdef HAVE_SIGACTION
     125             : #include <signal.h>               /* to block SIGPIPE */
     126             : #endif
     127             : static int rowsperpage = 0;     /* for SQL pagination */
     128             : static int pagewidth = 0;       /* -1: take whatever is necessary, >0: limit */
     129             : static bool pagewidthset = false; /* whether the user set the width explicitly */
     130             : static int croppedfields = 0;   /* whatever got cropped/truncated */
     131             : static bool firstcrop = true;   /* first time we see cropping/truncation */
     132             : 
     133             : enum modifiers {
     134             :         NOmodifier,
     135             :         DEBUGmodifier
     136             : };
     137             : static enum modifiers specials = NOmodifier;
     138             : /* set when we see DEBUG (only if mode == SQL).  Also retain these
     139             :  * modes until after you have received the answer. */
     140             : 
     141             : /* keep these aligned, the MINCOLSIZE should ensure you can always
     142             :  * write the NULLSTRING */
     143             : #define MINCOLSIZE 4
     144             : static char default_nullstring[] = "null";
     145             : static char *nullstring = default_nullstring;
     146             : /* this is the minimum size (that still makes some sense) for writing
     147             :  * variable length columns */
     148             : #define MINVARCOLSIZE 10
     149             : 
     150             : #include <time.h>
     151             : #ifdef HAVE_FTIME
     152             : #include <sys/timeb.h>            /* ftime */
     153             : #endif
     154             : #ifdef HAVE_SYS_TIME_H
     155             : #include <sys/time.h>             /* gettimeofday */
     156             : #endif
     157             : #ifdef HAVE_STROPTS_H
     158             : #include <stropts.h>              /* ioctl on Solaris */
     159             : #endif
     160             : #ifdef HAVE_SYS_IOCTL_H
     161             : #include <sys/ioctl.h>
     162             : #endif
     163             : #ifdef HAVE_TERMIOS_H
     164             : #include <termios.h>              /* TIOCGWINSZ/TIOCSWINSZ */
     165             : #endif
     166             : 
     167             : #if defined(_MSC_VER) && _MSC_VER >= 1400
     168             : #define fileno _fileno
     169             : #endif
     170             : 
     171             : #define my_isspace(c)   ((c) == '\f' || (c) == '\n' || (c) == ' ')
     172             : 
     173             : #include <ctype.h>
     174             : #include "mhelp.h"
     175             : 
     176             : static timertype
     177      290558 : gettime(void)
     178             : {
     179             :         /* Return the time in milliseconds since an epoch.  The epoch
     180             :            is roughly the time this program started. */
     181             : #ifdef _MSC_VER
     182             :         static LARGE_INTEGER freq, start;       /* automatically initialized to 0 */
     183             :         LARGE_INTEGER ctr;
     184             : 
     185             :         if (start.QuadPart == 0 &&
     186             :             (!QueryPerformanceFrequency(&freq) ||
     187             :              !QueryPerformanceCounter(&start)))
     188             :                 start.QuadPart = -1;
     189             :         if (start.QuadPart > 0) {
     190             :                 QueryPerformanceCounter(&ctr);
     191             :                 return (timertype) (((ctr.QuadPart - start.QuadPart) * 1000000) / freq.QuadPart);
     192             :         }
     193             : #endif
     194             : #ifdef HAVE_GETTIMEOFDAY
     195             :         {
     196             :                 static struct timeval tpbase;   /* automatically initialized to 0 */
     197             :                 struct timeval tp;
     198             : 
     199      290558 :                 if (tpbase.tv_sec == 0)
     200         183 :                         gettimeofday(&tpbase, NULL);
     201      290558 :                 gettimeofday(&tp, NULL);
     202      290558 :                 tp.tv_sec -= tpbase.tv_sec;
     203      290558 :                 return (timertype) tp.tv_sec * 1000000 + (timertype) tp.tv_usec;
     204             :         }
     205             : #else
     206             : #ifdef HAVE_FTIME
     207             :         {
     208             :                 static struct timeb tbbase;     /* automatically initialized to 0 */
     209             :                 struct timeb tb;
     210             : 
     211             :                 if (tbbase.time == 0)
     212             :                         ftime(&tbbase);
     213             :                 ftime(&tb);
     214             :                 tb.time -= tbbase.time;
     215             :                 return (timertype) tb.time * 1000000 + (timertype) tb.millitm * 1000;
     216             :         }
     217             : #endif  /* HAVE_FTIME */
     218             : #endif  /* HAVE_GETTIMEOFDAY */
     219             : }
     220             : 
     221             : static void
     222             : timerStart(void)
     223             : {
     224        5622 :         t0 = gettime();
     225           6 : }
     226             : 
     227             : static void
     228      141470 : timerPause(void)
     229             : {
     230      141470 :         t1 = gettime();
     231      141470 :         if (t0 == 0)
     232         164 :                 t0 = t1;
     233      141470 : }
     234             : 
     235             : static void
     236      132217 : timerResume(void)
     237             : {
     238      132217 :         if (t1 == 0)
     239           0 :                 t1 = gettime();
     240      132217 :         assert(t1 >= t0);
     241      132217 :         t0 = gettime() - (t1 - t0);
     242      132217 : }
     243             : 
     244             : static void
     245        5622 : timerEnd(void)
     246             : {
     247        5622 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     248        5622 :         t1 = gettime();
     249        5622 :         assert(t1 >= t0);
     250        5622 : }
     251             : 
     252             : static timertype th = 0;
     253             : static void
     254             : timerHumanStop(void)
     255             : {
     256        5627 :         th = gettime();
     257             : }
     258             : 
     259             : static enum itimers {
     260             :         T_NONE = 0,     // don't render the timing information
     261             :         T_CLOCK,        // render wallclock time in human readable format
     262             :         T_PERF          // return detailed performance
     263             : } timermode = T_NONE;
     264             : 
     265             : static bool timerHumanCalled = false;
     266             : static void
     267        9822 : timerHuman(int64_t sqloptimizer, int64_t maloptimizer, int64_t querytime, bool singleinstr, bool total)
     268             : {
     269        9822 :         timertype t = th - t0;
     270             : 
     271        9822 :         timerHumanCalled = true;
     272             : 
     273             :         /*
     274             :          * report only the times we do actually measure:
     275             :          * - client-measured wall-clock time per query only when executing individual queries,
     276             :          *   otherwise only the total wall-clock time at the end of a batch;
     277             :          * - server-measured detailed performance measures only per query.
     278             :          */
     279             : 
     280             :         /* "(singleinstr != total)" is C for (logical) "(singleinstr XOR total)" */
     281        9822 :         if (timermode == T_CLOCK && (singleinstr != total)) {
     282             :                 /* print wall-clock in "human-friendly" format */
     283           0 :                 fflush(stderr);
     284           0 :                 mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     285           0 :                 if (t / 1000 < 1000) {
     286           0 :                         fprintf(stderr, "clk: %" PRId64 ".%03d ms\n", t / 1000, (int) (t % 1000));
     287           0 :                         fflush(stderr);
     288           0 :                         return;
     289             :                 }
     290           0 :                 t /= 1000;
     291           0 :                 if (t / 1000 < 60) {
     292           0 :                         fprintf(stderr, "clk: %" PRId64 ".%03d sec\n", t / 1000, (int) (t % 1000));
     293           0 :                         fflush(stderr);
     294           0 :                         return;
     295             :                 }
     296           0 :                 t /= 1000;
     297           0 :                 if (t / 60 < 60) {
     298           0 :                         fprintf(stderr, "clk: %" PRId64 ":%02d min\n", t / 60, (int) (t % 60));
     299           0 :                         fflush(stderr);
     300           0 :                         return;
     301             :                 }
     302           0 :                 t /= 60;
     303           0 :                 fprintf(stderr, "clk: %" PRId64 ":%02d h\n", t / 60, (int) (t % 60));
     304           0 :                 fflush(stderr);
     305           0 :                 return;
     306             :         }
     307        9822 :         if (timermode == T_PERF && (!total || singleinstr != total)) {
     308             :                 /* for performance measures we use milliseconds as the base */
     309           0 :                 fflush(stderr);
     310           0 :                 mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     311           0 :                 if (!total)
     312           0 :                         fprintf(stderr, "sql:%" PRId64 ".%03d opt:%" PRId64 ".%03d run:%" PRId64 ".%03d ",
     313           0 :                                  sqloptimizer / 1000, (int) (sqloptimizer % 1000),
     314           0 :                                  maloptimizer / 1000, (int) (maloptimizer % 1000),
     315           0 :                                  querytime / 1000, (int) (querytime % 1000));
     316           0 :                 if (singleinstr != total)
     317           0 :                         fprintf(stderr, "clk:%" PRId64 ".%03d ", t / 1000, (int) (t % 1000));
     318           0 :                 fprintf(stderr, "ms\n");
     319           0 :                 fflush(stderr);
     320           0 :                 return;
     321             :         }
     322             :         return;
     323             : }
     324             : 
     325             : /* The Mapi library eats away the comment lines, which we need to
     326             :  * detect end of debugging. We overload the routine to our liking. */
     327             : 
     328             : static char *
     329       95606 : fetch_line(MapiHdl hdl)
     330             : {
     331             :         char *reply;
     332             : 
     333       95606 :         if ((reply = mapi_fetch_line(hdl)) == NULL)
     334             :                 return NULL;
     335       91610 :         if (strncmp(reply, "mdb>#", 5) == 0) {
     336           0 :                 if (strncmp(reply, "mdb>#EOD", 8) == 0)
     337           0 :                         setPrompt();
     338             :                 else
     339           0 :                         snprintf(promptbuf, sizeof(promptbuf), "mdb>");
     340             :         }
     341             :         return reply;
     342             : }
     343             : 
     344             : static int
     345        8980 : fetch_row(MapiHdl hdl)
     346             : {
     347             :         char *reply;
     348             : 
     349             :         do {
     350       10260 :                 if ((reply = fetch_line(hdl)) == NULL)
     351             :                         return 0;
     352        9940 :         } while (*reply != '[' && *reply != '=');
     353        8660 :         return mapi_split_line(hdl);
     354             : }
     355             : 
     356             : static void
     357      137436 : SQLsetSpecial(const char *command)
     358             : {
     359      137436 :         if (mode == SQL && command && specials == NOmodifier) {
     360             :                 /* catch the specials for better rendering */
     361      141356 :                 while (*command == ' ' || *command == '\t')
     362        3920 :                         command++;
     363      137436 :                 if (strncmp(command, "debug", 5) == 0)
     364           0 :                         specials = DEBUGmodifier;
     365             :                 else
     366      137436 :                         specials = NOmodifier;
     367             :         }
     368      137436 : }
     369             : 
     370             : /* return the display length of a UTF-8 string
     371             :    if e is not NULL, return length up to e */
     372             : static size_t
     373          12 : utf8strlenmax(char *s, char *e, size_t max, char **t)
     374             : {
     375             :         size_t len = 0, len0 = 0;
     376             :         int c;
     377             :         int n;
     378             :         char *t0 = s;
     379             : 
     380          12 :         assert(max == 0 || t != NULL);
     381          12 :         if (s == NULL)
     382             :                 return 0;
     383             :         c = 0;
     384             :         n = 0;
     385         204 :         while (*s != 0 && (e == NULL || s < e)) {
     386         192 :                 if (*s == '\n') {
     387           0 :                         assert(n == 0);
     388           0 :                         if (max) {
     389           0 :                                 *t = s;
     390           0 :                                 return len;
     391             :                         }
     392           0 :                         len++;
     393             :                         n = 0;
     394         192 :                 } else if (*s == '\t') {
     395           0 :                         assert(n == 0);
     396           0 :                         len++;  /* rendered as single space */
     397             :                         n = 0;
     398         192 :                 } else if ((unsigned char) *s <= 0x1F || *s == '\177') {
     399           0 :                         assert(n == 0);
     400           0 :                         len += 4;
     401             :                         n = 0;
     402         192 :                 } else if ((*s & 0x80) == 0) {
     403         156 :                         assert(n == 0);
     404         156 :                         len++;
     405             :                         n = 0;
     406          36 :                 } else if ((*s & 0xC0) == 0x80) {
     407          18 :                         c = (c << 6) | (*s & 0x3F);
     408          18 :                         if (--n == 0) {
     409             :                                 /* last byte of a multi-byte character */
     410          18 :                                 len++;
     411             :                                 /* this list was created by combining
     412             :                                  * the code points marked as
     413             :                                  * Emoji_Presentation in
     414             :                                  * /usr/share/unicode/emoji/emoji-data.txt
     415             :                                  * and code points marked either F or
     416             :                                  * W in EastAsianWidth.txt; this list
     417             :                                  * is up-to-date with Unicode 11.0 */
     418          18 :                                 if ((0x1100 <= c && c <= 0x115F) ||
     419          18 :                                     (0x231A <= c && c <= 0x231B) ||
     420          18 :                                     (0x2329 <= c && c <= 0x232A) ||
     421          18 :                                     (0x23E9 <= c && c <= 0x23EC) ||
     422          18 :                                     c == 0x23F0 ||
     423          18 :                                     c == 0x23F3 ||
     424          18 :                                     (0x25FD <= c && c <= 0x25FE) ||
     425          18 :                                     (0x2614 <= c && c <= 0x2615) ||
     426          18 :                                     (0x2648 <= c && c <= 0x2653) ||
     427          18 :                                     c == 0x267F ||
     428          18 :                                     c == 0x2693 ||
     429          18 :                                     c == 0x26A1 ||
     430          18 :                                     (0x26AA <= c && c <= 0x26AB) ||
     431          18 :                                     (0x26BD <= c && c <= 0x26BE) ||
     432          18 :                                     (0x26C4 <= c && c <= 0x26C5) ||
     433          18 :                                     c == 0x26CE ||
     434          18 :                                     c == 0x26D4 ||
     435          18 :                                     c == 0x26EA ||
     436          18 :                                     (0x26F2 <= c && c <= 0x26F3) ||
     437          18 :                                     c == 0x26F5 ||
     438          18 :                                     c == 0x26FA ||
     439          18 :                                     c == 0x26FD ||
     440          18 :                                     c == 0x2705 ||
     441          18 :                                     (0x270A <= c && c <= 0x270B) ||
     442          18 :                                     c == 0x2728 ||
     443          18 :                                     c == 0x274C ||
     444          18 :                                     c == 0x274E ||
     445          18 :                                     (0x2753 <= c && c <= 0x2755) ||
     446          18 :                                     c == 0x2757 ||
     447          18 :                                     (0x2795 <= c && c <= 0x2797) ||
     448          18 :                                     c == 0x27B0 ||
     449          18 :                                     c == 0x27BF ||
     450          18 :                                     (0x2B1B <= c && c <= 0x2B1C) ||
     451          18 :                                     c == 0x2B50 ||
     452          18 :                                     c == 0x2B55 ||
     453          18 :                                     (0x2E80 <= c && c <= 0x2E99) ||
     454          18 :                                     (0x2E9B <= c && c <= 0x2EF3) ||
     455          18 :                                     (0x2F00 <= c && c <= 0x2FD5) ||
     456          18 :                                     (0x2FF0 <= c && c <= 0x2FFB) ||
     457          18 :                                     (0x3000 <= c && c <= 0x303E) ||
     458          18 :                                     (0x3041 <= c && c <= 0x3096) ||
     459          18 :                                     (0x3099 <= c && c <= 0x30FF) ||
     460          18 :                                     (0x3105 <= c && c <= 0x312F) ||
     461          18 :                                     (0x3131 <= c && c <= 0x318E) ||
     462          18 :                                     (0x3190 <= c && c <= 0x31BA) ||
     463          18 :                                     (0x31C0 <= c && c <= 0x31E3) ||
     464          18 :                                     (0x31F0 <= c && c <= 0x321E) ||
     465          18 :                                     (0x3220 <= c && c <= 0x3247) ||
     466          18 :                                     (0x3250 <= c && c <= 0x32FE) ||
     467          18 :                                     (0x3300 <= c && c <= 0x4DBF) ||
     468          18 :                                     (0x4E00 <= c && c <= 0xA48C) ||
     469          18 :                                     (0xA490 <= c && c <= 0xA4C6) ||
     470          18 :                                     (0xA960 <= c && c <= 0xA97C) ||
     471          18 :                                     (0xAC00 <= c && c <= 0xD7A3) ||
     472          18 :                                     (0xF900 <= c && c <= 0xFAFF) ||
     473          18 :                                     (0xFE10 <= c && c <= 0xFE19) ||
     474          18 :                                     (0xFE30 <= c && c <= 0xFE52) ||
     475          18 :                                     (0xFE54 <= c && c <= 0xFE66) ||
     476          18 :                                     (0xFE68 <= c && c <= 0xFE6B) ||
     477          18 :                                     (0xFF01 <= c && c <= 0xFF60) ||
     478          18 :                                     (0xFFE0 <= c && c <= 0xFFE6) ||
     479          18 :                                     (0x16FE0 <= c && c <= 0x16FE1) ||
     480          18 :                                     (0x17000 <= c && c <= 0x187F1) ||
     481          18 :                                     (0x18800 <= c && c <= 0x18AF2) ||
     482          18 :                                     (0x1B000 <= c && c <= 0x1B11E) ||
     483          18 :                                     (0x1B170 <= c && c <= 0x1B2FB) ||
     484          18 :                                     c == 0x1F004 ||
     485          18 :                                     c == 0x1F0CF ||
     486          18 :                                     c == 0x1F18E ||
     487          18 :                                     (0x1F191 <= c && c <= 0x1F19A) ||
     488          18 :                                     (0x1F200 <= c && c <= 0x1F202) ||
     489          18 :                                     (0x1F210 <= c && c <= 0x1F23B) ||
     490          18 :                                     (0x1F240 <= c && c <= 0x1F248) ||
     491          18 :                                     (0x1F250 <= c && c <= 0x1F251) ||
     492          18 :                                     (0x1F260 <= c && c <= 0x1F265) ||
     493          18 :                                     (0x1F300 <= c && c <= 0x1F320) ||
     494          18 :                                     (0x1F32D <= c && c <= 0x1F335) ||
     495          18 :                                     (0x1F337 <= c && c <= 0x1F37C) ||
     496          18 :                                     (0x1F37E <= c && c <= 0x1F393) ||
     497          18 :                                     (0x1F3A0 <= c && c <= 0x1F3CA) ||
     498          18 :                                     (0x1F3CF <= c && c <= 0x1F3D3) ||
     499          18 :                                     (0x1F3E0 <= c && c <= 0x1F3F0) ||
     500          18 :                                     c == 0x1F3F4 ||
     501          18 :                                     (0x1F3F8 <= c && c <= 0x1F43E) ||
     502          18 :                                     c == 0x1F440 ||
     503          18 :                                     (0x1F442 <= c && c <= 0x1F4FC) ||
     504          18 :                                     (0x1F4FF <= c && c <= 0x1F53D) ||
     505          18 :                                     (0x1F54B <= c && c <= 0x1F54E) ||
     506          18 :                                     (0x1F550 <= c && c <= 0x1F567) ||
     507          18 :                                     c == 0x1F57A ||
     508          18 :                                     (0x1F595 <= c && c <= 0x1F596) ||
     509          18 :                                     c == 0x1F5A4 ||
     510          18 :                                     (0x1F5FB <= c && c <= 0x1F64F) ||
     511          18 :                                     (0x1F680 <= c && c <= 0x1F6C5) ||
     512          18 :                                     c == 0x1F6CC ||
     513          18 :                                     (0x1F6D0 <= c && c <= 0x1F6D2) ||
     514          18 :                                     (0x1F6EB <= c && c <= 0x1F6EC) ||
     515          18 :                                     (0x1F6F4 <= c && c <= 0x1F6F9) ||
     516          18 :                                     (0x1F910 <= c && c <= 0x1F93E) ||
     517          18 :                                     (0x1F940 <= c && c <= 0x1F970) ||
     518          18 :                                     (0x1F973 <= c && c <= 0x1F976) ||
     519          18 :                                     c == 0x1F97A ||
     520          18 :                                     (0x1F97C <= c && c <= 0x1F9A2) ||
     521          18 :                                     (0x1F9B0 <= c && c <= 0x1F9B9) ||
     522          18 :                                     (0x1F9C0 <= c && c <= 0x1F9C2) ||
     523          18 :                                     (0x1F9D0 <= c && c <= 0x1F9FF) ||
     524          18 :                                     (0x20000 <= c && c <= 0x2FFFD) ||
     525          18 :                                     (0x30000 <= c && c <= 0x3FFFD))
     526           0 :                                         len++;
     527          18 :                                 else if (0x0080 <= c && c <= 0x009F)
     528           0 :                                         len += 5;
     529             : 
     530             :                         }
     531          18 :                 } else if ((*s & 0xE0) == 0xC0) {
     532          18 :                         assert(n == 0);
     533             :                         n = 1;
     534          18 :                         c = *s & 0x1F;
     535           0 :                 } else if ((*s & 0xF0) == 0xE0) {
     536           0 :                         assert(n == 0);
     537             :                         n = 2;
     538           0 :                         c = *s & 0x0F;
     539           0 :                 } else if ((*s & 0xF8) == 0xF0) {
     540           0 :                         assert(n == 0);
     541             :                         n = 3;
     542           0 :                         c = *s & 0x07;
     543           0 :                 } else if ((*s & 0xFC) == 0xF8) {
     544           0 :                         assert(n == 0);
     545             :                         n = 4;
     546           0 :                         c = *s & 0x03;
     547             :                 } else {
     548           0 :                         assert(0);
     549             :                         n = 0;
     550             :                 }
     551         192 :                 s++;
     552         192 :                 if (n == 0) {
     553         174 :                         if (max != 0) {
     554           0 :                                 if (len > max) {
     555           0 :                                         *t = t0;
     556           0 :                                         return len0;
     557             :                                 }
     558           0 :                                 if (len == max) {
     559           0 :                                         *t = s;
     560           0 :                                         return len;
     561             :                                 }
     562             :                         }
     563             :                         t0 = s;
     564             :                         len0 = len;
     565             :                 }
     566             :         }
     567          12 :         if (max != 0)
     568           0 :                 *t = s;
     569             :         return len;
     570             : }
     571             : 
     572             : static size_t
     573             : utf8strlen(char *s, char *e)
     574             : {
     575          12 :         return utf8strlenmax(s, e, 0, NULL);
     576             : }
     577             : 
     578             : /* skip the specified number of UTF-8 characters, but stop at a newline */
     579             : static char *
     580             : utf8skip(char *s, size_t i)
     581             : {
     582           0 :         utf8strlenmax(s, NULL, i, &s);
     583           0 :         return s;
     584             : }
     585             : 
     586             : static int
     587           9 : SQLrow(int *len, int *numeric, char **rest, int fields, int trim, char wm)
     588             : {
     589             :         int i;
     590             :         bool more, first = true;
     591             :         char *t;
     592             :         int rows = 0;           /* return number of output lines printed */
     593             :         size_t ulen;
     594           9 :         int *cutafter = malloc(sizeof(int) * fields);
     595             : 
     596           9 :         if (cutafter == NULL) {
     597           0 :                 fprintf(stderr,"Malloc for SQLrow failed");
     598           0 :                 exit(2);
     599             :         }
     600             :         /* trim the text if needed */
     601           9 :         if (trim == 1) {
     602           6 :                 for (i = 0; i < fields; i++) {
     603           3 :                         if ((t = rest[i]) != NULL &&
     604           3 :                             utf8strlen(t, NULL) > (size_t) len[i]) {
     605             :                                 /* eat leading whitespace */
     606           0 :                                 while (*t != 0 && my_isspace(*t))
     607           0 :                                         t++;
     608           0 :                                 rest[i] = t;
     609             :                         }
     610             :                 }
     611             :         }
     612             : 
     613          18 :         for (i = 0; i < fields; i++)
     614           9 :                 cutafter[i] = -1;
     615             : 
     616             :         do {
     617             :                 more = false;
     618          18 :                 for (i = 0; i < fields; i++) {
     619           9 :                         if (rest[i] == NULL || *rest[i] == 0) {
     620           0 :                                 mnstr_printf(toConsole, "%c %*s ",
     621           0 :                                              first ? '|' : i > 0 && cutafter[i - 1] == 0 ? '>' : ':',
     622           0 :                                              len[i], "");
     623             :                         } else {
     624             :                                 ulen = utf8strlen(rest[i], NULL);
     625             : 
     626           9 :                                 if (first && trim == 2) {
     627             :                                         /* calculate the height of
     628             :                                          * this field according to the
     629             :                                          * golden ratio, with a
     630             :                                          * correction for a terminal
     631             :                                          * screen (1.62 * 2 -> 3 :
     632             :                                          * 9.72~10) */
     633           6 :                                         if (ulen > (size_t) len[i]) {
     634           0 :                                                 cutafter[i] = 3 * len[i] / 10;
     635           0 :                                                 if (cutafter[i] == 1)
     636           0 :                                                         cutafter[i]++;
     637             :                                         }
     638             :                                 }
     639             : 
     640             :                                 /* on each cycle we get closer to the limit */
     641           9 :                                 if (cutafter[i] >= 0)
     642           0 :                                         cutafter[i]--;
     643             : 
     644             :                                 /* break the string into pieces and
     645             :                                  * left-adjust them in the column */
     646           9 :                                 t = strchr(rest[i], '\n');
     647           9 :                                 if (ulen > (size_t) len[i] || t) {
     648             :                                         char *s;
     649             : 
     650           0 :                                         t = utf8skip(rest[i], len[i]);
     651           0 :                                         if (trim == 1) {
     652           0 :                                                 while (t > rest[i] && !my_isspace(*t))
     653           0 :                                                         while ((*--t & 0xC0) == 0x80)
     654             :                                                                 ;
     655           0 :                                                 if (t == rest[i] && !my_isspace(*t))
     656           0 :                                                         t = utf8skip(rest[i], len[i]);
     657             :                                         }
     658           0 :                                         mnstr_printf(toConsole, "%c",
     659           0 :                                                      first ? '|' : i > 0 && cutafter[i - 1] == 0 ? '>' : ':');
     660           0 :                                         if (numeric[i])
     661           0 :                                                 mnstr_printf(toConsole, "%*s",
     662           0 :                                                              (int) (len[i] - (ulen - utf8strlen(t, NULL))),
     663             :                                                              "");
     664             : 
     665             :                                         s = t;
     666           0 :                                         if (trim == 1)
     667           0 :                                                 while (my_isspace(*s))
     668           0 :                                                         s++;
     669           0 :                                         if (trim == 2 && *s == '\n')
     670           0 :                                                 s++;
     671           0 :                                         if (*s && cutafter[i] == 0) {
     672           0 :                                                 t = utf8skip(rest[i], len[i] - 2);
     673             :                                                 s = t;
     674           0 :                                                 if (trim == 1)
     675           0 :                                                         while (my_isspace(*s))
     676           0 :                                                                 s++;
     677           0 :                                                 if (trim == 2 && *s == '\n')
     678           0 :                                                         s++;
     679           0 :                                                 mnstr_write(toConsole, " ", 1, 1);
     680           0 :                                                 for (char *p = rest[i]; p < t; p++) {
     681           0 :                                                         if (*p == '\t')
     682           0 :                                                                 mnstr_write(toConsole, " ", 1, 1);
     683           0 :                                                         else if ((unsigned char) *p <= 0x1F || *p == '\177')
     684           0 :                                                                 mnstr_printf(toConsole, "\\%03o", (unsigned char) *p);
     685           0 :                                                         else if (*p == '\302' &&
     686           0 :                                                                  (p[1] & 0xE0) == 0x80) {
     687           0 :                                                                 mnstr_printf(toConsole, "\\u%04x", (unsigned) ((p[1] & 0x3F) | 0x80));
     688           0 :                                                                 p++;
     689             :                                                         } else
     690           0 :                                                                 mnstr_write(toConsole, p, 1, 1);
     691             :                                                 }
     692           0 :                                                 mnstr_printf(toConsole, "...%*s",
     693           0 :                                                              len[i] - 2 - (int) utf8strlen(rest[i], t),
     694             :                                                              "");
     695           0 :                                                 croppedfields++;
     696             :                                         } else {
     697           0 :                                                 mnstr_write(toConsole, " ", 1, 1);
     698           0 :                                                 for (char *p = rest[i]; p < t; p++) {
     699           0 :                                                         if (*p == '\t')
     700           0 :                                                                 mnstr_write(toConsole, " ", 1, 1);
     701           0 :                                                         else if ((unsigned char) *p <= 0x1F || *p == '\177')
     702           0 :                                                                 mnstr_printf(toConsole, "\\%03o", (unsigned char) *p);
     703           0 :                                                         else if (*p == '\302' &&
     704           0 :                                                                  (p[1] & 0xE0) == 0x80) {
     705           0 :                                                                 mnstr_printf(toConsole, "\\u%04x", (unsigned) ((p[1] & 0x3F) | 0x80));
     706           0 :                                                                 p++;
     707             :                                                         } else
     708           0 :                                                                 mnstr_write(toConsole, p, 1, 1);
     709             :                                                 }
     710           0 :                                                 mnstr_write(toConsole, " ", 1, 1);
     711           0 :                                                 if (!numeric[i])
     712           0 :                                                         mnstr_printf(toConsole, "%*s",
     713           0 :                                                                      (int) (len[i] - (ulen - utf8strlen(t, NULL))),
     714             :                                                                      "");
     715             :                                         }
     716           0 :                                         rest[i] = *s ? s : 0;
     717           0 :                                         if (rest[i] == NULL) {
     718             :                                                 /* avoid > as border
     719             :                                                  * marker if
     720             :                                                  * everything actually
     721             :                                                  * just fits */
     722           0 :                                                 cutafter[i] = -1;
     723             :                                         }
     724           0 :                                         if (cutafter[i] == 0)
     725           0 :                                                 rest[i] = NULL;
     726           0 :                                         if (rest[i])
     727             :                                                 more = true;
     728             :                                 } else {
     729           9 :                                         mnstr_printf(toConsole, "%c",
     730           0 :                                                      first ? '|' : i > 0 && cutafter[i - 1] == 0 ? '>' : ':');
     731           9 :                                         if (numeric[i]) {
     732           0 :                                                 mnstr_printf(toConsole, "%*s",
     733           0 :                                                              (int) (len[i] - ulen),
     734             :                                                              "");
     735           0 :                                                 mnstr_printf(toConsole, " %s ",
     736             :                                                              rest[i]);
     737             :                                         }
     738           9 :                                         if (!numeric[i]) {
     739             :                                                 char *p;
     740             :                                                 /* replace tabs with a
     741             :                                                  * single space to
     742             :                                                  * avoid screwup the
     743             :                                                  * width
     744             :                                                  * calculations */
     745           9 :                                                 mnstr_write(toConsole, " ", 1, 1);
     746         198 :                                                 for (p = rest[i]; *p; p++) {
     747         189 :                                                         if (*p == '\t')
     748           0 :                                                                 mnstr_write(toConsole, " ", 1, 1);
     749         189 :                                                         else if ((unsigned char) *p <= 0x1F || *p == '\177')
     750           0 :                                                                 mnstr_printf(toConsole, "\\%03o", (unsigned char) *p);
     751         189 :                                                         else if (*p == '\302' &&
     752           0 :                                                                  (p[1] & 0xE0) == 0x80) {
     753           0 :                                                                 mnstr_printf(toConsole, "\\u%04x", (unsigned) ((p[1] & 0x3F) | 0x80));
     754           0 :                                                                 p++;
     755             :                                                         } else
     756         189 :                                                                 mnstr_write(toConsole, p, 1, 1);
     757             :                                                 }
     758           9 :                                                 mnstr_printf(toConsole, " %*s",
     759           9 :                                                              (int) (len[i] - ulen),
     760             :                                                              "");
     761             :                                         }
     762           9 :                                         rest[i] = 0;
     763             :                                         /* avoid > as border marker if
     764             :                                          * everything actually just
     765             :                                          * fits */
     766           9 :                                         if (cutafter[i] == 0)
     767           0 :                                                 cutafter[i] = -1;
     768             :                                 }
     769             :                         }
     770             :                 }
     771          18 :                 mnstr_printf(toConsole, "%c%s\n",
     772           0 :                              first ? '|' : i > 0 && cutafter[i - 1] == 0 ? '>' : ':',
     773             :                              wm ? ">" : "");
     774             :                 first = false;
     775           9 :                 rows++;
     776           9 :         } while (more);
     777             : 
     778           9 :         free(cutafter);
     779           9 :         return rows;
     780             : }
     781             : 
     782             : static void
     783           0 : XMLprdata(const char *val)
     784             : {
     785           0 :         if (val == NULL)
     786             :                 return;
     787           0 :         while (*val) {
     788           0 :                 if (*val == '&')
     789           0 :                         mnstr_printf(toConsole, "&amp;");
     790           0 :                 else if (*val == '<')
     791           0 :                         mnstr_printf(toConsole, "&lt;");
     792           0 :                 else if (*val == '>')
     793           0 :                         mnstr_printf(toConsole, "&gt;");
     794           0 :                 else if (*val == '"')
     795           0 :                         mnstr_printf(toConsole, "&quot;");
     796           0 :                 else if (*val == '\'')
     797           0 :                         mnstr_printf(toConsole, "&apos;");
     798           0 :                 else if ((*val & 0xFF) < 0x20)   /* control character */
     799           0 :                         mnstr_printf(toConsole, "&#%d;", *val & 0xFF);
     800           0 :                 else if ((*val & 0x80) != 0 /* && encoding != NULL */ ) {
     801             :                         int n;
     802             :                         unsigned int m;
     803           0 :                         unsigned int c = *val & 0x7F;
     804             : 
     805           0 :                         for (n = 0, m = 0x40; c & m; n++, m >>= 1)
     806           0 :                                 c &= ~m;
     807           0 :                         while (--n >= 0)
     808           0 :                                 c = (c << 6) | (*++val & 0x3F);
     809           0 :                         mnstr_printf(toConsole, "&#x%x;", c);
     810             :                 } else
     811           0 :                         mnstr_write(toConsole, val, 1, 1);
     812           0 :                 val++;
     813             :         }
     814             : }
     815             : 
     816             : static void
     817           0 : XMLprattr(const char *name, const char *val)
     818             : {
     819           0 :         mnstr_printf(toConsole, " %s=\"", name);
     820           0 :         XMLprdata(val);
     821           0 :         mnstr_write(toConsole, "\"", 1, 1);
     822           0 : }
     823             : 
     824             : static void
     825           0 : XMLrenderer(MapiHdl hdl)
     826             : {
     827             :         int i, fields;
     828             :         char *name;
     829             : 
     830             :         /* we must use toConsole since the XML file is encoded in UTF-8 */
     831           0 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     832           0 :         mnstr_printf(toConsole, "<?xml version='1.0' encoding='UTF-8'?>\n"
     833             :                                 "<!DOCTYPE table [\n"
     834             :                                 " <!ELEMENT table (row)*>\n" /* a table consists of zero or more rows */
     835             :                                 " <!ELEMENT row (column)+>\n"   /* a row consists of one or more columns */
     836             :                                 " <!ELEMENT column (#PCDATA)>\n"
     837             :                                 " <!ATTLIST table name CDATA #IMPLIED>\n"       /* a table may have a name */
     838             :                                 " <!ATTLIST column name CDATA #IMPLIED\n"  /* a column may have a name */
     839             :                                 "                  isnull (true|false) 'false'>]>\n"
     840             :                                 "<table");
     841           0 :         name = mapi_get_table(hdl, 0);
     842           0 :         if (name != NULL && *name != 0)
     843           0 :                 XMLprattr("name", name);
     844           0 :         mnstr_printf(toConsole, ">\n");
     845           0 :         while (!mnstr_errnr(toConsole) && (fields = fetch_row(hdl)) != 0) {
     846           0 :                 mnstr_printf(toConsole, "<row>");
     847           0 :                 for (i = 0; i < fields; i++) {
     848           0 :                         char *data = mapi_fetch_field(hdl, i);
     849             : 
     850           0 :                         mnstr_printf(toConsole, "<column");
     851           0 :                         name = mapi_get_name(hdl, i);
     852           0 :                         if (name != NULL && *name != 0)
     853           0 :                                 XMLprattr("name", name);
     854           0 :                         if (data == NULL) {
     855           0 :                                 XMLprattr("isnull", "true");
     856           0 :                                 mnstr_write(toConsole, "/", 1, 1);
     857             :                         }
     858           0 :                         mnstr_write(toConsole, ">", 1, 1);
     859           0 :                         if (data) {
     860           0 :                                 XMLprdata(data);
     861           0 :                                 mnstr_printf(toConsole, "</column>");
     862             :                         }
     863             :                 }
     864           0 :                 mnstr_printf(toConsole, "</row>\n");
     865             :         }
     866           0 :         mnstr_printf(toConsole, "</table>\n");
     867           0 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     868           0 : }
     869             : 
     870             : static void
     871           0 : EXPANDEDrenderer(MapiHdl hdl)
     872             : {
     873             :         int i, fields, fieldw, rec = 0;
     874             : 
     875           0 :         fields = mapi_get_field_count(hdl);
     876             :         fieldw = 0;
     877           0 :         for (i = 0; i < fields; i++) {
     878           0 :                 int w = (int) utf8strlen(mapi_get_name(hdl, i), NULL);
     879             :                 if (w > fieldw)
     880             :                         fieldw = w;
     881             :         }
     882           0 :         while (!mnstr_errnr(toConsole) && (fields = fetch_row(hdl)) != 0) {
     883             :                 int valuew = 0, len;
     884           0 :                 ++rec;
     885           0 :                 for (i = 0; i < fields; i++) {
     886           0 :                         char *data = mapi_fetch_field(hdl, i);
     887             :                         char *edata;
     888             :                         int w;
     889             : 
     890           0 :                         if (data == NULL)
     891           0 :                                 data = nullstring;
     892             :                         do {
     893           0 :                                 edata = utf8skip(data, ~(size_t)0);
     894           0 :                                 w = (int) utf8strlen(data, edata);
     895             :                                 if (w > valuew)
     896             :                                         valuew = w;
     897             :                                 data = edata;
     898           0 :                                 if (*data)
     899           0 :                                         data++;
     900           0 :                         } while (*edata);
     901             :                 }
     902           0 :                 len = mnstr_printf(toConsole, "-[ RECORD %d ]-", rec);
     903           0 :                 while (len++ < fieldw + valuew + 3)
     904           0 :                         mnstr_write(toConsole, "-", 1, 1);
     905           0 :                 mnstr_write(toConsole, "\n", 1, 1);
     906           0 :                 for (i = 0; i < fields; i++) {
     907           0 :                         char *data = mapi_fetch_field(hdl, i);
     908             :                         char *edata;
     909           0 :                         const char *name = mapi_get_name(hdl, i);
     910           0 :                         if (data == NULL)
     911           0 :                                 data = nullstring;
     912             :                         do {
     913           0 :                                 edata = utf8skip(data, ~(size_t)0);
     914           0 :                                 mnstr_printf(toConsole, "%-*s | %.*s\n", fieldw, name, (int) (edata - data), data ? data : "");
     915             :                                 name = "";
     916             :                                 data = edata;
     917           0 :                                 if (*data)
     918           0 :                                         data++;
     919           0 :                         } while (*edata);
     920             :                 }
     921             :         }
     922           0 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
     923           0 : }
     924             : 
     925             : static void
     926         207 : CSVrenderer(MapiHdl hdl)
     927             : {
     928             :         int fields;
     929             :         const char *s;
     930         207 :         const char specials[] = {'"', '\\', '\n', '\r', '\t', *separator, '\0'};
     931             :         int i;
     932             : 
     933         207 :         if (csvheader) {
     934           0 :                 fields = mapi_get_field_count(hdl);
     935           0 :                 for (i = 0; i < fields; i++) {
     936           0 :                         s = mapi_get_name(hdl, i);
     937           0 :                         if (s == NULL)
     938             :                                 s = "";
     939           0 :                         mnstr_printf(toConsole, "%s%s", i == 0 ? "" : separator, s);
     940             :                 }
     941           0 :                 mnstr_printf(toConsole, "\n");
     942             :         }
     943        3703 :         while (!mnstr_errnr(toConsole) && (fields = fetch_row(hdl)) != 0) {
     944        7044 :                 for (i = 0; i < fields; i++) {
     945        3548 :                         s = mapi_fetch_field(hdl, i);
     946        3548 :                         if (!noquote && s != NULL && s[strcspn(s, specials)] != '\0') {
     947        3451 :                                 mnstr_printf(toConsole, "%s\"",
     948             :                                              i == 0 ? "" : separator);
     949       80381 :                                 while (*s) {
     950       76930 :                                         switch (*s) {
     951           2 :                                         case '\n':
     952           2 :                                                 mnstr_write(toConsole, "\\n", 1, 2);
     953           2 :                                                 break;
     954           3 :                                         case '\t':
     955           3 :                                                 mnstr_write(toConsole, "\\t", 1, 2);
     956           3 :                                                 break;
     957           0 :                                         case '\r':
     958           0 :                                                 mnstr_write(toConsole, "\\r", 1, 2);
     959           0 :                                                 break;
     960        3443 :                                         case '\\':
     961        3443 :                                                 mnstr_write(toConsole, "\\\\", 1, 2);
     962        3443 :                                                 break;
     963        4277 :                                         case '"':
     964        4277 :                                                 mnstr_write(toConsole, "\"\"", 1, 2);
     965        4277 :                                                 break;
     966       69205 :                                         default:
     967       69205 :                                                 mnstr_write(toConsole, s, 1, 1);
     968       69205 :                                                 break;
     969             :                                         }
     970       76930 :                                         s++;
     971             :                                 }
     972        3451 :                                 mnstr_write(toConsole, "\"", 1, 1);
     973             :                         } else {
     974          97 :                                 if (s == NULL)
     975           0 :                                         s = nullstring == default_nullstring ? "" : nullstring;
     976          97 :                                 mnstr_printf(toConsole, "%s%s",
     977             :                                              i == 0 ? "" : separator, s);
     978             :                         }
     979             :                 }
     980        3496 :                 mnstr_printf(toConsole, "\n");
     981             :         }
     982         207 : }
     983             : 
     984             : static void
     985           9 : SQLseparator(int *len, int fields, char sep)
     986             : {
     987             :         int i, j;
     988             : 
     989           9 :         mnstr_printf(toConsole, "+");
     990          18 :         for (i = 0; i < fields; i++) {
     991           9 :                 mnstr_printf(toConsole, "%c", sep);
     992         297 :                 for (j = 0; j < (len[i] < 0 ? -len[i] : len[i]); j++)
     993         288 :                         mnstr_printf(toConsole, "%c", sep);
     994           9 :                 mnstr_printf(toConsole, "%c+", sep);
     995             :         }
     996           9 :         mnstr_printf(toConsole, "\n");
     997           9 : }
     998             : 
     999             : static void
    1000        4851 : SQLqueryEcho(MapiHdl hdl)
    1001             : {
    1002        4851 :         if (echoquery) {
    1003             :                 char *qry;
    1004             : 
    1005        1122 :                 qry = mapi_get_query(hdl);
    1006        1122 :                 if (qry) {
    1007        1122 :                         if (formatter != TABLEformatter) {
    1008             :                                 char *p = qry;
    1009             :                                 char *q = p;
    1010        3651 :                                 while ((q = strchr(q, '\n')) != NULL) {
    1011        2531 :                                         *q++ = '\0';
    1012        2531 :                                         mnstr_printf(toConsole, "#%s\n", p);
    1013             :                                         p = q;
    1014             :                                 }
    1015        1120 :                                 if (*p) {
    1016             :                                         /* query does not end in \n */
    1017          51 :                                         mnstr_printf(toConsole, "#%s\n", p);
    1018             :                                 }
    1019             :                         } else {
    1020           2 :                                 size_t qrylen = strlen(qry);
    1021             : 
    1022           2 :                                 mnstr_printf(toConsole, "%s", qry);
    1023           2 :                                 if (qrylen > 0 && qry[qrylen - 1] != '\n') {
    1024             :                                         /* query does not end in \n */
    1025           2 :                                         mnstr_printf(toConsole, "\n");
    1026             :                                 }
    1027             :                         }
    1028        1122 :                         free(qry);
    1029             :                 }
    1030             :         }
    1031        4851 : }
    1032             : 
    1033             : /* state machine to recognize integers, floating point numbers, OIDs */
    1034             : static char *
    1035           1 : classify(const char *s, size_t l)
    1036             : {
    1037             :         /* state is the current state of the state machine:
    1038             :          * 0 - initial state, no input seen
    1039             :          * 1 - initial sign
    1040             :          * 2 - valid integer (optionally preceded by a sign)
    1041             :          * 3 - valid integer, followed by a decimal point
    1042             :          * 4 - fixed point number of the form [sign] digits period digits
    1043             :          * 5 - exponent marker after integer or fixed point number
    1044             :          * 6 - sign after exponent marker
    1045             :          * 7 - valid floating point number with exponent
    1046             :          * 8 - integer followed by single 'L'
    1047             :          * 9 - integer followed by 'LL' (lng)
    1048             :          * 10 - fixed or floating point number followed by single 'L'
    1049             :          * 11 - fixed or floating point number followed by 'LL' (dbl)
    1050             :          * 12 - integer followed by '@'
    1051             :          * 13 - valid OID (integer followed by '@0')
    1052             :          */
    1053             :         int state = 0;
    1054             : 
    1055           1 :         if ((l == 4 && strcmp(s, "true") == 0) ||
    1056           0 :             (l == 5 && strcmp(s, "false") == 0))
    1057             :                 return "bit";
    1058           4 :         while (l != 0) {
    1059           3 :                 if (*s == 0)
    1060             :                         return "str";
    1061           3 :                 switch (*s) {
    1062           0 :                 case '0':
    1063           0 :                         if (state == 12) {
    1064             :                                 state = 13;     /* int + '@0' (oid) */
    1065             :                                 break;
    1066             :                         }
    1067             :                         /* fall through */
    1068             :                 case '1':
    1069             :                 case '2':
    1070             :                 case '3':
    1071             :                 case '4':
    1072             :                 case '5':
    1073             :                 case '6':
    1074             :                 case '7':
    1075             :                 case '8':
    1076             :                 case '9':
    1077             :                         switch (state) {
    1078           1 :                         case 0:
    1079             :                         case 1:
    1080             :                                 state = 2;      /* digit after optional sign */
    1081           1 :                                 break;
    1082           0 :                         case 3:
    1083             :                                 state = 4;      /* digit after decimal point */
    1084           0 :                                 break;
    1085           0 :                         case 5:
    1086             :                         case 6:
    1087             :                                 state = 7;      /* digit after exponent marker and optional sign */
    1088           0 :                                 break;
    1089             :                         case 2:
    1090             :                         case 4:
    1091             :                         case 7:
    1092             :                                 break;          /* more digits */
    1093             :                         default:
    1094             :                                 return "str";
    1095             :                         }
    1096             :                         break;
    1097           0 :                 case '.':
    1098           0 :                         if (state == 2)
    1099             :                                 state = 3;      /* decimal point */
    1100             :                         else
    1101             :                                 return "str";
    1102             :                         break;
    1103           0 :                 case 'e':
    1104             :                 case 'E':
    1105           0 :                         if (state == 2 || state == 4)
    1106             :                                 state = 5;      /* exponent marker */
    1107             :                         else
    1108             :                                 return "str";
    1109             :                         break;
    1110           0 :                 case '+':
    1111             :                 case '-':
    1112           0 :                         if (state == 0)
    1113             :                                 state = 1;      /* sign at start */
    1114           0 :                         else if (state == 5)
    1115             :                                 state = 6;      /* sign after exponent marker */
    1116             :                         else
    1117             :                                 return "str";
    1118             :                         break;
    1119           0 :                 case '@':
    1120           0 :                         if (state == 2)
    1121             :                                 state = 12;     /* OID marker */
    1122             :                         else
    1123             :                                 return "str";
    1124             :                         break;
    1125           0 :                 case 'L':
    1126             :                         switch (state) {
    1127             :                         case 2:
    1128             :                                 state = 8;      /* int + 'L' */
    1129             :                                 break;
    1130           0 :                         case 8:
    1131             :                                 state = 9;      /* int + 'LL' */
    1132           0 :                                 break;
    1133           0 :                         case 4:
    1134             :                         case 7:
    1135             :                                 state = 10;     /* dbl + 'L' */
    1136           0 :                                 break;
    1137           0 :                         case 10:
    1138             :                                 state = 11;     /* dbl + 'LL' */
    1139           0 :                                 break;
    1140             :                         default:
    1141             :                                 return "str";
    1142             :                         }
    1143             :                         break;
    1144             :                 default:
    1145             :                         return "str";
    1146             :                 }
    1147           3 :                 s++;
    1148           3 :                 l--;
    1149             :         }
    1150           1 :         switch (state) {
    1151             :         case 13:
    1152             :                 return "oid";
    1153             :         case 2:
    1154             :                 return "int";
    1155             :         case 4:
    1156             :         case 7:
    1157             :         case 11:
    1158             :                 return "dbl";
    1159             :         case 9:
    1160             :                 return "lng";
    1161             :         default:
    1162             :                 return "str";
    1163             :         }
    1164             : }
    1165             : 
    1166             : static void
    1167        3673 : TESTrenderer(MapiHdl hdl)
    1168             : {
    1169             :         int fields;
    1170             :         char *reply;
    1171             :         char *s;
    1172             :         size_t l;
    1173             :         char *tp;
    1174             :         char *sep;
    1175             :         int i;
    1176             : 
    1177       85325 :         while (!mnstr_errnr(toConsole) && (reply = fetch_line(hdl)) != 0) {
    1178       81652 :                 if (*reply != '[') {
    1179       14689 :                         if (*reply == '=')
    1180          13 :                                 reply++;
    1181       14689 :                         mnstr_printf(toConsole, "%s\n", reply);
    1182       14689 :                         continue;
    1183             :                 }
    1184       66963 :                 fields = mapi_split_line(hdl);
    1185             :                 sep = "[ ";
    1186     1635411 :                 for (i = 0; i < fields; i++) {
    1187     1568448 :                         s = mapi_fetch_field(hdl, i);
    1188     1568448 :                         l = mapi_fetch_field_len(hdl, i);
    1189     1568448 :                         tp = mapi_get_type(hdl, i);
    1190     1568448 :                         if (strcmp(tp, "unknown") == 0)
    1191           1 :                                 tp = classify(s, l);
    1192     1568448 :                         mnstr_printf(toConsole, "%s", sep);
    1193             :                         sep = ",\t";
    1194     1568448 :                         if (s == NULL)
    1195      939859 :                                 mnstr_printf(toConsole, "%s", mode == SQL ? "NULL" : "nil");
    1196      628589 :                         else if (strcmp(tp, "varchar") == 0 ||
    1197      352884 :                                  strcmp(tp, "char") == 0 ||
    1198      254925 :                                  strcmp(tp, "clob") == 0 ||
    1199      174193 :                                  strcmp(tp, "str") == 0 ||
    1200      174193 :                                  strcmp(tp, "json") == 0 ||
    1201             :                                  /* NULL byte in string? */
    1202      174193 :                                  strlen(s) < l ||
    1203             :                                  /* start or end with white space? */
    1204      174193 :                                  my_isspace(*s) ||
    1205      174193 :                                  (l > 0 && my_isspace(s[l - 1])) ||
    1206             :                                  /* timezone can have embedded comma */
    1207      174193 :                                  strcmp(tp, "timezone") == 0 ||
    1208             :                                  /* a bunch of geom types */
    1209      174193 :                                  strcmp(tp, "curve") == 0 ||
    1210      174193 :                                  strcmp(tp, "geometry") == 0 ||
    1211      174148 :                                  strcmp(tp, "linestring") == 0 ||
    1212      174148 :                                  strcmp(tp, "mbr") == 0 ||
    1213      174119 :                                  strcmp(tp, "multilinestring") == 0 ||
    1214      174119 :                                  strcmp(tp, "point") == 0 ||
    1215      174119 :                                  strcmp(tp, "polygon") == 0 ||
    1216      174119 :                                  strcmp(tp, "surface") == 0) {
    1217      454470 :                                 mnstr_printf(toConsole, "\"");
    1218     5662796 :                                 while (l != 0) {
    1219     5208326 :                                         switch (*s) {
    1220         230 :                                         case '\n':
    1221         230 :                                                 mnstr_write(toConsole, "\\n", 1, 2);
    1222         230 :                                                 break;
    1223           0 :                                         case '\t':
    1224           0 :                                                 mnstr_write(toConsole, "\\t", 1, 2);
    1225           0 :                                                 break;
    1226           0 :                                         case '\r':
    1227           0 :                                                 mnstr_write(toConsole, "\\r", 1, 2);
    1228           0 :                                                 break;
    1229         360 :                                         case '\\':
    1230         360 :                                                 mnstr_write(toConsole, "\\\\", 1, 2);
    1231         360 :                                                 break;
    1232       15468 :                                         case '"':
    1233       15468 :                                                 mnstr_write(toConsole, "\\\"", 1, 2);
    1234       15468 :                                                 break;
    1235       80777 :                                         case '0':
    1236             :                                         case '1':
    1237             :                                         case '2':
    1238             :                                         case '3':
    1239             :                                         case '4':
    1240             :                                         case '5':
    1241             :                                         case '6':
    1242             :                                         case '7':
    1243             :                                         case '8':
    1244             :                                         case '9':
    1245       80777 :                                                 if (strcmp(tp, "curve") == 0 ||
    1246       80777 :                                                     strcmp(tp, "geometry") == 0 ||
    1247       80615 :                                                     strcmp(tp, "linestring") == 0 ||
    1248       80615 :                                                     strcmp(tp, "mbr") == 0 ||
    1249       80499 :                                                     strcmp(tp, "multilinestring") == 0 ||
    1250       80499 :                                                     strcmp(tp, "point") == 0 ||
    1251       80499 :                                                     strcmp(tp, "polygon") == 0 ||
    1252       80499 :                                                     strcmp(tp, "surface") == 0) {
    1253             :                                                         char *e;
    1254             :                                                         double d;
    1255         278 :                                                         d = strtod(s, &e);
    1256         278 :                                                         if (s != e) {
    1257         278 :                                                                 mnstr_printf(toConsole, "%.10g", d);
    1258         278 :                                                                 l -= e - s;
    1259             :                                                                 s = e;
    1260         278 :                                                                 continue;
    1261             :                                                         }
    1262             :                                                 }
    1263             :                                                 /* fall through */
    1264             :                                         default:
    1265     5191990 :                                                 if ((unsigned char) *s < ' ')
    1266           0 :                                                         mnstr_printf(toConsole,
    1267             :                                                                      "\\%03o",
    1268             :                                                                      (unsigned char) *s);
    1269             :                                                 else
    1270     5191990 :                                                         mnstr_write(toConsole, s, 1, 1);
    1271             :                                                 break;
    1272             :                                         }
    1273     5208048 :                                         s++;
    1274     5208048 :                                         l--;
    1275             :                                 }
    1276      454470 :                                 mnstr_write(toConsole, "\"", 1, 1);
    1277      174119 :                         } else if (strcmp(tp, "double") == 0 ||
    1278      174119 :                                    strcmp(tp, "dbl") == 0) {
    1279             :                                 char buf[32];
    1280             :                                 int j;
    1281             :                                 double v;
    1282        3591 :                                 if (strcmp(s, "-0") == 0) /* normalize -0 */
    1283             :                                         s = "0";
    1284        3591 :                                 v = strtod(s, NULL);
    1285       21768 :                                 for (j = 4; j < 11; j++) {
    1286       19175 :                                         snprintf(buf, sizeof(buf), "%.*g", j, v);
    1287       19175 :                                         if (v == strtod(buf, NULL))
    1288             :                                                 break;
    1289             :                                 }
    1290        3591 :                                 mnstr_printf(toConsole, "%s", buf);
    1291      170528 :                         } else if (strcmp(tp, "real") == 0) {
    1292             :                                 char buf[32];
    1293             :                                 int j;
    1294             :                                 float v;
    1295           0 :                                 if (strcmp(s, "-0") == 0) /* normalize -0 */
    1296             :                                         s = "0";
    1297           0 :                                 v = strtof(s, NULL);
    1298           0 :                                 for (j = 4; j < 6; j++) {
    1299           0 :                                         snprintf(buf, sizeof(buf), "%.*g", j, v);
    1300           0 :                                         if (v == strtof(buf, NULL))
    1301             :                                                 break;
    1302             :                                 }
    1303           0 :                                 mnstr_printf(toConsole, "%s", buf);
    1304             :                         } else
    1305      170528 :                                 mnstr_printf(toConsole, "%s", s);
    1306             :                 }
    1307       66963 :                 mnstr_printf(toConsole, "\t]\n");
    1308             :         }
    1309        3673 : }
    1310             : 
    1311             : static void
    1312           3 : RAWrenderer(MapiHdl hdl)
    1313             : {
    1314             :         char *line;
    1315             : 
    1316          21 :         while ((line = fetch_line(hdl)) != 0) {
    1317          18 :                 if (*line == '=')
    1318           0 :                         line++;
    1319          18 :                 mnstr_printf(toConsole, "%s\n", line);
    1320             :         }
    1321           3 : }
    1322             : 
    1323             : static void
    1324           3 : SQLheader(MapiHdl hdl, int *len, int fields, char more)
    1325             : {
    1326           3 :         SQLseparator(len, fields, '-');
    1327           3 :         if (mapi_get_name(hdl, 0)) {
    1328             :                 int i;
    1329           3 :                 char **names = (char **) malloc(fields * sizeof(char *));
    1330           3 :                 int *numeric = (int *) malloc(fields * sizeof(int));
    1331             : 
    1332           3 :                 if (names == NULL || numeric == NULL) {
    1333           0 :                         free(names);
    1334           0 :                         free(numeric);
    1335           0 :                         fprintf(stderr,"Malloc for SQLheader failed");
    1336           0 :                         exit(2);
    1337             :                 }
    1338           6 :                 for (i = 0; i < fields; i++) {
    1339           3 :                         names[i] = mapi_get_name(hdl, i);
    1340           3 :                         numeric[i] = 0;
    1341             :                 }
    1342           3 :                 SQLrow(len, numeric, names, fields, 1, more);
    1343           3 :                 SQLseparator(len, fields, '=');
    1344           3 :                 free(names);
    1345           3 :                 free(numeric);
    1346             :         }
    1347           3 : }
    1348             : 
    1349             : static void
    1350           0 : SQLdebugRendering(MapiHdl hdl)
    1351             : {
    1352             :         char *reply;
    1353             :         int cnt = 0;
    1354             : 
    1355           0 :         snprintf(promptbuf, sizeof(promptbuf), "mdb>");
    1356           0 :         while ((reply = fetch_line(hdl))) {
    1357           0 :                 cnt++;
    1358           0 :                 mnstr_printf(toConsole, "%s\n", reply);
    1359           0 :                 if (strncmp(reply, "mdb>#EOD", 8) == 0) {
    1360             :                         cnt = 0;
    1361           0 :                         while ((reply = fetch_line(hdl)))
    1362           0 :                                 mnstr_printf(toConsole, "%s\n", reply);
    1363             :                         break;
    1364             :                 }
    1365             :         }
    1366           0 :         if (cnt == 0) {
    1367           0 :                 setPrompt();
    1368           0 :                 specials = NOmodifier;
    1369             :         }
    1370           0 : }
    1371             : 
    1372             : static void
    1373           0 : SQLpagemove(int *len, int fields, int *ps, bool *silent)
    1374             : {
    1375             :         char buf[512];
    1376             :         ssize_t sz;
    1377             : 
    1378           0 :         SQLseparator(len, fields, '-');
    1379           0 :         mnstr_printf(toConsole, "next page? (continue,quit,next)");
    1380           0 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
    1381           0 :         sz = mnstr_readline(fromConsole, buf, sizeof(buf));
    1382           0 :         if (sz > 0) {
    1383           0 :                 if (buf[0] == 'c')
    1384           0 :                         *ps = 0;
    1385           0 :                 if (buf[0] == 'q')
    1386           0 :                         *silent = true;
    1387           0 :                 while (sz > 0 && buf[sz - 1] != '\n')
    1388           0 :                         sz = mnstr_readline(fromConsole, buf, sizeof(buf));
    1389             :         }
    1390           0 :         if (!*silent)
    1391           0 :                 SQLseparator(len, fields, '-');
    1392           0 : }
    1393             : 
    1394             : static void
    1395           3 : SQLrenderer(MapiHdl hdl)
    1396             : {
    1397             :         int i, total, lentotal, vartotal, minvartotal;
    1398             :         int fields, rfields, printfields = 0, max = 1, graphwaste = 0;
    1399             :         int *len = NULL, *hdr = NULL, *numeric = NULL;
    1400             :         char **rest = NULL;
    1401             :         char buf[50];
    1402           3 :         int ps = rowsperpage;
    1403           3 :         bool silent = false;
    1404             :         int64_t rows = 0;
    1405             : 
    1406           3 :         croppedfields = 0;
    1407           3 :         fields = mapi_get_field_count(hdl);
    1408           3 :         rows = mapi_get_row_count(hdl);
    1409             : 
    1410           3 :         len = calloc(fields, sizeof(*len));
    1411           3 :         hdr = calloc(fields, sizeof(*hdr));
    1412           3 :         rest = calloc(fields, sizeof(*rest));
    1413           3 :         numeric = calloc(fields, sizeof(*numeric));
    1414           3 :         if (len == NULL || hdr == NULL || rest == NULL || numeric == NULL) {
    1415           0 :                 if (len)
    1416           0 :                         free(len);
    1417           0 :                 if (hdr)
    1418           0 :                         free(hdr);
    1419           0 :                 if (rest)
    1420           0 :                         free(rest);
    1421           0 :                 if (numeric)
    1422           0 :                         free(numeric);
    1423           0 :                 fprintf(stderr,"Malloc for SQLrenderer failed");
    1424           0 :                 exit(2);
    1425             :         }
    1426             : 
    1427             :         total = 0;
    1428             :         lentotal = 0;
    1429             :         vartotal = 0;
    1430             :         minvartotal = 0;
    1431           6 :         for (i = 0; i < fields; i++) {
    1432             :                 char *s;
    1433             : 
    1434           3 :                 len[i] = mapi_get_len(hdl, i);
    1435           3 :                 if (len[i] == 0) {
    1436           0 :                         if ((s = mapi_get_type(hdl, i)) == NULL ||
    1437           0 :                             (strcmp(s, "varchar") != 0 &&
    1438           0 :                              strcmp(s, "clob") != 0 &&
    1439           0 :                              strcmp(s, "char") != 0 &&
    1440           0 :                              strcmp(s, "str") != 0 &&
    1441           0 :                              strcmp(s, "json") != 0)) {
    1442             :                                 /* no table width known, use maximum,
    1443             :                                  * rely on squeezing later on to fix
    1444             :                                  * it to whatever is available; note
    1445             :                                  * that for a column type of varchar,
    1446             :                                  * 0 means the complete column is NULL
    1447             :                                  * or empty string, so MINCOLSIZE
    1448             :                                  * (below) will work great */
    1449           0 :                                 len[i] = pagewidth <= 0 ? DEFWIDTH : pagewidth;
    1450           0 :                         } else if (strcmp(s, "uuid") == 0) {
    1451             :                                 /* we know how large the UUID representation
    1452             :                                  * is, even if the server doesn't */
    1453           0 :                                 len[i] = 36;
    1454             :                         }
    1455             :                 }
    1456           3 :                 if (len[i] < MINCOLSIZE)
    1457           0 :                         len[i] = MINCOLSIZE;
    1458           3 :                 s = mapi_get_name(hdl, i);
    1459           3 :                 if (s != NULL) {
    1460           3 :                         size_t l = strlen(s);
    1461           3 :                         assert(l <= INT_MAX);
    1462           3 :                         hdr[i] = (int) l;
    1463             :                 } else {
    1464           0 :                         hdr[i] = 0;
    1465             :                 }
    1466             :                 /* if no rows, just try to draw headers nicely */
    1467           3 :                 if (rows == 0)
    1468           0 :                         len[i] = hdr[i];
    1469           3 :                 s = mapi_get_type(hdl, i);
    1470           3 :                 numeric[i] = s != NULL &&
    1471           3 :                         (strcmp(s, "int") == 0 ||
    1472           3 :                          strcmp(s, "tinyint") == 0 ||
    1473           3 :                          strcmp(s, "bigint") == 0 ||
    1474           3 :                          strcmp(s, "hugeint") == 0 ||
    1475           3 :                          strcmp(s, "oid") == 0 ||
    1476           3 :                          strcmp(s, "smallint") == 0 ||
    1477           3 :                          strcmp(s, "double") == 0 ||
    1478           3 :                          strcmp(s, "float") == 0 ||
    1479           3 :                          strcmp(s, "decimal") == 0);
    1480             : 
    1481           3 :                 if (rows == 0) {
    1482           0 :                         minvartotal += len[i]; /* don't wrap column headers if no data */
    1483           3 :                 } else if (numeric[i]) {
    1484             :                         /* minimum size is equal to maximum size */
    1485           0 :                         minvartotal += len[i];
    1486             :                 } else {
    1487             :                         /* minimum size for wide columns is MINVARCOLSIZE */
    1488           3 :                         minvartotal += len[i] > MINVARCOLSIZE ? MINVARCOLSIZE : len[i];
    1489             :                 }
    1490           3 :                 vartotal += len[i];
    1491             :                 total += len[i];
    1492             : 
    1493             :                 /* do a very pessimistic calculation to determine if more
    1494             :                  * columns would actually fit on the screen */
    1495           3 :                 if (pagewidth > 0 &&
    1496           0 :                     ((((printfields + 1) * 3) - 1) + 2) + /* graphwaste */
    1497           0 :                     (total - vartotal) + minvartotal > pagewidth) {
    1498             :                         /* this last column was too much */
    1499             :                         total -= len[i];
    1500             :                         if (!numeric[i])
    1501             :                                 vartotal -= len[i];
    1502             :                         break;
    1503             :                 }
    1504             : 
    1505           3 :                 lentotal += (hdr[i] > len[i] ? hdr[i] : len[i]);
    1506           3 :                 printfields++;
    1507             :         }
    1508             : 
    1509             :         /* what we waste on space on the display is the column separators '
    1510             :          * | ', but the edges lack the edgespace of course */
    1511           3 :         graphwaste = ((printfields * 3) - 1) + 2;
    1512             :         /* make sure we can indicate we dropped columns */
    1513           3 :         if (fields != printfields)
    1514           0 :                 graphwaste++;
    1515             : 
    1516             :         /* punish the column headers first until you cannot squeeze any
    1517             :          * further */
    1518           3 :         while (pagewidth > 0 && graphwaste + lentotal > pagewidth) {
    1519             :                 /* pick the column where the header is longest compared to its
    1520             :                  * content */
    1521             :                 max = -1;
    1522           0 :                 for (i = 0; i < printfields; i++) {
    1523           0 :                         if (hdr[i] > len[i]) {
    1524           0 :                                 if (max == -1 ||
    1525           0 :                                     hdr[max] - len[max] < hdr[i] - len[i])
    1526             :                                         max = i;
    1527             :                         }
    1528             :                 }
    1529           0 :                 if (max == -1)
    1530             :                         break;
    1531           0 :                 hdr[max]--;
    1532           0 :                 lentotal--;
    1533             :         }
    1534             : 
    1535             :         /* correct the lengths if the headers are wider than the content,
    1536             :          * since the headers are maximally squeezed to the content above, if
    1537             :          * a header is larger than its content, it means there was space
    1538             :          * enough.  If not, the content will be squeezed below. */
    1539           6 :         for (i = 0; i < printfields; i++)
    1540           3 :                 if (len[i] < hdr[i])
    1541           0 :                         len[i] = hdr[i];
    1542             : 
    1543             :         /* worst case: lentotal = total, which means it still doesn't fit,
    1544             :          * values will be squeezed next */
    1545           3 :         while (pagewidth > 0 && graphwaste + total > pagewidth) {
    1546             :                 max = -1;
    1547           0 :                 for (i = 0; i < printfields; i++) {
    1548           0 :                         if (!numeric[i] && (max == -1 || len[i] > len[max]))
    1549             :                                 max = i;
    1550             :                 }
    1551             : 
    1552             :                 /* no varsized fields that we can squeeze */
    1553           0 :                 if (max == -1)
    1554             :                         break;
    1555             :                 /* penalty for largest field */
    1556           0 :                 len[max]--;
    1557           0 :                 total--;
    1558             :                 /* no more squeezing possible */
    1559           0 :                 if (len[max] == 1)
    1560             :                         break;
    1561             :         }
    1562             : 
    1563           3 :         SQLheader(hdl, len, printfields, fields != printfields);
    1564             : 
    1565           9 :         while ((rfields = fetch_row(hdl)) != 0) {
    1566           6 :                 if (mnstr_errnr(toConsole))
    1567           0 :                         continue;
    1568           6 :                 if (rfields != fields) {
    1569           0 :                         mnstr_printf(stderr_stream,
    1570             :                                      "invalid tuple received from server, "
    1571             :                                      "got %d columns, expected %d, ignoring\n", rfields, fields);
    1572           0 :                         continue;
    1573             :                 }
    1574           6 :                 if (silent)
    1575           0 :                         continue;
    1576          12 :                 for (i = 0; i < printfields; i++) {
    1577           6 :                         rest[i] = mapi_fetch_field(hdl, i);
    1578           6 :                         if (rest[i] == NULL)
    1579           0 :                                 rest[i] = nullstring;
    1580             :                         else {
    1581             :                                 char *p = rest[i];
    1582             : 
    1583           6 :                                 while ((p = strchr(p, '\r')) != 0) {
    1584           0 :                                         switch (p[1]) {
    1585           0 :                                         case '\0':
    1586             :                                                 /* end of string: remove CR */
    1587           0 :                                                 *p = 0;
    1588           0 :                                                 break;
    1589           0 :                                         case '\n':
    1590             :                                                 /* followed by LF: remove CR */
    1591             :                                                 /* note: copy including NUL */
    1592           0 :                                                 memmove(p, p + 1, strlen(p));
    1593           0 :                                                 break;
    1594           0 :                                         default:
    1595             :                                                 /* replace with ' ' */
    1596           0 :                                                 *p = ' ';
    1597           0 :                                                 break;
    1598             :                                         }
    1599             :                                 }
    1600             :                         }
    1601             :                 }
    1602             : 
    1603           6 :                 if (ps > 0 && rows >= ps && fromConsole != NULL) {
    1604           0 :                         SQLpagemove(len, printfields, &ps, &silent);
    1605             :                         rows = 0;
    1606           0 :                         if (silent)
    1607           0 :                                 continue;
    1608             :                 }
    1609             : 
    1610           6 :                 rows += SQLrow(len, numeric, rest, printfields, 2, 0);
    1611             :         }
    1612           3 :         if (fields)
    1613           3 :                 SQLseparator(len, printfields, '-');
    1614           3 :         rows = mapi_get_row_count(hdl);
    1615           3 :         snprintf(buf, sizeof(buf), "%" PRId64 " rows", rows);
    1616           3 :         mnstr_printf(toConsole, "%" PRId64 " tuple%s", rows, rows != 1 ? "s" : "");
    1617             : 
    1618           3 :         if (fields != printfields || croppedfields > 0)
    1619           0 :                 mnstr_printf(toConsole, " !");
    1620           3 :         if (fields != printfields) {
    1621           0 :                 rows = fields - printfields;
    1622           0 :                 mnstr_printf(toConsole, "%" PRId64 " column%s dropped", rows, rows != 1 ? "s" : "");
    1623             :         }
    1624           3 :         if (fields != printfields && croppedfields > 0)
    1625           0 :                 mnstr_printf(toConsole, ", ");
    1626           3 :         if (croppedfields > 0)
    1627           0 :                 mnstr_printf(toConsole, "%d field%s truncated",
    1628             :                        croppedfields, croppedfields != 1 ? "s" : "");
    1629           3 :         if (fields != printfields || croppedfields > 0) {
    1630           0 :                 mnstr_printf(toConsole, "!");
    1631           0 :                 if (firstcrop) {
    1632           0 :                         firstcrop = false;
    1633           0 :                         mnstr_printf(toConsole, "\nnote: to disable dropping columns and/or truncating fields use \\w-1");
    1634             :                 }
    1635             :         }
    1636           3 :         mnstr_printf(toConsole, "\n");
    1637             : 
    1638           3 :         free(len);
    1639           3 :         free(hdr);
    1640           3 :         free(rest);
    1641           3 :         free(numeric);
    1642           3 : }
    1643             : 
    1644             : static void
    1645         184 : setFormatter(const char *s)
    1646             : {
    1647         184 :         if (separator)
    1648           0 :                 free(separator);
    1649         184 :         separator = NULL;
    1650         184 :         csvheader = false;
    1651         184 :         noquote = false;
    1652             : #ifdef _TWO_DIGIT_EXPONENT
    1653             :         if (formatter == TESTformatter)
    1654             :                 _set_output_format(0);
    1655             : #endif
    1656         184 :         if (strcmp(s, "sql") == 0) {
    1657           5 :                 formatter = TABLEformatter;
    1658         179 :         } else if (strcmp(s, "csv") == 0) {
    1659          58 :                 formatter = CSVformatter;
    1660          58 :                 separator = strdup(",");
    1661         121 :         } else if (strncmp(s, "csv=", 4) == 0) {
    1662           0 :                 formatter = CSVformatter;
    1663           0 :                 if (s[4] == '"') {
    1664           0 :                         separator = strdup(s + 5);
    1665           0 :                         if (separator[strlen(separator) - 1] == '"')
    1666           0 :                                 separator[strlen(separator) - 1] = 0;
    1667             :                 } else
    1668           0 :                         separator = strdup(s + 4);
    1669         121 :         } else if (strncmp(s, "csv+", 4) == 0) {
    1670           0 :                 formatter = CSVformatter;
    1671           0 :                 if (s[4] == '"') {
    1672           0 :                         separator = strdup(s + 5);
    1673           0 :                         if (separator[strlen(separator) - 1] == '"')
    1674           0 :                                 separator[strlen(separator) - 1] = 0;
    1675             :                 } else
    1676           0 :                         separator = strdup(s + 4);
    1677           0 :                 csvheader = true;
    1678         121 :         } else if (strcmp(s, "csv-noquote") == 0) {
    1679           0 :                 noquote = true;
    1680           0 :                 formatter = CSVformatter;
    1681           0 :                 separator = strdup(",");
    1682         121 :         } else if (strncmp(s, "csv-noquote=", 12) == 0) {
    1683           0 :                 noquote = true;
    1684           0 :                 formatter = CSVformatter;
    1685           0 :                 if (s[12] == '"') {
    1686           0 :                         separator = strdup(s + 13);
    1687           0 :                         if (separator[strlen(separator) - 1] == '"')
    1688           0 :                                 separator[strlen(separator) - 1] = 0;
    1689             :                 } else
    1690           0 :                         separator = strdup(s + 12);
    1691         121 :         } else if (strncmp(s, "csv-noquote+", 12) == 0) {
    1692           0 :                 noquote = true;
    1693           0 :                 formatter = CSVformatter;
    1694           0 :                 if (s[12] == '"') {
    1695           0 :                         separator = strdup(s + 13);
    1696           0 :                         if (separator[strlen(separator) - 1] == '"')
    1697           0 :                                 separator[strlen(separator) - 1] = 0;
    1698             :                 } else
    1699           0 :                         separator = strdup(s + 12);
    1700           0 :                 csvheader = true;
    1701         121 :         } else if (strcmp(s, "tab") == 0) {
    1702           0 :                 formatter = CSVformatter;
    1703           0 :                 separator = strdup("\t");
    1704         121 :         } else if (strcmp(s, "raw") == 0) {
    1705           3 :                 formatter = RAWformatter;
    1706         118 :         } else if (strcmp(s, "xml") == 0) {
    1707           0 :                 formatter = XMLformatter;
    1708         118 :         } else if (strcmp(s, "test") == 0) {
    1709             : #ifdef _TWO_DIGIT_EXPONENT
    1710             :                 _set_output_format(_TWO_DIGIT_EXPONENT);
    1711             : #endif
    1712         117 :                 formatter = TESTformatter;
    1713           1 :         } else if (strcmp(s, "trash") == 0) {
    1714           1 :                 formatter = TRASHformatter;
    1715           0 :         } else if (strcmp(s, "rowcount") == 0) {
    1716           0 :                 formatter = ROWCOUNTformatter;
    1717           0 :         } else if (strcmp(s, "x") == 0 || strcmp(s, "expanded") == 0) {
    1718           0 :                 formatter = EXPANDEDformatter;
    1719             :         } else {
    1720           0 :                 mnstr_printf(toConsole, "unsupported formatter\n");
    1721             :         }
    1722         184 : }
    1723             : 
    1724             : static void
    1725        5625 : setWidth(void)
    1726             : {
    1727        5625 :         if (!pagewidthset) {
    1728             : #ifdef TIOCGWINSZ
    1729             :                 struct winsize ws;
    1730             : 
    1731        5625 :                 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
    1732           0 :                         pagewidth = ws.ws_col;
    1733             :                 else
    1734             : #endif
    1735             :                 {
    1736        5625 :                         pagewidth = -1;
    1737             :                 }
    1738             :         }
    1739        5625 : }
    1740             : 
    1741             : #ifdef HAVE_POPEN
    1742             : static void
    1743        8993 : start_pager(stream **saveFD)
    1744             : {
    1745        8993 :         *saveFD = NULL;
    1746             : 
    1747        8993 :         if (pager) {
    1748             :                 FILE *p;
    1749             : 
    1750           0 :                 p = popen(pager, "w");
    1751           0 :                 if (p == NULL)
    1752           0 :                         fprintf(stderr, "Starting '%s' failed\n", pager);
    1753             :                 else {
    1754           0 :                         *saveFD = toConsole;
    1755             :                         /* put | in name to indicate that file should be closed with pclose */
    1756           0 :                         if ((toConsole = file_wstream(p, false, "|pager")) == NULL) {
    1757           0 :                                 toConsole = *saveFD;
    1758           0 :                                 *saveFD = NULL;
    1759           0 :                                 fprintf(stderr, "Starting '%s' failed\n", pager);
    1760             :                         }
    1761             : #ifdef HAVE_ICONV
    1762           0 :                         if (encoding != NULL) {
    1763           0 :                                 if ((toConsole = iconv_wstream(toConsole, encoding, "pager")) == NULL) {
    1764           0 :                                         toConsole = *saveFD;
    1765           0 :                                         *saveFD = NULL;
    1766           0 :                                         fprintf(stderr, "Starting '%s' failed\n", pager);
    1767             :                                 }
    1768             :                         }
    1769             : #endif
    1770             :                 }
    1771             :         }
    1772        8993 : }
    1773             : 
    1774             : static void
    1775        8993 : end_pager(stream *saveFD)
    1776             : {
    1777        8993 :         if (saveFD) {
    1778           0 :                 close_stream(toConsole);
    1779           0 :                 toConsole = saveFD;
    1780             :         }
    1781        8993 : }
    1782             : #endif
    1783             : 
    1784             : static int
    1785        5625 : format_result(Mapi mid, MapiHdl hdl, bool singleinstr)
    1786             : {
    1787             :         MapiMsg rc = MERROR;
    1788             :         int64_t aff, lid;
    1789             :         char *reply;
    1790             :         int64_t sqloptimizer = 0;
    1791             :         int64_t maloptimizer = 0;
    1792             :         int64_t querytime = 0;
    1793             :         int64_t rows = 0;
    1794             : #ifdef HAVE_POPEN
    1795             :         stream *saveFD;
    1796             : 
    1797        5625 :         start_pager(&saveFD);
    1798             : #endif
    1799             : 
    1800        5625 :         setWidth();
    1801             : 
    1802        5625 :         timerHumanCalled = false;
    1803             : 
    1804             :         do {
    1805             :                 // get the timings as reported by the backend
    1806        5627 :                 sqloptimizer = mapi_get_sqloptimizertime(hdl);
    1807        5627 :                 maloptimizer = mapi_get_maloptimizertime(hdl);
    1808        5627 :                 querytime = mapi_get_querytime(hdl);
    1809             :                 timerHumanStop();
    1810             :                 /* handle errors first */
    1811        5627 :                 if (mapi_result_error(hdl) != NULL) {
    1812         110 :                         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
    1813         110 :                         if (formatter == TABLEformatter) {
    1814           0 :                                 mapi_noexplain(mid, "");
    1815             :                         } else {
    1816         110 :                                 mapi_noexplain(mid, NULL);
    1817             :                         }
    1818         110 :                         mapi_explain_result(hdl, stderr);
    1819         110 :                         errseen = true;
    1820             :                         /* don't need to print something like '0
    1821             :                          * tuples' if we got an error */
    1822         110 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1823         110 :                         continue;
    1824             :                 }
    1825             : 
    1826        5517 :                 switch (mapi_get_querytype(hdl)) {
    1827         764 :                 case Q_BLOCK:
    1828             :                 case Q_PARSE:
    1829             :                         /* should never see these */
    1830         764 :                         continue;
    1831         154 :                 case Q_UPDATE:
    1832         154 :                         SQLqueryEcho(hdl);
    1833         154 :                         if (formatter == RAWformatter ||
    1834             :                             formatter == TESTformatter) {
    1835          94 :                                 mnstr_printf(toConsole, "[ %" PRId64 "\t]\n", mapi_rows_affected(hdl));
    1836          60 :                         } else if (formatter != TRASHformatter && formatter != CSVformatter) {
    1837           0 :                                 aff = mapi_rows_affected(hdl);
    1838           0 :                                 lid = mapi_get_last_id(hdl);
    1839           0 :                                 mnstr_printf(toConsole,
    1840             :                                              "%" PRId64 " affected row%s",
    1841             :                                              aff,
    1842             :                                              aff != 1 ? "s" : "");
    1843           0 :                                 if (lid != -1) {
    1844           0 :                                         mnstr_printf(toConsole,
    1845             :                                                      ", last generated key: "
    1846             :                                                      "%" PRId64,
    1847             :                                                      lid);
    1848             :                                 }
    1849           0 :                                 mnstr_printf(toConsole, "\n");
    1850             :                         }
    1851         154 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1852         154 :                         continue;
    1853         565 :                 case Q_SCHEMA:
    1854         565 :                         SQLqueryEcho(hdl);
    1855         565 :                         if (formatter == TABLEformatter ||
    1856             :                             formatter == ROWCOUNTformatter) {
    1857           2 :                                 mnstr_printf(toConsole, "operation successful\n");
    1858             :                         }
    1859         565 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1860         565 :                         continue;
    1861         148 :                 case Q_TRANS:
    1862         148 :                         SQLqueryEcho(hdl);
    1863         148 :                         if (formatter == TABLEformatter ||
    1864             :                             formatter == ROWCOUNTformatter)
    1865           0 :                                 mnstr_printf(toConsole, "auto commit mode: %s\n", mapi_get_autocommit(mid) ? "on" : "off");
    1866         148 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1867         148 :                         continue;
    1868          98 :                 case Q_PREPARE:
    1869          98 :                         SQLqueryEcho(hdl);
    1870          98 :                         if (formatter == TABLEformatter ||
    1871             :                             formatter == ROWCOUNTformatter)
    1872           0 :                                 mnstr_printf(toConsole,
    1873             :                                              "execute prepared statement "
    1874             :                                              "using: EXEC %d(...)\n",
    1875             :                                              mapi_get_tableid(hdl));
    1876          98 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1877          98 :                         break;
    1878             :                 case Q_TABLE:
    1879             :                         break;
    1880           7 :                 default:
    1881           7 :                         if ((formatter == TABLEformatter ||
    1882           0 :                              formatter == ROWCOUNTformatter) &&
    1883           0 :                             specials != DEBUGmodifier) {
    1884             :                                 int i;
    1885           0 :                                 mnstr_printf(stderr_stream,
    1886             :                                              "invalid/unknown response from server, "
    1887             :                                              "ignoring output\n");
    1888           0 :                                 for (i = 0; i < 5 && (reply = fetch_line(hdl)) != 0; i++)
    1889           0 :                                         mnstr_printf(stderr_stream, "? %s\n", reply);
    1890           0 :                                 if (i == 5 && fetch_line(hdl) != 0) {
    1891           0 :                                         mnstr_printf(stderr_stream,
    1892             :                                                      "(remaining output omitted, "
    1893             :                                                      "use \\fraw to examine in detail)\n");
    1894             :                                         /* skip over the
    1895             :                                          * unknown/invalid stuff,
    1896             :                                          * otherwise mapi_next_result
    1897             :                                          * call will assert in
    1898             :                                          * close_result because the
    1899             :                                          * logic there doesn't expect
    1900             :                                          * random unread garbage
    1901             :                                          * somehow */
    1902           0 :                                         while (fetch_line(hdl) != 0)
    1903             :                                                 ;
    1904             :                                 }
    1905           0 :                                 continue;
    1906             :                         }
    1907             :                 }
    1908             : 
    1909             :                 /* note: specials != NOmodifier implies mode == SQL */
    1910        3886 :                 if (specials != NOmodifier && debugMode()) {
    1911           0 :                         SQLdebugRendering(hdl);
    1912           0 :                         continue;
    1913             :                 }
    1914        3886 :                 if (debugMode())
    1915           0 :                         RAWrenderer(hdl);
    1916             :                 else {
    1917        3886 :                         SQLqueryEcho(hdl);
    1918             : 
    1919        3886 :                         switch (formatter) {
    1920           0 :                         case TRASHformatter:
    1921           0 :                                 mapi_finish(hdl);
    1922           0 :                                 break;
    1923           0 :                         case XMLformatter:
    1924           0 :                                 XMLrenderer(hdl);
    1925           0 :                                 break;
    1926         207 :                         case CSVformatter:
    1927         207 :                                 CSVrenderer(hdl);
    1928         207 :                                 break;
    1929        3673 :                         case TESTformatter:
    1930        3673 :                                 TESTrenderer(hdl);
    1931        3673 :                                 break;
    1932           3 :                         case TABLEformatter:
    1933           3 :                                 switch (specials) {
    1934           0 :                                 case DEBUGmodifier:
    1935           0 :                                         SQLdebugRendering(hdl);
    1936           0 :                                         break;
    1937           3 :                                 default:
    1938           3 :                                         SQLrenderer(hdl);
    1939           3 :                                         break;
    1940             :                                 }
    1941             :                                 break;
    1942           0 :                         case ROWCOUNTformatter:
    1943           0 :                                 rows = mapi_get_row_count(hdl);
    1944           0 :                                 mnstr_printf(toConsole,
    1945             :                                                 "%" PRId64 " tuple%s\n", rows, rows != 1 ? "s" : "");
    1946           0 :                                 mapi_finish(hdl);
    1947           0 :                                 break;
    1948           0 :                         case EXPANDEDformatter:
    1949           0 :                                 EXPANDEDrenderer(hdl);
    1950           0 :                                 break;
    1951           3 :                         default:
    1952           3 :                                 RAWrenderer(hdl);
    1953           3 :                                 break;
    1954             :                         }
    1955             : 
    1956        3886 :                         timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, false);
    1957             :                 }
    1958        5627 :         } while (!mnstr_errnr(toConsole) && (rc = mapi_next_result(hdl)) == 1);
    1959             :         /*
    1960             :          * in case we called timerHuman() in the loop above with "total == false",
    1961             :          * call it again with "total == true" to get the total wall-clock time
    1962             :          * in case "singleinstr == false".
    1963             :          */
    1964        5625 :         if (timerHumanCalled)
    1965        4861 :                 timerHuman(sqloptimizer, maloptimizer, querytime, singleinstr, true);
    1966        5625 :         if (mnstr_errnr(toConsole)) {
    1967           2 :                 mnstr_printf(stderr_stream, "write error: %s\n", mnstr_peek_error(toConsole));
    1968           2 :                 mnstr_clearerr(toConsole);
    1969           2 :                 errseen = true;
    1970             :         }
    1971             : #ifdef HAVE_POPEN
    1972        5625 :         end_pager(saveFD);
    1973             : #endif
    1974             : 
    1975        5625 :         return rc;
    1976             : }
    1977             : 
    1978             : static bool
    1979          13 : doRequest(Mapi mid, const char *buf)
    1980             : {
    1981             :         MapiHdl hdl;
    1982             : 
    1983          13 :         if (mode == SQL)
    1984          12 :                 SQLsetSpecial(buf);
    1985             : 
    1986          13 :         hdl = mapi_query(mid, buf);
    1987          13 :         if (hdl == NULL) {
    1988           0 :                 if (formatter == TABLEformatter) {
    1989           0 :                         mapi_noexplain(mid, "");
    1990             :                 } else {
    1991           0 :                         mapi_noexplain(mid, NULL);
    1992             :                 }
    1993           0 :                 mapi_explain(mid, stderr);
    1994           0 :                 errseen = true;
    1995           0 :                 return true;
    1996             :         }
    1997             : 
    1998          13 :         if (mapi_needmore(hdl) == MMORE)
    1999             :                 return false;
    2000             : 
    2001          13 :         format_result(mid, hdl, false);
    2002             : 
    2003          13 :         if (mapi_get_active(mid) == NULL)
    2004          13 :                 mapi_close_handle(hdl);
    2005          13 :         return errseen;
    2006             : }
    2007             : 
    2008             : #define CHECK_RESULT(mid, hdl, buf, fp)                         \
    2009             :         switch (mapi_error(mid)) {                              \
    2010             :         case MOK:       /* everything A OK */                   \
    2011             :                 break;                                          \
    2012             :         case MERROR:    /* some error, but try to continue */   \
    2013             :         case MTIMEOUT:  /* lost contact with the server */      \
    2014             :                 if (formatter == TABLEformatter) {              \
    2015             :                         mapi_noexplain(mid, "");              \
    2016             :                 } else {                                        \
    2017             :                         mapi_noexplain(mid, NULL);              \
    2018             :                 }                                               \
    2019             :                 if (hdl) {                                      \
    2020             :                         mapi_explain_query(hdl, stderr);        \
    2021             :                         mapi_close_handle(hdl);                 \
    2022             :                         hdl = NULL;                             \
    2023             :                 } else                                          \
    2024             :                         mapi_explain(mid, stderr);              \
    2025             :                 errseen = true;                                 \
    2026             :                 if (mapi_error(mid) == MERROR)                  \
    2027             :                         continue; /* why not in do-while */     \
    2028             :                 timerEnd();                                     \
    2029             :                 if (buf)                                        \
    2030             :                         free(buf);                              \
    2031             :                 if (fp)                                         \
    2032             :                         close_stream(fp);                       \
    2033             :                 return 1;                                       \
    2034             :         }
    2035             : 
    2036             : static bool
    2037           6 : doFileBulk(Mapi mid, stream *fp)
    2038             : {
    2039             :         char *buf = NULL;
    2040             :         ssize_t length;
    2041           6 :         MapiHdl hdl = mapi_get_active(mid);
    2042             :         MapiMsg rc = MOK;
    2043             :         size_t bufsize = 0;
    2044             : 
    2045             :         bufsize = 10240;
    2046           6 :         buf = malloc(bufsize + 1);
    2047           6 :         if (!buf) {
    2048           0 :                 mnstr_printf(stderr_stream, "cannot allocate memory for send buffer\n");
    2049           0 :                 if (fp)
    2050           0 :                         close_stream(fp);
    2051           0 :                 return true;
    2052             :         }
    2053             : 
    2054             :         timerStart();
    2055             :         do {
    2056          12 :                 timerPause();
    2057          12 :                 if (fp == NULL) {
    2058           0 :                         if (hdl == NULL)
    2059             :                                 break;
    2060             :                         length = 0;
    2061           0 :                         buf[0] = 0;
    2062          12 :                 } else if ((length = mnstr_read(fp, buf, 1, bufsize)) <= 0) {
    2063             :                         /* end of file or error */
    2064           6 :                         if (hdl == NULL)
    2065             :                                 break;  /* nothing more to do */
    2066           0 :                         buf[0] = 0;
    2067             :                         length = 0; /* handle error like EOF */
    2068             :                 } else {
    2069           6 :                         buf[length] = 0;
    2070           6 :                         if (strlen(buf) < (size_t) length) {
    2071           0 :                                 mnstr_printf(stderr_stream, "NULL byte in input\n");
    2072           0 :                                 errseen = true;
    2073           0 :                                 break;
    2074             :                         }
    2075             :                 }
    2076           6 :                 timerResume();
    2077           6 :                 if (hdl == NULL) {
    2078           6 :                         hdl = mapi_query_prep(mid);
    2079           6 :                         CHECK_RESULT(mid, hdl, buf, fp);
    2080             :                 }
    2081             : 
    2082           6 :                 assert(hdl != NULL);
    2083           6 :                 mapi_query_part(hdl, buf, (size_t) length);
    2084           6 :                 CHECK_RESULT(mid, hdl, buf, fp);
    2085             : 
    2086             :                 /* if not at EOF, make sure there is a newline in the
    2087             :                  * buffer */
    2088           6 :                 if (length > 0 && strchr(buf, '\n') == NULL)
    2089           0 :                         continue;
    2090             : 
    2091             :                 assert(hdl != NULL);
    2092             :                 /* If the server wants more but we're at the end of
    2093             :                  * file (length == 0), notify the server that we
    2094             :                  * don't have anything more.  If the server still
    2095             :                  * wants more (shouldn't happen according to the
    2096             :                  * protocol) we break out of the loop (via the
    2097             :                  * continue).  The assertion at the end will then go
    2098             :                  * off. */
    2099           6 :                 if (mapi_query_done(hdl) == MMORE &&
    2100           0 :                     (length > 0 || mapi_query_done(hdl) == MMORE))
    2101           0 :                         continue;       /* get more data */
    2102             : 
    2103           6 :                 CHECK_RESULT(mid, hdl, buf, fp);
    2104             : 
    2105           6 :                 rc = format_result(mid, hdl, false);
    2106             : 
    2107           6 :                 if (rc == MMORE && (length > 0 || mapi_query_done(hdl) != MOK))
    2108           0 :                         continue;       /* get more data */
    2109             : 
    2110           6 :                 CHECK_RESULT(mid, hdl, buf, fp);
    2111             : 
    2112           6 :                 mapi_close_handle(hdl);
    2113             :                 hdl = NULL;
    2114             : 
    2115           6 :         } while (length > 0);
    2116             :         /* reached on end of file */
    2117           6 :         if (hdl)
    2118           0 :                 mapi_close_handle(hdl);
    2119           6 :         timerEnd();
    2120             : 
    2121           6 :         free(buf);
    2122           6 :         mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
    2123           6 :         if (fp)
    2124           6 :                 close_stream(fp);
    2125           6 :         return errseen;
    2126             : }
    2127             : 
    2128             : /* The options available for controlling input and rendering depends
    2129             :  * on the language mode. */
    2130             : 
    2131             : static void
    2132           0 : showCommands(void)
    2133             : {
    2134             :         /* shared control options */
    2135           0 :         mnstr_printf(toConsole, "\\?       - show this message\n");
    2136           0 :         if (mode == MAL)
    2137           0 :                 mnstr_printf(toConsole, "?pat     - MAL function help. pat=[modnme[.fcnnme][(][)]] wildcard *\n");
    2138           0 :         mnstr_printf(toConsole, "\\<file   - read input from file\n"
    2139             :                                 "\\>file   - save response in file, or stdout if no file is given\n");
    2140             : #ifdef HAVE_POPEN
    2141           0 :         mnstr_printf(toConsole, "\\|cmd    - pipe result to process, or stop when no command is given\n");
    2142             : #endif
    2143             : #ifdef HAVE_LIBREADLINE
    2144           0 :         mnstr_printf(toConsole, "\\history - show the readline history\n");
    2145             : #endif
    2146           0 :         if (mode == SQL) {
    2147           0 :                 mnstr_printf(toConsole, "\\help    - synopsis of the SQL syntax\n"
    2148             :                                         "\\D table - dumps the table, or the complete database if none given.\n"
    2149             :                                         "\\d[Stvsfn]+ [obj] - list database objects, or describe if obj given\n"
    2150             :                                         "\\A       - enable auto commit\n"
    2151             :                                         "\\a       - disable auto commit\n");
    2152             :         }
    2153           0 :         mnstr_printf(toConsole, "\\e       - echo the query in sql formatting mode\n"
    2154             :                                 "\\t       - set the timer {none,clock,performance} (none is default)\n"
    2155             :                                 "\\f       - format using renderer {csv,tab,raw,sql,xml,trash,rowcount,expanded}\n"
    2156             :                                 "\\w#      - set maximal page width (-1=unlimited, 0=terminal width, >0=limit to num)\n"
    2157             :                                 "\\r#      - set maximum rows per page (-1=raw)\n"
    2158             :                                 "\\L file  - save client-server interaction\n"
    2159             :                                 "\\X       - trace mclient code\n"
    2160             :                                 "\\q       - terminate session and quit mclient\n");
    2161           0 : }
    2162             : 
    2163             : #define MD_TABLE    1
    2164             : #define MD_VIEW     2
    2165             : #define MD_SEQ      4
    2166             : #define MD_FUNC     8
    2167             : #define MD_SCHEMA  16
    2168             : 
    2169             : #define READBLOCK 8192
    2170             : 
    2171             : #ifdef HAVE_LIBREADLINE
    2172             : struct myread_t {
    2173             :         stream *s;
    2174             :         const char *prompt;
    2175             :         char *buf;
    2176             :         size_t read;
    2177             :         size_t len;
    2178             : };
    2179             : 
    2180             : static ssize_t
    2181           0 : myread(void *restrict private, void *restrict buf, size_t elmsize, size_t cnt)
    2182             : {
    2183             :         struct myread_t *p = private;
    2184           0 :         size_t size = elmsize * cnt;
    2185             :         size_t cpsize = size;
    2186             : 
    2187           0 :         assert(elmsize == 1);
    2188           0 :         if (size == 0)
    2189           0 :                 return cnt;
    2190           0 :         if (p->buf == NULL) {
    2191             :                 rl_completion_func_t *func = NULL;
    2192             : 
    2193           0 :                 if (strcmp(p->prompt, "more>") == 0)
    2194           0 :                         func = suspend_completion();
    2195           0 :                 p->buf = readline(p->prompt);
    2196           0 :                 if (func)
    2197           0 :                         continue_completion(func);
    2198           0 :                 if (p->buf == NULL)
    2199             :                         return 0;
    2200           0 :                 p->len = strlen(p->buf);
    2201           0 :                 p->read = 0;
    2202           0 :                 if (p->len > 1)
    2203           0 :                         save_line(p->buf);
    2204             :         }
    2205           0 :         if (p->read < p->len) {
    2206           0 :                 if (p->len - p->read < size)
    2207             :                         cpsize = p->len - p->read;
    2208           0 :                 memcpy(buf, p->buf + p->read, cpsize);
    2209           0 :                 p->read += cpsize;
    2210             :         } else {
    2211             :                 cpsize = 0;
    2212             :         }
    2213           0 :         if (p->read == p->len && cpsize < size) {
    2214           0 :                 ((char *) buf)[cpsize++] = '\n';
    2215           0 :                 free(p->buf);
    2216           0 :                 p->buf = NULL;
    2217             :         }
    2218           0 :         return cpsize / elmsize;
    2219             : }
    2220             : 
    2221             : static void
    2222           0 : mydestroy(void *private)
    2223             : {
    2224             :         struct myread_t *p = private;
    2225             : 
    2226           0 :         if (p->buf)
    2227           0 :                 free(p->buf);
    2228           0 : }
    2229             : #endif
    2230             : 
    2231             : static bool
    2232         170 : doFile(Mapi mid, stream *fp, bool useinserts, bool interactive, bool save_history)
    2233             : {
    2234             :         char *line = NULL;
    2235             :         char *buf = NULL;
    2236             :         size_t length;
    2237             :         size_t bufsiz = 0;
    2238             :         MapiHdl hdl;
    2239             :         MapiMsg rc = MOK;
    2240             :         int lineno = 1;
    2241             :         char *prompt = NULL;
    2242             :         int prepno = 0;
    2243             : #ifdef HAVE_LIBREADLINE
    2244             :         struct myread_t rl;
    2245             : #endif
    2246             :         int fd;
    2247             : 
    2248             :         (void) save_history;    /* not used if no readline */
    2249         170 :         if ((fd = getFileNo(fp)) >= 0 && isatty(fd)
    2250             : #ifdef WIN32                    /* isatty may not give expected result */
    2251             :             && formatter != TESTformatter
    2252             : #endif
    2253             :                 ) {
    2254             :                 interactive = true;
    2255           0 :                 setPrompt();
    2256             :                 prompt = promptbuf;
    2257           0 :                 fromConsole = fp;
    2258             : #ifdef HAVE_LIBREADLINE
    2259           0 :                 init_readline(mid, language, save_history);
    2260           0 :                 rl.s = fp;
    2261           0 :                 rl.buf = NULL;
    2262           0 :                 if ((fp = callback_stream(&rl, myread, NULL, NULL, mydestroy, mnstr_name(fp))) == NULL) {
    2263           0 :                         mnstr_printf(stderr_stream,"Malloc for doFile failed");
    2264           0 :                         exit(2);
    2265             :                 }
    2266             : #endif
    2267             :         }
    2268             : #ifdef HAVE_ICONV
    2269         170 :         if (encoding) {
    2270           0 :                 if ((fp = iconv_rstream(fp, encoding, mnstr_name(fp))) == NULL) {
    2271           0 :                         mnstr_printf(stderr_stream,"Malloc failure");
    2272           0 :                         exit(2);
    2273             :                 }
    2274             :         }
    2275             : #endif
    2276             : 
    2277         170 :         if (!interactive && !echoquery)
    2278           6 :                 return doFileBulk(mid, fp);
    2279             : 
    2280         164 :         hdl = mapi_get_active(mid);
    2281             : 
    2282             :         bufsiz = READBLOCK;
    2283         164 :         buf = malloc(bufsiz);
    2284         164 :         if (buf == NULL) {
    2285           0 :                 mnstr_printf(stderr_stream,"Malloc for doFile failed");
    2286           0 :                 exit(2);
    2287             :         }
    2288             : 
    2289             :         do {
    2290             :                 bool seen_null_byte = false;
    2291             : 
    2292      141458 :                 if (prompt) {
    2293           0 :                         char *p = hdl ? "more>" : prompt;
    2294             :                         /* clear errors when interactive */
    2295           0 :                         errseen = false;
    2296             : #ifdef HAVE_LIBREADLINE
    2297           0 :                         rl.prompt = p;
    2298             : #else
    2299             :                         mnstr_write(toConsole, p, 1, strlen(p));
    2300             : #endif
    2301             :                 }
    2302      141458 :                 mnstr_flush(toConsole, MNSTR_FLUSH_DATA);
    2303      141458 :                 timerPause();
    2304             :                 /* read a line */
    2305             :                 length = 0;
    2306             :                 for (;;) {
    2307             :                         ssize_t l;
    2308             :                         char *newbuf;
    2309      141779 :                         l = mnstr_readline(fp, buf + length, bufsiz - length);
    2310      141779 :                         if (l <= 0)
    2311             :                                 break;
    2312      141550 :                         if (!seen_null_byte && strlen(buf + length) < (size_t) l) {
    2313           1 :                                 mnstr_printf(stderr_stream, "NULL byte in input on line %d of input\n", lineno);
    2314             :                                 seen_null_byte = true;
    2315           1 :                                 errseen = true;
    2316           1 :                                 if (hdl) {
    2317           0 :                                         mapi_close_handle(hdl);
    2318             :                                         hdl = NULL;
    2319             :                                 }
    2320             :                         }
    2321      141550 :                         length += l;
    2322      141550 :                         if (buf[length - 1] == '\n')
    2323             :                                 break;
    2324         321 :                         newbuf = realloc(buf, bufsiz += READBLOCK);
    2325         321 :                         if (newbuf) {
    2326             :                                 buf = newbuf;
    2327             :                         } else {
    2328           0 :                                 mnstr_printf(stderr_stream,"Malloc failure");
    2329             :                                 length = 0;
    2330           0 :                                 errseen = true;
    2331           0 :                                 if (hdl) {
    2332           0 :                                         mapi_close_handle(hdl);
    2333             :                                         hdl = NULL;
    2334             :                                 }
    2335             :                                 break;
    2336             :                         }
    2337             :                 }
    2338             :                 line = buf;
    2339      141458 :                 lineno++;
    2340      141458 :                 if (seen_null_byte)
    2341           1 :                         continue;
    2342      141457 :                 if (length == 0) {
    2343             :                         /* end of file */
    2344         169 :                         if (hdl == NULL) {
    2345             :                                 /* nothing more to do */
    2346         164 :                                 goto bailout;
    2347             :                         }
    2348             : 
    2349             :                         /* hdl != NULL, we should finish the current query */
    2350             :                 }
    2351      141293 :                 if (hdl == NULL && length > 0 && interactive) {
    2352             :                         /* test for special commands */
    2353        9081 :                         if (mode != MAL)
    2354        9757 :                                 while (length > 0 &&
    2355        9372 :                                        (*line == '\f' ||
    2356        8996 :                                         *line == '\n' ||
    2357             :                                         *line == ' ')) {
    2358         676 :                                         line++;
    2359         676 :                                         length--;
    2360             :                                 }
    2361             :                         /* in the switch, use continue if the line was
    2362             :                          * processed, use break to send to server */
    2363        9081 :                         switch (*line) {
    2364             :                         case '\n':
    2365             :                         case '\0':
    2366             :                                 break;
    2367          66 :                         case 'e':
    2368             :                         case 'E':
    2369             :                                 /* a bit of a hack for prepare/exec/deallocate
    2370             :                                  * tests: replace "exec[ute] **" with the
    2371             :                                  * ID of the last prepared statement */
    2372          66 :                                 if (mode == SQL && formatter == TESTformatter) {
    2373          66 :                                         if (strncasecmp(line, "exec **", 7) == 0) {
    2374          61 :                                                 line[5] = prepno < 10 ? ' ' : prepno / 10 + '0';
    2375          61 :                                                 line[6] = prepno % 10 + '0';
    2376           5 :                                         } else if (strncasecmp(line, "execute **", 10) == 0) {
    2377           2 :                                                 line[8] = prepno < 10 ? ' ' : prepno / 10 + '0';
    2378           2 :                                                 line[9] = prepno % 10 + '0';
    2379             :                                         }
    2380             :                                 }
    2381          66 :                                 if (strncasecmp(line, "exit\n", 5) == 0) {
    2382           0 :                                         goto bailout;
    2383             :                                 }
    2384             :                                 break;
    2385          82 :                         case 'd':
    2386             :                         case 'D':
    2387             :                                 /* a bit of a hack for prepare/exec/deallocate
    2388             :                                  * tests: replace "deallocate **" with the
    2389             :                                  * ID of the last prepared statement */
    2390          82 :                                 if (mode == SQL && formatter == TESTformatter && strncasecmp(line, "deallocate **", 13) == 0) {
    2391           3 :                                         line[11] = prepno < 10 ? ' ' : prepno / 10 + '0';
    2392           3 :                                         line[12] = prepno % 10 + '0';
    2393             :                                 }
    2394             :                                 break;
    2395           0 :                         case 'q':
    2396             :                         case 'Q':
    2397           0 :                                 if (strncasecmp(line, "quit\n", 5) == 0) {
    2398           0 :                                         goto bailout;
    2399             :                                 }
    2400             :                                 break;
    2401        3479 :                         case '\\':
    2402        3479 :                                 switch (line[1]) {
    2403           0 :                                 case 'q':
    2404           0 :                                         goto bailout;
    2405           0 :                                 case 'X':
    2406             :                                         /* toggle interaction trace */
    2407           0 :                                         mapi_trace(mid, !mapi_get_trace(mid));
    2408           0 :                                         continue;
    2409           0 :                                 case 'A':
    2410           0 :                                         if (mode != SQL)
    2411             :                                                 break;
    2412           0 :                                         mapi_setAutocommit(mid, true);
    2413           0 :                                         continue;
    2414           0 :                                 case 'a':
    2415           0 :                                         if (mode != SQL)
    2416             :                                                 break;
    2417           0 :                                         mapi_setAutocommit(mid, false);
    2418           0 :                                         continue;
    2419           0 :                                 case 'w':
    2420           0 :                                         pagewidth = atoi(line + 2);
    2421           0 :                                         pagewidthset = pagewidth != 0;
    2422           0 :                                         continue;
    2423           0 :                                 case 'r':
    2424           0 :                                         rowsperpage = atoi(line + 2);
    2425           0 :                                         continue;
    2426        3478 :                                 case 'd': {
    2427             :                                         bool hasWildcard = false;
    2428             :                                         bool hasSchema = false;
    2429             :                                         bool wantsSystem = false;
    2430             :                                         unsigned int x = 0;
    2431             :                                         char *p, *q;
    2432             :                                         bool escaped = false;
    2433        3478 :                                         if (mode != SQL)
    2434             :                                                 break;
    2435        6956 :                                         while (my_isspace(line[length - 1]))
    2436        3478 :                                                 line[--length] = 0;
    2437        3478 :                                         for (line += 2;
    2438       10274 :                                              *line && !my_isspace(*line);
    2439        6796 :                                              line++) {
    2440        6796 :                                                 switch (*line) {
    2441         405 :                                                 case 't':
    2442         405 :                                                         x |= MD_TABLE;
    2443         405 :                                                         break;
    2444         784 :                                                 case 'v':
    2445         784 :                                                         x |= MD_VIEW;
    2446         784 :                                                         break;
    2447          22 :                                                 case 's':
    2448          22 :                                                         x |= MD_SEQ;
    2449          22 :                                                         break;
    2450        2157 :                                                 case 'f':
    2451        2157 :                                                         x |= MD_FUNC;
    2452        2157 :                                                         break;
    2453          97 :                                                 case 'n':
    2454          97 :                                                         x |= MD_SCHEMA;
    2455          97 :                                                         break;
    2456             :                                                 case 'S':
    2457             :                                                         wantsSystem = true;
    2458             :                                                         break;
    2459           0 :                                                 default:
    2460           0 :                                                         mnstr_printf(stderr_stream, "unknown sub-command for \\d: %c\n", *line);
    2461             :                                                         length = 0;
    2462           0 :                                                         line[1] = '\0';
    2463           0 :                                                         break;
    2464             :                                                 }
    2465             :                                         }
    2466        3478 :                                         if (length == 0)
    2467           0 :                                                 continue;
    2468        3478 :                                         if (x == 0) /* default to tables and views */
    2469             :                                                 x = MD_TABLE | MD_VIEW;
    2470        6846 :                                         for ( ; *line && my_isspace(*line); line++)
    2471             :                                                 ;
    2472             : 
    2473             :                                         /* lowercase the object, except for quoted parts */
    2474             :                                         q = line;
    2475       63397 :                                         for (p = line; *p != '\0'; p++) {
    2476       59919 :                                                 if (*p == '"') {
    2477        4270 :                                                         if (escaped) {
    2478        2135 :                                                                 if (*(p + 1) == '"') {
    2479             :                                                                         /* SQL escape */
    2480           0 :                                                                         *q++ = *p++;
    2481             :                                                                 } else {
    2482             :                                                                         escaped = false;
    2483             :                                                                 }
    2484             :                                                         } else {
    2485             :                                                                 escaped = true;
    2486             :                                                         }
    2487             :                                                 } else {
    2488       55649 :                                                         if (!escaped) {
    2489       32289 :                                                                 *q++ = tolower((int) *p);
    2490       32289 :                                                                 if (*p == '*') {
    2491           0 :                                                                         *p = '%';
    2492             :                                                                         hasWildcard = true;
    2493       32289 :                                                                 } else if (*p == '?') {
    2494           0 :                                                                         *p = '_';
    2495             :                                                                         hasWildcard = true;
    2496       32289 :                                                                 } else if (*p == '.') {
    2497             :                                                                         hasSchema = true;
    2498             :                                                                 }
    2499             :                                                         } else {
    2500       23360 :                                                                 *q++ = *p;
    2501             :                                                         }
    2502             :                                                 }
    2503             :                                         }
    2504        3478 :                                         *q = '\0';
    2505        3478 :                                         if (escaped) {
    2506           0 :                                                 mnstr_printf(stderr_stream, "unexpected end of string while "
    2507             :                                                         "looking for matching \"\n");
    2508           0 :                                                 continue;
    2509             :                                         }
    2510             : 
    2511        6846 :                                         if (*line && !hasWildcard) {
    2512             : #ifdef HAVE_POPEN
    2513             :                                                 stream *saveFD;
    2514             : 
    2515        3368 :                                                 start_pager(&saveFD);
    2516             : #endif
    2517        3368 :                                                 if (x & MD_TABLE || x & MD_VIEW)
    2518        1159 :                                                         describe_table(mid, NULL, line, toConsole, 1, false);
    2519        3368 :                                                 if (x & MD_SEQ)
    2520           1 :                                                         describe_sequence(mid, NULL, line, toConsole);
    2521        3368 :                                                 if (x & MD_FUNC)
    2522        2138 :                                                         dump_functions(mid, toConsole, 0, NULL, line, NULL);
    2523        3368 :                                                 if (x & MD_SCHEMA)
    2524          70 :                                                         describe_schema(mid, line, toConsole);
    2525             : #ifdef HAVE_POPEN
    2526        3368 :                                                 end_pager(saveFD);
    2527             : #endif
    2528             :                                         } else {
    2529             :                                                 /* get all object names in current schema */
    2530             :                                                 const char *with_clause =
    2531             :                                                         "with describe_all_objects AS (\n"
    2532             :                                                         "  SELECT s.name AS sname,\n"
    2533             :                                                         "      t.name,\n"
    2534             :                                                         "      s.name || '.' || t.name AS fullname,\n"
    2535             :                                                         "      CAST(CASE t.type\n"
    2536             :                                                         "      WHEN 1 THEN 2\n" /* ntype for views */
    2537             :                                                         "      ELSE 1\n" /* ntype for tables */
    2538             :                                                         "      END AS SMALLINT) AS ntype,\n"
    2539             :                                                         "      (CASE WHEN t.system THEN 'SYSTEM ' ELSE '' END) || tt.table_type_name AS type,\n"
    2540             :                                                         "      t.system,\n"
    2541             :                                                         "      c.remark AS remark\n"
    2542             :                                                         "    FROM sys._tables t\n"
    2543             :                                                         "    LEFT OUTER JOIN sys.comments c ON t.id = c.id\n"
    2544             :                                                         "    LEFT OUTER JOIN sys.schemas s ON t.schema_id = s.id\n"
    2545             :                                                         "    LEFT OUTER JOIN sys.table_types tt ON t.type = tt.table_type_id\n"
    2546             :                                                         "  UNION ALL\n"
    2547             :                                                         "  SELECT s.name AS sname,\n"
    2548             :                                                         "      sq.name,\n"
    2549             :                                                         "      s.name || '.' || sq.name AS fullname,\n"
    2550             :                                                         "      CAST(4 AS SMALLINT) AS ntype,\n"
    2551             :                                                         "      'SEQUENCE' AS type,\n"
    2552             :                                                         "      false AS system,\n"
    2553             :                                                         "      c.remark AS remark\n"
    2554             :                                                         "    FROM sys.sequences sq\n"
    2555             :                                                         "    LEFT OUTER JOIN sys.comments c ON sq.id = c.id\n"
    2556             :                                                         "    LEFT OUTER JOIN sys.schemas s ON sq.schema_id = s.id\n"
    2557             :                                                         "  UNION ALL\n"
    2558             :                                                         "  SELECT DISTINCT s.name AS sname,\n" /* DISTINCT is needed to filter out duplicate overloaded function/procedure names */
    2559             :                                                         "      f.name,\n"
    2560             :                                                         "      s.name || '.' || f.name AS fullname,\n"
    2561             :                                                         "      CAST(8 AS SMALLINT) AS ntype,\n"
    2562             :                                                         "      (CASE WHEN f.system THEN 'SYSTEM ' ELSE '' END) || function_type_keyword AS type,\n"
    2563             :                                                         "      f.system AS system,\n"
    2564             :                                                         "      c.remark AS remark\n"
    2565             :                                                         "    FROM sys.functions f\n"
    2566             :                                                         "    LEFT OUTER JOIN sys.comments c ON f.id = c.id\n"
    2567             :                                                         "    LEFT OUTER JOIN sys.function_types ft ON f.type = ft.function_type_id\n"
    2568             :                                                         "    LEFT OUTER JOIN sys.schemas s ON f.schema_id = s.id\n"
    2569             :                                                         "  UNION ALL\n"
    2570             :                                                         "  SELECT NULL AS sname,\n"
    2571             :                                                         "      s.name,\n"
    2572             :                                                         "      s.name AS fullname,\n"
    2573             :                                                         "      CAST(16 AS SMALLINT) AS ntype,\n"
    2574             :                                                         "      (CASE WHEN s.system THEN 'SYSTEM SCHEMA' ELSE 'SCHEMA' END) AS type,\n"
    2575             :                                                         "      s.system,\n"
    2576             :                                                         "      c.remark AS remark\n"
    2577             :                                                         "    FROM sys.schemas s\n"
    2578             :                                                         "    LEFT OUTER JOIN sys.comments c ON s.id = c.id\n"
    2579             :                                                         "  ORDER BY system, name, sname, ntype)\n"
    2580             :                                                         ;
    2581         110 :                                                 size_t len = strlen(with_clause) + 400 + strlen(line);
    2582         110 :                                                 char *query = malloc(len);
    2583         110 :                                                 char *q = query, *endq = query + len;
    2584             : 
    2585         110 :                                                 if (query == NULL) {
    2586           0 :                                                         mnstr_printf(stderr_stream, "memory allocation failure\n");
    2587           0 :                                                         continue;
    2588             :                                                 }
    2589             : 
    2590             :                                                 /*
    2591             :                                                  * | LINE            | SCHEMA FILTER | NAME FILTER                   |
    2592             :                                                  * |-----------------+---------------+-------------------------------|
    2593             :                                                  * | ""              | yes           | -                             |
    2594             :                                                  * | "my_table"      | yes           | name LIKE 'my_table'          |
    2595             :                                                  * | "my*"           | yes           | name LIKE 'my%'               |
    2596             :                                                  * | "data.my_table" | no            | fullname LIKE 'data.my_table' |
    2597             :                                                  * | "data.my*"      | no            | fullname LIKE 'data.my%'      |
    2598             :                                                  * | "*a.my*"        | no            | fullname LIKE '%a.my%'        |
    2599             :                                                  */
    2600         110 :                                                 q += snprintf(q, endq - q, "%s", with_clause);
    2601         110 :                                                 q += snprintf(q, endq - q, " SELECT type, fullname, remark FROM describe_all_objects WHERE (ntype & %u) > 0", x);
    2602         110 :                                                 if (!wantsSystem) {
    2603          65 :                                                         q += snprintf(q, endq - q, " AND NOT system");
    2604             :                                                 }
    2605         110 :                                                 if (!hasSchema) {
    2606         110 :                                                         q += snprintf(q, endq - q, " AND (sname IS NULL OR sname = current_schema)");
    2607             :                                                 }
    2608         110 :                                                 if (*line) {
    2609           0 :                                                         q += snprintf(q, endq - q, " AND (%s LIKE '%s')", (hasSchema ? "fullname" : "name"), line);
    2610             :                                                 }
    2611         110 :                                                 q += snprintf(q, endq - q, " ORDER BY fullname, type, remark");
    2612             : 
    2613         110 :                                                 hdl = mapi_query(mid, query);
    2614         110 :                                                 free(query);
    2615         110 :                                                 CHECK_RESULT(mid, hdl, buf, fp);
    2616        5268 :                                                 while (fetch_row(hdl) == 3) {
    2617        5158 :                                                         char *type = mapi_fetch_field(hdl, 0);
    2618        5158 :                                                         char *name = mapi_fetch_field(hdl, 1);
    2619        5158 :                                                         char *remark = mapi_fetch_field(hdl, 2);
    2620        5158 :                                                         int type_width = mapi_get_len(hdl, 0);
    2621        5158 :                                                         int name_width = mapi_get_len(hdl, 1);
    2622        5158 :                                                         mnstr_printf(toConsole,
    2623             :                                                                      "%-*s  %-*s",
    2624             :                                                                      type_width, type,
    2625        5158 :                                                                      name_width * (remark != NULL), name);
    2626        5158 :                                                         if (remark) {
    2627             :                                                                 char *c;
    2628          21 :                                                                 mnstr_printf(toConsole, "  '");
    2629         222 :                                                                 for (c = remark; *c; c++) {
    2630         201 :                                                                         switch (*c) {
    2631           0 :                                                                         case '\'':
    2632           0 :                                                                                 mnstr_printf(toConsole, "''");
    2633           0 :                                                                                 break;
    2634         201 :                                                                         default:
    2635         201 :                                                                                 mnstr_writeChr(toConsole, *c);
    2636             :                                                                         }
    2637             :                                                                 }
    2638          21 :                                                                 mnstr_printf(toConsole, "'");
    2639             :                                                         }
    2640        5158 :                                                         mnstr_printf(toConsole, "\n");
    2641             : 
    2642             :                                                 }
    2643         110 :                                                 mapi_close_handle(hdl);
    2644             :                                                 hdl = NULL;
    2645             :                                         }
    2646        3478 :                                         continue;
    2647             :                                 }
    2648           0 :                                 case 'D':{
    2649             : #ifdef HAVE_POPEN
    2650             :                                         stream *saveFD;
    2651             : #endif
    2652             : 
    2653           0 :                                         if (mode != SQL)
    2654             :                                                 break;
    2655           0 :                                         while (my_isspace(line[length - 1]))
    2656           0 :                                                 line[--length] = 0;
    2657           0 :                                         if (line[2] && !my_isspace(line[2])) {
    2658           0 :                                                 mnstr_printf(stderr_stream, "space required after \\D\n");
    2659           0 :                                                 continue;
    2660             :                                         }
    2661           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2662             :                                                 ;
    2663             : #ifdef HAVE_POPEN
    2664           0 :                                         start_pager(&saveFD);
    2665             : #endif
    2666           0 :                                         if (*line) {
    2667           0 :                                                 mnstr_printf(toConsole, "START TRANSACTION;\n");
    2668           0 :                                                 dump_table(mid, NULL, line, toConsole, false, true, useinserts, false, false);
    2669           0 :                                                 mnstr_printf(toConsole, "COMMIT;\n");
    2670             :                                         } else
    2671           0 :                                                 dump_database(mid, toConsole, 0, useinserts, false);
    2672             : #ifdef HAVE_POPEN
    2673           0 :                                         end_pager(saveFD);
    2674             : #endif
    2675           0 :                                         continue;
    2676             :                                 }
    2677             :                                 case '<': {
    2678             :                                         stream *s;
    2679             :                                         /* read commands from file */
    2680           0 :                                         while (my_isspace(line[length - 1]))
    2681           0 :                                                 line[--length] = 0;
    2682           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2683             :                                                 ;
    2684             :                                         /* use open_rastream to
    2685             :                                          * convert filename from UTF-8
    2686             :                                          * to locale */
    2687           0 :                                         if ((s = open_rastream(line)) == NULL ||
    2688           0 :                                             mnstr_errnr(s)) {
    2689           0 :                                                 if (s)
    2690           0 :                                                         close_stream(s);
    2691           0 :                                                 mnstr_printf(stderr_stream, "Cannot open %s: %s\n", line, mnstr_peek_error(NULL));
    2692             :                                         } else
    2693           0 :                                                 doFile(mid, s, 0, 0, 0);
    2694           0 :                                         continue;
    2695             :                                 }
    2696             :                                 case '>':
    2697             :                                         /* redirect output to file */
    2698           0 :                                         while (my_isspace(line[length - 1]))
    2699           0 :                                                 line[--length] = 0;
    2700           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2701             :                                                 ;
    2702           0 :                                         if (toConsole != stdout_stream &&
    2703           0 :                                             toConsole != stderr_stream) {
    2704           0 :                                                 close_stream(toConsole);
    2705             :                                         }
    2706           0 :                                         if (*line == 0 ||
    2707           0 :                                             strcmp(line, "stdout") == 0)
    2708           0 :                                                 toConsole = stdout_stream;
    2709           0 :                                         else if (strcmp(line, "stderr") == 0)
    2710           0 :                                                 toConsole = stderr_stream;
    2711           0 :                                         else if ((toConsole = open_wastream(line)) == NULL ||
    2712           0 :                                                  mnstr_errnr(toConsole)) {
    2713           0 :                                                 mnstr_printf(stderr_stream, "Cannot open %s: %s\n", line, mnstr_peek_error(toConsole));
    2714           0 :                                                 if (toConsole != NULL) {
    2715           0 :                                                         close_stream(toConsole);
    2716             :                                                 }
    2717           0 :                                                 toConsole = stdout_stream;
    2718             :                                         }
    2719           0 :                                         continue;
    2720           0 :                                 case 'L':
    2721           0 :                                         free(logfile);
    2722           0 :                                         logfile = NULL;
    2723           0 :                                         while (my_isspace(line[length - 1]))
    2724           0 :                                                 line[--length] = 0;
    2725           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2726             :                                                 ;
    2727           0 :                                         if (*line == 0) {
    2728             :                                                 /* turn of logging */
    2729           0 :                                                 mapi_log(mid, NULL);
    2730             :                                         } else {
    2731           0 :                                                 logfile = strdup(line);
    2732           0 :                                                 mapi_log(mid, logfile);
    2733             :                                         }
    2734           0 :                                         continue;
    2735           0 :                                 case '?':
    2736           0 :                                         showCommands();
    2737           0 :                                         continue;
    2738             : #ifdef HAVE_POPEN
    2739           0 :                                 case '|':
    2740           0 :                                         if (pager)
    2741           0 :                                                 free(pager);
    2742           0 :                                         pager = NULL;
    2743           0 :                                         setWidth();     /* reset to system default */
    2744             : 
    2745           0 :                                         while (my_isspace(line[length - 1]))
    2746           0 :                                                 line[--length] = 0;
    2747           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2748             :                                                 ;
    2749           0 :                                         if (*line == 0)
    2750           0 :                                                 continue;
    2751           0 :                                         pager = strdup(line);
    2752           0 :                                         continue;
    2753             : #endif
    2754           0 :                                 case 'h':
    2755             :                                 {
    2756             : #ifdef HAVE_LIBREADLINE
    2757             :                                         int h;
    2758             :                                         char *nl;
    2759             : 
    2760           0 :                                         if (strcmp(line,"\\history\n") == 0) {
    2761           0 :                                                 for (h = 0; h < history_length; h++) {
    2762           0 :                                                         nl = history_get(h) ? history_get(h)->line : 0;
    2763           0 :                                                         if (nl)
    2764           0 :                                                                 mnstr_printf(toConsole, "%d %s\n", h, nl);
    2765             :                                                 }
    2766             :                                         } else
    2767             : #endif
    2768             :                                         {
    2769           0 :                                                 setWidth();
    2770           0 :                                                 sql_help(line, toConsole, pagewidth <= 0 ? DEFWIDTH : pagewidth);
    2771             :                                         }
    2772           0 :                                         continue;
    2773             :                                 }
    2774             : #if 0 /* for later */
    2775             : #ifdef HAVE_LIBREADLINE
    2776             :                                 case '!':
    2777             :                                 {
    2778             :                                         char *nl;
    2779             : 
    2780             :                                         nl = strchr(line, '\n');
    2781             :                                         if (nl)
    2782             :                                                 *nl = 0;
    2783             :                                         if (history_expand(line + 2, &nl)) {
    2784             :                                                 mnstr_printf(toConsole, "%s\n", nl);
    2785             :                                         }
    2786             :                                         mnstr_printf(toConsole, "Expansion needs work\n");
    2787             :                                         continue;
    2788             :                                 }
    2789             : #endif
    2790             : #endif  /* 0 */
    2791           0 :                                 case 'e':
    2792           0 :                                         echoquery = true;
    2793           0 :                                         continue;
    2794             :                                 case 'f':
    2795           2 :                                         while (my_isspace(line[length - 1]))
    2796           1 :                                                 line[--length] = 0;
    2797           2 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2798             :                                                 ;
    2799           1 :                                         if (*line == 0) {
    2800           0 :                                                 mnstr_printf(toConsole, "Current formatter: ");
    2801           0 :                                                 switch (formatter) {
    2802           0 :                                                 case RAWformatter:
    2803           0 :                                                         mnstr_printf(toConsole, "raw\n");
    2804           0 :                                                         break;
    2805           0 :                                                 case TABLEformatter:
    2806           0 :                                                         mnstr_printf(toConsole, "sql\n");
    2807           0 :                                                         break;
    2808           0 :                                                 case CSVformatter:
    2809           0 :                                                         mnstr_printf(toConsole, "%s\n", separator[0] == '\t' ? "tab" : "csv");
    2810           0 :                                                         break;
    2811           0 :                                                 case TRASHformatter:
    2812           0 :                                                         mnstr_printf(toConsole, "trash\n");
    2813           0 :                                                         break;
    2814           0 :                                                 case ROWCOUNTformatter:
    2815           0 :                                                         mnstr_printf(toConsole, "rowcount\n");
    2816           0 :                                                         break;
    2817           0 :                                                 case XMLformatter:
    2818           0 :                                                         mnstr_printf(toConsole, "xml\n");
    2819           0 :                                                         break;
    2820           0 :                                                 case EXPANDEDformatter:
    2821           0 :                                                         mnstr_printf(toConsole, "expanded\n");
    2822           0 :                                                         break;
    2823           0 :                                                 default:
    2824           0 :                                                         mnstr_printf(toConsole, "none\n");
    2825           0 :                                                         break;
    2826             :                                                 }
    2827             :                                         } else
    2828           1 :                                                 setFormatter(line);
    2829           1 :                                         continue;
    2830             :                                 case 't':
    2831           0 :                                         while (my_isspace(line[length - 1]))
    2832           0 :                                                 line[--length] = 0;
    2833           0 :                                         for (line += 2; *line && my_isspace(*line); line++)
    2834             :                                                 ;
    2835           0 :                                         if (*line == 0) {
    2836           0 :                                                 mnstr_printf(toConsole, "Current time formatter: ");
    2837           0 :                                                 if (timermode == T_NONE)
    2838           0 :                                                         mnstr_printf(toConsole,"none\n");
    2839           0 :                                                 if (timermode == T_CLOCK)
    2840           0 :                                                         mnstr_printf(toConsole,"clock\n");
    2841           0 :                                                 if (timermode == T_PERF)
    2842           0 :                                                         mnstr_printf(toConsole,"performance\n");
    2843           0 :                                         } else if (strcmp(line,"none") == 0) {
    2844           0 :                                                 timermode = T_NONE;
    2845           0 :                                         } else if (strcmp(line,"clock") == 0) {
    2846           0 :                                                 timermode = T_CLOCK;
    2847           0 :                                         } else if (strncmp(line,"perf",4) == 0 || strcmp(line,"performance") == 0) {
    2848           0 :                                                 timermode = T_PERF;
    2849             :                                         } else if (*line != '\0') {
    2850           0 :                                                 mnstr_printf(stderr_stream, "warning: invalid argument to -t: %s\n",
    2851             :                                                         line);
    2852             :                                         }
    2853           0 :                                         continue;
    2854           0 :                                 default:
    2855           0 :                                         showCommands();
    2856           0 :                                         continue;
    2857             :                                 }
    2858      132212 :                         }
    2859             :                 }
    2860             : 
    2861      137814 :                 if (hdl == NULL) {
    2862             :                         timerStart();
    2863        5603 :                         hdl = mapi_query_prep(mid);
    2864        5603 :                         CHECK_RESULT(mid, hdl, buf, fp);
    2865             :                 } else
    2866      132211 :                         timerResume();
    2867             : 
    2868      137814 :                 assert(hdl != NULL);
    2869             : 
    2870      137814 :                 if (length > 0) {
    2871      137424 :                         SQLsetSpecial(line);
    2872      137424 :                         mapi_query_part(hdl, line, length);
    2873      137814 :                         CHECK_RESULT(mid, hdl, buf, fp);
    2874             :                 }
    2875             : 
    2876             :                 /* If the server wants more but we're at the
    2877             :                  * end of file (line == NULL), notify the
    2878             :                  * server that we don't have anything more.
    2879             :                  * If the server still wants more (shouldn't
    2880             :                  * happen according to the protocol) we break
    2881             :                  * out of the loop (via the continue).  The
    2882             :                  * assertion at the end will then go off. */
    2883      137814 :                 if (mapi_query_done(hdl) == MMORE) {
    2884      132208 :                         if (line != NULL) {
    2885      132208 :                                 continue;       /* get more data */
    2886           0 :                         } else if (mapi_query_done(hdl) == MMORE) {
    2887             :                                 hdl = NULL;
    2888           0 :                                 continue;       /* done */
    2889             :                         }
    2890             :                 }
    2891        5606 :                 CHECK_RESULT(mid, hdl, buf, fp);
    2892             : 
    2893        5606 :                 if (mapi_get_querytype(hdl) == Q_PREPARE) {
    2894          98 :                         prepno = mapi_get_tableid(hdl);
    2895          98 :                         assert(mode != SQL || formatter != TESTformatter || prepno < 100); /* prepno is used only at the TestWeb */
    2896             :                 }
    2897             : 
    2898        5606 :                 rc = format_result(mid, hdl, interactive || echoquery);
    2899             : 
    2900        5606 :                 if (rc == MMORE && (line != NULL || mapi_query_done(hdl) != MOK))
    2901           3 :                         continue;       /* get more data */
    2902             : 
    2903        5603 :                 CHECK_RESULT(mid, hdl, buf, fp);
    2904             : 
    2905        5603 :                 timerEnd();
    2906        5603 :                 mapi_close_handle(hdl);
    2907             :                 hdl = NULL;
    2908      141294 :         } while (line != NULL);
    2909             :         /* reached on end of file */
    2910           0 :         assert(hdl == NULL);
    2911         164 :   bailout:
    2912         164 :         free(buf);
    2913             : #ifdef HAVE_LIBREADLINE
    2914         164 :         if (prompt)
    2915           0 :                 deinit_readline();
    2916             : #endif
    2917         164 :         close_stream(fp);
    2918         164 :         return errseen;
    2919             : }
    2920             : 
    2921             : struct privdata {
    2922             :         stream *f;
    2923             :         char *buf;
    2924             : };
    2925             : 
    2926             : #define READSIZE        (1 << 16)
    2927             : //#define READSIZE      (1 << 20)
    2928             : 
    2929             : static char alpha[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    2930             :         "abcdefghijklmnopqrstuvwxyz";
    2931             : 
    2932             : static char *
    2933        5257 : getfile(void *data, const char *filename, bool binary,
    2934             :         uint64_t offset, size_t *size)
    2935             : {
    2936             :         stream *f;
    2937             :         char *buf;
    2938             :         struct privdata *priv = data;
    2939             :         ssize_t s;
    2940             : 
    2941        5257 :         if (size)
    2942        5256 :                 *size = 0;      /* most returns require this */
    2943        5257 :         if (priv->buf == NULL) {
    2944          18 :                 priv->buf = malloc(READSIZE);
    2945          18 :                 if (priv->buf == NULL)
    2946             :                         return "allocation failed in client";
    2947             :         }
    2948        5257 :         buf = priv->buf;
    2949        5257 :         if (filename != NULL) {
    2950          50 :                 if (binary) {
    2951          50 :                         f = open_rstream(filename);
    2952          50 :                         assert(offset <= 1);
    2953             :                         offset = 0;
    2954             :                 } else {
    2955           0 :                         f = open_rastream(filename);
    2956           0 :                         if (f == NULL) {
    2957             :                                 size_t x;
    2958             :                                 /* simplistic check for URL
    2959             :                                  * (schema://...) */
    2960           0 :                                 if ((x = strspn(filename, alpha)) > 0
    2961           0 :                                     && filename[x] == ':'
    2962           0 :                                     && filename[x+1] == '/'
    2963           0 :                                     && filename[x+2] == '/') {
    2964           0 :                                         if (allow_remote)
    2965           0 :                                                 f = open_urlstream(filename);
    2966             :                                         else
    2967             :                                                 return "client refuses to retrieve remote content";
    2968             :                                 }
    2969             :                         }
    2970             : #ifdef HAVE_ICONV
    2971           0 :                         else if (encoding) {
    2972             :                                 stream *tmpf = f;
    2973           0 :                                 f = iconv_rstream(f, encoding, mnstr_name(f));
    2974           0 :                                 if (f == NULL)
    2975           0 :                                         close_stream(tmpf);
    2976             :                         }
    2977             : #endif
    2978             :                 }
    2979          50 :                 if (f == NULL)
    2980           0 :                         return (char*) mnstr_peek_error(NULL);
    2981          50 :                 while (offset > 1) {
    2982           0 :                         s = mnstr_readline(f, buf, READSIZE);
    2983           0 :                         if (s < 0) {
    2984           0 :                                 close_stream(f);
    2985           0 :                                 return "error reading file";
    2986             :                         }
    2987           0 :                         if (s == 0) {
    2988             :                                 /* reached EOF withing offset lines */
    2989           0 :                                 close_stream(f);
    2990           0 :                                 return NULL;
    2991             :                         }
    2992           0 :                         if (buf[s - 1] == '\n')
    2993           0 :                                 offset--;
    2994             :                 }
    2995          50 :                 priv->f = f;
    2996             :         } else {
    2997        5207 :                 f = priv->f;
    2998        5207 :                 if (size == NULL) {
    2999             :                         /* done reading before reaching EOF */
    3000           1 :                         close_stream(f);
    3001           1 :                         priv->f = NULL;
    3002           1 :                         return NULL;
    3003             :                 }
    3004             :         }
    3005        5256 :         s = mnstr_read(f, buf, 1, READSIZE);
    3006        5256 :         if (s <= 0) {
    3007          49 :                 close_stream(f);
    3008          49 :                 priv->f = NULL;
    3009          49 :                 return s < 0 ? "error reading file" : NULL;
    3010             :         }
    3011        5207 :         if (size)
    3012        5207 :                 *size = (size_t) s;
    3013             :         return buf;
    3014             : }
    3015             : 
    3016             : static char *
    3017           0 : putfile(void *data, const char *filename, const void *buf, size_t bufsize)
    3018             : {
    3019             :         struct privdata *priv = data;
    3020             : 
    3021           0 :         if (filename != NULL) {
    3022           0 :                 if ((priv->f = open_wastream(filename)) == NULL)
    3023           0 :                         return (char*)mnstr_peek_error(NULL);
    3024             : #ifdef HAVE_ICONV
    3025           0 :                 if (encoding) {
    3026             :                         stream *f = priv->f;
    3027           0 :                         priv->f = iconv_wstream(f, encoding, mnstr_name(f));
    3028           0 :                         if (priv->f == NULL) {
    3029           0 :                                 close_stream(f);
    3030           0 :                                 return (char*)mnstr_peek_error(NULL);
    3031             :                         }
    3032             :                 }
    3033             : #endif
    3034           0 :                 if (buf == NULL || bufsize == 0)
    3035             :                         return NULL; /* successfully opened file */
    3036           0 :         } else if (buf == NULL) {
    3037             :                 /* done writing */
    3038           0 :                 int flush = mnstr_flush(priv->f, MNSTR_FLUSH_DATA);
    3039           0 :                 close_stream(priv->f);
    3040           0 :                 priv->f = NULL;
    3041           0 :                 return flush < 0 ? "error writing output" : NULL;
    3042             :         }
    3043           0 :         if (mnstr_write(priv->f, buf, 1, bufsize) < (ssize_t) bufsize) {
    3044           0 :                 close_stream(priv->f);
    3045           0 :                 priv->f = NULL;
    3046           0 :                 return "error writing output";
    3047             :         }
    3048             :         return NULL;            /* success */
    3049             : }
    3050             : 
    3051             : static _Noreturn void usage(const char *prog, int xit);
    3052             : 
    3053             : static void
    3054           0 : usage(const char *prog, int xit)
    3055             : {
    3056           0 :         mnstr_printf(stderr_stream, "Usage: %s [ options ] [ file or database [ file ... ] ]\n", prog);
    3057           0 :         mnstr_printf(stderr_stream, "\nOptions are:\n");
    3058             : #ifdef HAVE_SYS_UN_H
    3059           0 :         mnstr_printf(stderr_stream, " -h hostname | --host=hostname    host or UNIX domain socket to connect to\n");
    3060             : #else
    3061             :         mnstr_printf(stderr_stream, " -h hostname | --host=hostname    host to connect to\n");
    3062             : #endif
    3063           0 :         mnstr_printf(stderr_stream, " -p portnr   | --port=portnr      port to connect to\n");
    3064           0 :         mnstr_printf(stderr_stream, " -u user     | --user=user        user id\n");
    3065           0 :         mnstr_printf(stderr_stream, " -d database | --database=database  database to connect to (may be URI)\n");
    3066             : 
    3067           0 :         mnstr_printf(stderr_stream, " -e          | --echo             echo the query\n");
    3068             : #ifdef HAVE_ICONV
    3069           0 :         mnstr_printf(stderr_stream, " -E charset  | --encoding=charset specify encoding (character set) of the terminal\n");
    3070             : #endif
    3071           0 :         mnstr_printf(stderr_stream, " -f kind     | --format=kind      specify output format {csv,tab,raw,sql,xml,trash,rowcount}\n");
    3072           0 :         mnstr_printf(stderr_stream, " -H          | --history          load/save cmdline history (default off)\n");
    3073           0 :         mnstr_printf(stderr_stream, " -i          | --interactive      interpret `\\' commands on stdin\n");
    3074           0 :         mnstr_printf(stderr_stream, " -t          | --timer=format     use time formatting {none,clock,performance} (none is default)\n");
    3075           0 :         mnstr_printf(stderr_stream, " -l language | --language=lang    {sql,mal}\n");
    3076           0 :         mnstr_printf(stderr_stream, " -L logfile  | --log=logfile      save client/server interaction\n");
    3077           0 :         mnstr_printf(stderr_stream, " -s stmt     | --statement=stmt   run single statement\n");
    3078           0 :         mnstr_printf(stderr_stream, " -X          | --Xdebug           trace mapi network interaction\n");
    3079           0 :         mnstr_printf(stderr_stream, " -z          | --timezone         do not tell server our timezone\n");
    3080             : #ifdef HAVE_POPEN
    3081           0 :         mnstr_printf(stderr_stream, " -| cmd      | --pager=cmd        for pagination\n");
    3082             : #endif
    3083           0 :         mnstr_printf(stderr_stream, " -v          | --version          show version information and exit\n");
    3084           0 :         mnstr_printf(stderr_stream, " -?          | --help             show this usage message\n");
    3085             : 
    3086           0 :         mnstr_printf(stderr_stream, "\nSQL specific opions \n");
    3087           0 :         mnstr_printf(stderr_stream, " -n nullstr  | --null=nullstr     change NULL representation for sql, csv and tab output modes\n");
    3088           0 :         mnstr_printf(stderr_stream, " -a          | --autocommit       turn off autocommit mode\n");
    3089           0 :         mnstr_printf(stderr_stream, " -R          | --allow-remote     allow remote content\n");
    3090           0 :         mnstr_printf(stderr_stream, " -r nr       | --rows=nr          for pagination\n");
    3091           0 :         mnstr_printf(stderr_stream, " -w nr       | --width=nr         for pagination\n");
    3092           0 :         mnstr_printf(stderr_stream, " -D          | --dump             create an SQL dump\n");
    3093           0 :         mnstr_printf(stderr_stream, " -N          | --inserts          use INSERT INTO statements when dumping\n");
    3094           0 :         mnstr_printf(stderr_stream, "The file argument can be - for stdin\n");
    3095           0 :         exit(xit);
    3096             : }
    3097             : 
    3098             : static inline bool
    3099           0 : isfile(FILE *fp)
    3100             : {
    3101             :         struct stat stb;
    3102           0 :         if (fstat(fileno(fp), &stb) < 0 ||
    3103           0 :             (stb.st_mode & S_IFMT) != S_IFREG) {
    3104           0 :                 fclose(fp);
    3105           0 :                 return false;
    3106             :         }
    3107             :         return true;
    3108             : }
    3109             : 
    3110             : int
    3111             : #ifdef _MSC_VER
    3112             : wmain(int argc, wchar_t **wargv)
    3113             : #else
    3114         190 : main(int argc, char **argv)
    3115             : #endif
    3116             : {
    3117             :         int port = 0;
    3118             :         char *user = NULL;
    3119             :         char *passwd = NULL;
    3120             :         char *host = NULL;
    3121             :         char *command = NULL;
    3122             :         char *dbname = NULL;
    3123             :         char *output = NULL;    /* output format as string */
    3124         190 :         DotMonetdb dotfile = {0};
    3125             :         stream *s = NULL;
    3126             :         bool trace = false;
    3127             :         bool dump = false;
    3128             :         bool useinserts = false;
    3129             :         int c = 0;
    3130             :         Mapi mid;
    3131             :         bool save_history = false;
    3132             :         bool interactive = false;
    3133             :         bool has_fileargs = false;
    3134         190 :         int option_index = 0;
    3135             :         bool settz = true;
    3136             :         bool autocommit = true; /* autocommit mode default on */
    3137             :         bool user_set_as_flag = false;
    3138             :         bool passwd_set_as_flag = false;
    3139             :         static struct option long_options[] = {
    3140             :                 {"autocommit", 0, 0, 'a'},
    3141             :                 {"database", 1, 0, 'd'},
    3142             :                 {"dump", 0, 0, 'D'},
    3143             :                 {"inserts", 0, 0, 'N'},
    3144             :                 {"echo", 0, 0, 'e'},
    3145             : #ifdef HAVE_ICONV
    3146             :                 {"encoding", 1, 0, 'E'},
    3147             : #endif
    3148             :                 {"format", 1, 0, 'f'},
    3149             :                 {"help", 0, 0, '?'},
    3150             :                 {"history", 0, 0, 'H'},
    3151             :                 {"host", 1, 0, 'h'},
    3152             :                 {"interactive", 0, 0, 'i'},
    3153             :                 {"timer", 1, 0, 't'},
    3154             :                 {"language", 1, 0, 'l'},
    3155             :                 {"log", 1, 0, 'L'},
    3156             :                 {"null", 1, 0, 'n'},
    3157             : #ifdef HAVE_POPEN
    3158             :                 {"pager", 1, 0, '|'},
    3159             : #endif
    3160             :                 {"port", 1, 0, 'p'},
    3161             :                 {"rows", 1, 0, 'r'},
    3162             :                 {"statement", 1, 0, 's'},
    3163             :                 {"user", 1, 0, 'u'},
    3164             :                 {"version", 0, 0, 'v'},
    3165             :                 {"width", 1, 0, 'w'},
    3166             :                 {"Xdebug", 0, 0, 'X'},
    3167             :                 {"timezone", 0, 0, 'z'},
    3168             :                 {"allow-remote", 0, 0, 'R'},
    3169             :                 {0, 0, 0, 0}
    3170             :         };
    3171             : 
    3172             : #ifdef _MSC_VER
    3173             :         char **argv = malloc((argc + 1) * sizeof(char *));
    3174             :         if (argv == NULL) {
    3175             :                 fprintf(stderr, "cannot allocate memory for argument conversion\n");
    3176             :                 exit(1);
    3177             :         }
    3178             :         for (int i = 0; i < argc; i++) {
    3179             :                 if ((argv[i] = wchartoutf8(wargv[i])) == NULL) {
    3180             :                         fprintf(stderr, "cannot convert argument to UTF-8\n");
    3181             :                         exit(1);
    3182             :                 }
    3183             :         }
    3184             :         argv[argc] = NULL;
    3185             : #endif
    3186             : #ifndef WIN32
    3187             :         /* don't set locale on Windows: setting the locale like this
    3188             :          * causes the output to be converted (we could set it to
    3189             :          * ".OCP" if we knew for sure that we were running in a cmd
    3190             :          * window) */
    3191         190 :         if(setlocale(LC_CTYPE, "") == NULL) {
    3192           0 :                 fprintf(stderr, "error: could not set locale\n");
    3193           0 :                 exit(2);
    3194             :         }
    3195             : #endif
    3196             : #ifdef HAVE_SIGACTION
    3197             :         struct sigaction act;
    3198             :         /* ignore SIGPIPE so that we get an error instead of signal */
    3199         190 :         act.sa_handler = SIG_IGN;
    3200         190 :         (void) sigemptyset(&act.sa_mask);
    3201         190 :         act.sa_flags = 0;
    3202         190 :         if (sigaction(SIGPIPE, &act, NULL) == -1)
    3203           0 :                 perror("sigaction");
    3204             : #endif
    3205             : 
    3206         190 :         if (mnstr_init() < 0) {
    3207           0 :                 fprintf(stderr, "error: could not initialize streams library");
    3208           0 :                 exit(2);
    3209             :         }
    3210             : 
    3211         190 :         toConsole = stdout_stream = stdout_wastream();
    3212         190 :         stderr_stream = stderr_wastream();
    3213         190 :         if(!stdout_stream || !stderr_stream) {
    3214           0 :                 if(stdout_stream)
    3215           0 :                         close_stream(stdout_stream);
    3216           0 :                 if(stderr_stream)
    3217           0 :                         close_stream(stderr_stream);
    3218           0 :                 fprintf(stderr, "error: could not open an output stream\n");
    3219           0 :                 exit(2);
    3220             :         }
    3221             : 
    3222             :         /* parse config file first, command line options override */
    3223         190 :         parse_dotmonetdb(&dotfile);
    3224         190 :         user = dotfile.user;
    3225         190 :         passwd = dotfile.passwd;
    3226         190 :         dbname = dotfile.dbname;
    3227         190 :         language = dotfile.language;
    3228         190 :         host = dotfile.host;
    3229         190 :         save_history = dotfile.save_history;
    3230         190 :         output = dotfile.output;
    3231         190 :         pagewidth = dotfile.pagewidth;
    3232         190 :         port = dotfile.port;
    3233         190 :         pagewidthset = pagewidth != 0;
    3234         190 :         if (language) {
    3235           0 :                 if (strcmp(language, "sql") == 0) {
    3236           0 :                         mode = SQL;
    3237           0 :                 } else if (strcmp(language, "mal") == 0) {
    3238           0 :                         mode = MAL;
    3239             :                 }
    3240             :         } else {
    3241         190 :                 language = strdup("sql");
    3242         190 :                 mode = SQL;
    3243             :         }
    3244             : 
    3245        1887 :         while ((c = getopt_long(argc, argv, "ad:De"
    3246             : #ifdef HAVE_ICONV
    3247             :                                 "E:"
    3248             : #endif
    3249             :                                 "f:h:Hil:L:n:Np:P:r:Rs:t:u:vw:Xz"
    3250             : #ifdef HAVE_POPEN
    3251             :                                 "|:"
    3252             : #endif
    3253             :                                 "?",
    3254             :                                 long_options, &option_index)) != -1) {
    3255        1698 :                 switch (c) {
    3256             :                 case 0:
    3257             :                         /* only needed for options that only have a
    3258             :                          * long form */
    3259             :                         break;
    3260           0 :                 case 'a':
    3261             :                         autocommit = false;
    3262           0 :                         break;
    3263         183 :                 case 'd':
    3264         183 :                         assert(optarg);
    3265         183 :                         if (dbname)
    3266           0 :                                 free(dbname);
    3267         183 :                         dbname = strdup(optarg);
    3268         183 :                         break;
    3269           6 :                 case 'D':
    3270             :                         dump = true;
    3271           6 :                         break;
    3272         149 :                 case 'e':
    3273         149 :                         echoquery = true;
    3274         149 :                         break;
    3275             : #ifdef HAVE_ICONV
    3276         189 :                 case 'E':
    3277         189 :                         assert(optarg);
    3278         189 :                         encoding = optarg;
    3279         189 :                         break;
    3280             : #endif
    3281         237 :                 case 'f':
    3282         237 :                         assert(optarg);
    3283         237 :                         if (output != NULL)
    3284          48 :                                 free(output);
    3285         237 :                         output = strdup(optarg);        /* output format */
    3286         237 :                         break;
    3287         187 :                 case 'h':
    3288         187 :                         assert(optarg);
    3289             :                         host = optarg;
    3290             :                         break;
    3291           0 :                 case 'H':
    3292             :                         save_history = true;
    3293           0 :                         break;
    3294         162 :                 case 'i':
    3295             :                         interactive = true;
    3296         162 :                         break;
    3297         195 :                 case 'l':
    3298         195 :                         assert(optarg);
    3299             :                         /* accept unambiguous prefix of language */
    3300         195 :                         if (strcmp(optarg, "sql") == 0 ||
    3301           7 :                             strcmp(optarg, "sq") == 0 ||
    3302           7 :                             strcmp(optarg, "s") == 0) {
    3303         188 :                                 free(language);
    3304         188 :                                 language = strdup(optarg);
    3305         188 :                                 mode = SQL;
    3306           7 :                         } else if (strcmp(optarg, "mal") == 0 ||
    3307           0 :                                    strcmp(optarg, "ma") == 0) {
    3308           7 :                                 free(language);
    3309           7 :                                 language = strdup("mal");
    3310           7 :                                 mode = MAL;
    3311           0 :                         } else if (strcmp(optarg, "msql") == 0) {
    3312           0 :                                 free(language);
    3313           0 :                                 language = strdup("msql");
    3314           0 :                                 mode = MAL;
    3315             :                         } else {
    3316           0 :                                 mnstr_printf(stderr_stream, "language option needs to be sql or mal\n");
    3317           0 :                                 exit(-1);
    3318             :                         }
    3319             :                         break;
    3320           0 :                 case 'L':
    3321           0 :                         assert(optarg);
    3322           0 :                         logfile = strdup(optarg);
    3323           0 :                         break;
    3324           0 :                 case 'n':
    3325           0 :                         assert(optarg);
    3326           0 :                         nullstring = optarg;
    3327           0 :                         break;
    3328           0 :                 case 'N':
    3329             :                         useinserts = true;
    3330           0 :                         break;
    3331         187 :                 case 'p':
    3332         187 :                         assert(optarg);
    3333             :                         port = atoi(optarg);
    3334         187 :                         break;
    3335           0 :                 case 'P':
    3336           0 :                         assert(optarg);
    3337           0 :                         if (passwd)
    3338           0 :                                 free(passwd);
    3339           0 :                         passwd = strdup(optarg);
    3340             :                         passwd_set_as_flag = true;
    3341           0 :                         break;
    3342           0 :                 case 'r':
    3343           0 :                         assert(optarg);
    3344           0 :                         rowsperpage = atoi(optarg);
    3345           0 :                         break;
    3346           0 :                 case 'R':
    3347           0 :                         allow_remote = true;
    3348           0 :                         break;
    3349          13 :                 case 's':
    3350          13 :                         assert(optarg);
    3351             :                         command = optarg;
    3352             :                         break;
    3353         189 :                 case 't':
    3354         189 :                         if (optarg != NULL) {
    3355         189 :                                 if (strcmp(optarg,"none") == 0) {
    3356         189 :                                         timermode = T_NONE;
    3357           0 :                                 } else if (strcmp(optarg,"clock") == 0) {
    3358           0 :                                         timermode = T_CLOCK;
    3359           0 :                                 } else if (strcmp(optarg, "perf") == 0 || strcmp(optarg, "performance") == 0) {
    3360           0 :                                         timermode = T_PERF;
    3361           0 :                                 } else if (*optarg != '\0') {
    3362           0 :                                         mnstr_printf(stderr_stream, "warning: invalid argument to -t: %s\n",
    3363             :                                                 optarg);
    3364             :                                 }
    3365             :                         }
    3366             :                         break;
    3367           0 :                 case 'u':
    3368           0 :                         assert(optarg);
    3369           0 :                         if (user)
    3370           0 :                                 free(user);
    3371           0 :                         user = strdup(optarg);
    3372             :                         user_set_as_flag = true;
    3373           0 :                         break;
    3374           1 :                 case 'v': {
    3375           1 :                         mnstr_printf(toConsole,
    3376             :                                      "mclient, the MonetDB interactive "
    3377             :                                      "terminal, version %s", MONETDB_VERSION);
    3378             : #ifdef MONETDB_RELEASE
    3379             :                         mnstr_printf(toConsole, " (%s)", MONETDB_RELEASE);
    3380             : #else
    3381           1 :                         const char *rev = mercurial_revision();
    3382           1 :                         if (strcmp(rev, "Unknown") != 0)
    3383           0 :                                 mnstr_printf(toConsole, " (hg id: %s)", rev);
    3384             : #endif
    3385           1 :                         mnstr_printf(toConsole, "\n");
    3386             : #ifdef HAVE_LIBREADLINE
    3387           1 :                         mnstr_printf(toConsole,
    3388             :                                      "support for command-line editing "
    3389             :                                      "compiled-in\n");
    3390             : #endif
    3391             : #ifdef HAVE_ICONV
    3392             : #ifdef HAVE_NL_LANGINFO
    3393           1 :                         if (encoding == NULL)
    3394           1 :                                 encoding = nl_langinfo(CODESET);
    3395             : #endif
    3396           1 :                         mnstr_printf(toConsole,
    3397             :                                      "character encoding: %s\n",
    3398           1 :                                      encoding ? encoding : "utf-8 (default)");
    3399             : #endif
    3400           1 :                         mnstr_printf(toConsole, "using mapi library %s\n",
    3401             :                                                  mapi_get_mapi_version());
    3402           1 :                         return 0;
    3403             :                 }
    3404           0 :                 case 'w':
    3405           0 :                         assert(optarg);
    3406           0 :                         pagewidth = atoi(optarg);
    3407           0 :                         pagewidthset = pagewidth != 0;
    3408           0 :                         break;
    3409           0 :                 case 'X':
    3410             :                         trace = true;
    3411           0 :                         break;
    3412           0 :                 case 'z':
    3413             :                         settz = false;
    3414           0 :                         break;
    3415             : #ifdef HAVE_POPEN
    3416           0 :                 case '|':
    3417           0 :                         assert(optarg);
    3418           0 :                         pager = optarg;
    3419           0 :                         break;
    3420             : #endif
    3421           0 :                 case '?':
    3422             :                         /* a bit of a hack: look at the option that the
    3423             :                          * current `c' is based on and see if we recognize
    3424             :                          * it: if -? or --help, exit with 0, else with -1 */
    3425           0 :                         usage(argv[0], strcmp(argv[optind - 1], "-?") == 0 || strcmp(argv[optind - 1], "--help") == 0 ? 0 : -1);
    3426             :                         /* not reached */
    3427           0 :                 default:
    3428           0 :                         usage(argv[0], -1);
    3429             :                         /* not reached */
    3430             :                 }
    3431             :         }
    3432         189 :         if (passwd_set_as_flag &&
    3433           0 :             (output == NULL || strcmp(output, "test") != 0)) {
    3434           0 :                 usage(argv[0], -1);
    3435             :                 /* not reached */
    3436             :         }
    3437             : 
    3438             : #ifdef HAVE_ICONV
    3439             : #ifdef HAVE_NL_LANGINFO
    3440         189 :         if (encoding == NULL)
    3441           0 :                 encoding = nl_langinfo(CODESET);
    3442             : #endif
    3443         189 :         if (encoding != NULL && strcasecmp(encoding, "utf-8") == 0)
    3444         185 :                 encoding = NULL;
    3445         189 :         if (encoding != NULL) {
    3446           4 :                 stream *s = iconv_wstream(toConsole, encoding, "stdout");
    3447           4 :                 if (s == NULL || mnstr_errnr(s)) {
    3448           0 :                         mnstr_printf(stderr_stream, "warning: cannot convert local character set %s to UTF-8\n", encoding);
    3449           0 :                         close_stream(s);
    3450             :                 } else
    3451           4 :                         toConsole = s;
    3452           4 :                 stdout_stream = toConsole;
    3453             :         }
    3454             : #endif /* HAVE_ICONV */
    3455             : 
    3456             :         /* when config file would provide defaults */
    3457         189 :         if (user_set_as_flag) {
    3458           0 :                 if (passwd && !passwd_set_as_flag) {
    3459           0 :                         free(passwd);
    3460             :                         passwd = NULL;
    3461             :                 }
    3462             :         }
    3463             : 
    3464         189 :         if (user == NULL)
    3465           0 :                 user = simple_prompt("user", BUFSIZ, 1, prompt_getlogin());
    3466         189 :         if (passwd == NULL)
    3467           0 :                 passwd = simple_prompt("password", BUFSIZ, 0, NULL);
    3468             : 
    3469             :         c = 0;
    3470         189 :         has_fileargs = optind != argc;
    3471             : 
    3472         189 :         if (dbname == NULL && has_fileargs) {
    3473           0 :                 s = open_rastream(argv[optind]);
    3474           0 :                 if (s == NULL || !isfile(getFile(s))) {
    3475           0 :                         mnstr_close(s);
    3476             :                         s = NULL;
    3477             :                 }
    3478             :                 if (s == NULL) {
    3479           0 :                         dbname = strdup(argv[optind]);
    3480           0 :                         optind++;
    3481           0 :                         has_fileargs = optind != argc;
    3482             :                 }
    3483             :         }
    3484             : 
    3485         189 :         if (dbname != NULL && strncmp(dbname, "mapi:monetdb://", 15) == 0) {
    3486           2 :                 mid = mapi_mapiuri(dbname, user, passwd, language);
    3487             :         } else {
    3488         187 :                 mid = mapi_mapi(host, port, user, passwd, language, dbname);
    3489             :         }
    3490         189 :         if (user)
    3491         189 :                 free(user);
    3492             :         user = NULL;
    3493         189 :         if (passwd)
    3494         189 :                 free(passwd);
    3495             :         passwd = NULL;
    3496         189 :         if (dbname)
    3497         183 :                 free(dbname);
    3498             :         dbname = NULL;
    3499             : 
    3500         189 :         if (mid == NULL) {
    3501           0 :                 mnstr_printf(stderr_stream, "failed to allocate Mapi structure\n");
    3502           0 :                 exit(2);
    3503             :         }
    3504             : 
    3505         189 :         mapi_cache_limit(mid, -1);
    3506         189 :         mapi_setAutocommit(mid, autocommit);
    3507         189 :         if (mode == SQL && !settz)
    3508           0 :                 mapi_set_time_zone(mid, 0);
    3509             : 
    3510         189 :         if (mapi_error(mid) == MOK)
    3511         189 :                 mapi_reconnect(mid);    /* actually, initial connect */
    3512             : 
    3513         189 :         if (mapi_error(mid)) {
    3514           0 :                 if (trace)
    3515           0 :                         mapi_explain(mid, stderr);
    3516             :                 else
    3517           0 :                         mnstr_printf(stderr_stream, "%s\n", mapi_error_str(mid));
    3518           0 :                 exit(2);
    3519             :         }
    3520         189 :         if (dump) {
    3521           6 :                 if (mode == SQL) {
    3522           6 :                         exit(dump_database(mid, toConsole, 0, useinserts, false));
    3523             :                 } else {
    3524           0 :                         mnstr_printf(stderr_stream, "Dump only supported for SQL\n");
    3525           0 :                         exit(1);
    3526             :                 }
    3527             :         }
    3528             : 
    3529             :         struct privdata priv;
    3530         183 :         priv = (struct privdata) {0};
    3531         183 :         mapi_setfilecallback(mid, getfile, putfile, &priv);
    3532             : 
    3533         183 :         if (logfile)
    3534           0 :                 mapi_log(mid, logfile);
    3535             : 
    3536         183 :         mapi_trace(mid, trace);
    3537         183 :         if (output) {
    3538         183 :                 setFormatter(output);
    3539         183 :                 free(output);
    3540             :         } else {
    3541           0 :                 if (mode == SQL) {
    3542           0 :                         setFormatter("sql");
    3543             :                 } else {
    3544           0 :                         setFormatter("raw");
    3545             :                 }
    3546             :         }
    3547             :         /* give the user a welcome message with some general info */
    3548         183 :         if (!has_fileargs && command == NULL && isatty(fileno(stdin))) {
    3549             :                 char *lang;
    3550             : 
    3551           0 :                 if (mode == SQL) {
    3552             :                         lang = "/SQL";
    3553             :                 } else {
    3554             :                         lang = "";
    3555             :                 }
    3556             : 
    3557           0 :                 mnstr_printf(toConsole,
    3558             :                              "Welcome to mclient, the MonetDB%s "
    3559             :                              "interactive terminal (%s)\n",
    3560             :                              lang,
    3561             : #ifdef MONETDB_RELEASE
    3562             :                              MONETDB_RELEASE
    3563             : #else
    3564             :                              "unreleased"
    3565             : #endif
    3566             :                         );
    3567             : 
    3568           0 :                 if (mode == SQL)
    3569           0 :                         dump_version(mid, toConsole, "Database:");
    3570             : 
    3571           0 :                 mnstr_printf(toConsole, "FOLLOW US on https://twitter.com/MonetDB "
    3572             :                                         "or https://github.com/MonetDB/MonetDB\n"
    3573             :                                         "Type \\q to quit, \\? for a list of available commands\n");
    3574           0 :                 if (mode == SQL)
    3575           0 :                         mnstr_printf(toConsole, "auto commit mode: %s\n",
    3576           0 :                                      mapi_get_autocommit(mid) ? "on" : "off");
    3577             :         }
    3578             : 
    3579         183 :         if (command != NULL) {
    3580             : #if !defined(_MSC_VER) && defined(HAVE_ICONV)
    3581             :                 /* no need on Windows: using wmain interface */
    3582             :                 iconv_t cd_in;
    3583             :                 bool free_command = false;
    3584             : 
    3585          17 :                 if (encoding != NULL &&
    3586           8 :                     (cd_in = iconv_open("utf-8", encoding)) != (iconv_t) -1) {
    3587             :                         char *savecommand = command;
    3588           4 :                         ICONV_CONST char *from = command;
    3589           4 :                         size_t fromlen = strlen(from);
    3590             :                         int factor = 4;
    3591           4 :                         size_t tolen = factor * fromlen + 1;
    3592           4 :                         char *to = malloc(tolen);
    3593             : 
    3594           4 :                         if (to == NULL) {
    3595           0 :                                 mnstr_printf(stderr_stream,"Malloc in main failed");
    3596           0 :                                 exit(2);
    3597             :                         }
    3598             :                         free_command = true;
    3599             : 
    3600           4 :                   try_again:
    3601           4 :                         command = to;
    3602           4 :                         if (iconv(cd_in, &from, &fromlen, &to, &tolen) == (size_t) -1) {
    3603           0 :                                 switch (errno) {
    3604           0 :                                 case EILSEQ:
    3605             :                                         /* invalid multibyte sequence */
    3606           0 :                                         mnstr_printf(stderr_stream, "Illegal input sequence in command line\n");
    3607           0 :                                         exit(-1);
    3608           0 :                                 case E2BIG:
    3609             :                                         /* output buffer too small */
    3610           0 :                                         from = savecommand;
    3611           0 :                                         fromlen = strlen(from);
    3612           0 :                                         factor *= 2;
    3613           0 :                                         tolen = factor * fromlen + 1;
    3614           0 :                                         free(command);
    3615           0 :                                         to = malloc(tolen);
    3616           0 :                                         if (to == NULL) {
    3617           0 :                                                 mnstr_printf(stderr_stream,"Malloc in main failed");
    3618           0 :                                                 exit(2);
    3619             :                                         }
    3620           0 :                                         goto try_again;
    3621           0 :                                 case EINVAL:
    3622             :                                         /* incomplete multibyte sequence */
    3623           0 :                                         mnstr_printf(stderr_stream, "Incomplete input sequence on command line\n");
    3624           0 :                                         exit(-1);
    3625             :                                 default:
    3626             :                                         break;
    3627             :                                 }
    3628           4 :                         }
    3629           4 :                         *to = 0;
    3630           4 :                         iconv_close(cd_in);
    3631           9 :                 } else if (encoding)
    3632           0 :                         mnstr_printf(stderr_stream, "warning: cannot convert local character set %s to UTF-8\n", encoding);
    3633             : #endif
    3634             :                 /* execute from command-line, need interactive to know whether
    3635             :                  * to keep the mapi handle open */
    3636             :                 timerStart();
    3637          13 :                 c = doRequest(mid, command);
    3638          13 :                 timerEnd();
    3639             : #if !defined(_MSC_VER) && defined(HAVE_ICONV)
    3640          13 :                 if (free_command)
    3641           4 :                         free(command);
    3642             : #endif
    3643             :         }
    3644             : 
    3645         183 :         if (optind < argc) {
    3646             :                 /* execute from file(s) */
    3647           4 :                 while (optind < argc) {
    3648           2 :                         const char *arg = argv[optind];
    3649             : 
    3650           2 :                         if (s == NULL) {
    3651           2 :                                 if (strcmp(arg, "-") == 0)
    3652           0 :                                         s = stdin_rastream();
    3653             :                                 else
    3654           2 :                                         s = open_rastream(arg);
    3655             :                         }
    3656           2 :                         if (s == NULL) {
    3657           0 :                                 mnstr_printf(stderr_stream, "%s: cannot open: %s\n", arg, mnstr_peek_error(NULL));
    3658             :                                 c |= 1;
    3659           0 :                                 optind++;
    3660           0 :                                 continue;
    3661             :                         }
    3662             :                         // doFile closes 's'.
    3663           2 :                         c |= doFile(mid, s, useinserts, interactive, save_history);
    3664             :                         s = NULL;
    3665           2 :                         optind++;
    3666             :                 }
    3667         181 :         } else if (command && mapi_get_active(mid))
    3668           0 :                 c = doFileBulk(mid, NULL);
    3669             : 
    3670         183 :         if (!has_fileargs && command == NULL) {
    3671         168 :                 s = stdin_rastream();
    3672         168 :                 if(!s) {
    3673           0 :                         mapi_destroy(mid);
    3674           0 :                         mnstr_destroy(stdout_stream);
    3675           0 :                         mnstr_destroy(stderr_stream);
    3676           0 :                         fprintf(stderr,"Failed to open stream for stdin\n");
    3677           0 :                         exit(2);
    3678             :                 }
    3679         168 :                 c = doFile(mid, s, useinserts, interactive, save_history);
    3680             :                 s = NULL;
    3681             :         }
    3682             : 
    3683         183 :         mapi_destroy(mid);
    3684         183 :         mnstr_destroy(stdout_stream);
    3685         183 :         mnstr_destroy(stderr_stream);
    3686         183 :         if (priv.buf != NULL)
    3687          18 :                 free(priv.buf);
    3688             : 
    3689             :         return c;
    3690             : }

Generated by: LCOV version 1.14