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