1 /* asranlib.c - ranlib for asxxxx arvhives
2 version 1.0.0, April 27th, 2008
4 Copyright (C) 2008-2009 Borut Razem, borut dot razem at siol dot net
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 3, or (at your option) any
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
25 #include "dbuf_string.h"
34 #define NELEM(x) (sizeof (x) / sizeof (*x))
37 static int verbose = 0;
39 static int print_index = 0;
47 if (!(ret = fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, ARMAG, SARMAG) == 0))
54 static int sym_tab_size;
57 get_member_name (char *name, size_t *p_size, int allocate, FILE * libfp)
61 if (0 == memcmp (name, "#1/", 3))
64 size_t len = strtoul (&name [3], &p, 10);
67 /* BSD appends real file name to the file header */
73 char *n = (char *) malloc (len);
74 if (fread (n, 1, len, libfp) != len)
76 /* not an ar archive or broken ar archive */
84 /* just advance the file pointer */
85 fseek (libfp, len, SEEK_CUR);
91 /* not an ar archive or broken ar archive */
103 int name_offset = strtol (++name, &p, 0);
104 if (p != name && name_offset < sym_tab_size)
106 int len = p - name + 1;
107 while (len < AR_NAME_LEN && name[len++] == ' ')
109 if (len == AR_NAME_LEN)
113 /* long name: get it from the symbol table */
114 name = &sym_tab[name_offset];
115 for (p = name; *p != '/' && *p != '\n'; ++p)
116 assert (p < &sym_tab[sym_tab_size]);
118 if (p[0] != '/' || p[1] != '\n')
120 assert (p < &sym_tab[sym_tab_size]);
122 n = (char *) malloc (p - name + 1);
123 memcpy (n, name, p - name);
132 char *p = strrchr (name, '/');
137 while (name[++len] == ' ')
139 if (len == AR_NAME_LEN)
141 char *n = (char *) malloc (p - name + 1);
142 memcpy (n, name, p - name);
149 /* BSD formed member name:
150 trim trailing spaces */
153 p = name + AR_NAME_LEN;
154 while (*--p == ' ' && p >= name)
157 n = (char *) malloc (p - name + 1);
158 memcpy (n, name, p - name);
164 /* bad formed member name:
167 return strdup (name);
174 ar_get_header (struct ar_hdr *hdr, FILE * libfp, char **p_obj_name)
176 char header[ARHDR_LEN];
177 char buf[AR_DATE_LEN + 1];
181 if (fread (header, 1, sizeof (header), libfp) != sizeof (header)
182 || memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
184 /* not an ar archive */
188 memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
189 hdr->ar_name[AR_NAME_LEN] = '\0';
191 memcpy (buf, &header[AR_DATE_OFFSET], AR_DATE_LEN);
192 buf[AR_DATE_LEN] = '\0';
193 hdr->ar_date = strtol (buf, NULL, 0);
195 memcpy (buf, &header[AR_UID_OFFSET], AR_GID_LEN);
196 buf[AR_GID_LEN] = '\0';
197 hdr->ar_uid = (uid_t) strtol (buf, NULL, 0);
199 memcpy (buf, &header[AR_GID_OFFSET], AR_DATE_LEN);
200 buf[AR_DATE_LEN] = '\0';
201 hdr->ar_gid = (gid_t) strtol (buf, NULL, 0);
203 memcpy (buf, &header[AR_MODE_OFFSET], AR_MODE_LEN);
204 buf[AR_MODE_LEN] = '\0';
205 hdr->ar_mode = (mode_t) strtoul (buf, NULL, 0);
207 memcpy (buf, &header[AR_SIZE_OFFSET], AR_SIZE_LEN);
208 buf[AR_SIZE_LEN] = '\0';
209 hdr->ar_size = strtol (buf, NULL, 0);
211 if (NULL == (obj_name = get_member_name (hdr->ar_name, &size, p_obj_name != NULL, libfp)) && p_obj_name != NULL)
213 /* Malformed archive */
217 if (p_obj_name != NULL)
218 *p_obj_name = obj_name;
220 /* treat BSD appended real file name as a part of the header */
221 hdr->ar_size -= size;
223 return size + ARHDR_LEN;
227 get_member_name_by_offset (FILE * fp, long offset)
232 fseek (fp, offset, SEEK_SET);
233 return (ar_get_header (&hdr, fp, &name) != 0) ? name : NULL;
240 struct symbol_s *next;
243 struct symbol_s *symlist, *lastsym;
244 unsigned int offset, first_member_offset;
247 add_symbol (const char *sym, void *param)
251 if ((s = (struct symbol_s *) malloc (sizeof (struct symbol_s))) == NULL)
255 printf ("%s\n", sym);
257 s->name = strdup (sym);
258 s->offset = offset - first_member_offset;
263 lastsym = symlist = s;
275 is_rel (FILE * libfp)
278 long pos = ftell (libfp);
282 if (((c = getc (libfp)) == 'X' || c == 'D' || c == 'Q') && ((c = getc (libfp)) == 'H' || c == 'L'))
284 switch (getc (libfp))
287 if (getc (libfp) == '\n')
299 if (fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, "!FILE ", 6) == 0)
302 fseek (libfp, pos, SEEK_SET);
307 enum_symbols (FILE * fp, long size, int (*func) (const char *sym, void *param), void *param)
311 struct dbuf_s symname;
313 assert (func != NULL);
315 dbuf_init (&buf, 512);
316 dbuf_init (&symname, 32);
318 end = (size >= 0) ? ftell (fp) + size : -1;
321 * Read in the object file. Look for lines that
322 * begin with "S" and end with "D". These are
323 * symbol table definitions. If we find one, see
324 * if it is our symbol. Make sure we only read in
325 * our object file and don't go into the next one.
328 while (end < 0 || ftell (fp) < end)
332 dbuf_set_length (&buf, 0);
333 if (dbuf_getline (&buf, fp) == 0)
336 p = dbuf_c_str (&buf);
342 * Skip everything that's not a symbol record.
344 if ('S' == p[0] && ' ' == p[1])
346 dbuf_set_length (&symname, 0);
348 for (p += 2; *p && ' ' != *p; ++p)
349 dbuf_append_char (&symname, *p);
351 /* If it's an actual symbol, record it */
352 if (' ' == p[0] && 'D' == p[1])
354 if ((*func) (dbuf_c_str (&symname), NULL))
360 dbuf_destroy (&symname);
366 process_symbol_table (struct ar_hdr *hdr, FILE *fp)
368 long pos = ftell (fp);
376 printf ("Archive index:\n");
378 buf = (char *) malloc (hdr->ar_size);
380 if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
391 for (i = 0; i < nsym; ++i)
398 if (NULL == (obj = get_member_name_by_offset (fp, offset))) /* member name */
401 printf ("%s in %s", ps, obj);
403 printf (" at 0x%04x\n", offset);
408 ps += strlen(ps) + 1;
413 fseek (fp, pos, SEEK_SET);
418 /* skip the symbol table */
419 fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
425 process_bsd_symbol_table (struct ar_hdr *hdr, FILE *fp)
427 long pos = ftell (fp);
436 printf ("Archive index:\n");
438 buf = (char *) malloc (hdr->ar_size);
440 if (fread (buf, 1, hdr->ar_size, fp) != hdr->ar_size)
446 tablesize = sgetl (buf);
447 nsym = tablesize / 8;
451 ps = po + tablesize + 4;
453 for (i = 0; i < nsym; ++i)
463 printf ("%s in ", ps + sym);
465 if (NULL == (obj = get_member_name_by_offset (fp, offset))) /* member name */
468 printf ("%s\n", obj);
475 /* skip the symbol table */
476 fseek (fp, pos + hdr->ar_size + (hdr->ar_size & 1), SEEK_SET);
482 get_symbols (FILE * fp, const char *archive)
488 if (!is_ar (fp) || !(hdr_len = ar_get_header (&hdr, fp, &name)))
495 if (AR_IS_SYMBOL_TABLE (name))
499 if (!process_symbol_table (&hdr, fp))
504 else if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
507 else if (AR_IS_BSD_SYMBOL_TABLE (name))
511 if (!process_bsd_symbol_table (&hdr, fp))
516 else if (!(hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)))
519 else if (!verbose && !list)
522 first_member_offset = ftell (fp) - hdr_len;
524 /* walk trough all archive members */
531 printf ("%s%s\n", name, verbose ? ":" : "");
537 long mdule_offset = ftell (fp);
539 offset = mdule_offset - hdr_len;
541 enum_symbols (fp, hdr.ar_size, add_symbol, NULL);
543 fseek (fp, mdule_offset + hdr.ar_size + (hdr.ar_size & 1), SEEK_SET);
553 fprintf (stderr, "asranlib: %s: File format not recognized\n", name);
557 /* skip if the member is not a .REL format */
558 fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
561 while ((hdr_len = ar_get_header (&hdr, fp, (verbose || list) ? &name : NULL)));
563 return feof (fp) ? 1 : 0;
567 do_ranlib (const char *archive)
571 if (NULL == (infp = fopen (archive, "rb")))
573 fprintf (stderr, "asranlib: %s: ", archive);
578 if (!get_symbols (infp, archive))
580 fprintf (stderr, "asranlib: %s: Malformed archive\n", archive);
584 else if (!list && !print_index)
587 struct symbol_s *symp;
593 char tmpfile[] = "arXXXXXX";
594 struct stat stat_buf;
598 if (NULL == _mktemp (tmpfile) || NULL == (outfp = fopen (tmpfile, "wb")))
601 fprintf (stderr, "asranlib: %s: ", tmpfile);
606 if ((pad = mkstemp (tmpfile)) < 0)
609 fprintf (stderr, "asranlib: %s: ", tmpfile);
614 if (NULL == (outfp = fdopen (pad, "wb")))
623 /* calculate the size of symbol table */
624 for (str_length = 0, nsym = 0, symp = symlist; symp; ++nsym, symp = symp->next)
626 str_length += strlen (symp->name) + 1;
629 symtab_size = 4 + 4 * nsym + str_length;
631 fprintf (outfp, ARMAG AR_SYMBOL_TABLE_NAME "%-12d%-6d%-6d%-8d%-10d" ARFMAG, (int) time (NULL), 0, 0, 0, symtab_size);
641 symtab_size += SARMAG + ARHDR_LEN;
644 fwrite (buf, 1, sizeof (buf), outfp);
646 for (symp = symlist; symp; symp = symp->next)
648 sputl (symp->offset + symtab_size, buf);
649 fwrite (buf, 1, sizeof (buf), outfp);
652 for (symp = symlist; symp; symp = symp->next)
654 fputs (symp->name, outfp);
661 fseek (infp, first_member_offset, SEEK_SET);
663 while (EOF != (pad = getc (infp)))
668 if (0 != fstat(fileno(infp), &stat_buf))
670 fprintf (stderr, "asranlib: can't stat %s: ", archive);
680 if (0 != remove (archive))
682 fprintf (stderr, "asranlib: can't remove %s to %s: ", tmpfile, archive);
685 else if (0 != rename (tmpfile, archive))
687 fprintf (stderr, "asranlib: can't rename %s to %s: ", tmpfile, archive);
690 else if (!can_stat || 0 != chmod (archive, stat_buf.st_mode))
692 fprintf (stderr, "asranlib: can't chmod %s: ", archive);
709 printf ("SDCC asxxxx ranlib 1.0.0 $Revision$\n");
730 const char *long_opt;
731 void (*optfnc) (void);
736 { 'v', "verbose", &do_verbose, "Be more verbose about the operation" },
737 { 'V', "version", &print_version, "Print this help message" },
738 { 'h', "help", &usage, "Print version information" },
739 { 't', "list", &do_list, "List the contents of an archive" },
740 { 's', "print-armap", &print_armap, "Print the archive index" },
748 printf ("Usage: asranlib [options] archive\n"
749 " Generate an index to speed access to archives\n"
750 " The options are:\n");
752 for (i = 0; i < NELEM (opts); ++i)
755 if ('\0' != opts[i].short_opt)
756 printf (" -%c ", opts[i].short_opt);
760 if (NULL != opts[i].long_opt)
762 printf ("--%s ", opts[i].long_opt);
763 len += strlen (opts[i].long_opt);
768 printf ("%s\n", opts[i].comment);
771 printf ("asranlib: supported targets: asxxxx\n");
777 main (int argc, char *argv[])
783 for (argp = argv + 1; *argp; ++argp)
785 if (!noopts && (*argp)[0] == '-')
789 if ((*argp)[1] == '-')
791 if ((*argp)[2] == '\0')
800 for (i = 0; i < NELEM (opts); ++i)
802 if (0 == strcmp (&(*argp)[2], opts[i].long_opt))
804 if (NULL != opts[i].optfnc)
806 (*opts[i].optfnc) ();
811 if (i >= NELEM (opts))
813 fprintf (stderr, "asranlib: unrecognized option `%s'\n", *argp);
823 for (optp = &(*argp)[1]; *optp != '\0'; ++optp)
825 for (i = 0; i < NELEM (opts); ++i)
827 if (*optp == opts[i].short_opt)
829 if (NULL != opts[i].optfnc)
831 (*opts[i].optfnc) ();
836 if (i >= NELEM (opts))
838 fprintf (stderr, "asranlib: invalid option -- %c\n", *optp);