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