load.c 14 KB

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