load.c revision 1.1.1.2 1 /* $NetBSD: load.c,v 1.1.1.2 2012/01/31 21:27:50 kardel Exp $ */
2
3
4 /**
5 * \file load.c
6 * Time-stamp: "2010-12-18 11:46:07 bkorb"
7 *
8 * This file contains the routines that deal with processing text strings
9 * for options, either from a NUL-terminated string passed in or from an
10 * rc/ini file.
11 *
12 * This file is part of AutoOpts, a companion to AutoGen.
13 * AutoOpts is free software.
14 * AutoOpts is Copyright (c) 1992-2011 by Bruce Korb - all rights reserved
15 *
16 * AutoOpts is available under any one of two licenses. The license
17 * in use must be one of these two and the choice is under the control
18 * of the user of the license.
19 *
20 * The GNU Lesser General Public License, version 3 or later
21 * See the files "COPYING.lgplv3" and "COPYING.gplv3"
22 *
23 * The Modified Berkeley Software Distribution License
24 * See the file "COPYING.mbsd"
25 *
26 * These files have the following md5sums:
27 *
28 * 43b91e8ca915626ed3818ffb1b71248b pkg/libopts/COPYING.gplv3
29 * 06a1a2e4760c90ea5e1dad8dfaac4d39 pkg/libopts/COPYING.lgplv3
30 * 66a5cedaf62c4b2637025f049f9b826f pkg/libopts/COPYING.mbsd
31 */
32
33 /* = = = START-STATIC-FORWARD = = = */
34 static ag_bool
35 insertProgramPath(char * pzBuf, int bufSize, char const * pzName,
36 char const * pzProgPath);
37
38 static ag_bool
39 insertEnvVal(char * pzBuf, int bufSize, char const * pzName,
40 char const * pzProgPath);
41
42 static char*
43 assembleArgValue(char* pzTxt, tOptionLoadMode mode);
44 /* = = = END-STATIC-FORWARD = = = */
45
46 /*=export_func optionMakePath
47 * private:
48 *
49 * what: translate and construct a path
50 * arg: + char* + pzBuf + The result buffer +
51 * arg: + int + bufSize + The size of this buffer +
52 * arg: + char const* + pzName + The input name +
53 * arg: + char const* + pzProgPath + The full path of the current program +
54 *
55 * ret-type: ag_bool
56 * ret-desc: AG_TRUE if the name was handled, otherwise AG_FALSE.
57 * If the name does not start with ``$'', then it is handled
58 * simply by copying the input name to the output buffer and
59 * resolving the name with either
60 * @code{canonicalize_file_name(3GLIBC)} or @code{realpath(3C)}.
61 *
62 * doc:
63 *
64 * This routine will copy the @code{pzName} input name into the
65 * @code{pzBuf} output buffer, not exceeding @code{bufSize} bytes. If the
66 * first character of the input name is a @code{'$'} character, then there
67 * is special handling:
68 * @*
69 * @code{$$} is replaced with the directory name of the @code{pzProgPath},
70 * searching @code{$PATH} if necessary.
71 * @*
72 * @code{$@} is replaced with the AutoGen package data installation directory
73 * (aka @code{pkgdatadir}).
74 * @*
75 * @code{$NAME} is replaced by the contents of the @code{NAME} environment
76 * variable. If not found, the search fails.
77 *
78 * Please note: both @code{$$} and @code{$NAME} must be at the start of the
79 * @code{pzName} string and must either be the entire string or be followed
80 * by the @code{'/'} (backslash on windows) character.
81 *
82 * err: @code{AG_FALSE} is returned if:
83 * @*
84 * @bullet{} The input name exceeds @code{bufSize} bytes.
85 * @*
86 * @bullet{} @code{$$}, @code{$@@} or @code{$NAME} is not the full string
87 * and the next character is not '/'.
88 * @*
89 * @bullet{} libopts was built without PKGDATADIR defined and @code{$@@}
90 * was specified.
91 * @*
92 * @bullet{} @code{NAME} is not a known environment variable
93 * @*
94 * @bullet{} @code{canonicalize_file_name} or @code{realpath} return
95 * errors (cannot resolve the resulting path).
96 =*/
97 ag_bool
98 optionMakePath(char * pzBuf, int bufSize, char const * pzName,
99 char const * pzProgPath)
100 {
101 size_t name_len = strlen(pzName);
102
103 if ((bufSize <= name_len) || (name_len == 0))
104 return AG_FALSE;
105
106 /*
107 * IF not an environment variable, just copy the data
108 */
109 if (*pzName != '$') {
110 char const* pzS = pzName;
111 char* pzD = pzBuf;
112 int ct = bufSize;
113
114 for (;;) {
115 if ( (*(pzD++) = *(pzS++)) == NUL)
116 break;
117 if (--ct <= 0)
118 return AG_FALSE;
119 }
120 }
121
122 /*
123 * IF the name starts with "$$", then it must be "$$" or
124 * it must start with "$$/". In either event, replace the "$$"
125 * with the path to the executable and append a "/" character.
126 */
127 else switch (pzName[1]) {
128 case NUL:
129 return AG_FALSE;
130
131 case '$':
132 if (! insertProgramPath(pzBuf, bufSize, pzName, pzProgPath))
133 return AG_FALSE;
134 break;
135
136 case '@':
137 if (program_pkgdatadir[0] == NUL)
138 return AG_FALSE;
139
140 if (snprintf(pzBuf, bufSize, "%s%s", program_pkgdatadir, pzName + 2)
141 >= bufSize)
142 return AG_FALSE;
143 break;
144
145 default:
146 if (! insertEnvVal(pzBuf, bufSize, pzName, pzProgPath))
147 return AG_FALSE;
148 }
149
150 #if defined(HAVE_CANONICALIZE_FILE_NAME)
151 {
152 char * pz = canonicalize_file_name(pzBuf);
153 if (pz == NULL)
154 return AG_FALSE;
155
156 name_len = strlen(pz);
157 if (name_len >= bufSize) {
158 free(pz);
159 return AG_FALSE;
160 }
161
162 memcpy(pzBuf, pz, name_len + 1);
163 free(pz);
164 }
165
166 #elif defined(HAVE_REALPATH)
167 {
168 char z[PATH_MAX+1];
169
170 if (realpath(pzBuf, z) == NULL)
171 return AG_FALSE;
172
173 name_len = strlen(z);
174 if (name_len >= bufSize)
175 return AG_FALSE;
176
177 memcpy(pzBuf, z, name_len + 1);
178 }
179 #endif
180
181 return AG_TRUE;
182 }
183
184
185 static ag_bool
186 insertProgramPath(char * pzBuf, int bufSize, char const * pzName,
187 char const * pzProgPath)
188 {
189 char const* pzPath;
190 char const* pz;
191 int skip = 2;
192
193 switch (pzName[2]) {
194 case DIRCH:
195 skip = 3;
196 case NUL:
197 break;
198 default:
199 return AG_FALSE;
200 }
201
202 /*
203 * See if the path is included in the program name.
204 * If it is, we're done. Otherwise, we have to hunt
205 * for the program using "pathfind".
206 */
207 if (strchr(pzProgPath, DIRCH) != NULL)
208 pzPath = pzProgPath;
209 else {
210 pzPath = pathfind(getenv("PATH"), (char*)pzProgPath, "rx");
211
212 if (pzPath == NULL)
213 return AG_FALSE;
214 }
215
216 pz = strrchr(pzPath, DIRCH);
217
218 /*
219 * IF we cannot find a directory name separator,
220 * THEN we do not have a path name to our executable file.
221 */
222 if (pz == NULL)
223 return AG_FALSE;
224
225 pzName += skip;
226
227 /*
228 * Concatenate the file name to the end of the executable path.
229 * The result may be either a file or a directory.
230 */
231 if ((pz - pzPath)+1 + strlen(pzName) >= bufSize)
232 return AG_FALSE;
233
234 memcpy(pzBuf, pzPath, (size_t)((pz - pzPath)+1));
235 strcpy(pzBuf + (pz - pzPath) + 1, pzName);
236
237 /*
238 * If the "pzPath" path was gotten from "pathfind()", then it was
239 * allocated and we need to deallocate it.
240 */
241 if (pzPath != pzProgPath)
242 AGFREE(pzPath);
243 return AG_TRUE;
244 }
245
246
247 static ag_bool
248 insertEnvVal(char * pzBuf, int bufSize, char const * pzName,
249 char const * pzProgPath)
250 {
251 char* pzDir = pzBuf;
252
253 for (;;) {
254 int ch = (int)*++pzName;
255 if (! IS_VALUE_NAME_CHAR(ch))
256 break;
257 *(pzDir++) = (char)ch;
258 }
259
260 if (pzDir == pzBuf)
261 return AG_FALSE;
262
263 *pzDir = NUL;
264
265 pzDir = getenv(pzBuf);
266
267 /*
268 * Environment value not found -- skip the home list entry
269 */
270 if (pzDir == NULL)
271 return AG_FALSE;
272
273 if (strlen(pzDir) + 1 + strlen(pzName) >= bufSize)
274 return AG_FALSE;
275
276 sprintf(pzBuf, "%s%s", pzDir, pzName);
277 return AG_TRUE;
278 }
279
280
281 LOCAL void
282 mungeString(char* pzTxt, tOptionLoadMode mode)
283 {
284 char* pzE;
285
286 if (mode == OPTION_LOAD_KEEP)
287 return;
288
289 if (IS_WHITESPACE_CHAR(*pzTxt)) {
290 char* pzS = pzTxt;
291 char* pzD = pzTxt;
292 while (IS_WHITESPACE_CHAR(*++pzS)) ;
293 while ((*(pzD++) = *(pzS++)) != NUL) ;
294 pzE = pzD-1;
295 } else
296 pzE = pzTxt + strlen(pzTxt);
297
298 while ((pzE > pzTxt) && IS_WHITESPACE_CHAR(pzE[-1])) pzE--;
299 *pzE = NUL;
300
301 if (mode == OPTION_LOAD_UNCOOKED)
302 return;
303
304 switch (*pzTxt) {
305 default: return;
306 case '"':
307 case '\'': break;
308 }
309
310 switch (pzE[-1]) {
311 default: return;
312 case '"':
313 case '\'': break;
314 }
315
316 (void)ao_string_cook(pzTxt, NULL);
317 }
318
319
320 static char*
321 assembleArgValue(char* pzTxt, tOptionLoadMode mode)
322 {
323 static char const zBrk[] = " \t\n:=";
324 char* pzEnd = strpbrk(pzTxt, zBrk);
325 int space_break;
326
327 /*
328 * Not having an argument to a configurable name is okay.
329 */
330 if (pzEnd == NULL)
331 return pzTxt + strlen(pzTxt);
332
333 /*
334 * If we are keeping all whitespace, then the modevalue starts with the
335 * character that follows the end of the configurable name, regardless
336 * of which character caused it.
337 */
338 if (mode == OPTION_LOAD_KEEP) {
339 *(pzEnd++) = NUL;
340 return pzEnd;
341 }
342
343 /*
344 * If the name ended on a white space character, remember that
345 * because we'll have to skip over an immediately following ':' or '='
346 * (and the white space following *that*).
347 */
348 space_break = IS_WHITESPACE_CHAR(*pzEnd);
349 *(pzEnd++) = NUL;
350 while (IS_WHITESPACE_CHAR(*pzEnd)) pzEnd++;
351 if (space_break && ((*pzEnd == ':') || (*pzEnd == '=')))
352 while (IS_WHITESPACE_CHAR(*++pzEnd)) ;
353
354 return pzEnd;
355 }
356
357
358 /*
359 * Load an option from a block of text. The text must start with the
360 * configurable/option name and be followed by its associated value.
361 * That value may be processed in any of several ways. See "tOptionLoadMode"
362 * in autoopts.h.
363 */
364 LOCAL void
365 loadOptionLine(
366 tOptions* pOpts,
367 tOptState* pOS,
368 char* pzLine,
369 tDirection direction,
370 tOptionLoadMode load_mode )
371 {
372 while (IS_WHITESPACE_CHAR(*pzLine)) pzLine++;
373
374 {
375 char* pzArg = assembleArgValue(pzLine, load_mode);
376
377 if (! SUCCESSFUL(longOptionFind(pOpts, pzLine, pOS)))
378 return;
379 if (pOS->flags & OPTST_NO_INIT)
380 return;
381 pOS->pzOptArg = pzArg;
382 }
383
384 switch (pOS->flags & (OPTST_IMM|OPTST_DISABLE_IMM)) {
385 case 0:
386 /*
387 * The selected option has no immediate action.
388 * THEREFORE, if the direction is PRESETTING
389 * THEN we skip this option.
390 */
391 if (PRESETTING(direction))
392 return;
393 break;
394
395 case OPTST_IMM:
396 if (PRESETTING(direction)) {
397 /*
398 * We are in the presetting direction with an option we handle
399 * immediately for enablement, but normally for disablement.
400 * Therefore, skip if disabled.
401 */
402 if ((pOS->flags & OPTST_DISABLED) == 0)
403 return;
404 } else {
405 /*
406 * We are in the processing direction with an option we handle
407 * immediately for enablement, but normally for disablement.
408 * Therefore, skip if NOT disabled.
409 */
410 if ((pOS->flags & OPTST_DISABLED) != 0)
411 return;
412 }
413 break;
414
415 case OPTST_DISABLE_IMM:
416 if (PRESETTING(direction)) {
417 /*
418 * We are in the presetting direction with an option we handle
419 * immediately for disablement, but normally for disablement.
420 * Therefore, skip if NOT disabled.
421 */
422 if ((pOS->flags & OPTST_DISABLED) != 0)
423 return;
424 } else {
425 /*
426 * We are in the processing direction with an option we handle
427 * immediately for disablement, but normally for disablement.
428 * Therefore, skip if disabled.
429 */
430 if ((pOS->flags & OPTST_DISABLED) == 0)
431 return;
432 }
433 break;
434
435 case OPTST_IMM|OPTST_DISABLE_IMM:
436 /*
437 * The selected option is always for immediate action.
438 * THEREFORE, if the direction is PROCESSING
439 * THEN we skip this option.
440 */
441 if (PROCESSING(direction))
442 return;
443 break;
444 }
445
446 /*
447 * Fix up the args.
448 */
449 if (OPTST_GET_ARGTYPE(pOS->pOD->fOptState) == OPARG_TYPE_NONE) {
450 if (*pOS->pzOptArg != NUL)
451 return;
452 pOS->pzOptArg = NULL;
453
454 } else if (pOS->pOD->fOptState & OPTST_ARG_OPTIONAL) {
455 if (*pOS->pzOptArg == NUL)
456 pOS->pzOptArg = NULL;
457 else {
458 AGDUPSTR(pOS->pzOptArg, pOS->pzOptArg, "option argument");
459 pOS->flags |= OPTST_ALLOC_ARG;
460 }
461
462 } else {
463 if (*pOS->pzOptArg == NUL)
464 pOS->pzOptArg = zNil;
465 else {
466 AGDUPSTR(pOS->pzOptArg, pOS->pzOptArg, "option argument");
467 pOS->flags |= OPTST_ALLOC_ARG;
468 }
469 }
470
471 {
472 tOptionLoadMode sv = option_load_mode;
473 option_load_mode = load_mode;
474 handle_opt(pOpts, pOS);
475 option_load_mode = sv;
476 }
477 }
478
479
480 /*=export_func optionLoadLine
481 *
482 * what: process a string for an option name and value
483 *
484 * arg: tOptions*, pOpts, program options descriptor
485 * arg: char const*, pzLine, NUL-terminated text
486 *
487 * doc:
488 *
489 * This is a client program callable routine for setting options from, for
490 * example, the contents of a file that they read in. Only one option may
491 * appear in the text. It will be treated as a normal (non-preset) option.
492 *
493 * When passed a pointer to the option struct and a string, it will find
494 * the option named by the first token on the string and set the option
495 * argument to the remainder of the string. The caller must NUL terminate
496 * the string. Any embedded new lines will be included in the option
497 * argument. If the input looks like one or more quoted strings, then the
498 * input will be "cooked". The "cooking" is identical to the string
499 * formation used in AutoGen definition files (@pxref{basic expression}),
500 * except that you may not use backquotes.
501 *
502 * err: Invalid options are silently ignored. Invalid option arguments
503 * will cause a warning to print, but the function should return.
504 =*/
505 void
506 optionLoadLine(tOptions * pOpts, char const * pzLine)
507 {
508 tOptState st = OPTSTATE_INITIALIZER(SET);
509 char* pz;
510 AGDUPSTR(pz, pzLine, "user option line");
511 loadOptionLine(pOpts, &st, pz, DIRECTION_PROCESS, OPTION_LOAD_COOKED);
512 AGFREE(pz);
513 }
514 /*
515 * Local Variables:
516 * mode: C
517 * c-file-style: "stroustrup"
518 * indent-tabs-mode: nil
519 * End:
520 * end of autoopts/load.c */
521