1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* filetab.h */ |
4 | /* */ |
5 | /* Input file table for ca65 */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2000-2008 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 "check.h" |
42 | #include "coll.h" |
43 | #include "hashtab.h" |
44 | #include "xmalloc.h" |
45 | |
46 | /* ca65 */ |
47 | #include "error.h" |
48 | #include "filetab.h" |
49 | #include "global.h" |
50 | #include "objfile.h" |
51 | #include "spool.h" |
52 | |
53 | |
54 | |
55 | /*****************************************************************************/ |
56 | /* Forwards */ |
57 | /*****************************************************************************/ |
58 | |
59 | |
60 | |
61 | static unsigned HT_GenHash (const void* Key); |
62 | /* Generate the hash over a key. */ |
63 | |
64 | static const void* HT_GetKey (const void* Entry); |
65 | /* Given a pointer to the user entry data, return a pointer to the key. */ |
66 | |
67 | static int HT_Compare (const void* Key1, const void* Key2); |
68 | /* Compare two keys. The function must return a value less than zero if |
69 | ** Key1 is smaller than Key2, zero if both are equal, and a value greater |
70 | ** than zero if Key1 is greater then Key2. |
71 | */ |
72 | |
73 | |
74 | |
75 | /*****************************************************************************/ |
76 | /* Data */ |
77 | /*****************************************************************************/ |
78 | |
79 | |
80 | |
81 | /* Number of entries in the table and the mask to generate the hash */ |
82 | #define HASHTAB_MASK 0x1F |
83 | #define HASHTAB_COUNT (HASHTAB_MASK + 1) |
84 | |
85 | /* An entry in the file table */ |
86 | typedef struct FileEntry FileEntry; |
87 | struct FileEntry { |
88 | HashNode Node; |
89 | unsigned Name; /* File name */ |
90 | unsigned Index; /* Index of entry */ |
91 | FileType Type; /* Type of file */ |
92 | unsigned long Size; /* Size of file */ |
93 | unsigned long MTime; /* Time of last modification */ |
94 | }; |
95 | |
96 | /* Array of all entries, listed by index */ |
97 | static Collection FileTab = STATIC_COLLECTION_INITIALIZER; |
98 | |
99 | /* Hash table functions */ |
100 | static const HashFunctions HashFunc = { |
101 | HT_GenHash, |
102 | HT_GetKey, |
103 | HT_Compare |
104 | }; |
105 | |
106 | /* Hash table, hashed by name */ |
107 | static HashTable HashTab = STATIC_HASHTABLE_INITIALIZER (HASHTAB_COUNT, &HashFunc); |
108 | |
109 | |
110 | |
111 | /*****************************************************************************/ |
112 | /* Hash table functions */ |
113 | /*****************************************************************************/ |
114 | |
115 | |
116 | |
117 | static unsigned HT_GenHash (const void* Key) |
118 | /* Generate the hash over a key. */ |
119 | { |
120 | return (*(const unsigned*)Key & HASHTAB_MASK); |
121 | } |
122 | |
123 | |
124 | |
125 | static const void* HT_GetKey (const void* Entry) |
126 | /* Given a pointer to the user entry data, return a pointer to the index */ |
127 | { |
128 | return &((FileEntry*) Entry)->Name; |
129 | } |
130 | |
131 | |
132 | |
133 | static int HT_Compare (const void* Key1, const void* Key2) |
134 | /* Compare two keys. The function must return a value less than zero if |
135 | ** Key1 is smaller than Key2, zero if both are equal, and a value greater |
136 | ** than zero if Key1 is greater then Key2. |
137 | */ |
138 | { |
139 | return (int)*(const unsigned*)Key1 - (int)*(const unsigned*)Key2; |
140 | } |
141 | |
142 | |
143 | |
144 | /*****************************************************************************/ |
145 | /* Code */ |
146 | /*****************************************************************************/ |
147 | |
148 | |
149 | |
150 | static FileEntry* NewFileEntry (unsigned Name, FileType Type, |
151 | unsigned long Size, unsigned long MTime) |
152 | /* Create a new FileEntry, insert it into the tables and return it */ |
153 | { |
154 | /* Allocate memory for the entry */ |
155 | FileEntry* F = xmalloc (sizeof (FileEntry)); |
156 | |
157 | /* Initialize the fields */ |
158 | InitHashNode (&F->Node); |
159 | F->Name = Name; |
160 | F->Index = CollCount (&FileTab) + 1; /* First file has index #1 */ |
161 | F->Type = Type; |
162 | F->Size = Size; |
163 | F->MTime = MTime; |
164 | |
165 | /* Insert the file into the file table */ |
166 | CollAppend (&FileTab, F); |
167 | |
168 | /* Insert the entry into the hash table */ |
169 | HT_Insert (&HashTab, F); |
170 | |
171 | /* Return the new entry */ |
172 | return F; |
173 | } |
174 | |
175 | |
176 | |
177 | const StrBuf* GetFileName (unsigned Name) |
178 | /* Get the name of a file where the name index is known */ |
179 | { |
180 | static const StrBuf ErrorMsg = LIT_STRBUF_INITIALIZER ("(outside file scope)" ); |
181 | |
182 | const FileEntry* F; |
183 | |
184 | if (Name == 0) { |
185 | /* Name was defined outside any file scope, use the name of the first |
186 | ** file instead. Errors are then reported with a file position of |
187 | ** line zero in the first file. |
188 | */ |
189 | if (CollCount (&FileTab) == 0) { |
190 | /* No files defined until now */ |
191 | return &ErrorMsg; |
192 | } else { |
193 | F = CollConstAt (&FileTab, 0); |
194 | } |
195 | } else { |
196 | F = CollConstAt (&FileTab, Name-1); |
197 | } |
198 | return GetStrBuf (F->Name); |
199 | } |
200 | |
201 | |
202 | |
203 | unsigned GetFileIndex (const StrBuf* Name) |
204 | /* Return the file index for the given file name. */ |
205 | { |
206 | /* Get the string pool index from the name */ |
207 | unsigned NameIdx = GetStrBufId (Name); |
208 | |
209 | /* Search in the hash table for the name */ |
210 | const FileEntry* F = HT_Find (&HashTab, &NameIdx); |
211 | |
212 | /* If we don't have this index, print a diagnostic and use the main file */ |
213 | if (F == 0) { |
214 | Error ("File name '%m%p' not found in file table" , Name); |
215 | return 0; |
216 | } else { |
217 | return F->Index; |
218 | } |
219 | } |
220 | |
221 | |
222 | |
223 | unsigned AddFile (const StrBuf* Name, FileType Type, |
224 | unsigned long Size, unsigned long MTime) |
225 | /* Add a new file to the list of input files. Return the index of the file in |
226 | ** the table. |
227 | */ |
228 | { |
229 | /* Create a new file entry and insert it into the tables */ |
230 | FileEntry* F = NewFileEntry (GetStrBufId (Name), Type, Size, MTime); |
231 | |
232 | /* Return the index */ |
233 | return F->Index; |
234 | } |
235 | |
236 | |
237 | |
238 | void WriteFiles (void) |
239 | /* Write the list of input files to the object file */ |
240 | { |
241 | unsigned I; |
242 | |
243 | /* Tell the obj file module that we're about to start the file list */ |
244 | ObjStartFiles (); |
245 | |
246 | /* Write the file count */ |
247 | ObjWriteVar (CollCount (&FileTab)); |
248 | |
249 | /* Write the file data */ |
250 | for (I = 0; I < CollCount (&FileTab); ++I) { |
251 | /* Get a pointer to the entry */ |
252 | const FileEntry* F = CollConstAt (&FileTab, I); |
253 | /* Write the fields */ |
254 | ObjWriteVar (F->Name); |
255 | ObjWrite32 (F->MTime); |
256 | ObjWriteVar (F->Size); |
257 | } |
258 | |
259 | /* Done writing files */ |
260 | ObjEndFiles (); |
261 | } |
262 | |
263 | |
264 | |
265 | static void WriteEscaped (FILE* F, const char* Name) |
266 | /* Write a file name to a dependency file escaping spaces */ |
267 | { |
268 | while (*Name) { |
269 | if (*Name == ' ') { |
270 | /* Escape spaces */ |
271 | fputc ('\\', F); |
272 | } |
273 | fputc (*Name, F); |
274 | ++Name; |
275 | } |
276 | } |
277 | |
278 | |
279 | |
280 | static void WriteDep (FILE* F, FileType Types) |
281 | /* Helper function. Writes all file names that match Types to the output */ |
282 | { |
283 | unsigned I; |
284 | |
285 | /* Loop over all files */ |
286 | for (I = 0; I < CollCount (&FileTab); ++I) { |
287 | |
288 | const StrBuf* Filename; |
289 | |
290 | /* Get the next input file */ |
291 | const FileEntry* E = (const FileEntry*) CollAt (&FileTab, I); |
292 | |
293 | /* Ignore it if it is not of the correct type */ |
294 | if ((E->Type & Types) == 0) { |
295 | continue; |
296 | } |
297 | |
298 | /* If this is not the first file, add a space */ |
299 | if (I > 0) { |
300 | fputc (' ', F); |
301 | } |
302 | |
303 | /* Print the dependency escaping spaces */ |
304 | Filename = GetStrBuf (E->Name); |
305 | WriteEscaped (F, SB_GetConstBuf (Filename)); |
306 | } |
307 | } |
308 | |
309 | |
310 | |
311 | static void CreateDepFile (const char* Name, FileType Types) |
312 | /* Create a dependency file with the given name and place dependencies for |
313 | ** all files with the given types there. |
314 | */ |
315 | { |
316 | /* Open the file */ |
317 | FILE* F = fopen (Name, "w" ); |
318 | if (F == 0) { |
319 | Fatal ("Cannot open dependency file '%s': %s" , Name, strerror (errno)); |
320 | } |
321 | |
322 | /* Print the output file followed by a tab char */ |
323 | WriteEscaped (F, OutFile); |
324 | fputs (":\t" , F); |
325 | |
326 | /* Write out the dependencies for the output file */ |
327 | WriteDep (F, Types); |
328 | fputs ("\n\n" , F); |
329 | |
330 | /* Write out a phony dependency for the included files */ |
331 | WriteDep (F, Types); |
332 | fputs (":\n\n" , F); |
333 | |
334 | /* Close the file, check for errors */ |
335 | if (fclose (F) != 0) { |
336 | remove (Name); |
337 | Fatal ("Cannot write to dependeny file (disk full?)" ); |
338 | } |
339 | } |
340 | |
341 | |
342 | |
343 | void CreateDependencies (void) |
344 | /* Create dependency files requested by the user */ |
345 | { |
346 | if (SB_NotEmpty (&DepName)) { |
347 | CreateDepFile (SB_GetConstBuf (&DepName), |
348 | FT_MAIN | FT_INCLUDE | FT_BINARY); |
349 | } |
350 | if (SB_NotEmpty (&FullDepName)) { |
351 | CreateDepFile (SB_GetConstBuf (&FullDepName), |
352 | FT_MAIN | FT_INCLUDE | FT_BINARY | FT_DBGINFO); |
353 | } |
354 | } |
355 | |