LCOV - code coverage report
Current view: top level - src/odbc/unittests - parser.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 141 183 77.0 %
Date: 2025-01-18 11:50:39 Functions: 13 15 86.7 %

          Line data    Source code
       1             : /*
       2             :  * Test reading data with SQLBindCol
       3             :  */
       4             : #include "common.h"
       5             : #include <assert.h>
       6             : #include <ctype.h>
       7             : #include "parser.h"
       8             : 
       9             : enum
      10             : {
      11             :         MAX_BOOLS = 64,
      12             :         MAX_CONDITIONS = 32
      13             : };
      14             : 
      15             : typedef struct
      16             : {
      17             :         char *name;
      18             :         bool value;
      19             : } bool_t;
      20             : 
      21             : struct odbc_parser
      22             : {
      23             :         unsigned int line_num;
      24             : 
      25             :         bool_t bools[MAX_BOOLS];
      26             : 
      27             :         bool conds[MAX_CONDITIONS];
      28             :         unsigned cond_level;
      29             : 
      30             :         void *read_param;
      31             :         odbc_read_line_p read_func;
      32             : 
      33             :         char line_buf[1024];
      34             : };
      35             : 
      36             : unsigned int
      37           0 : odbc_line_num(odbc_parser *parser)
      38             : {
      39           0 :         return parser->line_num;
      40             : }
      41             : 
      42             : void
      43           0 : odbc_fatal(odbc_parser *parser, const char *msg, ...)
      44             : {
      45             :         va_list ap;
      46             : 
      47           0 :         va_start(ap, msg);
      48           0 :         if (msg[0] == ':')
      49           0 :                 fprintf(stderr, "Line %u", parser->line_num);
      50           0 :         vfprintf(stderr, msg, ap);
      51           0 :         va_end(ap);
      52             : 
      53           0 :         exit(1);
      54             : }
      55             : 
      56             : #define SEP " \t\n"
      57             : 
      58             : const char *
      59       24438 : odbc_get_tok(char **p)
      60             : {
      61       24438 :         char *s = *p, *end;
      62             : 
      63       24438 :         s += strspn(s, SEP);
      64       24438 :         if (!*s)
      65             :                 return NULL;
      66       22934 :         end = s + strcspn(s, SEP);
      67       22934 :         *end = 0;
      68       22934 :         *p = end + 1;
      69       22934 :         return s;
      70             : }
      71             : 
      72             : static void
      73        2056 : parse_cstr(odbc_parser *parser, char *s)
      74             : {
      75             :         char hexbuf[4];
      76        2056 :         char *d = s;
      77             : 
      78       39600 :         while (*s) {
      79       35488 :                 if (*s != '\\') {
      80       35440 :                         *d++ = *s++;
      81       35440 :                         continue;
      82             :                 }
      83             : 
      84          48 :                 switch (*++s) {
      85           0 :                 case '\"':
      86           0 :                         *d++ = *s++;
      87           0 :                         break;
      88           0 :                 case '\\':
      89           0 :                         *d++ = *s++;
      90           0 :                         break;
      91          48 :                 case 'x':
      92          48 :                         if (strlen(s) < 3)
      93           0 :                                 odbc_fatal(parser, ": wrong string format\n");
      94          48 :                         memcpy(hexbuf, ++s, 2);
      95          48 :                         hexbuf[2] = 0;
      96          48 :                         *d++ = (char) strtoul(hexbuf, NULL, 16);
      97          48 :                         s += 2;
      98          48 :                         break;
      99           0 :                 default:
     100           0 :                         odbc_fatal(parser, ": wrong string format\n");
     101             :                 }
     102             :         }
     103        2056 :         *d = 0;
     104        2056 : }
     105             : 
     106             : const char *
     107        9368 : odbc_get_str(odbc_parser *parser, char **p)
     108             : {
     109        9368 :         char *s = *p, *end;
     110             : 
     111        9368 :         s += strspn(s, SEP);
     112        9368 :         if (!*s)
     113           0 :                 odbc_fatal(parser, ": unable to get string\n");
     114             : 
     115        9368 :         if (strncmp(s, "\"\"\"", 3) == 0) {
     116          80 :                 s += 3;
     117          80 :                 end = strstr(s, "\"\"\"");
     118          80 :                 if (!end)
     119           0 :                         odbc_fatal(parser, ": string not terminated\n");
     120          80 :                 *end = 0;
     121          80 :                 *p = end + 3;
     122        9288 :         } else if (s[0] == '\"') {
     123        2056 :                 ++s;
     124        2056 :                 end = strchr(s, '\"');
     125        2056 :                 if (!end)
     126           0 :                         odbc_fatal(parser, ": string not terminated\n");
     127        2056 :                 *end = 0;
     128        2056 :                 parse_cstr(parser, s);
     129        2056 :                 *p = end + 1;
     130             :         } else {
     131        7232 :                 return odbc_get_tok(p);
     132             :         }
     133             :         return s;
     134             : }
     135             : 
     136             : void
     137         684 : odbc_set_bool(odbc_parser *parser, const char *name, bool value)
     138             : {
     139             :         unsigned n;
     140             : 
     141        1734 :         for (n = 0; n < MAX_BOOLS && parser->bools[n].name; ++n)
     142        1094 :                 if (!strcmp(parser->bools[n].name, name)) {
     143          44 :                         parser->bools[n].value = value;
     144          44 :                         return;
     145             :                 }
     146             : 
     147         640 :         if (n == MAX_BOOLS)
     148           0 :                 odbc_fatal(parser, ": no more boolean variable free\n");
     149         640 :         parser->bools[n].name = strdup(name);
     150         640 :         if (!parser->bools[n].name)
     151           0 :                 odbc_fatal(parser, ": out of memory\n");
     152         640 :         parser->bools[n].value = value;
     153             : }
     154             : 
     155             : static bool
     156         228 : get_bool(odbc_parser *parser, const char *name)
     157             : {
     158             :         unsigned n;
     159             : 
     160         228 :         if (!name)
     161           0 :                 odbc_fatal(parser, ": boolean variable not provided\n");
     162         670 :         for (n = 0; n < MAX_BOOLS && parser->bools[n].name; ++n)
     163         898 :                 if (!strcmp(parser->bools[n].name, name))
     164         228 :                         return parser->bools[n].value;
     165             : 
     166           0 :         odbc_fatal(parser, ": boolean variable %s not found\n", name);
     167             :         return false;
     168             : }
     169             : 
     170             : /** initialize booleans, call after connection */
     171             : static void
     172         192 : init_bools(odbc_parser *parser)
     173             : {
     174         192 :         int big_endian = 1;
     175             : 
     176             :         if (((char *) &big_endian)[0] == 1)
     177         192 :                 big_endian = 0;
     178         192 :         odbc_set_bool(parser, "bigendian", !!big_endian);
     179             : 
     180         192 :         odbc_set_bool(parser, "msdb", odbc_db_is_microsoft());
     181         192 :         odbc_set_bool(parser, "freetds", odbc_driver_is_freetds());
     182         192 : }
     183             : 
     184             : static void
     185             : clear_bools(odbc_parser *parser)
     186             : {
     187             :         unsigned n;
     188             : 
     189         832 :         for (n = 0; n < MAX_BOOLS && parser->bools[n].name; ++n) {
     190         640 :                 free(parser->bools[n].name);
     191         640 :                 parser->bools[n].name = NULL;
     192             :         }
     193             : }
     194             : 
     195             : static bool
     196             : pop_condition(odbc_parser *parser)
     197             : {
     198         272 :         if (parser->cond_level == 0)
     199           0 :                 odbc_fatal(parser, ": no related if\n");
     200         272 :         return parser->conds[--parser->cond_level];
     201             : }
     202             : 
     203             : static void
     204             : push_condition(odbc_parser *parser, bool cond)
     205             : {
     206         272 :         if (parser->cond_level >= MAX_CONDITIONS)
     207           0 :                 odbc_fatal(parser, ": too much nested conditions\n");
     208         272 :         parser->conds[parser->cond_level++] = cond;
     209             : }
     210             : 
     211             : static bool
     212         228 : get_not_cond(odbc_parser *parser, char **p)
     213             : {
     214             :         bool cond;
     215         228 :         const char *tok = odbc_get_tok(p);
     216             : 
     217         228 :         if (!tok)
     218           0 :                 odbc_fatal(parser, ": wrong condition syntax\n");
     219             : 
     220         228 :         if (!strcmp(tok, "not"))
     221          70 :                 cond = !get_bool(parser, odbc_get_tok(p));
     222             :         else
     223         158 :                 cond = get_bool(parser, tok);
     224             : 
     225         228 :         return cond;
     226             : }
     227             : 
     228             : static bool
     229         200 : get_condition(odbc_parser *parser, char **p)
     230             : {
     231         200 :         bool cond1 = get_not_cond(parser, p), cond2;
     232             :         const char *tok;
     233             : 
     234         428 :         while ((tok = odbc_get_tok(p)) != NULL) {
     235             : 
     236          28 :                 cond2 = get_not_cond(parser, p);
     237             : 
     238          28 :                 if (!strcmp(tok, "or"))
     239           0 :                         cond1 = cond1 || cond2;
     240          28 :                 else if (!strcmp(tok, "and"))
     241          28 :                         cond1 = cond1 && cond2;
     242             :                 else
     243           0 :                         odbc_fatal(parser, ": wrong condition syntax\n");
     244             :         }
     245         200 :         return cond1;
     246             : }
     247             : 
     248             : odbc_parser *
     249         192 : odbc_init_parser_func(odbc_read_line_p read_func, void *param)
     250             : {
     251             :         odbc_parser *parser;
     252             : 
     253         192 :         if (!read_func) {
     254           0 :                 fprintf(stderr, "Missing reading function\n");
     255           0 :                 exit(1);
     256             :         }
     257             : 
     258         192 :         parser = tds_new0(odbc_parser, 1);
     259         192 :         if (!parser) {
     260           0 :                 fprintf(stderr, "out of memory\n");
     261           0 :                 exit(1);
     262             :         }
     263         192 :         parser->read_param = param;
     264         192 :         parser->read_func = read_func;
     265         192 :         init_bools(parser);
     266             : 
     267         192 :         return parser;
     268             : }
     269             : 
     270             : static char *
     271        8424 : read_file(void *param, char *s, size_t size)
     272             : {
     273        8424 :         return fgets(s, size, (FILE *) param);
     274             : }
     275             : 
     276             : odbc_parser *
     277          16 : odbc_init_parser(FILE *f)
     278             : {
     279          16 :         return odbc_init_parser_func(read_file, f);
     280             : }
     281             : 
     282             : void
     283         192 : odbc_free_parser(odbc_parser *parser)
     284             : {
     285         192 :         clear_bools(parser);
     286         192 :         free(parser);
     287         192 : }
     288             : 
     289             : const char *
     290        7848 : odbc_get_cmd_line(odbc_parser *parser, char **p_s, bool *cond)
     291             : {
     292       17464 :         while (parser->read_func(parser->read_param, parser->line_buf, sizeof(parser->line_buf))) {
     293        9424 :                 char *p = parser->line_buf;
     294             :                 const char *cmd;
     295             : 
     296        9424 :                 ++parser->line_num;
     297             : 
     298        9424 :                 cmd = odbc_get_tok(&p);
     299             : 
     300             :                 /* skip comments */
     301        9424 :                 if (!cmd || cmd[0] == '#' || cmd[0] == 0 || cmd[0] == '\n')
     302        2960 :                         continue;
     303             : 
     304             :                 /* conditional statement */
     305        8232 :                 if (!strcmp(cmd, "else")) {
     306          16 :                         bool c = pop_condition(parser);
     307             : 
     308          32 :                         push_condition(parser, c);
     309          16 :                         *cond = c && !*cond;
     310          16 :                         continue;
     311             :                 }
     312        8216 :                 if (!strcmp(cmd, "endif")) {
     313         256 :                         *cond = pop_condition(parser);
     314         256 :                         continue;
     315             :                 }
     316        7960 :                 if (!strcmp(cmd, "if")) {
     317         512 :                         push_condition(parser, *cond);
     318         256 :                         if (*cond)
     319         200 :                                 *cond = get_condition(parser, &p);
     320         256 :                         continue;
     321             :                 }
     322             : 
     323        7704 :                 if (strcmp(cmd, "tds_version_cmp") == 0) {
     324          48 :                         const char *bool_name = odbc_get_tok(&p);
     325          48 :                         const char *cmp = odbc_get_tok(&p);
     326          48 :                         const char *s_ver = odbc_get_tok(&p);
     327          48 :                         int ver = odbc_tds_version();
     328             :                         int expected;
     329             :                         bool res;
     330             :                         unsigned M, m;
     331             : 
     332          48 :                         if (!cmp || !s_ver)
     333           0 :                                 odbc_fatal(parser, ": missing parameters\n");
     334          48 :                         if (sscanf(s_ver, "%u.%u", &M, &m) != 2)
     335           0 :                                 odbc_fatal(parser, ": invalid version %s\n", s_ver);
     336          48 :                         expected = M * 0x100u + m;
     337             : 
     338          48 :                         if (strcmp(cmp, ">") == 0)
     339           0 :                                 res = ver > expected;
     340          48 :                         else if (strcmp(cmp, ">=") == 0)
     341          32 :                                 res = ver >= expected;
     342          16 :                         else if (strcmp(cmp, "<") == 0)
     343           0 :                                 res = ver < expected;
     344          16 :                         else if (strcmp(cmp, "<=") == 0)
     345           0 :                                 res = ver <= expected;
     346          16 :                         else if (strcmp(cmp, "==") == 0)
     347          16 :                                 res = ver == expected;
     348           0 :                         else if (strcmp(cmp, "!=") == 0)
     349           0 :                                 res = ver != expected;
     350             :                         else
     351           0 :                                 odbc_fatal(parser, ": invalid operator %s\n", cmp);
     352             : 
     353          48 :                         if (*cond)
     354          42 :                                 odbc_set_bool(parser, bool_name, res);
     355          48 :                         continue;
     356             :                 }
     357             : 
     358        7656 :                 *p_s = p;
     359        7656 :                 return cmd;
     360             :         }
     361             :         return NULL;
     362             : }

Generated by: LCOV version 1.13