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

Contents of /trunk/uar/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 30 - (show annotations)
Tue Aug 6 14:24:41 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 13106 byte(s)
fix(uar): io logging
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 <rakinar2@onesoftnet.eu.org>
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 "uar@onesoftnet.eu.org"
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 <rakinar2@onesoftnet.eu.org>\n");
134 }
135
136 #ifndef NDEBUG
137 /* Print a debug message. */
138 static void
139 pdebug (char const *file, int line, char const *format, ...)
140 {
141 if (!params.verbose)
142 return;
143
144 va_list args;
145 va_start (args, format);
146 fprintf (stderr, "%s(verbose): %s:%i: ", progname, file, line);
147 vfprintf (stderr, format, args);
148 va_end (args);
149 }
150 #endif
151
152 /* Print a message. */
153 static void
154 pinfo (char const *format, ...)
155 {
156 va_list args;
157 va_start (args, format);
158 fprintf (stdout, "%s: ", progname);
159 vfprintf (stdout, format, args);
160 va_end (args);
161 }
162
163 /* Print an error message. */
164 static void
165 perr (char const *format, ...)
166 {
167 va_list args;
168 va_start (args, format);
169 fprintf (stderr, "%s: ", progname);
170 vfprintf (stderr, format, args);
171 va_end (args);
172 }
173
174 /* Cleanup memory. */
175 static void
176 cleanup ()
177 {
178 if (params.params.create.targets != NULL)
179 free (params.params.create.targets);
180
181 if (params.cwd != NULL)
182 free (params.cwd);
183
184 if (params.file != NULL)
185 free (params.file);
186 }
187
188 /* Initialize the program. */
189 static void
190 initialize (char *argv0)
191 {
192 atexit (&cleanup);
193
194 progname = strrchr (argv0, '/');
195
196 if (progname != NULL)
197 progname++;
198 else
199 progname = argv0;
200 }
201
202 static bool
203 create_archive_callback (struct uar_file *file,
204 const char *fullname __attribute__ ((unused)),
205 const char *fullpath __attribute__ ((unused)))
206 {
207 enum uar_file_type type = uar_get_entry_type (file);
208 pinfo ("adding %s: %s\n",
209 type == UF_FILE ? "file"
210 : type == UF_DIR ? "directory"
211 : "link",
212 uar_get_file_name (file));
213 return true;
214 }
215
216 /* Create an archive. */
217 static void
218 create_archive (void)
219 {
220 assert (params.mode == MODE_CREATE);
221 assert (params.params.create.ntargets > 0);
222 assert (params.params.create.targets != NULL);
223
224 pinfo ("creating archive: %s\n", params.file);
225
226 struct uar_archive *uar = uar_create ();
227
228 if (uar == NULL || uar_has_error (uar))
229 {
230 pinfo ("failed to create archive: %s\n", strerror (errno));
231 return;
232 }
233
234 for (size_t i = 0; i < params.params.create.ntargets; i++)
235 {
236 struct stat stinfo = { 0 };
237
238 if (stat (params.params.create.targets[i], &stinfo) != 0)
239 {
240 perr ("cannot stat '%s': %s\n",
241 params.params.create.targets[i], strerror (errno));
242 uar_close (uar);
243 return;
244 }
245
246 struct uar_file *file = NULL;
247
248 if (S_ISREG (stinfo.st_mode))
249 {
250 pinfo ("adding file: %s\n",
251 params.params.create.targets[i]);
252 file = uar_add_file (
253 uar, basename (params.params.create.targets[i]),
254 params.params.create.targets[i]);
255
256 if (file == NULL)
257 {
258 perr ("failed to add file: %s\n", strerror (errno));
259 uar_close (uar);
260 return;
261 }
262 }
263 else if (S_ISDIR (stinfo.st_mode))
264 {
265 file = uar_add_dir (
266 uar, basename (params.params.create.targets[i]),
267 params.params.create.targets[i],
268 &create_archive_callback);
269
270 if (file == NULL)
271 {
272 perr ("failed to add directory: %s (%s)\n",
273 strerror (errno), uar_strerror (uar));
274 uar_close (uar);
275 return;
276 }
277 }
278 else if (S_ISLNK (stinfo.st_mode))
279 {
280 assert (false && "Not implemented");
281 }
282 else
283 {
284 perr ("failed to add file: %s: file type not supported\n",
285 params.params.create.targets[i]);
286 uar_close (uar);
287 return;
288 }
289
290 assert (file != NULL);
291 uar_file_set_mode (file, stinfo.st_mode & 07777);
292 }
293
294 pinfo ("writing archive: %s\n", params.file);
295
296 if (!uar_write (uar, params.file))
297 {
298 perr ("failed to write archive: %s\n", strerror (errno));
299 uar_close (uar);
300 return;
301 }
302
303 #ifdef UAR_PRINT_VERBOSE_IMPL_INFO
304 uar_debug_print (uar, false);
305 #endif
306 uar_close (uar);
307 }
308
309 /* Archive extraction callback. */
310 static bool
311 extract_archive_callback (struct uar_file *file)
312 {
313 pinfo ("extracting: %s\n", uar_get_file_name (file));
314 return true;
315 }
316
317 /* Extract an archive. */
318 static void
319 extract_archive ()
320 {
321 assert (params.mode == MODE_EXTRACT);
322
323 pinfo ("extracting archive: %s\n", params.file);
324
325 struct uar_archive *uar = uar_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 #ifdef UAR_PRINT_VERBOSE_IMPL_INFO
334 uar_debug_print (uar, false);
335 #endif
336
337 if (!uar_extract (uar, params.cwd, &extract_archive_callback))
338 {
339 pinfo ("failed to extract archive: %s\n", strerror (errno));
340 return;
341 }
342
343 uar_close (uar);
344 }
345
346 int
347 main (int argc, char **argv)
348 {
349 initialize (argv[0]);
350 assert (progname != NULL && "progname is NULL");
351
352 while (true)
353 {
354 int opt
355 = getopt_long (argc, argv, short_options, long_options, NULL);
356
357 if (opt == -1)
358 break;
359
360 switch (opt)
361 {
362 case 'c':
363 params.mode = MODE_CREATE;
364 break;
365
366 case 'x':
367 params.mode = MODE_EXTRACT;
368 break;
369
370 case 't':
371 params.mode = MODE_LIST;
372 break;
373
374 case 'v':
375 params.verbose = true;
376 debug ("Verbose mode enabled\n", progname);
377 break;
378
379 case 'f':
380 params.file = optarg;
381 break;
382
383 case 'C':
384 params.cwd = optarg;
385 break;
386
387 case 'h':
388 usage ();
389 exit (0);
390
391 case 'V':
392 show_version ();
393 exit (0);
394
395 case '?':
396 usage ();
397 exit (1);
398
399 default:
400 exit (1);
401 }
402 }
403
404 if (params.file != NULL)
405 {
406 char *file = params.file;
407
408 if ((params.mode == MODE_EXTRACT || params.mode == MODE_LIST))
409 {
410 params.file = realpath (file, NULL);
411
412 if (params.file == NULL)
413 {
414 perr ("failed to read '%s': %s\n", file,
415 strerror (errno));
416 exit (1);
417 }
418 }
419 else
420 {
421 params.file = strdup (file);
422 }
423 }
424
425 if (params.cwd != NULL)
426 {
427 char *dir = params.cwd;
428 params.cwd = realpath (dir, NULL);
429
430 if (params.cwd == NULL)
431 {
432 perr ("failed to change working directory to '%s': %s\n",
433 dir, strerror (errno));
434 exit (1);
435 }
436 }
437
438 if (params.verbose)
439 {
440 debug ("Summary of options:\n");
441 debug (" mode: %s\n", params.mode == MODE_CREATE ? "create"
442 : params.mode == MODE_EXTRACT ? "extract"
443 : "list");
444 debug (" verbose: %s\n", params.verbose ? "yes" : "no");
445 debug (" file: %s\n", params.file);
446 debug (" working directory: %s\n", params.cwd);
447 }
448
449 switch (params.mode)
450 {
451 case MODE_CREATE:
452 for (int i = optind; i < argc; i++)
453 {
454 params.params.create.targets = xrealloc (
455 params.params.create.targets,
456 (params.params.create.ntargets + 1) * sizeof (char *));
457 params.params.create.targets[params.params.create.ntargets]
458 = argv[i];
459 params.params.create.ntargets++;
460 }
461
462 create_archive ();
463 break;
464
465 case MODE_EXTRACT:
466 extract_archive ();
467 break;
468
469 case MODE_LIST:
470 assert (false && "Not implemented yet");
471 break;
472
473 default:
474 usage ();
475 exit (1);
476 }
477
478 return 0;
479 }

team@onesoftnet.eu.org
ViewVC Help
Powered by ViewVC 1.1.26