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

Contents of /trunk/uar/main.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 25 - (show annotations)
Tue Aug 6 13:06:45 2024 UTC (7 months, 3 weeks ago) by rakinar2
File MIME type: text/x-c
File size: 12680 byte(s)
fix(uar): memory corruption issues and segmentation faults
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 <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 (%s)\n",
258 strerror (errno), uar_strerror (uar));
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\n", 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\n",
418 dir, 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