Am Tue, 12. May 2009, 10:41:31 +0200 schrieb Jö Fahlke: > Applied is a first version of a patch to improve ifparser_init(). I didn't > use getline() after all. It compiles and I did some rudimentary testing. I'm > still working on an improvement regarding the handling of continued lines.
And here comes the improved version, which I consider final unless someone cares to comment. Bye, Jö. -- featured product: Debian GNU/Linux - http://www.debian.org
Index: network-manager-0.7.1/system-settings/plugins/ifupdown/interface_parser.c =================================================================== --- network-manager-0.7.1.orig/system-settings/plugins/ifupdown/interface_parser.c 2009-05-12 23:11:11.000000000 +0200 +++ network-manager-0.7.1/system-settings/plugins/ifupdown/interface_parser.c 2009-05-12 23:18:35.000000000 +0200 @@ -26,6 +26,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <ctype.h> #include "nm-utils.h" if_block* first; @@ -74,86 +75,266 @@ //printf("added data '%s' with key '%s'\n",data,key); } -#define SPACE_OR_TAB(string,ret) {ret = strchr(string,' ');ret=(ret == NULL?strchr(string,'\t'):ret);} + +/** Append a line read from a stream to a string + * + * Read a line from stream and append it at the location *str+*str_len. If + * the line terminated in a newline, it is stripped. *str_len is then updated + * to reflect the new size. On entrance, *str may be unterminated, but on + * exit it is always terminted with a newline (unless an allocation error + * occured). + * + * If the space available for *str is insufficient, new space is allocated + * using realloc(). *str_size is updated accordingly. + * + * On entrance, *str may be NULL. In this case the initial values of + * *str_size and *str_len don't matter and some space is allocated using + * malloc(). + * + * On success, the return value is the number of characters read, which is + * usually more than the number of characters actually appended because of the + * newline-stripping. On error or EOF -1 is returned. The return value will + * never be 0 size zero characters read is interpreted as EOF. + * + * \param str Pointer to the string to be appended to. + * \param str_size Allocated space for the string. + * \param str_len Length of string. + * \param stream The stream to read from. + */ +static ssize_t append_line(char **str, size_t *str_size, size_t *str_len, FILE *stream) +{ + + size_t chars_read; + size_t global_chars_read = 0; + + if(*str == NULL) + { + char *newstr = (char *)malloc(80); + if(newstr == NULL) + return -1; + *str = newstr; + *str_size = 80; + *str_len = 0; + (*str)[0] = '\0'; + } + + do { + if(*str_size <= *str_len + 1) + { + char *newstr = (char *)realloc(*str, *str_size * 2); + if(newstr == NULL) + return -1; + *str = newstr; + *str_size = *str_size * 2; + } + + if(fgets(*str+*str_len, *str_size-*str_len, stream) == NULL) + /* restore terminating \0 in case there was an error */ + (*str)[*str_len] = '\0'; + chars_read = strlen(*str+*str_len); + *str_len += chars_read; + global_chars_read += chars_read; + } while(chars_read > 0 && (*str)[*str_len-1] != '\n'); + + /* trim trailing newline */ + if(global_chars_read > 0 && (*str)[*str_len-1] == '\n') + (*str)[--*str_len] = '\0'; + + if(global_chars_read == 0) + return -1; + return global_chars_read; +} + +/** Get the next non-empty non-comment line + * + * Get the next line from stream which is neither empty nor a comment starting + * with '#'. Lines may be continued using '\\' just before the newline, + * except inside comments. The "\\\n" pair is stripped, as are trailing + * newlines. + * + * The line is stored in *line. The initial allocated space of line should be + * passed in *line_size. If this space is insufficient, new space is + * allocated with realloc() and *line and *line_size are updated accordingly. + * On exit, *line_length will contain the length of the line and *start will + * contain the position of the first non-whitespace character. + * + * It is permissible to pass *line == NULL. In this case the initial value of + * *line_size is ignored and some space is allocated first. + * + * On error, or when EOF was reached and no valid line was read, -1 is + * returned. In this case *line_length and *start have undefined values and + * *line containes garbage. *line_size will still contain the allocated size, + * however. If *line was initially NULL, it may be NULL on return and + * *linesize may still contain it's initial value. + * + * On success the return value is 0. + * + * \param line Pointer to the string to be appended to. + * \param line_size Allocated space for the string. + * \param[out] line_length Length of the line. + * \param[out] start Position of first non-whitespace character. + * \param[in] stream The stream to read from. + */ +static ssize_t getline_extended(char **line, size_t *line_size, size_t *line_length, + size_t *start, FILE *stream) +{ + /* loop until non-empty non-comment line was found */ + while(1) + { + *line_length = 0; + *start = 0; + /* loop for line continuation */ + while(1) + { + /* read a line part */ + ssize_t ret = append_line(line, line_size, line_length, stream); + /* if we failed to read anything, assume EOF */ + if(ret == -1) + { + /* either the whole line is empty, then we don't have a + line to return */ + if((*line)[*start] == '\0') + return -1; + /* or we do return what we have so far */ + else + return 0; + } + + /* trim leading whitespace */ + while(isspace((*line)[*start])) + ++*start; + + /* skip comments and empty lines */ + if ((*line)[*start] == '#' || (*line)[*start] == '\0') + break; + + /* check for line continuation */ + /* no need to check whether *line_length == 0, we already + excluded that possiblity when we checked for empty lines + above */ + if((*line)[*line_length-1] != '\\') + return 0; /* no continuation */ + + /* continuation */ + --*line_length; + /* no need to truncate line, append_line cares abount + *line_length only anyway. */ + } + } +} void ifparser_init(void) { FILE *inp = fopen(ENI_INTERFACES_FILE, "r"); - int ret = 0; - char *line; - char *space; - char rline[255]; + ssize_t ret = 0; + char *line = NULL; + size_t line_space = 0; /* reserved space for line */ + size_t line_length; /* actual string length of line */ + size_t word1, word1_end; + size_t word2; + size_t line_end; if (inp == NULL) { nm_warning ("Error: Can't open %s\n", ENI_INTERFACES_FILE); return; } - first = last = NULL; + while(1) { - line = space = NULL; - ret = fscanf(inp,"%255[^\n]\n",rline); - if (ret == EOF) - break; - // If the line did not match, skip it - if (ret == 0) { - char *ignored; + line_length = 0; + word1 = 0; - ignored = fgets(rline, 255, inp); + ret = getline_extended(&line, &line_space, &line_length, &word1, inp); + if(ret == -1) + break; /* failed to read line; probably EOF */ + + /* find end of first word */ + word1_end = word1; + while(line[word1_end] != '\0' && !isspace(line[word1_end])) + ++word1_end; + /* find beginning of second word */ + word2 = word1_end; + while(isspace(line[word2])) + ++word2; + /* skip line if there is no second word */ + if(line[word2] == '\0') + { + nm_warning ("Error: Can't parse interface line '%s'\n",line); continue; } - line = rline; - while(line[0] == ' ') - line++; - if (line[0]=='#' || line[0]=='\0') - continue; - - SPACE_OR_TAB(line,space) - if (space == NULL) - { - nm_warning ("Error: Can't parse interface line '%s'\n",line); - continue; - } - space[0] = '\0'; + /* trim end of line */ + line_end = line_length-1; + while(isspace(line[line_end])) + --line_end; + ++line_end; // There are four different stanzas: // iface, mapping, auto and allow-*. Create a block for each of them. - if (strcmp(line,"iface")==0) + if (word1_end == word1+5 && strncmp(line+word1,"iface",5)==0) { - char *space2 = strchr(space+1,' '); - if (space2 == NULL) + size_t word2_end; + size_t word3, word3_end; + size_t word4; + + /* find end of second word */ + word2_end = word2; + while(line[word2_end] != '\0' && !isspace(line[word2_end])) + ++word2_end; + + /* find beginning of third word */ + word3 = word2_end; + while(isspace(line[word3])) + ++word3; + if (line[word3] == '\0') { - nm_warning ("Error: Can't parse iface line '%s'\n",space+1); + nm_warning ("Error: Can't parse iface line '%s'\n",line); continue; } - space2[0]='\0'; - add_block(line,space+1); - if (space2[1]!='\0') + /* find end of third word */ + word3_end = word3; + while(line[word3_end] != '\0' && !isspace(line[word3_end])) + ++word3_end; + + /* find beginning of fourth word */ + word4 = word3_end; + while(isspace(line[word4])) + ++word4; + if (line[word4] == '\0') { - space = strchr(space2+1,' '); - if (space == NULL) - { - nm_warning ("Error: Can't parse data '%s'\n",space2+1); - continue; - } - space[0] = '\0'; - add_data(space2+1,space+1); + nm_warning ("Error: Can't parse iface line '%s'\n",line); + continue; } + + /* no more error-printing to be done here, we can modify line */ + /* add block with i.e. "iface" "eth0" */ + line[word1_end] = '\0'; + line[word2_end] = '\0'; + add_block(line+word1, line+word2); + + /* add data with i.e. "inet" "dhcp" */ + line[word3_end] = '\0'; + line[line_end] = '\0'; + add_data(line+word3, line+word4); } - else if (strcmp(line,"auto")==0) - add_block(line,space+1); - else if (strcmp(line,"mapping")==0) - add_block(line,space+1); - else if (strncmp(line,"allow-",6)==0) - add_block(line,space+1); else - add_data(line,space+1); + { + /* no more error-printing to be done here, we can modify line */ + line[word1_end] = '\0'; + line[line_end] = '\0'; + if(strcmp(line+word1,"auto")==0 || + strcmp(line+word1,"mapping")==0 || + strncmp(line+word1,"allow-",6)==0) + add_block(line+word1, line+word2); + else + add_data(line+word1, line+word2); + } - //printf("line: '%s' ret=%d\n",rline,ret); } + + free(line); fclose(inp); }
signature.asc
Description: Digital signature