load.c 15 KB

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