* as/asranlib/asranlib.c: retain the original file mode
[fw/sdcc] / as / asranlib / asranlib.c
index e5449cfdf7490cb8c2e68b4b68b2169d6f57e3ce..dde9f787a24beafdac75ff1f1607070418072b89 100644 (file)
@@ -5,7 +5,7 @@
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
 
 This program is free software; you can redistribute it and/or modify it
 under the terms of the GNU General Public License as published by the
-Free Software Foundation; either version 2, or (at your option) any
+Free Software Foundation; either version 3, or (at your option) any
 later version.
 
 This program is distributed in the hope that it will be useful,
 later version.
 
 This program is distributed in the hope that it will be useful,
@@ -14,8 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
+along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <stdio.h>
 #include <stdlib.h>
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -27,12 +26,17 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
 
 #ifdef _WIN32
 #include <io.h>
 
 #ifdef _WIN32
 #include <io.h>
-#define mktemp  _mktemp
+#else
+#include <unistd.h>
 #endif
 
 #define NELEM(x)  (sizeof (x) / sizeof (*x))
 
 
 #endif
 
 #define NELEM(x)  (sizeof (x) / sizeof (*x))
 
 
+static int verbose = 0;
+static int list = 0;
+static int print_index = 0;
+
 int
 is_ar (FILE * libfp)
 {
 int
 is_ar (FILE * libfp)
 {
@@ -45,17 +49,139 @@ is_ar (FILE * libfp)
   return ret;
 }
 
   return ret;
 }
 
-static struct ar_hdr *
-ar_get_header (struct ar_hdr *hdr, FILE * libfp)
+static char *sym_tab;
+static int sym_tab_size;
+
+char *
+get_member_name (char *name, size_t *p_size, int allocate, FILE * libfp)
+{
+  *p_size = 0;
+
+  if (0 == memcmp (name, "#1/", 3))
+    {
+      char *p;
+      size_t len = strtoul (&name [3], &p, 10);
+      if (p > &name [3])
+        {
+          /* BSD appends real file name to the file header */
+          if (p_size != NULL)
+            *p_size = len;
+
+          if (allocate)
+            {
+              char *n = (char *) malloc (len);
+              if (fread (n, 1, len, libfp) != len)
+                {
+                  /* not an ar archive or broken ar archive */
+                  return NULL;
+                }
+              else
+                return n;
+            }
+          else
+            {
+              /* just advance the file pointer */
+              fseek (libfp, len, SEEK_CUR);
+              return NULL;
+            }
+        }
+      else
+        {
+          /* not an ar archive or broken ar archive */
+          return NULL;
+        }
+    }
+  else if (allocate)
+    {
+      if (name[0] == '/')
+        {
+          if (NULL != sym_tab)
+            {
+              char *p;
+
+              int name_offset = strtol (++name, &p, 0);
+              if (p != name && name_offset < sym_tab_size)
+                {
+                  int len = p - name + 1;
+                  while (len < AR_NAME_LEN && name[len++] == ' ')
+                    ;
+                  if (len == AR_NAME_LEN)
+                    {
+                      char *n;
+
+                      /* long name: get it from the symbol table */
+                      name = &sym_tab[name_offset];
+                      for (p = name; *p != '/' && *p != '\n'; ++p)
+                        assert (p < &sym_tab[sym_tab_size]);
+
+                      if (p[0] != '/' || p[1] != '\n')
+                        while (*++p != '\n')
+                          assert (p < &sym_tab[sym_tab_size]);
+
+                      n = (char *) malloc (p - name + 1);
+                      memcpy (n, name, p - name);
+                      n[p - name] = '\0';
+                      return n;
+                    }
+                }
+            }
+        }
+      else
+        {
+          char *p = strrchr (name, '/');
+
+          if (NULL != p)
+            {
+              int len = p - name;
+              while (name[++len] == ' ')
+                ;
+              if (len == AR_NAME_LEN)
+                {
+                  char *n = (char *) malloc (p - name + 1);
+                  memcpy (n, name, p - name);
+                  n[p - name] = '\0';
+                  return n;
+                }
+            }
+          else
+            {
+              /* BSD formed member name:
+                 trim trailing spaces */
+              char *n;
+
+              p = name + AR_NAME_LEN;
+              while (*--p == ' ' && p >= name)
+                ;
+              ++p;
+              n = (char *) malloc (p - name + 1);
+              memcpy (n, name, p - name);
+              n[p - name] = '\0';
+              return n;
+            }
+        }
+
+      /* bad formed member name:
+       just return it */
+
+      return strdup (name);
+    }
+  else
+    return NULL;
+}
+
+size_t
+ar_get_header (struct ar_hdr *hdr, FILE * libfp, char **p_obj_name)
 {
   char header[ARHDR_LEN];
   char buf[AR_DATE_LEN + 1];
 {
   char header[ARHDR_LEN];
   char buf[AR_DATE_LEN + 1];
+  char *obj_name;
+  size_t size;
 
 
-  if (fread (header, 1, sizeof (header), libfp) != sizeof (header) ||
-      memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
+  if (fread (header, 1, sizeof (header), libfp) != sizeof (header)
+      || memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
     {
       /* not an ar archive */
     {
       /* not an ar archive */
-      return NULL;
+      return 0;
     }
 
   memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
     }
 
   memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
@@ -81,14 +207,35 @@ ar_get_header (struct ar_hdr *hdr, FILE * libfp)
   buf[AR_SIZE_LEN] = '\0';
   hdr->ar_size = strtol (buf, NULL, 0);
 
   buf[AR_SIZE_LEN] = '\0';
   hdr->ar_size = strtol (buf, NULL, 0);
 
-  return hdr;
+  if (NULL == (obj_name = get_member_name (hdr->ar_name, &size, p_obj_name != NULL, libfp)) && p_obj_name != NULL)
+    {
+      /* Malformed archive */
+      return 0;
+    }
+
+  if (p_obj_name != NULL)
+    *p_obj_name = obj_name;
+
+  /* treat BSD appended real file name as a part of the header */
+  hdr->ar_size -= size;
+
+  return size + ARHDR_LEN;
 }
 
 }
 
+static char *
+get_member_name_by_offset (FILE * fp, long offset)
+{
+  struct ar_hdr hdr;
+  char *name;
+
+  fseek (fp, offset, SEEK_SET);
+  return (ar_get_header (&hdr, fp, &name) != 0) ? name : NULL;
+}
 
 struct symbol_s
   {
     const char *name;
 
 struct symbol_s
   {
     const char *name;
-    off_t offset;
+    size_t offset;
     struct symbol_s *next;
   };
 
     struct symbol_s *next;
   };
 
@@ -103,6 +250,9 @@ add_symbol (const char *sym, void *param)
   if ((s = (struct symbol_s *) malloc (sizeof (struct symbol_s))) == NULL)
     return 0;
 
   if ((s = (struct symbol_s *) malloc (sizeof (struct symbol_s))) == NULL)
     return 0;
 
+  if (verbose)
+    printf ("%s\n", sym);
+
   s->name = strdup (sym);
   s->offset = offset - first_member_offset;
   s->next = NULL;
   s->name = strdup (sym);
   s->offset = offset - first_member_offset;
   s->next = NULL;
@@ -120,7 +270,6 @@ add_symbol (const char *sym, void *param)
   return 0;
 }
 
   return 0;
 }
 
-
 int
 is_rel (FILE * libfp)
 {
 int
 is_rel (FILE * libfp)
 {
@@ -212,56 +361,207 @@ enum_symbols (FILE * fp, long size, int (*func) (const char *sym, void *param),
   return 0;
 }
 
   return 0;
 }
 
+static int
+process_symbol_table (struct ar_hdr *hdr, FILE *fp)
+{
+  long pos = ftell (fp);
+
+  if (print_index)
+    {
+      char *buf, *po, *ps;
+      int i;
+      long nsym;
+
+      printf ("Archive index:\n");
+
+      buf = (char *) malloc (hdr->ar_size);
+
+      if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
+        {
+          free (buf);
+          return 0;
+        }
+
+      nsym = sgetl (buf);
+
+      po = buf + 4;
+      ps = po + nsym * 4;
+
+      for (i = 0; i < nsym; ++i)
+        {
+          char *obj;
+
+          offset = sgetl (po);
+          po += 4;
+
+          if (NULL == (obj = get_member_name_by_offset (fp, offset))) /* member name */
+            return 0;
+
+          printf ("%s in %s", ps, obj);
+          if (verbose)
+            printf (" at 0x%04x\n", offset);
+          else
+            putchar ('\n');
+          free (obj);
+
+          ps += strlen(ps) + 1;
+        
+        }
+      free (buf);
+
+      fseek (fp, pos, SEEK_SET);
+
+      putchar ('\n');
+    }
+
+  /* skip the symbol table */
+  fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
+
+  return 1;
+}
+
+static int
+process_bsd_symbol_table (struct ar_hdr *hdr, FILE *fp)
+{
+  long pos = ftell (fp);
+
+  if (print_index)
+    {
+      char *buf, *po, *ps;
+      int i;
+      long tablesize;
+      long nsym;
+
+      printf ("Archive index:\n");
+
+      buf = (char *) malloc (hdr->ar_size);
+
+      if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
+        {
+          free (buf);
+          return 0;
+        }
+
+      tablesize = sgetl (buf);
+      nsym = tablesize / 8;
+
+      po = buf + 4;
+
+      ps = po + tablesize + 4;
+
+      for (i = 0; i < nsym; ++i)
+        {
+          char *obj;
+          long sym;
+
+          sym = sgetl (po);
+          po += 4;
+          offset = sgetl (po);
+          po += 4;
+
+          printf ("%s in ", ps + sym);
+
+          if (NULL == (obj = get_member_name_by_offset (fp, offset))) /* member name */
+            return 0;
+
+          printf ("%s\n", obj);
+          free (obj);
+        }
+      free (buf);
+      putchar ('\n');
+    }
+
+  /* skip the symbol table */
+  fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
+
+  return 1;
+}
+
 int
 get_symbols (FILE * fp, const char *archive)
 {
   struct ar_hdr hdr;
 int
 get_symbols (FILE * fp, const char *archive)
 {
   struct ar_hdr hdr;
+  size_t hdr_len;
+  char *name;
 
 
-  if (!is_ar (fp) || !ar_get_header (&hdr, fp))
+  if (!is_ar (fp) || !(hdr_len = ar_get_header (&hdr, fp, &name)))
     {
     {
-      fprintf (stderr, "asranlib: %s: File format not recognized\n", archive);
-      exit (1);
+      free (name);
+
+      return 0;
     }
 
     }
 
-  if (AR_IS_SYMBOL_TABLE (hdr))
+  if (AR_IS_SYMBOL_TABLE (name))
     {
     {
-      /* skip the symbol table */
-      fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
+      free (name);
+
+      if (!process_symbol_table (&hdr, fp))
+        return 0;
+
+      if (feof (fp))
+        return 1;
+      else if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
+        return 0;
     }
     }
+  else if (AR_IS_BSD_SYMBOL_TABLE (name))
+    {
+      free (name);
+
+      if (!process_bsd_symbol_table (&hdr, fp))
+        return 0;
+
+      if (feof (fp))
+        return 1;
+      else if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
+        return 0;
+    }
+  else if (!verbose && !list)
+    free (name);
 
 
-  first_member_offset = ftell (fp) - ARHDR_LEN;
+  first_member_offset = ftell (fp) - hdr_len;
 
   /* walk trough all archive members */
   do
     {
       if (is_rel (fp))
         {
 
   /* walk trough all archive members */
   do
     {
       if (is_rel (fp))
         {
-          long mdule_offset = ftell (fp);
+          if (verbose || list)
+            {
+              printf ("%s%s\n", name, verbose ? ":" : "");
+              free (name);
+            }
 
 
-          offset = mdule_offset - ARHDR_LEN;
+          if (!list)
+            {
+              long mdule_offset = ftell (fp);
 
 
-          enum_symbols (fp, hdr.ar_size, add_symbol, NULL);
+              offset = mdule_offset - hdr_len;
 
 
-          fseek (fp, mdule_offset + hdr.ar_size, SEEK_SET);
+              enum_symbols (fp, hdr.ar_size, add_symbol, NULL);
 
 
-          if (hdr.ar_size & 1)
-            {
-              int c = getc (fp);
-              assert (c == EOF || c == '\n');
+              fseek (fp, mdule_offset + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
             }
             }
+
+          if (verbose)
+            putchar ('\n');
         }
       else
         {
         }
       else
         {
+          if (verbose || list)
+            {
+              fprintf (stderr, "asranlib: %s: File format not recognized\n", name);
+              free (name);
+            }
+
           /* skip if the member is not a .REL format */
           fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
         }
     }
           /* skip if the member is not a .REL format */
           fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
         }
     }
-  while (ar_get_header (&hdr, fp));
+  while ((hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)));
 
 
-  return 1;
+  return feof (fp) ? 1 : 0;
 }
 
 }
 
-
 void
 do_ranlib (const char *archive)
 {
 void
 do_ranlib (const char *archive)
 {
@@ -274,18 +574,36 @@ do_ranlib (const char *archive)
       exit (1);
     }
 
       exit (1);
     }
 
-  if (get_symbols (infp, archive))
+  if (!get_symbols (infp, archive))
+    {
+      fprintf (stderr, "asranlib: %s: Malformed archive\n", archive);
+      fclose (infp);
+      exit (1);
+    }
+  else if (!list && !print_index)
     {
     {
-      FILE *outfp = NULL;
+      FILE *outfp;
       struct symbol_s *symp;
       char buf[4];
       struct symbol_s *symp;
       char buf[4];
-      int str_length = 0;
-      int pad = 0;
+      int str_length;
+      int pad;
       int nsym;
       int symtab_size;
       char tmpfile[] = "arXXXXXX";
       int nsym;
       int symtab_size;
       char tmpfile[] = "arXXXXXX";
+      struct stat stat_buf;
+      int can_stat;
 
 
-      if (NULL == mktemp (tmpfile) || NULL == (outfp = fopen (tmpfile, "wb")))
+      /* TODO: create tmpfile in temporery directory (TMP, TMPDIR, /usr/tmp, /tmp) */
+#ifdef _WIN32
+      if (NULL == _mktemp (tmpfile) || NULL == (outfp = fopen (tmpfile, "wb")))
+        {
+          fclose (infp);
+          fprintf (stderr, "asranlib: %s: ", tmpfile);
+          perror (NULL);
+          exit (1);
+        }
+#else
+      if ((pad = mkstemp (tmpfile)) < 0)
         {
           fclose (infp);
           fprintf (stderr, "asranlib: %s: ", tmpfile);
         {
           fclose (infp);
           fprintf (stderr, "asranlib: %s: ", tmpfile);
@@ -293,8 +611,17 @@ do_ranlib (const char *archive)
           exit (1);
         }
 
           exit (1);
         }
 
+      if (NULL == (outfp = fdopen (pad, "wb")))
+        {
+          close (pad);
+          fclose (infp);
+          perror ("asranlib");
+          exit (1);
+        }
+#endif
+
       /* calculate the size of symbol table */
       /* calculate the size of symbol table */
-      for (nsym = 0, symp = symlist; symp; ++nsym, symp = symp->next)
+      for (str_length = 0, nsym = 0, symp = symlist; symp; ++nsym, symp = symp->next)
         {
           str_length += strlen (symp->name) + 1;
         }
         {
           str_length += strlen (symp->name) + 1;
         }
@@ -322,7 +649,6 @@ do_ranlib (const char *archive)
           fwrite (buf, 1, sizeof (buf), outfp);
         }
 
           fwrite (buf, 1, sizeof (buf), outfp);
         }
 
-
       for (symp = symlist; symp; symp = symp->next)
         {
           fputs (symp->name, outfp);
       for (symp = symlist; symp; symp = symp->next)
         {
           fputs (symp->name, outfp);
@@ -338,6 +664,17 @@ do_ranlib (const char *archive)
         putc (pad, outfp);
 
       fclose (outfp);
         putc (pad, outfp);
 
       fclose (outfp);
+
+      if (0 != fstat(fileno(infp), &stat_buf))
+        {
+          fprintf (stderr, "asranlib: can't stat %s: ", infp);
+          perror (NULL);
+          fclose (infp);
+          can_stat = 0;
+        }
+      else
+        can_stat = 1;
+
       fclose (infp);
 
       if (0 != remove (archive))
       fclose (infp);
 
       if (0 != remove (archive))
@@ -350,11 +687,21 @@ do_ranlib (const char *archive)
           fprintf (stderr, "asranlib: can't rename %s to %s: ", tmpfile, archive);
           perror (NULL);
         }
           fprintf (stderr, "asranlib: can't rename %s to %s: ", tmpfile, archive);
           perror (NULL);
         }
+      else if (!can_stat || 0 != chmod (archive, stat_buf.st_mode))
+        {
+          fprintf (stderr, "asranlib: can't chmod %s: ", archive);
+          perror (NULL);
+        }
     }
   else
     fclose (infp);
 }
 
     }
   else
     fclose (infp);
 }
 
+void
+do_verbose (void)
+{
+  verbose = 1;
+}
 
 void
 print_version (void)
 
 void
 print_version (void)
@@ -363,80 +710,147 @@ print_version (void)
   exit (0);
 }
 
   exit (0);
 }
 
-
 void
 void
-usage (void)
+do_list (void)
 {
 {
-  printf ("Usage: asranlib [options] archive\n"
-    " Generate an index to speed access to archives\n"
-    " The options are:\n"
-    "  -h --help                    Print this help message\n"
-    "  -V --version                 Print version information\n"
-    "asranlib: supported targets: asxxxx\n");
+  list = 1;
+}
 
 
-  exit (1);
+void
+print_armap (void)
+{
+  print_index = 1;
 }
 
 }
 
+void usage (void);
 
 struct opt_s
   {
 
 struct opt_s
   {
-    const char *opt;
+    char short_opt;
+    const char *long_opt;
     void (*optfnc) (void);
     void (*optfnc) (void);
+    const char *comment;
   }
 opts[] =
   {
   }
 opts[] =
   {
-    { "-v", &print_version, },
-    { "-V", &print_version, },
-    { "--version", &print_version, },
-    { "-h", &usage, },
-    { "--help", &usage, },
+    { 'v', "verbose", &do_verbose, "Be more verbose about the operation" },
+    { 'V', "version", &print_version, "Print this help message" },
+    { 'h', "help", &usage, "Print version information" },
+    { 't', "list", &do_list, "List the contents of an archive" },
+    { 's', "print-armap", &print_armap, "Print the archive index" },
   };
 
   };
 
-
 void
 void
-process_options (int argc, char *argv[])
+usage (void)
+{
+  int i;
+
+  printf ("Usage: asranlib [options] archive\n"
+    " Generate an index to speed access to archives\n"
+    " The options are:\n");
+
+  for (i = 0; i < NELEM (opts); ++i)
+    {
+      int len = 5;
+      if ('\0' != opts[i].short_opt)
+        printf ("  -%c ", opts[i].short_opt);
+      else
+        printf ("     ");
+
+      if (NULL != opts[i].long_opt)
+        {
+          printf ("--%s ", opts[i].long_opt);
+          len += strlen (opts[i].long_opt);
+        }
+
+      while (len++ < 30)
+        putchar (' ');
+      printf ("%s\n", opts[i].comment);
+    }
+
+  printf ("asranlib: supported targets: asxxxx\n");
+
+  exit (1);
+}
+
+int
+main (int argc, char *argv[])
 {
   char **argp;
   int noopts = 0;
   int narch = 0;
 {
   char **argp;
   int noopts = 0;
   int narch = 0;
+
   for (argp = argv + 1; *argp; ++argp)
     {
       if (!noopts && (*argp)[0] == '-')
         {
           int i;
 
   for (argp = argv + 1; *argp; ++argp)
     {
       if (!noopts && (*argp)[0] == '-')
         {
           int i;
 
-          if ((*argp)[1] == '-' && (*argp)[2] == '\0')
+          if ((*argp)[1] == '-')
             {
             {
-              noopts = 1;
-              continue;
+              if ((*argp)[2] == '\0')
+                {
+                  /* end of options */
+                  noopts = 1;
+                  continue;
+                }
+              else
+                {
+                  /* long option */
+                  for (i = 0; i < NELEM (opts); ++i)
+                    {
+                      if (0 == strcmp (&(*argp)[2], opts[i].long_opt))
+                        {
+                          if (NULL != opts[i].optfnc)
+                            {
+                              (*opts[i].optfnc) ();
+                              break;
+                            }
+                        }
+                    }
+                  if (i >= NELEM (opts))
+                    {
+                      fprintf (stderr, "asranlib: unrecognized option `%s'\n", *argp);
+                      usage ();
+                    }
+                }
             }
             }
-
-          for (i = 0; i < NELEM (opts); ++i)
+          else
             {
             {
-              if (0 == strcmp (*argp, opts[i].opt))
+              char *optp;
+
+              /* short option */
+              for (optp = &(*argp)[1]; *optp != '\0'; ++optp)
                 {
                 {
-                  if (NULL != opts[i].optfnc)
+                  for (i = 0; i < NELEM (opts); ++i)
+                    {
+                      if (*optp == opts[i].short_opt)
+                        {
+                          if (NULL != opts[i].optfnc)
+                            {
+                              (*opts[i].optfnc) ();
+                              break;
+                            }
+                        }
+                    }
+                  if (i >= NELEM (opts))
                     {
                     {
-                      (*opts[i].optfnc) ();
-                      continue;
+                      fprintf (stderr, "asranlib: invalid option -- %c\n", *optp);
+                      usage ();
                     }
                 }
             }
         }
                     }
                 }
             }
         }
-
-      do_ranlib (*argp);
-      ++narch;
+      else
+        {
+          /* not an option */
+          do_ranlib (*argp);
+          ++narch;
+        }
     }
 
   if (!narch)
     usage ();
     }
 
   if (!narch)
     usage ();
-}
-
-
-int
-main (int argc, char *argv[])
-{
-  process_options (argc, argv);
 
   return 0;
 }
 
   return 0;
 }