load.c 14 KB

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