load.c 16 KB

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