e5449cfdf7490cb8c2e68b4b68b2169d6f57e3ce
[fw/sdcc] / as / asranlib / asranlib.c
1 /* asranlib.c - ranlib for asxxxx arvhives
2    version 1.0.0, April 27th, 2008
3
4    Copyright (C) 2008-2009 Borut Razem, borut dot razem at siol dot net
5
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 2, or (at your option) any
9 later version.
10
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.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <assert.h>
24 #include <time.h>
25 #include "dbuf_string.h"
26 #include "lkar.h"
27
28 #ifdef _WIN32
29 #include <io.h>
30 #define mktemp  _mktemp
31 #endif
32
33 #define NELEM(x)  (sizeof (x) / sizeof (*x))
34
35
36 int
37 is_ar (FILE * libfp)
38 {
39   char buf[SARMAG];
40   int ret;
41
42   if (!(ret = fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, ARMAG, SARMAG) == 0))
43     rewind (libfp);
44
45   return ret;
46 }
47
48 static struct ar_hdr *
49 ar_get_header (struct ar_hdr *hdr, FILE * libfp)
50 {
51   char header[ARHDR_LEN];
52   char buf[AR_DATE_LEN + 1];
53
54   if (fread (header, 1, sizeof (header), libfp) != sizeof (header) ||
55       memcmp (header + AR_FMAG_OFFSET, ARFMAG, AR_FMAG_LEN) != 0)
56     {
57       /* not an ar archive */
58       return NULL;
59     }
60
61   memcpy (hdr->ar_name, &header[AR_NAME_OFFSET], AR_NAME_LEN);
62   hdr->ar_name[AR_NAME_LEN] = '\0';
63
64   memcpy (buf, &header[AR_DATE_OFFSET], AR_DATE_LEN);
65   buf[AR_DATE_LEN] = '\0';
66   hdr->ar_date = strtol (buf, NULL, 0);
67
68   memcpy (buf, &header[AR_UID_OFFSET], AR_GID_LEN);
69   buf[AR_GID_LEN] = '\0';
70   hdr->ar_uid = (uid_t) strtol (buf, NULL, 0);
71
72   memcpy (buf, &header[AR_GID_OFFSET], AR_DATE_LEN);
73   buf[AR_DATE_LEN] = '\0';
74   hdr->ar_gid = (gid_t) strtol (buf, NULL, 0);
75
76   memcpy (buf, &header[AR_MODE_OFFSET], AR_MODE_LEN);
77   buf[AR_MODE_LEN] = '\0';
78   hdr->ar_mode = (mode_t) strtoul (buf, NULL, 0);
79
80   memcpy (buf, &header[AR_SIZE_OFFSET], AR_SIZE_LEN);
81   buf[AR_SIZE_LEN] = '\0';
82   hdr->ar_size = strtol (buf, NULL, 0);
83
84   return hdr;
85 }
86
87
88 struct symbol_s
89   {
90     const char *name;
91     off_t offset;
92     struct symbol_s *next;
93   };
94
95 struct symbol_s *symlist, *lastsym;
96 unsigned int offset, first_member_offset;
97
98 int
99 add_symbol (const char *sym, void *param)
100 {
101   struct symbol_s *s;
102
103   if ((s = (struct symbol_s *) malloc (sizeof (struct symbol_s))) == NULL)
104     return 0;
105
106   s->name = strdup (sym);
107   s->offset = offset - first_member_offset;
108   s->next = NULL;
109
110   if (NULL == symlist)
111     {
112       lastsym = symlist = s;
113     }
114   else
115     {
116       lastsym->next = s;
117       lastsym = s;
118     }
119
120   return 0;
121 }
122
123
124 int
125 is_rel (FILE * libfp)
126 {
127   int c;
128   long pos = ftell (libfp);
129   int ret = 0;
130
131   /* [XDQ][HL] */
132   if (((c = getc (libfp)) == 'X' || c == 'D' || c == 'Q') && ((c = getc (libfp)) == 'H' || c == 'L'))
133     {
134       switch (getc (libfp))
135         {
136         case '\r':
137           if (getc (libfp) == '\n')
138             ret = 1;
139           break;
140
141         case '\n':
142           ret = 1;
143         }
144     }
145   else if (c == ';')
146     {
147       char buf[6];
148
149       if (fread (buf, 1, sizeof (buf), libfp) == sizeof (buf) && memcmp (buf, "!FILE ", 6) == 0)
150         ret = 1;
151     }
152   fseek (libfp, pos, SEEK_SET);
153   return ret;
154 }
155
156 int
157 enum_symbols (FILE * fp, long size, int (*func) (const char *sym, void *param), void *param)
158 {
159   long end;
160   struct dbuf_s buf;
161   struct dbuf_s symname;
162
163   assert (func != NULL);
164
165   dbuf_init (&buf, 512);
166   dbuf_init (&symname, 32);
167
168   end = (size >= 0) ? ftell (fp) + size : -1;
169
170   /*
171    * Read in the object file.  Look for lines that
172    * begin with "S" and end with "D".  These are
173    * symbol table definitions.  If we find one, see
174    * if it is our symbol.  Make sure we only read in
175    * our object file and don't go into the next one.
176    */
177
178   while (end < 0 || ftell (fp) < end)
179     {
180       const char *p;
181
182       dbuf_set_length (&buf, 0);
183       if (dbuf_getline (&buf, fp) == 0)
184         break;
185
186       p = dbuf_c_str (&buf);
187
188       if ('T' == p[0])
189         break;
190
191       /*
192        * Skip everything that's not a symbol record.
193        */
194       if ('S' == p[0] && ' ' == p[1])
195         {
196           dbuf_set_length (&symname, 0);
197
198           for (p += 2; *p && ' ' != *p; ++p)
199             dbuf_append_char (&symname, *p);
200
201           /* If it's an actual symbol, record it */
202           if (' ' == p[0] && 'D' == p[1])
203             if (func != NULL)
204               if ((*func) (dbuf_c_str (&symname), NULL))
205                 return 1;
206         }
207     }
208
209   dbuf_destroy (&buf);
210   dbuf_destroy (&symname);
211
212   return 0;
213 }
214
215 int
216 get_symbols (FILE * fp, const char *archive)
217 {
218   struct ar_hdr hdr;
219
220   if (!is_ar (fp) || !ar_get_header (&hdr, fp))
221     {
222       fprintf (stderr, "asranlib: %s: File format not recognized\n", archive);
223       exit (1);
224     }
225
226   if (AR_IS_SYMBOL_TABLE (hdr))
227     {
228       /* skip the symbol table */
229       fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
230     }
231
232   first_member_offset = ftell (fp) - ARHDR_LEN;
233
234   /* walk trough all archive members */
235   do
236     {
237       if (is_rel (fp))
238         {
239           long mdule_offset = ftell (fp);
240
241           offset = mdule_offset - ARHDR_LEN;
242
243           enum_symbols (fp, hdr.ar_size, add_symbol, NULL);
244
245           fseek (fp, mdule_offset + hdr.ar_size, SEEK_SET);
246
247           if (hdr.ar_size & 1)
248             {
249               int c = getc (fp);
250               assert (c == EOF || c == '\n');
251             }
252         }
253       else
254         {
255           /* skip if the member is not a .REL format */
256           fseek (fp, hdr.ar_size + (hdr.ar_size & 1), SEEK_CUR);
257         }
258     }
259   while (ar_get_header (&hdr, fp));
260
261   return 1;
262 }
263
264
265 void
266 do_ranlib (const char *archive)
267 {
268   FILE *infp;
269
270   if (NULL == (infp = fopen (archive, "rb")))
271     {
272       fprintf (stderr, "asranlib: %s: ", archive);
273       perror (NULL);
274       exit (1);
275     }
276
277   if (get_symbols (infp, archive))
278     {
279       FILE *outfp = NULL;
280       struct symbol_s *symp;
281       char buf[4];
282       int str_length = 0;
283       int pad = 0;
284       int nsym;
285       int symtab_size;
286       char tmpfile[] = "arXXXXXX";
287
288       if (NULL == mktemp (tmpfile) || NULL == (outfp = fopen (tmpfile, "wb")))
289         {
290           fclose (infp);
291           fprintf (stderr, "asranlib: %s: ", tmpfile);
292           perror (NULL);
293           exit (1);
294         }
295
296       /* calculate the size of symbol table */
297       for (nsym = 0, symp = symlist; symp; ++nsym, symp = symp->next)
298         {
299           str_length += strlen (symp->name) + 1;
300         }
301
302       symtab_size = 4 + 4 * nsym + str_length;
303
304       fprintf (outfp, ARMAG AR_SYMBOL_TABLE_NAME "%-12d%-6d%-6d%-8d%-10d" ARFMAG, (int) time (NULL), 0, 0, 0, symtab_size);
305
306       if (symtab_size & 1)
307         {
308           pad = 1;
309           ++symtab_size;
310         }
311       else
312         pad = 0;
313
314       symtab_size += SARMAG + ARHDR_LEN;
315
316       sputl (nsym, buf);
317       fwrite (buf, 1, sizeof (buf), outfp);
318
319       for (symp = symlist; symp; symp = symp->next)
320         {
321           sputl (symp->offset + symtab_size, buf);
322           fwrite (buf, 1, sizeof (buf), outfp);
323         }
324
325
326       for (symp = symlist; symp; symp = symp->next)
327         {
328           fputs (symp->name, outfp);
329           putc ('\0', outfp);
330         }
331
332       if (pad)
333         putc ('\n', outfp);
334
335       fseek (infp, first_member_offset, SEEK_SET);
336
337       while (EOF != (pad = getc (infp)))
338         putc (pad, outfp);
339
340       fclose (outfp);
341       fclose (infp);
342
343       if (0 != remove (archive))
344         {
345           fprintf (stderr, "asranlib: can't remove %s to %s: ", tmpfile, archive);
346           perror (NULL);
347         }
348       else if (0 != rename (tmpfile, archive))
349         {
350           fprintf (stderr, "asranlib: can't rename %s to %s: ", tmpfile, archive);
351           perror (NULL);
352         }
353     }
354   else
355     fclose (infp);
356 }
357
358
359 void
360 print_version (void)
361 {
362   printf ("SDCC asxxxx ranlib 1.0.0 $Revision$\n");
363   exit (0);
364 }
365
366
367 void
368 usage (void)
369 {
370   printf ("Usage: asranlib [options] archive\n"
371     " Generate an index to speed access to archives\n"
372     " The options are:\n"
373     "  -h --help                    Print this help message\n"
374     "  -V --version                 Print version information\n"
375     "asranlib: supported targets: asxxxx\n");
376
377   exit (1);
378 }
379
380
381 struct opt_s
382   {
383     const char *opt;
384     void (*optfnc) (void);
385   }
386 opts[] =
387   {
388     { "-v", &print_version, },
389     { "-V", &print_version, },
390     { "--version", &print_version, },
391     { "-h", &usage, },
392     { "--help", &usage, },
393   };
394
395
396 void
397 process_options (int argc, char *argv[])
398 {
399   char **argp;
400   int noopts = 0;
401   int narch = 0;
402   for (argp = argv + 1; *argp; ++argp)
403     {
404       if (!noopts && (*argp)[0] == '-')
405         {
406           int i;
407
408           if ((*argp)[1] == '-' && (*argp)[2] == '\0')
409             {
410               noopts = 1;
411               continue;
412             }
413
414           for (i = 0; i < NELEM (opts); ++i)
415             {
416               if (0 == strcmp (*argp, opts[i].opt))
417                 {
418                   if (NULL != opts[i].optfnc)
419                     {
420                       (*opts[i].optfnc) ();
421                       continue;
422                     }
423                 }
424             }
425         }
426
427       do_ranlib (*argp);
428       ++narch;
429     }
430
431   if (!narch)
432     usage ();
433 }
434
435
436 int
437 main (int argc, char *argv[])
438 {
439   process_options (argc, argv);
440
441   return 0;
442 }