Commit | Line | Data |
---|---|---|
2f5a0e7a KM |
1 | /*- |
2 | * Copyright (c) 1992 Keith Muller. | |
3 | * Copyright (c) 1992 The Regents of the University of California. | |
4 | * All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Keith Muller of the University of California, San Diego. | |
8 | * | |
9 | * %sccs.include.redist.c% | |
10 | */ | |
11 | ||
12 | #ifndef lint | |
e2e6a850 | 13 | static char sccsid[] = "@(#)options.c 1.2 (Berkeley) %G%"; |
2f5a0e7a KM |
14 | #endif /* not lint */ |
15 | ||
16 | #include <sys/types.h> | |
17 | #include <sys/time.h> | |
18 | #include <sys/stat.h> | |
19 | #include <sys/mtio.h> | |
20 | #include <sys/param.h> | |
21 | #include <stdio.h> | |
22 | #include <ctype.h> | |
23 | #include <string.h> | |
24 | #include <unistd.h> | |
25 | #include <stdlib.h> | |
26 | #include <limits.h> | |
27 | #include "pax.h" | |
28 | #include "options.h" | |
29 | #include "cpio.h" | |
30 | #include "extern.h" | |
31 | ||
32 | /* | |
33 | * Routines which handle command line options | |
34 | */ | |
35 | ||
36 | static char flgch[] = FLGCH; /* list of all possible flags */ | |
37 | static OPLIST *ophead = NULL; /* head for format specific options -x */ | |
38 | static OPLIST *optail = NULL; /* option tail */ | |
39 | ||
40 | static int no_op __P((void)); | |
41 | static void printflg __P((unsigned int)); | |
42 | static int c_frmt __P((const void *, const void *)); | |
43 | static off_t str_offt __P((char *)); | |
44 | ||
45 | /* | |
46 | * Format specific routine table - MUST BE IN SORTED ORDER BY NAME | |
47 | * (see pax.h for description of each function) | |
48 | * | |
49 | * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read, | |
50 | * read, end_read, st_write, write, end_write, trail, | |
51 | * rd_data, wr_data, options | |
52 | */ | |
53 | ||
54 | FSUB fsub[] = { | |
55 | /* 0: OLD BINARY CPIO */ | |
56 | "bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd, | |
57 | bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, | |
58 | rd_wrfile, wr_rdfile, bad_opt, | |
59 | ||
60 | /* 1: OLD OCTAL CHARACTER CPIO */ | |
61 | "cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd, | |
62 | cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, | |
63 | rd_wrfile, wr_rdfile, bad_opt, | |
64 | ||
65 | /* 2: SVR4 HEX CPIO */ | |
66 | "sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd, | |
67 | vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, | |
68 | rd_wrfile, wr_rdfile, bad_opt, | |
69 | ||
70 | /* 3: SVR4 HEX CPIO WITH CRC */ | |
71 | "sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd, | |
72 | vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, | |
73 | rd_wrfile, wr_rdfile, bad_opt, | |
74 | ||
75 | /* 4: OLD TAR */ | |
76 | "tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op, | |
77 | tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail, | |
78 | rd_wrfile, wr_rdfile, tar_opt, | |
79 | ||
80 | /* 5: POSIX USTAR */ | |
81 | "ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd, | |
82 | ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail, | |
83 | rd_wrfile, wr_rdfile, bad_opt, | |
84 | }; | |
85 | #define DEFLT 5 /* default write format from list above */ | |
86 | ||
87 | /* | |
88 | * ford is the archive search order used by get_arc() to determine what kind | |
89 | * of archive we are dealing with. This helps to properly id archive formats | |
90 | * some formats may be subsets of others.... | |
91 | */ | |
92 | int ford[] = {5, 4, 3, 2, 1, 0, -1 }; | |
93 | ||
94 | /* | |
95 | * options() | |
96 | * look at the user specified flags. set globals as required and check if | |
97 | * the user specified a legal set of flags. If not, complain and exit | |
98 | */ | |
99 | ||
100 | #if __STDC__ | |
101 | void | |
102 | options(register int argc, register char **argv) | |
103 | #else | |
104 | void | |
105 | options(argc, argv) | |
106 | register int argc; | |
107 | register char **argv; | |
108 | #endif | |
109 | { | |
110 | register int c; | |
111 | register int i; | |
112 | unsigned int flg = 0; | |
113 | unsigned int bflg = 0; | |
114 | register char *pt; | |
115 | FSUB tmp; | |
116 | extern char *optarg; | |
117 | extern int optind; | |
118 | ||
119 | /* | |
120 | * process option flags | |
121 | */ | |
e2e6a850 | 122 | while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:B:DE:G:HLT:U:XYZ")) |
2f5a0e7a KM |
123 | != EOF) { |
124 | switch (c) { | |
125 | case 'a': | |
126 | /* | |
127 | * append | |
128 | */ | |
129 | flg |= AF; | |
130 | break; | |
131 | case 'b': | |
132 | /* | |
133 | * specify blocksize on write | |
134 | */ | |
135 | flg |= BF; | |
136 | if ((wrblksz = (int)str_offt(optarg)) <= 0) { | |
137 | warn(1, "Invalid block size %s", optarg); | |
138 | usage(); | |
139 | } | |
140 | break; | |
141 | case 'c': | |
142 | /* | |
143 | * inverse match on patterns | |
144 | */ | |
145 | cflag = 1; | |
146 | flg |= CF; | |
147 | break; | |
148 | case 'd': | |
149 | /* | |
150 | * match only dir on extract, not the subtree at dir | |
151 | */ | |
152 | dflag = 1; | |
153 | flg |= DF; | |
154 | break; | |
155 | case 'f': | |
156 | /* | |
157 | * filename where the archive is stored | |
158 | */ | |
159 | arcname = optarg; | |
160 | flg |= FF; | |
161 | break; | |
162 | case 'i': | |
163 | /* | |
164 | * interactive file rename | |
165 | */ | |
166 | iflag = 1; | |
167 | flg |= IF; | |
168 | break; | |
169 | case 'k': | |
170 | /* | |
171 | * do not clobber files that exist | |
172 | */ | |
173 | kflag = 1; | |
174 | flg |= KF; | |
175 | break; | |
176 | case 'l': | |
177 | /* | |
178 | * try to link src to dest with copy (-rw) | |
179 | */ | |
180 | lflag = 1; | |
181 | flg |= LF; | |
182 | break; | |
183 | case 'n': | |
184 | /* | |
185 | * select first match for a pattern only | |
186 | */ | |
187 | nflag = 1; | |
188 | flg |= NF; | |
189 | break; | |
190 | case 'o': | |
191 | /* | |
192 | * pass format specific options | |
193 | */ | |
194 | flg |= OF; | |
195 | if (opt_add(optarg) < 0) | |
196 | usage(); | |
197 | break; | |
198 | case 'p': | |
199 | /* | |
200 | * specify file characteristic options | |
201 | */ | |
202 | for (pt = optarg; *pt != '\0'; ++pt) { | |
203 | switch(*pt) { | |
204 | case 'a': | |
205 | /* | |
206 | * do not preserve access time | |
207 | */ | |
208 | patime = 0; | |
209 | break; | |
210 | case 'e': | |
211 | /* | |
212 | * preserve user id, group id, file | |
213 | * mode, access/modification times | |
214 | */ | |
215 | pids = 1; | |
216 | pmode = 1; | |
217 | patime = 1; | |
218 | pmtime = 1; | |
219 | break; | |
220 | case 'm': | |
221 | /* | |
222 | * do not preserve modification time | |
223 | */ | |
224 | pmtime = 0; | |
225 | break; | |
226 | case 'o': | |
227 | /* | |
228 | * preserve uid/gid | |
229 | */ | |
230 | pids = 1; | |
231 | break; | |
232 | case 'p': | |
233 | /* | |
234 | * preserver file mode bits | |
235 | */ | |
236 | pmode = 1; | |
237 | break; | |
238 | default: | |
239 | warn(1, "Invalid -p string: %c", *pt); | |
240 | usage(); | |
241 | break; | |
242 | } | |
243 | } | |
244 | flg |= PF; | |
245 | break; | |
246 | case 'r': | |
247 | /* | |
248 | * read the archive | |
249 | */ | |
250 | flg |= RF; | |
251 | break; | |
252 | case 's': | |
253 | /* | |
254 | * file name substitution name pattern | |
255 | */ | |
256 | if (rep_add(optarg) < 0) { | |
257 | usage(); | |
258 | break; | |
259 | } | |
260 | flg |= SF; | |
261 | break; | |
262 | case 't': | |
263 | /* | |
264 | * preserve access time on filesystem nodes we read | |
265 | */ | |
266 | tflag = 1; | |
267 | flg |= TF; | |
268 | break; | |
269 | case 'u': | |
270 | /* | |
271 | * ignore those older files | |
272 | */ | |
273 | uflag = 1; | |
274 | flg |= UF; | |
275 | break; | |
276 | case 'v': | |
277 | /* | |
278 | * verbose operation mode | |
279 | */ | |
280 | vflag = 1; | |
281 | flg |= VF; | |
282 | break; | |
283 | case 'w': | |
284 | /* | |
285 | * write an archive | |
286 | */ | |
287 | flg |= WF; | |
288 | break; | |
289 | case 'x': | |
290 | /* | |
291 | * specify an archive format on write | |
292 | */ | |
293 | tmp.name = optarg; | |
294 | if (frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, | |
295 | sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) { | |
296 | flg |= XF; | |
297 | break; | |
298 | } | |
299 | warn(1, "Unknown -x format: %s", optarg); | |
300 | (void)fputs("pax: Known -x formats are:", stderr); | |
301 | for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) | |
302 | (void)fprintf(stderr, " %s", fsub[i].name); | |
303 | (void)fputs("\n\n", stderr); | |
304 | usage(); | |
305 | break; | |
306 | case 'B': | |
307 | /* | |
308 | * non-standard option on number of bytes written on a | |
309 | * single archive volume. | |
310 | */ | |
311 | if ((wrlimit = str_offt(optarg)) <= 0) { | |
312 | warn(1, "Invalid write limit %s", optarg); | |
313 | usage(); | |
314 | } | |
315 | if (wrlimit % BLKMULT) { | |
316 | warn(1, "Write limit is not a %d byte multiple", | |
317 | BLKMULT); | |
318 | usage(); | |
319 | } | |
320 | flg |= CBF; | |
321 | break; | |
e2e6a850 KM |
322 | case 'D': |
323 | /* | |
324 | * On extraction check file inode change time before the | |
325 | * modification of the file name. Non standard option. | |
326 | */ | |
327 | Dflag = 1; | |
328 | flg |= CDF; | |
329 | break; | |
2f5a0e7a KM |
330 | case 'E': |
331 | /* | |
332 | * non-standard limit on read faults | |
333 | * 0 indicates stop after first error, values | |
334 | * indicate a limit, "NONE" try forever | |
335 | */ | |
336 | flg |= CEF; | |
337 | if (strcmp(NONE, optarg) == 0) | |
338 | maxflt = -1; | |
339 | else if ((maxflt = atoi(optarg)) < 0) { | |
340 | warn(1, "Error count value must be positive"); | |
341 | usage(); | |
342 | } | |
343 | break; | |
344 | case 'G': | |
345 | /* | |
346 | * non-standard option for selecting files within an | |
347 | * archive by group (gid or name) | |
348 | */ | |
349 | if (grp_add(optarg) < 0) { | |
350 | usage(); | |
351 | break; | |
352 | } | |
353 | flg |= CGF; | |
354 | break; | |
355 | case 'H': | |
356 | /* | |
357 | * follow command line symlinks only | |
358 | */ | |
359 | Hflag = 1; | |
360 | flg |= CHF; | |
361 | break; | |
362 | case 'L': | |
363 | /* | |
364 | * follow symlinks | |
365 | */ | |
366 | Lflag = 1; | |
367 | flg |= CLF; | |
368 | break; | |
369 | case 'T': | |
370 | /* | |
371 | * non-standard option for selecting files within an | |
372 | * archive by modification time range (lower,upper) | |
373 | */ | |
374 | if (trng_add(optarg) < 0) { | |
375 | usage(); | |
376 | break; | |
377 | } | |
378 | flg |= CTF; | |
379 | break; | |
380 | case 'U': | |
381 | /* | |
382 | * non-standard option for selecting files within an | |
383 | * archive by user (uid or name) | |
384 | */ | |
385 | if (usr_add(optarg) < 0) { | |
386 | usage(); | |
387 | break; | |
388 | } | |
389 | flg |= CUF; | |
390 | break; | |
391 | case 'X': | |
392 | /* | |
393 | * do not pass over mount points in the file system | |
394 | */ | |
395 | Xflag = 1; | |
396 | flg |= CXF; | |
397 | break; | |
e2e6a850 KM |
398 | case 'Y': |
399 | /* | |
400 | * On extraction check file inode change time after the | |
401 | * modification of the file name. Non standard option. | |
402 | */ | |
403 | Yflag = 1; | |
404 | flg |= CYF; | |
405 | break; | |
2f5a0e7a KM |
406 | case 'Z': |
407 | /* | |
e2e6a850 KM |
408 | * On extraction check modification time after the |
409 | * modification of the file name. Non standard option. | |
2f5a0e7a KM |
410 | */ |
411 | Zflag = 1; | |
412 | flg |= CZF; | |
413 | break; | |
414 | case '?': | |
415 | default: | |
416 | usage(); | |
417 | break; | |
418 | } | |
419 | } | |
420 | ||
421 | /* | |
422 | * figure out the operation mode of pax read,write,extract,copy,append | |
423 | * or list. check that we have not been given a bogus set of flags | |
424 | * for the operation mode. | |
425 | */ | |
426 | if (ISLIST(flg)) { | |
427 | act = LIST; | |
428 | bflg = flg & BDLIST; | |
429 | } else if (ISEXTRACT(flg)) { | |
430 | act = EXTRACT; | |
431 | bflg = flg & BDEXTR; | |
432 | } else if (ISARCHIVE(flg)) { | |
433 | act = ARCHIVE; | |
434 | bflg = flg & BDARCH; | |
435 | } else if (ISAPPND(flg)) { | |
436 | act = APPND; | |
437 | bflg = flg & BDARCH; | |
438 | } else if (ISCOPY(flg)) { | |
439 | act = COPY; | |
440 | bflg = flg & BDCOPY; | |
441 | } else | |
442 | usage(); | |
443 | if (bflg) { | |
444 | printflg(flg); | |
445 | usage(); | |
446 | } | |
447 | ||
448 | /* | |
449 | * if we are writing (ARCHIVE) we use the default format if the user | |
450 | * did not specify a format. when we write during an APPEND, we will | |
451 | * adopt the format of the existing archive if none was supplied. | |
452 | */ | |
453 | if (!(flg & XF) && (act == ARCHIVE)) | |
454 | frmt = &(fsub[DEFLT]); | |
455 | ||
456 | /* | |
457 | * process the args as they are interpreted by the operation mode | |
458 | */ | |
459 | switch (act) { | |
460 | case LIST: | |
461 | case EXTRACT: | |
462 | for (; optind < argc; optind++) | |
463 | if (pat_add(argv[optind]) < 0) | |
464 | usage(); | |
465 | break; | |
466 | case COPY: | |
467 | if (optind >= argc) { | |
468 | warn(0, "Destination directory was not supplied"); | |
469 | usage(); | |
470 | } | |
471 | --argc; | |
472 | dirptr = argv[argc]; | |
473 | /* FALL THROUGH */ | |
474 | case ARCHIVE: | |
475 | case APPND: | |
476 | for (; optind < argc; optind++) | |
477 | if (ftree_add(argv[optind]) < 0) | |
478 | usage(); | |
479 | /* | |
480 | * no read errors allowed on updates/append operation! | |
481 | */ | |
482 | maxflt = 0; | |
483 | break; | |
484 | } | |
485 | } | |
486 | ||
487 | /* | |
488 | * printflg() | |
489 | * print out those invalid flag sets found to the user | |
490 | */ | |
491 | ||
492 | #if __STDC__ | |
493 | static void | |
494 | printflg(unsigned int flg) | |
495 | #else | |
496 | static void | |
497 | printflg(flg) | |
498 | unsigned int flg; | |
499 | #endif | |
500 | { | |
501 | int nxt; | |
502 | int pos = 0; | |
503 | ||
504 | (void)fputs("pax: Invalid combination of options:", stderr); | |
505 | while (nxt = ffs(flg)) { | |
506 | flg = flg >> nxt; | |
507 | pos += nxt; | |
508 | (void)fprintf(stderr, " -%c", flgch[pos-1]); | |
509 | } | |
510 | (void)putc('\n', stderr); | |
511 | } | |
512 | ||
513 | /* | |
514 | * c_frmt() | |
515 | * comparison routine used by bsearch to find the format specified | |
516 | * by the user | |
517 | */ | |
518 | ||
519 | #if __STDC__ | |
520 | static int | |
521 | c_frmt(const void *a, const void *b) | |
522 | #else | |
523 | static int | |
524 | c_frmt(a, b) | |
525 | void *a; | |
526 | void *b; | |
527 | #endif | |
528 | { | |
529 | return(strcmp(((FSUB *)a)->name, ((FSUB *)b)->name)); | |
530 | } | |
531 | ||
532 | /* | |
533 | * opt_next() | |
534 | * called by format specific options routines to get each format specific | |
535 | * flag and value specified with -o | |
536 | * Return: | |
537 | * pointer to next OPLIST entry or NULL (end of list). | |
538 | */ | |
539 | ||
540 | #if __STDC__ | |
541 | OPLIST * | |
542 | opt_next(void) | |
543 | #else | |
544 | OPLIST * | |
545 | opt_next() | |
546 | #endif | |
547 | { | |
548 | OPLIST *opt; | |
549 | ||
550 | if ((opt = ophead) != NULL) | |
551 | ophead = ophead->fow; | |
552 | return(opt); | |
553 | } | |
554 | ||
555 | /* | |
556 | * bad_opt() | |
557 | * generic routine used to complain about a format specific options | |
558 | * when the format does not support options. | |
559 | */ | |
560 | ||
561 | #if __STDC__ | |
562 | int | |
563 | bad_opt(void) | |
564 | #else | |
565 | int | |
566 | bad_opt() | |
567 | #endif | |
568 | { | |
569 | register OPLIST *opt; | |
570 | ||
571 | if (ophead == NULL) | |
572 | return(0); | |
573 | /* | |
574 | * print all we were given | |
575 | */ | |
576 | warn(1,"These format options are not supported"); | |
577 | while ((opt = opt_next()) != NULL) | |
578 | (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); | |
579 | usage(); | |
580 | return(0); | |
581 | } | |
582 | ||
583 | /* | |
584 | * opt_add() | |
585 | * breaks the value supplied to -o into a option name and value. options | |
586 | * are given to -o in the form -o name-value,name=value | |
587 | * mulltiple -o may be specified. | |
588 | * Return: | |
589 | * 0 if format in name=value format, -1 if -o is passed junk | |
590 | */ | |
591 | ||
592 | #if __STDC__ | |
593 | int | |
594 | opt_add(register char *str) | |
595 | #else | |
596 | int | |
597 | opt_add(str) | |
598 | register char *str; | |
599 | #endif | |
600 | { | |
601 | register OPLIST *opt; | |
602 | register char *frpt; | |
603 | register char *pt; | |
604 | register char *endpt; | |
605 | ||
606 | if ((str == NULL) || (*str == '\0')) { | |
607 | warn(0, "Invalid option name"); | |
608 | return(-1); | |
609 | } | |
610 | frpt = endpt = str; | |
611 | ||
612 | /* | |
613 | * break into name and values pieces and stuff each one into a | |
614 | * OPLIST structure. When we know the format, the format specific | |
615 | * option function will go through this list | |
616 | */ | |
617 | while ((frpt != NULL) && (*frpt != '\0')) { | |
618 | if ((endpt = strchr(frpt, ',')) != NULL) | |
619 | *endpt = '\0'; | |
620 | if ((pt = strchr(frpt, '=')) == NULL) { | |
621 | warn(0, "Invalid options format"); | |
622 | return(-1); | |
623 | } | |
624 | if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { | |
625 | warn(0, "Unable to allocate space for option list"); | |
626 | return(-1); | |
627 | } | |
628 | *pt++ = '\0'; | |
629 | opt->name = frpt; | |
630 | opt->value = pt; | |
631 | opt->fow = NULL; | |
632 | if (endpt != NULL) | |
633 | frpt = endpt + 1; | |
634 | else | |
635 | frpt = NULL; | |
636 | if (ophead == NULL) { | |
637 | optail = ophead = opt; | |
638 | continue; | |
639 | } | |
640 | optail->fow = opt; | |
641 | optail = opt; | |
642 | } | |
643 | return(0); | |
644 | } | |
645 | ||
646 | /* | |
647 | * str_offt() | |
648 | * Convert an expression of the following forms to an off_t > 0. | |
649 | * 1) A positive decimal number. | |
650 | * 2) A positive decimal number followed by a b (mult by 512). | |
651 | * 3) A positive decimal number followed by a k (mult by 1024). | |
652 | * 4) A positive decimal number followed by a m (mult by 512). | |
653 | * 5) A positive decimal number followed by a w (mult by sizeof int) | |
654 | * 6) Two or more positive decimal numbers (with/without k,b or w). | |
655 | * seperated by x (also * for backwards compatibility), specifying | |
656 | * the product of the indicated values. | |
657 | * Return: | |
658 | * 0 for an error, a positive value o.w. | |
659 | */ | |
660 | ||
661 | #if __STDC__ | |
662 | static off_t | |
663 | str_offt(char *val) | |
664 | #else | |
665 | static off_t | |
666 | str_offt(val) | |
667 | char *val; | |
668 | #endif | |
669 | { | |
670 | char *expr; | |
671 | off_t num, t; | |
672 | ||
673 | # ifdef NET2_STAT | |
674 | num = strtol(val, &expr, 0); | |
675 | if ((num == LONG_MAX) || (num <= 0) || (expr == val)) | |
676 | # else | |
677 | num = strtoq(val, &expr, 0); | |
678 | if ((num == QUAD_MAX) || (num <= 0) || (expr == val)) | |
679 | # endif | |
680 | return(0); | |
681 | ||
682 | switch(*expr) { | |
683 | case 'b': | |
684 | t = num; | |
685 | num *= 512; | |
686 | if (t > num) | |
687 | return(0); | |
688 | ++expr; | |
689 | break; | |
690 | case 'k': | |
691 | t = num; | |
692 | num *= 1024; | |
693 | if (t > num) | |
694 | return(0); | |
695 | ++expr; | |
696 | break; | |
697 | case 'm': | |
698 | t = num; | |
699 | num *= 1048576; | |
700 | if (t > num) | |
701 | return(0); | |
702 | ++expr; | |
703 | break; | |
704 | case 'w': | |
705 | t = num; | |
706 | num *= sizeof(int); | |
707 | if (t > num) | |
708 | return(0); | |
709 | ++expr; | |
710 | break; | |
711 | } | |
712 | ||
713 | switch(*expr) { | |
714 | case '\0': | |
715 | break; | |
716 | case '*': | |
717 | case 'x': | |
718 | t = num; | |
719 | num *= str_offt(expr + 1); | |
720 | if (t > num) | |
721 | return(0); | |
722 | break; | |
723 | default: | |
724 | return(0); | |
725 | } | |
726 | return(num); | |
727 | } | |
728 | ||
729 | /* | |
730 | * no_op() | |
731 | * for those option functions where the archive format has nothing to do. | |
732 | * Return: | |
733 | * 0 | |
734 | */ | |
735 | ||
736 | #if __STDC__ | |
737 | static int | |
738 | no_op(void) | |
739 | #else | |
740 | static int | |
741 | no_op() | |
742 | #endif | |
743 | { | |
744 | return(0); | |
745 | } |