makedep.c 29.4 KB
Newer Older
Alexandre Julliard's avatar
Alexandre Julliard committed
1 2 3 4
/*
 * Generate include file dependencies
 *
 * Copyright 1996 Alexandre Julliard
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
Alexandre Julliard's avatar
Alexandre Julliard committed
19 20
 */

21
#include "config.h"
22
#define NO_LIBWINE_PORT
23 24
#include "wine/port.h"

25
#include <assert.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
26
#include <ctype.h>
27
#include <errno.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
28 29
#include <stdio.h>
#include <stdlib.h>
30
#include <stdarg.h>
Alexandre Julliard's avatar
Alexandre Julliard committed
31
#include <string.h>
32 33 34
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
35
#include "wine/list.h"
Alexandre Julliard's avatar
Alexandre Julliard committed
36 37

/* Max first-level includes per file */
38
#define MAX_INCLUDES 200
Alexandre Julliard's avatar
Alexandre Julliard committed
39 40 41

typedef struct _INCL_FILE
{
42
    struct list        entry;
Alexandre Julliard's avatar
Alexandre Julliard committed
43 44
    char              *name;
    char              *filename;
45
    char              *sourcename;    /* source file name for generated headers */
46 47
    struct _INCL_FILE *included_by;   /* file that included this one */
    int                included_line; /* line where this file was included */
48
    int                system;        /* is it a system include (#include <name>) */
Alexandre Julliard's avatar
Alexandre Julliard committed
49 50 51 52
    struct _INCL_FILE *owner;
    struct _INCL_FILE *files[MAX_INCLUDES];
} INCL_FILE;

53 54
static struct list sources = LIST_INIT(sources);
static struct list includes = LIST_INIT(includes);
Alexandre Julliard's avatar
Alexandre Julliard committed
55 56 57

typedef struct _INCL_PATH
{
58 59
    struct list entry;
    const char *name;
Alexandre Julliard's avatar
Alexandre Julliard committed
60 61
} INCL_PATH;

62
static struct list paths = LIST_INIT(paths);
Alexandre Julliard's avatar
Alexandre Julliard committed
63

64 65 66
static const char *src_dir;
static const char *top_src_dir;
static const char *top_obj_dir;
Alexandre Julliard's avatar
Alexandre Julliard committed
67 68 69
static const char *OutputFileName = "Makefile";
static const char *Separator = "### Dependencies";
static const char *ProgramName;
70
static int input_line;
Alexandre Julliard's avatar
Alexandre Julliard committed
71 72 73 74 75 76

static const char Usage[] =
    "Usage: %s [options] [files]\n"
    "Options:\n"
    "   -Idir   Search for include files in directory 'dir'\n"
    "   -Cdir   Search for source files in directory 'dir'\n"
77
    "   -Sdir   Set the top source directory\n"
78
    "   -Tdir   Set the top object directory\n"
Alexandre Julliard's avatar
Alexandre Julliard committed
79 80 81 82
    "   -fxxx   Store output in file 'xxx' (default: Makefile)\n"
    "   -sxxx   Use 'xxx' as separator (default: \"### Dependencies\")\n";


83 84 85 86 87 88 89 90 91 92 93 94 95
/*******************************************************************
 *         fatal_error
 */
static void fatal_error( const char *msg, ... )
{
    va_list valist;
    va_start( valist, msg );
    vfprintf( stderr, msg, valist );
    va_end( valist );
    exit(1);
}


Alexandre Julliard's avatar
Alexandre Julliard committed
96 97 98
/*******************************************************************
 *         xmalloc
 */
99
static void *xmalloc( size_t size )
Alexandre Julliard's avatar
Alexandre Julliard committed
100 101 102
{
    void *res;
    if (!(res = malloc (size ? size : 1)))
103
        fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
Alexandre Julliard's avatar
Alexandre Julliard committed
104 105 106 107
    return res;
}


108 109 110
/*******************************************************************
 *         xrealloc
 */
111
static void *xrealloc (void *ptr, size_t size)
112 113 114 115 116 117 118 119
{
    void *res;
    assert( size );
    if (!(res = realloc( ptr, size )))
        fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
    return res;
}

Alexandre Julliard's avatar
Alexandre Julliard committed
120 121 122 123 124 125
/*******************************************************************
 *         xstrdup
 */
static char *xstrdup( const char *str )
{
    char *res = strdup( str );
126
    if (!res) fatal_error( "%s: Virtual memory exhausted.\n", ProgramName );
Alexandre Julliard's avatar
Alexandre Julliard committed
127 128 129 130
    return res;
}


131 132 133
/*******************************************************************
 *         strmake
 */
134
static char *strmake( const char* fmt, ... )
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
{
    int n;
    size_t size = 100;
    va_list ap;

    for (;;)
    {
        char *p = xmalloc (size);
        va_start(ap, fmt);
        n = vsnprintf (p, size, fmt, ap);
        va_end(ap);
        if (n == -1) size *= 2;
        else if ((size_t)n >= size) size = n + 1;
        else return p;
        free(p);
    }
}


154 155 156 157 158 159 160 161 162 163 164
/*******************************************************************
 *         strendswith
 */
static int strendswith( const char* str, const char* end )
{
    int l = strlen(str);
    int m = strlen(end);

    return l >= m && strcmp(str + l - m, end) == 0;
}

165 166 167 168 169 170 171 172 173 174 175
/*******************************************************************
 *         get_extension
 */
static char *get_extension( char *filename )
{
    char *ext = strrchr( filename, '.' );
    if (ext && strchr( ext, '/' )) ext = NULL;
    return ext;
}


176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/*******************************************************************
 *         get_line
 */
static char *get_line( FILE *file )
{
    static char *buffer;
    static unsigned int size;

    if (!size)
    {
        size = 1024;
        buffer = xmalloc( size );
    }
    if (!fgets( buffer, size, file )) return NULL;
    input_line++;

    for (;;)
    {
        char *p = buffer + strlen(buffer);
        /* if line is larger than buffer, resize buffer */
        while (p == buffer + size - 1 && p[-1] != '\n')
        {
            buffer = xrealloc( buffer, size * 2 );
            fgets( buffer + size - 1, size + 1, file );
            p = buffer + strlen(buffer);
            size *= 2;
        }
        if (p > buffer && p[-1] == '\n')
        {
            *(--p) = 0;
            if (p > buffer && p[-1] == '\r') *(--p) = 0;
            if (p > buffer && p[-1] == '\\')
            {
                *(--p) = 0;
                /* line ends in backslash, read continuation line */
                fgets( p, size - (p - buffer), file );
                input_line++;
                continue;
            }
        }
        return buffer;
    }
}

Alexandre Julliard's avatar
Alexandre Julliard committed
220 221 222 223 224 225 226 227
/*******************************************************************
 *         add_include_path
 *
 * Add a directory to the include path.
 */
static void add_include_path( const char *name )
{
    INCL_PATH *path = xmalloc( sizeof(*path) );
228
    list_add_tail( &paths, &path->entry );
Alexandre Julliard's avatar
Alexandre Julliard committed
229 230 231
    path->name = name;
}

232 233 234 235 236 237 238 239 240 241 242
/*******************************************************************
 *         find_src_file
 */
static INCL_FILE *find_src_file( const char *name )
{
    INCL_FILE *file;

    LIST_FOR_EACH_ENTRY( file, &sources, INCL_FILE, entry )
        if (!strcmp( name, file->name )) return file;
    return NULL;
}
Alexandre Julliard's avatar
Alexandre Julliard committed
243 244 245 246 247 248

/*******************************************************************
 *         add_include
 *
 * Add an include file if it doesn't already exists.
 */
249
static INCL_FILE *add_include( INCL_FILE *pFile, const char *name, int line, int system )
Alexandre Julliard's avatar
Alexandre Julliard committed
250
{
251
    INCL_FILE *include;
252
    char *ext;
Alexandre Julliard's avatar
Alexandre Julliard committed
253 254 255 256
    int pos;

    for (pos = 0; pos < MAX_INCLUDES; pos++) if (!pFile->files[pos]) break;
    if (pos >= MAX_INCLUDES)
257 258 259 260 261 262 263 264 265 266
        fatal_error( "%s: %s: too many included files, please fix MAX_INCLUDES\n",
                     ProgramName, pFile->name );

    /* enforce some rules for the Wine tree */

    if (!memcmp( name, "../", 3 ))
        fatal_error( "%s:%d: #include directive with relative path not allowed\n",
                     pFile->filename, line );

    if (!strcmp( name, "config.h" ))
Alexandre Julliard's avatar
Alexandre Julliard committed
267
    {
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
        if ((ext = strrchr( pFile->filename, '.' )) && !strcmp( ext, ".h" ))
            fatal_error( "%s:%d: config.h must not be included by a header file\n",
                         pFile->filename, line );
        if (pos)
            fatal_error( "%s:%d: config.h must be included before anything else\n",
                         pFile->filename, line );
    }
    else if (!strcmp( name, "wine/port.h" ))
    {
        if ((ext = strrchr( pFile->filename, '.' )) && !strcmp( ext, ".h" ))
            fatal_error( "%s:%d: wine/port.h must not be included by a header file\n",
                         pFile->filename, line );
        if (!pos) fatal_error( "%s:%d: config.h must be included before wine/port.h\n",
                               pFile->filename, line );
        if (pos > 1)
            fatal_error( "%s:%d: wine/port.h must be included before everything except config.h\n",
                         pFile->filename, line );
        if (strcmp( pFile->files[0]->name, "config.h" ))
            fatal_error( "%s:%d: config.h must be included before wine/port.h\n",
                         pFile->filename, line );
Alexandre Julliard's avatar
Alexandre Julliard committed
288 289
    }

290 291 292 293 294 295 296 297
    LIST_FOR_EACH_ENTRY( include, &includes, INCL_FILE, entry )
        if (!strcmp( name, include->name )) goto found;

    include = xmalloc( sizeof(INCL_FILE) );
    memset( include, 0, sizeof(INCL_FILE) );
    include->name = xstrdup(name);
    include->included_by = pFile;
    include->included_line = line;
298
    include->system = system;
299 300 301 302
    list_add_tail( &includes, &include->entry );
found:
    pFile->files[pos] = include;
    return include;
Alexandre Julliard's avatar
Alexandre Julliard committed
303 304 305 306 307 308 309 310 311 312
}


/*******************************************************************
 *         open_src_file
 */
static FILE *open_src_file( INCL_FILE *pFile )
{
    FILE *file;

313 314 315 316 317 318 319
    /* first try name as is */
    if ((file = fopen( pFile->name, "r" )))
    {
        pFile->filename = xstrdup( pFile->name );
        return file;
    }
    /* now try in source dir */
320
    if (src_dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
321
    {
322
        pFile->filename = strmake( "%s/%s", src_dir, pFile->name );
323
        file = fopen( pFile->filename, "r" );
Alexandre Julliard's avatar
Alexandre Julliard committed
324
    }
325
    if (!file)
Alexandre Julliard's avatar
Alexandre Julliard committed
326
    {
327
        perror( pFile->name );
Alexandre Julliard's avatar
Alexandre Julliard committed
328 329 330 331 332 333 334 335 336 337 338 339
        exit(1);
    }
    return file;
}


/*******************************************************************
 *         open_include_file
 */
static FILE *open_include_file( INCL_FILE *pFile )
{
    FILE *file = NULL;
340
    char *filename, *p;
Alexandre Julliard's avatar
Alexandre Julliard committed
341 342
    INCL_PATH *path;

343 344
    errno = ENOENT;

345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404
    /* check for generated bison header */

    if (strendswith( pFile->name, ".tab.h" ))
    {
        if (src_dir)
            filename = strmake( "%s/%.*s.y", src_dir, strlen(pFile->name) - 6, pFile->name );
        else
            filename = strmake( "%.*s.y", strlen(pFile->name) - 6, pFile->name );

        if ((file = fopen( filename, "r" )))
        {
            pFile->sourcename = filename;
            pFile->filename = xstrdup( pFile->name );
            /* don't bother to parse it */
            fclose( file );
            return NULL;
        }
        free( filename );
    }

    /* check for generated message resource */

    if (strendswith( pFile->name, ".mc.rc" ))
    {
        if (src_dir)
            filename = strmake( "%s/%s", src_dir, pFile->name );
        else
            filename = xstrdup( pFile->name );

        filename[strlen(filename) - 3] = 0;

        if ((file = fopen( filename, "r" )))
        {
            pFile->sourcename = filename;
            pFile->filename = xstrdup( pFile->name );
            /* don't bother to parse it */
            fclose( file );
            return NULL;
        }
        free( filename );
    }

    /* check for corresponding idl file in source dir */

    if (strendswith( pFile->name, ".h" ))
    {
        if (src_dir)
            filename = strmake( "%s/%.*s.idl", src_dir, strlen(pFile->name) - 2, pFile->name );
        else
            filename = strmake( "%.*s.idl", strlen(pFile->name) - 2, pFile->name );

        if ((file = fopen( filename, "r" )))
        {
            pFile->sourcename = filename;
            pFile->filename = xstrdup( pFile->name );
            return file;
        }
        free( filename );
    }

405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    /* first try name as is */
    if ((file = fopen( pFile->name, "r" )))
    {
        pFile->filename = xstrdup( pFile->name );
        return file;
    }

    /* now try in source dir */
    if (src_dir)
    {
        filename = strmake( "%s/%s", src_dir, pFile->name );
        if ((file = fopen( filename, "r" ))) goto found;
        free( filename );
    }

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441
    /* check for corresponding idl file in global includes */

    if (strendswith( pFile->name, ".h" ))
    {
        if (top_src_dir)
            filename = strmake( "%s/include/%.*s.idl",
                                top_src_dir, strlen(pFile->name) - 2, pFile->name );
        else if (top_obj_dir)
            filename = strmake( "%s/include/%.*s.idl",
                                top_obj_dir, strlen(pFile->name) - 2, pFile->name );
        else
            filename = NULL;

        if (filename && (file = fopen( filename, "r" )))
        {
            pFile->sourcename = filename;
            pFile->filename = strmake( "%s/include/%s", top_obj_dir, pFile->name );
            return file;
        }
        free( filename );
    }

442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
    /* now try in global includes */
    if (top_obj_dir)
    {
        filename = strmake( "%s/include/%s", top_obj_dir, pFile->name );
        if ((file = fopen( filename, "r" ))) goto found;
        free( filename );
    }
    if (top_src_dir)
    {
        filename = strmake( "%s/include/%s", top_src_dir, pFile->name );
        if ((file = fopen( filename, "r" ))) goto found;
        free( filename );
    }

    /* now search in include paths */
457
    LIST_FOR_EACH_ENTRY( path, &paths, INCL_PATH, entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
458
    {
459 460
        filename = strmake( "%s/%s", path->name, pFile->name );
        if ((file = fopen( filename, "r" ))) goto found;
Alexandre Julliard's avatar
Alexandre Julliard committed
461 462
        free( filename );
    }
463
    if (pFile->system) return NULL;  /* ignore system files we cannot find */
464

465
    /* try in src file directory */
466
    if ((p = strrchr(pFile->included_by->filename, '/')))
467
    {
468 469 470 471 472 473
        int l = p - pFile->included_by->filename + 1;
        filename = xmalloc(l + strlen(pFile->name) + 1);
        memcpy( filename, pFile->included_by->filename, l );
        strcpy( filename + l, pFile->name );
        if ((file = fopen( filename, "r" ))) goto found;
        free( filename );
474 475
    }

476 477
    perror( pFile->name );
    while (pFile->included_by)
Alexandre Julliard's avatar
Alexandre Julliard committed
478
    {
479 480
        const char *parent = pFile->included_by->sourcename;
        if (!parent) parent = pFile->included_by->name;
481
        fprintf( stderr, "  %s was first included from %s:%d\n",
482
                 pFile->name, parent, pFile->included_line );
483
        pFile = pFile->included_by;
Alexandre Julliard's avatar
Alexandre Julliard committed
484
    }
485 486 487 488
    exit(1);

found:
    pFile->filename = filename;
Alexandre Julliard's avatar
Alexandre Julliard committed
489 490 491 492 493
    return file;
}


/*******************************************************************
494
 *         parse_idl_file
495 496 497
 *
 * If for_h_file is non-zero, it means we are not interested in the idl file
 * itself, but only in the contents of the .h file that will be generated from it.
Alexandre Julliard's avatar
Alexandre Julliard committed
498
 */
499
static void parse_idl_file( INCL_FILE *pFile, FILE *file, int for_h_file )
Alexandre Julliard's avatar
Alexandre Julliard committed
500
{
501
    char *buffer, *include;
502

503 504 505 506 507 508 509
    if (for_h_file)
    {
        /* generated .h file always includes these */
        add_include( pFile, "rpc.h", 0, 1 );
        add_include( pFile, "rpcndr.h", 0, 1 );
    }

510 511
    input_line = 0;
    while ((buffer = get_line( file )))
512
    {
513
        char quote;
514 515
        char *p = buffer;
        while (*p && isspace(*p)) p++;
516 517 518 519 520

        if (!strncmp( p, "import", 6 ))
        {
            p += 6;
            while (*p && isspace(*p)) p++;
521 522 523 524 525 526 527 528
            if (*p != '"') continue;
            include = ++p;
            while (*p && (*p != '"')) p++;
            if (!*p) fatal_error( "%s:%d: Malformed import directive\n", pFile->filename, input_line );
            *p = 0;
            if (for_h_file && strendswith( include, ".idl" )) strcpy( p - 4, ".h" );
            add_include( pFile, include, input_line, 0 );
            continue;
529
        }
530 531

        if (for_h_file)  /* only check for #include inside cpp_quote */
532
        {
533 534 535 536 537 538
            if (strncmp( p, "cpp_quote", 9 )) continue;
            p += 9;
            while (*p && isspace(*p)) p++;
            if (*p++ != '(') continue;
            while (*p && isspace(*p)) p++;
            if (*p++ != '"') continue;
539 540 541 542 543
            if (*p++ != '#') continue;
            while (*p && isspace(*p)) p++;
            if (strncmp( p, "include", 7 )) continue;
            p += 7;
            while (*p && isspace(*p)) p++;
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
            if (*p == '\\' && p[1] == '"')
            {
                p += 2;
                quote = '"';
            }
            else
            {
                if (*p++ != '<' ) continue;
                quote = '>';
            }
            include = p;
            while (*p && (*p != quote)) p++;
            if (!*p || (quote == '"' && p[-1] != '\\'))
                fatal_error( "%s:%d: Malformed #include directive inside cpp_quote\n",
                             pFile->filename, input_line );
            if (quote == '"') p--;  /* remove backslash */
            *p = 0;
            add_include( pFile, include, input_line, (quote == '>') );
            continue;
563 564
        }

565 566 567 568 569 570 571
        /* check for normal #include */
        if (*p++ != '#') continue;
        while (*p && isspace(*p)) p++;
        if (strncmp( p, "include", 7 )) continue;
        p += 7;
        while (*p && isspace(*p)) p++;
        if (*p != '\"' && *p != '<' ) continue;
572 573
        quote = *p++;
        if (quote == '<') quote = '>';
574
        include = p;
575
        while (*p && (*p != quote)) p++;
576
        if (!*p) fatal_error( "%s:%d: Malformed #include directive\n", pFile->filename, input_line );
577
        *p = 0;
578
        add_include( pFile, include, input_line, (quote == '>') );
579
    }
580
}
581

582 583 584 585 586
/*******************************************************************
 *         parse_c_file
 */
static void parse_c_file( INCL_FILE *pFile, FILE *file )
{
587
    char *buffer, *include;
Alexandre Julliard's avatar
Alexandre Julliard committed
588

589 590
    input_line = 0;
    while ((buffer = get_line( file )))
Alexandre Julliard's avatar
Alexandre Julliard committed
591
    {
592
        char quote;
Alexandre Julliard's avatar
Alexandre Julliard committed
593 594 595 596 597 598 599
        char *p = buffer;
        while (*p && isspace(*p)) p++;
        if (*p++ != '#') continue;
        while (*p && isspace(*p)) p++;
        if (strncmp( p, "include", 7 )) continue;
        p += 7;
        while (*p && isspace(*p)) p++;
600 601 602
        if (*p != '\"' && *p != '<' ) continue;
        quote = *p++;
        if (quote == '<') quote = '>';
Alexandre Julliard's avatar
Alexandre Julliard committed
603
        include = p;
604
        while (*p && (*p != quote)) p++;
605
        if (!*p) fatal_error( "%s:%d: Malformed #include directive\n",
606
                              pFile->filename, input_line );
Alexandre Julliard's avatar
Alexandre Julliard committed
607
        *p = 0;
608
        add_include( pFile, include, input_line, (quote == '>') );
Alexandre Julliard's avatar
Alexandre Julliard committed
609
    }
610 611 612
}


613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669
/*******************************************************************
 *         parse_rc_file
 */
static void parse_rc_file( INCL_FILE *pFile, FILE *file )
{
    char *buffer, *include;

    input_line = 0;
    while ((buffer = get_line( file )))
    {
        char quote;
        char *p = buffer;
        while (*p && isspace(*p)) p++;

        if (p[0] == '/' && p[1] == '*')  /* check for magic makedep comment */
        {
            p += 2;
            while (*p && isspace(*p)) p++;
            if (strncmp( p, "@makedep:", 9 )) continue;
            p += 9;
            while (*p && isspace(*p)) p++;
            quote = '"';
            if (*p == quote)
            {
                include = ++p;
                while (*p && *p != quote) p++;
            }
            else
            {
                include = p;
                while (*p && !isspace(*p) && *p != '*') p++;
            }
            if (!*p)
                fatal_error( "%s:%d: Malformed makedep comment\n", pFile->filename, input_line );
            *p = 0;
        }
        else  /* check for #include */
        {
            if (*p++ != '#') continue;
            while (*p && isspace(*p)) p++;
            if (strncmp( p, "include", 7 )) continue;
            p += 7;
            while (*p && isspace(*p)) p++;
            if (*p != '\"' && *p != '<' ) continue;
            quote = *p++;
            if (quote == '<') quote = '>';
            include = p;
            while (*p && (*p != quote)) p++;
            if (!*p) fatal_error( "%s:%d: Malformed #include directive\n",
                                  pFile->filename, input_line );
            *p = 0;
        }
        add_include( pFile, include, input_line, (quote == '>') );
    }
}


670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
/*******************************************************************
 *         parse_generated_idl
 */
static void parse_generated_idl( INCL_FILE *source )
{
    char *header, *basename;

    basename = xstrdup( source->name );
    basename[strlen(basename) - 4] = 0;
    header = strmake( "%s.h", basename );
    source->filename = xstrdup( source->name );

    if (strendswith( source->name, "_c.c" ))
    {
        add_include( source, header, 0, 0 );
    }
    else if (strendswith( source->name, "_i.c" ))
    {
        add_include( source, "rpc.h", 0, 1 );
        add_include( source, "rpcndr.h", 0, 1 );
        add_include( source, "guiddef.h", 0, 1 );
    }
    else if (strendswith( source->name, "_p.c" ))
    {
694
        add_include( source, "objbase.h", 0, 1 );
695
        add_include( source, "rpcproxy.h", 0, 1 );
696
        add_include( source, "wine/exception.h", 0, 1 );
697 698 699 700
        add_include( source, header, 0, 0 );
    }
    else if (strendswith( source->name, "_s.c" ))
    {
701
        add_include( source, "wine/exception.h", 0, 1 );
702 703
        add_include( source, header, 0, 0 );
    }
704 705 706 707 708
    else if (!strcmp( source->name, "dlldata.c" ))
    {
        add_include( source, "objbase.h", 0, 1 );
        add_include( source, "rpcproxy.h", 0, 1 );
    }
709 710 711 712 713

    free( header );
    free( basename );
}

714 715 716 717 718 719 720
/*******************************************************************
 *         parse_file
 */
static void parse_file( INCL_FILE *pFile, int src )
{
    FILE *file;

721 722 723 724
    /* special case for source files generated from idl */
    if (strendswith( pFile->name, "_c.c" ) ||
        strendswith( pFile->name, "_i.c" ) ||
        strendswith( pFile->name, "_p.c" ) ||
725 726
        strendswith( pFile->name, "_s.c" ) ||
        !strcmp( pFile->name, "dlldata.c" ))
727 728
    {
        parse_generated_idl( pFile );
729 730 731 732 733 734 735
        return;
    }

    /* don't try to open .tlb files */
    if (strendswith( pFile->name, ".tlb" ))
    {
        pFile->filename = xstrdup( pFile->name );
736 737 738
        return;
    }

739 740
    file = src ? open_src_file( pFile ) : open_include_file( pFile );
    if (!file) return;
741 742 743 744 745

    if (pFile->sourcename && strendswith( pFile->sourcename, ".idl" ))
        parse_idl_file( pFile, file, 1 );
    else if (strendswith( pFile->filename, ".idl" ))
        parse_idl_file( pFile, file, 0 );
746 747 748 749
    else if (strendswith( pFile->filename, ".c" ) ||
             strendswith( pFile->filename, ".h" ) ||
             strendswith( pFile->filename, ".l" ) ||
             strendswith( pFile->filename, ".y" ))
750
        parse_c_file( pFile, file );
751 752
    else if (strendswith( pFile->filename, ".rc" ))
        parse_rc_file( pFile, file );
Alexandre Julliard's avatar
Alexandre Julliard committed
753
    fclose(file);
Alexandre Julliard's avatar
Alexandre Julliard committed
754 755 756
}


757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775
/*******************************************************************
 *         add_src_file
 *
 * Add a source file to the list.
 */
static INCL_FILE *add_src_file( const char *name )
{
    INCL_FILE *file;

    if (find_src_file( name )) return NULL;  /* we already have it */
    file = xmalloc( sizeof(*file) );
    memset( file, 0, sizeof(*file) );
    file->name = xstrdup(name);
    list_add_tail( &sources, &file->entry );
    parse_file( file, 1 );
    return file;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
776 777 778 779 780 781 782 783 784
/*******************************************************************
 *         output_include
 */
static void output_include( FILE *file, INCL_FILE *pFile,
                            INCL_FILE *owner, int *column )
{
    int i;

    if (pFile->owner == owner) return;
785
    if (!pFile->filename) return;
Alexandre Julliard's avatar
Alexandre Julliard committed
786 787 788 789 790 791 792 793 794 795 796 797 798 799
    pFile->owner = owner;
    if (*column + strlen(pFile->filename) + 1 > 70)
    {
        fprintf( file, " \\\n" );
        *column = 0;
    }
    fprintf( file, " %s", pFile->filename );
    *column += strlen(pFile->filename) + 1;
    for (i = 0; i < MAX_INCLUDES; i++)
        if (pFile->files[i]) output_include( file, pFile->files[i],
                                             owner, column );
}


Alexandre Julliard's avatar
Alexandre Julliard committed
800 801 802
/*******************************************************************
 *         output_src
 */
803
static int output_src( FILE *file, INCL_FILE *pFile, int *column )
Alexandre Julliard's avatar
Alexandre Julliard committed
804
{
805
    char *obj = xstrdup( pFile->name );
806
    char *ext = get_extension( obj );
Alexandre Julliard's avatar
Alexandre Julliard committed
807 808
    if (ext)
    {
809 810
        *ext++ = 0;
        if (!strcmp( ext, "y" ))  /* yacc file */
Alexandre Julliard's avatar
Alexandre Julliard committed
811
        {
812
            *column += fprintf( file, "%s.tab.o: %s.tab.c", obj, obj );
Alexandre Julliard's avatar
Alexandre Julliard committed
813
        }
814
        else if (!strcmp( ext, "l" ))  /* lex file */
Alexandre Julliard's avatar
Alexandre Julliard committed
815
        {
816
            *column += fprintf( file, "%s.yy.o: %s.yy.c", obj, obj );
Alexandre Julliard's avatar
Alexandre Julliard committed
817
        }
818
        else if (!strcmp( ext, "rc" ))  /* resource file */
Alexandre Julliard's avatar
Alexandre Julliard committed
819
        {
820 821
            *column += fprintf( file, "%s.res: %s", obj, pFile->filename );
        }
822
        else if (!strcmp( ext, "mc" ))  /* message file */
823
        {
824
            *column += fprintf( file, "%s.mc.rc: %s", obj, pFile->filename );
Alexandre Julliard's avatar
Alexandre Julliard committed
825
        }
826 827
        else if (!strcmp( ext, "idl" ))  /* IDL file */
        {
828
            char *name;
829 830
            int got_header = 0;
            const char *suffix = "cips";
831

832 833 834 835 836 837 838
            name = strmake( "%s.tlb", obj );
            if (find_src_file( name )) *column += fprintf( file, "%s", name );
            else
            {
                got_header = 1;
                *column += fprintf( file, "%s.h", obj );
            }
839 840
            free( name );

841 842 843 844 845 846 847 848 849 850 851
            while (*suffix)
            {
                name = strmake( "%s_%c.c", obj, *suffix );
                if (find_src_file( name ))
                {
                    if (!got_header++) *column += fprintf( file, " %s.h", obj );
                    *column += fprintf( file, " %s", name );
                }
                free( name );
                suffix++;
            }
852
            *column += fprintf( file, ": %s", pFile->filename );
853
        }
854 855 856 857
        else if (!strcmp( ext, "tlb" ))
        {
            return 0;  /* nothing to do for typelib files */
        }
Alexandre Julliard's avatar
Alexandre Julliard committed
858 859
        else
        {
860
            *column += fprintf( file, "%s.o: %s", obj, pFile->filename );
Alexandre Julliard's avatar
Alexandre Julliard committed
861 862 863
        }
    }
    free( obj );
864
    return 1;
Alexandre Julliard's avatar
Alexandre Julliard committed
865 866 867
}


868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894
/*******************************************************************
 *         create_temp_file
 */
static FILE *create_temp_file( char **tmp_name )
{
    char *name = xmalloc( strlen(OutputFileName) + 13 );
    unsigned int i, id = getpid();
    int fd;
    FILE *ret = NULL;

    for (i = 0; i < 100; i++)
    {
        sprintf( name, "%s.tmp%08x", OutputFileName, id );
        if ((fd = open( name, O_RDWR | O_CREAT | O_EXCL, 0600 )) != -1)
        {
            ret = fdopen( fd, "w" );
            break;
        }
        if (errno != EEXIST) break;
        id += 7777;
    }
    if (!ret) fatal_error( "failed to create output file for '%s'\n", OutputFileName );
    *tmp_name = name;
    return ret;
}


Alexandre Julliard's avatar
Alexandre Julliard committed
895 896 897 898 899 900 901 902
/*******************************************************************
 *         output_dependencies
 */
static void output_dependencies(void)
{
    INCL_FILE *pFile;
    int i, column;
    FILE *file = NULL;
903
    char *tmp_name = NULL;
Alexandre Julliard's avatar
Alexandre Julliard committed
904

905
    if (Separator && ((file = fopen( OutputFileName, "r" ))))
Alexandre Julliard's avatar
Alexandre Julliard committed
906
    {
907 908 909 910
        char buffer[1024];
        FILE *tmp_file = create_temp_file( &tmp_name );

        while (fgets( buffer, sizeof(buffer), file ))
911
        {
912 913 914
            if (fwrite( buffer, 1, strlen(buffer), tmp_file ) != strlen(buffer))
                fatal_error( "error writing to %s\n", tmp_name );
            if (!strncmp( buffer, Separator, strlen(Separator) )) break;
915
        }
916 917
        fclose( file );
        file = tmp_file;
Alexandre Julliard's avatar
Alexandre Julliard committed
918
    }
919
    else
Alexandre Julliard's avatar
Alexandre Julliard committed
920
    {
921
        if (!(file = fopen( OutputFileName, Separator ? "a" : "w" )))
Alexandre Julliard's avatar
Alexandre Julliard committed
922 923 924 925 926
        {
            perror( OutputFileName );
            exit(1);
        }
    }
927
    LIST_FOR_EACH_ENTRY( pFile, &sources, INCL_FILE, entry )
Alexandre Julliard's avatar
Alexandre Julliard committed
928
    {
Alexandre Julliard's avatar
Alexandre Julliard committed
929
        column = 0;
930
        if (!output_src( file, pFile, &column )) continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
931 932 933 934 935
        for (i = 0; i < MAX_INCLUDES; i++)
            if (pFile->files[i]) output_include( file, pFile->files[i],
                                                 pFile, &column );
        fprintf( file, "\n" );
    }
936 937 938 939 940 941 942 943 944 945 946
    fclose( file );

    if (tmp_name)
    {
        if (rename( tmp_name, OutputFileName ) == -1)
        {
            unlink( tmp_name );
            fatal_error( "failed to rename output file to '%s'\n", OutputFileName );
        }
        free( tmp_name );
    }
Alexandre Julliard's avatar
Alexandre Julliard committed
947 948 949 950 951 952 953 954 955 956 957 958 959 960
}


/*******************************************************************
 *         parse_option
 */
static void parse_option( const char *opt )
{
    switch(opt[1])
    {
    case 'I':
        if (opt[2]) add_include_path( opt + 2 );
        break;
    case 'C':
961 962 963 964 965 966 967
        src_dir = opt + 2;
        break;
    case 'S':
        top_src_dir = opt + 2;
        break;
    case 'T':
        top_obj_dir = opt + 2;
Alexandre Julliard's avatar
Alexandre Julliard committed
968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989
        break;
    case 'f':
        if (opt[2]) OutputFileName = opt + 2;
        break;
    case 's':
        if (opt[2]) Separator = opt + 2;
        else Separator = NULL;
        break;
    default:
        fprintf( stderr, "Unknown option '%s'\n", opt );
        fprintf( stderr, Usage, ProgramName );
        exit(1);
    }
}


/*******************************************************************
 *         main
 */
int main( int argc, char *argv[] )
{
    INCL_FILE *pFile;
990 991
    INCL_PATH *path, *next;
    int i, j;
Alexandre Julliard's avatar
Alexandre Julliard committed
992 993

    ProgramName = argv[0];
994 995 996

    i = 1;
    while (i < argc)
Alexandre Julliard's avatar
Alexandre Julliard committed
997
    {
998 999 1000 1001 1002 1003 1004 1005 1006
        if (argv[i][0] == '-')
        {
            parse_option( argv[i] );
            for (j = i; j < argc; j++) argv[j] = argv[j+1];
            argc--;
        }
        else i++;
    }

1007 1008 1009 1010
    /* ignore redundant source paths */
    if (src_dir && !strcmp( src_dir, "." )) src_dir = NULL;
    if (top_src_dir && top_obj_dir && !strcmp( top_src_dir, top_obj_dir )) top_src_dir = NULL;

1011 1012 1013 1014 1015
    /* get rid of absolute paths that don't point into the source dir */
    LIST_FOR_EACH_ENTRY_SAFE( path, next, &paths, INCL_PATH, entry )
    {
        if (path->name[0] != '/') continue;
        if (top_src_dir)
Alexandre Julliard's avatar
Alexandre Julliard committed
1016
        {
1017 1018
            if (!strncmp( path->name, top_src_dir, strlen(top_src_dir) ) &&
                path->name[strlen(top_src_dir)] == '/') continue;
Alexandre Julliard's avatar
Alexandre Julliard committed
1019
        }
1020 1021 1022 1023 1024 1025
        list_remove( &path->entry );
        free( path );
    }

    for (i = 1; i < argc; i++)
    {
1026 1027
        add_src_file( argv[i] );
        if (strendswith( argv[i], "_p.c" )) add_src_file( "dlldata.c" );
Alexandre Julliard's avatar
Alexandre Julliard committed
1028
    }
1029
    LIST_FOR_EACH_ENTRY( pFile, &includes, INCL_FILE, entry ) parse_file( pFile, 0 );
1030
    output_dependencies();
Alexandre Julliard's avatar
Alexandre Julliard committed
1031 1032
    return 0;
}