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