3 #include "utils_base.h"
6 /* np_ini_info contains the result of parsing a "locator" in the format
7 * [stanza_name][@config_filename] (check_foo@/etc/foo.ini, for example)
14 /* eat all characters from a FILE pointer until n is encountered */
15 #define GOBBLE_TO(f, c, n) do { (c)=fgetc((f)); } while((c)!=EOF && (c)!=(n))
17 /* internal function that returns the constructed defaults options */
18 static char* read_defaults(FILE *f
, const char *stanza
);
19 /* internal function that converts a single line into options format */
20 static int add_option(FILE *f
, char **optbuf
, size_t *bufsize
);
22 /* parse_locator decomposes a string of the form
24 * into its seperate parts
26 static void parse_locator(const char *locator
, const char *def_stanza
, np_ini_info
*i
){
27 size_t locator_len
, stanza_len
;
29 locator_len
=strlen(locator
);
30 stanza_len
=strcspn(locator
, "@");
31 /* if a non-default stanza is provided */
33 i
->stanza
=(char*)malloc(sizeof(char)*(stanza_len
+1));
34 strncpy(i
->stanza
, locator
, stanza_len
);
35 i
->stanza
[stanza_len
]='\0';
36 } else { /* otherwise we use the default stanza */
37 i
->stanza
=strdup(def_stanza
);
39 /* if there is no @file part */
40 if(stanza_len
==locator_len
){
41 i
->file
=strdup(NP_DEFAULT_INI_PATH
);
43 i
->file
=strdup(&(locator
[stanza_len
+1]));
46 if(i
->file
==NULL
|| i
->stanza
==NULL
){
47 die(STATE_UNKNOWN
, _("malloc() failed!\n"));
51 /* this is the externally visible function used by plugins */
52 char* np_get_defaults(const char *locator
, const char *default_section
){
57 parse_locator(locator
, default_section
, &i
);
58 /* if a file was specified or if we're using the default file */
59 if(i
.file
!= NULL
&& strlen(i
.file
) > 0){
60 if(strcmp(i
.file
, "-")==0){
63 inifile
=fopen(i
.file
, "r");
65 if(inifile
==NULL
) die(STATE_UNKNOWN
, _("Config file error"));
66 defaults
=read_defaults(inifile
, i
.stanza
);
68 if(inifile
!=stdout
) fclose(inifile
);
74 /* read_defaults is where the meat of the parsing takes place.
76 * note that this may be called by a setuid binary, so we need to
77 * be extra careful about user-supplied input (i.e. avoiding possible
78 * format string vulnerabilities, etc)
80 static char* read_defaults(FILE *f
, const char *stanza
){
83 size_t i
, stanza_len
, opts_buf_size
=0;
84 enum { NOSTANZA
, WRONGSTANZA
, RIGHTSTANZA
} stanzastate
=NOSTANZA
;
86 stanza_len
=strlen(stanza
);
88 /* our little stanza-parsing state machine. */
89 while((c
=fgetc(f
))!=EOF
){
90 /* gobble up leading whitespace */
91 if(isspace(c
)) continue;
93 /* globble up coment lines */
95 GOBBLE_TO(f
, c
, '\n');
97 /* start of a stanza. check to see if it matches */
99 stanzastate
=WRONGSTANZA
;
100 for(i
=0; i
<stanza_len
; i
++){
102 /* nope, read to the end of the stanza header */
104 GOBBLE_TO(f
, c
, ']');
108 /* if it matched up to here and the next char is ']'... */
111 if(c
==']') stanzastate
=RIGHTSTANZA
;
114 /* otherwise, we're in the body of a stanza or a parse error */
117 /* we never found the start of the first stanza, so
118 * we're dealing with a config error
121 die(STATE_UNKNOWN
, _("Config file error"));
123 /* we're in a stanza, but for a different plugin */
125 GOBBLE_TO(f
, c
, '\n');
127 /* okay, this is where we start taking the config */
130 if(add_option(f
, &opts
, &opts_buf_size
)){
131 die(STATE_UNKNOWN
, _("Config file error"));
142 * read one line of input in the format
143 * ^option[[:space:]]*(=[[:space:]]*value)?
144 * and creates it as a cmdline argument
146 * appending it to the string pointed to by optbuf (which will
147 * be dynamically grown if needed)
149 static int add_option(FILE *f
, char **optbuf
, size_t *bufsize
){
150 char *newbuf
=*optbuf
;
151 char *linebuf
=NULL
, *lineend
=NULL
, *optptr
=NULL
, *optend
=NULL
;
152 char *eqptr
=NULL
, *valptr
=NULL
, *spaceptr
=NULL
, *valend
=NULL
;
153 short done_reading
=0, equals
=0, value
=0;
154 size_t cfg_len
=0, read_sz
=8, linebuf_sz
=0, read_pos
=0, bs
=*bufsize
;
155 size_t opt_len
=0, val_len
=0;
157 /* read one line from the file */
158 while(!done_reading
){
159 /* grow if necessary */
160 if(linebuf
==NULL
|| read_pos
+read_sz
>= linebuf_sz
){
161 linebuf_sz
=(linebuf_sz
>0)?linebuf_sz
<<1:read_sz
;
162 linebuf
=realloc(linebuf
, linebuf_sz
);
163 if(linebuf
==NULL
) die(STATE_UNKNOWN
, _("malloc() failed!\n"));
165 if(fgets(&linebuf
[read_pos
], read_sz
, f
)==NULL
) done_reading
=1;
167 read_pos
=strlen(linebuf
);
168 if(linebuf
[read_pos
-1]=='\n') {
169 linebuf
[--read_pos
]='\0';
174 lineend
=&linebuf
[read_pos
];
175 /* all that to read one line. isn't C fun? :) now comes the parsing :/ */
177 /* skip leading whitespace */
178 for(optptr
=linebuf
; optptr
<lineend
&& isspace(*optptr
); optptr
++);
179 /* continue to '=' or EOL, watching for spaces that might precede it */
180 for(eqptr
=optptr
; eqptr
<lineend
&& *eqptr
!='='; eqptr
++){
181 if(isspace(*eqptr
) && optend
==NULL
) optend
=eqptr
;
184 if(optend
==NULL
) optend
=eqptr
;
186 /* ^[[:space:]]*=foo is a syntax error */
187 if(optptr
==eqptr
) die(STATE_UNKNOWN
, _("Config file error\n"));
188 /* continue from '=' to start of value or EOL */
189 for(valptr
=eqptr
+1; valptr
<lineend
&& isspace(*valptr
); valptr
++);
190 /* continue to the end of value, watching for trailing space/comments */
191 for(valend
=valptr
; valend
<lineend
; valend
++){
192 if(isspace(*valend
) && spaceptr
==NULL
) spaceptr
=valend
;
193 else if(*valend
=='#') break;
196 if(spaceptr
!=NULL
) valend
=spaceptr
;
198 /* calculate the length of "--foo" */
199 opt_len
=1+optend
-optptr
;
201 /* if valptr<lineend then we have to also allocate space for "=bar" */
204 val_len
=1+valend
-valptr
;
207 /* if valptr==valend then we have "=" but no "bar" */
208 else if (valptr
==lineend
) {
213 /* okay, now we have all the info we need, so we grow the default opts
214 * buffer if it's necessary, and put everything together.
215 * (+2 is for a potential space and a null byte)
217 read_pos
=(newbuf
==NULL
)?0:strlen(newbuf
);
218 if(newbuf
==NULL
|| read_pos
+cfg_len
+2 >= bs
){
219 bs
=(bs
>0)?(bs
+cfg_len
+2)<<1:cfg_len
+1;
220 newbuf
=realloc(newbuf
, bs
);
221 if(newbuf
==NULL
) die(STATE_UNKNOWN
, _("malloc() failed!\n"));
223 if(read_pos
>0) newbuf
[read_pos
++]=' ';
224 strncpy(&newbuf
[read_pos
], "--", 2); read_pos
+=2;
225 strncpy(&newbuf
[read_pos
], optptr
, opt_len
); read_pos
+=opt_len
;
226 if(equals
) newbuf
[read_pos
++]='=';
228 strncpy(&newbuf
[read_pos
], valptr
, val_len
); read_pos
+=val_len
;
230 newbuf
[read_pos
]='\0';