1/*****************************************************************************/
2/* */
3/* library.c */
4/* */
5/* Library data structures and helpers for the ar65 archiver */
6/* */
7/* */
8/* */
9/* (C) 1998-2013, Ullrich von Bassewitz */
10/* Roemerstrasse 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 <stdio.h>
37#include <string.h>
38#include <errno.h>
39
40/* common */
41#include "cmdline.h"
42#include "exprdefs.h"
43#include "libdefs.h"
44#include "print.h"
45#include "symdefs.h"
46#include "xmalloc.h"
47
48/* ar65 */
49#include "error.h"
50#include "exports.h"
51#include "fileio.h"
52#include "global.h"
53#include "library.h"
54#include "objdata.h"
55#include "objfile.h"
56
57
58
59/*****************************************************************************/
60/* Data */
61/*****************************************************************************/
62
63
64
65/* Name of the library file */
66const char* LibName = 0;
67static char* NewLibName = 0;
68
69/* File descriptor for the library file */
70static FILE* Lib = 0;
71static FILE* NewLib = 0;
72
73/* The library header */
74static LibHeader Header = {
75 LIB_MAGIC,
76 LIB_VERSION,
77 0,
78 0
79};
80
81
82
83/*****************************************************************************/
84/* Writing file data structures */
85/*****************************************************************************/
86
87
88
89static void ReadHeader (void)
90/* Read the header of a library file */
91{
92 /* Seek to position zero */
93 fseek (Lib, 0, SEEK_SET);
94
95 /* Read the header fields, checking magic and version */
96 Header.Magic = Read32 (Lib);
97 if (Header.Magic != LIB_MAGIC) {
98 Error ("'%s' is not a valid library file", LibName);
99 }
100 Header.Version = Read16 (Lib);
101 if (Header.Version != LIB_VERSION) {
102 Error ("Wrong data version in '%s'", LibName);
103 }
104 Header.Flags = Read16 (Lib);
105 Header.IndexOffs = Read32 (Lib);
106}
107
108
109
110static void ReadIndexEntry (void)
111/* Read one entry in the index */
112{
113 /* Create a new entry and insert it into the list */
114 ObjData* O = NewObjData ();
115
116 /* Module name/flags/MTime/Start/Size */
117 O->Name = ReadStr (Lib);
118 O->Flags = Read16 (Lib);
119 O->MTime = Read32 (Lib);
120 O->Start = Read32 (Lib);
121 O->Size = Read32 (Lib);
122}
123
124
125
126static void ReadIndex (void)
127/* Read the index of a library file */
128{
129 unsigned Count, I;
130
131 /* Seek to the start of the index */
132 fseek (Lib, Header.IndexOffs, SEEK_SET);
133
134 /* Read the object file count and calculate the cross ref size */
135 Count = ReadVar (Lib);
136
137 /* Read all entries in the index */
138 while (Count--) {
139 ReadIndexEntry ();
140 }
141
142 /* Read basic object file data from the actual entries */
143 for (I = 0; I < CollCount (&ObjPool); ++I) {
144
145 /* Get the object file entry */
146 ObjData* O = CollAtUnchecked (&ObjPool, I);
147
148 /* Read data */
149 ObjReadData (Lib, O);
150 }
151}
152
153
154
155/*****************************************************************************/
156/* Writing file data structures */
157/*****************************************************************************/
158
159
160
161static void WriteHeader (void)
162/* Write the header to the library file */
163{
164 /* Seek to position zero */
165 fseek (NewLib, 0, SEEK_SET);
166
167 /* Write the header fields */
168 Write32 (NewLib, Header.Magic);
169 Write16 (NewLib, Header.Version);
170 Write16 (NewLib, Header.Flags);
171 Write32 (NewLib, Header.IndexOffs);
172}
173
174
175
176static void WriteIndexEntry (const ObjData* O)
177/* Write one index entry */
178{
179 /* Module name/flags/MTime/start/size */
180 WriteStr (NewLib, O->Name);
181 Write16 (NewLib, O->Flags & ~OBJ_HAVEDATA);
182 Write32 (NewLib, O->MTime);
183 Write32 (NewLib, O->Start);
184 Write32 (NewLib, O->Size);
185}
186
187
188
189static void WriteIndex (void)
190/* Write the index of a library file */
191{
192 unsigned I;
193
194 /* Sync I/O in case the last operation was a read */
195 fseek (NewLib, 0, SEEK_CUR);
196
197 /* Remember the current offset in the header */
198 Header.IndexOffs = ftell (NewLib);
199
200 /* Write the object file count */
201 WriteVar (NewLib, CollCount (&ObjPool));
202
203 /* Write the object files */
204 for (I = 0; I < CollCount (&ObjPool); ++I) {
205 WriteIndexEntry (CollConstAt (&ObjPool, I));
206 }
207}
208
209
210
211/*****************************************************************************/
212/* High level stuff */
213/*****************************************************************************/
214
215
216
217void LibOpen (const char* Name, int MustExist, int NeedTemp)
218/* Open an existing library and a temporary copy. If MustExist is true, the
219** old library is expected to exist. If NeedTemp is true, a temporary library
220** is created.
221*/
222{
223 /* Remember the name */
224 LibName = xstrdup (Name);
225
226 /* Open the existing library for reading */
227 Lib = fopen (Name, "rb");
228 if (Lib == 0) {
229
230 /* File does not exist */
231 if (MustExist) {
232 Error ("Library '%s' does not exist", Name);
233 } else {
234 /* Announce the library's creation if ar65 is verbose. */
235 Print (stdout, 1,
236 "%s: Library '%s' will be created.\n", ProgName, Name);
237 }
238
239 } else {
240
241 /* We have an existing file: Read the header */
242 ReadHeader ();
243
244 /* Now read the existing index */
245 ReadIndex ();
246
247 }
248
249 if (NeedTemp) {
250
251 /* Create the temporary library name */
252 NewLibName = xmalloc (strlen (Name) + strlen (".temp") + 1);
253 strcpy (NewLibName, Name);
254 strcat (NewLibName, ".temp");
255
256 /* Create the temporary library */
257 NewLib = fopen (NewLibName, "w+b");
258 if (NewLib == 0) {
259 Error ("Cannot create temporary library file: %s", strerror (errno));
260 }
261
262 /* Write a dummy header to the temp file */
263 WriteHeader ();
264 }
265}
266
267
268
269unsigned long LibCopyTo (FILE* F, unsigned long Bytes)
270/* Copy data from F to the temp library file, return the start position in
271** the temporary library file.
272*/
273{
274 unsigned char Buf [4096];
275
276 /* Remember the position */
277 unsigned long Pos = ftell (NewLib);
278
279 /* Copy loop */
280 while (Bytes) {
281 unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes;
282 ReadData (F, Buf, Count);
283 WriteData (NewLib, Buf, Count);
284 Bytes -= Count;
285 }
286
287 /* Return the start position */
288 return Pos;
289}
290
291
292
293void LibCopyFrom (unsigned long Pos, unsigned long Bytes, FILE* F)
294/* Copy data from the library file into another file */
295{
296 unsigned char Buf [4096];
297
298 /* Seek to the correct position */
299 fseek (Lib, Pos, SEEK_SET);
300
301 /* Copy loop */
302 while (Bytes) {
303 unsigned Count = (Bytes > sizeof (Buf))? sizeof (Buf) : Bytes;
304 ReadData (Lib, Buf, Count);
305 WriteData (F, Buf, Count);
306 Bytes -= Count;
307 }
308}
309
310
311
312static void LibCheckExports (ObjData* O)
313/* Insert all exports from the given object file into the global list
314** checking for duplicates.
315*/
316{
317 unsigned I;
318
319 /* Let the user know what we do */
320 Print (stdout, 2, "Module '%s' (%u exports):\n", O->Name, CollCount (&O->Exports));
321
322 /* Insert the exports into the global table */
323 for (I = 0; I < CollCount (&O->Exports); ++I) {
324
325 /* Get the name of the export */
326 const char* Name = CollConstAt (&O->Exports, I);
327
328 /* Insert the name into the hash table */
329 Print (stdout, 2, " %s\n", Name);
330 ExpInsert (Name, O);
331 }
332}
333
334
335
336void LibClose (void)
337/* Write remaining data, close both files and copy the temp file to the old
338** filename
339*/
340{
341 /* Do we have a temporary library? */
342 if (NewLib) {
343
344 unsigned I;
345 unsigned char Buf [4096];
346 size_t Count;
347
348 /* Walk through the object file list, inserting exports into the
349 ** export list checking for duplicates. Copy any data that is still
350 ** in the old library into the new one.
351 */
352 for (I = 0; I < CollCount (&ObjPool); ++I) {
353
354 /* Get a pointer to the object */
355 ObjData* O = CollAtUnchecked (&ObjPool, I);
356
357 /* Check exports, make global export table */
358 LibCheckExports (O);
359
360 /* Copy data if needed */
361 if ((O->Flags & OBJ_HAVEDATA) == 0) {
362 /* Data is still in the old library */
363 fseek (Lib, O->Start, SEEK_SET);
364 O->Start = ftell (NewLib);
365 LibCopyTo (Lib, O->Size);
366 O->Flags |= OBJ_HAVEDATA;
367 }
368 }
369
370 /* Write the index */
371 WriteIndex ();
372
373 /* Write the updated header */
374 WriteHeader ();
375
376 /* Close the file */
377 if (Lib && fclose (Lib) != 0) {
378 Error ("Error closing library: %s", strerror (errno));
379 }
380
381 /* Reopen the library and truncate it */
382 Lib = fopen (LibName, "wb");
383 if (Lib == 0) {
384 Error ("Cannot open library '%s' for writing: %s",
385 LibName, strerror (errno));
386 }
387
388 /* Copy the temporary library to the new one */
389 fseek (NewLib, 0, SEEK_SET);
390 while ((Count = fread (Buf, 1, sizeof (Buf), NewLib)) != 0) {
391 if (fwrite (Buf, 1, Count, Lib) != Count) {
392 Error ("Cannot write to '%s': %s", LibName, strerror (errno));
393 }
394 }
395 }
396
397 /* Close both files */
398 if (Lib && fclose (Lib) != 0) {
399 Error ("Problem closing '%s': %s", LibName, strerror (errno));
400 }
401 if (NewLib && fclose (NewLib) != 0) {
402 Error ("Problem closing temporary library file: %s", strerror (errno));
403 }
404 if (NewLibName && remove (NewLibName) != 0) {
405 Error ("Problem deleting temporary library file: %s", strerror (errno));
406 }
407}
408