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
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 char *l;
00333 int i;
00334
00335 i=strtol(string, &l, 0);
00336 if (*l) return EINVAL;
00337
00338 if (i)
00339 {
00340 if (i<4)
00341 i=4;
00342 else
00343
00344 i = (i+3) & ~3;
00345
00346 i *= 1024;
00347 }
00348
00349 ent->i_val=i;
00350
00351 return 0;
00352 }
00353
00354
00356 int opt___atoi(struct opt__list_t *ent, char *string,
00357 enum opt__prio_e prio UNUSED)
00358 {
00359 char *l;
00360
00361 ent->i_val=strtol(string, &l, 0);
00362
00363 if (*l) return EINVAL;
00364 return 0;
00365 }
00366
00367
00369 inline int opt___find_string(const struct opt___val_str_t *list,
00370 const char *string,
00371 int *result)
00372 {
00373 for(; list->string; list++)
00374 {
00375 if (strcmp(string, list->string) == 0)
00376 {
00377 *result = list->val;
00378 return 0;
00379 }
00380 }
00381
00382 return EINVAL;
00383 }
00384
00385
00387 int opt___string2val(struct opt__list_t *ent, char *string,
00388 enum opt__prio_e prio UNUSED)
00389 {
00390 int i;
00391 int status;
00392
00393 STOPIF( opt___find_string(ent->parm, string, &i), NULL);
00394
00395 ent->i_val=i;
00396
00397 ex:
00398 return status;
00399 }
00400
00401
00406 int opt___strings2bitmap(struct opt__list_t *ent, char *string,
00407 enum opt__prio_e prio UNUSED)
00408 {
00409 static const char delim[]=";,:/";
00410 int status;
00411 int val, i;
00412 char buffer[strlen(string)+1];
00413 char *cp;
00414
00415 status=0;
00416
00417 strcpy(buffer, string);
00418 string=buffer;
00419
00420 val=ent->i_val;
00421 DEBUGP("Bitmap starting with 0x%X, from %s", val, string);
00422
00423 while ( (cp=strsep(&string, delim)) )
00424 {
00425
00426 status=opt___find_string(ent->parm, cp, &i);
00427 if (status) goto ex;
00428
00429 if (i == 0 || ((i & BITMAP_CLEAR_MASK) == BITMAP_CLEAR))
00430 val=0;
00431 else val |= i;
00432 }
00433
00434 DEBUGP("New bitmap is 0x%X", val);
00435
00436 ent->i_val=val;
00437
00438 ex:
00439 return status;
00440 }
00441
00442
00446 int opt___strings2empty_bm(struct opt__list_t *ent, char *string,
00447 enum opt__prio_e prio)
00448 {
00449 ent->i_val=0;
00450 return opt___strings2bitmap(ent, string, prio);
00451 }
00452
00453
00455 int opt___store_string(struct opt__list_t *ent, char *string,
00456 enum opt__prio_e prio UNUSED)
00457 {
00458 int status;
00459
00460 ent->i_val=strlen(string);
00461
00462
00463
00464 STOPIF( hlp__strnalloc(ent->i_val, (char**)&ent->cp_val, string), NULL);
00465
00466 ex:
00467 return status;
00468 }
00469
00470
00472 int opt___store_env_noempty(struct opt__list_t *ent, char *string,
00473 enum opt__prio_e prio)
00474 {
00475
00476
00477 if (string[0] == '$') string=getenv(string+1);
00478
00479 if (!string || !*string)
00480 return 0;
00481
00482 return opt___store_string(ent, string, prio);
00483 }
00484
00485
00487 int opt___parse_warnings(struct opt__list_t *ent, char *string,
00488 enum opt__prio_e prio)
00489 {
00490 int status;
00491
00492 STOPIF( wa__split_process(string, prio), NULL);
00493 ex:
00494 return status;
00495 }
00496
00497
00501 int opt__parse_option(enum opt__settings_e which, enum opt__prio_e prio,
00502 char *string)
00503 {
00504 int status;
00505 struct opt__list_t *ent;
00506
00507 status=0;
00508
00509 string=hlp__skip_ws(string);
00510 ent=opt__list+which;
00511 if (ent->prio <= prio)
00512 {
00513 STOPIF( ent->parse(ent, string, prio),
00514 "!Parsing value '%s' for option '%s' failed.",
00515 string, ent->name);
00516 ent->prio=prio;
00517 }
00518
00519 ex:
00520 return status;
00521 }
00522
00523
00528 int opt__parse(char *key, char *value, enum opt__prio_e prio,
00529 int quiet_errors)
00530 {
00531 int status;
00532 int klen;
00533 int i;
00534
00535
00536 status=0;
00537
00538
00539 key=hlp__skip_ws(key);
00540
00541
00542 if (!value)
00543 {
00544 value=strchr(key, '=');
00545 STOPIF_CODE_ERR(!value, EINVAL,
00546 "!Cannot find value in string '%s'.", key);
00547 klen=value-key;
00548 value++;
00549 }
00550 else
00551 klen=strlen(key);
00552
00553 while (klen && isspace(key[klen])) klen--;
00554
00555 value=hlp__skip_ws(value);
00556
00557
00558
00559
00560 for(i=0; i<OPT__COUNT; i++)
00561 {
00562 if (hlp__strncmp_uline_eq_dash(opt__list[i].name, key, klen) == 0 &&
00563 opt__list[i].name[klen] == 0)
00564 {
00565 DEBUGP("parsing %s[%i] = %s", opt__list[i].name, i, value);
00566 STOPIF( opt__parse_option( i, prio, value), NULL);
00567 goto ex;
00568 }
00569 }
00570
00571 status=ENOENT;
00572 if (!quiet_errors)
00573 STOPIF( status, "!Option name '%s' unknown.", key);
00574
00575 ex:
00576 return status;
00577 }
00578
00579
00583 int opt__load_settings(char *path, char *name, enum opt__prio_e prio)
00584 {
00585 int status;
00586 char fn[strlen(path) + 1 + (name ? strlen(name) : 0) + 1];
00587 char *buffer;
00588 FILE *fp;
00589
00590
00591 status=0;
00592 strcpy(fn, path);
00593 if (name)
00594 {
00595 strcat(fn, "/");
00596 strcat(fn, name);
00597 }
00598
00599 DEBUGP("reading settings from %s, with prio %d",
00600 fn, prio);
00601 fp=fopen(fn, "rt");
00602 if (!fp)
00603 {
00604 status=errno;
00605 if (status == ENOENT)
00606 {
00607
00608 status=0;
00609 goto ex;
00610 }
00611 STOPIF( status, "Open file '%s'", fn);
00612 }
00613
00614 hlp__string_from_filep(NULL, NULL, NULL, SFF_RESET_LINENUM);
00615 while (!feof(fp))
00616 {
00617 status=hlp__string_from_filep(fp, &buffer, NULL,
00618 SFF_WHITESPACE | SFF_COMMENT);
00619 if (status == EOF) break;
00620 STOPIF( status, NULL);
00621
00622 if (*buffer == '#') continue;
00623
00624 STOPIF( opt__parse(buffer, NULL, prio, 0),
00625 "In file '%s' on line %u", fn,
00626 hlp__string_from_filep(NULL, NULL, NULL, SFF_GET_LINENUM));
00627 }
00628
00629
00630 status=0;
00631
00632 ex:
00633 if (fp) fclose(fp);
00634
00635 return status;
00636 }
00637
00638
00644 int opt__load_env(char **env)
00645 {
00646 int status;
00647 char *cur;
00648 char buffer[32];
00649 int i;
00650
00651 status=0;
00652
00653 while ( (cur=*(env++)) )
00654 {
00655 if (strncmp(cur, ENV_PREFIX, strlen(ENV_PREFIX)) == 0)
00656 {
00657 DEBUGP("found env %s", cur);
00658 cur += strlen(ENV_PREFIX);
00659
00660 for(i=0; cur[i] != '=' && i<sizeof(buffer)-1; i++)
00661 buffer[i]=tolower(cur[i]);
00662 buffer[i]=0;
00663
00664
00665
00666 if (cur[i] != '=')
00667 {
00668 DEBUGP("rejected - key too long.");
00669 continue;
00670 }
00671
00672 status=opt__parse(buffer, cur+i+1, PRIO_ENV, 1);
00673 if (status == ENOENT)
00674 {
00675 DEBUGP("key not known.");
00676 }
00677 else STOPIF(status, NULL);
00678 }
00679 }
00680
00681 status=0;
00682
00683 ex:
00684 return status;
00685 }
00686
00687
00688 int opt___normalized_path(struct opt__list_t *ent, char *string,
00689 enum opt__prio_e prio)
00690 {
00691 char path[strlen(string)+1];
00692 int p;
00693
00694 hlp__pathcopy(path, &p, string, NULL);
00695
00696 p--;
00697 while (p>0 && path[p] == PATH_SEPARATOR) path[p--]=0;
00698
00699 if (p > 0)
00700 return opt___store_string(ent, path, prio);
00701 else
00702 return EINVAL;
00703 }
00704
00707 int opt__help(struct estat *root, int argc, char *argv[])
00708 {
00709 return EBUSY;
00710 }
00711
00715 int opt__doesnt_say_off(const char *string)
00716 {
00717 int i;
00718
00719 i=OPT__YES;
00720 if (opt___find_string(opt___yes_no+3, string, &i)) return 1;
00721 return i;
00722 }
00723
00724
00729 char *opt__variable_from_option(enum opt__settings_e which)
00730 {
00731 static char buffer[ strlen(ENV_PREFIX) +
00732 sizeof(opt__list[0].name) + 1] = ENV_PREFIX;
00733 char * const target=buffer+strlen(ENV_PREFIX);
00734 int i;
00735
00736 i=0;
00737 while ( (target[i] = toupper(opt__list[which].name[i])) )
00738 i++;
00739
00740 return buffer;
00741 }