00001
00002
00003
00004
00005
00006
00007
00008
00009 #include <stdlib.h>
00010 #include <unistd.h>
00011 #include <ctype.h>
00012 #include <pcre.h>
00013 #include <sys/mman.h>
00014
00015
00016 #include "global.h"
00017 #include "interface.h"
00018 #include "waa.h"
00019 #include "est_ops.h"
00020 #include "helper.h"
00021 #include "warnings.h"
00022 #include "direnum.h"
00023 #include "ignore.h"
00024 #include "url.h"
00025
00026
00031
00032
00033
00034
00035
00036
00037
00606 apr_hash_t *ign___groups=NULL;
00607
00611 int ign__max_group_name_len=6;
00612
00613
00614 #define RESERVE_IGNORE_ENTRIES (4)
00615
00617 static const char ign_header_str[] = "%u",
00618 ign__group_take[]="take",
00619 ign__group_ign[]="ignore";
00620
00621 const char ign___parm_delimiter=',';
00622
00624 int max_ignore_entries=0;
00626 int used_ignore_entries=0;
00627
00629 static struct ignore_t *ignore_list=NULL;
00630
00632 static char *memory;
00633
00634
00637 static const char
00638 pcre_prefix[]="PCRE:",
00639 dev_prefix[]="DEVICE:",
00640 inode_prefix[]="INODE:",
00641 norm_prefix[]= { '.', PATH_SEPARATOR, 0 },
00642
00643 wildcard_prefix[]= { PATH_SEPARATOR, '*', '*', 0 },
00644
00645 abs_shell_prefix[]= { PATH_SEPARATOR, 0 };
00651 int ign___translate_bracketed_expr(char *end_of_buffer,
00652 char **src, char **dest)
00653 {
00654 int status = 0;
00655 int pos_in_bracket_expr = -1;
00656 int backslashed = 0;
00657
00658
00659 STOPIF(**src != '[',
00660 "invalid argument, **src does not point to "
00661 "start of bracket expression");
00662
00663 do
00664 {
00665 if (backslashed)
00666 {
00667
00668 *((*dest)++) = *((*src)++);
00669 backslashed = 0;
00670
00671 }
00672 else if ( pos_in_bracket_expr == 0 &&
00673 (**src == '!' || **src == '^') )
00674 {
00675 *((*dest)++) = '^';
00676 ++(*src);
00677
00678
00679
00680 }
00681 else
00682 {
00683 if (**src == ']' && pos_in_bracket_expr > 0)
00684 {
00685
00686
00687 pos_in_bracket_expr = -1;
00688 }
00689 else
00690 {
00691
00692 ++pos_in_bracket_expr;
00693 }
00694
00695
00696 backslashed = (**src == '\\');
00697
00698 *((*dest)++) = *((*src)++);
00699 }
00700
00701
00702
00703
00704
00705 STOPIF_CODE_ERR( end_of_buffer - *dest < 5, ENOSPC,
00706 "not enough space in buffer");
00707 }
00708 while(**src && pos_in_bracket_expr >= 0);
00709
00710
00711 ex:
00712 return status;
00713 }
00714
00715
00718 int ign__compile_pattern(struct ignore_t *ignore)
00719 {
00720 const char *err;
00721 int offset;
00722 int len;
00723 char *buffer;
00724 char *src, *dest;
00725 int status;
00726 int backslashed;
00727
00728
00729 status=0;
00730 if (ignore->type == PT_PCRE)
00731 dest=ignore->compare_string;
00732 else if (ignore->type == PT_SHELL ||
00733 ignore->type == PT_SHELL_ABS)
00734 {
00735
00736 len=strlen(ignore->compare_string)*5+16;
00737 STOPIF( hlp__alloc( &buffer, len), NULL);
00738
00739 dest=buffer;
00740 src=ignore->compare_string;
00741
00742 if (ignore->type == PT_SHELL_ABS)
00743 {
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756 if (strncmp(src, wc_path, wc_path_len) == 0)
00757 {
00758
00759 src += 1+ (wc_path_len == 1 ? 0 : wc_path_len);
00760 }
00761 else if (strncmp(src, wildcard_prefix, strlen(wildcard_prefix)) == 0)
00762 {
00763
00764
00765 src++;
00766 }
00767 else
00768 STOPIF( wa__warn(WRN__IGNPAT_WCBASE, EINVAL,
00769 "The absolute shell pattern\n"
00770 " \"%s\"\n"
00771 "does neither have the working copy base path\n"
00772 " \"%s\"\n"
00773 "nor a wildcard path (like \"%s\") at the beginning;\n"
00774 "maybe you want a wc-relative pattern, "
00775 "starting with \"%s\"?",
00776 src, wc_path, wildcard_prefix, norm_prefix), NULL);
00777
00778
00779
00780
00781
00782
00783
00784 strncpy(dest, norm_prefix, strlen(norm_prefix));
00785 dest+=strlen(norm_prefix);
00786 }
00787
00788 backslashed = 0;
00789 do
00790 {
00791 if (backslashed)
00792 {
00793
00794 *(dest++) = *(src++);
00795 backslashed = 0;
00796 }
00797 else
00798 {
00799 switch(*src)
00800 {
00801 case '*':
00802 if (src[1] == '*')
00803 {
00804 if (dest[-1] == PATH_SEPARATOR && src[2] == PATH_SEPARATOR)
00805 {
00806
00807
00808 *(dest++) = '(';
00809 *(dest++) = '.';
00810 *(dest++) = '*';
00811 *(dest++) = PATH_SEPARATOR;
00812 *(dest++) = ')';
00813 *(dest++) = '?';
00814
00815 src+=3;
00816 }
00817 else
00818 {
00819
00820
00821 *(dest++) = '.';
00822 *(dest++) = '*';
00823 while (*src == '*') src++;
00824 }
00825 }
00826 else
00827 {
00828
00829 *(dest++) = '[';
00830 *(dest++) = '^';
00831 *(dest++) = PATH_SEPARATOR;
00832 *(dest++) = ']';
00833 *(dest++) = '*';
00834 src++;
00835 }
00836 break;
00837 case '?':
00838 *(dest++) = '.';
00839 src++;
00840 break;
00841 case '[':
00842
00843 STOPIF(ign___translate_bracketed_expr(buffer + len, &src, &dest),
00844 "processing a bracket expression failed");
00845 break;
00846 case '0' ... '9':
00847 case 'a' ... 'z':
00848 case 'A' ... 'Z':
00849
00850
00851
00852
00853 case '/':
00854 case '-':
00855 *(dest++) = *(src++);
00856 break;
00857 case '\\':
00858 backslashed = 1;
00859 *(dest++) = *(src++);
00860 break;
00861
00862
00863 case '.':
00864 default:
00865 *(dest++) = '\\';
00866 *(dest++) = *(src++);
00867 break;
00868 }
00869 }
00870
00871
00872
00873
00874
00875
00876 STOPIF_CODE_ERR( buffer+len - dest < 6+5+1+6, ENOSPC,
00877 "not enough space in buffer");
00878 } while (*src);
00879
00880 if (src != ignore->compare_string)
00881 {
00882 *(dest++) = '$';
00883
00884
00885 if(src[-1] == PATH_SEPARATOR)
00886 {
00887
00888
00889
00890 dest[-2] = '(';
00891 *(dest++) = '|';
00892 *(dest++) = PATH_SEPARATOR;
00893 *(dest++) = ')';
00894 }
00895 }
00896
00897 *dest=0;
00898
00899 STOPIF( hlp__realloc( &buffer, dest-buffer+2), NULL);
00900 ignore->compare_string=buffer;
00901 dest=buffer;
00902 }
00903 else
00904 {
00905 BUG("unknown pattern type %d", ignore->type);
00906
00907 dest=NULL;
00908 }
00909
00910 DEBUGP("compiled \"%s\"", ignore->pattern);
00911 DEBUGP(" into \"%s\"", ignore->compare_string);
00912
00913
00914 ignore->compiled = pcre_compile(dest,
00915 PCRE_DOTALL | PCRE_NO_AUTO_CAPTURE | PCRE_UNGREEDY | PCRE_ANCHORED |
00916 (ignore->is_icase ? PCRE_CASELESS : 0),
00917 &err, &offset, NULL);
00918
00919 STOPIF_CODE_ERR( !ignore->compiled, EINVAL,
00920 "pattern \"%s\" (from \"%s\") not valid; error %s at offset %d.",
00921 dest, ignore->pattern, err, offset);
00922
00923
00924
00925
00926 ignore->extra = pcre_study(ignore->compiled, 0, &err);
00927 STOPIF_CODE_ERR( err, EINVAL,
00928 "pattern \"%s\" not studied; error %s.",
00929 ignore->pattern, err);
00930
00931 ex:
00932 return status;
00933 }
00934
00935
00938 int ign___init_pattern_into(char *pattern, char *end, struct ignore_t *ignore)
00939 {
00940 int status, stop;
00941 int and_value, cmp_value, speclen;
00942 char *cp, *eo_word, *param, *eo_parm;
00943 int data_seen, pattern_len;
00944
00945
00946 status=0;
00947 pattern_len=strlen(pattern);
00948 cp=pattern+pattern_len;
00949 if (!end || end>cp) end=cp;
00950
00951
00952
00953 while (isspace(*pattern))
00954 {
00955 pattern++;
00956 STOPIF_CODE_ERR( pattern>=end, EINVAL, "pattern has no pattern");
00957 }
00958
00959
00960 data_seen=0;
00961 int have_now(int cur, char *err)
00962 {
00963 int status;
00964
00965 status=0;
00966 STOPIF_CODE_ERR(data_seen & cur, EINVAL,
00967 "!The pattern \"%s\" includes more than a single %s specification.",
00968 ignore->pattern, err);
00969 data_seen |= cur;
00970 ex:
00971 return status;
00972 }
00973
00974
00975
00976
00977 eo_parm=NULL;
00978
00979 memset(ignore, 0, sizeof(*ignore));
00980 ignore->pattern = pattern;
00981 while (*pattern)
00982 {
00983 eo_word=pattern;
00984 while (isalpha(*eo_word)) eo_word++;
00985 speclen=eo_word-pattern;
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004 if (speclen == 0)
01005 goto shell_pattern;
01006
01007 if (*eo_word == ':')
01008 {
01009
01010 param= eo_word + 1;
01011 }
01012 else
01013 param=NULL;
01014
01015 eo_parm=strchr(eo_word, ign___parm_delimiter);
01016 if (!eo_parm) eo_parm=eo_word+strlen(eo_word);
01017
01018
01019
01020 if (strncmp(ign__group_take, pattern, speclen)==0)
01021 {
01022 STOPIF( have_now(HAVE_GROUP, "group"), NULL);
01023 ignore->group_name=ign__group_take;
01024 }
01025 else if (strncmp(ign__group_ign, pattern, speclen)==0)
01026 {
01027 STOPIF( have_now(HAVE_GROUP, "group"), NULL);
01028 ignore->group_name=ign__group_ign;
01029 }
01030 else if (strncmp("group:", pattern, speclen)==0)
01031 {
01032 STOPIF( have_now(HAVE_GROUP, "group"), NULL);
01033 STOPIF_CODE_ERR( !param || eo_parm==param, EINVAL,
01034 "!Missing group name in pattern \"%s\".",
01035 ignore->pattern);
01036
01037 speclen=eo_parm-param;
01038 STOPIF( hlp__strnalloc( speclen,
01039 (char**)&ignore->group_name, param), NULL);
01040
01041 if (speclen > ign__max_group_name_len)
01042 ign__max_group_name_len=speclen;
01043
01044
01045 while (param != eo_parm)
01046 {
01047 STOPIF_CODE_ERR( !isalnum(*param), EINVAL,
01048 "!The group name may (currently) "
01049 "only use alphanumeric characters;\n"
01050 "so \"%s\" is invalid.", ignore->pattern);
01051 param++;
01052 }
01053 }
01054 else if (strncmp("dironly", pattern, speclen)==0)
01055 {
01056 ignore->dir_only=1;
01057 STOPIF( have_now(HAVE_DIR, "dironly"), NULL);
01058 data_seen |= HAVE_PATTERN_SUBST;
01059 }
01060 else if (strncmp("nocase", pattern, speclen)==0 ||
01061 strncmp("insens", pattern, speclen)==0)
01062 {
01063 ignore->is_icase=1;
01064 STOPIF( have_now(HAVE_CASE, "case ignore"), NULL);
01065 }
01066 else if (strncmp("mode:", pattern, speclen)==0)
01067 {
01068 STOPIF( have_now(HAVE_MODE, "mode"), NULL);
01069 STOPIF_CODE_ERR( !param, EINVAL,
01070 "!Invalid mode specification in \"%s\".", ignore->pattern);
01071
01072 STOPIF_CODE_ERR( sscanf(param, "%o:%o%n",
01073 &and_value, &cmp_value, &stop) != 2, EINVAL,
01074 "!Ignore pattern \"%s\" has a bad mode specification;\n"
01075 "the expected syntax is \"mode:<AND>:<CMP>\".",
01076 ignore->pattern);
01077
01078 STOPIF_CODE_ERR( param+stop != eo_parm, EINVAL,
01079 "!Garbage after mode specification in \"%s\".",
01080 ignore->pattern);
01081
01082 STOPIF_CODE_ERR( and_value>07777 || cmp_value>0777 ||
01083 (cmp_value & ~and_value), EINVAL,
01084 "!Mode matching specification in \"%s\" has invalid numbers.",
01085 ignore->pattern);
01086
01087 ignore->mode_match_and=and_value;
01088 ignore->mode_match_cmp=cmp_value;
01089 data_seen |= HAVE_PATTERN_SUBST;
01090 stop=0;
01091 }
01092
01093
01094
01095 else if (strncmp(dev_prefix, pattern, strlen(dev_prefix)) == 0)
01096 {
01097 ignore->type=PT_DEVICE;
01098 ignore->compare_string = pattern;
01099 ignore->compare = PAT_DEV__UNSPECIFIED;
01100 pattern+=strlen(dev_prefix);
01101
01102 stop=0;
01103 while (!stop)
01104 {
01105 switch (*pattern)
01106 {
01107 case '<':
01108 ignore->compare |= PAT_DEV__LESS;
01109 break;
01110 case '=':
01111 ignore->compare |= PAT_DEV__EQUAL;
01112 break;
01113 case '>':
01114 ignore->compare |= PAT_DEV__GREATER;
01115 break;
01116 default:
01117 stop=1;
01118 break;
01119 }
01120 if (!stop) pattern++;
01121 }
01122
01123 if (ignore->compare == PAT_DEV__UNSPECIFIED)
01124 ignore->compare = PAT_DEV__EQUAL;
01125
01126 ignore->major=strtoul(pattern, &cp, 0);
01127 DEBUGP("device pattern: major=%d, left=%s", ignore->major, cp);
01128 STOPIF_CODE_ERR( cp == pattern, EINVAL,
01129 "!No major number found in \"%s\"", ignore->pattern);
01130
01131
01132 if (*cp)
01133 {
01134 STOPIF_CODE_ERR( *(cp++) != ':', EINVAL,
01135 "!Expected ':' between major and minor number in %s",
01136 ignore->pattern);
01137
01138 pattern=cp;
01139 ignore->minor=strtoul(pattern, &cp, 0);
01140 STOPIF_CODE_ERR( cp == pattern, EINVAL,
01141 "!No minor number in \"%s\"", ignore->pattern);
01142
01143 STOPIF_CODE_ERR( *cp, EINVAL,
01144 "!Garbage after minor number in \"%s\"",
01145 ignore->pattern);
01146 ignore->has_minor=1;
01147 }
01148 else
01149 {
01150 ignore->minor=PAT_DEV__UNSPECIFIED;
01151 ignore->has_minor=0;
01152 }
01153 status=0;
01154 data_seen |= HAVE_PATTERN;
01155 }
01156 else if (strncmp(inode_prefix, pattern, strlen(inode_prefix)) == 0)
01157 {
01158 #ifdef DEVICE_NODES_DISABLED
01159 DEVICE_NODES_DISABLED();
01160 #else
01161 int mj, mn;
01162
01163 ignore->type=PT_INODE;
01164 ignore->compare_string = pattern;
01165 pattern+=strlen(inode_prefix);
01166
01167 mj=strtoul(pattern, &cp, 0);
01168 STOPIF_CODE_ERR( cp == pattern || *(cp++) != ':', EINVAL,
01169 "!No major number in %s?", ignore->pattern);
01170
01171 pattern=cp;
01172 mn=strtoul(pattern, &cp, 0);
01173 STOPIF_CODE_ERR( cp == pattern || *(cp++) != ':', EINVAL,
01174 "!No minor number in %s?", ignore->pattern);
01175
01176 ignore->dev=MKDEV(mj, mn);
01177
01178 pattern=cp;
01179 ignore->inode=strtoull(pattern, &cp, 0);
01180 STOPIF_CODE_ERR( cp == pattern || *cp!= 0, EINVAL,
01181 "!Garbage after inode in %s?", ignore->pattern);
01182
01183 #endif
01184 status=0;
01185 data_seen |= HAVE_PATTERN;
01186 }
01187 else
01188 {
01189 shell_pattern:
01190 if (strncmp(pattern, norm_prefix, strlen(norm_prefix)) == 0)
01191 {
01192 ignore->type=PT_SHELL;
01193 DEBUGP("shell pattern matching");
01194
01195 }
01196 else if (strncmp(pattern, abs_shell_prefix, strlen(abs_shell_prefix)) == 0)
01197 {
01198 ignore->type=PT_SHELL_ABS;
01199 DEBUGP("absolute shell pattern matching");
01200 }
01201 else if (strncmp(pcre_prefix, pattern, strlen(pcre_prefix)) == 0)
01202 {
01203 ignore->type=PT_PCRE;
01204 pattern += strlen(pcre_prefix);
01205 DEBUGP("pcre matching");
01206 }
01207 else
01208 STOPIF_CODE_ERR(1, EINVAL,
01209 "!Expected a shell pattern, starting with \"%s\" or \"%s\"!",
01210 norm_prefix, abs_shell_prefix);
01211
01212
01213 STOPIF_CODE_ERR( strlen(pattern)<3, EINVAL,
01214 "!Pattern \"%s\" too short!", ignore->pattern);
01215
01216
01217 ignore->compare_string = pattern;
01218 status=ign__compile_pattern(ignore);
01219 STOPIF(status, "compile returned an error");
01220 data_seen |= HAVE_PATTERN;
01221 }
01222
01223
01224 if (data_seen & HAVE_PATTERN) break;
01225
01226
01227
01228 pattern=eo_parm;
01229
01230 while (*pattern == ign___parm_delimiter) pattern++;
01231
01232 DEBUGP("now at %d == %p; end=%p", *pattern, pattern, end);
01233 STOPIF_CODE_ERR( pattern>end || (pattern == end && *end!=0),
01234 EINVAL, "pattern not \\0-terminated");
01235 }
01236
01237
01238
01239 STOPIF_CODE_ERR(!(data_seen & (HAVE_PATTERN | HAVE_PATTERN_SUBST)),
01240 EINVAL, "!Pattern \"%s\" ends prematurely", ignore->pattern);
01241
01242
01243
01244
01245 if (!ignore->group_name && (action->i_val & HAVE_GROUP))
01246 {
01247 ignore->group_name=ign__group_ign;
01248 eo_word="group:";
01249
01250 STOPIF( hlp__strmnalloc(
01251 strlen(eo_word) + strlen(ign__group_ign) + 1 +
01252 pattern_len + 1,
01253 &ignore->pattern,
01254 eo_word, ign__group_ign, ",",
01255 ignore->pattern, NULL), NULL);
01256 }
01257
01258 STOPIF_CODE_ERR( !ignore->group_name || !*ignore->group_name, EINVAL,
01259 "!No group name given in \"%s\".", ignore->pattern);
01260
01261
01262 DEBUGP("pattern: %scase, group \"%s\", %s, mode&0%o==0%o",
01263 ignore->is_icase ? "I" : "",
01264 ignore->group_name,
01265 ignore->dir_only ? "dironly" : "all entries",
01266 ignore->mode_match_and, ignore->mode_match_cmp);
01267
01268 if (!*pattern)
01269 {
01270
01271
01272 ignore->type=PT_SHELL;
01273 }
01274
01275 ex:
01276 return status;
01277 }
01278
01279
01282 int ign__load_list(char *dir)
01283 {
01284 int status, fh, l;
01285 struct stat st;
01286 char *cp,*cp2;
01287 int count;
01288
01289
01290 fh=-1;
01291 status=waa__open_byext(dir, WAA__IGNORE_EXT, WAA__READ, &fh);
01292 if (status == ENOENT)
01293 {
01294 DEBUGP("no ignore list found");
01295 status=0;
01296 goto ex;
01297 }
01298 else STOPIF_CODE_ERR(status, status, "reading ignore list");
01299
01300 STOPIF_CODE_ERR( fstat(fh, &st), errno, NULL);
01301
01302 memory=mmap(NULL, st.st_size,
01303 PROT_READ, MAP_SHARED,
01304 fh, 0);
01305
01306
01307 status=errno;
01308 l=close(fh);
01309 STOPIF_CODE_ERR( memory == MAP_FAILED, status, "mmap failed");
01310 STOPIF_CODE_ERR( l, errno, "close() failed");
01311
01312
01313
01314 cp=memchr(memory, '\n', st.st_size);
01315 if (!cp)
01316 {
01317
01318
01319
01320 DEBUGP("Grouping list header is invalid.");
01321 status=0;
01322 goto ex;
01323 }
01324
01325 status=sscanf(memory, ign_header_str,
01326 &count);
01327 STOPIF_CODE_ERR( status != 1, EINVAL,
01328 "grouping header is invalid");
01329
01330 cp++;
01331 STOPIF( ign__new_pattern(count, NULL, NULL, 0, 0), NULL );
01332
01333
01334
01335 cp2=memory+st.st_size;
01336 for(l=0; l<count; l++)
01337 {
01338 STOPIF( ign__new_pattern(1, &cp, cp2, 1, PATTERN_POSITION_END), NULL);
01339
01340
01341 cp+=strlen(cp)+1;
01342 if (cp >= cp2) break;
01343 }
01344
01345 if (l != count)
01346 DEBUGP("Ignore-list defect - header count (%u) bigger than actual number"
01347 "of patterns (%u)",
01348 count, l);
01349 if (cp >= cp2)
01350 DEBUGP("Ignore-list defect - garbage after counted patterns");
01351 l=used_ignore_entries;
01352
01353 status=0;
01354
01355 ex:
01356
01357 if (status)
01358 used_ignore_entries=0;
01359
01360 return status;
01361 }
01362
01363
01368 inline int ign___compare_dev(struct sstat_t *st, struct ignore_t *ign)
01369 {
01370 #ifdef DEVICE_NODES_DISABLED
01371 DEVICE_NODES_DISABLED();
01372 #else
01373 int mj, mn;
01374
01375 mj=(int)MAJOR(st->dev);
01376 mn=(int)MINOR(st->dev);
01377
01378 if (mj > ign->major) return +2;
01379 if (mj < ign->major) return -2;
01380
01381 if (!ign->has_minor) return 0;
01382 if (mn > ign->minor) return +1;
01383 if (mn < ign->minor) return -1;
01384 #endif
01385
01386 return 0;
01387 }
01388
01389
01390 int ign___new_group(struct ignore_t *ign, struct grouping_t **result)
01391 {
01392 int status;
01393 int gn_len;
01394 struct grouping_t *group;
01395
01396
01397 status=0;
01398 DEBUGP("making group %s", ign->group_name);
01399 gn_len=strlen(ign->group_name);
01400
01401 if (ign___groups)
01402 group=apr_hash_get(ign___groups, ign->group_name, gn_len);
01403 else
01404 {
01405 ign___groups=apr_hash_make(global_pool);
01406 group=NULL;
01407 }
01408
01409 if (group)
01410 {
01411
01412 }
01413 else
01414 {
01415 STOPIF( hlp__calloc(&group, 1, sizeof(*group)), NULL);
01416 apr_hash_set(ign___groups, ign->group_name, gn_len, group);
01417 }
01418
01419 *result=group;
01420 ign->group_def=group;
01421
01422 ex:
01423 return status;
01424 }
01425
01426
01429 int ign___load_group(struct ignore_t *ign)
01430 {
01431 int status;
01432 struct grouping_t *group;
01433 char *copy, *fn, *eos, *conf_start, *input;
01434 FILE *g_in;
01435 int is_ok, gn_len;
01436 static const char ps[]= { PATH_SEPARATOR, 0 };
01437 char *cause;
01438 svn_string_t *str;
01439
01440
01441 BUG_ON(ign->group_def, "already loaded");
01442
01443 status=0;
01444 copy=NULL;
01445 g_in=NULL;
01446 gn_len=strlen(ign->group_name);
01447
01448 STOPIF( ign___new_group(ign, &group), NULL);
01449
01450
01451 if (strcmp(ign->group_name, ign__group_take) == 0)
01452 is_ok=1;
01453 else if (strcmp(ign->group_name, ign__group_ign) == 0)
01454 is_ok=2;
01455 else
01456 is_ok=0;
01457
01458
01459
01460
01461
01462
01463 STOPIF( waa__get_waa_directory( wc_path,
01464 &fn, &eos, &conf_start, GWD_CONF), NULL);
01465
01466
01467 STOPIF( hlp__strmnalloc(waa_tmp_path_len +
01468 strlen(CONFIGDIR_GROUP) + 1 + gn_len + 1, ©,
01469 fn, CONFIGDIR_GROUP, ps, ign->group_name, NULL), NULL);
01470
01471
01472 DEBUGP("try specific group: %s", copy);
01473 g_in=fopen(copy, "rt");
01474 if (!g_in)
01475 {
01476 STOPIF_CODE_ERR(errno != ENOENT, errno,
01477 "!Cannot read group definition \"%s\"", copy);
01478
01479
01480
01481
01482
01483
01484
01485 memmove(copy + (conf_start-fn),
01486 copy + (eos-fn),
01487 strlen(CONFIGDIR_GROUP) + 1 + gn_len + 1);
01488
01489 DEBUGP("try for common: %s", copy);
01490 g_in=fopen(copy, "rt");
01491 STOPIF_CODE_ERR(!g_in && errno != ENOENT, errno,
01492 "!Cannot read group definition \"%s\"", copy);
01493 }
01494
01495 DEBUGP("Got filename %s", copy);
01496
01497 if (!g_in)
01498 {
01499 STOPIF_CODE_ERR(!is_ok, ENOENT,
01500 "!Group definition for \"%s\" not found;\n"
01501 "used in pattern \"%s\".",
01502 ign->group_name, ign->pattern);
01503
01504 goto defaults;
01505 }
01506
01507
01508 hlp__string_from_filep(NULL, NULL, NULL, SFF_RESET_LINENUM);
01509
01510 while (1)
01511 {
01512 status=hlp__string_from_filep(g_in, &input, NULL,
01513 SFF_WHITESPACE | SFF_COMMENT);
01514 if (status == EOF) break;
01515 STOPIF(status, "reading group file %s", copy);
01516
01517 conf_start=input;
01518 DEBUGP("parsing %s", conf_start);
01519 eos=hlp__get_word(conf_start, &conf_start);
01520
01521 if (*eos) *(eos++)=0;
01522 else eos=NULL;
01523
01524 if (strcmp(conf_start, "take") == 0)
01525 {
01526 group->is_take=1;
01527 continue;
01528 }
01529 else if (strcmp(conf_start, "ignore") == 0)
01530 {
01531 group->is_ignore=1;
01532 continue;
01533 }
01534 else if (strcmp(conf_start, "auto-prop") == 0)
01535 {
01536 cause="no property name";
01537 if (!eos) goto invalid;
01538 cause="no whitespace after name";
01539 if (sscanf(eos, "%s%n", input, &gn_len) != 1) goto invalid;
01540
01541 eos=hlp__skip_ws(eos+gn_len);
01542 DEBUGP("Got property name=%s, value=%s", input, eos);
01543
01544 cause="no property value";
01545 if (!*input || !*eos) goto invalid;
01546
01547 if (!group->auto_props)
01548 group->auto_props=apr_hash_make(global_pool);
01549
01550 STOPIF( hlp__strdup( &fn, eos), NULL);
01551
01552 gn_len=strlen(input);
01553 STOPIF( hlp__strnalloc( gn_len, &eos, input), NULL);
01554
01555
01556
01557 str=svn_string_create(fn, global_pool);
01558 apr_hash_set(group->auto_props, eos, gn_len, str);
01559 }
01560 else
01561 {
01562 cause="invalid keyword";
01563 invalid:
01564 STOPIF( EINVAL, "!Cannot parse line #%d in file \"%s\" (%s).",
01565 hlp__string_from_filep(NULL, NULL, NULL, SFF_GET_LINENUM),
01566 copy, cause);
01567 }
01568 }
01569
01570 defaults:
01571 status=0;
01572
01573 STOPIF_CODE_ERR( group->is_ignore && group->is_take, EINVAL,
01574 "Either \"take\" or \"ignore\" must be given, in \"%s\".",
01575 copy);
01576 if (!group->is_ignore && !group->is_take)
01577 {
01578 if (is_ok == 2)
01579 group->is_ignore=1;
01580 else
01581 group->is_take=1;
01582 }
01583
01584 DEBUGP("group has %sauto-props; ign=%d, take=%d, url=%s",
01585 group->auto_props ? "" : "no ",
01586 group->is_ignore, group->is_take,
01587 group->url ? group->url->url : "(default)");
01588
01589 ex:
01590 IF_FREE(copy);
01591 if (g_in) fclose(g_in);
01592 return status;
01593 }
01594
01595
01612 int ign__is_ignore(struct estat *sts,
01613 int *is_ignored)
01614 {
01615 struct estat *dir;
01616 int status, namelen UNUSED, len, i, path_len UNUSED;
01617 char *path UNUSED, *cp;
01618 struct ignore_t **ign_list UNUSED;
01619 struct ignore_t *ign;
01620 struct sstat_t *st;
01621 struct estat sts_cmp;
01622
01623
01624 *is_ignored=0;
01625 status=0;
01626 dir=sts->parent;
01627
01628 if (!dir) goto ex;
01629
01630 if (sts->to_be_ignored)
01631 {
01632 *is_ignored=1;
01633 goto ex;
01634 }
01635
01636
01637
01638
01639 STOPIF( ops__build_path(&cp, sts), NULL);
01640 DEBUGP("testing %s for being ignored", cp);
01641
01642 len=strlen(cp);
01643 for(i=0; i<used_ignore_entries; i++)
01644 {
01645 ign=ignore_list+i;
01646
01647 if (!ign->group_def)
01648 STOPIF( ign___load_group(ign), NULL);
01649
01650 ign->stats_tested++;
01651
01652 if (ign->type == PT_SHELL || ign->type == PT_PCRE ||
01653 ign->type == PT_SHELL_ABS)
01654 {
01655 DEBUGP("matching %s(0%o) against \"%s\" "
01656 "(dir_only=%d; and=0%o, cmp=0%o)",
01657 cp, sts->st.mode, ign->pattern, ign->dir_only,
01658 ign->mode_match_and, ign->mode_match_cmp);
01659 if (ign->dir_only && !S_ISDIR(sts->st.mode))
01660 {
01661 status=PCRE_ERROR_NOMATCH;
01662 }
01663 else if (ign->mode_match_and &&
01664 ((sts->st.mode & ign->mode_match_and) != ign->mode_match_cmp))
01665 {
01666 status=PCRE_ERROR_NOMATCH;
01667 }
01668 else if (ign->compiled)
01669 {
01670 status=pcre_exec(ign->compiled, ign->extra,
01671 cp, len,
01672 0, 0,
01673 NULL, 0);
01674 STOPIF_CODE_ERR( status && status != PCRE_ERROR_NOMATCH,
01675 status, "cannot match pattern %s on data %s",
01676 ign->pattern, cp);
01677 }
01678 }
01679 else if (ign->type == PT_DEVICE)
01680 {
01681
01682 st=(S_ISDIR(sts->st.mode)) ? &(dir->st) : &(sts->st);
01683
01684 switch (ign->compare)
01685 {
01686 case PAT_DEV__LESS:
01687 status= ign___compare_dev(st, ign) < 0;
01688 break;
01689 case PAT_DEV__LESS | PAT_DEV__EQUAL:
01690 status= ign___compare_dev(st, ign) <= 0;
01691 break;
01692 case PAT_DEV__EQUAL:
01693 status= ign___compare_dev(st, ign) == 0;
01694 break;
01695 case PAT_DEV__EQUAL | PAT_DEV__GREATER:
01696 status= ign___compare_dev(st, ign) >= 0;
01697 break;
01698 case PAT_DEV__GREATER:
01699 status= ign___compare_dev(st, ign) > 0;
01700 break;
01701 }
01702
01703
01704 status = !status;
01705 DEBUGP("device compare pattern status=%d", status);
01706 }
01707 else if (ign->type == PT_INODE)
01708 {
01709 sts_cmp.st.dev=ign->dev;
01710 sts_cmp.st.ino=ign->inode;
01711 status = dir___f_sort_by_inodePP(&sts_cmp, sts) != 0;
01712 DEBUGP("inode compare %llX:%llu status=%d",
01713 (t_ull)ign->dev, (t_ull)ign->inode, status);
01714 }
01715 else
01716 BUG("unknown pattern type 0x%X", ign->type);
01717
01718
01719 if (status == 0)
01720 {
01721 ign->stats_matches++;
01722 *is_ignored = ign->group_def->is_ignore ? +1 : -1;
01723 sts->match_pattern=ign;
01724 DEBUGP("pattern found - result %d", *is_ignored);
01725 goto ex;
01726 }
01727 }
01728
01729
01730 status=0;
01731
01732 ex:
01733 return status;
01734 }
01735
01736
01739 int ign__save_ignorelist(char *basedir)
01740 {
01741 int status, fh, l, i;
01742 struct ignore_t *ign;
01743 char buffer[HEADER_LEN];
01744
01745
01746 DEBUGP("saving ignore list: have %d", used_ignore_entries);
01747 fh=-1;
01748 if (!basedir) basedir=wc_path;
01749
01750 if (used_ignore_entries==0)
01751 {
01752 STOPIF( waa__delete_byext(basedir, WAA__IGNORE_EXT, 1), NULL);
01753 goto ex;
01754 }
01755
01756 STOPIF( waa__open_byext(basedir, WAA__IGNORE_EXT, WAA__WRITE, &fh), NULL);
01757
01758
01759 for(i=l=0; i<used_ignore_entries; i++)
01760 {
01761 if (ignore_list[i].is_user_pat) l++;
01762 }
01763
01764 status=snprintf(buffer, sizeof(buffer)-1,
01765 ign_header_str,
01766 l);
01767 STOPIF_CODE_ERR(status >= sizeof(buffer)-1, ENOSPC,
01768 "can't prepare header to write; buffer too small");
01769
01770 strcat(buffer, "\n");
01771 l=strlen(buffer);
01772 status=write(fh, buffer, l);
01773 STOPIF_CODE_ERR( status != l, errno, "error writing header");
01774
01775
01776
01777 ign=ignore_list;
01778 for(i=0; i<used_ignore_entries; i++)
01779 {
01780 if (ignore_list[i].is_user_pat)
01781 {
01782 l=strlen(ign->pattern)+1;
01783 status=write(fh, ign->pattern, l);
01784 STOPIF_CODE_ERR( status != l, errno, "error writing data");
01785
01786 status=write(fh, "\n", 1);
01787 STOPIF_CODE_ERR( status != 1, errno, "error writing newline");
01788 }
01789
01790 ign++;
01791 }
01792
01793 status=0;
01794
01795 ex:
01796 if (fh!=-1)
01797 {
01798 l=waa__close(fh, status);
01799 fh=-1;
01800 STOPIF(l, "error closing ignore data");
01801 }
01802
01803 return status;
01804 }
01805
01806
01807 int ign__new_pattern(unsigned count, char *pattern[],
01808 char *ends,
01809 int user_pattern,
01810 int position)
01811 {
01812 int status;
01813 unsigned i;
01814 struct ignore_t *ign;
01815
01816
01817 status=0;
01818 DEBUGP("getting %d new entries - max is %d, used are %d",
01819 count, max_ignore_entries, used_ignore_entries);
01820 if (used_ignore_entries+count >= max_ignore_entries)
01821 {
01822 max_ignore_entries = used_ignore_entries+count+RESERVE_IGNORE_ENTRIES;
01823 STOPIF( hlp__realloc( &ignore_list,
01824 sizeof(*ignore_list) * max_ignore_entries), NULL);
01825 }
01826
01827
01828
01829
01830 if (!pattern) goto ex;
01831
01832
01833
01834 if (position != PATTERN_POSITION_END && used_ignore_entries>0)
01835 {
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847
01848
01849 for(i=0; i<used_ignore_entries; i++)
01850 if (ignore_list[i].is_user_pat) break;
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862 i+=position;
01863
01864 memmove(ignore_list+i+count, ignore_list+i,
01865 (used_ignore_entries-i)*sizeof(*ignore_list));
01866
01867 position=i;
01868 }
01869 else
01870 position=used_ignore_entries;
01871
01872 BUG_ON(position > used_ignore_entries || position<0);
01873
01874 status=0;
01875 for(i=0; i<count; i++)
01876 {
01877
01878
01879
01880 DEBUGP("new pattern %s", *pattern);
01881 ign=ignore_list+i+position;
01882
01883
01884
01885
01886 STOPIF( ign___init_pattern_into(*pattern, ends, ign), NULL);
01887
01888 ign->is_user_pat=user_pattern;
01889 pattern++;
01890 }
01891
01892 used_ignore_entries+=count;
01893
01894 ex:
01895 return status;
01896 }
01897
01898
01901 int ign___parse_position(char *arg, int *position, int *advance)
01902 {
01903 int status;
01904 int i;
01905
01906 status=0;
01907 *advance=0;
01908
01909
01910 *position=PATTERN_POSITION_END;
01911 if (strcmp(arg, "prepend") == 0)
01912 {
01913 *advance=1;
01914 *position=PATTERN_POSITION_START;
01915 }
01916 else if (sscanf(arg, "at=%d", &i) == 1)
01917 {
01918 *advance=1;
01919 STOPIF_CODE_ERR(i > used_ignore_entries, EINVAL,
01920 "The position %d where the pattern "
01921 "should be inserted is invalid.\n", i);
01922 *position=i;
01923 }
01924 else if (strcmp(arg, "append") == 0)
01925 {
01926
01927 *advance=1;
01928 }
01929
01930 ex:
01931 return status;
01932 }
01933
01934
01935 int ign___test_single_pattern(struct estat *sts)
01936 {
01937 int status;
01938 char *path;
01939
01940 status=0;
01941 BUG_ON(!(sts->entry_status & FS_NEW));
01942
01943 if (sts->match_pattern)
01944 {
01945 STOPIF( ops__build_path(&path, sts), NULL);
01946 if (opt__is_verbose() >= 0)
01947 STOPIF_CODE_EPIPE( printf("%s\n", path), NULL);
01948 }
01949
01950 ex:
01951 return status;
01952 }
01953
01954
01955 int ign___test_all_patterns(struct estat *sts)
01956 {
01957 int status;
01958 char *path;
01959 struct ignore_t *ign;
01960
01961
01962 status=0;
01963 BUG_ON(!(sts->entry_status & FS_NEW));
01964
01965 STOPIF( ops__build_path(&path, sts), NULL);
01966 ign=sts->match_pattern;
01967
01968 if (opt__is_verbose() >= 0)
01969 STOPIF_CODE_EPIPE(
01970 opt__is_verbose()>0 ?
01971 printf("%s\t%s\t%s\n",
01972 ign ? ign->group_name : "(none)",
01973 ign ? ign->pattern : "(none)",
01974 path) :
01975 printf("%s\t%s\n", ign ? ign->group_name : "(none)", path), NULL);
01976
01977 ex:
01978 return status; return 0;
01979 }
01980
01981
01983 int ign__print_group_stats(FILE *output)
01984 {
01985 int status;
01986 int i;
01987 struct ignore_t *ign;
01988
01989 STOPIF_CODE_EPIPE( fprintf(output, "\nGrouping statistics ("
01990 "tested, matched, groupname, pattern):\n\n"), NULL);
01991
01992 for(i=0; i<used_ignore_entries; i++)
01993 {
01994 ign=ignore_list+i;
01995
01996 if (ign->is_user_pat || opt__is_verbose()>0)
01997 {
01998 STOPIF_CODE_EPIPE( fprintf(output, "%u\t%u\t%s\t%s\n",
01999 ign->stats_tested, ign->stats_matches,
02000 ign->group_name, ign->pattern), NULL);
02001 }
02002 }
02003
02004 ex:
02005 return status;
02006 }
02007
02008
02012 int ign__work(struct estat *root UNUSED, int argc, char *argv[])
02013 {
02014 int status;
02015 int position, i;
02016 char *cp, *copy;
02017 char *arg[2];
02018 struct grouping_t *group;
02019
02020
02021 status=0;
02022
02023
02024
02025 if (argc==0)
02026 ac__Usage_this();
02027
02028
02029
02030
02031 status=waa__find_common_base(0, NULL, NULL);
02032 if (status == ENOENT)
02033 STOPIF(EINVAL, "!No working copy base was found.");
02034 STOPIF(status, NULL);
02035
02036
02037 DEBUGP("first argument is %s", argv[0]);
02038
02039 status=0;
02040 if (strcmp(argv[0], parm_test) == 0)
02041 {
02042 argv++;
02043 argc--;
02044
02045 if (argc>0)
02046 {
02047 STOPIF( ign___parse_position(argv[0], &position, &i), NULL);
02048 argv+=i;
02049 argc-=i;
02050
02051
02052
02053
02054 action->i_val |= HAVE_GROUP;
02055
02056 STOPIF( ign__new_pattern(argc, argv, NULL, 1, position), NULL);
02057
02058 action->local_callback=ign___test_single_pattern;
02059 }
02060 else
02061 {
02062 STOPIF( ign__load_list(NULL), NULL);
02063 action->local_callback=ign___test_all_patterns;
02064 }
02065
02066 opt__set_int(OPT__FILTER, PRIO_MUSTHAVE, FS_NEW);
02067
02068
02069
02070 for(i=0; i<used_ignore_entries; i++)
02071 {
02072 STOPIF( ign___new_group(ignore_list+i, &group), NULL);
02073 ignore_list[i].group_def->is_ignore=0;
02074 ignore_list[i].group_def->is_take=1;
02075 }
02076
02077
02078 STOPIF( url__load_list(NULL, 0), NULL);
02079
02080
02081
02082 if (start_path_len == wc_path_len)
02083 arg[0]=".";
02084 else
02085 arg[0]=start_path+wc_path_len+1;
02086
02087 arg[1]=NULL;
02088 STOPIF( waa__read_or_build_tree(root, 1, arg, arg, NULL, 0), NULL);
02089
02090 if (opt__get_int(OPT__GROUP_STATS))
02091 STOPIF( ign__print_group_stats(stdout), NULL);
02092
02093
02094 goto dont_store;
02095 }
02096 else if (strcmp(argv[0], parm_load) == 0)
02097 {
02098 i=0;
02099 while (1)
02100 {
02101 status=hlp__string_from_filep(stdin, &cp, NULL, SFF_WHITESPACE);
02102 if (status == EOF) break;
02103
02104 STOPIF(status, NULL);
02105
02106 STOPIF( hlp__strdup( ©, cp), NULL);
02107 STOPIF( ign__new_pattern(1, ©, NULL,
02108 1, PATTERN_POSITION_END), NULL);
02109 i++;
02110 }
02111
02112 if (opt__is_verbose() >= 0)
02113 printf("%d pattern%s loaded.\n", i, i==1 ? "" : "s");
02114 }
02115 else
02116 {
02117
02118 STOPIF( ign__load_list(NULL), NULL);
02119
02120 if (strcmp(argv[0], parm_dump) == 0)
02121 {
02122
02123 for(i=position=0; i < used_ignore_entries; i++, position++)
02124 if (ignore_list[i].is_user_pat)
02125 {
02126 if (opt__is_verbose() > 0)
02127 printf("%3d: ", position);
02128
02129 printf("%s\n", ignore_list[i].pattern);
02130 }
02131
02132
02133 goto dont_store;
02134 }
02135 else
02136 {
02137 STOPIF( ign___parse_position(argv[0], &position, &i), NULL);
02138 argv+=i;
02139 argc-=i;
02140 STOPIF( ign__new_pattern(argc, argv, NULL, 1, position), NULL);
02141 }
02142 }
02143
02144 STOPIF( ign__save_ignorelist(NULL), NULL);
02145
02146 dont_store:
02147 ex:
02148 return status;
02149 }
02150
02151
02155 int ign__rign(struct estat *root UNUSED, int argc, char *argv[])
02156 {
02157 int status;
02158 int i, position;
02159 char **normalized;
02160
02161 status=0;
02162 if (argc==0) ac__Usage_this();
02163
02164
02165 STOPIF( ign___parse_position(argv[0], &position, &i), NULL);
02166 argv+=i;
02167 argc-=i;
02168
02169
02170 status=waa__find_common_base2(argc, argv, &normalized,
02171 FCB__PUT_DOTSLASH | FCB__NO_REALPATH);
02172 if (status == ENOENT)
02173 STOPIF(EINVAL, "!No working copy base was found.");
02174 STOPIF(status, NULL);
02175
02176
02177 STOPIF( ign__load_list(NULL), NULL);
02178 STOPIF( ign__new_pattern(argc, normalized, NULL, 1, position), NULL);
02179 STOPIF( ign__save_ignorelist(NULL), NULL);
02180
02181 ex:
02182 return status;
02183 }
02184