load.c 15 KB

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