/[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 30 by rakinar2, Tue Aug 6 14:24:41 2024 UTC revision 32 by rakinar2, Wed Aug 7 18:57:45 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 175  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 199  initialize (char *argv0) Line 205  initialize (char *argv0)
205          progname = argv0;          progname = argv0;
206  }  }
207    
208  static bool  static bool __attribute__ ((unused))
209  create_archive_callback (struct uar_file *file,  create_archive_callback (struct uar_archive *uar __attribute__ ((unused)),
210                             struct uar_file *file __attribute__ ((unused)),
211                           const char *fullname __attribute__ ((unused)),                           const char *fullname __attribute__ ((unused)),
212                           const char *fullpath __attribute__ ((unused)))                           const char *fullpath)
213  {  {
214      enum uar_file_type type = uar_get_entry_type (file);      if (!params.verbose)
215      pinfo ("adding %s: %s\n",          return true;
216             type == UF_FILE  ? "file"  
217             : type == UF_DIR ? "directory"      fprintf (stdout, "%s\n", fullpath);
                             : "link",  
            uar_get_file_name (file));  
218      return true;      return true;
219  }  }
220    
# Line 218  static void Line 223  static void
223  create_archive (void)  create_archive (void)
224  {  {
225      assert (params.mode == MODE_CREATE);      assert (params.mode == MODE_CREATE);
226      assert (params.params.create.ntargets > 0);      assert (params.ntargets > 0);
227      assert (params.params.create.targets != NULL);      assert (params.targets != NULL);
228    
229      pinfo ("creating archive: %s\n", params.file);      if (params.verbose)
230            pinfo ("creating archive: %s\n", params.file);
231    
232      struct uar_archive *uar = uar_create ();      struct uar_archive *uar = uar_create_stream ();
233    
234      if (uar == NULL || uar_has_error (uar))      if (uar == NULL)
235          {          {
236              pinfo ("failed to create archive: %s\n", strerror (errno));              pinfo ("failed to create archive: %s\n", strerror (errno));
237              return;              return;
238          }          }
239    
240      for (size_t i = 0; i < params.params.create.ntargets; i++)      for (size_t i = 0; i < params.ntargets; i++)
241          {          {
242              struct stat stinfo = { 0 };              struct stat stinfo = { 0 };
243    
244              if (stat (params.params.create.targets[i], &stinfo) != 0)              if (stat (params.targets[i], &stinfo) != 0)
245                  {                  {
246                      perr ("cannot stat '%s': %s\n",                      perr ("cannot stat '%s': %s\n", params.targets[i],
247                            params.params.create.targets[i], strerror (errno));                            strerror (errno));
248                      uar_close (uar);                      uar_close (uar);
249                      return;                      return;
250                  }                  }
251    
252              struct uar_file *file = NULL;              struct uar_file *file = uar_stream_add_entry (
253                    uar, basename (params.rtargets[i]), params.rtargets[i], &stinfo,
254                    &create_archive_callback);
255    
256              if (S_ISREG (stinfo.st_mode))              if (file == NULL || uar_has_error (uar))
                 {  
                     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))  
                 {  
                     file = uar_add_dir (  
                         uar, basename (params.params.create.targets[i]),  
                         params.params.create.targets[i],  
                         &create_archive_callback);  
   
                     if (file == NULL)  
                         {  
                             perr ("failed to add directory: %s (%s)\n",  
                                   strerror (errno), uar_strerror (uar));  
                             uar_close (uar);  
                             return;  
                         }  
                 }  
             else if (S_ISLNK (stinfo.st_mode))  
257                  {                  {
258                      assert (false && "Not implemented");                      const char *error_file = uar_get_error_file (uar);
259                  }                      perr ("failed to add '%s': %s\n",
260              else                            error_file == NULL ? params.targets[i] : error_file,
261                  {                            uar_strerror (uar));
262                      perr ("failed to add file: %s: file type not supported\n",                      exit (1);
                           params.params.create.targets[i]);  
                     uar_close (uar);  
                     return;  
263                  }                  }
   
             assert (file != NULL);  
             uar_file_set_mode (file, stinfo.st_mode & 07777);  
264          }          }
265    
266      pinfo ("writing archive: %s\n", params.file);      if (!uar_stream_write (uar, params.file))
   
     if (!uar_write (uar, params.file))  
267          {          {
268              perr ("failed to write archive: %s\n", strerror (errno));              const char *error_file = uar_get_error_file (uar);
269              uar_close (uar);              pinfo ("failed to write archive: %s%s%s\n",
270                       error_file == NULL ? "" : error_file,
271                       error_file == NULL ? "" : ": ", uar_strerror (uar));
272              return;              return;
273          }          }
274    
# Line 310  create_archive (void) Line 282  create_archive (void)
282  static bool  static bool
283  extract_archive_callback (struct uar_file *file)  extract_archive_callback (struct uar_file *file)
284  {  {
285      pinfo ("extracting: %s\n", uar_get_file_name (file));      pinfo ("extracting: %s\n", uar_file_get_name (file));
286      return true;      return true;
287  }  }
288    
289  /* Extract an archive. */  /* Extract an archive. */
290  static void  static void
291  extract_archive ()  extract_archive (void)
292  {  {
293      assert (params.mode == MODE_EXTRACT);      assert (params.mode == MODE_EXTRACT);
294    
# Line 343  extract_archive () Line 315  extract_archive ()
315      uar_close (uar);      uar_close (uar);
316  }  }
317    
318    static const char *
319    stringify_mode (mode_t mode)
320    {
321        static char str[11];
322    
323        str[0] = S_ISDIR (mode) ? 'd' : S_ISLNK (mode) ? 'l' : '-';
324    
325        for (int i = 1; i < 10; i++)
326            str[i] = mode & (1 << (9 - i)) ? "rwxrwxrwx"[i - 1] : '-';
327    
328        return str;
329    }
330    
331    static int
332    count_dec_numlen (uint64_t num)
333    {
334        int len = 0;
335    
336        do
337            {
338                num /= 10;
339                len++;
340            }
341        while (num > 0);
342    
343        return len;
344    }
345    
346    static char *
347    format_iec_size (uint64_t size)
348    {
349        static char buf[32] = { 0 };
350        const char suffix[] = { ' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' };
351        size_t i = 0;
352        long double computed = size;
353    
354        while (computed > 1024 && i < sizeof (suffix) / sizeof (suffix[0]))
355            {
356                computed /= 1024;
357                i++;
358            }
359    
360        snprintf (buf, sizeof (buf), "%.02Lf%c", computed, suffix[i]);
361        return buf;
362    }
363    
364    struct archive_file_info
365    {
366        mode_t mode;
367        const char *name;
368        time_t mtime;
369        union
370        {
371    
372            uint64_t bytes;
373            char str[64];
374        } size;
375    };
376    
377    struct archive_file_table
378    {
379        struct archive_file_info *files;
380        size_t nfiles;
381        int widths[1];
382    };
383    
384    #define TABLE_WIDTH_SIZE 1
385    
386    static bool
387    list_archive_callback_analyze (struct uar_file *file, void *data)
388    {
389        struct archive_file_table *table = (struct archive_file_table *) data;
390        struct archive_file_info info = { 0 };
391    
392        mode_t mode = uar_file_get_mode (file);
393        const char *name = uar_file_get_name (file);
394        uint64_t size = uar_file_get_size (file);
395        int size_len = 0;
396    
397        info.mode = mode;
398        info.name = name;
399        info.mtime = uar_file_get_mtime (file);
400    
401        if (params.hr_sizes)
402            {
403                char *str = format_iec_size (size);
404                size_len = strlen (str);
405                strncpy (info.size.str, str, 32);
406            }
407        else
408            {
409                info.size.bytes = size;
410                size_len = count_dec_numlen (size);
411            }
412    
413        if (size_len > table->widths[TABLE_WIDTH_SIZE])
414            table->widths[TABLE_WIDTH_SIZE] = size_len;
415    
416        table->files[table->nfiles++] = info;
417        return true;
418    }
419    
420    static void
421    list_archive (void)
422    {
423        assert (params.mode == MODE_LIST);
424        struct archive_file_table *table = NULL;
425        struct uar_archive *uar = uar_open (params.file);
426    
427        if (uar == NULL || uar_has_error (uar))
428            {
429                pinfo ("failed to open archive: %s\n", strerror (errno));
430                goto list_archive_end;
431            }
432    
433        uint64_t nfiles = uar_get_file_count (uar);
434    
435        table = xcalloc (1, sizeof (struct archive_file_table));
436        table->files = xcalloc (nfiles, sizeof (struct archive_file_info));
437        table->nfiles = 0;
438    
439        if (!uar_iterate (uar, &list_archive_callback_analyze, (void *) table))
440            {
441                pinfo ("failed to read archive: %s\n", strerror (errno));
442                goto list_archive_end;
443            }
444    
445        for (size_t i = 0; i < nfiles; i++)
446            {
447                struct archive_file_info info = table->files[i];
448                struct tm *tm = localtime (&info.mtime);
449                char mtime_str[10] = "none";
450                const char *mode_str = stringify_mode (info.mode);
451    
452                if (tm == NULL)
453                    {
454                        fprintf (stderr,
455                                 "%s: warning: failed to convert time: %s\n",
456                                 progname, strerror (errno));
457                    }
458                else
459                    {
460                        strftime (mtime_str, sizeof (mtime_str), "%b %d", tm);
461                    }
462    
463                if (params.hr_sizes)
464                    {
465    
466                        fprintf (stdout, "%s %*s %s %s\n", mode_str,
467                                 table->widths[TABLE_WIDTH_SIZE], info.size.str,
468                                 mtime_str, info.name);
469                    }
470                else
471                    {
472    
473                        fprintf (stdout, "%s %*lu %s %s\n", mode_str,
474                                 table->widths[TABLE_WIDTH_SIZE], info.size.bytes,
475                                 mtime_str, info.name);
476                    }
477            }
478    
479    list_archive_end:
480        free (table->files);
481        free (table);
482        uar_close (uar);
483    }
484    
485  int  int
486  main (int argc, char **argv)  main (int argc, char **argv)
487  {  {
# Line 357  main (int argc, char **argv) Line 496  main (int argc, char **argv)
496              if (opt == -1)              if (opt == -1)
497                  break;                  break;
498    
499                if ((opt == 'c' || opt == 'x' || opt == 't')
500                    && params.mode != MODE_NONE)
501                    {
502                        perr ("only one mode can be specified\n");
503                        exit (1);
504                    }
505    
506              switch (opt)              switch (opt)
507                  {                  {
508                  case 'c':                  case 'c':
# Line 376  main (int argc, char **argv) Line 522  main (int argc, char **argv)
522                      debug ("Verbose mode enabled\n", progname);                      debug ("Verbose mode enabled\n", progname);
523                      break;                      break;
524    
525                    case 'm':
526                        params.hr_sizes = true;
527                        break;
528    
529                  case 'f':                  case 'f':
530                      params.file = optarg;                      params.file = optarg;
531                      break;                      break;
# Line 393  main (int argc, char **argv) Line 543  main (int argc, char **argv)
543                      exit (0);                      exit (0);
544    
545                  case '?':                  case '?':
                     usage ();  
                     exit (1);  
   
546                  default:                  default:
547                        debug ("Unknown/Unhandled option: %c\n", opt);
548                        bzero (&params, sizeof (params));
549                      exit (1);                      exit (1);
550                  }                  }
551          }          }
# Line 424  main (int argc, char **argv) Line 573  main (int argc, char **argv)
573    
574      if (params.cwd != NULL)      if (params.cwd != NULL)
575          {          {
576                if (params.mode == MODE_LIST)
577                    {
578                        params.cwd = NULL;
579                        perr ("option '-C' or '--directory' does not make sense in "
580                              "list mode\n");
581                        exit (1);
582                    }
583    
584              char *dir = params.cwd;              char *dir = params.cwd;
585              params.cwd = realpath (dir, NULL);              params.cwd = realpath (dir, NULL);
586    
# Line 449  main (int argc, char **argv) Line 606  main (int argc, char **argv)
606      switch (params.mode)      switch (params.mode)
607          {          {
608          case MODE_CREATE:          case MODE_CREATE:
609                if (params.file == NULL)
610                    {
611                        perr ("no archive file name specified\n");
612                        exit (1);
613                    }
614    
615              for (int i = optind; i < argc; i++)              for (int i = optind; i < argc; i++)
616                  {                  {
617                      params.params.create.targets = xrealloc (                      char *path = realpath (argv[i], NULL);
618                          params.params.create.targets,  
619                          (params.params.create.ntargets + 1) * sizeof (char *));                      if (path == NULL)
620                      params.params.create.targets[params.params.create.ntargets]                          {
621                          = argv[i];                              perr ("failed to read '%s': %s\n", argv[i],
622                      params.params.create.ntargets++;                                    strerror (errno));
623                                exit (1);
624                            }
625    
626                        params.targets
627                            = xrealloc (params.targets,
628                                        (params.ntargets + 1) * sizeof (char *));
629                        params.targets[params.ntargets] = path;
630                        params.ntargets++;
631                    }
632    
633                params.rtargets = argv + optind;
634    
635                if (params.ntargets == 0)
636                    {
637                        perr ("no files or directories specified\n");
638                        exit (1);
639                  }                  }
640    
641              create_archive ();              create_archive ();
642              break;              break;
643    
644          case MODE_EXTRACT:          case MODE_EXTRACT:
645                if (params.file == NULL)
646                    {
647                        perr ("no archive file specified\n");
648                        exit (1);
649                    }
650    
651              extract_archive ();              extract_archive ();
652              break;              break;
653    
654          case MODE_LIST:          case MODE_LIST:
655              assert (false && "Not implemented yet");              if (params.file == NULL)
656                    {
657                        perr ("no archive file specified\n");
658                        exit (1);
659                    }
660    
661                list_archive ();
662              break;              break;
663    
664          default:          default:

Legend:
Removed from v.30  
changed lines
  Added in v.32

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26