Changeset 1770 in MondoRescue for branches/stable/mindi-busybox/coreutils/sort.c
- Timestamp:
- Nov 6, 2007, 11:01:53 AM (16 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
branches/stable/mindi-busybox/coreutils/sort.c
r821 r1770 6 6 * 7 7 * MAINTAINER: Rob Landley <rob@landley.net> 8 * 8 * 9 9 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. 10 10 * … … 13 13 */ 14 14 15 #include <ctype.h> 16 #include <math.h> 17 #include <stdio.h> 18 #include <stdlib.h> 19 #include <string.h> 20 #include <time.h> 21 #include <unistd.h> 22 #include "busybox.h" 23 24 static int global_flags; 15 #include "libbb.h" 16 17 /* This is a NOEXEC applet. Be very careful! */ 18 25 19 26 20 /* … … 30 24 31 25 /* These are sort types */ 32 #define FLAG_n 1 /* Numeric sort */ 33 #define FLAG_g 2 /* Sort using strtod() */ 34 #define FLAG_M 4 /* Sort date */ 26 static const char OPT_STR[] ALIGN1 = "ngMucszbrdfimS:T:o:k:t:"; 27 enum { 28 FLAG_n = 1, /* Numeric sort */ 29 FLAG_g = 2, /* Sort using strtod() */ 30 FLAG_M = 4, /* Sort date */ 35 31 /* ucsz apply to root level only, not keys. b at root level implies bb */ 36 #define FLAG_u 8/* Unique */37 #define FLAG_c 16/* Check: no output, exit(!ordered) */38 #define FLAG_s 32/* Stable sort, no ascii fallback at end */39 #define FLAG_z 64/* Input is null terminated, not \n */32 FLAG_u = 8, /* Unique */ 33 FLAG_c = 0x10, /* Check: no output, exit(!ordered) */ 34 FLAG_s = 0x20, /* Stable sort, no ascii fallback at end */ 35 FLAG_z = 0x40, /* Input is null terminated, not \n */ 40 36 /* These can be applied to search keys, the previous four can't */ 41 #define FLAG_b 128 /* Ignore leading blanks */ 42 #define FLAG_r 256 /* Reverse */ 43 #define FLAG_d 512 /* Ignore !(isalnum()|isspace()) */ 44 #define FLAG_f 1024 /* Force uppercase */ 45 #define FLAG_i 2048 /* Ignore !isprint() */ 46 #define FLAG_bb 32768 /* Ignore trailing blanks */ 47 48 49 #ifdef CONFIG_FEATURE_SORT_BIG 37 FLAG_b = 0x80, /* Ignore leading blanks */ 38 FLAG_r = 0x100, /* Reverse */ 39 FLAG_d = 0x200, /* Ignore !(isalnum()|isspace()) */ 40 FLAG_f = 0x400, /* Force uppercase */ 41 FLAG_i = 0x800, /* Ignore !isprint() */ 42 FLAG_m = 0x1000, /* ignored: merge already sorted files; do not sort */ 43 FLAG_S = 0x2000, /* ignored: -S, --buffer-size=SIZE */ 44 FLAG_T = 0x4000, /* ignored: -T, --temporary-directory=DIR */ 45 FLAG_o = 0x8000, 46 FLAG_k = 0x10000, 47 FLAG_t = 0x20000, 48 FLAG_bb = 0x80000000, /* Ignore trailing blanks */ 49 }; 50 51 #if ENABLE_FEATURE_SORT_BIG 50 52 static char key_separator; 51 53 52 static struct sort_key 53 { 54 static struct sort_key { 54 55 struct sort_key *next_key; /* linked list */ 55 unsigned shortrange[4]; /* start word, start char, end word, end char */56 intflags;56 unsigned range[4]; /* start word, start char, end word, end char */ 57 unsigned flags; 57 58 } *key_list; 58 59 59 60 static char *get_key(char *str, struct sort_key *key, int flags) 60 61 { 61 int start =0,end,len,i,j;62 int start = 0, end = 0, len, i, j; 62 63 63 64 /* Special case whole string, so we don't have to make a copy */ 64 if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] 65 && !(flags&(FLAG_b&FLAG_d&FLAG_f&FLAG_i&FLAG_bb))) return str; 66 /* Find start of key on first pass, end on second pass*/ 67 len=strlen(str); 68 69 for(j=0;j<2;j++) { 70 if(!key->range[2*j]) end=len; 65 if (key->range[0] == 1 && !key->range[1] && !key->range[2] && !key->range[3] 66 && !(flags & (FLAG_b | FLAG_d | FLAG_f | FLAG_i | FLAG_bb)) 67 ) { 68 return str; 69 } 70 71 /* Find start of key on first pass, end on second pass */ 72 len = strlen(str); 73 for (j = 0; j < 2; j++) { 74 if (!key->range[2*j]) 75 end = len; 71 76 /* Loop through fields */ 72 77 else { 73 end=0; 74 for(i=1;i<key->range[2*j]+j;i++) { 75 /* Skip leading blanks or first separator */ 76 if(str[end]) { 77 if(!key_separator && isspace(str[end])) 78 while(isspace(str[end])) end++; 79 } 80 /* Skip body of key */ 81 for(;str[end];end++) { 82 if(key_separator) { 83 if(str[end]==key_separator) break; 84 } else if(isspace(str[end])) break; 78 end = 0; 79 for (i = 1; i < key->range[2*j] + j; i++) { 80 if (key_separator) { 81 /* Skip body of key and separator */ 82 while (str[end]) { 83 if (str[end++] == key_separator) 84 break; 85 } 86 } else { 87 /* Skip leading blanks */ 88 while (isspace(str[end])) 89 end++; 90 /* Skip body of key */ 91 while (str[end]) { 92 if (isspace(str[end])) 93 break; 94 end++; 95 } 85 96 } 86 97 } 87 98 } 88 if(!j) start=end; 89 } 90 /* Key with explicit separator starts after separator */ 91 if(key_separator && str[start]==key_separator) start++; 99 if (!j) start = end; 100 } 92 101 /* Strip leading whitespace if necessary */ 93 if(flags&FLAG_b) while(isspace(str[start])) start++; 102 //XXX: skip_whitespace() 103 if (flags & FLAG_b) 104 while (isspace(str[start])) start++; 94 105 /* Strip trailing whitespace if necessary */ 95 if(flags&FLAG_bb) while(end>start && isspace(str[end-1])) end--; 106 if (flags & FLAG_bb) 107 while (end > start && isspace(str[end-1])) end--; 96 108 /* Handle offsets on start and end */ 97 if (key->range[3]) {98 end +=key->range[3]-1;99 if (end>len) end=len;100 } 101 if (key->range[1]) {102 start +=key->range[1]-1;103 if (start>len) start=len;109 if (key->range[3]) { 110 end += key->range[3] - 1; 111 if (end > len) end = len; 112 } 113 if (key->range[1]) { 114 start += key->range[1] - 1; 115 if (start > len) start = len; 104 116 } 105 117 /* Make the copy */ 106 if (end<start) end=start;107 str =bb_xstrndup(str+start,end-start);118 if (end < start) end = start; 119 str = xstrndup(str+start, end-start); 108 120 /* Handle -d */ 109 if(flags&FLAG_d) { 110 for(start=end=0;str[end];end++) 111 if(isspace(str[end]) || isalnum(str[end])) str[start++]=str[end]; 112 str[start]=0; 121 if (flags & FLAG_d) { 122 for (start = end = 0; str[end]; end++) 123 if (isspace(str[end]) || isalnum(str[end])) 124 str[start++] = str[end]; 125 str[start] = '\0'; 113 126 } 114 127 /* Handle -i */ 115 if(flags&FLAG_i) { 116 for(start=end=0;str[end];end++) 117 if(isprint(str[end])) str[start++]=str[end]; 118 str[start]=0; 128 if (flags & FLAG_i) { 129 for (start = end = 0; str[end]; end++) 130 if (isprint(str[end])) 131 str[start++] = str[end]; 132 str[start] = '\0'; 119 133 } 120 134 /* Handle -f */ 121 if(flags*FLAG_f) for(i=0;str[i];i++) str[i]=toupper(str[i]); 135 if (flags & FLAG_f) 136 for (i = 0; str[i]; i++) 137 str[i] = toupper(str[i]); 122 138 123 139 return str; … … 126 142 static struct sort_key *add_key(void) 127 143 { 128 struct sort_key **pkey=&key_list; 129 while(*pkey) pkey=&((*pkey)->next_key); 130 return *pkey=xcalloc(1,sizeof(struct sort_key)); 144 struct sort_key **pkey = &key_list; 145 while (*pkey) 146 pkey = &((*pkey)->next_key); 147 return *pkey = xzalloc(sizeof(struct sort_key)); 131 148 } 132 149 133 #define GET_LINE(fp) (global_flags&FLAG_z) ? bb_get_chunk_from_file(fp,NULL) \ 134 : bb_get_chomped_line_from_file(fp) 150 #define GET_LINE(fp) \ 151 ((option_mask32 & FLAG_z) \ 152 ? bb_get_chunk_from_file(fp, NULL) \ 153 : xmalloc_getline(fp)) 135 154 #else 136 #define GET_LINE(fp) bb_get_chomped_line_from_file(fp)155 #define GET_LINE(fp) xmalloc_getline(fp) 137 156 #endif 138 157 … … 140 159 static int compare_keys(const void *xarg, const void *yarg) 141 160 { 142 int flags =global_flags,retval=0;143 char *x, *y;144 145 #if def CONFIG_FEATURE_SORT_BIG161 int flags = option_mask32, retval = 0; 162 char *x, *y; 163 164 #if ENABLE_FEATURE_SORT_BIG 146 165 struct sort_key *key; 147 166 148 for (key=key_list;!retval && key;key=key->next_key) {149 flags =(key->flags) ? key->flags : global_flags;167 for (key = key_list; !retval && key; key = key->next_key) { 168 flags = key->flags ? key->flags : option_mask32; 150 169 /* Chop out and modify key chunks, handling -dfib */ 151 x =get_key(*(char **)xarg,key,flags);152 y =get_key(*(char **)yarg,key,flags);170 x = get_key(*(char **)xarg, key, flags); 171 y = get_key(*(char **)yarg, key, flags); 153 172 #else 154 173 /* This curly bracket serves no purpose but to match the nesting 155 level of the for () loop we're not using */174 level of the for () loop we're not using */ 156 175 { 157 x =*(char **)xarg;158 y =*(char **)yarg;176 x = *(char **)xarg; 177 y = *(char **)yarg; 159 178 #endif 160 179 /* Perform actual comparison */ 161 switch(flags&7) { 162 default: 163 bb_error_msg_and_die("Unknown sort type."); 164 break; 165 /* Ascii sort */ 166 case 0: 167 retval=strcmp(x,y); 168 break; 169 #ifdef CONFIG_FEATURE_SORT_BIG 170 case FLAG_g: 171 { 172 char *xx,*yy; 173 double dx=strtod(x,&xx), dy=strtod(y,&yy); 174 /* not numbers < NaN < -infinity < numbers < +infinity) */ 175 if(x==xx) retval=(y==yy ? 0 : -1); 176 else if(y==yy) retval=1; 177 /* Check for isnan */ 178 else if(dx != dx) retval = (dy != dy) ? 0 : -1; 179 else if(dy != dy) retval = 1; 180 /* Check for infinity. Could underflow, but it avoids libm. */ 181 else if(1.0/dx == 0.0) { 182 if(dx<0) retval=((1.0/dy == 0.0 && dy<0) ? 0 : -1); 183 else retval=((1.0/dy == 0.0 && dy>0) ? 0 : 1); 184 } else if(1.0/dy == 0.0) retval=dy<0 ? 1 : -1; 185 else retval=dx>dy ? 1 : (dx<dy ? -1 : 0); 186 break; 187 } 188 case FLAG_M: 189 { 190 struct tm thyme; 191 int dx; 192 char *xx,*yy; 193 194 xx=strptime(x,"%b",&thyme); 195 dx=thyme.tm_mon; 196 yy=strptime(y,"%b",&thyme); 197 if(!xx) retval=(!yy ? 0 : -1); 198 else if(!yy) retval=1; 199 else retval=(dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon); 200 break; 201 } 202 /* Full floating point version of -n */ 203 case FLAG_n: 204 { 205 double dx=atof(x),dy=atof(y); 206 retval=dx>dy ? 1 : (dx<dy ? -1 : 0); 207 break; 208 } 209 } 180 switch (flags & 7) { 181 default: 182 bb_error_msg_and_die("unknown sort type"); 183 break; 184 /* Ascii sort */ 185 case 0: 186 #if ENABLE_LOCALE_SUPPORT 187 retval = strcoll(x, y); 188 #else 189 retval = strcmp(x, y); 190 #endif 191 break; 192 #if ENABLE_FEATURE_SORT_BIG 193 case FLAG_g: { 194 char *xx, *yy; 195 double dx = strtod(x, &xx); 196 double dy = strtod(y, &yy); 197 /* not numbers < NaN < -infinity < numbers < +infinity) */ 198 if (x == xx) 199 retval = (y == yy ? 0 : -1); 200 else if (y == yy) 201 retval = 1; 202 /* Check for isnan */ 203 else if (dx != dx) 204 retval = (dy != dy) ? 0 : -1; 205 else if (dy != dy) 206 retval = 1; 207 /* Check for infinity. Could underflow, but it avoids libm. */ 208 else if (1.0 / dx == 0.0) { 209 if (dx < 0) 210 retval = (1.0 / dy == 0.0 && dy < 0) ? 0 : -1; 211 else 212 retval = (1.0 / dy == 0.0 && dy > 0) ? 0 : 1; 213 } else if (1.0 / dy == 0.0) 214 retval = (dy < 0) ? 1 : -1; 215 else 216 retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); 217 break; 218 } 219 case FLAG_M: { 220 struct tm thyme; 221 int dx; 222 char *xx, *yy; 223 224 xx = strptime(x, "%b", &thyme); 225 dx = thyme.tm_mon; 226 yy = strptime(y, "%b", &thyme); 227 if (!xx) 228 retval = (!yy) ? 0 : -1; 229 else if (!yy) 230 retval = 1; 231 else 232 retval = (dx == thyme.tm_mon) ? 0 : dx - thyme.tm_mon; 233 break; 234 } 235 /* Full floating point version of -n */ 236 case FLAG_n: { 237 double dx = atof(x); 238 double dy = atof(y); 239 retval = (dx > dy) ? 1 : ((dx < dy) ? -1 : 0); 240 break; 241 } 242 } /* switch */ 210 243 /* Free key copies. */ 211 if (x!=*(char **)xarg) free(x);212 if (y!=*(char **)yarg) free(y);213 if(retval) break;244 if (x != *(char **)xarg) free(x); 245 if (y != *(char **)yarg) free(y); 246 /* if (retval) break; - done by for () anyway */ 214 247 #else 215 /* Integer version of -n for tiny systems */ 216 case FLAG_n: 217 retval=atoi(x)-atoi(y); 218 break; 219 } 220 #endif 221 } 248 /* Integer version of -n for tiny systems */ 249 case FLAG_n: 250 retval = atoi(x) - atoi(y); 251 break; 252 } /* switch */ 253 #endif 254 } /* for */ 255 222 256 /* Perform fallback sort if necessary */ 223 if(!retval && !(global_flags&FLAG_s)) 224 retval=strcmp(*(char **)xarg, *(char **)yarg); 225 //dprintf(2,"reverse=%d\n",flags&FLAG_r); 226 return ((flags&FLAG_r)?-1:1)*retval; 257 if (!retval && !(option_mask32 & FLAG_s)) 258 retval = strcmp(*(char **)xarg, *(char **)yarg); 259 260 if (flags & FLAG_r) return -retval; 261 return retval; 227 262 } 228 263 264 #if ENABLE_FEATURE_SORT_BIG 265 static unsigned str2u(char **str) 266 { 267 unsigned long lu; 268 if (!isdigit((*str)[0])) 269 bb_error_msg_and_die("bad field specification"); 270 lu = strtoul(*str, str, 10); 271 if ((sizeof(long) > sizeof(int) && lu > INT_MAX) || !lu) 272 bb_error_msg_and_die("bad field specification"); 273 return lu; 274 } 275 #endif 276 277 int sort_main(int argc, char **argv); 229 278 int sort_main(int argc, char **argv) 230 279 { 231 FILE *fp,*outfile=NULL; 232 int linecount=0,i,flag; 233 char *line,**lines=NULL,*optlist="ngMucszbrdfimS:T:o:k:t:"; 234 int c; 235 236 bb_default_error_retval = 2; 280 FILE *fp, *outfile = stdout; 281 char *line, **lines = NULL; 282 char *str_ignored, *str_o, *str_t; 283 llist_t *lst_k = NULL; 284 int i, flag; 285 int linecount = 0; 286 287 xfunc_error_retval = 2; 288 237 289 /* Parse command line options */ 238 while((c=getopt(argc,argv,optlist))>0) { 239 line=strchr(optlist,c); 240 if(!line) bb_show_usage(); 241 switch(*line) { 242 #ifdef CONFIG_FEATURE_SORT_BIG 243 case 'o': 244 if(outfile) bb_error_msg_and_die("Too many -o."); 245 outfile=bb_xfopen(optarg,"w"); 246 break; 247 case 't': 248 if(key_separator || optarg[1]) 249 bb_error_msg_and_die("Too many -t."); 250 key_separator=*optarg; 251 break; 252 /* parse sort key */ 253 case 'k': 254 { 255 struct sort_key *key=add_key(); 256 char *temp, *temp2; 257 258 temp=optarg; 259 for(i=0;*temp;) { 260 /* Start of range */ 261 key->range[2*i]=(unsigned short)strtol(temp,&temp,10); 262 if(*temp=='.') 263 key->range[(2*i)+1]=(unsigned short)strtol(temp+1,&temp,10); 264 for(;*temp;temp++) { 265 if(*temp==',' && !i++) { 266 temp++; 267 break; 268 } /* no else needed: fall through to syntax error 269 because comma isn't in optlist */ 270 temp2=strchr(optlist,*temp); 271 flag=(1<<(temp2-optlist)); 272 if(!temp2 || (flag>FLAG_M && flag<FLAG_b)) 273 bb_error_msg_and_die("Unknown key option."); 274 /* b after , means strip _trailing_ space */ 275 if(i && flag==FLAG_b) flag=FLAG_bb; 276 key->flags|=flag; 277 } 278 } 279 break; 290 /* -o and -t can be given at most once */ 291 opt_complementary = "o--o:t--t:" /* -t, -o: maximum one of each */ 292 "k::"; /* -k takes list */ 293 getopt32(argv, OPT_STR, &str_ignored, &str_ignored, &str_o, &lst_k, &str_t); 294 #if ENABLE_FEATURE_SORT_BIG 295 if (option_mask32 & FLAG_o) outfile = xfopen(str_o, "w"); 296 if (option_mask32 & FLAG_t) { 297 if (!str_t[0] || str_t[1]) 298 bb_error_msg_and_die("bad -t parameter"); 299 key_separator = str_t[0]; 300 } 301 /* parse sort key */ 302 while (lst_k) { 303 enum { 304 FLAG_allowed_for_k = 305 FLAG_n | /* Numeric sort */ 306 FLAG_g | /* Sort using strtod() */ 307 FLAG_M | /* Sort date */ 308 FLAG_b | /* Ignore leading blanks */ 309 FLAG_r | /* Reverse */ 310 FLAG_d | /* Ignore !(isalnum()|isspace()) */ 311 FLAG_f | /* Force uppercase */ 312 FLAG_i | /* Ignore !isprint() */ 313 0 314 }; 315 struct sort_key *key = add_key(); 316 char *str_k = lst_k->data; 317 const char *temp2; 318 319 i = 0; /* i==0 before comma, 1 after (-k3,6) */ 320 while (*str_k) { 321 /* Start of range */ 322 /* Cannot use bb_strtou - suffix can be a letter */ 323 key->range[2*i] = str2u(&str_k); 324 if (*str_k == '.') { 325 str_k++; 326 key->range[2*i+1] = str2u(&str_k); 280 327 } 281 #endif 282 default: 283 global_flags|=(1<<(line-optlist)); 284 /* global b strips leading and trailing spaces */ 285 if(global_flags&FLAG_b) global_flags|=FLAG_bb; 286 break; 287 } 288 } 328 while (*str_k) { 329 if (*str_k == ',' && !i++) { 330 str_k++; 331 break; 332 } /* no else needed: fall through to syntax error 333 because comma isn't in OPT_STR */ 334 temp2 = strchr(OPT_STR, *str_k); 335 if (!temp2) 336 bb_error_msg_and_die("unknown key option"); 337 flag = 1 << (temp2 - OPT_STR); 338 if (flag & ~FLAG_allowed_for_k) 339 bb_error_msg_and_die("unknown sort type"); 340 /* b after ',' means strip _trailing_ space */ 341 if (i && flag == FLAG_b) flag = FLAG_bb; 342 key->flags |= flag; 343 str_k++; 344 } 345 } 346 /* leaking lst_k... */ 347 lst_k = lst_k->link; 348 } 349 #endif 350 /* global b strips leading and trailing spaces */ 351 if (option_mask32 & FLAG_b) option_mask32 |= FLAG_bb; 352 289 353 /* Open input files and read data */ 290 for(i=argv[optind] ? optind : optind-1;argv[i];i++) { 291 if(i<optind || (*argv[i]=='-' && !argv[i][1])) fp=stdin; 292 else fp=bb_xfopen(argv[i],"r"); 293 for(;;) { 294 line=GET_LINE(fp); 295 if(!line) break; 296 if(!(linecount&63)) 297 lines=xrealloc(lines, sizeof(char *)*(linecount+64)); 298 lines[linecount++]=line; 354 for (i = argv[optind] ? optind : optind-1; argv[i]; i++) { 355 fp = stdin; 356 if (i >= optind && NOT_LONE_DASH(argv[i])) 357 fp = xfopen(argv[i], "r"); 358 for (;;) { 359 line = GET_LINE(fp); 360 if (!line) break; 361 if (!(linecount & 63)) 362 lines = xrealloc(lines, sizeof(char *) * (linecount + 64)); 363 lines[linecount++] = line; 299 364 } 300 365 fclose(fp); 301 366 } 302 #if def CONFIG_FEATURE_SORT_BIG367 #if ENABLE_FEATURE_SORT_BIG 303 368 /* if no key, perform alphabetic sort */ 304 if(!key_list) add_key()->range[0]=1; 369 if (!key_list) 370 add_key()->range[0] = 1; 305 371 /* handle -c */ 306 if (global_flags&FLAG_c) {307 int j =(global_flags&FLAG_u) ? -1 : 0;308 for (i=1;i<linecount;i++)309 if (compare_keys(&lines[i-1],&lines[i])>j) {310 fprintf(stderr, "Check line %d\n",i);372 if (option_mask32 & FLAG_c) { 373 int j = (option_mask32 & FLAG_u) ? -1 : 0; 374 for (i = 1; i < linecount; i++) 375 if (compare_keys(&lines[i-1], &lines[i]) > j) { 376 fprintf(stderr, "Check line %d\n", i); 311 377 return 1; 312 378 } … … 315 381 #endif 316 382 /* Perform the actual sort */ 317 qsort(lines, linecount,sizeof(char *),compare_keys);383 qsort(lines, linecount, sizeof(char *), compare_keys); 318 384 /* handle -u */ 319 if(global_flags&FLAG_u) { 320 for(flag=0,i=1;i<linecount;i++) { 321 if(!compare_keys(&lines[flag],&lines[i])) free(lines[i]); 322 else lines[++flag]=lines[i]; 323 } 324 if(linecount) linecount=flag+1; 385 if (option_mask32 & FLAG_u) { 386 flag = 0; 387 /* coreutils 6.3 drop lines for which only key is the same */ 388 /* -- disabling last-resort compare... */ 389 option_mask32 |= FLAG_s; 390 for (i = 1; i < linecount; i++) { 391 if (!compare_keys(&lines[flag], &lines[i])) 392 free(lines[i]); 393 else 394 lines[++flag] = lines[i]; 395 } 396 if (linecount) linecount = flag+1; 325 397 } 326 398 /* Print it */ 327 if(!outfile) outfile=stdout; 328 for(i=0;i<linecount;i++) fprintf(outfile,"%s\n",lines[i]); 329 bb_fflush_stdout_and_exit(EXIT_SUCCESS); 399 for (i = 0; i < linecount; i++) 400 fprintf(outfile, "%s\n", lines[i]); 401 402 fflush_stdout_and_exit(EXIT_SUCCESS); 330 403 }
Note:
See TracChangeset
for help on using the changeset viewer.