root/usr/src/cmd/make/bin/rep.cc
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
 * Use is subject to license terms.
 */

/*
 *      rep.c
 *
 *      This file handles the .nse_depinfo file
 */

/*
 * Included files
 */
#include <mk/defs.h>
#include <mksh/misc.h>          /* retmem() */
#include <vroot/report.h>       /* NSE_DEPINFO */

/*
 * Static variables
 */
static  Recursive_make  recursive_list;
static  Recursive_make  *bpatch = &recursive_list;
static  Boolean         changed;

/*
 * File table of contents
 */


/*
 *      report_recursive_init()
 *
 *      Read the .nse_depinfo file and make a list of all the
 *      .RECURSIVE entries.
 *
 *      Parameters:
 *
 *      Static variables used:
 *              bpatch          Points to slot where next cell should be added
 *
 *      Global variables used:
 *              recursive_name  The Name ".RECURSIVE", compared against
 */

void
report_recursive_init(void)
{
        char            *search_dir;
        char            nse_depinfo[MAXPATHLEN];
        FILE            *fp;
        int             line_size, line_index;
        wchar_t         *line;
        wchar_t         *bigger_line;
        wchar_t         *colon;
        wchar_t         *dollar;
        Recursive_make  rp;

        /*
         * This routine can be called more than once,  don't do
         * anything after the first time.
         */
        if (depinfo_already_read) {
                return;
        } else {
                depinfo_already_read = true;
        }

        search_dir = getenv("NSE_DEP");
        if (search_dir == NULL) {
                return;
        }
        (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO);
        fp = fopen(nse_depinfo, "r");
        if (fp == NULL) {
                return;
        }
        line_size = MAXPATHLEN;
        line_index = line_size - 1;
        line = ALLOC_WC(line_size);
        Wstring rns(recursive_name);
        wchar_t * wcb = rns.get_string();
        while (fgetws(line, line_size, fp) != NULL) {
                while (wcslen(line) == line_index) {
                        if (line[wcslen(line) - 1] == '\n') {
                                continue;
                        }
                        bigger_line = ALLOC_WC(2 * line_size);
                        wcscpy(bigger_line, line);
                        retmem(line);
                        line = bigger_line;
                        if (fgetws(&line[line_index], line_size, fp) == NULL)
                                continue;
                        line_index = 2 * line_index;
                        line_size = 2 * line_size;
                }

                colon = (wchar_t *) wcschr(line, (int) colon_char);
                if (colon == NULL) {
                        continue;
                }
                dollar = (wchar_t *) wcschr(line, (int) dollar_char);
                line[wcslen(line) - 1] = (int) nul_char;
                if (IS_WEQUALN(&colon[2], wcb,
                    (int) recursive_name->hash.length)) {
                        /*
                         * If this entry is an old entry, ignore it
                         */
                        MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
                        if (dollar == NULL ||
                            !IS_WEQUALN(wcs_buffer, (dollar+1) - VER_LEN, VER_LEN)){
                                continue;
                            }
                        rp = ALLOC(Recursive_make);
                        (void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
                        /*
                         * set conditional_macro_string if string is present
                         */
                        rp->oldline = (wchar_t *) wcsdup(line);
                        if ( dollar != NULL ){
                                rp->cond_macrostring =
                                    (wchar_t *) wcsdup(dollar - VER_LEN + 1);
                        }
                        /*
                         * get target name into recursive struct
                         */
                        *colon = (int) nul_char;
                        rp->target = (wchar_t *) wcsdup(line);
                        *bpatch = rp;
                        bpatch = &rp->next;
                }
        }
        (void) fclose(fp);
}

/*
 *      report_recursive_dep(target, line)
 *
 *      Report a target as recursive.
 *
 *      Parameters:
 *              line            Dependency line reported
 *
 *      Static variables used:
 *              bpatch          Points to slot where next cell should be added
 *              changed         Written if report set changed
 */
void
report_recursive_dep(Name target, wchar_t *line)
{
        Recursive_make  rp;
        wchar_t         rec_buf[STRING_BUFFER_LENGTH];
        String_rec      string;

        INIT_STRING_FROM_STACK(string, rec_buf);
        cond_macros_into_string(target, &string);
        /*
         * find an applicable recursive entry, if there isn't one, create it
         */
        rp = find_recursive_target(target);
        if (rp == NULL) {
                rp = ALLOC(Recursive_make);
                (void) memset((char *) rp, 0, sizeof (Recursive_make_rec));
                wchar_t * wcb = get_wstring(target->string_mb); // XXX Tolik: needs retmem
                rp->target = wcb;
                rp->newline = (wchar_t *) wcsdup(line);
                rp->cond_macrostring = (wchar_t *) wcsdup(rec_buf);
                *bpatch = rp;
                bpatch = &rp->next;
                changed = true;
        } else {
                if ((rp->oldline != NULL) && !IS_WEQUAL(rp->oldline, line)) {
                        rp->newline = (wchar_t *) wcsdup(line);
                        changed = true;
                }
                rp->removed = false;
        }
}

/*
 *      find_recursive_target(target)
 *
 *      Search the list for a given target.
 *
 *      Return value:
 *                              The target cell
 *
 *      Parameters:
 *              target          The target we need
 *              top_level_target more info used to determinde the
 *                               target we need
 *
 *      Static variables used:
 *              recursive_list  The list of targets
 */
Recursive_make
find_recursive_target(Name target)
{
        Recursive_make  rp;
        String_rec      string;
        wchar_t         rec_buf[STRING_BUFFER_LENGTH];

        INIT_STRING_FROM_STACK(string, rec_buf);
        cond_macros_into_string(target, &string);

        Wstring tstr(target);
        wchar_t * wcb = tstr.get_string();
        for (rp = recursive_list; rp != NULL; rp = rp->next) {
                /*
                 * If this entry has already been removed, ignore it.
                 */
                if (rp->removed)
                        continue;
                /*
                 * If this target, and the target on the list are the same
                 * and if one of them contains conditional macro info, while
                 * the other doesn't,  remove this entry from the list of
                 * recursive entries.  This can only happen if the Makefile
                 * has changed to no longer contain conditional macros.
                 */
                if (IS_WEQUAL(rp->target, wcb)) {
                        if (rp->cond_macrostring[VER_LEN] == '\0' &&
                            string.buffer.start[VER_LEN] != '\0'){
                                rp->removed = true;
                                continue;
                        } else if (rp->cond_macrostring[VER_LEN] != '\0' &&
                            string.buffer.start[VER_LEN] == '\0'){
                                rp->removed = true;
                                continue;
                        }
                }
                /*
                 * If this is not a VERS2 entry,  only need to match
                 * the target name.  toptarg information from VERS1 entries
                 * are ignored.
                 */
                MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION);
                if (IS_WEQUALN(wcs_buffer, string.buffer.start, VER_LEN)) {
                        if (IS_WEQUAL(rp->cond_macrostring,
                            string.buffer.start) &&
                            IS_WEQUAL(rp->target, wcb)) {
                                return rp;
                        }
                } else {
                        if (IS_WEQUAL(rp->target, wcb)) {
                                return rp;
                        }
                }
        }
        return NULL;
}

/*
 *      remove_recursive_dep(target, top_level_target)
 *
 *      Mark a target as no longer recursive.
 *
 *      Parameters:
 *              target          The target we want to remove
 *              top_level_target target we want to remove must be built from
 *                               the same top level target
 *
 *      Static variables used:
 *              changed         Written if report set changed
 */
void
remove_recursive_dep(Name target)
{
        Recursive_make  rp;

        rp = find_recursive_target(target);

        if ( rp != NULL ) {
                rp->removed = true;
                changed = true;
                if(rp->target) {
                        retmem(rp->target);
                        rp->target = NULL;
                }
                if(rp->newline) {
                        retmem(rp->newline);
                        rp->newline = NULL;
                }
                if(rp->oldline) {
                        retmem(rp->oldline);
                        rp->oldline = NULL;
                }
                if(rp->cond_macrostring) {
                        retmem(rp->cond_macrostring);
                        rp->cond_macrostring = NULL;
                }
        }
}


/* gather_recursive_deps()
 *
 *      Create or update list of recursive targets.
 */
void
gather_recursive_deps(void)
{
        Name_set::iterator      np, e;
        String_rec              rec;
        wchar_t                 rec_buf[STRING_BUFFER_LENGTH];
        Property        lines;
        Boolean                 has_recursive;
        Dependency              dp;

        report_recursive_init();

        /* Go thru all targets and dump recursive dependencies */
        for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) {
                if (np->has_recursive_dependency){
                        has_recursive = false;
                        /*
                         * start .RECURSIVE line with target:
                         */
                        INIT_STRING_FROM_STACK(rec, rec_buf);
                        APPEND_NAME(np, &rec, FIND_LENGTH);
                        append_char((int) colon_char, &rec);
                        append_char((int) space_char, &rec);

                        for (lines = get_prop(np->prop,recursive_prop);
                            lines != NULL;
                            lines = get_prop(lines->next, recursive_prop)) {
                                /*
                                 * if entry is already in depinfo
                                 * file or entry was not built, ignore it
                                 */
                                if (lines->body.recursive.in_depinfo)
                                        continue;
                                if (!lines->body.recursive.has_built)
                                        continue;
                                has_recursive = true;
                                lines->body.recursive.in_depinfo=true;

                                /*
                                * Write the remainder of the
                                * .RECURSIVE line
                                */
                                APPEND_NAME(recursive_name, &rec,
                                    FIND_LENGTH);
                                append_char((int) space_char, &rec);
                                APPEND_NAME(lines->body.recursive.directory,
                                        &rec, FIND_LENGTH);
                                append_char((int) space_char, &rec);
                                APPEND_NAME(lines->body.recursive.target,
                                        &rec, FIND_LENGTH);
                                append_char((int) space_char, &rec);

                                /* Complete list of makefiles used */
                                for (dp = lines->body.recursive.makefiles;
                                    dp != NULL;
                                    dp = dp->next) {
                                        APPEND_NAME(dp->name, &rec,  FIND_LENGTH);
                                        append_char((int) space_char, &rec);
                                }
                        }
                        /*
                         * dump list of conditional targets,
                         * and report recursive entry, if needed
                         */
                        cond_macros_into_string(np, &rec);
                        if (has_recursive){
                                report_recursive_dep(np, rec.buffer.start);
                        }

                } else if ( np->has_built ) {
                        remove_recursive_dep(np);
                }
        }
}