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