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