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