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