quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

tool_parsecfg.c (9380B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 #include "tool_setup.h"
     25 
     26 #include "tool_cfgable.h"
     27 #include "tool_getparam.h"
     28 #include "tool_helpers.h"
     29 #include "tool_findfile.h"
     30 #include "tool_msgs.h"
     31 #include "tool_parsecfg.h"
     32 #include "tool_util.h"
     33 #include "memdebug.h" /* keep this as LAST include */
     34 
     35 /* only acknowledge colon or equals as separators if the option was not
     36    specified with an initial dash! */
     37 #define ISSEP(x,dash) (!dash && (((x) == '=') || ((x) == ':')))
     38 
     39 static const char *unslashquote(const char *line, char *param);
     40 
     41 #define MAX_CONFIG_LINE_LENGTH (10*1024*1024)
     42 
     43 /* return 0 on everything-is-fine, and non-zero otherwise */
     44 int parseconfig(const char *filename, struct GlobalConfig *global)
     45 {
     46   FILE *file = NULL;
     47   bool usedarg = FALSE;
     48   int rc = 0;
     49   struct OperationConfig *config = global->last;
     50   char *pathalloc = NULL;
     51 
     52   if(!filename) {
     53     /* NULL means load .curlrc from homedir! */
     54     char *curlrc = findfile(".curlrc", CURLRC_DOTSCORE);
     55     if(curlrc) {
     56       file = fopen(curlrc, FOPEN_READTEXT);
     57       if(!file) {
     58         free(curlrc);
     59         return 1;
     60       }
     61       filename = pathalloc = curlrc;
     62     }
     63 #if defined(_WIN32) && !defined(UNDER_CE)
     64     else {
     65       char *fullp;
     66       /* check for .curlrc then _curlrc in the dir of the executable */
     67       file = tool_execpath(".curlrc", &fullp);
     68       if(!file)
     69         file = tool_execpath("_curlrc", &fullp);
     70       if(file)
     71         /* this is the filename we read from */
     72         filename = fullp;
     73     }
     74 #endif
     75   }
     76   else {
     77     if(strcmp(filename, "-"))
     78       file = fopen(filename, FOPEN_READTEXT);
     79     else
     80       file = stdin;
     81   }
     82 
     83   if(file) {
     84     char *line;
     85     char *option;
     86     char *param;
     87     int lineno = 0;
     88     bool dashed_option;
     89     struct dynbuf buf;
     90     bool fileerror = FALSE;
     91     curlx_dyn_init(&buf, MAX_CONFIG_LINE_LENGTH);
     92     DEBUGASSERT(filename);
     93 
     94     while(!rc && my_get_line(file, &buf, &fileerror)) {
     95       ParameterError res;
     96       bool alloced_param = FALSE;
     97       lineno++;
     98       line = curlx_dyn_ptr(&buf);
     99       if(!line) {
    100         rc = 1; /* out of memory */
    101         break;
    102       }
    103 
    104       /* the option keywords starts here */
    105       option = line;
    106 
    107       /* the option starts with a dash? */
    108       dashed_option = (option[0] == '-');
    109 
    110       while(*line && !ISBLANK(*line) && !ISSEP(*line, dashed_option))
    111         line++;
    112       /* ... and has ended here */
    113 
    114       if(*line)
    115         *line++ = '\0'; /* null-terminate, we have a local copy of the data */
    116 
    117 #ifdef DEBUG_CONFIG
    118       fprintf(tool_stderr, "GOT: %s\n", option);
    119 #endif
    120 
    121       /* pass spaces and separator(s) */
    122       while(ISBLANK(*line) || ISSEP(*line, dashed_option))
    123         line++;
    124 
    125       /* the parameter starts here (unless quoted) */
    126       if(*line == '\"') {
    127         /* quoted parameter, do the quote dance */
    128         line++;
    129         param = malloc(strlen(line) + 1); /* parameter */
    130         if(!param) {
    131           /* out of memory */
    132           rc = 1;
    133           break;
    134         }
    135         alloced_param = TRUE;
    136         (void)unslashquote(line, param);
    137       }
    138       else {
    139         param = line; /* parameter starts here */
    140         while(*line && !ISSPACE(*line)) /* stop also on CRLF */
    141           line++;
    142 
    143         if(*line) {
    144           *line = '\0'; /* null-terminate */
    145 
    146           /* to detect mistakes better, see if there is data following */
    147           line++;
    148           /* pass all spaces */
    149           while(ISBLANK(*line))
    150             line++;
    151 
    152           switch(*line) {
    153           case '\0':
    154           case '\r':
    155           case '\n':
    156           case '#': /* comment */
    157             break;
    158           default:
    159             warnf(config->global, "%s:%d: warning: '%s' uses unquoted "
    160                   "whitespace", filename, lineno, option);
    161             warnf(config->global, "This may cause side-effects. "
    162                   "Consider using double quotes?");
    163           }
    164         }
    165         if(!*param)
    166           /* do this so getparameter can check for required parameters.
    167              Otherwise it always thinks there is a parameter. */
    168           param = NULL;
    169       }
    170 
    171 #ifdef DEBUG_CONFIG
    172       fprintf(tool_stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
    173 #endif
    174       res = getparameter(option, param, &usedarg, config);
    175       config = global->last;
    176 
    177       if(!res && param && *param && !usedarg)
    178         /* we passed in a parameter that was not used! */
    179         res = PARAM_GOT_EXTRA_PARAMETER;
    180 
    181       if(res == PARAM_NEXT_OPERATION) {
    182         if(config->url_list && config->url_list->url) {
    183           /* Allocate the next config */
    184           config->next = config_alloc(global);
    185           if(config->next) {
    186             /* Update the last operation pointer */
    187             global->last = config->next;
    188 
    189             /* Move onto the new config */
    190             config->next->prev = config;
    191             config = config->next;
    192           }
    193           else
    194             res = PARAM_NO_MEM;
    195         }
    196       }
    197 
    198       if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) {
    199         /* the help request is not really an error */
    200         if(!strcmp(filename, "-")) {
    201           filename = "<stdin>";
    202         }
    203         if(res != PARAM_HELP_REQUESTED &&
    204            res != PARAM_MANUAL_REQUESTED &&
    205            res != PARAM_VERSION_INFO_REQUESTED &&
    206            res != PARAM_ENGINES_REQUESTED &&
    207            res != PARAM_CA_EMBED_REQUESTED) {
    208           const char *reason = param2text(res);
    209           errorf(config->global, "%s:%d: '%s' %s",
    210                  filename, lineno, option, reason);
    211           rc = (int)res;
    212         }
    213       }
    214 
    215       if(alloced_param)
    216         tool_safefree(param);
    217     }
    218     curlx_dyn_free(&buf);
    219     if(file != stdin)
    220       fclose(file);
    221     if(fileerror)
    222       rc = 1;
    223   }
    224   else
    225     rc = 1; /* could not open the file */
    226 
    227   free(pathalloc);
    228   return rc;
    229 }
    230 
    231 /*
    232  * Copies the string from line to the buffer at param, unquoting
    233  * backslash-quoted characters and null-terminating the output string. Stops
    234  * at the first non-backslash-quoted double quote character or the end of the
    235  * input string. param must be at least as long as the input string. Returns
    236  * the pointer after the last handled input character.
    237  */
    238 static const char *unslashquote(const char *line, char *param)
    239 {
    240   while(*line && (*line != '\"')) {
    241     if(*line == '\\') {
    242       char out;
    243       line++;
    244 
    245       /* default is to output the letter after the backslash */
    246       switch(out = *line) {
    247       case '\0':
    248         continue; /* this'll break out of the loop */
    249       case 't':
    250         out = '\t';
    251         break;
    252       case 'n':
    253         out = '\n';
    254         break;
    255       case 'r':
    256         out = '\r';
    257         break;
    258       case 'v':
    259         out = '\v';
    260         break;
    261       }
    262       *param++ = out;
    263       line++;
    264     }
    265     else
    266       *param++ = *line++;
    267   }
    268   *param = '\0'; /* always null-terminate */
    269   return line;
    270 }
    271 
    272 static bool get_line(FILE *input, struct dynbuf *buf, bool *error)
    273 {
    274   CURLcode result;
    275   char buffer[128];
    276   curlx_dyn_reset(buf);
    277   while(1) {
    278     char *b = fgets(buffer, sizeof(buffer), input);
    279 
    280     if(b) {
    281       size_t rlen = strlen(b);
    282 
    283       if(!rlen)
    284         break;
    285 
    286       result = curlx_dyn_addn(buf, b, rlen);
    287       if(result) {
    288         /* too long line or out of memory */
    289         *error = TRUE;
    290         return FALSE; /* error */
    291       }
    292 
    293       else if(b[rlen-1] == '\n') {
    294         /* end of the line, drop the newline */
    295         size_t len = curlx_dyn_len(buf);
    296         if(len)
    297           curlx_dyn_setlen(buf, len - 1);
    298         return TRUE; /* all good */
    299       }
    300 
    301       else if(feof(input))
    302         return TRUE; /* all good */
    303     }
    304     else if(curlx_dyn_len(buf))
    305       return TRUE; /* all good */
    306     else
    307       break;
    308   }
    309   return FALSE;
    310 }
    311 
    312 /*
    313  * Returns a line from the given file. Every line is null-terminated (no
    314  * newline). Skips #-commented and space/tabs-only lines automatically.
    315  */
    316 bool my_get_line(FILE *input, struct dynbuf *buf, bool *error)
    317 {
    318   bool retcode;
    319   do {
    320     retcode = get_line(input, buf, error);
    321     if(!*error && retcode) {
    322       size_t len = curlx_dyn_len(buf);
    323       if(len) {
    324         const char *line = curlx_dyn_ptr(buf);
    325         while(ISBLANK(*line))
    326           line++;
    327 
    328         /* a line with # in the first non-blank column is a comment! */
    329         if((*line == '#') || !*line)
    330           continue;
    331       }
    332       else
    333         continue; /* avoid returning an empty line */
    334     }
    335     break;
    336   } while(retcode);
    337   return retcode;
    338 }