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