]> git.sur5r.net Git - cc65/blob - src/ar65/objfile.c
Allow to set the ProDOS type and auxtype on creating new files in a similiar way...
[cc65] / src / ar65 / objfile.c
1 /*****************************************************************************/
2 /*                                                                           */
3 /*                                 objfile.c                                 */
4 /*                                                                           */
5 /*                Object file handling for the ar65 archiver                 */
6 /*                                                                           */
7 /*                                                                           */
8 /*                                                                           */
9 /* (C) 1998-2003 Ullrich von Bassewitz                                       */
10 /*               Römerstraße 52                                              */
11 /*               D-70794 Filderstadt                                         */
12 /* EMail:        uz@cc65.org                                                 */
13 /*                                                                           */
14 /*                                                                           */
15 /* This software is provided 'as-is', without any expressed or implied       */
16 /* warranty.  In no event will the authors be held liable for any damages    */
17 /* arising from the use of this software.                                    */
18 /*                                                                           */
19 /* Permission is granted to anyone to use this software for any purpose,     */
20 /* including commercial applications, and to alter it and redistribute it    */
21 /* freely, subject to the following restrictions:                            */
22 /*                                                                           */
23 /* 1. The origin of this software must not be misrepresented; you must not   */
24 /*    claim that you wrote the original software. If you use this software   */
25 /*    in a product, an acknowledgment in the product documentation would be  */
26 /*    appreciated but is not required.                                       */
27 /* 2. Altered source versions must be plainly marked as such, and must not   */
28 /*    be misrepresented as being the original software.                      */
29 /* 3. This notice may not be removed or altered from any source              */
30 /*    distribution.                                                          */
31 /*                                                                           */
32 /*****************************************************************************/
33
34
35
36 #include <string.h>
37 #include <errno.h>
38 #if defined(__WATCOMC__) || defined(_MSC_VER) || defined(__MINGW32__)
39 /* The Windows compilers have the file in the wrong directory */
40 #  include <sys/utime.h>
41 #else
42 #  include <sys/types.h>                /* FreeBSD needs this */
43 #  include <utime.h>
44 #endif
45 #include <time.h>
46 #include <sys/stat.h>
47
48 /* common */
49 #include "xmalloc.h"
50
51 /* ar65 */
52 #include "error.h"
53 #include "objdata.h"
54 #include "fileio.h"
55 #include "library.h"
56 #include "objfile.h"
57
58
59
60 /*****************************************************************************/
61 /*                                   Code                                    */
62 /*****************************************************************************/
63
64
65
66 static const char* GetModule (const char* Name)
67 /* Get a module name from the file name */
68 {
69     /* Make a module name from the file name */
70     const char* Module = Name + strlen (Name);
71     while (Module > Name) {
72         --Module;
73         if (*Module == '/' || *Module == '\\') {
74             ++Module;
75             break;
76         }
77     }
78     if (*Module == 0) {
79         Error ("Cannot make module name from `%s'", Name);
80     }
81     return Module;
82 }
83
84
85
86 void ObjReadHeader (FILE* Obj, ObjHeader* H, const char* Name)
87 /* Read the header of the object file checking the signature */
88 {
89     H->Magic      = Read32 (Obj);
90     if (H->Magic != OBJ_MAGIC) {
91         Error ("`%s' is not an object file", Name);
92     }
93     H->Version    = Read16 (Obj);
94     if (H->Version != OBJ_VERSION) {
95         Error ("Object file `%s' has wrong version", Name);
96     }
97     H->Flags        = Read16 (Obj);
98     H->OptionOffs   = Read32 (Obj);
99     H->OptionSize   = Read32 (Obj);
100     H->FileOffs     = Read32 (Obj);
101     H->FileSize     = Read32 (Obj);
102     H->SegOffs      = Read32 (Obj);
103     H->SegSize      = Read32 (Obj);
104     H->ImportOffs   = Read32 (Obj);
105     H->ImportSize   = Read32 (Obj);
106     H->ExportOffs   = Read32 (Obj);
107     H->ExportSize   = Read32 (Obj);
108     H->DbgSymOffs   = Read32 (Obj);
109     H->DbgSymSize   = Read32 (Obj);
110     H->LineInfoOffs = Read32 (Obj);
111     H->LineInfoSize = Read32 (Obj);
112     H->StrPoolOffs  = Read32 (Obj);
113     H->StrPoolSize  = Read32 (Obj);
114     H->AssertOffs   = Read32 (Obj);
115     H->AssertSize   = Read32 (Obj);
116     H->ScopeOffs    = Read32 (Obj);
117     H->ScopeSize    = Read32 (Obj);
118 }
119
120
121
122 void ObjWriteHeader (FILE* Obj, ObjHeader* H)
123 /* Write the header of the object file */
124 {
125     Write32 (Obj, H->Magic);
126     Write16 (Obj, H->Version);
127     Write16 (Obj, H->Flags);
128     Write32 (Obj, H->OptionOffs);
129     Write32 (Obj, H->OptionSize);
130     Write32 (Obj, H->FileOffs);
131     Write32 (Obj, H->FileSize);
132     Write32 (Obj, H->SegOffs);
133     Write32 (Obj, H->SegSize);
134     Write32 (Obj, H->ImportOffs);
135     Write32 (Obj, H->ImportSize);
136     Write32 (Obj, H->ExportOffs);
137     Write32 (Obj, H->ExportSize);
138     Write32 (Obj, H->DbgSymOffs);
139     Write32 (Obj, H->DbgSymSize);
140     Write32 (Obj, H->LineInfoOffs);
141     Write32 (Obj, H->LineInfoSize);
142     Write32 (Obj, H->StrPoolOffs);
143     Write32 (Obj, H->StrPoolSize);
144     Write32 (Obj, H->AssertOffs);
145     Write32 (Obj, H->AssertSize);
146     Write32 (Obj, H->ScopeOffs);
147     Write32 (Obj, H->ScopeSize);
148 }
149
150
151
152 void ObjAdd (const char* Name)
153 /* Add an object file to the library */
154 {
155     struct stat StatBuf;
156     const char* Module;
157     ObjHeader H;
158     ObjData* O;
159     unsigned I;
160
161     /* Open the object file */
162     FILE* Obj = fopen (Name, "rb");
163     if (Obj == 0) {
164         Error ("Could not open `%s': %s", Name, strerror (errno));
165     }
166
167     /* Get the modification time of the object file. There a race condition 
168      * here, since we cannot use fileno() (non standard identifier in standard
169      * header file), and therefore not fstat. When using stat with the    
170      * file name, there's a risk that the file was deleted and recreated
171      * while it was open. Since mtime and size are only used to check
172      * if a file has changed in the debugger, we will ignore this problem
173      * here.
174      */
175     if (stat (Name, &StatBuf) != 0) {
176         Error ("Cannot stat object file `%s': %s", Name, strerror (errno));
177     }
178
179     /* Read and check the header */
180     ObjReadHeader (Obj, &H, Name);
181
182     /* Make a module name from the file name */
183     Module = GetModule (Name);
184
185     /* Check if we already have a module with this name */
186     O = FindObjData (Module);
187     if (O == 0) {
188         /* Not found, create a new entry */
189         O = NewObjData ();
190     } else {
191         /* Found - check the file modification times of the internal copy
192          * and the external one.
193          */
194         if (difftime ((time_t)O->MTime, StatBuf.st_mtime) > 0.0) {
195             Warning ("Replacing module `%s' by older version", O->Name);
196         }
197     }
198
199     /* Initialize the object module data structure */
200     O->Name       = xstrdup (Module);
201     O->Flags      = OBJ_HAVEDATA;
202     O->MTime      = StatBuf.st_mtime;
203     O->ImportSize = H.ImportSize;
204     O->Imports    = xmalloc (O->ImportSize);
205     O->ExportSize = H.ExportSize;
206     O->Exports    = xmalloc (O->ExportSize);
207
208     /* Read imports and exports */
209     fseek (Obj, H.ImportOffs, SEEK_SET);
210     ReadData (Obj, O->Imports, O->ImportSize);
211     fseek (Obj, H.ExportOffs, SEEK_SET);
212     ReadData (Obj, O->Exports, O->ExportSize);
213
214     /* Read the string pool */
215     fseek (Obj, H.StrPoolOffs, SEEK_SET);
216     O->StringCount = ReadVar (Obj);
217     O->Strings     = xmalloc (O->StringCount * sizeof (char*));
218     for (I = 0; I < O->StringCount; ++I) {
219         O->Strings[I] = ReadStr (Obj);
220     }
221
222     /* Skip the object file header */
223     O->Start = ftell (NewLib);
224     fseek (NewLib, OBJ_HDR_SIZE, SEEK_CUR);
225
226     /* Copy the remaining sections */
227     fseek (Obj, H.DbgSymOffs, SEEK_SET);
228     H.DbgSymOffs = LibCopyTo (Obj, H.DbgSymSize) - O->Start;
229     fseek (Obj, H.OptionOffs, SEEK_SET);
230     H.OptionOffs = LibCopyTo (Obj, H.OptionSize) - O->Start;
231     fseek (Obj, H.SegOffs, SEEK_SET);
232     H.SegOffs = LibCopyTo (Obj, H.SegSize) - O->Start;
233     fseek (Obj, H.FileOffs, SEEK_SET);
234     H.FileOffs = LibCopyTo (Obj, H.FileSize) - O->Start;
235     fseek (Obj, H.LineInfoOffs, SEEK_SET);
236     H.LineInfoOffs = LibCopyTo (Obj, H.LineInfoSize) - O->Start;
237     fseek (Obj, H.AssertOffs, SEEK_SET);
238     H.AssertOffs = LibCopyTo (Obj, H.AssertSize) - O->Start;
239     fseek (Obj, H.ScopeOffs, SEEK_SET);
240     H.ScopeOffs = LibCopyTo (Obj, H.ScopeSize) - O->Start;
241
242     /* Calculate the amount of data written */
243     O->Size = ftell (NewLib) - O->Start;
244
245     /* Clear the remaining header fields */
246     H.ImportOffs  = H.ImportSize  = 0;
247     H.ExportOffs  = H.ExportSize  = 0;
248     H.StrPoolOffs = H.StrPoolSize = 0;
249
250     /* Seek back and write the updated header */
251     fseek (NewLib, O->Start, SEEK_SET);
252     ObjWriteHeader (NewLib, &H);
253
254     /* Now seek again to end of file */
255     fseek (NewLib, 0, SEEK_END);
256
257     /* Done, close the file (we read it only, so no error check) */
258     fclose (Obj);
259 }
260
261
262
263 void ObjExtract (const char* Name)
264 /* Extract a module from the library */
265 {
266     unsigned long ImportStart;
267     unsigned long ExportStart;
268     unsigned long StrPoolStart;
269     unsigned long StrPoolSize;
270     struct utimbuf U;
271     ObjHeader H;
272     FILE* Obj;
273     unsigned I;
274
275     /* Make a module name from the file name */
276     const char* Module = GetModule (Name);
277
278     /* Try to find the module in the library */
279     ObjData* O = FindObjData (Module);
280
281     /* Bail out if the module does not exist */
282     if (O == 0) {
283         Error ("Module `%s' not found in library", Module);
284     }
285
286     /* Open the output file */
287     Obj = fopen (Name, "w+b");
288     if (Obj == 0) {
289         Error ("Cannot open target file `%s': %s", Name, strerror (errno));
290     }
291
292     /* Copy anything to the new file that has no special handling */
293     LibCopyFrom (O->Start, O->Size, Obj);
294
295     /* Write imports and exports */
296     ImportStart = ftell (Obj);
297     WriteData (Obj, O->Imports, O->ImportSize);
298     ExportStart = ftell (Obj);
299     WriteData (Obj, O->Exports, O->ExportSize);
300
301     /* Write the string pool */
302     StrPoolStart = ftell (Obj);
303     WriteVar (Obj, O->StringCount);
304     for (I = 0; I < O->StringCount; ++I) {
305         WriteStr (Obj, O->Strings[I]);
306     }
307     StrPoolSize = ftell (Obj) - StrPoolStart;
308
309     /* Seek back and read the header */
310     fseek (Obj, 0, SEEK_SET);
311     ObjReadHeader (Obj, &H, Name);
312
313     /* Update the header fields */
314     H.ImportOffs  = ImportStart;
315     H.ImportSize  = O->ImportSize;
316     H.ExportOffs  = ExportStart;
317     H.ExportSize  = O->ExportSize;
318     H.StrPoolOffs = StrPoolStart;
319     H.StrPoolSize = StrPoolSize;
320
321     /* Write the changed header */
322     fseek (Obj, 0, SEEK_SET);
323     ObjWriteHeader (Obj, &H);
324
325     /* Close the file */
326     if (fclose (Obj) != 0) {
327         Error ("Problem closing object file `%s': %s", Name, strerror (errno));
328     }
329
330     /* Set access and modification time */
331     U.actime = O->MTime;
332     U.modtime = O->MTime;
333     if (utime (Name, &U) != 0) {
334         Error ("Cannot set mod time on `%s': %s", Name, strerror (errno));
335     }
336 }
337
338
339