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 }