load.c 16 KB

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