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