1 /***********************************************************************
2 This file is part of HA, a general purpose file archiver.
3 Copyright (C) 1995 Harri Hirvola
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 ************************************************************************
19 HA main program
20 ***********************************************************************/
21
22 #include <string.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <sys/types.h>
27 #include <dirent.h>
28 #include "error.h"
29 #include "ha.h"
30 #include "archive.h"
31 #include "haio.h"
32 #include "cpy.h"
33 #include "asc.h"
34 #include "hsc.h"
35
36 /***********************************************************************
37 Commands
38 */
39
40 #define ADD 'a'
41 #define EXTRACT 'e'
42 #define PEXTRACT 'x'
43 #define FRESHEN 'f'
44 #define UPDATE 'u'
45 #define LIST 'l'
46 #define DELETE 'd'
47 #define TEST 't'
48 #define INFO 'h'
49
50 char *myname;
51 int quiet=0,useattr=0,special=0;
52 static unsigned ilen=0;
53 static int fulllist=0,usepath=1,yes=0,touch=0,recurse=0,savedir=0,move=0;
54 static char *defpat[]={ALLFILES};
55 static int metqueue[M_UNK+1]={M_UNK};
56 static int (*addthis)(char*, char*);
dummy(void)57 static void dummy(void) {
58 /* Do nothing */
59 }
60
61 struct {
62 char *name;
63 void (*encode)(void);
64 void (*decode)(void);
65 void (*cleanup)(void); } method[]= {
66 {"CPY",cpy,cpy,dummy},
67 {"ASC",asc_pack,asc_unpack,asc_cleanup},
68 {"HSC",hsc_pack,hsc_unpack,hsc_cleanup},
69 {"3"},{"4"},{"5"},{"6"},{"7"},{"8"},{"9"},{"10"},{"11"},{"12"},{"13"},
70 {"DIR"},
71 {"SPC"}
72 };
73
banner(void)74 static void banner(void) {
75
76 fprintf(stderr,BANNER);
77 fflush(stderr);
78 }
79
getinfo(unsigned char * buf,unsigned blen)80 static unsigned getinfo(unsigned char *buf, unsigned blen) {
81
82 static unsigned char *idat=infodat;
83 unsigned i;
84
85 for (i=0;i<blen && --ilen;i++) {
86 buf[i]=*idat++;
87 }
88 return i;
89 }
90
info(void)91 static void info(void) {
92
93 setoutput(STDOUT_FILENO,0,"stdout");
94 ilen=infolen;
95 inspecial=getinfo;
96 ibl=0;
97 fprintf(stdout,BANNER);
98 fflush(stdout);
99 (*method[M_HSC].decode)();
100 fflush(stdout);
101 exit(lasterror);
102 }
103
104
infoout(unsigned char * buf,unsigned blen)105 static void infoout(unsigned char *buf, unsigned blen) {
106
107 U16B i;
108 char ds[10];
109
110 for (i=0;i<blen;++i) {
111 if (ilen) write(arcfile,",",1);
112 if (!(ilen++&0xf)) write(arcfile,"\n\t",2);
113 sprintf(ds,"0x%02X",buf[i]);
114 write(arcfile,ds,strlen(ds));
115 }
116 }
117
makeinfo(char * ifile,char * ofile)118 static void makeinfo(char *ifile, char *ofile) {
119
120 int df;
121 char ds[256];
122
123 if ((df=open(ifile,O_RDONLY|O_BINARY))<0 ||
124 (arcfile=open(ofile,O_WRONLY|O_BINARY|O_TRUNC|O_CREAT,DEF_FILEATTR))<0)
125 exit(99);
126 setinput(df,0,"ifile");
127 outspecial=infoout;
128 obl=0;
129 strcpy(ds,"unsigned char infodat[]={");
130 write(arcfile,ds,strlen(ds));
131 (*method[M_HSC].encode)();
132 sprintf(ds,"\n};\n\nunsigned infolen=%d;\n",ilen);
133 write(arcfile,ds,strlen(ds));
134 close(df);
135 close(arcfile);
136 exit(0);
137 }
138
usage(int ex)139 static void usage(int ex) {
140
141 banner();
142 fprintf(stderr,"\n usage : HA <cmd> archive [files]"
143 EXAMPLE
144 "\n"
145 "\n commands :"
146 "\n a[sdqemr012] - Add files d[q] - Delete files"
147 "\n e[aqty] - Extract files f[sdqemr012] - Freshen files"
148 "\n l[f] - List files t[q] - Test files"
149 "\n u[sdqemr012] - Update files x[aqty] -"
150 " eXtract files with pathnames"
151 "\n"
152 "\n switches :"
153 "\n 0,1,2 - try method (0-CPY,1-ASC,2-HSC)"
154 "\n t - Touch files r - Recurse subdirs"
155 "\n f - Full listing y -"
156 " assume Yes on all questions"
157 "\n m - Move files a -"
158 " set system specific file Attributes"
159 "\n e - Exclude pathnames s - find Special files"
160 "\n q - Quiet operation d -"
161 " make Directory entries"
162 "\n"
163 "\nType \"ha h | more\" to get more information about HA."
164 "\n"
165 );
166 fflush(stderr);
167 if (ex) {
168 cu_do(NULL);
169 exit(ex);
170 }
171 }
172
yesno(char * format,char * string)173 static int yesno(char *format, char *string) {
174
175 int rep;
176
177 if (yes || quiet) return 1;
178 printf(format,string);
179 fflush(stdout);
180 for (rep=0;!rep;) {
181 switch (rep=getchar()) {
182 case 'Y' :
183 case 'y' :
184 case 'N' :
185 case 'n' :
186 case 'A' :
187 case 'a' :
188 break;
189 default :
190 rep=0;
191 break;
192 }
193 }
194 if (rep=='Y'||rep=='y') rep=1;
195 else if (rep=='A'||rep=='a') rep=yes=1;
196 else rep=0;
197 return rep;
198 }
199
backstep(int len)200 static void backstep(int len) {
201
202 while (--len) putchar(0x08);
203 }
204
do_list(void)205 static void do_list(void) {
206
207 U32B tcs,tos;
208 unsigned files;
209 Fheader *hd;
210
211 arc_reset();
212 if ((hd=arc_seek())==NULL) error(1,ERR_NOFILES);
213 printf("\n filename original compressed"
214 " rate date time m");
215 if (fulllist) {
216 printf("\n CRC-32 path");
217 md_listhdr();
218 }
219 printf("\n================================="
220 "==========================================");
221 tcs=tos=files=0;
222 for(;;) {
223 if (hd->type==0xff) continue;
224 if (fulllist && files) {
225 printf("\n-------------------------"
226 "--------------------------------------------------");
227 }
228 printf("\n %-15s %-11" F_32B " %-11" F_32B " %3d.%d %% %s %s",
229 hd->name,hd->olen,hd->clen,
230 (hd->olen==0?100:(int)(100*hd->clen/hd->olen)),
231 (hd->olen==0?0:(int)((1000*hd->clen/hd->olen)%10)),
232 md_timestring(hd->time),method[hd->type].name);
233 if (fulllist) {
234 printf("\n %08" FX_32B " %s",hd->crc,
235 *hd->path==0?"(none)":md_tomdpath(hd->path));
236 md_listdat();
237 }
238 tcs+=hd->clen;
239 tos+=hd->olen;
240 ++files;
241 if ((hd=arc_seek())==NULL) break;
242 }
243 printf("\n============================="
244 "==============================================");
245 printf("\n %-4d %-11" F_32B " %-11" F_32B " %3d.%d %%\n",
246 files,tos,tcs,(tos==0?100:(int)(100*tcs/tos)),
247 (tos==0?0:(int)((1000*tcs/tos)%10)));
248 }
249
do_extract(void)250 static void do_extract(void) {
251
252 Fheader *hd;
253 char *ofname;
254 unsigned char *sdata;
255 int of,newdir;
256 void *cumark;
257
258 arc_reset();
259 if ((hd=arc_seek())==NULL) error(1,ERR_NOFILES);
260 do {
261 if (usepath) {
262 makepath(hd->path);
263 ofname=md_tomdpath(fullpath(hd->path,hd->name));
264 }
265 else ofname=md_tomdpath(hd->name);
266 switch(hd->type) {
267 case M_SPECIAL:
268 if (!access(ofname,F_OK)) {
269 if (!yesno("\nOverwrite special file %s ? (y/n/a) ",ofname))
270 break;
271 }
272 if (!quiet) {
273 printf("\nMaking SPC %s",ofname);
274 backstep(strlen(ofname)+8);
275 }
276 if (hd->clen) {
277 if ((sdata=malloc(hd->clen))==NULL)
278 error(1,ERR_MEM,"do_extract()");
279 if (read(arcfile,sdata,hd->clen)!=hd->clen)
280 error(1,ERR_READ,arcname);
281 }
282 else sdata=NULL;
283 if (!md_mkspecial(ofname,hd->clen,sdata)) {
284 if (sdata!=NULL) free(sdata);
285 break;
286 }
287 if (sdata!=NULL) free(sdata);
288 if (touch) md_setft(ofname,md_systime());
289 else md_setft(ofname,hd->time);
290 if (!quiet) printf("DONE");
291 break;
292 case M_DIR:
293 if (!(newdir=access(ofname,F_OK)) && useattr) {
294 if (!yesno("\nRemake directory %s ? (y/n/a) ",ofname)) break;
295 }
296 if (!quiet) {
297 printf("\nMaking DIR %s",ofname);
298 backstep(strlen(ofname)+8);
299 }
300 if (newdir) {
301 if (mkdir(ofname,DEF_DIRATTR)<0) error(0,ERR_MKDIR,ofname);
302 }
303 if (touch) md_setft(ofname,md_systime());
304 else md_setft(ofname,hd->time);
305 if (useattr) md_setfattrs(ofname);
306 if (!quiet) printf("DONE");
307 break;
308 default:
309 of=open(ofname,O_WRONLY|O_BINARY|O_CREAT|O_EXCL,DEF_FILEATTR);
310 if (of<0) {
311 if (!yesno("\nOverwrite file %s ? (y/n/a) ",ofname)) continue;
312 if (remove(ofname)<0) {
313 error(0,ERR_REMOVE,ofname);
314 continue;
315 }
316 if ((of=open(ofname,O_WRONLY|O_BINARY|O_CREAT|O_EXCL,
317 DEF_FILEATTR))<0) error(0,ERR_OPEN,ofname);
318 }
319 setinput(arcfile,0,arcname);
320 if (quiet) setoutput(of,CRCCALC,ofname);
321 else setoutput(of,CRCCALC|PROGDISP,ofname);
322 if (!quiet) {
323 printf("\nUnpacking %s %s",
324 method[hd->type].name,ofname);
325 backstep(strlen(ofname)+8);
326 }
327 fflush(stdout);
328 if (hd->olen!=0) {
329 totalsize=hd->olen;
330 cumark=cu_add(CU_FUNC,method[hd->type].cleanup);
331 cu_add(CU_RMFILE|CU_CANRELAX,ofname,of);
332 (*method[hd->type].decode)();
333 cu_relax(cumark);
334 cu_do(cumark);
335 }
336 else if (!quiet) printf("100 %%");
337 fflush(stdout);
338 close(of);
339 if (hd->crc!=getcrc()) error(0,ERR_CRC,NULL);
340 if (touch) md_setft(ofname,md_systime());
341 else md_setft(ofname,hd->time);
342 if (useattr) md_setfattrs(ofname);
343 break;
344 }
345 } while ((hd=arc_seek())!=NULL);
346 if (!quiet) printf("\n");
347 }
348
do_test(void)349 static void do_test(void) {
350
351 Fheader *hd;
352 char *ofname;
353 void *cumark;
354
355 arc_reset();
356 if ((hd=arc_seek())==NULL) error(1,ERR_NOFILES);
357 do {
358 ofname=md_tomdpath(fullpath(hd->path,hd->name));
359 switch(hd->type) {
360 case M_DIR:
361 if (!quiet) printf("\nTesting DIR DONE %s",ofname);
362 break;
363 case M_SPECIAL:
364 if (!quiet) printf("\nTesting SPC DONE %s",ofname);
365 break;
366 default:
367 setinput(arcfile,0,arcname);
368 if (quiet) setoutput(-1,CRCCALC,"none ??");
369 else setoutput(-1,CRCCALC|PROGDISP,"none ??");
370 if (!quiet) {
371 printf("\nTesting %s %s",method[hd->type].name,ofname);
372 backstep(strlen(ofname)+8);
373 fflush(stdout);
374 }
375 if (hd->olen!=0) {
376 totalsize=hd->olen;
377 cumark=cu_add(CU_FUNC,method[hd->type].cleanup);
378 (*method[hd->type].decode)();
379 cu_do(cumark);
380 }
381 else if (!quiet) printf("100 %%");
382 fflush(stdout);
383 if (hd->crc!=getcrc()) error(0,ERR_CRC,NULL);
384 break;
385 }
386 } while ((hd=arc_seek())!=NULL);
387 if (!quiet) printf("\n");
388 }
389
do_delete(void)390 static void do_delete(void) {
391
392 Fheader *hd;
393
394 arc_reset();
395 if ((hd=arc_seek())==NULL) error(1,ERR_NOFILES);
396 do {
397 if (!quiet) {
398 printf("\nDeleting %s",md_tomdpath(fullpath(hd->path,hd->name)));
399 fflush(stdout);
400 }
401 arc_delete();
402 } while ((hd=arc_seek())!=NULL);
403 if (!quiet) printf("\n");
404 }
405
adddir(char * path,char * name)406 static int adddir(char *path, char *name) {
407
408 char *fullname;
409
410 fullname=md_pconcat(0,path,name);
411 if (!quiet) {
412 printf("\nSaving DIR %s",fullname);
413 backstep(strlen(fullname)+10);
414 fflush(stdout);
415 }
416 arc_newfile(usepath?path:"",name);
417 if (arc_adddir()){
418 if (!quiet) {
419 printf(" DONE");
420 fflush(stdout);
421 }
422 if (move) cu_add(CU_RMDIR,fullname);
423 free(fullname);
424 return 1;
425 }
426 else {
427 free(fullname);
428 return 0;
429 }
430 }
431
addspecial(char * path,char * name)432 static int addspecial(char *path, char *name) {
433
434 char *fullname;
435
436 fullname=md_pconcat(0,path,name);
437 if (!quiet) {
438 printf("\nSaving SPC %s",fullname);
439 backstep(strlen(fullname)+10);
440 fflush(stdout);
441 }
442 arc_newfile(usepath?path:"",name);
443 if (arc_addspecial(fullname)) {
444 if (!quiet) {
445 printf(" DONE");
446 fflush(stdout);
447 }
448 if (move) {
449 if (remove(fullname)<0) {
450 error(0,ERR_REMOVE,fullname);
451 }
452 }
453 free(fullname);
454 return 1;
455 }
456 else {
457 free(fullname);
458 return 0;
459 }
460 }
461
addfile(char * path,char * name)462 static int addfile(char *path, char *name) {
463
464 char *fullname;
465 int i,best,inf;
466 U32B bestsize;
467 void *cumark;
468
469 bestsize=totalsize=md_curfilesize();
470 best=M_CPY;
471 arc_newfile(usepath?path:"",name);
472 fullname=md_pconcat(0,path,name);
473 if ((inf=open(fullname,O_RDONLY|O_BINARY))<0) {
474 error(0,ERR_OPEN,fullname);
475 free(fullname);
476 return 0;
477 }
478 if (!quiet) printf("\n");
479 setoutput(arcfile,0,arcname);
480 if (quiet) setinput(inf,CRCCALC,fullname);
481 else setinput(inf,CRCCALC|PROGDISP,fullname);
482 if (totalsize) {
483 for (i=0;;) {
484 arc_trynext();
485 cumark=cu_add(CU_FUNC,method[metqueue[i]].cleanup);
486 if (!quiet) {
487 printf("\rPacking %s %s",
488 method[metqueue[i]].name,fullname);
489 backstep(strlen(fullname)+10);
490 fflush(stdout);
491 }
492 (*method[metqueue[i]].encode)();
493 cu_do(cumark);
494 if (ocnt<bestsize || metqueue[i]==M_CPY) {
495 arc_accept(best=metqueue[i]);
496 bestsize=ocnt;
497 }
498 if (metqueue[++i]==M_UNK ||
499 (metqueue[i]==M_CPY && bestsize!=totalsize)) break;
500 setoutput(arcfile,0,arcname);
501 lseek(inf,0,SEEK_SET);
502 if (quiet) setinput(inf,CRCCALC,fullname);
503 else setinput(inf,CRCCALC|PROGDISP,fullname);
504 }
505 }
506 else {
507 if (!quiet) {
508 printf("\rPacking CPY %s",fullname);
509 backstep(strlen(fullname)+10);
510 fflush(stdout);
511 }
512 arc_accept(M_CPY);
513 }
514 if (!quiet) {
515 backstep(5);
516 printf("%s %3d.%d %%",method[best].name,
517 (bestsize==0?100:(int)(bestsize*100/totalsize)),
518 (bestsize==0?0:(int)((bestsize*1000/totalsize)%10)));
519 }
520 fflush(stdout);
521 arc_addfile();
522 if (move) {
523 if (remove(fullname)<0) {
524 error(0,ERR_REMOVE,fullname);
525 }
526 }
527 free(fullname);
528 close(inf);
529 return 1;
530 }
531
addtest(char * path,char * name)532 static int addtest(char* path, char* name) {
533
534 return 1;
535 }
536
freshentest(char * path,char * name)537 static int freshentest(char *path, char *name) {
538
539 Fheader *hd;
540 char *ptab[1];
541
542 ptab[0]=fullpath(md_tohapath(path),name);
543 patterns=ptab;
544 patcnt=1;
545 arc_reset();
546 if ((hd=arc_seek())==NULL || hd->time>=md_curfiletime()) return 0;
547 return 1;
548 }
549
updatetest(char * path,char * name)550 static int updatetest(char *path, char *name) {
551
552 Fheader *hd;
553 char *ptab[1];
554
555 ptab[0]=fullpath(md_tohapath(path),name);
556 patterns=ptab;
557 patcnt=1;
558 arc_reset();
559 if ((hd=arc_seek())!=NULL && hd->time>=md_curfiletime()) return 0;
560 return 1;
561 }
562
addindir(char * path,char * pattern)563 static int addindir(char *path, char *pattern) {
564
565 int found;
566 char *newpath;
567 DIR *dir;
568 struct dirent *ent;
569 void *cumark;
570
571 found=0;
572 if (*path) dir=opendir(path);
573 else dir=opendir(".");
574 if (dir==NULL) {
575 error(0,ERR_DIROPEN,path);
576 return found;
577 }
578 cumark=cu_getmark();
579 while ((ent=readdir(dir))!=NULL) {
580 switch(md_filetype(path,md_strcase(ent->d_name))) {
581 case T_DIR:
582 if (savedir && addthis(path,ent->d_name))
583 found|=adddir(path,ent->d_name);
584 if (!recurse) break;
585 newpath=md_pconcat(1,path,ent->d_name);
586 found|=addindir(newpath,pattern);
587 free(newpath);
588 break;
589 case T_SPECIAL:
590 if (!md_namecmp(pattern,ent->d_name) || !special) break;
591 if (addthis(path,ent->d_name)) found|=addspecial(path,ent->d_name);
592 break;
593 case T_REGULAR:
594 if (!md_namecmp(pattern,ent->d_name)) break;
595 if (addthis(path,ent->d_name)) found|=addfile(path,ent->d_name);
596 break;
597 }
598 }
599 closedir(dir);
600 cu_do(cumark);
601 return found;
602 }
603
do_add(void)604 static void do_add(void) {
605
606 int i,found;
607 char *path,*pattern;
608
609 for (found=i=0;i<patcnt;++i) {
610 path=md_strippath(patterns[i]);
611 pattern=md_stripname(patterns[i]);
612 found|=addindir(md_strcase(path),md_strcase(pattern));
613 }
614 if (!quiet) {
615 if (found) printf("\n");
616 else printf("\nNothing to do\n");
617 fflush(stdout);
618 }
619 }
620
switchparse(char * s,char * valid)621 static void switchparse(char *s, char *valid) {
622
623 int i;
624
625 while (*s) {
626 if (strchr(valid,tolower(*s))==NULL) error(1,ERR_INVSW,*s);
627 switch (tolower(*s)) {
628 case 'q':
629 quiet=1;
630 break;
631 case 'y':
632 yes=1;
633 break;
634 case 'f':
635 fulllist=1;
636 break;
637 case 'a':
638 useattr=1;
639 break;
640 case 't':
641 touch=1;
642 break;
643 case 'r':
644 recurse=1;
645 break;
646 case 's':
647 special=1;
648 break;
649 case 'd':
650 savedir=1;
651 break;
652 case 'e':
653 usepath=0;
654 break;
655 case 'm':
656 move=1;
657 break;
658 case '0':
659 case '1':
660 case '2':
661 for (*s-='0',i=0;i<M_UNK;++i) {
662 if (metqueue[i]==*s) break;
663 else if (metqueue[i]==M_UNK) {
664 metqueue[i]=*s;
665 metqueue[i+1]=M_UNK;
666 break;
667 }
668 }
669 break;
670 default :
671 error(1,ERR_INVSW,NULL,*s);
672 break;
673 }
674 ++s;
675 }
676 if (!quiet) banner();
677 }
678
fix_methods(void)679 static void fix_methods(void) {
680
681 int i;
682
683 if (metqueue[0]==M_UNK) {
684 metqueue[0]=M_ASC;
685 metqueue[1]=M_CPY;
686 metqueue[2]=M_UNK;
687 }
688 else {
689 for (i=0;metqueue[i]!=M_UNK;++i) {
690 if (metqueue[i]==M_CPY) break;
691 }
692 if (metqueue[i]==M_UNK) {
693 metqueue[i]=M_CPY;
694 metqueue[i+1]=M_UNK;
695 }
696 }
697 }
698
parse_cmds(char * cs[])699 static void (*parse_cmds(char *cs[]))(void) {
700
701 void (*cmd)(void)=do_list;
702
703 switch(tolower(cs[0][0])) {
704 case ADD:
705 switchparse(cs[0]+1,"sdqemr012");
706 if (!usepath) savedir=0;
707 arc_open(cs[1],ARC_OLD|ARC_NEW);
708 addthis=addtest;
709 cmd=do_add;
710 fix_methods();
711 break;
712 case FRESHEN:
713 switchparse(cs[0]+1,"sdqemr012");
714 if (!usepath) savedir=0;
715 arc_open(cs[1],ARC_OLD);
716 addthis=freshentest;
717 cmd=do_add;
718 fix_methods();
719 sloppymatch=0;
720 break;
721 case UPDATE:
722 switchparse(cs[0]+1,"sdqemr012");
723 if (!usepath) savedir=0;
724 arc_open(cs[1],ARC_OLD|ARC_NEW);
725 addthis=updatetest;
726 cmd=do_add;
727 fix_methods();
728 sloppymatch=0;
729 break;
730 case EXTRACT:
731 usepath=0;
732 case PEXTRACT:
733 switchparse(cs[0]+1,"aqty");
734 arc_open(cs[1],ARC_OLD|ARC_RDO);
735 cmd=do_extract;
736 break;
737 case TEST:
738 switchparse(cs[0]+1,"qe");
739 arc_open(cs[1],ARC_OLD|ARC_RDO);
740 cmd=do_test;
741 break;
742 case LIST:
743 switchparse(cs[0]+1,"f");
744 arc_open(cs[1],ARC_OLD|ARC_RDO);
745 cmd=do_list;
746 break;
747 case DELETE:
748 switchparse(cs[0]+1,"qe");
749 arc_open(cs[1],ARC_OLD);
750 cmd=do_delete;
751 break;
752 case INFO:
753 info();
754 break;
755 default:
756 usage(ERR_UNKNOWN);
757 }
758 return cmd;
759 }
760
main(int argc,char * argv[])761 int main(int argc, char *argv[]) {
762
763 void (*command)(void);
764
765 myname=argv[0];
766 md_init();
767 if (argc<2) usage(ERR_UNKNOWN);
768 if (argc==4 && strcmp(argv[1],"MAKEINFO")==0) makeinfo(argv[2],argv[3]);
769 command=parse_cmds(argv+1);
770 testsizes();
771 switch (argc) {
772 case 2:
773 if (command!=info) usage(ERR_UNKNOWN);
774 break;
775 case 3:
776 patterns=defpat;
777 patcnt=1;
778 break;
779 default :
780 patterns=argv+3;
781 patcnt=argc-3;
782 break;
783 }
784 command();
785 cu_do(NULL);
786 return lasterror;
787 }
788
789
790