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