diff --git a/src/cfgparse.c b/src/cfgparse.c index 39abf6b..6a7f80c 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -5821,12 +5821,19 @@ out: */ int readcfgfile(const char *file) { - char thisline[LINESIZE]; + char *thisline; + int linesize = LINESIZE; FILE *f; int linenum = 0; int err_code = 0; struct cfg_section *cs = NULL; struct cfg_section *ics; + int readbytes = 0; + + if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) { + Alert("parsing [%s] : out of memory.\n", file); + return -1; + } /* Register internal sections */ if (!cfg_register_section("listen", cfg_parse_listen) || @@ -5842,11 +5849,14 @@ int readcfgfile(const char *file) if ((f=fopen(file,"r")) == NULL) return -1; - while (fgets(thisline, sizeof(thisline), f) != NULL) { +next_line: + while (fgets(thisline + readbytes, linesize - readbytes, f) != NULL) { int arg, kwm = KWM_STD; char *end; char *args[MAX_LINE_ARGS + 1]; char *line = thisline; + int dquote = 0; /* double quote */ + int squote = 0; /* simple quote */ linenum++; @@ -5856,11 +5866,25 @@ int readcfgfile(const char *file) /* Check if we reached the limit and the last char is not \n. * Watch out for the last line without the terminating '\n'! */ - Alert("parsing [%s:%d]: line too long, limit: %d.\n", - file, linenum, (int)sizeof(thisline)-1); - err_code |= ERR_ALERT | ERR_FATAL; + char *newline; + int newlinesize = linesize * 2; + + newline = realloc(thisline, sizeof(*thisline) * newlinesize); + if (newline == NULL) { + Alert("parsing [%s:%d]: line too long, cannot allocate memory.\n", + file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + continue; + } + + readbytes = linesize - 1; + linesize = newlinesize; + thisline = newline; + continue; } + readbytes = 0; + /* skip leading spaces */ while (isspace((unsigned char)*line)) line++; @@ -5869,10 +5893,26 @@ int readcfgfile(const char *file) args[arg] = line; while (*line && arg < MAX_LINE_ARGS) { - /* first, we'll replace \\, \, \#, \r, \n, \t, \xXX with their - * C equivalent value. Other combinations left unchanged (eg: \1). - */ - if (*line == '\\') { + if (*line == '"' && !squote) { /* double quote outside single quotes */ + if (dquote) + dquote = 0; + else + dquote = 1; + memmove(line, line + 1, end - line); + end--; + } + else if (*line == '\'' && !dquote) { /* single quote outside double quotes */ + if (squote) + squote = 0; + else + squote = 1; + memmove(line, line + 1, end - line); + end--; + } + else if (*line == '\\' && !squote) { + /* first, we'll replace \\, \, \#, \r, \n, \t, \xXX with their + * C equivalent value. Other combinations left unchanged (eg: \1). + */ int skip = 0; if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') { *line = line[1]; @@ -5904,6 +5944,15 @@ int readcfgfile(const char *file) Alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; } + } else if (line[1] == '"') { + *line = '"'; + skip = 1; + } else if (line[1] == '\'') { + *line = '\''; + skip = 1; + } else if (line[1] == '$' && dquote) { /* escaping of $ only inside double quotes */ + *line = '$'; + skip = 1; } if (skip) { memmove(line + 1, line + 1 + skip, end - (line + skip)); @@ -5911,23 +5960,117 @@ int readcfgfile(const char *file) } line++; } - else if (*line == '#' || *line == '\n' || *line == '\r') { + else if ((!squote && !dquote && *line == '#') || *line == '\n' || *line == '\r') { /* end of string, end of loop */ *line = 0; break; } - else if (isspace((unsigned char)*line)) { + else if (!squote && !dquote && isspace((unsigned char)*line)) { /* a non-escaped space is an argument separator */ *line++ = '\0'; while (isspace((unsigned char)*line)) line++; args[++arg] = line; } + else if (dquote && *line == '$') { + /* environment variables are evaluated inside double quotes */ + char *var_beg; + char *var_end; + char save_char; + char *value; + int val_len; + int newlinesize; + int braces = 0; + + var_beg = line + 1; + var_end = var_beg; + + if (*var_beg == '{') { + var_beg++; + var_end++; + braces = 1; + } + + if (!isalpha((int)(unsigned char)*var_beg) && *var_beg != '_') { + Alert("parsing [%s:%d] : Variable expansion: Unrecognized character '%c' in variable name.\n", file, linenum, *var_beg); + err_code |= ERR_ALERT | ERR_FATAL; + goto next_line; /* skip current line */ + } + + while (isalnum((int)(unsigned char)*var_end) || *var_end == '_') + var_end++; + + save_char = *var_end; + *var_end = '\0'; + value = getenv(var_beg); + *var_end = save_char; + val_len = value ? strlen(value) : 0; + + if (braces) { + if (*var_end == '}') { + var_end++; + braces = 0; + } else { + Alert("parsing [%s:%d] : Variable expansion: Mismatched braces.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto next_line; /* skip current line */ + } + } + + newlinesize = (end - thisline) - (var_end - line) + val_len + 1; + + /* if not enough space in thisline */ + if (newlinesize > linesize) { + char *newline; + + newline = realloc(thisline, newlinesize * sizeof(*thisline)); + if (newline == NULL) { + Alert("parsing [%s:%d] : Variable expansion: Not enough memory.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto next_line; /* slip current line */ + } + /* recompute pointers if realloc returns a new pointer */ + if (newline != thisline) { + int i; + int diff; + + for (i = 0; i <= arg; i++) { + diff = args[i] - thisline; + args[i] = newline + diff; + } + + diff = var_end - thisline; + var_end = newline + diff; + diff = end - thisline; + end = newline + diff; + diff = line - thisline; + line = newline + diff; + thisline = newline; + } + linesize = newlinesize; + } + + /* insert value inside the line */ + memmove(line + val_len, var_end, end - var_end + 1); + memcpy(line, value, val_len); + end += val_len - (var_end - line); + line += val_len; + } else { line++; } } + if (dquote) { + Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } + + if (squote) { + Alert("parsing [%s:%d] : Mismatched simple quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } + /* empty line */ if (!**args) continue; @@ -5998,6 +6141,7 @@ int readcfgfile(const char *file) break; } cursection = NULL; + free(thisline); fclose(f); return err_code; }