Home | History | Annotate | Download | only in src
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel (at) 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.haxx.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  ***************************************************************************/
     22 #include "tool_setup.h"
     23 
     24 #define ENABLE_CURLX_PRINTF
     25 /* use our own printf() functions */
     26 #include "curlx.h"
     27 
     28 #include "tool_cfgable.h"
     29 #include "tool_getparam.h"
     30 #include "tool_helpers.h"
     31 #include "tool_homedir.h"
     32 #include "tool_msgs.h"
     33 #include "tool_parsecfg.h"
     34 
     35 #include "memdebug.h" /* keep this as LAST include */
     36 
     37 #define CURLRC DOT_CHAR "curlrc"
     38 
     39 /* only acknowledge colon or equals as separators if the option was not
     40    specified with an initial dash! */
     41 #define ISSEP(x,dash) (!dash && (((x) == '=') || ((x) == ':')))
     42 
     43 static const char *unslashquote(const char *line, char *param);
     44 static char *my_get_line(FILE *fp);
     45 
     46 /* return 0 on everything-is-fine, and non-zero otherwise */
     47 int parseconfig(const char *filename, struct GlobalConfig *global)
     48 {
     49   int res;
     50   FILE *file;
     51   char filebuffer[512];
     52   bool usedarg;
     53   char *home;
     54   int rc = 0;
     55   struct OperationConfig *operation = global->first;
     56 
     57   if(!filename || !*filename) {
     58     /* NULL or no file name attempts to load .curlrc from the homedir! */
     59 
     60 #ifndef __AMIGA__
     61     filename = CURLRC;   /* sensible default */
     62     home = homedir();    /* portable homedir finder */
     63     if(home) {
     64       if(strlen(home) < (sizeof(filebuffer) - strlen(CURLRC))) {
     65         snprintf(filebuffer, sizeof(filebuffer),
     66                  "%s%s%s", home, DIR_CHAR, CURLRC);
     67 
     68 #ifdef WIN32
     69         /* Check if the file exists - if not, try CURLRC in the same
     70          * directory as our executable
     71          */
     72         file = fopen(filebuffer, FOPEN_READTEXT);
     73         if(file != NULL) {
     74           fclose(file);
     75           filename = filebuffer;
     76         }
     77         else {
     78           /* Get the filename of our executable. GetModuleFileName is
     79            * already declared via inclusions done in setup header file.
     80            * We assume that we are using the ASCII version here.
     81            */
     82           int n = GetModuleFileName(0, filebuffer, sizeof(filebuffer));
     83           if(n > 0 && n < (int)sizeof(filebuffer)) {
     84             /* We got a valid filename - get the directory part */
     85             char *lastdirchar = strrchr(filebuffer, '\\');
     86             if(lastdirchar) {
     87               size_t remaining;
     88               *lastdirchar = 0;
     89               /* If we have enough space, build the RC filename */
     90               remaining = sizeof(filebuffer) - strlen(filebuffer);
     91               if(strlen(CURLRC) < remaining - 1) {
     92                 snprintf(lastdirchar, remaining,
     93                          "%s%s", DIR_CHAR, CURLRC);
     94                 /* Don't bother checking if it exists - we do
     95                  * that later
     96                  */
     97                 filename = filebuffer;
     98               }
     99             }
    100           }
    101         }
    102 #else /* WIN32 */
    103         filename = filebuffer;
    104 #endif /* WIN32 */
    105       }
    106       Curl_safefree(home); /* we've used it, now free it */
    107     }
    108 
    109 # else /* __AMIGA__ */
    110     /* On AmigaOS all the config files are into env:
    111      */
    112     filename = "ENV:" CURLRC;
    113 
    114 #endif
    115   }
    116 
    117   if(strcmp(filename, "-"))
    118     file = fopen(filename, FOPEN_READTEXT);
    119   else
    120     file = stdin;
    121 
    122   if(file) {
    123     char *line;
    124     char *aline;
    125     char *option;
    126     char *param;
    127     int lineno = 0;
    128     bool alloced_param;
    129     bool dashed_option;
    130 
    131     while(NULL != (aline = my_get_line(file))) {
    132       lineno++;
    133       line = aline;
    134       alloced_param=FALSE;
    135 
    136       /* line with # in the first non-blank column is a comment! */
    137       while(*line && ISSPACE(*line))
    138         line++;
    139 
    140       switch(*line) {
    141       case '#':
    142       case '/':
    143       case '\r':
    144       case '\n':
    145       case '*':
    146       case '\0':
    147         Curl_safefree(aline);
    148         continue;
    149       }
    150 
    151       /* the option keywords starts here */
    152       option = line;
    153 
    154       /* the option starts with a dash? */
    155       dashed_option = option[0]=='-'?TRUE:FALSE;
    156 
    157       while(*line && !ISSPACE(*line) && !ISSEP(*line, dashed_option))
    158         line++;
    159       /* ... and has ended here */
    160 
    161       if(*line)
    162         *line++ = '\0'; /* zero terminate, we have a local copy of the data */
    163 
    164 #ifdef DEBUG_CONFIG
    165       fprintf(stderr, "GOT: %s\n", option);
    166 #endif
    167 
    168       /* pass spaces and separator(s) */
    169       while(*line && (ISSPACE(*line) || ISSEP(*line, dashed_option)))
    170         line++;
    171 
    172       /* the parameter starts here (unless quoted) */
    173       if(*line == '\"') {
    174         /* quoted parameter, do the quote dance */
    175         line++;
    176         param = malloc(strlen(line) + 1); /* parameter */
    177         if(!param) {
    178           /* out of memory */
    179           Curl_safefree(aline);
    180           rc = 1;
    181           break;
    182         }
    183         alloced_param = TRUE;
    184         (void)unslashquote(line, param);
    185       }
    186       else {
    187         param = line; /* parameter starts here */
    188         while(*line && !ISSPACE(*line))
    189           line++;
    190 
    191         if(*line) {
    192           *line = '\0'; /* zero terminate */
    193 
    194           /* to detect mistakes better, see if there's data following */
    195           line++;
    196           /* pass all spaces */
    197           while(*line && ISSPACE(*line))
    198             line++;
    199 
    200           switch(*line) {
    201           case '\0':
    202           case '\r':
    203           case '\n':
    204           case '#': /* comment */
    205             break;
    206           default:
    207             warnf(operation->global, "%s:%d: warning: '%s' uses unquoted "
    208                   "white space in the line that may cause side-effects!\n",
    209                   filename, lineno, option);
    210           }
    211         }
    212       }
    213 
    214       if(!*param) {
    215         /* do this so getparameter can check for required parameters.
    216            Otherwise it always thinks there's a parameter. */
    217         if(alloced_param)
    218           Curl_safefree(param);
    219         param = NULL;
    220       }
    221 
    222 #ifdef DEBUG_CONFIG
    223       fprintf(stderr, "PARAM: \"%s\"\n",(param ? param : "(null)"));
    224 #endif
    225       res = getparameter(option, param, &usedarg, global, operation);
    226 
    227       if(param && *param && !usedarg)
    228         /* we passed in a parameter that wasn't used! */
    229         res = PARAM_GOT_EXTRA_PARAMETER;
    230 
    231       if(res == PARAM_NEXT_OPERATION) {
    232         if(operation->url_list && operation->url_list->url) {
    233           /* Allocate the next config */
    234           operation->next = malloc(sizeof(struct OperationConfig));
    235           if(operation->next) {
    236             /* Initialise the newly created config */
    237             config_init(operation->next);
    238 
    239             /* Copy the easy handle */
    240             operation->next->easy = global->easy;
    241 
    242             /* Set the global config pointer */
    243             operation->next->global = global;
    244 
    245             /* Update the last operation pointer */
    246             global->last = operation->next;
    247 
    248             /* Move onto the new config */
    249             operation->next->prev = operation;
    250             operation = operation->next;
    251           }
    252           else
    253             res = PARAM_NO_MEM;
    254         }
    255       }
    256 
    257       if(res != PARAM_OK && res != PARAM_NEXT_OPERATION) {
    258         /* the help request isn't really an error */
    259         if(!strcmp(filename, "-")) {
    260           filename = (char *)"<stdin>";
    261         }
    262         if(res != PARAM_HELP_REQUESTED &&
    263            res != PARAM_MANUAL_REQUESTED &&
    264            res != PARAM_VERSION_INFO_REQUESTED &&
    265            res != PARAM_ENGINES_REQUESTED) {
    266           const char *reason = param2text(res);
    267           warnf(operation->global, "%s:%d: warning: '%s' %s\n",
    268                 filename, lineno, option, reason);
    269         }
    270       }
    271 
    272       if(alloced_param)
    273         Curl_safefree(param);
    274 
    275       Curl_safefree(aline);
    276     }
    277     if(file != stdin)
    278       fclose(file);
    279   }
    280   else
    281     rc = 1; /* couldn't open the file */
    282 
    283   return rc;
    284 }
    285 
    286 /*
    287  * Copies the string from line to the buffer at param, unquoting
    288  * backslash-quoted characters and NUL-terminating the output string.
    289  * Stops at the first non-backslash-quoted double quote character or the
    290  * end of the input string. param must be at least as long as the input
    291  * string.  Returns the pointer after the last handled input character.
    292  */
    293 static const char *unslashquote(const char *line, char *param)
    294 {
    295   while(*line && (*line != '\"')) {
    296     if(*line == '\\') {
    297       char out;
    298       line++;
    299 
    300       /* default is to output the letter after the backslash */
    301       switch(out = *line) {
    302       case '\0':
    303         continue; /* this'll break out of the loop */
    304       case 't':
    305         out = '\t';
    306         break;
    307       case 'n':
    308         out = '\n';
    309         break;
    310       case 'r':
    311         out = '\r';
    312         break;
    313       case 'v':
    314         out = '\v';
    315         break;
    316       }
    317       *param++ = out;
    318       line++;
    319     }
    320     else
    321       *param++ = *line++;
    322   }
    323   *param = '\0'; /* always zero terminate */
    324   return line;
    325 }
    326 
    327 /*
    328  * Reads a line from the given file, ensuring is NUL terminated.
    329  * The pointer must be freed by the caller.
    330  * NULL is returned on an out of memory condition.
    331  */
    332 static char *my_get_line(FILE *fp)
    333 {
    334   char buf[4096];
    335   char *nl = NULL;
    336   char *line = NULL;
    337 
    338   do {
    339     if(NULL == fgets(buf, sizeof(buf), fp))
    340       break;
    341     if(!line) {
    342       line = strdup(buf);
    343       if(!line)
    344         return NULL;
    345     }
    346     else {
    347       char *ptr;
    348       size_t linelen = strlen(line);
    349       ptr = realloc(line, linelen + strlen(buf) + 1);
    350       if(!ptr) {
    351         Curl_safefree(line);
    352         return NULL;
    353       }
    354       line = ptr;
    355       strcpy(&line[linelen], buf);
    356     }
    357     nl = strchr(line, '\n');
    358   } while(!nl);
    359 
    360   if(nl)
    361     *nl = '\0';
    362 
    363   return line;
    364 }
    365 
    366