/[osn-commons]/trunk/uar/main.c
ViewVC logotype

Annotation of /trunk/uar/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 23 - (hide annotations)
Mon Aug 5 17:15:48 2024 UTC (7 months, 4 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 12651 byte(s)
feat: add programs implementing the universal archive format
1 rakinar2 23 /*
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 <libgen.h>
28     #include <stdarg.h>
29     #include <stdbool.h>
30     #include <stdio.h>
31     #include <stdlib.h>
32     #include <string.h>
33     #include <sys/stat.h>
34    
35     #include "uar.h"
36     #include "xmalloc.h"
37    
38     #ifdef HAVE_CONFIG_H
39     # include "config.h"
40     #else
41     # define PACKAGE_NAME "uar"
42     # define PACKAGE_VERSION "1.0"
43     # define PACKAGE_BUGREPORT "[email protected]"
44     #endif
45    
46     #ifndef NDEBUG
47     # define debug(...) pdebug (__FILE__, __LINE__, __VA_ARGS__)
48     #else
49     # define debug(...)
50     #endif
51    
52     /* Command line options. */
53     static struct option const long_options[] = {
54     { "create", no_argument, NULL, 'c' },
55     { "extract", no_argument, NULL, 'x' },
56     { "list", no_argument, NULL, 't' },
57     { "verbose", no_argument, NULL, 'v' },
58     { "file", required_argument, NULL, 'f' },
59     { "directory", required_argument, NULL, 'C' },
60     { "help", no_argument, NULL, 'h' },
61     { "version", no_argument, NULL, 'V' },
62     { NULL, 0, NULL, 0 },
63     };
64    
65     static char const short_options[] = "cxtvf:C:hV";
66    
67     /* Program name. */
68     static char *progname = NULL;
69    
70     /* Flags for the command line options. */
71     enum uar_mode
72     {
73     MODE_NONE,
74     MODE_CREATE,
75     MODE_EXTRACT,
76     MODE_LIST
77     };
78    
79     struct uar_params
80     {
81     enum uar_mode mode;
82     bool verbose;
83     char *file;
84     char *cwd;
85     union
86     {
87     struct
88     {
89     char **targets;
90     size_t ntargets;
91     } create;
92     } params;
93     };
94    
95     static struct uar_params params = { 0 };
96    
97     /* Print usage information. */
98     static void
99     usage (void)
100     {
101     printf ("Usage:\n");
102     printf (" uar [OPTION]... [FILE]...\n");
103     printf ("\n");
104     printf ("Universal Archive utility.\n");
105     printf ("\n");
106     printf ("Options:\n");
107     printf (" -c, --create Create a new archive\n");
108     printf (" -x, --extract Extract files from an archive\n");
109     printf (" -t, --list List the contents of an archive\n");
110     printf (" -v, --verbose Verbose mode\n");
111     printf (
112     " -f, --file=ARCHIVE Use archive file or directory ARCHIVE\n");
113     printf (" -C, --directory=DIR Change to directory DIR\n");
114     printf (" -h, --help Display this help and exit\n");
115     printf (" -V, --version Output version information and exit\n");
116     printf ("\n");
117     printf ("Report bugs to: <" PACKAGE_BUGREPORT ">\n");
118     }
119    
120     /* Print version information. */
121     static void
122     show_version (void)
123     {
124     printf ("OSN %s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
125     printf ("\n");
126     printf ("Copyright (C) 2024 OSN, Inc.\n");
127     printf ("License GPLv3+: GNU GPL version 3 or later "
128     "<http://gnu.org/licenses/gpl.html>\n");
129     printf (
130     "This is free software: you are free to change and redistribute it.\n");
131     printf ("There is NO WARRANTY, to the extent permitted by law.\n");
132     printf ("\n");
133     printf ("Written by Ar Rakin <[email protected]>\n");
134     }
135    
136     /* Print a debug message. */
137     static void
138     pdebug (char const *file, int line, char const *format, ...)
139     {
140     if (!params.verbose)
141     return;
142    
143     va_list args;
144     va_start (args, format);
145     fprintf (stderr, "%s(verbose): %s:%i: ", progname, file, line);
146     vfprintf (stderr, format, args);
147     va_end (args);
148     }
149    
150     /* Print a message. */
151     static void
152     pinfo (char const *format, ...)
153     {
154     va_list args;
155     va_start (args, format);
156     fprintf (stdout, "%s: ", progname);
157     vfprintf (stdout, format, args);
158     va_end (args);
159     }
160    
161     /* Print an error message. */
162     static void
163     perr (char const *format, ...)
164     {
165     va_list args;
166     va_start (args, format);
167     fprintf (stderr, "%s: ", progname);
168     vfprintf (stderr, format, args);
169     va_end (args);
170     }
171    
172     /* Cleanup memory. */
173     static void
174     cleanup ()
175     {
176     if (params.params.create.targets != NULL)
177     free (params.params.create.targets);
178    
179     if (params.cwd != NULL)
180     free (params.cwd);
181    
182     if (params.file != NULL)
183     free (params.file);
184     }
185    
186     /* Initialize the program. */
187     static void
188     initialize (char *argv0)
189     {
190     atexit (&cleanup);
191    
192     progname = strrchr (argv0, '/');
193    
194     if (progname != NULL)
195     progname++;
196     else
197     progname = argv0;
198     }
199    
200     /* Create an archive. */
201     static void
202     create_archive (void)
203     {
204     assert (params.mode == MODE_CREATE);
205     assert (params.params.create.ntargets > 0);
206     assert (params.params.create.targets != NULL);
207    
208     pinfo ("creating archive: %s\n", params.file);
209    
210     struct uar_archive *uar = uar_create ();
211    
212     if (uar == NULL || uar_has_error (uar))
213     {
214     pinfo ("failed to create archive: %s\n", strerror (errno));
215     return;
216     }
217    
218     for (size_t i = 0; i < params.params.create.ntargets; i++)
219     {
220     struct stat stinfo = { 0 };
221    
222     if (stat (params.params.create.targets[i], &stinfo) != 0)
223     {
224     perr ("cannot stat '%s': %s\n",
225     params.params.create.targets[i], strerror (errno));
226     uar_close (uar);
227     return;
228     }
229    
230     struct uar_file *file = NULL;
231    
232     if (S_ISREG (stinfo.st_mode))
233     {
234     pinfo ("adding file: %s\n",
235     params.params.create.targets[i]);
236     file = uar_add_file (
237     uar, basename (params.params.create.targets[i]),
238     params.params.create.targets[i]);
239    
240     if (file == NULL)
241     {
242     perr ("failed to add file: %s\n", strerror (errno));
243     uar_close (uar);
244     return;
245     }
246     }
247     else if (S_ISDIR (stinfo.st_mode))
248     {
249     pinfo ("adding directory: %s\n",
250     params.params.create.targets[i]);
251     file = uar_add_dir (
252     uar, basename (params.params.create.targets[i]),
253     params.params.create.targets[i]);
254    
255     if (file == NULL)
256     {
257     perr ("failed to add directory: %s\n",
258     strerror (errno));
259     uar_close (uar);
260     return;
261     }
262     }
263     else if (S_ISLNK (stinfo.st_mode))
264     {
265     assert (false && "Not implemented");
266     }
267     else
268     {
269     perr ("failed to add file: %s: file type not supported\n",
270     params.params.create.targets[i]);
271     uar_close (uar);
272     return;
273     }
274    
275     assert (file != NULL);
276     uar_file_set_mode (file, stinfo.st_mode & 07777);
277     }
278    
279     pinfo ("writing archive: %s\n", params.file);
280    
281     if (!uar_write (uar, params.file))
282     {
283     perr ("failed to write archive: %s\n", strerror (errno));
284     uar_close (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_file *file)
297     {
298     pinfo ("extracting: %s\n", uar_get_file_name (file));
299     return true;
300     }
301    
302     /* Extract an archive. */
303     static void
304     extract_archive ()
305     {
306     assert (params.mode == MODE_EXTRACT);
307    
308     pinfo ("extracting archive: %s\n", params.file);
309    
310     struct uar_archive *uar = uar_open (params.file);
311    
312     if (uar == NULL || uar_has_error (uar))
313     {
314     pinfo ("failed to open archive: %s\n", strerror (errno));
315     return;
316     }
317    
318     #ifdef UAR_PRINT_VERBOSE_IMPL_INFO
319     uar_debug_print (uar, false);
320     #endif
321    
322     if (!uar_extract (uar, params.cwd, &extract_archive_callback))
323     {
324     pinfo ("failed to extract archive: %s\n", strerror (errno));
325     return;
326     }
327    
328     uar_close (uar);
329     }
330    
331     int
332     main (int argc, char **argv)
333     {
334     initialize (argv[0]);
335     assert (progname != NULL && "progname is NULL");
336    
337     while (true)
338     {
339     int opt
340     = getopt_long (argc, argv, short_options, long_options, NULL);
341    
342     if (opt == -1)
343     break;
344    
345     switch (opt)
346     {
347     case 'c':
348     params.mode = MODE_CREATE;
349     break;
350    
351     case 'x':
352     params.mode = MODE_EXTRACT;
353     break;
354    
355     case 't':
356     params.mode = MODE_LIST;
357     break;
358    
359     case 'v':
360     params.verbose = true;
361     debug ("Verbose mode enabled\n", progname);
362     break;
363    
364     case 'f':
365     params.file = optarg;
366     break;
367    
368     case 'C':
369     params.cwd = optarg;
370     break;
371    
372     case 'h':
373     usage ();
374     exit (0);
375    
376     case 'V':
377     show_version ();
378     exit (0);
379    
380     case '?':
381     usage ();
382     exit (1);
383    
384     default:
385     exit (1);
386     }
387     }
388    
389     if (params.file != NULL)
390     {
391     char *file = params.file;
392    
393     if ((params.mode == MODE_EXTRACT || params.mode == MODE_LIST))
394     {
395     params.file = realpath (file, NULL);
396    
397     if (params.file == NULL)
398     {
399     perr ("failed to read '%s': %s", file,
400     strerror (errno));
401     exit (1);
402     }
403     }
404     else
405     {
406     params.file = strdup (file);
407     }
408     }
409    
410     if (params.cwd != NULL)
411     {
412     char *dir = params.cwd;
413     params.cwd = realpath (dir, NULL);
414    
415     if (params.cwd == NULL)
416     {
417     perr ("failed to change working directory to '%s': %s", dir,
418     strerror (errno));
419     exit (1);
420     }
421     }
422    
423     if (params.verbose)
424     {
425     debug ("Summary of options:\n");
426     debug (" mode: %s\n", params.mode == MODE_CREATE ? "create"
427     : params.mode == MODE_EXTRACT ? "extract"
428     : "list");
429     debug (" verbose: %s\n", params.verbose ? "yes" : "no");
430     debug (" file: %s\n", params.file);
431     debug (" working directory: %s\n", params.cwd);
432     }
433    
434     switch (params.mode)
435     {
436     case MODE_CREATE:
437     for (int i = optind; i < argc; i++)
438     {
439     params.params.create.targets = xrealloc (
440     params.params.create.targets,
441     (params.params.create.ntargets + 1) * sizeof (char *));
442     params.params.create.targets[params.params.create.ntargets]
443     = argv[i];
444     params.params.create.ntargets++;
445     }
446    
447     create_archive ();
448     break;
449    
450     case MODE_EXTRACT:
451     extract_archive ();
452     break;
453    
454     case MODE_LIST:
455     assert (false && "Not implemented yet");
456     break;
457    
458     default:
459     usage ();
460     exit (1);
461     }
462    
463     return 0;
464     }

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26