- Added support for %struct16 (used by CLSID).
[wine] / tools / makedep.c
1 /*
2  * Generate include file dependencies
3  *
4  * Copyright 1996 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #ifdef HAVE_UNISTD_H
29 # include <unistd.h>
30 #endif
31
32 /* Max first-level includes per file */
33 #define MAX_INCLUDES 200
34
35 typedef struct _INCL_FILE
36 {
37     struct _INCL_FILE *next;
38     char              *name;
39     char              *filename;
40     struct _INCL_FILE *included_by;   /* file that included this one */
41     int                included_line; /* line where this file was included */
42     int                system;        /* is it a system include (#include <name>) */
43     struct _INCL_FILE *owner;
44     struct _INCL_FILE *files[MAX_INCLUDES];
45 } INCL_FILE;
46
47 static INCL_FILE *firstSrc;
48 static INCL_FILE *firstInclude;
49
50 typedef struct _INCL_PATH
51 {
52     struct _INCL_PATH *next;
53     const char        *name;
54 } INCL_PATH;
55
56 static INCL_PATH *firstPath;
57
58 static const char *SrcDir = NULL;
59 static const char *OutputFileName = "Makefile";
60 static const char *Separator = "### Dependencies";
61 static const char *ProgramName;
62
63 static const char Usage[] =
64     "Usage: %s [options] [files]\n"
65     "Options:\n"
66     "   -Idir   Search for include files in directory 'dir'\n"
67     "   -Cdir   Search for source files in directory 'dir'\n"
68     "   -fxxx   Store output in file 'xxx' (default: Makefile)\n"
69     "   -sxxx   Use 'xxx' as separator (default: \"### Dependencies\")\n";
70
71
72 /*******************************************************************
73  *         xmalloc
74  */
75 static void *xmalloc( int size )
76 {
77     void *res;
78     if (!(res = malloc (size ? size : 1)))
79     {
80         fprintf( stderr, "%s: Virtual memory exhausted.\n", ProgramName );
81         exit(1);
82     }
83     return res;
84 }
85
86
87 /*******************************************************************
88  *         xstrdup
89  */
90 static char *xstrdup( const char *str )
91 {
92     char *res = strdup( str );
93     if (!res)
94     {
95         fprintf( stderr, "%s: Virtual memory exhausted.\n", ProgramName );
96         exit(1);
97     }
98     return res;
99 }
100
101
102 /*******************************************************************
103  *         is_generated
104  *
105  * Test if a given file type is generated during the make process
106  */
107 static int is_generated( const char *name )
108 {
109     static const char * const extensions[] = { ".tab.h", ".mc.rc" };
110     size_t i, len = strlen(name);
111     for (i = 0; i < sizeof(extensions)/sizeof(extensions[0]); i++)
112     {
113         if (len <= strlen(extensions[i])) continue;
114         if (!strcmp( name + len - strlen(extensions[i]), extensions[i] )) return 1;
115     }
116     return 0;
117 }
118
119 /*******************************************************************
120  *         add_include_path
121  *
122  * Add a directory to the include path.
123  */
124 static void add_include_path( const char *name )
125 {
126     INCL_PATH *path = xmalloc( sizeof(*path) );
127     INCL_PATH **p = &firstPath;
128     while (*p) p = &(*p)->next;
129     *p = path;
130     path->next = NULL;
131     path->name = name;
132 }
133
134
135 /*******************************************************************
136  *         add_src_file
137  *
138  * Add a source file to the list.
139  */
140 static INCL_FILE *add_src_file( const char *name )
141 {
142     INCL_FILE **p = &firstSrc;
143     INCL_FILE *file = xmalloc( sizeof(*file) );
144     memset( file, 0, sizeof(*file) );
145     file->name = xstrdup(name);
146     while (*p) p = &(*p)->next;
147     *p = file;
148     return file;
149 }
150
151
152 /*******************************************************************
153  *         add_include
154  *
155  * Add an include file if it doesn't already exists.
156  */
157 static INCL_FILE *add_include( INCL_FILE *pFile, const char *name, int line, int system )
158 {
159     INCL_FILE **p = &firstInclude;
160     int pos;
161
162     for (pos = 0; pos < MAX_INCLUDES; pos++) if (!pFile->files[pos]) break;
163     if (pos >= MAX_INCLUDES)
164     {
165         fprintf( stderr, "%s: %s: too many included files, please fix MAX_INCLUDES\n",
166                  ProgramName, pFile->name );
167         exit(1);
168     }
169
170     while (*p && strcmp( name, (*p)->name )) p = &(*p)->next;
171     if (!*p)
172     {
173         *p = xmalloc( sizeof(INCL_FILE) );
174         memset( *p, 0, sizeof(INCL_FILE) );
175         (*p)->name = xstrdup(name);
176         (*p)->included_by = pFile;
177         (*p)->included_line = line;
178         (*p)->system = system || pFile->system;
179     }
180     pFile->files[pos] = *p;
181     return *p;
182 }
183
184
185 /*******************************************************************
186  *         open_src_file
187  */
188 static FILE *open_src_file( INCL_FILE *pFile )
189 {
190     FILE *file;
191
192     if (SrcDir)
193     {
194         pFile->filename = xmalloc( strlen(SrcDir) + strlen(pFile->name) + 2 );
195         strcpy( pFile->filename, SrcDir );
196         strcat( pFile->filename, "/" );
197         strcat( pFile->filename, pFile->name );
198     }
199     else pFile->filename = xstrdup( pFile->name );
200
201     if (!(file = fopen( pFile->filename, "r" )))
202     {
203         perror( pFile->filename );
204         exit(1);
205     }
206     return file;
207 }
208
209
210 /*******************************************************************
211  *         open_include_file
212  */
213 static FILE *open_include_file( INCL_FILE *pFile )
214 {
215     FILE *file = NULL;
216     INCL_PATH *path;
217
218     for (path = firstPath; path; path = path->next)
219     {
220         char *filename = xmalloc(strlen(path->name) + strlen(pFile->name) + 2);
221         strcpy( filename, path->name );
222         strcat( filename, "/" );
223         strcat( filename, pFile->name );
224         if ((file = fopen( filename, "r" )))
225         {
226             pFile->filename = filename;
227             break;
228         }
229         free( filename );
230     }
231     if (!file && pFile->system) return NULL;  /* ignore system files we cannot find */
232
233     /* try in src file directory */
234     if (!file)
235     {
236         char *p = strrchr(pFile->included_by->filename, '/');
237         if (p)
238         {
239             int l = p - pFile->included_by->filename + 1;
240             char *filename = xmalloc(l + strlen(pFile->name) + 1);
241             memcpy( filename, pFile->included_by->filename, l );
242             strcpy( filename + l, pFile->name );
243             if ((file = fopen( filename, "r" ))) pFile->filename = filename;
244             else free( filename );
245         }
246     }
247
248     if (!file)
249     {
250         if (pFile->included_by->system) return NULL;  /* ignore if included by a system file */
251         if (firstPath) perror( pFile->name );
252         else fprintf( stderr, "%s: %s: File not found\n",
253                       ProgramName, pFile->name );
254         while (pFile->included_by)
255         {
256             fprintf( stderr, "  %s was first included from %s:%d\n",
257                      pFile->name, pFile->included_by->name, pFile->included_line );
258             pFile = pFile->included_by;
259         }
260         exit(1);
261     }
262     return file;
263 }
264
265
266 /*******************************************************************
267  *         parse_file
268  */
269 static void parse_file( INCL_FILE *pFile, int src )
270 {
271     char buffer[1024];
272     char *include;
273     int line = 0;
274     FILE *file;
275
276     if (is_generated( pFile->name ))
277     {
278         /* file is generated during make, don't try to open it */
279         pFile->filename = xstrdup( pFile->name );
280         return;
281     }
282
283     file = src ? open_src_file( pFile ) : open_include_file( pFile );
284     if (!file) return;
285
286     while (fgets( buffer, sizeof(buffer)-1, file ))
287     {
288         char quote;
289         char *p = buffer;
290         line++;
291         while (*p && isspace(*p)) p++;
292         if (*p++ != '#') continue;
293         while (*p && isspace(*p)) p++;
294         if (strncmp( p, "include", 7 )) continue;
295         p += 7;
296         while (*p && isspace(*p)) p++;
297         if (*p != '\"' && *p != '<' ) continue;
298         quote = *p++;
299         if (quote == '<') quote = '>';
300         include = p;
301         while (*p && (*p != quote)) p++;
302         if (!*p)
303         {
304             fprintf( stderr, "%s:%d: Malformed #include directive\n",
305                      pFile->filename, line );
306             exit(1);
307         }
308         *p = 0;
309         add_include( pFile, include, line, (quote == '>') );
310     }
311     fclose(file);
312 }
313
314
315 /*******************************************************************
316  *         output_include
317  */
318 static void output_include( FILE *file, INCL_FILE *pFile,
319                             INCL_FILE *owner, int *column )
320 {
321     int i;
322
323     if (pFile->owner == owner) return;
324     if (!pFile->filename) return;
325     pFile->owner = owner;
326     if (*column + strlen(pFile->filename) + 1 > 70)
327     {
328         fprintf( file, " \\\n" );
329         *column = 0;
330     }
331     fprintf( file, " %s", pFile->filename );
332     *column += strlen(pFile->filename) + 1;
333     for (i = 0; i < MAX_INCLUDES; i++)
334         if (pFile->files[i]) output_include( file, pFile->files[i],
335                                              owner, column );
336 }
337
338
339 /*******************************************************************
340  *         output_src
341  */
342 static void output_src( FILE *file, INCL_FILE *pFile, int *column )
343 {
344     char *obj = xstrdup( pFile->name );
345     char *ext = strrchr( obj, '.' );
346     if (ext && strchr( ext, '/' )) ext = NULL;
347     if (ext)
348     {
349         *ext++ = 0;
350         if (!strcmp( ext, "y" ))  /* yacc file */
351         {
352             *column += fprintf( file, "y.tab.o: y.tab.c" );
353         }
354         else if (!strcmp( ext, "l" ))  /* lex file */
355         {
356             *column += fprintf( file, "%s.o: %s.c", LEX_OUTPUT_ROOT, LEX_OUTPUT_ROOT );
357         }
358         else if (!strcmp( ext, "rc" ))  /* resource file */
359         {
360             *column += fprintf( file, "%s.res: %s", obj, pFile->filename );
361         }
362         else if (!strcmp( ext, "mc" ))  /* message file */
363         {
364             *column += fprintf( file, "%s.mc.rc: %s", obj, pFile->filename );
365         }
366         else
367         {
368             *column += fprintf( file, "%s.o: %s", obj, pFile->filename );
369         }
370     }
371     free( obj );
372 }
373
374
375 /*******************************************************************
376  *         output_dependencies
377  */
378 static void output_dependencies(void)
379 {
380     INCL_FILE *pFile;
381     int i, column;
382     FILE *file = NULL;
383     char buffer[1024];
384
385     if (Separator && ((file = fopen( OutputFileName, "r+" ))))
386     {
387         while (fgets( buffer, sizeof(buffer), file ))
388             if (!strncmp( buffer, Separator, strlen(Separator) )) break;
389         ftruncate( fileno(file), ftell(file) );
390         fseek( file, 0L, SEEK_END );
391     }
392     if (!file)
393     {
394         if (!(file = fopen( OutputFileName, Separator ? "a" : "w" )))
395         {
396             perror( OutputFileName );
397             exit(1);
398         }
399     }
400     for( pFile = firstSrc; pFile; pFile = pFile->next)
401     {
402         column = 0;
403         output_src( file, pFile, &column );
404         for (i = 0; i < MAX_INCLUDES; i++)
405             if (pFile->files[i]) output_include( file, pFile->files[i],
406                                                  pFile, &column );
407         fprintf( file, "\n" );
408     }
409     fclose(file);
410 }
411
412
413 /*******************************************************************
414  *         parse_option
415  */
416 static void parse_option( const char *opt )
417 {
418     switch(opt[1])
419     {
420     case 'I':
421         if (opt[2]) add_include_path( opt + 2 );
422         break;
423     case 'C':
424         if (opt[2]) SrcDir = opt + 2;
425         else SrcDir = NULL;
426         break;
427     case 'f':
428         if (opt[2]) OutputFileName = opt + 2;
429         break;
430     case 's':
431         if (opt[2]) Separator = opt + 2;
432         else Separator = NULL;
433         break;
434     default:
435         fprintf( stderr, "Unknown option '%s'\n", opt );
436         fprintf( stderr, Usage, ProgramName );
437         exit(1);
438     }
439 }
440
441
442 /*******************************************************************
443  *         main
444  */
445 int main( int argc, char *argv[] )
446 {
447     INCL_FILE *pFile;
448
449     ProgramName = argv[0];
450     while (argc > 1)
451     {
452         if (*argv[1] == '-') parse_option( argv[1] );
453         else
454         {
455             pFile = add_src_file( argv[1] );
456             parse_file( pFile, 1 );
457         }
458         argc--;
459         argv++;
460     }
461     for (pFile = firstInclude; pFile; pFile = pFile->next)
462         parse_file( pFile, 0 );
463     if( firstSrc ) output_dependencies();
464     return 0;
465 }