load.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  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-2016 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. size_t fname_len;
  185. size_t dir_len; //!< length of the directory portion of the path to the exe
  186. switch (fname[2]) {
  187. case DIRCH:
  188. skip = 3;
  189. case NUL:
  190. break;
  191. default:
  192. return false;
  193. }
  194. /*
  195. * See if the path is included in the program name.
  196. * If it is, we're done. Otherwise, we have to hunt
  197. * for the program using "pathfind".
  198. */
  199. if (strchr(prg_path, DIRCH) != NULL)
  200. path = prg_path;
  201. else {
  202. path = pathfind(getenv("PATH"), (char *)prg_path, "rx");
  203. if (path == NULL)
  204. return false;
  205. }
  206. pz = strrchr(path, DIRCH);
  207. /*
  208. * IF we cannot find a directory name separator,
  209. * THEN we do not have a path name to our executable file.
  210. */
  211. if (pz == NULL)
  212. return false;
  213. fname += skip;
  214. fname_len = strlen(fname) + 1; // + NUL byte
  215. dir_len = (pz - path) + 1; // + dir sep character
  216. /*
  217. * Concatenate the file name to the end of the executable path.
  218. * The result may be either a file or a directory.
  219. */
  220. if (dir_len + fname_len > (unsigned)b_sz)
  221. return false;
  222. memcpy(buf, path, dir_len);
  223. memcpy(buf + dir_len, fname, fname_len);
  224. /*
  225. * If the "path" path was gotten from "pathfind()", then it was
  226. * allocated and we need to deallocate it.
  227. */
  228. if (path != prg_path)
  229. AGFREE(path);
  230. return true;
  231. }
  232. /**
  233. * Add an environment variable value.
  234. */
  235. static bool
  236. add_env_val(char * buf, int buf_sz, char const * name)
  237. {
  238. char * dir_part = buf;
  239. for (;;) {
  240. int ch = (int)*++name;
  241. if (! IS_VALUE_NAME_CHAR(ch))
  242. break;
  243. *(dir_part++) = (char)ch;
  244. }
  245. if (dir_part == buf)
  246. return false;
  247. *dir_part = NUL;
  248. dir_part = getenv(buf);
  249. /*
  250. * Environment value not found -- skip the home list entry
  251. */
  252. if (dir_part == NULL)
  253. return false;
  254. {
  255. size_t dir_len = strlen(dir_part);
  256. size_t nm_len = strlen(name) + 1;
  257. if (dir_len + nm_len >= (unsigned)buf_sz)
  258. return false;
  259. memcpy(buf, dir_part, dir_len);
  260. memcpy(buf + dir_len, name, nm_len);
  261. }
  262. return true;
  263. }
  264. /**
  265. * Trim leading and trailing white space.
  266. * If we are cooking the text and the text is quoted, then "cook"
  267. * the string. To cook, the string must be quoted.
  268. *
  269. * @param[in,out] txt the input and output string
  270. * @param[in] mode the handling mode (cooking method)
  271. */
  272. LOCAL void
  273. munge_str(char * txt, tOptionLoadMode mode)
  274. {
  275. char * end;
  276. if (mode == OPTION_LOAD_KEEP)
  277. return;
  278. if (IS_WHITESPACE_CHAR(*txt)) {
  279. char * src = SPN_WHITESPACE_CHARS(txt+1);
  280. size_t l = strlen(src) + 1;
  281. memmove(txt, src, l);
  282. end = txt + l - 1;
  283. } else
  284. end = txt + strlen(txt);
  285. end = SPN_WHITESPACE_BACK(txt, end);
  286. *end = NUL;
  287. if (mode == OPTION_LOAD_UNCOOKED)
  288. return;
  289. switch (*txt) {
  290. default: return;
  291. case '"':
  292. case '\'': break;
  293. }
  294. switch (end[-1]) {
  295. default: return;
  296. case '"':
  297. case '\'': break;
  298. }
  299. (void)ao_string_cook(txt, NULL);
  300. }
  301. static char *
  302. assemble_arg_val(char * txt, tOptionLoadMode mode)
  303. {
  304. char * end = strpbrk(txt, ARG_BREAK_STR);
  305. int space_break;
  306. /*
  307. * Not having an argument to a configurable name is okay.
  308. */
  309. if (end == NULL)
  310. return txt + strlen(txt);
  311. /*
  312. * If we are keeping all whitespace, then the modevalue starts with the
  313. * character that follows the end of the configurable name, regardless
  314. * of which character caused it.
  315. */
  316. if (mode == OPTION_LOAD_KEEP) {
  317. *(end++) = NUL;
  318. return end;
  319. }
  320. /*
  321. * If the name ended on a white space character, remember that
  322. * because we'll have to skip over an immediately following ':' or '='
  323. * (and the white space following *that*).
  324. */
  325. space_break = IS_WHITESPACE_CHAR(*end);
  326. *(end++) = NUL;
  327. end = SPN_WHITESPACE_CHARS(end);
  328. if (space_break && ((*end == ':') || (*end == '=')))
  329. end = SPN_WHITESPACE_CHARS(end+1);
  330. return end;
  331. }
  332. static char *
  333. trim_quotes(char * arg)
  334. {
  335. switch (*arg) {
  336. case '"':
  337. case '\'':
  338. ao_string_cook(arg, NULL);
  339. }
  340. return arg;
  341. }
  342. /**
  343. * See if the option is to be processed in the current scan direction
  344. * (-1 or +1).
  345. */
  346. static bool
  347. direction_ok(opt_state_mask_t f, int dir)
  348. {
  349. if (dir == 0)
  350. return true;
  351. switch (f & (OPTST_IMM|OPTST_DISABLE_IMM)) {
  352. case 0:
  353. /*
  354. * The selected option has no immediate action.
  355. * THEREFORE, if the direction is PRESETTING
  356. * THEN we skip this option.
  357. */
  358. if (PRESETTING(dir))
  359. return false;
  360. break;
  361. case OPTST_IMM:
  362. if (PRESETTING(dir)) {
  363. /*
  364. * We are in the presetting direction with an option we handle
  365. * immediately for enablement, but normally for disablement.
  366. * Therefore, skip if disabled.
  367. */
  368. if ((f & OPTST_DISABLED) == 0)
  369. return false;
  370. } else {
  371. /*
  372. * We are in the processing direction with an option we handle
  373. * immediately for enablement, but normally for disablement.
  374. * Therefore, skip if NOT disabled.
  375. */
  376. if ((f & OPTST_DISABLED) != 0)
  377. return false;
  378. }
  379. break;
  380. case OPTST_DISABLE_IMM:
  381. if (PRESETTING(dir)) {
  382. /*
  383. * We are in the presetting direction with an option we handle
  384. * immediately for disablement, but normally for disablement.
  385. * Therefore, skip if NOT disabled.
  386. */
  387. if ((f & OPTST_DISABLED) != 0)
  388. return false;
  389. } else {
  390. /*
  391. * We are in the processing direction with an option we handle
  392. * immediately for disablement, but normally for disablement.
  393. * Therefore, skip if disabled.
  394. */
  395. if ((f & OPTST_DISABLED) == 0)
  396. return false;
  397. }
  398. break;
  399. case OPTST_IMM|OPTST_DISABLE_IMM:
  400. /*
  401. * The selected option is always for immediate action.
  402. * THEREFORE, if the direction is PROCESSING
  403. * THEN we skip this option.
  404. */
  405. if (PROCESSING(dir))
  406. return false;
  407. break;
  408. }
  409. return true;
  410. }
  411. /**
  412. * Load an option from a block of text. The text must start with the
  413. * configurable/option name and be followed by its associated value.
  414. * That value may be processed in any of several ways. See "tOptionLoadMode"
  415. * in autoopts.h.
  416. *
  417. * @param[in,out] opts program options descriptor
  418. * @param[in,out] opt_state option processing state
  419. * @param[in,out] line source line with long option name in it
  420. * @param[in] direction current processing direction (preset or not)
  421. * @param[in] load_mode option loading mode (OPTION_LOAD_*)
  422. */
  423. LOCAL void
  424. load_opt_line(tOptions * opts, tOptState * opt_state, char * line,
  425. tDirection direction, tOptionLoadMode load_mode )
  426. {
  427. /*
  428. * When parsing a stored line, we only look at the characters after
  429. * a hyphen. Long names must always be at least two characters and
  430. * short options are always exactly one character long.
  431. */
  432. line = SPN_LOAD_LINE_SKIP_CHARS(line);
  433. {
  434. char * arg = assemble_arg_val(line, load_mode);
  435. if (IS_OPTION_NAME_CHAR(line[1])) {
  436. if (! SUCCESSFUL(opt_find_long(opts, line, opt_state)))
  437. return;
  438. } else if (! SUCCESSFUL(opt_find_short(opts, *line, opt_state)))
  439. return;
  440. if ((! CALLED(direction)) && (opt_state->flags & OPTST_NO_INIT))
  441. return;
  442. opt_state->pzOptArg = trim_quotes(arg);
  443. }
  444. if (! direction_ok(opt_state->flags, direction))
  445. return;
  446. /*
  447. * Fix up the args.
  448. */
  449. if (OPTST_GET_ARGTYPE(opt_state->pOD->fOptState) == OPARG_TYPE_NONE) {
  450. if (*opt_state->pzOptArg != NUL)
  451. return;
  452. opt_state->pzOptArg = NULL;
  453. } else if (opt_state->pOD->fOptState & OPTST_ARG_OPTIONAL) {
  454. if (*opt_state->pzOptArg == NUL)
  455. opt_state->pzOptArg = NULL;
  456. else {
  457. AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
  458. opt_state->flags |= OPTST_ALLOC_ARG;
  459. }
  460. } else {
  461. if (*opt_state->pzOptArg == NUL)
  462. opt_state->pzOptArg = zNil;
  463. else {
  464. AGDUPSTR(opt_state->pzOptArg, opt_state->pzOptArg, "opt arg");
  465. opt_state->flags |= OPTST_ALLOC_ARG;
  466. }
  467. }
  468. {
  469. tOptionLoadMode sv = option_load_mode;
  470. option_load_mode = load_mode;
  471. handle_opt(opts, opt_state);
  472. option_load_mode = sv;
  473. }
  474. }
  475. /*=export_func optionLoadLine
  476. *
  477. * what: process a string for an option name and value
  478. *
  479. * arg: tOptions *, opts, program options descriptor
  480. * arg: char const *, line, NUL-terminated text
  481. *
  482. * doc:
  483. *
  484. * This is a client program callable routine for setting options from, for
  485. * example, the contents of a file that they read in. Only one option may
  486. * appear in the text. It will be treated as a normal (non-preset) option.
  487. *
  488. * When passed a pointer to the option struct and a string, it will find
  489. * the option named by the first token on the string and set the option
  490. * argument to the remainder of the string. The caller must NUL terminate
  491. * the string. The caller need not skip over any introductory hyphens.
  492. * Any embedded new lines will be included in the option
  493. * argument. If the input looks like one or more quoted strings, then the
  494. * input will be "cooked". The "cooking" is identical to the string
  495. * formation used in AutoGen definition files (@pxref{basic expression}),
  496. * except that you may not use backquotes.
  497. *
  498. * err: Invalid options are silently ignored. Invalid option arguments
  499. * will cause a warning to print, but the function should return.
  500. =*/
  501. void
  502. optionLoadLine(tOptions * opts, char const * line)
  503. {
  504. tOptState st = OPTSTATE_INITIALIZER(SET);
  505. char * pz;
  506. proc_state_mask_t sv_flags = opts->fOptSet;
  507. opts->fOptSet &= ~OPTPROC_ERRSTOP;
  508. AGDUPSTR(pz, line, "opt line");
  509. load_opt_line(opts, &st, pz, DIRECTION_CALLED, OPTION_LOAD_COOKED);
  510. AGFREE(pz);
  511. opts->fOptSet = sv_flags;
  512. }
  513. /** @}
  514. *
  515. * Local Variables:
  516. * mode: C
  517. * c-file-style: "stroustrup"
  518. * indent-tabs-mode: nil
  519. * End:
  520. * end of autoopts/load.c */