1 |
/* |
2 |
* main.c -- entry point and argument parsing for the UAR program |
3 |
* |
4 |
* This program is part of the UAR (Universal Archive) utility suite. |
5 |
* Copyright (C) 2024 OSN, Inc. |
6 |
* Author: Ar Rakin <[email protected]> |
7 |
* |
8 |
* This program is free software: you can redistribute it and/or modify |
9 |
* it under the terms of the GNU General Public License as published by |
10 |
* the Free Software Foundation, either version 3 of the License, or |
11 |
* (at your option) any later version. |
12 |
* |
13 |
* This program is distributed in the hope that it will be useful, |
14 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 |
* GNU General Public License for more details. |
17 |
* |
18 |
* You should have received a copy of the GNU General Public License |
19 |
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
20 |
*/ |
21 |
|
22 |
#define _XOPEN_SOURCE 500 |
23 |
|
24 |
#include <assert.h> |
25 |
#include <errno.h> |
26 |
#include <getopt.h> |
27 |
#include <grp.h> |
28 |
#include <libgen.h> |
29 |
#include <limits.h> |
30 |
#include <linux/limits.h> |
31 |
#include <math.h> |
32 |
#include <pwd.h> |
33 |
#include <stdarg.h> |
34 |
#include <stdbool.h> |
35 |
#include <stdio.h> |
36 |
#include <stdlib.h> |
37 |
#include <string.h> |
38 |
#include <strings.h> |
39 |
#include <sys/stat.h> |
40 |
#include <time.h> |
41 |
#include <unistd.h> |
42 |
|
43 |
#include "uar.h" |
44 |
#include "xmalloc.h" |
45 |
|
46 |
#ifdef HAVE_CONFIG_H |
47 |
# include "config.h" |
48 |
#else |
49 |
# define PACKAGE_NAME "uar" |
50 |
# define PACKAGE_VERSION "1.0" |
51 |
# define PACKAGE_BUGREPORT "[email protected]" |
52 |
#endif |
53 |
|
54 |
#ifndef NDEBUG |
55 |
# define debug(...) pdebug (__FILE__, __LINE__, __VA_ARGS__) |
56 |
#else |
57 |
# define debug(...) |
58 |
#endif |
59 |
|
60 |
/* Command line options. */ |
61 |
static struct option const long_options[] = { |
62 |
{ "create", no_argument, NULL, 'c' }, |
63 |
{ "extract", no_argument, NULL, 'x' }, |
64 |
{ "human-readable", no_argument, NULL, 'm' }, |
65 |
{ "list", no_argument, NULL, 't' }, |
66 |
{ "verbose", no_argument, NULL, 'v' }, |
67 |
{ "file", required_argument, NULL, 'f' }, |
68 |
{ "directory", required_argument, NULL, 'C' }, |
69 |
{ "help", no_argument, NULL, 'h' }, |
70 |
{ "version", no_argument, NULL, 'V' }, |
71 |
{ NULL, 0, NULL, 0 }, |
72 |
}; |
73 |
|
74 |
static char const short_options[] = "cxtvmf:C:hV"; |
75 |
|
76 |
/* Program name. */ |
77 |
static char *progname = NULL; |
78 |
|
79 |
/* Flags for the command line options. */ |
80 |
enum uar_mode |
81 |
{ |
82 |
MODE_NONE = 0, |
83 |
MODE_CREATE, |
84 |
MODE_EXTRACT, |
85 |
MODE_LIST |
86 |
}; |
87 |
|
88 |
struct uar_params |
89 |
{ |
90 |
enum uar_mode mode; |
91 |
bool verbose; |
92 |
bool hr_sizes; |
93 |
char *file; |
94 |
char *cwd; |
95 |
char **targets; |
96 |
char **rtargets; |
97 |
size_t ntargets; |
98 |
}; |
99 |
|
100 |
static struct uar_params params = { 0 }; |
101 |
|
102 |
/* Print usage information. */ |
103 |
static void |
104 |
usage (void) |
105 |
{ |
106 |
printf ("Usage:\n"); |
107 |
printf (" uar [OPTION]... [FILE]...\n"); |
108 |
printf ("\n"); |
109 |
printf ("Universal Archive utility.\n"); |
110 |
printf ("\n"); |
111 |
printf ("Options:\n"); |
112 |
printf (" -c, --create Create a new archive\n"); |
113 |
printf (" -x, --extract Extract files from an archive\n"); |
114 |
printf (" -t, --list List the contents of an archive\n"); |
115 |
printf (" -m, --human-readable Print human-readable sizes\n"); |
116 |
printf (" -v, --verbose Verbose mode\n"); |
117 |
printf ( |
118 |
" -f, --file=ARCHIVE Use archive file or directory ARCHIVE\n"); |
119 |
printf (" -C, --directory=DIR Change to directory DIR\n"); |
120 |
printf (" -h, --help Display this help and exit\n"); |
121 |
printf (" -V, --version Output version information and exit\n"); |
122 |
printf ("\n"); |
123 |
printf ("Report bugs to: <" PACKAGE_BUGREPORT ">\n"); |
124 |
} |
125 |
|
126 |
/* Print version information. */ |
127 |
static void |
128 |
show_version (void) |
129 |
{ |
130 |
printf ("OSN %s %s\n", PACKAGE_NAME, PACKAGE_VERSION); |
131 |
printf ("\n"); |
132 |
printf ("Copyright (C) 2024 OSN, Inc.\n"); |
133 |
printf ("License GPLv3+: GNU GPL version 3 or later " |
134 |
"<http://gnu.org/licenses/gpl.html>\n"); |
135 |
printf ( |
136 |
"This is free software: you are free to change and redistribute it.\n"); |
137 |
printf ("There is NO WARRANTY, to the extent permitted by law.\n"); |
138 |
printf ("\n"); |
139 |
printf ("Written by Ar Rakin <[email protected]>\n"); |
140 |
} |
141 |
|
142 |
#ifndef NDEBUG |
143 |
/* Print a debug message. */ |
144 |
static void |
145 |
pdebug (char const *file, int line, char const *format, ...) |
146 |
{ |
147 |
if (!params.verbose) |
148 |
return; |
149 |
|
150 |
va_list args; |
151 |
va_start (args, format); |
152 |
fprintf (stderr, "%s(verbose): %s:%i: ", progname, file, line); |
153 |
vfprintf (stderr, format, args); |
154 |
va_end (args); |
155 |
} |
156 |
#endif |
157 |
|
158 |
/* Print a message. */ |
159 |
static void |
160 |
pinfo (char const *format, ...) |
161 |
{ |
162 |
va_list args; |
163 |
va_start (args, format); |
164 |
fprintf (stdout, "%s: ", progname); |
165 |
vfprintf (stdout, format, args); |
166 |
va_end (args); |
167 |
} |
168 |
|
169 |
/* Print an error message. */ |
170 |
static void |
171 |
perr (char const *format, ...) |
172 |
{ |
173 |
va_list args; |
174 |
va_start (args, format); |
175 |
fprintf (stderr, "%s: ", progname); |
176 |
vfprintf (stderr, format, args); |
177 |
va_end (args); |
178 |
} |
179 |
|
180 |
/* Cleanup memory. */ |
181 |
static void |
182 |
cleanup () |
183 |
{ |
184 |
for (size_t i = 0; i < params.ntargets; i++) |
185 |
free (params.targets[i]); |
186 |
|
187 |
if (params.targets != NULL) |
188 |
free (params.targets); |
189 |
|
190 |
if (params.cwd != NULL) |
191 |
free (params.cwd); |
192 |
|
193 |
if (params.file != NULL) |
194 |
free (params.file); |
195 |
} |
196 |
|
197 |
/* Initialize the program. */ |
198 |
static void |
199 |
initialize (char *argv0) |
200 |
{ |
201 |
atexit (&cleanup); |
202 |
progname = argv0; |
203 |
} |
204 |
|
205 |
/* Create archive callback. */ |
206 |
static bool |
207 |
create_archive_callback (struct uar_archive *uar, |
208 |
struct uar_file *file __attribute__ ((unused)), |
209 |
const char *uar_name __attribute__ ((unused)), |
210 |
const char *fs_name, enum uar_error_level level, |
211 |
const char *message) |
212 |
{ |
213 |
if (level == UAR_ELEVEL_NONE) |
214 |
{ |
215 |
if (params.verbose) |
216 |
fprintf (stdout, "%s\n", fs_name); |
217 |
} |
218 |
else if (level == UAR_ELEVEL_WARNING) |
219 |
perr ("warning: %s: %s\n", fs_name, |
220 |
message != NULL ? message : uar_strerror (uar)); |
221 |
else if (level == UAR_ELEVEL_ERROR) |
222 |
perr ("error: %s: %s\n", fs_name, |
223 |
message != NULL ? message : uar_strerror (uar)); |
224 |
|
225 |
return true; |
226 |
} |
227 |
|
228 |
/* Create an archive. */ |
229 |
static void |
230 |
create_archive (void) |
231 |
{ |
232 |
assert (params.mode == MODE_CREATE); |
233 |
assert (params.ntargets > 0); |
234 |
assert (params.targets != NULL); |
235 |
|
236 |
if (params.verbose) |
237 |
pinfo ("creating archive: %s\n", params.file); |
238 |
|
239 |
struct uar_archive *uar = uar_stream_create (); |
240 |
|
241 |
if (uar == NULL) |
242 |
{ |
243 |
pinfo ("failed to create archive: %s\n", strerror (errno)); |
244 |
return; |
245 |
} |
246 |
|
247 |
uar_set_create_callback (uar, &create_archive_callback); |
248 |
|
249 |
for (size_t i = 0; i < params.ntargets; i++) |
250 |
{ |
251 |
struct stat stinfo = { 0 }; |
252 |
|
253 |
if (stat (params.targets[i], &stinfo) != 0) |
254 |
{ |
255 |
perr ("cannot stat '%s': %s\n", params.targets[i], |
256 |
strerror (errno)); |
257 |
uar_close (uar); |
258 |
return; |
259 |
} |
260 |
|
261 |
const char *base = basename (params.rtargets[i]); |
262 |
|
263 |
if (strcmp (base, ".") == 0 || strcmp (base, "..") == 0) |
264 |
base = basename (params.targets[i]); |
265 |
|
266 |
struct uar_file *file |
267 |
= uar_stream_add_entry (uar, base, params.rtargets[i], &stinfo); |
268 |
|
269 |
if (file == NULL || uar_has_error (uar)) |
270 |
{ |
271 |
const char *error_file = uar_get_error_file (uar); |
272 |
perr ("failed to add '%s': %s\n", |
273 |
error_file == NULL ? params.targets[i] : error_file, |
274 |
uar_strerror (uar)); |
275 |
exit (1); |
276 |
} |
277 |
} |
278 |
|
279 |
if (!uar_stream_write (uar, params.file)) |
280 |
{ |
281 |
const char *error_file = uar_get_error_file (uar); |
282 |
pinfo ("failed to write archive: %s%s%s\n", |
283 |
error_file == NULL ? "" : error_file, |
284 |
error_file == NULL ? "" : ": ", uar_strerror (uar)); |
285 |
return; |
286 |
} |
287 |
|
288 |
#ifdef UAR_PRINT_VERBOSE_IMPL_INFO |
289 |
uar_debug_print (uar, false); |
290 |
#endif |
291 |
uar_close (uar); |
292 |
} |
293 |
|
294 |
/* Archive extraction callback. */ |
295 |
static bool |
296 |
extract_archive_callback (struct uar_archive *uar, |
297 |
struct uar_file *file __attribute__ ((unused)), |
298 |
const char *uar_name __attribute__ ((unused)), |
299 |
const char *fs_name, enum uar_error_level level, |
300 |
const char *message) |
301 |
{ |
302 |
if (level == UAR_ELEVEL_NONE) |
303 |
{ |
304 |
if (params.verbose) |
305 |
fprintf (stdout, "%s\n", fs_name); |
306 |
} |
307 |
else if (level == UAR_ELEVEL_WARNING) |
308 |
perr ("warning: %s: %s\n", fs_name, |
309 |
message != NULL ? message : uar_strerror (uar)); |
310 |
else if (level == UAR_ELEVEL_ERROR) |
311 |
perr ("error: %s: %s\n", fs_name, |
312 |
message != NULL ? message : uar_strerror (uar)); |
313 |
|
314 |
return true; |
315 |
} |
316 |
|
317 |
/* Extract an archive. */ |
318 |
static void |
319 |
extract_archive (void) |
320 |
{ |
321 |
assert (params.mode == MODE_EXTRACT); |
322 |
|
323 |
pinfo ("extracting archive: %s\n", params.file); |
324 |
|
325 |
struct uar_archive *uar = uar_stream_open (params.file); |
326 |
|
327 |
if (uar == NULL || uar_has_error (uar)) |
328 |
{ |
329 |
pinfo ("failed to open archive: %s\n", strerror (errno)); |
330 |
return; |
331 |
} |
332 |
|
333 |
uar_set_extract_callback (uar, &extract_archive_callback); |
334 |
|
335 |
if (params.cwd != NULL) |
336 |
{ |
337 |
if (chdir (params.cwd) != 0) |
338 |
{ |
339 |
pinfo ("failed to change working directory: %s\n", |
340 |
strerror (errno)); |
341 |
return; |
342 |
} |
343 |
} |
344 |
|
345 |
char *cwd = getcwd (NULL, 0); |
346 |
|
347 |
if (!uar_stream_extract (uar, cwd)) |
348 |
{ |
349 |
pinfo ("failed to extract archive: %s\n", strerror (errno)); |
350 |
free (cwd); |
351 |
return; |
352 |
} |
353 |
|
354 |
free (cwd); |
355 |
uar_close (uar); |
356 |
} |
357 |
|
358 |
static const char * |
359 |
stringify_mode (mode_t mode) |
360 |
{ |
361 |
static char str[11]; |
362 |
|
363 |
str[0] = S_ISDIR (mode) ? 'd' : S_ISLNK (mode) ? 'l' : '-'; |
364 |
|
365 |
for (int i = 1; i < 10; i++) |
366 |
str[i] = mode & (1 << (9 - i)) ? "rwxrwxrwx"[i - 1] : '-'; |
367 |
|
368 |
return str; |
369 |
} |
370 |
|
371 |
static int |
372 |
count_dec_numlen (uint64_t num) |
373 |
{ |
374 |
int len = 0; |
375 |
|
376 |
do |
377 |
{ |
378 |
num /= 10; |
379 |
len++; |
380 |
} |
381 |
while (num > 0); |
382 |
|
383 |
return len; |
384 |
} |
385 |
|
386 |
static char * |
387 |
format_iec_size (uint64_t size) |
388 |
{ |
389 |
static char buf[32] = { 0 }; |
390 |
const char suffix[] = { 0, 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; |
391 |
size_t i = 0; |
392 |
long double computed = size; |
393 |
|
394 |
while (computed > 1024 && i < sizeof (suffix) / sizeof (suffix[0])) |
395 |
{ |
396 |
computed /= 1024; |
397 |
i++; |
398 |
} |
399 |
|
400 |
if (computed == 0.00) |
401 |
{ |
402 |
snprintf (buf, sizeof (buf), "0%c", suffix[i]); |
403 |
return buf; |
404 |
} |
405 |
|
406 |
snprintf (buf, sizeof (buf), "%.2Lf%c", computed, suffix[i]); |
407 |
return buf; |
408 |
} |
409 |
|
410 |
struct archive_file_info |
411 |
{ |
412 |
const char *name; |
413 |
char owner[128]; |
414 |
char group[128]; |
415 |
mode_t mode; |
416 |
time_t mtime; |
417 |
union |
418 |
{ |
419 |
|
420 |
uint64_t bytes; |
421 |
char str[64]; |
422 |
} size; |
423 |
}; |
424 |
|
425 |
struct archive_file_table |
426 |
{ |
427 |
struct archive_file_info *files; |
428 |
size_t nfiles; |
429 |
int widths[3]; |
430 |
}; |
431 |
|
432 |
#define TABLE_WIDTH_SIZE 0 |
433 |
#define TABLE_WIDTH_OWNER 1 |
434 |
#define TABLE_WIDTH_GROUP 2 |
435 |
|
436 |
static bool |
437 |
list_archive_callback_analyze (struct uar_file *file, void *data) |
438 |
{ |
439 |
struct archive_file_table *table = (struct archive_file_table *) data; |
440 |
struct archive_file_info info = { 0 }; |
441 |
|
442 |
mode_t mode = uar_file_get_mode (file); |
443 |
const char *name = uar_file_get_name (file); |
444 |
uint64_t size = uar_file_get_size (file); |
445 |
int size_len = 0; |
446 |
|
447 |
info.mode = mode; |
448 |
info.name = name; |
449 |
info.mtime = uar_file_get_mtime (file); |
450 |
|
451 |
uid_t uid = uar_file_get_uid (file); |
452 |
gid_t gid = uar_file_get_gid (file); |
453 |
|
454 |
struct passwd *pw = getpwuid (uid); |
455 |
struct group *gr = getgrgid (gid); |
456 |
|
457 |
if (pw == NULL) |
458 |
{ |
459 |
if (params.verbose) |
460 |
perr ("warning: failed to get user info (%i): %s\n", uid, |
461 |
strerror (errno)); |
462 |
|
463 |
strncpy (info.owner, "unknown", sizeof (info.owner) - 1); |
464 |
} |
465 |
else |
466 |
strncpy (info.owner, pw->pw_name, sizeof (info.owner) - 1); |
467 |
|
468 |
if (gr == NULL) |
469 |
{ |
470 |
if (params.verbose) |
471 |
perr ("warning: failed to get group info (%i): %s\n", gid, |
472 |
strerror (errno)); |
473 |
|
474 |
strncpy (info.group, "unknown", sizeof (info.group) - 1); |
475 |
} |
476 |
else |
477 |
strncpy (info.group, gr->gr_name, sizeof (info.group) - 1); |
478 |
|
479 |
if (params.hr_sizes) |
480 |
{ |
481 |
char *str = format_iec_size (size); |
482 |
size_len = strlen (str); |
483 |
strncpy (info.size.str, str, 32); |
484 |
} |
485 |
else |
486 |
{ |
487 |
info.size.bytes = size; |
488 |
size_len = count_dec_numlen (size); |
489 |
} |
490 |
|
491 |
if (size_len > table->widths[TABLE_WIDTH_SIZE]) |
492 |
table->widths[TABLE_WIDTH_SIZE] = size_len; |
493 |
|
494 |
int owner_len = strlen (info.owner); |
495 |
int group_len = strlen (info.group); |
496 |
|
497 |
if (owner_len > table->widths[TABLE_WIDTH_OWNER]) |
498 |
table->widths[TABLE_WIDTH_OWNER] = owner_len; |
499 |
|
500 |
if (group_len > table->widths[TABLE_WIDTH_GROUP]) |
501 |
table->widths[TABLE_WIDTH_GROUP] = group_len; |
502 |
|
503 |
table->files[table->nfiles++] = info; |
504 |
return true; |
505 |
} |
506 |
|
507 |
static void |
508 |
list_archive (void) |
509 |
{ |
510 |
assert (params.mode == MODE_LIST); |
511 |
struct archive_file_table *table = NULL; |
512 |
struct uar_archive *uar = uar_stream_open (params.file); |
513 |
|
514 |
if (uar == NULL) |
515 |
{ |
516 |
pinfo ("failed to open archive file: %s\n", strerror (errno)); |
517 |
goto list_archive_end; |
518 |
} |
519 |
|
520 |
if (uar_has_error (uar)) |
521 |
{ |
522 |
pinfo ("failed to read archive: %s\n", uar_strerror (uar)); |
523 |
goto list_archive_end; |
524 |
} |
525 |
|
526 |
uint64_t nfiles = uar_get_file_count (uar); |
527 |
|
528 |
table = xcalloc (1, sizeof (struct archive_file_table)); |
529 |
table->files = xcalloc (nfiles, sizeof (struct archive_file_info)); |
530 |
table->nfiles = 0; |
531 |
|
532 |
if (!uar_stream_iterate (uar, &list_archive_callback_analyze, |
533 |
(void *) table)) |
534 |
{ |
535 |
pinfo ("failed to read archive: %s\n", strerror (errno)); |
536 |
goto list_archive_end; |
537 |
} |
538 |
|
539 |
for (size_t i = 0; i < nfiles; i++) |
540 |
{ |
541 |
struct archive_file_info info = table->files[i]; |
542 |
struct tm *tm = localtime (&info.mtime); |
543 |
char mtime_str[10] = "never"; |
544 |
const char *mode_str = stringify_mode (info.mode); |
545 |
|
546 |
if (tm == NULL) |
547 |
{ |
548 |
if (params.verbose) |
549 |
perr ("warning: failed to convert time: %s\n", |
550 |
strerror (errno)); |
551 |
} |
552 |
else |
553 |
strftime (mtime_str, sizeof (mtime_str), "%b %d", tm); |
554 |
|
555 |
if (params.hr_sizes) |
556 |
fprintf (stdout, "%s %-*s %-*s %*s %s %s\n", mode_str, |
557 |
table->widths[TABLE_WIDTH_OWNER], info.owner, |
558 |
table->widths[TABLE_WIDTH_GROUP], info.group, |
559 |
table->widths[TABLE_WIDTH_SIZE], info.size.str, |
560 |
mtime_str, info.name); |
561 |
|
562 |
else |
563 |
fprintf (stdout, "%s %-*s %-*s %*lu %s %s\n", mode_str, |
564 |
table->widths[TABLE_WIDTH_OWNER], info.owner, |
565 |
table->widths[TABLE_WIDTH_GROUP], info.group, |
566 |
table->widths[TABLE_WIDTH_SIZE], info.size.bytes, |
567 |
mtime_str, info.name); |
568 |
} |
569 |
|
570 |
list_archive_end: |
571 |
free (table->files); |
572 |
free (table); |
573 |
uar_close (uar); |
574 |
} |
575 |
|
576 |
int |
577 |
main (int argc, char **argv) |
578 |
{ |
579 |
initialize (argv[0]); |
580 |
assert (progname != NULL && "progname is NULL"); |
581 |
|
582 |
while (true) |
583 |
{ |
584 |
int opt |
585 |
= getopt_long (argc, argv, short_options, long_options, NULL); |
586 |
|
587 |
if (opt == -1) |
588 |
break; |
589 |
|
590 |
if ((opt == 'c' || opt == 'x' || opt == 't') |
591 |
&& params.mode != MODE_NONE) |
592 |
{ |
593 |
perr ("only one mode can be specified\n"); |
594 |
exit (1); |
595 |
} |
596 |
|
597 |
switch (opt) |
598 |
{ |
599 |
case 'c': |
600 |
params.mode = MODE_CREATE; |
601 |
break; |
602 |
|
603 |
case 'x': |
604 |
params.mode = MODE_EXTRACT; |
605 |
break; |
606 |
|
607 |
case 't': |
608 |
params.mode = MODE_LIST; |
609 |
break; |
610 |
|
611 |
case 'v': |
612 |
params.verbose = true; |
613 |
debug ("Verbose mode enabled\n", progname); |
614 |
break; |
615 |
|
616 |
case 'm': |
617 |
params.hr_sizes = true; |
618 |
break; |
619 |
|
620 |
case 'f': |
621 |
params.file = optarg; |
622 |
break; |
623 |
|
624 |
case 'C': |
625 |
params.cwd = optarg; |
626 |
break; |
627 |
|
628 |
case 'h': |
629 |
usage (); |
630 |
exit (0); |
631 |
|
632 |
case 'V': |
633 |
show_version (); |
634 |
exit (0); |
635 |
|
636 |
case '?': |
637 |
default: |
638 |
debug ("Unknown/Unhandled option: %c\n", opt); |
639 |
bzero (¶ms, sizeof (params)); |
640 |
exit (1); |
641 |
} |
642 |
} |
643 |
|
644 |
if (params.file != NULL) |
645 |
{ |
646 |
char *file = params.file; |
647 |
|
648 |
if ((params.mode == MODE_EXTRACT || params.mode == MODE_LIST)) |
649 |
{ |
650 |
params.file = realpath (file, NULL); |
651 |
|
652 |
if (params.file == NULL) |
653 |
{ |
654 |
perr ("failed to read '%s': %s\n", file, |
655 |
strerror (errno)); |
656 |
exit (1); |
657 |
} |
658 |
} |
659 |
else |
660 |
{ |
661 |
params.file = strdup (file); |
662 |
} |
663 |
} |
664 |
|
665 |
if (params.cwd != NULL) |
666 |
{ |
667 |
if (params.mode == MODE_LIST) |
668 |
{ |
669 |
params.cwd = NULL; |
670 |
perr ("option '-C' or '--directory' does not make sense in " |
671 |
"list mode\n"); |
672 |
exit (1); |
673 |
} |
674 |
|
675 |
char *dir = params.cwd; |
676 |
params.cwd = realpath (dir, NULL); |
677 |
|
678 |
if (params.cwd == NULL) |
679 |
{ |
680 |
perr ("failed to change working directory to '%s': %s\n", |
681 |
dir, strerror (errno)); |
682 |
exit (1); |
683 |
} |
684 |
} |
685 |
|
686 |
if (params.verbose) |
687 |
{ |
688 |
debug ("Summary of options:\n"); |
689 |
debug (" mode: %s\n", params.mode == MODE_CREATE ? "create" |
690 |
: params.mode == MODE_EXTRACT ? "extract" |
691 |
: "list"); |
692 |
debug (" verbose: %s\n", params.verbose ? "yes" : "no"); |
693 |
debug (" file: %s\n", params.file); |
694 |
debug (" working directory: %s\n", params.cwd); |
695 |
} |
696 |
|
697 |
switch (params.mode) |
698 |
{ |
699 |
case MODE_CREATE: |
700 |
if (params.file == NULL) |
701 |
{ |
702 |
perr ("no archive file name specified\n"); |
703 |
exit (1); |
704 |
} |
705 |
|
706 |
for (int i = optind; i < argc; i++) |
707 |
{ |
708 |
char *path = realpath (argv[i], NULL); |
709 |
|
710 |
if (path == NULL) |
711 |
{ |
712 |
perr ("failed to read '%s': %s\n", argv[i], |
713 |
strerror (errno)); |
714 |
exit (1); |
715 |
} |
716 |
|
717 |
params.targets |
718 |
= xrealloc (params.targets, |
719 |
(params.ntargets + 1) * sizeof (char *)); |
720 |
params.targets[params.ntargets] = path; |
721 |
params.ntargets++; |
722 |
} |
723 |
|
724 |
params.rtargets = argv + optind; |
725 |
|
726 |
if (params.ntargets == 0) |
727 |
{ |
728 |
perr ("no files or directories specified\n"); |
729 |
exit (1); |
730 |
} |
731 |
|
732 |
create_archive (); |
733 |
break; |
734 |
|
735 |
case MODE_EXTRACT: |
736 |
if (params.file == NULL) |
737 |
{ |
738 |
perr ("no archive file specified\n"); |
739 |
exit (1); |
740 |
} |
741 |
|
742 |
extract_archive (); |
743 |
break; |
744 |
|
745 |
case MODE_LIST: |
746 |
if (params.file == NULL) |
747 |
{ |
748 |
perr ("no archive file specified\n"); |
749 |
exit (1); |
750 |
} |
751 |
|
752 |
list_archive (); |
753 |
break; |
754 |
|
755 |
default: |
756 |
usage (); |
757 |
exit (1); |
758 |
} |
759 |
|
760 |
return 0; |
761 |
} |