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

Diff of /trunk/uar/main.c

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 23 by rakinar2, Mon Aug 5 17:15:48 2024 UTC revision 33 by rakinar2, Thu Aug 8 16:57:39 2024 UTC
# Line 25  Line 25 
25  #include <errno.h>  #include <errno.h>
26  #include <getopt.h>  #include <getopt.h>
27  #include <libgen.h>  #include <libgen.h>
28    #include <limits.h>
29    #include <math.h>
30  #include <stdarg.h>  #include <stdarg.h>
31  #include <stdbool.h>  #include <stdbool.h>
32  #include <stdio.h>  #include <stdio.h>
33  #include <stdlib.h>  #include <stdlib.h>
34  #include <string.h>  #include <string.h>
35    #include <strings.h>
36  #include <sys/stat.h>  #include <sys/stat.h>
37    #include <time.h>
38    #include <unistd.h>
39    
40  #include "uar.h"  #include "uar.h"
41  #include "xmalloc.h"  #include "xmalloc.h"
# Line 51  Line 56 
56    
57  /* Command line options. */  /* Command line options. */
58  static struct option const long_options[] = {  static struct option const long_options[] = {
59      { "create",    no_argument,       NULL, 'c' },      { "create",         no_argument,       NULL, 'c' },
60      { "extract",   no_argument,       NULL, 'x' },      { "extract",        no_argument,       NULL, 'x' },
61      { "list",      no_argument,       NULL, 't' },      { "human-readable", no_argument,       NULL, 'm' },
62      { "verbose",   no_argument,       NULL, 'v' },      { "list",           no_argument,       NULL, 't' },
63      { "file",      required_argument, NULL, 'f' },      { "verbose",        no_argument,       NULL, 'v' },
64      { "directory", required_argument, NULL, 'C' },      { "file",           required_argument, NULL, 'f' },
65      { "help",      no_argument,       NULL, 'h' },      { "directory",      required_argument, NULL, 'C' },
66      { "version",   no_argument,       NULL, 'V' },      { "help",           no_argument,       NULL, 'h' },
67      { NULL,        0,                 NULL, 0   },      { "version",        no_argument,       NULL, 'V' },
68        { NULL,             0,                 NULL, 0   },
69  };  };
70    
71  static char const short_options[] = "cxtvf:C:hV";  static char const short_options[] = "cxtvmf:C:hV";
72    
73  /* Program name. */  /* Program name. */
74  static char *progname = NULL;  static char *progname = NULL;
# Line 70  static char *progname = NULL; Line 76  static char *progname = NULL;
76  /* Flags for the command line options. */  /* Flags for the command line options. */
77  enum uar_mode  enum uar_mode
78  {  {
79      MODE_NONE,      MODE_NONE = 0,
80      MODE_CREATE,      MODE_CREATE,
81      MODE_EXTRACT,      MODE_EXTRACT,
82      MODE_LIST      MODE_LIST
# Line 80  struct uar_params Line 86  struct uar_params
86  {  {
87      enum uar_mode mode;      enum uar_mode mode;
88      bool verbose;      bool verbose;
89        bool hr_sizes;
90      char *file;      char *file;
91      char *cwd;      char *cwd;
92      union      char **targets;
93      {      char **rtargets;
94          struct      size_t ntargets;
         {  
             char **targets;  
             size_t ntargets;  
         } create;  
     } params;  
95  };  };
96    
97  static struct uar_params params = { 0 };  static struct uar_params params = { 0 };
# Line 107  usage (void) Line 109  usage (void)
109      printf ("  -c, --create            Create a new archive\n");      printf ("  -c, --create            Create a new archive\n");
110      printf ("  -x, --extract           Extract files from an archive\n");      printf ("  -x, --extract           Extract files from an archive\n");
111      printf ("  -t, --list              List the contents of an archive\n");      printf ("  -t, --list              List the contents of an archive\n");
112        printf ("  -m, --human-readable    Print human-readable sizes\n");
113      printf ("  -v, --verbose           Verbose mode\n");      printf ("  -v, --verbose           Verbose mode\n");
114      printf (      printf (
115          "  -f, --file=ARCHIVE      Use archive file or directory ARCHIVE\n");          "  -f, --file=ARCHIVE      Use archive file or directory ARCHIVE\n");
# Line 133  show_version (void) Line 136  show_version (void)
136      printf ("Written by Ar Rakin <[email protected]>\n");      printf ("Written by Ar Rakin <[email protected]>\n");
137  }  }
138    
139    #ifndef NDEBUG
140  /* Print a debug message. */  /* Print a debug message. */
141  static void  static void
142  pdebug (char const *file, int line, char const *format, ...)  pdebug (char const *file, int line, char const *format, ...)
# Line 146  pdebug (char const *file, int line, char Line 150  pdebug (char const *file, int line, char
150      vfprintf (stderr, format, args);      vfprintf (stderr, format, args);
151      va_end (args);      va_end (args);
152  }  }
153    #endif
154    
155  /* Print a message. */  /* Print a message. */
156  static void  static void
# Line 173  perr (char const *format, ...) Line 178  perr (char const *format, ...)
178  static void  static void
179  cleanup ()  cleanup ()
180  {  {
181      if (params.params.create.targets != NULL)      for (size_t i = 0; i < params.ntargets; i++)
182          free (params.params.create.targets);          free (params.targets[i]);
183    
184        if (params.targets != NULL)
185            free (params.targets);
186    
187      if (params.cwd != NULL)      if (params.cwd != NULL)
188          free (params.cwd);          free (params.cwd);
# Line 188  static void Line 196  static void
196  initialize (char *argv0)  initialize (char *argv0)
197  {  {
198      atexit (&cleanup);      atexit (&cleanup);
199        progname = argv0;
200    }
201    
202      progname = strrchr (argv0, '/');  /* Create archive callback. */
203    static bool
204    create_archive_callback (struct uar_archive *uar,
205                             struct uar_file *file __attribute__ ((unused)),
206                             const char *uar_name __attribute__ ((unused)),
207                             const char *fs_name, enum uar_error_level level,
208                             const char *message)
209    {
210        if (level == UAR_ELEVEL_NONE)
211            {
212                if (params.verbose)
213                    fprintf (stdout, "%s\n", fs_name);
214            }
215        else if (level == UAR_ELEVEL_WARNING)
216            perr ("warning: %s: %s\n", fs_name,
217                  message != NULL ? message : uar_strerror (uar));
218        else if (level == UAR_ELEVEL_ERROR)
219            perr ("error: %s: %s\n", fs_name,
220                  message != NULL ? message : uar_strerror (uar));
221    
222      if (progname != NULL)      return true;
         progname++;  
     else  
         progname = argv0;  
223  }  }
224    
225  /* Create an archive. */  /* Create an archive. */
# Line 202  static void Line 227  static void
227  create_archive (void)  create_archive (void)
228  {  {
229      assert (params.mode == MODE_CREATE);      assert (params.mode == MODE_CREATE);
230      assert (params.params.create.ntargets > 0);      assert (params.ntargets > 0);
231      assert (params.params.create.targets != NULL);      assert (params.targets != NULL);
232    
233      pinfo ("creating archive: %s\n", params.file);      if (params.verbose)
234            pinfo ("creating archive: %s\n", params.file);
235    
236      struct uar_archive *uar = uar_create ();      struct uar_archive *uar = uar_create_stream ();
237    
238      if (uar == NULL || uar_has_error (uar))      if (uar == NULL)
239          {          {
240              pinfo ("failed to create archive: %s\n", strerror (errno));              pinfo ("failed to create archive: %s\n", strerror (errno));
241              return;              return;
242          }          }
243    
244      for (size_t i = 0; i < params.params.create.ntargets; i++)      uar_set_create_callback (uar, &create_archive_callback);
245    
246        for (size_t i = 0; i < params.ntargets; i++)
247          {          {
248              struct stat stinfo = { 0 };              struct stat stinfo = { 0 };
249    
250              if (stat (params.params.create.targets[i], &stinfo) != 0)              if (stat (params.targets[i], &stinfo) != 0)
251                  {                  {
252                      perr ("cannot stat '%s': %s\n",                      perr ("cannot stat '%s': %s\n", params.targets[i],
253                            params.params.create.targets[i], strerror (errno));                            strerror (errno));
254                      uar_close (uar);                      uar_close (uar);
255                      return;                      return;
256                  }                  }
257    
258              struct uar_file *file = NULL;              struct uar_file *file
259                    = uar_stream_add_entry (uar, basename (params.rtargets[i]),
260              if (S_ISREG (stinfo.st_mode))                                          params.rtargets[i], &stinfo);
                 {  
                     pinfo ("adding file: %s\n",  
                            params.params.create.targets[i]);  
                     file = uar_add_file (  
                         uar, basename (params.params.create.targets[i]),  
                         params.params.create.targets[i]);  
   
                     if (file == NULL)  
                         {  
                             perr ("failed to add file: %s\n", strerror (errno));  
                             uar_close (uar);  
                             return;  
                         }  
                 }  
             else if (S_ISDIR (stinfo.st_mode))  
                 {  
                     pinfo ("adding directory: %s\n",  
                            params.params.create.targets[i]);  
                     file = uar_add_dir (  
                         uar, basename (params.params.create.targets[i]),  
                         params.params.create.targets[i]);  
261    
262                      if (file == NULL)              if (file == NULL || uar_has_error (uar))
                         {  
                             perr ("failed to add directory: %s\n",  
                                   strerror (errno));  
                             uar_close (uar);  
                             return;  
                         }  
                 }  
             else if (S_ISLNK (stinfo.st_mode))  
263                  {                  {
264                      assert (false && "Not implemented");                      const char *error_file = uar_get_error_file (uar);
265                  }                      perr ("failed to add '%s': %s\n",
266              else                            error_file == NULL ? params.targets[i] : error_file,
267                  {                            uar_strerror (uar));
268                      perr ("failed to add file: %s: file type not supported\n",                      exit (1);
                           params.params.create.targets[i]);  
                     uar_close (uar);  
                     return;  
269                  }                  }
   
             assert (file != NULL);  
             uar_file_set_mode (file, stinfo.st_mode & 07777);  
270          }          }
271    
272      pinfo ("writing archive: %s\n", params.file);      if (!uar_stream_write (uar, params.file))
   
     if (!uar_write (uar, params.file))  
273          {          {
274              perr ("failed to write archive: %s\n", strerror (errno));              const char *error_file = uar_get_error_file (uar);
275              uar_close (uar);              pinfo ("failed to write archive: %s%s%s\n",
276                       error_file == NULL ? "" : error_file,
277                       error_file == NULL ? "" : ": ", uar_strerror (uar));
278              return;              return;
279          }          }
280    
# Line 295  create_archive (void) Line 288  create_archive (void)
288  static bool  static bool
289  extract_archive_callback (struct uar_file *file)  extract_archive_callback (struct uar_file *file)
290  {  {
291      pinfo ("extracting: %s\n", uar_get_file_name (file));      pinfo ("extracting: %s\n", uar_file_get_name (file));
292      return true;      return true;
293  }  }
294    
295  /* Extract an archive. */  /* Extract an archive. */
296  static void  static void
297  extract_archive ()  extract_archive (void)
298  {  {
299      assert (params.mode == MODE_EXTRACT);      assert (params.mode == MODE_EXTRACT);
300    
# Line 328  extract_archive () Line 321  extract_archive ()
321      uar_close (uar);      uar_close (uar);
322  }  }
323    
324    static const char *
325    stringify_mode (mode_t mode)
326    {
327        static char str[11];
328    
329        str[0] = S_ISDIR (mode) ? 'd' : S_ISLNK (mode) ? 'l' : '-';
330    
331        for (int i = 1; i < 10; i++)
332            str[i] = mode & (1 << (9 - i)) ? "rwxrwxrwx"[i - 1] : '-';
333    
334        return str;
335    }
336    
337    static int
338    count_dec_numlen (uint64_t num)
339    {
340        int len = 0;
341    
342        do
343            {
344                num /= 10;
345                len++;
346            }
347        while (num > 0);
348    
349        return len;
350    }
351    
352    static char *
353    format_iec_size (uint64_t size)
354    {
355        static char buf[32] = { 0 };
356        const char suffix[] = { ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
357        size_t i = 0;
358        long double computed = size;
359    
360        while (computed > 1024 && i < sizeof (suffix) / sizeof (suffix[0]))
361            {
362                computed /= 1024;
363                i++;
364            }
365    
366        snprintf (buf, sizeof (buf), "%.02Lf%c", computed, suffix[i]);
367        return buf;
368    }
369    
370    struct archive_file_info
371    {
372        mode_t mode;
373        const char *name;
374        time_t mtime;
375        union
376        {
377    
378            uint64_t bytes;
379            char str[64];
380        } size;
381    };
382    
383    struct archive_file_table
384    {
385        struct archive_file_info *files;
386        size_t nfiles;
387        int widths[1];
388    };
389    
390    #define TABLE_WIDTH_SIZE 1
391    
392    static bool
393    list_archive_callback_analyze (struct uar_file *file, void *data)
394    {
395        struct archive_file_table *table = (struct archive_file_table *) data;
396        struct archive_file_info info = { 0 };
397    
398        mode_t mode = uar_file_get_mode (file);
399        const char *name = uar_file_get_name (file);
400        uint64_t size = uar_file_get_size (file);
401        int size_len = 0;
402    
403        info.mode = mode;
404        info.name = name;
405        info.mtime = uar_file_get_mtime (file);
406    
407        if (params.hr_sizes)
408            {
409                char *str = format_iec_size (size);
410                size_len = strlen (str);
411                strncpy (info.size.str, str, 32);
412            }
413        else
414            {
415                info.size.bytes = size;
416                size_len = count_dec_numlen (size);
417            }
418    
419        if (size_len > table->widths[TABLE_WIDTH_SIZE])
420            table->widths[TABLE_WIDTH_SIZE] = size_len;
421    
422        table->files[table->nfiles++] = info;
423        return true;
424    }
425    
426    static void
427    list_archive (void)
428    {
429        assert (params.mode == MODE_LIST);
430        struct archive_file_table *table = NULL;
431        struct uar_archive *uar = uar_open (params.file);
432    
433        if (uar == NULL || uar_has_error (uar))
434            {
435                pinfo ("failed to open archive: %s\n", strerror (errno));
436                goto list_archive_end;
437            }
438    
439        uint64_t nfiles = uar_get_file_count (uar);
440    
441        table = xcalloc (1, sizeof (struct archive_file_table));
442        table->files = xcalloc (nfiles, sizeof (struct archive_file_info));
443        table->nfiles = 0;
444    
445        if (!uar_iterate (uar, &list_archive_callback_analyze, (void *) table))
446            {
447                pinfo ("failed to read archive: %s\n", strerror (errno));
448                goto list_archive_end;
449            }
450    
451        for (size_t i = 0; i < nfiles; i++)
452            {
453                struct archive_file_info info = table->files[i];
454                struct tm *tm = localtime (&info.mtime);
455                char mtime_str[10] = "none";
456                const char *mode_str = stringify_mode (info.mode);
457    
458                if (tm == NULL)
459                    {
460                        fprintf (stderr,
461                                 "%s: warning: failed to convert time: %s\n",
462                                 progname, strerror (errno));
463                    }
464                else
465                    {
466                        strftime (mtime_str, sizeof (mtime_str), "%b %d", tm);
467                    }
468    
469                if (params.hr_sizes)
470                    {
471    
472                        fprintf (stdout, "%s %*s %s %s\n", mode_str,
473                                 table->widths[TABLE_WIDTH_SIZE], info.size.str,
474                                 mtime_str, info.name);
475                    }
476                else
477                    {
478    
479                        fprintf (stdout, "%s %*lu %s %s\n", mode_str,
480                                 table->widths[TABLE_WIDTH_SIZE], info.size.bytes,
481                                 mtime_str, info.name);
482                    }
483            }
484    
485    list_archive_end:
486        free (table->files);
487        free (table);
488        uar_close (uar);
489    }
490    
491  int  int
492  main (int argc, char **argv)  main (int argc, char **argv)
493  {  {
# Line 342  main (int argc, char **argv) Line 502  main (int argc, char **argv)
502              if (opt == -1)              if (opt == -1)
503                  break;                  break;
504    
505                if ((opt == 'c' || opt == 'x' || opt == 't')
506                    && params.mode != MODE_NONE)
507                    {
508                        perr ("only one mode can be specified\n");
509                        exit (1);
510                    }
511    
512              switch (opt)              switch (opt)
513                  {                  {
514                  case 'c':                  case 'c':
# Line 361  main (int argc, char **argv) Line 528  main (int argc, char **argv)
528                      debug ("Verbose mode enabled\n", progname);                      debug ("Verbose mode enabled\n", progname);
529                      break;                      break;
530    
531                    case 'm':
532                        params.hr_sizes = true;
533                        break;
534    
535                  case 'f':                  case 'f':
536                      params.file = optarg;                      params.file = optarg;
537                      break;                      break;
# Line 378  main (int argc, char **argv) Line 549  main (int argc, char **argv)
549                      exit (0);                      exit (0);
550    
551                  case '?':                  case '?':
                     usage ();  
                     exit (1);  
   
552                  default:                  default:
553                        debug ("Unknown/Unhandled option: %c\n", opt);
554                        bzero (&params, sizeof (params));
555                      exit (1);                      exit (1);
556                  }                  }
557          }          }
# Line 396  main (int argc, char **argv) Line 566  main (int argc, char **argv)
566    
567                      if (params.file == NULL)                      if (params.file == NULL)
568                          {                          {
569                              perr ("failed to read '%s': %s", file,                              perr ("failed to read '%s': %s\n", file,
570                                    strerror (errno));                                    strerror (errno));
571                              exit (1);                              exit (1);
572                          }                          }
# Line 409  main (int argc, char **argv) Line 579  main (int argc, char **argv)
579    
580      if (params.cwd != NULL)      if (params.cwd != NULL)
581          {          {
582                if (params.mode == MODE_LIST)
583                    {
584                        params.cwd = NULL;
585                        perr ("option '-C' or '--directory' does not make sense in "
586                              "list mode\n");
587                        exit (1);
588                    }
589    
590              char *dir = params.cwd;              char *dir = params.cwd;
591              params.cwd = realpath (dir, NULL);              params.cwd = realpath (dir, NULL);
592    
593              if (params.cwd == NULL)              if (params.cwd == NULL)
594                  {                  {
595                      perr ("failed to change working directory to '%s': %s", dir,                      perr ("failed to change working directory to '%s': %s\n",
596                            strerror (errno));                            dir, strerror (errno));
597                      exit (1);                      exit (1);
598                  }                  }
599          }          }
# Line 434  main (int argc, char **argv) Line 612  main (int argc, char **argv)
612      switch (params.mode)      switch (params.mode)
613          {          {
614          case MODE_CREATE:          case MODE_CREATE:
615                if (params.file == NULL)
616                    {
617                        perr ("no archive file name specified\n");
618                        exit (1);
619                    }
620    
621              for (int i = optind; i < argc; i++)              for (int i = optind; i < argc; i++)
622                  {                  {
623                      params.params.create.targets = xrealloc (                      char *path = realpath (argv[i], NULL);
624                          params.params.create.targets,  
625                          (params.params.create.ntargets + 1) * sizeof (char *));                      if (path == NULL)
626                      params.params.create.targets[params.params.create.ntargets]                          {
627                          = argv[i];                              perr ("failed to read '%s': %s\n", argv[i],
628                      params.params.create.ntargets++;                                    strerror (errno));
629                                exit (1);
630                            }
631    
632                        params.targets
633                            = xrealloc (params.targets,
634                                        (params.ntargets + 1) * sizeof (char *));
635                        params.targets[params.ntargets] = path;
636                        params.ntargets++;
637                    }
638    
639                params.rtargets = argv + optind;
640    
641                if (params.ntargets == 0)
642                    {
643                        perr ("no files or directories specified\n");
644                        exit (1);
645                  }                  }
646    
647              create_archive ();              create_archive ();
648              break;              break;
649    
650          case MODE_EXTRACT:          case MODE_EXTRACT:
651                if (params.file == NULL)
652                    {
653                        perr ("no archive file specified\n");
654                        exit (1);
655                    }
656    
657              extract_archive ();              extract_archive ();
658              break;              break;
659    
660          case MODE_LIST:          case MODE_LIST:
661              assert (false && "Not implemented yet");              if (params.file == NULL)
662                    {
663                        perr ("no archive file specified\n");
664                        exit (1);
665                    }
666    
667                list_archive ();
668              break;              break;
669    
670          default:          default:

Legend:
Removed from v.23  
changed lines
  Added in v.33

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26