00001
00002
00003
00004
00005
00006
00007
00008 #include <ctype.h>
00009 #include <stdlib.h>
00010
00011 #include "global.h"
00012 #include "log.h"
00013 #include "interface.h"
00014 #include "options.h"
00015 #include "helper.h"
00016 #include "warnings.h"
00017
00018
00022 #define ENV_PREFIX "FSVS_"
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00044 struct opt___val_str_t
00045 {
00046 const char *string;
00047 int val;
00048 };
00049
00052 #define BITMAP_CLEAR (( (unsigned)INT_MIN) >> 1)
00053 #define BITMAP_CLEAR_MASK (BITMAP_CLEAR ^ (BITMAP_CLEAR >> 2))
00054
00057 const struct opt___val_str_t opt___path_strings[]= {
00058 { .val=PATH_PARMRELATIVE, .string="parameter"},
00059 { .val=PATH_ABSOLUTE, .string="absolute"},
00060 { .val=PATH_WCRELATIVE, .string="wcroot"},
00061 { .val=PATH_CACHEDENVIRON, .string="environment"},
00062 { .val=PATH_FULLENVIRON, .string="full-environment"},
00063 { .string=NULL, }
00064 };
00065
00067 const struct opt___val_str_t opt___log_output_strings[]= {
00068 { .val=LOG__OPT_COLOR, .string="color" },
00069 { .val=LOG__OPT_INDENT, .string="indent" },
00070 { .val=0, .string="normal" },
00071 { .string=NULL, }
00072 };
00073
00077 const struct opt___val_str_t opt___yes_no[]= {
00078 { .val=OPT__YES, .string="yes" },
00079 { .val=OPT__YES, .string="true" },
00080 { .val=OPT__YES, .string="on" },
00081 { .val=OPT__NO, .string="no" },
00082 { .val=OPT__NO, .string="off" },
00083 { .val=OPT__NO, .string="false" },
00084 { .string=NULL, }
00085 };
00086
00087
00088 const struct opt___val_str_t *opt___no_words = opt___yes_no+3;
00089
00090
00093 const struct opt___val_str_t opt___filter_strings[]= {
00094 { .val=FILTER__ALL, .string="any" },
00095 { .val=FS_CHANGED | FS_NEW | FS_REMOVED, .string="text" },
00096 { .val=FS_META_CHANGED, .string="meta" },
00097 { .val=FS_META_MTIME, .string="mtime" },
00098 { .val=FS_META_OWNER, .string="owner" },
00099 { .val=FS_META_UMODE, .string="mode" },
00100 { .val=FS_META_GROUP, .string="group" },
00101 { .val=FS_NEW, .string="new" },
00102 { .val=FS_CHANGED, .string="changed" },
00103 { .val=FS_REMOVED, .string="deleted" },
00104 { .val=FS_REMOVED, .string="removed" },
00105 { .val=FS__CHANGE_MASK, .string="default" },
00106 { .val=FS__CHANGE_MASK, .string="def" },
00107 { .val=0, .string="none" },
00108 { .string=NULL, }
00109 };
00110
00111
00114 const struct opt___val_str_t opt___chcheck_strings[]= {
00115 { .val=0, .string="none" },
00116 { .val=CHCHECK_FILE, .string="file_mtime" },
00117 { .val=CHCHECK_DIRS, .string="dir" },
00118 { .val=CHCHECK_ALLFILES, .string="allfiles" },
00119 { .val=-1, .string="full" },
00120 };
00121
00122
00125 const struct opt___val_str_t opt___verbosity_strings[]= {
00126 { .val=VERBOSITY_VERYQUIET | BITMAP_CLEAR, .string="none" },
00127 { .val=VERBOSITY_VERYQUIET | BITMAP_CLEAR, .string="veryquiet" },
00128 { .val=VERBOSITY_QUIET | BITMAP_CLEAR, .string="quiet" },
00129 { .val=VERBOSITY_SHOWCHG, .string="changes" },
00130 { .val=VERBOSITY_SHOWCHG, .string="status" },
00131 { .val=VERBOSITY_SHOWSIZE, .string="size" },
00132 { .val=VERBOSITY_SHOWNAME, .string="path" },
00133 { .val=VERBOSITY_SHOWNAME, .string="name" },
00134 { .val=VERBOSITY_SHOWTIME, .string="time" },
00135 { .val=VERBOSITY_DEFAULT, .string="default" },
00136 { .val=VERBOSITY_TOP_URL, .string="url" },
00137 { .val=VERBOSITY_ALL_URLS | VERBOSITY_TOP_URL, .string="urls" },
00138 { .val=VERBOSITY_COPYFROM, .string="copyfrom" },
00139 { .val=VERBOSITY_GROUP, .string="group" },
00140 { .val=VERBOSITY_STACKTRACE, .string="stack" },
00141 { .val=VERBOSITY_STACKTRACE, .string="backtrace" },
00142 { .val=VERBOSITY_STACKTRACE, .string="stacktrace" },
00143 { .val=-1, .string="all" },
00144 };
00145
00146
00149 const struct opt___val_str_t opt___delay_strings[]= {
00150 { .val=DELAY_COMMIT, .string="commit" },
00151 { .val=DELAY_UPDATE, .string="update" },
00152 { .val=DELAY_REVERT, .string="revert" },
00153 { .val=DELAY_CHECKOUT, .string="checkout" },
00154 { .val=-1, .string="yes" },
00155 { .val=0, .string="no" },
00156 { .string=NULL, }
00157 };
00158
00159
00162 const struct opt___val_str_t opt___conflict_strings[]= {
00163 { .val=CONFLICT_STOP, .string="stop" },
00164 { .val=CONFLICT_LOCAL, .string="local" },
00165 { .val=CONFLICT_REMOTE, .string="remote" },
00166 { .val=CONFLICT_BOTH, .string="both" },
00167 { .val=CONFLICT_MERGE, .string="merge" },
00168 { .string=NULL, }
00169 };
00170
00171
00172
00175 opt___parse_t opt___string2val;
00176 opt___parse_t opt___strings2bitmap;
00177 opt___parse_t opt___strings2empty_bm;
00178 opt___parse_t opt___store_string;
00179 opt___parse_t opt___store_env_noempty;
00180 opt___parse_t opt___normalized_path;
00181 opt___parse_t opt___parse_warnings;
00182 opt___parse_t opt___atoi;
00183 opt___parse_t opt___debug_buffer;
00192 struct opt__list_t opt__list[OPT__COUNT]=
00193 {
00194 [OPT__PATH] = {
00195 .name="path", .i_val=PATH_PARMRELATIVE,
00196 .parse=opt___string2val, .parm=opt___path_strings,
00197 },
00198 [OPT__LOG_MAXREV] = {
00199 .name="limit", .i_val=0, .parse=opt___atoi,
00200 },
00201 [OPT__LOG_OUTPUT] = {
00202 .name="log_output", .i_val=LOG__OPT_DEFAULT,
00203 .parse=opt___strings2empty_bm, .parm=opt___log_output_strings,
00204 },
00205 [OPT__COLORDIFF] = {
00206 .name="colordiff", .cp_val=NULL, .parse=opt___store_string,
00207 },
00208 [OPT__DIR_SORT] = {
00209 .name="dir_sort", .i_val=OPT__NO,
00210 .parse=opt___string2val, .parm=opt___yes_no,
00211 },
00212 [OPT__STATUS_COLOR] = {
00213 .name="stat_color", .i_val=OPT__NO,
00214 .parse=opt___string2val, .parm=opt___yes_no,
00215 },
00216 [OPT__STOP_ON_CHANGE] = {
00217 .name="stop_change", .i_val=OPT__NO,
00218 .parse=opt___string2val, .parm=opt___yes_no,
00219 },
00220 [OPT__FILTER] = {
00221 .name="filter", .i_val=0,
00222 .parse=opt___strings2bitmap, .parm=opt___filter_strings,
00223 },
00224 [OPT__CHANGECHECK] = {
00225 .name="change_check", .i_val=CHCHECK_FILE,
00226 .parse=opt___strings2bitmap, .parm=opt___chcheck_strings,
00227 },
00228 [OPT__ALL_REMOVED] = {
00229 .name="all_removed", .i_val=OPT__YES,
00230 .parse=opt___string2val, .parm=opt___yes_no,
00231 },
00232 [OPT__VERBOSE] = {
00233 .name="verbose", .i_val=VERBOSITY_DEFAULT,
00234 .parse=opt___strings2bitmap, .parm=opt___verbosity_strings,
00235 },
00236
00237 [OPT__DEBUG_OUTPUT] = {
00238 .name="debug_output", .cp_val=NULL, .parse=opt___store_string,
00239 },
00240 [OPT__DEBUG_BUFFER] = {
00241 .name="debug_buffer", .i_val=0, .parse=opt___debug_buffer,
00242 },
00243 [OPT__GROUP_STATS] = {
00244 .name="group_stats", .i_val=OPT__NO,
00245 .parse=opt___string2val, .parm=opt___yes_no,
00246 },
00247
00248 [OPT__CONFLICT] = {
00249 .name="conflict", .i_val=CONFLICT_MERGE,
00250 .parse=opt___string2val, .parm=opt___conflict_strings,
00251 },
00252 [OPT__MERGE_PRG] = {
00253 .name="merge_prg", .cp_val="diff3", .parse=opt___store_string,
00254 },
00255 [OPT__MERGE_OPT] = {
00256 .name="merge_opt", .cp_val="-m", .parse=opt___store_string,
00257 },
00258 [OPT__DIFF_PRG] = {
00259 .name="diff_prg", .cp_val="diff", .parse=opt___store_string,
00260 },
00261 [OPT__DIFF_OPT] = {
00262 .name="diff_opt", .cp_val="-pu", .parse=opt___store_string,
00263 },
00264 [OPT__DIFF_EXTRA] = {
00265 .name="diff_extra", .cp_val=NULL, .parse=opt___store_string,
00266 },
00267
00268 [OPT__WARNINGS] = {
00269 .name="warning", .parse=opt___parse_warnings,
00270 },
00271 [OPT__SOFTROOT] = {
00272 .name="softroot", .cp_val=NULL, .parse=opt___normalized_path,
00273 },
00274
00275 [OPT__MKDIR_BASE] = {
00276 .name="mkdir_base", .i_val=OPT__NO,
00277 .parse=opt___string2val, .parm=opt___yes_no,
00278 },
00279 [OPT__COMMIT_TO] = {
00280 .name="commit_to", .cp_val=NULL, .parse=opt___store_string,
00281 },
00282 [OPT__AUTHOR] = {
00283 .name="author", .cp_val="", .parse=opt___store_env_noempty,
00284 },
00285
00286
00287
00288 [OPT__WAA_PATH] = {
00289 .name="waa", .parse=opt___store_string,
00290
00291
00292 .cp_val=NULL, .i_val=0,
00293 },
00294 [OPT__CONF_PATH] = {
00295 .name="conf", .parse=opt___store_string,
00296
00297
00298 .cp_val=NULL, .i_val=0,
00299 },
00300 [OPT__CONFIG_DIR] = {
00301 .name="config_dir", .parse=opt___store_string,
00302 .cp_val=NULL, .i_val=0,
00303 },
00304
00305
00306 [OPT__EMPTY_COMMIT] = {
00307 .name="empty_commit", .i_val=OPT__YES,
00308 .parse=opt___string2val, .parm=opt___yes_no,
00309 },
00310 [OPT__EMPTY_MESSAGE] = {
00311 .name="empty_message", .i_val=OPT__YES,
00312 .parse=opt___string2val, .parm=opt___yes_no,
00313 },
00314 [OPT__DELAY] = {
00315 .name="delay", .i_val=OPT__NO,
00316 .parse=opt___strings2empty_bm, .parm=opt___delay_strings,
00317 },
00318 [OPT__COPYFROM_EXP] = {
00319 .name="copyfrom_exp", .i_val=OPT__YES,
00320 .parse=opt___string2val, .parm=opt___yes_no,
00321 },
00322 };
00323
00324
00329 int opt___debug_buffer(struct opt__list_t *ent, char *string,
00330 enum opt__prio_e prio UNUSED)
00331 {
00332 #ifndef ENABLE_DEBUGBUFFER
00333 int status;
00334 STOPIF(EINVAL, "!The debugbuffer option is not available, because\n"
00335 "fmemopen() was not found during compilation.");
00336 ex:
00337 return status;
00338 #else
00339 char *l;
00340 int i;
00341
00342 i=strtol(string, &l, 0);
00343 if (*l) return EINVAL;
00344
00345 if (i)
00346 {
00347 if (i<4)
00348 i=4;
00349 else
00350
00351 i = (i+3) & ~3;
00352
00353 i *= 1024;
00354 }
00355
00356 ent->i_val=i;
00357
00358 return 0;
00359 #endif
00360 }
00361
00362
00364 int opt___atoi(struct opt__list_t *ent, char *string,
00365 enum opt__prio_e prio UNUSED)
00366 {
00367 char *l;
00368
00369 ent->i_val=strtol(string, &l, 0);
00370
00371 if (*l) return EINVAL;
00372 return 0;
00373 }
00374
00375
00377 inline int opt___find_string(const struct opt___val_str_t *list,
00378 const char *string,
00379 int *result)
00380 {
00381 for(; list->string; list++)
00382 {
00383 if (strcmp(string, list->string) == 0)
00384 {
00385 *result = list->val;
00386 return 0;
00387 }
00388 }
00389
00390 return EINVAL;
00391 }
00392
00393
00395 int opt___string2val(struct opt__list_t *ent, char *string,
00396 enum opt__prio_e prio UNUSED)
00397 {
00398 int i;
00399 int status;
00400
00401 STOPIF( opt___find_string(ent->parm, string, &i), NULL);
00402
00403 ent->i_val=i;
00404
00405 ex:
00406 return status;
00407 }
00408
00409
00414 int opt___strings2bitmap(struct opt__list_t *ent, char *string,
00415 enum opt__prio_e prio UNUSED)
00416 {
00417 static const char delim[]=";,:/";
00418 int status;
00419 int val, i;
00420 char buffer[strlen(string)+1];
00421 char *cp;
00422
00423 status=0;
00424
00425 strcpy(buffer, string);
00426 string=buffer;
00427
00428 val=ent->i_val;
00429 DEBUGP("Bitmap starting with 0x%X, from %s", val, string);
00430
00431 while ( (cp=strsep(&string, delim)) )
00432 {
00433
00434 status=opt___find_string(ent->parm, cp, &i);
00435 if (status) goto ex;
00436
00437 if (i == 0 || ((i & BITMAP_CLEAR_MASK) == BITMAP_CLEAR))
00438 val=0;
00439 else val |= i;
00440 }
00441
00442 DEBUGP("New bitmap is 0x%X", val);
00443
00444 ent->i_val=val;
00445
00446 ex:
00447 return status;
00448 }
00449
00450
00454 int opt___strings2empty_bm(struct opt__list_t *ent, char *string,
00455 enum opt__prio_e prio)
00456 {
00457 ent->i_val=0;
00458 return opt___strings2bitmap(ent, string, prio);
00459 }
00460
00461
00463 int opt___store_string(struct opt__list_t *ent, char *string,
00464 enum opt__prio_e prio UNUSED)
00465 {
00466 int status;
00467
00468 ent->i_val=strlen(string);
00469
00470
00471
00472 STOPIF( hlp__strnalloc(ent->i_val, (char**)&ent->cp_val, string), NULL);
00473
00474 ex:
00475 return status;
00476 }
00477
00478
00480 int opt___store_env_noempty(struct opt__list_t *ent, char *string,
00481 enum opt__prio_e prio)
00482 {
00483
00484
00485 if (string[0] == '$') string=getenv(string+1);
00486
00487 if (!string || !*string)
00488 return 0;
00489
00490 return opt___store_string(ent, string, prio);
00491 }
00492
00493
00495 int opt___parse_warnings(struct opt__list_t *ent, char *string,
00496 enum opt__prio_e prio)
00497 {
00498 int status;
00499
00500 STOPIF( wa__split_process(string, prio), NULL);
00501 ex:
00502 return status;
00503 }
00504
00505
00509 int opt__parse_option(enum opt__settings_e which, enum opt__prio_e prio,
00510 char *string)
00511 {
00512 int status;
00513 struct opt__list_t *ent;
00514
00515 status=0;
00516
00517 string=hlp__skip_ws(string);
00518 ent=opt__list+which;
00519 if (ent->prio <= prio)
00520 {
00521 STOPIF( ent->parse(ent, string, prio),
00522 "!Parsing value '%s' for option '%s' failed.",
00523 string, ent->name);
00524 ent->prio=prio;
00525 }
00526
00527 ex:
00528 return status;
00529 }
00530
00531
00536 int opt__parse(char *key, char *value, enum opt__prio_e prio,
00537 int quiet_errors)
00538 {
00539 int status;
00540 int klen;
00541 int i;
00542
00543
00544 status=0;
00545
00546
00547 key=hlp__skip_ws(key);
00548
00549
00550 if (!value)
00551 {
00552 value=strchr(key, '=');
00553 STOPIF_CODE_ERR(!value, EINVAL,
00554 "!Cannot find value in string '%s'.", key);
00555 klen=value-key;
00556 value++;
00557 }
00558 else
00559 klen=strlen(key);
00560
00561 while (klen && isspace(key[klen])) klen--;
00562
00563 value=hlp__skip_ws(value);
00564
00565
00566
00567
00568 for(i=0; i<OPT__COUNT; i++)
00569 {
00570 if (hlp__strncmp_uline_eq_dash(opt__list[i].name, key, klen) == 0 &&
00571 opt__list[i].name[klen] == 0)
00572 {
00573 DEBUGP("parsing %s[%i] = %s", opt__list[i].name, i, value);
00574 STOPIF( opt__parse_option( i, prio, value), NULL);
00575 goto ex;
00576 }
00577 }
00578
00579 status=ENOENT;
00580 if (!quiet_errors)
00581 STOPIF( status, "!Option name '%s' unknown.", key);
00582
00583 ex:
00584 return status;
00585 }
00586
00587
00591 int opt__load_settings(char *path, char *name, enum opt__prio_e prio)
00592 {
00593 int status;
00594 char fn[strlen(path) + 1 + (name ? strlen(name) : 0) + 1];
00595 char *buffer;
00596 FILE *fp;
00597
00598
00599 status=0;
00600 strcpy(fn, path);
00601 if (name)
00602 {
00603 strcat(fn, "/");
00604 strcat(fn, name);
00605 }
00606
00607 DEBUGP("reading settings from %s, with prio %d",
00608 fn, prio);
00609 fp=fopen(fn, "rt");
00610 if (!fp)
00611 {
00612 status=errno;
00613 if (status == ENOENT)
00614 {
00615
00616 status=0;
00617 goto ex;
00618 }
00619 STOPIF( status, "Open file '%s'", fn);
00620 }
00621
00622 hlp__string_from_filep(NULL, NULL, NULL, SFF_RESET_LINENUM);
00623 while (!feof(fp))
00624 {
00625 status=hlp__string_from_filep(fp, &buffer, NULL,
00626 SFF_WHITESPACE | SFF_COMMENT);
00627 if (status == EOF) break;
00628 STOPIF( status, NULL);
00629
00630 if (*buffer == '#') continue;
00631
00632 STOPIF( opt__parse(buffer, NULL, prio, 0),
00633 "In file '%s' on line %u", fn,
00634 hlp__string_from_filep(NULL, NULL, NULL, SFF_GET_LINENUM));
00635 }
00636
00637
00638 status=0;
00639
00640 ex:
00641 if (fp) fclose(fp);
00642
00643 return status;
00644 }
00645
00646
00652 int opt__load_env(char **env)
00653 {
00654 int status;
00655 char *cur;
00656 char buffer[32];
00657 int i;
00658
00659 status=0;
00660
00661 while ( (cur=*(env++)) )
00662 {
00663 if (strncmp(cur, ENV_PREFIX, strlen(ENV_PREFIX)) == 0)
00664 {
00665 DEBUGP("found env %s", cur);
00666 cur += strlen(ENV_PREFIX);
00667
00668 for(i=0; cur[i] != '=' && i<sizeof(buffer)-1; i++)
00669 buffer[i]=tolower(cur[i]);
00670 buffer[i]=0;
00671
00672
00673
00674 if (cur[i] != '=')
00675 {
00676 DEBUGP("rejected - key too long.");
00677 continue;
00678 }
00679
00680 status=opt__parse(buffer, cur+i+1, PRIO_ENV, 1);
00681 if (status == ENOENT)
00682 {
00683 DEBUGP("key not known.");
00684 }
00685 else STOPIF(status, NULL);
00686 }
00687 }
00688
00689 status=0;
00690
00691 ex:
00692 return status;
00693 }
00694
00695
00696 int opt___normalized_path(struct opt__list_t *ent, char *string,
00697 enum opt__prio_e prio)
00698 {
00699 char path[strlen(string)+1];
00700 int p;
00701
00702 hlp__pathcopy(path, &p, string, NULL);
00703
00704 p--;
00705 while (p>0 && path[p] == PATH_SEPARATOR) path[p--]=0;
00706
00707 if (p > 0)
00708 return opt___store_string(ent, path, prio);
00709 else
00710 return EINVAL;
00711 }
00712
00715 int opt__help(struct estat *root, int argc, char *argv[])
00716 {
00717 return EBUSY;
00718 }
00719
00723 int opt__doesnt_say_off(const char *string)
00724 {
00725 int i;
00726
00727 i=OPT__YES;
00728 if (opt___find_string(opt___yes_no+3, string, &i)) return 1;
00729 return i;
00730 }
00731
00732
00737 char *opt__variable_from_option(enum opt__settings_e which)
00738 {
00739 static char buffer[ strlen(ENV_PREFIX) +
00740 sizeof(opt__list[0].name) + 1] = ENV_PREFIX;
00741 char * const target=buffer+strlen(ENV_PREFIX);
00742 int i;
00743
00744 i=0;
00745 while ( (target[i] = toupper(opt__list[which].name[i])) )
00746 i++;
00747
00748 return buffer;
00749 }