From 6804ba69d600db117d13b218dc8ad54fd64ebd6b Mon Sep 17 00:00:00 2001 From: Georg Sauthoff Date: Sun, 21 Aug 2016 14:32:02 +0200 Subject: [PATCH] import Sun's dmake from illumos Source: svn export https://github.com/illumos/illumos-gate/trunk/usr/src/cmd/make/ Yes, github also has a Subversion compatible interface - which seems to be useful when one is only interested in a set of files. The Github Subversion server printed: Exported revision 15816. This corresponds to: https://github.com/illumos/illumos-gate/tree/67c3092ccd4e8c261df7eded9df072ff9c4e330b/usr/src/cmd/make --- bin/ar.cc | 846 +++++++++ bin/depvar.cc | 108 ++ bin/doname.cc | 3209 ++++++++++++++++++++++++++++++++++ bin/dosys.cc | 163 ++ bin/files.cc | 711 ++++++++ bin/globals.cc | 181 ++ bin/implicit.cc | 1462 ++++++++++++++++ bin/macro.cc | 167 ++ bin/main.cc | 3215 +++++++++++++++++++++++++++++++++++ bin/make.rules.file | 498 ++++++ bin/misc.cc | 736 ++++++++ bin/nse_printdep.cc | 365 ++++ bin/parallel.cc | 1892 +++++++++++++++++++++ bin/pmake.cc | 420 +++++ bin/read.cc | 2148 +++++++++++++++++++++++ bin/read2.cc | 1898 +++++++++++++++++++++ bin/rep.cc | 394 +++++ bin/state.cc | 444 +++++ bin/svr4.make.rules.file | 241 +++ include/bsd/bsd.h | 47 + include/mk/defs.h | 396 +++++ include/mksh/defs.h | 945 ++++++++++ include/mksh/dosys.h | 37 + include/mksh/globals.h | 30 + include/mksh/i18n.h | 33 + include/mksh/libmksh_init.h | 31 + include/mksh/macro.h | 36 + include/mksh/misc.h | 55 + include/mksh/mksh.h | 37 + include/mksh/read.h | 33 + include/vroot/args.h | 63 + include/vroot/report.h | 48 + include/vroot/vroot.h | 61 + lib/bsd/bsd.cc | 73 + lib/makestate/ld_file.c | 188 ++ lib/makestate/lock.c | 172 ++ lib/mksh/dosys.cc | 577 +++++++ lib/mksh/globals.cc | 128 ++ lib/mksh/i18n.cc | 91 + lib/mksh/macro.cc | 1335 +++++++++++++++ lib/mksh/misc.cc | 1113 ++++++++++++ lib/mksh/mksh.cc | 131 ++ lib/mksh/read.cc | 170 ++ lib/vroot/access.cc | 42 + lib/vroot/args.cc | 31 + lib/vroot/chdir.cc | 41 + lib/vroot/chmod.cc | 46 + lib/vroot/chown.cc | 47 + lib/vroot/chroot.cc | 44 + lib/vroot/creat.cc | 47 + lib/vroot/execve.cc | 50 + lib/vroot/lock.cc | 175 ++ lib/vroot/lstat.cc | 46 + lib/vroot/mkdir.cc | 46 + lib/vroot/mount.cc | 47 + lib/vroot/open.cc | 49 + lib/vroot/readlink.cc | 46 + lib/vroot/report.cc | 333 ++++ lib/vroot/rmdir.cc | 44 + lib/vroot/setenv.cc | 61 + lib/vroot/stat.cc | 46 + lib/vroot/truncate.cc | 45 + lib/vroot/unlink.cc | 44 + lib/vroot/utimes.cc | 46 + lib/vroot/vroot.cc | 338 ++++ 65 files changed, 26642 insertions(+) create mode 100644 bin/ar.cc create mode 100644 bin/depvar.cc create mode 100644 bin/doname.cc create mode 100644 bin/dosys.cc create mode 100644 bin/files.cc create mode 100644 bin/globals.cc create mode 100644 bin/implicit.cc create mode 100644 bin/macro.cc create mode 100644 bin/main.cc create mode 100644 bin/make.rules.file create mode 100644 bin/misc.cc create mode 100644 bin/nse_printdep.cc create mode 100644 bin/parallel.cc create mode 100644 bin/pmake.cc create mode 100644 bin/read.cc create mode 100644 bin/read2.cc create mode 100644 bin/rep.cc create mode 100644 bin/state.cc create mode 100644 bin/svr4.make.rules.file create mode 100644 include/bsd/bsd.h create mode 100644 include/mk/defs.h create mode 100644 include/mksh/defs.h create mode 100644 include/mksh/dosys.h create mode 100644 include/mksh/globals.h create mode 100644 include/mksh/i18n.h create mode 100644 include/mksh/libmksh_init.h create mode 100644 include/mksh/macro.h create mode 100644 include/mksh/misc.h create mode 100644 include/mksh/mksh.h create mode 100644 include/mksh/read.h create mode 100644 include/vroot/args.h create mode 100644 include/vroot/report.h create mode 100644 include/vroot/vroot.h create mode 100644 lib/bsd/bsd.cc create mode 100644 lib/makestate/ld_file.c create mode 100644 lib/makestate/lock.c create mode 100644 lib/mksh/dosys.cc create mode 100644 lib/mksh/globals.cc create mode 100644 lib/mksh/i18n.cc create mode 100644 lib/mksh/macro.cc create mode 100644 lib/mksh/misc.cc create mode 100644 lib/mksh/mksh.cc create mode 100644 lib/mksh/read.cc create mode 100644 lib/vroot/access.cc create mode 100644 lib/vroot/args.cc create mode 100644 lib/vroot/chdir.cc create mode 100644 lib/vroot/chmod.cc create mode 100644 lib/vroot/chown.cc create mode 100644 lib/vroot/chroot.cc create mode 100644 lib/vroot/creat.cc create mode 100644 lib/vroot/execve.cc create mode 100644 lib/vroot/lock.cc create mode 100644 lib/vroot/lstat.cc create mode 100644 lib/vroot/mkdir.cc create mode 100644 lib/vroot/mount.cc create mode 100644 lib/vroot/open.cc create mode 100644 lib/vroot/readlink.cc create mode 100644 lib/vroot/report.cc create mode 100644 lib/vroot/rmdir.cc create mode 100644 lib/vroot/setenv.cc create mode 100644 lib/vroot/stat.cc create mode 100644 lib/vroot/truncate.cc create mode 100644 lib/vroot/unlink.cc create mode 100644 lib/vroot/utimes.cc create mode 100644 lib/vroot/vroot.cc diff --git a/bin/ar.cc b/bin/ar.cc new file mode 100644 index 0000000..898f7aa --- /dev/null +++ b/bin/ar.cc @@ -0,0 +1,846 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * ar.c + * + * Deal with the lib.a(member.o) and lib.a((entry-point)) notations + * + * Look inside archives for notations a(b) and a((b)) + * a(b) is file member b in archive a + * a((b)) is entry point b in object archive a + * + * For 6.0, create a make which can understand all archive + * formats. This is kind of tricky, and isnt any help. + */ + +/* + * Included files + */ +#include /* alloca() */ +#include +#include /* errno */ +#include /* open() */ +#include +#include +#include /* retmem_mb() */ + +struct ranlib { + union { + off_t ran_strx; /* string table index of */ + char *ran_name; /* symbol defined by */ + } ran_un; + off_t ran_off; /* library member at this offset */ +}; + +#include /* close() */ + + +/* + * Defined macros + */ +#ifndef S5EMUL +#undef BITSPERBYTE +#define BITSPERBYTE 8 +#endif + +/* + * Defines for all the different archive formats. See next comment + * block for justification for not using s versions. + */ +#define AR_5_MAGIC "" /* 5.0 format magic string */ +#define AR_5_MAGIC_LENGTH 4 /* 5.0 format string length */ + +#define AR_PORT_MAGIC "!\n" /* Port. (6.0) magic string */ +#define AR_PORT_MAGIC_LENGTH 8 /* Port. (6.0) string length */ +#define AR_PORT_END_MAGIC "`\n" /* Port. (6.0) end of header */ +#define AR_PORT_WORD 4 /* Port. (6.0) 'word' length */ + +/* + * typedefs & structs + */ +/* + * These are the archive file headers for the formats. Note + * that it really doesnt matter if these structures are defined + * here. They are correct as of the respective archive format + * releases. If the archive format is changed, then since backwards + * compatability is the desired behavior, a new structure is added + * to the list. + */ +typedef struct { /* 5.0 ar header format: vax family; 3b family */ + char ar_magic[AR_5_MAGIC_LENGTH]; /* AR_5_MAGIC*/ + char ar_name[16]; /* Space terminated */ + char ar_date[AR_PORT_WORD]; /* sgetl() accessed */ + char ar_syms[AR_PORT_WORD]; /* sgetl() accessed */ +} Arh_5; + +typedef struct { /* 5.0 ar symbol format: vax family; 3b family */ + char sym_name[8]; /* Space terminated */ + char sym_ptr[AR_PORT_WORD]; /* sgetl() accessed */ +} Ars_5; + +typedef struct { /* 5.0 ar member format: vax family; 3b family */ + char arf_name[16]; /* Space terminated */ + char arf_date[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_uid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_gid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_mode[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_size[AR_PORT_WORD]; /* sgetl() accessed */ +} Arf_5; + +typedef struct { /* Portable (6.0) ar format: vax family; 3b family */ + char ar_name[16]; /* Space terminated */ + /* left-adjusted fields; decimal ascii; blank filled */ + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; /* octal ascii */ + char ar_size[10]; + /* special end-of-header string (AR_PORT_END_MAGIC) */ + char ar_fmag[2]; +} Ar_port; + +enum ar_type { + AR_5, + AR_PORT +}; + +typedef unsigned int ar_port_word; // must be 4-bytes long + +typedef struct { + FILE *fd; + /* to distiguish ar format */ + enum ar_type type; + /* where first ar member header is at */ + long first_ar_mem; + /* where the symbol lookup starts */ + long sym_begin; + /* the number of symbols available */ + long num_symbols; + /* length of symbol directory file */ + long sym_size; + Arh_5 arh_5; + Ars_5 ars_5; + Arf_5 arf_5; + Ar_port ar_port; +} Ar; + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& read_archive(register Name target); +static Boolean open_archive(char *filename, register Ar *arp); +static void close_archive(register Ar *arp); +static Boolean read_archive_dir(register Ar *arp, Name library, char **long_names_table); +static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table); +static long sgetl(char *); + +/* + * read_archive(target) + * + * Read the contents of an ar file. + * + * Return value: + * The time the member was created + * + * Parameters: + * target The member to find time for + * + * Global variables used: + * empty_name The Name "" + */ + +int read_member_header (Ar_port *header, FILE *fd, char* filename); +int process_long_names_member (register Ar *arp, char **long_names_table, char *filename); + +timestruc_t& +read_archive(register Name target) +{ + register Property member; + wchar_t *slash; + String_rec true_member_name; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Name true_member = NULL; + Ar ar; + char *long_names_table = NULL; /* Table of long + member names */ + + member = get_prop(target->prop, member_prop); + /* + * Check if the member has directory component. + * If so, remove the dir and see if we know the date. + */ + if (member->body.member.member != NULL) { + Wstring member_string(member->body.member.member); + wchar_t * wcb = member_string.get_string(); + if((slash = (wchar_t *) wcsrchr(wcb, (int) slash_char)) != NULL) { + INIT_STRING_FROM_STACK(true_member_name, buffer); + append_string(member->body.member.library->string_mb, + &true_member_name, + FIND_LENGTH); + append_char((int) parenleft_char, &true_member_name); + append_string(slash + 1, &true_member_name, FIND_LENGTH); + append_char((int) parenright_char, &true_member_name); + true_member = GETNAME(true_member_name.buffer.start, + FIND_LENGTH); + if (true_member->stat.time != file_no_time) { + target->stat.time = true_member->stat.time; + return target->stat.time; + } + } + } + if (open_archive(member->body.member.library->string_mb, &ar) == failed) { + if (errno == ENOENT) { + target->stat.stat_errno = ENOENT; + close_archive(&ar); + if (member->body.member.member == NULL) { + member->body.member.member = empty_name; + } + return target->stat.time = file_doesnt_exist; + } else { + fatal(gettext("Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (target->stat.time == file_no_time) { + if (read_archive_dir(&ar, member->body.member.library, + &long_names_table) + == failed){ + fatal(gettext("Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (member->body.member.entry != NULL) { + translate_entry(&ar, target, member,&long_names_table); + } + close_archive(&ar); + if (long_names_table) { + retmem_mb(long_names_table); + } + if (true_member != NULL) { + target->stat.time = true_member->stat.time; + } + if (target->stat.time == file_no_time) { + target->stat.time = file_doesnt_exist; + } + return target->stat.time; +} + +/* + * open_archive(filename, arp) + * + * Return value: + * Indicates if open failed or not + * + * Parameters: + * filename The name of the archive we need to read + * arp Pointer to ar file description block + * + * Global variables used: + */ +static Boolean +open_archive(char *filename, register Ar *arp) +{ + int fd; + char mag_5[AR_5_MAGIC_LENGTH]; + char mag_port[AR_PORT_MAGIC_LENGTH]; + char buffer[4]; + + arp->fd = NULL; + fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT); + if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) { + return failed; + } + (void) fcntl(fileno(arp->fd), F_SETFD, 1); + + if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) { + return failed; + } + if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) { + arp->type = AR_PORT; + /* + * Read in first member header to find out if there is + * a symbol definition table. + */ + + int ret = read_member_header(&arp->ar_port, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd); + return succeeded; + } + /* + * The following values are the default if there is + * no symbol directory and long member names. + */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port); + + /* + * Do we have a symbol table? A symbol table is always + * the first member in an archive. In 4.1.x it has the + * name __.SYMDEF, in SVr4, it has the name "/ " + */ +/* + MBSTOWCS(wcs_buffer, "/ "); + if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { + */ + if (IS_EQUALN(arp->ar_port.ar_name, + "/ ", + 16)) { + if (sscanf(arp->ar_port.ar_size, + "%ld", + &arp->sym_size) != 1) { + return failed; + } + arp->sym_size += (arp->sym_size & 1); /* round up */ + if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) { + return failed; + } + arp->num_symbols = sgetl(buffer); + arp->sym_begin = ftell(arp->fd); + arp->first_ar_mem = arp->sym_begin + + arp->sym_size - sizeof buffer; + } + return succeeded; + } + fatal(gettext("`%s' is not an archive"), filename); + /* NOTREACHED */ + return failed; +} + + +/* + * close_archive(arp) + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +static void +close_archive(register Ar *arp) +{ + if (arp->fd != NULL) { + (void) fclose(arp->fd); + } +} + +/* + * read_archive_dir(arp, library, long_names_table) + * + * Reads the directory of an archive and enters all + * the members into the make symboltable in lib(member) format + * with their dates. + * + * Parameters: + * arp Pointer to ar file description block + * library Name of lib to enter members for. + * Used to form "lib(member)" string. + * long_names_table table that contains list of members + * with names > 15 characters long + * + * Global variables used: + */ +static Boolean +read_archive_dir(register Ar *arp, Name library, char **long_names_table) +{ + wchar_t *name_string; + wchar_t *member_string; + register long len; + register wchar_t *p; + register char *q; + register Name name; + Property member; + long ptr; + long date; + + int offset; + + /* + * If any of the members has a name > 15 chars, + * it will be found here. + */ + if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) { + return failed; + } + name_string = ALLOC_WC((int) (library->hash.length + + (int) ar_member_name_len * 2)); + (void) mbstowcs(name_string, library->string_mb, (int) library->hash.length); + member_string = name_string + library->hash.length; + *member_string++ = (int) parenleft_char; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + goto read_error; + } + /* Read the directory using the appropriate format */ + switch (arp->type) { + case AR_5: + for (;;) { + if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd) + != 1) { + if (feof(arp->fd)) { + return succeeded; + } + break; + } + len = sizeof arp->arf_5.arf_name; + for (p = member_string, q = arp->arf_5.arf_name; + (len > 0) && (*q != (int) nul_char) && !isspace(*q); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + /* + * [tolik] Fix for dmake bug 1234018. + * If name->stat.time is already set, then it should not + * be changed. (D)make propogates time stamp for one + * member, and when it calls exists() for another member, + * the first one may be changed. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + name->stat.time.tv_nsec = LONG_MAX; + } + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + ptr = sgetl(arp->arf_5.arf_size); + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + case AR_PORT: + for (;;) { + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + if (feof(arp->fd)) { + return succeeded; + } + fatal( + gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), + library->string_mb, + ftell(arp->fd) + ); + } + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + /* + * "len" is used for hashing the string. + * We're using "ar_member_name_len" instead of + * the actual name length since it's the longest + * string the "ar" command can handle at this + * point. + */ + len = ar_member_name_len; + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + q = *long_names_table + offset; + } else { + q = arp->ar_port.ar_name; + len = sizeof arp->ar_port.ar_name; + } + + for (p = member_string; + (len > 0) && + (*q != (int) nul_char) && + !isspace(*q) && + (*q != (int) slash_char); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(gettext("Bad date field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + /* + * [tolik] Fix for dmake bug 1234018. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = date; + name->stat.time.tv_nsec = LONG_MAX; + } + if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(gettext("Bad size field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + } + + /* Only here if fread() [or IS_EQUALN()] failed and not at EOF */ +read_error: + fatal(gettext("Read error in archive `%s': %s"), + library->string_mb, + errmsg(errno)); + /* NOTREACHED */ +} + + +/* + * process_long_names_member(arp) + * + * If the archive contains members with names longer + * than 15 characters, then it has a special member + * with the name "// " that contains a table + * of null-terminated long names. This member + * is always the first member, after the symbol table + * if it exists. + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +int +process_long_names_member(register Ar *arp, char **long_names_table, char *filename) +{ + Ar_port *ar_member_header; + int table_size; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + return failed; + } + if ((ar_member_header = + (Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){ + perror(gettext("memory allocation failure")); + return failed; + } + int ret = read_member_header(ar_member_header, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + return succeeded; + } + /* Do we have special member containing long names? */ + if (IS_EQUALN(ar_member_header->ar_name, + "// ", + 16)){ + if (sscanf(ar_member_header->ar_size, + "%ld", + &table_size) != 1) { + return failed; + } + *long_names_table = (char *) malloc(table_size); + /* Read the list of long member names into the table */ + if (fread(*long_names_table, table_size, 1, arp->fd) != 1) { + return failed; + } + arp->first_ar_mem = ftell(arp->fd); + } + return succeeded; +} + +/* + * translate_entry(arp, target, member) + * + * Finds the member for one lib.a((entry)) + * + * Parameters: + * arp Pointer to ar file description block + * target Target to find member name for + * member Property to fill in with info + * + * Global variables used: + */ +static void +translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table) +{ + register int len; + register int i; + wchar_t *member_string; + ar_port_word *offs; + int strtablen; + char *syms; /* string table */ + char *csym; /* string table */ + ar_port_word *offend; /* end of offsets table */ + int date; + register wchar_t *ap; + register char *hp; + int maxs; + int offset; + char buffer[4]; + + if (arp->sym_begin == 0L || arp->num_symbols == 0L) { + fatal(gettext("Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + } + + if (fseek(arp->fd, arp->sym_begin, 0) != 0) { + goto read_error; + } + member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2)); + + switch (arp->type) { + case AR_5: + if ((len = member->body.member.entry->hash.length) > 8) { + len = 8; + } + for (i = 0; i < arp->num_symbols; i++) { + if (fread((char *) &arp->ars_5, + sizeof arp->ars_5, + 1, + arp->fd) != 1) { + goto read_error; + } + if (IS_EQUALN(arp->ars_5.sym_name, + member->body.member.entry->string_mb, + len)) { + if ((fseek(arp->fd, + sgetl(arp->ars_5.sym_ptr), + 0) != 0) || + (fread((char *) &arp->arf_5, + sizeof arp->arf_5, + 1, + arp->fd) != 1)) { + goto read_error; + } + MBSTOWCS(wcs_buffer, arp->arf_5.arf_name); + (void) wcsncpy(member_string, + wcs_buffer, + wcslen(wcs_buffer)); + member_string[sizeof(arp->arf_5.arf_name)] = + (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + target->stat.time.tv_nsec = LONG_MAX; + return; + } + } + break; + case AR_PORT: + offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD)); + if (fread((char *) offs, + AR_PORT_WORD, + (int) arp->num_symbols, + arp->fd) != arp->num_symbols) { + goto read_error; + } + + for(i=0;inum_symbols;i++) { + *((int*)buffer)=offs[i]; + offs[i]=(ar_port_word)sgetl(buffer); + } + + strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD); + syms = (char *) alloca(strtablen); + if (fread(syms, + sizeof (char), + strtablen, + arp->fd) != strtablen) { + goto read_error; + } + offend = &offs[arp->num_symbols]; + while (offs < offend) { + maxs = strlen(member->body.member.entry->string_mb); + if(strlen(syms) > maxs) + maxs = strlen(syms); + if (IS_EQUALN(syms, + member->body.member.entry->string_mb, + maxs)) { + if (fseek(arp->fd, + (long) *offs, + 0) != 0) { + goto read_error; + } + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + goto read_error; + } + if (sscanf(arp->ar_port.ar_date, + "%ld", + &date) != 1) { + fatal(gettext("Bad date field for member `%s' in archive `%s'"), + arp->ar_port.ar_name, + target->string_mb); + } + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + len = ar_member_name_len; + hp = *long_names_table + offset; + } else { + len = sizeof arp->ar_port.ar_name; + hp = arp->ar_port.ar_name; + } + ap = member_string; + while (*hp && + (*hp != (int) slash_char) && + (ap < &member_string[len])) { + MBTOWC(ap, hp); + ap++; + hp++; + } + *ap = (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = date; + target->stat.time.tv_nsec = LONG_MAX; + return; + } + offs++; + while(*syms!='\0') syms++; + syms++; + } + } + fatal(gettext("Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + /*NOTREACHED*/ + +read_error: + if (ferror(arp->fd)) { + fatal(gettext("Read error in archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } else { + fatal(gettext("Read error in archive `%s': Premature EOF"), + member->body.member.library->string_mb); + } +} + +/* + * sgetl(buffer) + * + * The intent here is to provide a means to make the value of + * bytes in an io-buffer correspond to the value of a long + * in the memory while doing the io a long at a time. + * Files written and read in this way are machine-independent. + * + * Return value: + * Long int read from buffer + * Parameters: + * buffer buffer we need to read long int from + * + * Global variables used: + */ +static long +sgetl(register char *buffer) +{ + register long w = 0; + register int i = BITSPERBYTE * AR_PORT_WORD; + + while ((i -= BITSPERBYTE) >= 0) { + w |= (long) ((unsigned char) *buffer++) << i; + } + return w; +} + + +/* + * read_member_header(header, fd, filename) + * + * reads the member header for the 4.1.x and SVr4 archives. + * + * Return value: + * fails if read error or member + * header is not the right format + * Parameters: + * header There's one before each archive member + * fd file descriptor for the archive file. + * + * Global variables used: + */ +int +read_member_header(Ar_port *header, FILE *fd, char* filename) +{ + int num = fread((char *) header, sizeof (Ar_port), 1, fd); + if (num != 1 && feof(fd)) { + /* There is no member header - empty archive */ + return -1; + } + if ((num != 1) || + !IS_EQUALN( + AR_PORT_END_MAGIC, + header->ar_fmag, + sizeof (header->ar_fmag) + ) + ) { + fatal( + gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), + filename, + ftell(fd) + ); + } + return succeeded; +} + diff --git a/bin/depvar.cc b/bin/depvar.cc new file mode 100644 index 0000000..4f348a5 --- /dev/null +++ b/bin/depvar.cc @@ -0,0 +1,108 @@ +/* + * 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 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Included files + */ +#include + +#include +#include /* getmem() */ + +/* + * This file deals with "Dependency Variables". + * The "-V var" command line option is used to indicate + * that var is a dependency variable. Used in conjunction with + * the -P option the user is asking if the named variables affect + * the dependencies of the given target. + */ + +struct _Depvar { + Name name; /* Name of variable */ + struct _Depvar *next; /* Linked list */ + Boolean cmdline; /* Macro defined on the cmdline? */ +}; + +typedef struct _Depvar *Depvar; + +static Depvar depvar_list; +static Depvar *bpatch = &depvar_list; +static Boolean variant_deps; + +/* + * Add a name to the list. + */ + +void +depvar_add_to_list(Name name, Boolean cmdline) +{ + Depvar dv; + + dv = ALLOC(Depvar); + dv->name = name; + dv->next = NULL; + dv->cmdline = cmdline; + *bpatch = dv; + bpatch = &dv->next; +} + +/* + * The macro `name' has been used in either the left-hand or + * right-hand side of a dependency. See if it is in the + * list. Two things are looked for. Names given as args + * to the -V list are checked so as to set the same/differ + * output for the -P option. Names given as macro=value + * command-line args are checked and, if found, an NSE + * warning is produced. + */ +void +depvar_dep_macro_used(Name name) +{ + Depvar dv; + + for (dv = depvar_list; dv != NULL; dv = dv->next) { + if (name == dv->name) { + variant_deps = true; + break; + } + } +} + + +/* + * Print the results. If any of the Dependency Variables + * affected the dependencies then the dependencies potentially + * differ because of these variables. + */ +void +depvar_print_results(void) +{ + if (variant_deps) { + printf(gettext("differ\n")); + } else { + printf(gettext("same\n")); + } +} + diff --git a/bin/doname.cc b/bin/doname.cc new file mode 100644 index 0000000..85be203 --- /dev/null +++ b/bin/doname.cc @@ -0,0 +1,3209 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2016 RackTop Systems. + */ + +/* + * doname.c + * + * Figure out which targets are out of date and rebuild them + */ + +/* + * Included files + */ +#include /* alloca() */ +#include +#include +#include /* get_char_semantics_value() */ +#include /* getvar(), expand_value() */ +#include /* getmem() */ +#include +#include +#include +#include +#include +#include +#include +#include /* uname() */ +#include +#include /* close() */ + +/* + * Defined macros + */ +# define LOCALHOST "localhost" + +#define MAXRULES 100 + +// Sleep for .1 seconds between stat()'s +const int STAT_RETRY_SLEEP_TIME = 100000; + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char hostName[MAXNAMELEN] = ""; +static char userName[MAXNAMELEN] = ""; + + +static int second_pass = 0; + +/* + * File table of contents + */ +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +static Boolean check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals); +void dynamic_dependencies(Name target); +static Doname run_command(register Property line, Boolean print_machine); +extern Doname execute_serial(Property line); +extern Name vpath_translation(register Name cmd); +extern void check_state(Name temp_file_name); +static void read_dependency_file(register Name filename); +static void check_read_state_file(void); +static void do_assign(register Name line, register Name target); +static void build_command_strings(Name target, register Property line); +static Doname touch_command(register Property line, register Name target, Doname result); +extern void update_target(Property line, Doname result); +static Doname sccs_get(register Name target, register Property *command); +extern void read_directory_of_file(register Name file); +static void add_pattern_conditionals(register Name target); +extern void set_locals(register Name target, register Property old_locals); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +static void delete_query_chain(Chain ch); + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + + + +/* + * DONE. + * + * doname_check(target, do_get, implicit, automatic) + * + * Will call doname() and then inspect the return value + * + * Return value: + * Indication if the build failed or not + * + * Parameters: + * target The target to build + * do_get Passed thru to doname() + * implicit Passed thru to doname() + * automatic Are we building a hidden dependency? + * + * Global variables used: + * build_failed_seen Set if -k is on and error occurs + * continue_after_error Indicates that -k is on + * report_dependencies No error msg if -P is on + */ +Doname +doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + int first_time = 1; + (void) fflush(stdout); +try_again: + switch (doname(target, do_get, implicit, automatic)) { + case build_ok: + second_pass = 0; + return build_ok; + case build_running: + second_pass = 0; + return build_running; + case build_failed: + if (!continue_after_error) { + fatal(gettext("Target `%s' not remade because of errors"), + target->string_mb); + } + build_failed_seen = true; + second_pass = 0; + return build_failed; + case build_dont_know: + /* + * If we can't figure out how to build an automatic + * (hidden) dependency, we just ignore it. + * We later declare the target to be out of date just in + * case something changed. + * Also, don't complain if just reporting the dependencies + * and not building anything. + */ + if (automatic || (report_dependencies_level > 0)) { + second_pass = 0; + return build_dont_know; + } + if(first_time) { + first_time = 0; + second_pass = 1; + goto try_again; + } + second_pass = 0; + if (continue_after_error && !svr4) { + warning(gettext("Don't know how to make target `%s'"), + target->string_mb); + build_failed_seen = true; + return build_failed; + } + fatal(gettext("Don't know how to make target `%s'"), target->string_mb); + break; + } +#ifdef lint + return build_failed; +#endif +} + + +void +enter_explicit_rule_from_dynamic_rule(Name target, Name source) +{ + Property line, source_line; + Dependency dependency; + + source_line = get_prop(source->prop, line_prop); + line = maybe_append_prop(target, line_prop); + line->body.line.sccs_command = false; + line->body.line.target = target; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = source_line->body.line.command_template; + for (dependency = source_line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + enter_dependency(line, dependency->name, false); + } + line->body.line.less = target; + } + line->body.line.percent = NULL; +} + + + +Name +find_dyntarget(Name target) +{ + Dyntarget p; + int i; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *pp, * bufend; + wchar_t tbuffer[MAXPATHLEN]; + Wstring wcb(target); + + for (p = dyntarget_list; p != NULL; p = p->next) { + INIT_STRING_FROM_STACK(string, buffer); + expand_value(p->name, &string, false); + i = 0; + pp = string.buffer.start; + bufend = pp + STRING_BUFFER_LENGTH; + while((*pp != nul_char) && (pp < bufend)) { + if(iswspace(*pp)) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + pp++; + i = 0; + continue; + } + tbuffer[i] = *pp; + i++; + pp++; + if(*pp == nul_char) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + break; + } + } + } + return(NULL); +} + +/* + * DONE. + * + * doname(target, do_get, implicit) + * + * Chases all files the target depends on and builds any that + * are out of date. If the target is out of date it is then rebuilt. + * + * Return value: + * Indiates if build failed or nt + * + * Parameters: + * target Target to build + * do_get Run sccs get is nessecary + * implicit doname is trying to find an implicit rule + * + * Global variables used: + * assign_done True if command line assgnment has happened + * commands_done Preserved for the case that we need local value + * debug_level Should we trace make's actions? + * default_rule The rule for ".DEFAULT", used as last resort + * empty_name The Name "", used when looking for single sfx + * keep_state Indicates that .KEEP_STATE is on + * parallel True if building in parallel + * recursion_level Used for tracing + * report_dependencies make -P is on + */ +Doname +doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + Doname result = build_dont_know; + Chain out_of_date_list = NULL; + Chain target_group; + Property old_locals = NULL; + register Property line; + Property command = NULL; + register Dependency dependency; + Name less = NULL; + Name true_target = target; + Name *automatics = NULL; + register int auto_count; + Boolean rechecking_target = false; + Boolean saved_commands_done; + Boolean restart = false; + Boolean save_parallel = parallel; + Boolean doing_subtree = false; + + Boolean recheck_conditionals = false; + + if (target->state == build_running) { + return build_running; + } + line = get_prop(target->prop, line_prop); + if (line != NULL) { + /* + * If this target is a member of target group and one of the + * other members of the group is running, mark this target + * as running. + */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + target->state = build_running; + add_pending(target, + recursion_level, + do_get, + implicit, + false); + return build_running; + } + } + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + if (true_target->colon_splits > 0) { + /* Make sure we have a valid time for :: targets */ + Property time; + + time = get_prop(true_target->prop, time_prop); + if (time != NULL) { + true_target->stat.time = time->body.time.time; + } + } + } + (void) exists(true_target); + /* + * If the target has been processed, we don't need to do it again, + * unless it depends on conditional macros or a delayed assignment, + * or it has been done when KEEP_STATE is on. + */ + if (target->state == build_ok) { + if((!keep_state || (!target->depends_on_conditional && !assign_done))) { + return build_ok; + } else { + recheck_conditionals = true; + } + } + if (target->state == build_subtree) { + /* A dynamic macro subtree is being built */ + target->state = build_dont_know; + doing_subtree = true; + if (!target->checking_subtree) { + /* + * This target has been started before and therefore + * not all dependencies have to be built. + */ + restart = true; + } + } else if (target->state == build_pending) { + target->state = build_dont_know; + restart = true; +/* + } else if (parallel && + keep_state && + (target->conditional_cnt > 0)) { + if (!parallel_ok(target, false)) { + add_subtree(target, recursion_level, do_get, implicit); + target->state = build_running; + return build_running; + } + */ + } + /* + * If KEEP_STATE is on, we have to rebuild the target if the + * building of it caused new automatic dependencies to be reported. + * This is where we restart the build. + */ + if (line != NULL) { + line->body.line.percent = NULL; + } +recheck_target: + /* Init all local variables */ + result = build_dont_know; + out_of_date_list = NULL; + command = NULL; + less = NULL; + auto_count = 0; + if (!restart && line != NULL) { + /* + * If this target has never been built before, mark all + * of the dependencies as never built. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + dependency->built = false; + } + } + /* Save the set of automatic depes defined for this target */ + if (keep_state && + (line != NULL) && + (line->body.line.dependencies != NULL)) { + Name *p; + + /* + * First run thru the dependency list to see how many + * autos there are. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + auto_count++; + } + } + /* Create vector to hold the current autos */ + automatics = + (Name *) alloca((int) (auto_count * sizeof (Name))); + /* Copy them */ + for (p = automatics, dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + *p++ = dependency->name; + } + } + } + if (debug_level > 1) { + (void) printf("%*sdoname(%s)\n", + recursion_level, + "", + target->string_mb); + } + recursion_level++; + /* Avoid infinite loops */ + if (target->state == build_in_progress) { + warning(gettext("Infinite loop: Target `%s' depends on itself"), + target->string_mb); + return build_ok; + } + target->state = build_in_progress; + + /* Activate conditional macros for the target */ + if (!target->added_pattern_conditionals) { + add_pattern_conditionals(target); + target->added_pattern_conditionals = true; + } + if (target->conditional_cnt > 0) { + old_locals = (Property) alloca(target->conditional_cnt * + sizeof (Property_rec)); + set_locals(target, old_locals); + } + +/* + * after making the call to dynamic_dependecies unconditional we can handle + * target names that are same as file name. In this case $$@ in the + * dependencies did not mean anything. WIth this change it expands it + * as expected. + */ + if (!target->has_depe_list_expanded) + { + dynamic_dependencies(target); + } + +/* + * FIRST SECTION -- GO THROUGH DEPENDENCIES AND COLLECT EXPLICIT + * COMMANDS TO RUN + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + + +/* + * If the target is a :: type, do not try to find the rule for the target, + * all actions will be taken by separate branches. + * Else, we try to find an implicit rule using various methods, + * we quit as soon as one is found. + * + * [tolik, 12 Sep 2002] Do not try to find implicit rule for the target + * being rechecked - the target is being rechecked means that it already + * has explicit dependencies derived from an implicit rule found + * in previous step. + */ + if (target->colon_splits == 0 && !rechecking_target) { + /* Look for percent matched rule */ + if ((result == build_dont_know) && + (command == NULL)) { + switch (find_percent_rule( + target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_ok: + result = build_ok; + break; + } + } + /* Look for double suffix rule */ + if (result == build_dont_know) { + Property member; + + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + switch (find_ar_suffix_rule(target, + member->body. + member.member, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + default: + /* ALWAYS bind $% for old style */ + /* ar rules */ + if (line == NULL) { + line = + maybe_append_prop(target, + line_prop); + } + line->body.line.percent = + member->body.member.member; + break; + } + } else { + switch (find_double_suffix_rule(target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target-> + prop, + conditional_prop), + 0); + } + return build_running; + } + } + } + /* Look for single suffix rule */ + +/* /tolik/ + * I commented !implicit to fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * This caused problem with SVR4 tilde rules (infinite recursion). So I made some changes in "implicit.cc" + */ +/* /tolik, 06.21.96/ + * Regression! See BugId 1255360 + * If more than one percent rules are defined for the same target then + * the behaviour of 'make' with my previous fix may be different from one + * of the 'old make'. + * The global variable second_pass (maybe it should be an argument to doname()) + * is intended to avoid this regression. It is set in doname_check(). + * First, 'make' will work as it worked before. Only when it is + * going to say "don't know how to make target" it sets second_pass to true and + * run 'doname' again but now trying to use Single Suffix Rules. + */ + if ((result == build_dont_know) && !automatic && (!implicit || second_pass) && + ((line == NULL) || + ((line->body.line.target != NULL) && + !line->body.line.target->has_regular_dependency))) { + switch (find_suffix_rule(target, + target, + empty_name, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + } + } + /* Try to sccs get */ + if ((command == NULL) && + (result == build_dont_know) && + do_get) { + result = sccs_get(target, &command); + } + + /* Use .DEFAULT rule if it is defined. */ + if ((command == NULL) && + (result == build_dont_know) && + (true_target->colons == no_colon) && + default_rule && + !implicit) { + /* Make sure we have a line prop */ + line = maybe_append_prop(target, line_prop); + command = line; + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using .DEFAULT because it is out of date\n"), + recursion_level, + "", + true_target->string_mb); + } + } + line->body.line.sccs_command = false; + line->body.line.command_template = default_rule; + line->body.line.target = true_target; + line->body.line.star = NULL; + line->body.line.less = true_target; + line->body.line.percent = NULL; + } + } + + /* We say "target up to date" if no cmd were executed for the target */ + if (!target->is_double_colon_parent) { + commands_done = false; + } + + silent = silent_all; + ignore_errors = ignore_errors_all; + if (posix) + { + if (!silent) + { + silent = (Boolean) target->silent_mode; + } + if (!ignore_errors) + { + ignore_errors = (Boolean) target->ignore_error_mode; + } + } + + int doname_dyntarget = 0; +r_command: + /* Run commands if any. */ + if ((command != NULL) && + (command->body.line.command_template != NULL)) { + if (result != build_failed) { + result = run_command(command, + (Boolean) ((parallel || save_parallel) && !silent)); + } + switch (result) { + case build_running: + add_running(target, + true_target, + command, + --recursion_level, + auto_count, + automatics, + do_get, + implicit); + target->state = build_running; + if ((line = get_prop(target->prop, + line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_serial: + add_serial(target, + --recursion_level, + do_get, + implicit); + target->state = build_running; + line = get_prop(target->prop, line_prop); + if (line != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_ok: + /* If all went OK set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + break; + } + } else { + /* + * If no command was found for the target, and it doesn't + * exist, and it is mentioned as a target in the makefile, + * we say it is extremely new and that it is OK. + */ + if (target->colons != no_colon) { + if (true_target->stat.time == file_doesnt_exist){ + true_target->stat.time = file_max_time; + } + result = build_ok; + } + /* + * Trying dynamic targets. + */ + if(!doname_dyntarget) { + doname_dyntarget = 1; + Name dtarg = find_dyntarget(target); + if(dtarg!=NULL) { + if (!target->has_depe_list_expanded) { + dynamic_dependencies(target); + } + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) + { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + goto r_command; + } + } + /* + * If the file exists, it is OK that we couldnt figure + * out how to build it. + */ + (void) exists(target); + if ((target->stat.time != file_doesnt_exist) && + (result == build_dont_know)) { + result = build_ok; + } + } + + /* + * Some of the following is duplicated in the function finish_doname. + * If anything is changed here, check to see if it needs to be + * changed there. + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + target->state = result; + parallel = save_parallel; + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, conditional_prop), + 0); + } + recursion_level--; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member*/ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + rechecking_target = true; + saved_commands_done = commands_done; + goto recheck_target; + } + + if (rechecking_target && !commands_done) { + commands_done = saved_commands_done; + } + + return result; +} + +/* + * DONE. + * + * check_dependencies(result, line, do_get, + * target, true_target, doing_subtree, out_of_date_tail, + * old_locals, implicit, command, less, rechecking_target) + * + * Return value: + * True returned if some dependencies left running + * + * Parameters: + * result Pointer to cell we update if build failed + * line We get the dependencies from here + * do_get Allow use of sccs get in recursive doname() + * target The target to chase dependencies for + * true_target The real one for :: and lib(member) + * doing_subtree True if building a conditional macro subtree + * out_of_date_tail Used to set the $? list + * old_locals Used for resetting the local macros + * implicit Called when scanning for implicit rules? + * command Place to stuff command + * less Set to $< value + * + * Global variables used: + * command_changed Set if we suspect .make.state needs rewrite + * debug_level Should we trace actions? + * force The Name " FORCE", compared against + * recursion_level Used for tracing + * rewrite_statefile Set if .make.state needs rewriting + * wait_name The Name ".WAIT", compared against + */ +static Boolean +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +{ + Boolean dependencies_running; + register Dependency dependency; + Doname dep_result; + Boolean dependency_changed = false; + + line->body.line.dependency_time = file_doesnt_exist; + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + line->body.line.is_out_of_date = false; + dependencies_running = false; + /* + * Run thru all the dependencies and call doname() recursively + * on each of them. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Boolean this_dependency_changed = false; + + if (!dependency->automatic && + (rechecking_target || target->rechecking_target)) { + /* + * We only bother with the autos when rechecking + */ + continue; + } + + if (dependency->name == wait_name) { + /* + * The special target .WAIT means finish all of + * the prior dependencies before continuing. + */ + if (dependencies_running) { + break; + } + } else if ((!parallel_ok(dependency->name, false)) && + (dependencies_running)) { + /* + * If we can't execute the current dependency in + * parallel, hold off the dependency processing + * to preserve the order of the dependencies. + */ + break; + } else { + timestruc_t depe_time = file_doesnt_exist; + + + if (true_target->is_member) { + depe_time = exists(dependency->name); + } + if (dependency->built || + (dependency->name->state == build_failed)) { + dep_result = (Doname) dependency->name->state; + } else { + dep_result = doname_check(dependency->name, + do_get, + false, + (Boolean) dependency->automatic); + } + if (true_target->is_member || dependency->name->is_member) { + /* should compare only secs, cause lib members does not have nsec time resolution */ + if (depe_time.tv_sec != dependency->name->stat.time.tv_sec) { + this_dependency_changed = + dependency_changed = + true; + } + } else { + if (depe_time != dependency->name->stat.time) { + this_dependency_changed = + dependency_changed = + true; + } + } + dependency->built = true; + switch (dep_result) { + case build_running: + dependencies_running = true; + continue; + case build_failed: + *result = build_failed; + break; + case build_dont_know: +/* + * If make can't figure out how to make a dependency, maybe the dependency + * is out of date. In this case, we just declare the target out of date + * and go on. If we really need the dependency, the make'ing of the target + * will fail. This will only happen for automatic (hidden) dependencies. + */ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + /* + * Make sure the dependency is not saved + * in the state file. + */ + dependency->stale = true; + rewrite_statefile = + command_changed = + true; + if (debug_level > 0) { + (void) printf(gettext("Target %s rebuilt because dependency %s does not exist\n"), + true_target->string_mb, + dependency->name->string_mb); + } + break; + } + if (dependency->name->depends_on_conditional) { + target->depends_on_conditional = true; + } + if (dependency->name == force) { + target->stat.time = + dependency->name->stat.time; + } + /* + * Propagate new timestamp from "member" to + * "lib.a(member)". + */ + (void) exists(dependency->name); + + /* Collect the timestamp of the youngest dependency */ + line->body.line.dependency_time = + MAX(dependency->name->stat.time, + line->body.line.dependency_time); + + /* Correction: do not consider nanosecs for members */ + if(true_target->is_member || dependency->name->is_member) { + line->body.line.dependency_time.tv_nsec = 0; + } + + if (debug_level > 1) { + (void) printf(gettext("%*sDate(%s)=%s \n"), + recursion_level, + "", + dependency->name->string_mb, + time_to_string(dependency->name-> + stat.time)); + if (dependency->name->stat.time > line->body.line.dependency_time) { + (void) printf(gettext("%*sDate-dependencies(%s) set to %s\n"), + recursion_level, + "", + true_target->string_mb, + time_to_string(line->body.line. + dependency_time)); + } + } + + /* Build the $? list */ + if (true_target->is_member) { + if (this_dependency_changed == true) { + true_target->stat.time = dependency->name->stat.time; + true_target->stat.time.tv_sec--; + } else { + /* Dina: + * The next statement is commented + * out as a fix for bug #1051032. + * if dependency hasn't changed + * then there's no need to invalidate + * true_target. This statemnt causes + * make to take much longer to process + * an already-built archive. Soren + * said it was a quick fix for some + * problem he doesn't remember. + true_target->stat.time = file_no_time; + */ + (void) exists(true_target); + } + } else { + (void) exists(true_target); + } + Boolean out_of_date; + if (true_target->is_member || dependency->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + dependency->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + dependency->name->stat.time); + } + if ((build_unconditional || out_of_date) && + (dependency->name != force) && + (dependency->stale == false)) { + *out_of_date_tail = ALLOC(Chain); + if (dependency->name->is_member && + (get_prop(dependency->name->prop, + member_prop) != NULL)) { + (*out_of_date_tail)->name = + get_prop(dependency->name->prop, + member_prop)-> + body.member.member; + } else { + (*out_of_date_tail)->name = + dependency->name; + } + (*out_of_date_tail)->next = NULL; + out_of_date_tail = &(*out_of_date_tail)->next; + if (debug_level > 0) { + if (dependency->name->stat.time == file_max_time) { + (void) printf(gettext("%*sBuilding %s because %s does not exist\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } else { + (void) printf(gettext("%*sBuilding %s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } + } + } + if (dependency->name == force) { + force->stat.time = + file_max_time; + force->state = build_dont_know; + } + } + } + if (dependencies_running) { + if (doing_subtree) { + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } else { + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } + } + /* + * Collect the timestamp of the youngest double colon target + * dependency. + */ + if (target->is_double_colon_parent) { + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Property tmp_line; + + if ((tmp_line = get_prop(dependency->name->prop, line_prop)) != NULL) { + if(tmp_line->body.line.dependency_time != file_max_time) { + target->stat.time = + MAX(tmp_line->body.line.dependency_time, + target->stat.time); + } + } + } + } + if ((true_target->is_member) && (dependency_changed == true)) { + true_target->stat.time = file_no_time; + } + /* + * After scanning all the dependencies, we check the rule + * if we found one. + */ + if (line->body.line.command_template != NULL) { + if (line->body.line.command_template_redefined) { + warning(gettext("Too many rules defined for target %s"), + target->string_mb); + } + *command = line; + /* Check if the target is out of date */ + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date){ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + if(gnu_style) { + + // set $< for explicit rule + if(line->body.line.dependencies != NULL) { + less = line->body.line.dependencies->name; + } + + // set $* for explicit rule + Name target_body; + Name tt = true_target; + Property member; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Wstring targ_string; + Wstring suf_string; + + if (true_target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + tt = member->body.member.member; + } + targ_string.init(tt); + target_end = targ_string.get_string() + tt->hash.length; + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (tt->hash.length < suffix_length) { + continue; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + continue; + } + target_body = GETNAME( + targ_string.get_string(), + (int)(tt->hash.length - suffix_length) + ); + line->body.line.star = target_body; + } + + // set result = build_ok so that implicit rules are not used. + if(*result == build_dont_know) { + *result = build_ok; + } + } + if (less != NULL) { + line->body.line.less = less; + } + } + + return false; +} + +/* + * dynamic_dependencies(target) + * + * Checks if any dependency contains a macro ref + * If so, it replaces the dependency with the expanded version. + * Here, "$@" gets translated to target->string. That is + * the current name on the left of the colon in the + * makefile. Thus, + * xyz: s.$@.c + * translates into + * xyz: s.xyz.c + * + * Also, "$(@F)" translates to the same thing without a preceeding + * directory path (if one exists). + * Note, to enter "$@" on a dependency line in a makefile + * "$$@" must be typed. This is because make expands + * macros in dependency lists upon reading them. + * dynamic_dependencies() also expands file wildcards. + * If there are any Shell meta characters in the name, + * search the directory, and replace the dependency + * with the set of files the pattern matches + * + * Parameters: + * target Target to sanitize dependencies for + * + * Global variables used: + * c_at The Name "@", used to set macro value + * debug_level Should we trace actions? + * dot The Name ".", used to read directory + * recursion_level Used for tracing + */ +void +dynamic_dependencies(Name target) +{ + wchar_t pattern[MAXPATHLEN]; + register wchar_t *p; + Property line; + register Dependency dependency; + register Dependency *remove; + String_rec string; + wchar_t buffer[MAXPATHLEN]; + register Boolean set_at = false; + register wchar_t *start; + Dependency new_depe; + register Boolean reuse_cell; + Dependency first_member; + Name directory; + Name lib; + Name member; + Property prop; + Name true_target = target; + wchar_t *library; + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + /* If the target is constructed from a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + /* Scan all dependencies and process the ones that contain "$" chars */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->name->dollar) { + continue; + } + target->has_depe_list_expanded = true; + + /* The make macro $@ is bound to the target name once per */ + /* invocation of dynamic_dependencies() */ + if (!set_at) { + (void) SETVAR(c_at, true_target, false); + set_at = true; + } + /* Expand this dependency string */ + INIT_STRING_FROM_STACK(string, buffer); + expand_value(dependency->name, &string, false); + /* Scan the expanded string. It could contain whitespace */ + /* which mean it expands to several dependencies */ + start = string.buffer.start; + while (iswspace(*start)) { + start++; + } + /* Remove the cell (later) if the macro was empty */ + if (start[0] == (int) nul_char) { + dependency->name = NULL; + } + +/* azv 10/26/95 to fix bug BID_1170218 */ + if ((start[0] == (int) period_char) && + (start[1] == (int) slash_char)) { + start += 2; + } +/* azv */ + + first_member = NULL; + /* We use the original dependency cell for the first */ + /* dependency from the expansion */ + reuse_cell = true; + /* We also have to deal with dependencies that expand to */ + /* lib.a(members) notation */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) parenleft_char)) { + lib = GETNAME(start, p - start); + lib->is_member = true; + first_member = dependency; + start = p + 1; + while (iswspace(*start)) { + start++; + } + break; + } + } + do { + /* First skip whitespace */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) nul_char) || + iswspace(*p) || + (*p == (int) parenright_char)) { + break; + } + } + /* Enter dependency from expansion */ + if (p != start) { + /* Create new dependency cell if */ + /* this is not the first dependency */ + /* picked from the expansion */ + if (!reuse_cell) { + new_depe = ALLOC(Dependency); + new_depe->next = dependency->next; + new_depe->automatic = false; + new_depe->stale = false; + new_depe->built = false; + dependency->next = new_depe; + dependency = new_depe; + } + reuse_cell = false; + /* Internalize the dependency name */ + // tolik. Fix for bug 4110429: inconsistent expansion for macros that + // include "//" and "/./" + //dependency->name = GETNAME(start, p - start); + dependency->name = normalize_name(start, p - start); + if ((debug_level > 0) && + (first_member == NULL)) { + (void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + dependency->name->string_mb, + true_target->string_mb); + } + for (start = p; iswspace(*start); start++); + p = start; + } + } while ((*p != (int) nul_char) && + (*p != (int) parenright_char)); + /* If the expansion was of lib.a(members) format we now */ + /* enter the proper member cells */ + if (first_member != NULL) { + /* Scan the new dependencies and transform them from */ + /* "foo" to "lib.a(foo)" */ + for (; 1; first_member = first_member->next) { + /* Build "lib.a(foo)" name */ + INIT_STRING_FROM_STACK(string, buffer); + APPEND_NAME(lib, + &string, + (int) lib->hash.length); + append_char((int) parenleft_char, &string); + APPEND_NAME(first_member->name, + &string, + FIND_LENGTH); + append_char((int) parenright_char, &string); + member = first_member->name; + /* Replace "foo" with "lib.a(foo)" */ + first_member->name = + GETNAME(string.buffer.start, FIND_LENGTH); + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (debug_level > 0) { + (void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + first_member->name-> + string_mb, + true_target->string_mb); + } + first_member->name->is_member = lib->is_member; + /* Add member property to member */ + prop = maybe_append_prop(first_member->name, + member_prop); + prop->body.member.library = lib; + prop->body.member.entry = NULL; + prop->body.member.member = member; + if (first_member == dependency) { + break; + } + } + } + } + Wstring wcb; + /* Then scan all the dependencies again. This time we want to expand */ + /* shell file wildcards */ + for (remove = &line->body.line.dependencies, dependency = *remove; + dependency != NULL; + dependency = *remove) { + if (dependency->name == NULL) { + dependency = *remove = (*remove)->next; + continue; + } + /* If dependency name string contains shell wildcards */ + /* replace the name with the expansion */ + if (dependency->name->wildcard) { + wcb.init(dependency->name); + if ((start = (wchar_t *) wcschr(wcb.get_string(), + (int) parenleft_char)) != NULL) { + /* lib(*) type pattern */ + library = buffer; + (void) wcsncpy(buffer, + wcb.get_string(), + start - wcb.get_string()); + buffer[start-wcb.get_string()] = + (int) nul_char; + (void) wcsncpy(pattern, + start + 1, +(int) (dependency->name->hash.length-(start-wcb.get_string())-2)); + pattern[dependency->name->hash.length - + (start-wcb.get_string()) - 2] = + (int) nul_char; + } else { + library = NULL; + (void) wcsncpy(pattern, + wcb.get_string(), + (int) dependency->name->hash.length); + pattern[dependency->name->hash.length] = + (int) nul_char; + } + start = (wchar_t *) wcsrchr(pattern, (int) slash_char); + if (start == NULL) { + directory = dot; + p = pattern; + } else { + directory = GETNAME(pattern, start-pattern); + p = start+1; + } + /* The expansion is handled by the read_dir() routine*/ + if (read_dir(directory, p, line, library)) { + *remove = (*remove)->next; + } else { + remove = &dependency->next; + } + } else { + remove = &dependency->next; + } + } + + /* Then unbind $@ */ + (void) SETVAR(c_at, (Name) NULL, false); +} + +/* + * DONE. + * + * run_command(line) + * + * Takes one Cmd_line and runs the commands from it. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to run + * + * Global variables used: + * commands_done Set if we do run command + * current_line Set to the line we run a command from + * current_target Set to the target we run a command for + * file_number Used to form temp file name + * keep_state Indicates that .KEEP_STATE is on + * make_state The Name ".make.state", used to check timestamp + * parallel True if currently building in parallel + * parallel_process_cnt Count of parallel processes running + * quest Indicates that make -q is on + * rewrite_statefile Set if we do run a command + * sunpro_dependencies The Name "SUNPRO_DEPENDENCIES", set value + * temp_file_directory Used to form temp fie name + * temp_file_name Set to the name of the temp file + * touch Indicates that make -t is on + */ +static Doname +run_command(register Property line, Boolean) +{ + register Doname result = build_ok; + register Boolean remember_only = false; + register Name target = line->body.line.target; + wchar_t *string; + char tmp_file_path[MAXPATHLEN]; + + if (!line->body.line.is_out_of_date && target->rechecking_target) { + target->rechecking_target = false; + return build_ok; + } + + /* + * Build the command if we know the target is out of date, + * or if we want to check cmd consistency. + */ + if (line->body.line.is_out_of_date || keep_state) { + /* Hack for handling conditional macros in DMake. */ + if (!line->body.line.dont_rebuild_command_used) { + build_command_strings(target, line); + } + } + /* Never mind */ + if (!line->body.line.is_out_of_date) { + return build_ok; + } + /* If quest, then exit(1) because the target is out of date */ + if (quest) { + if (posix) { + result = execute_parallel(line, true); + } + exit_status = 1; + exit(1); + } + /* We actually had to do something this time */ + rewrite_statefile = commands_done = true; + /* + * If this is an sccs command, we have to do some extra checking + * and possibly complain. If the file can't be gotten because it's + * checked out, we complain and behave as if the command was + * executed eventhough we ignored the command. + */ + if (!touch && + line->body.line.sccs_command && + (target->stat.time != file_doesnt_exist) && + ((target->stat.mode & 0222) != 0)) { + fatal(gettext("%s is writable so it cannot be sccs gotten"), + target->string_mb); + target->has_complained = remember_only = true; + } + /* + * If KEEP_STATE is on, we make sure we have the timestamp for + * .make.state. If .make.state changes during the command run, + * we reread .make.state after the command. We also setup the + * environment variable that asks utilities to report dependencies. + */ + if (!touch && + keep_state && + !remember_only) { + (void) exists(make_state); + if((strlen(temp_file_directory) == 1) && + (temp_file_directory[0] == '/')) { + tmp_file_path[0] = '\0'; + } else { + strcpy(tmp_file_path, temp_file_directory); + } + sprintf(mbs_buffer, + "%s/.make.dependency.%08x.%d.%d", + tmp_file_path, + hostid, + getpid(), + file_number++); + MBSTOWCS(wcs_buffer, mbs_buffer); + Boolean fnd; + temp_file_name = getname_fn(wcs_buffer, FIND_LENGTH, false, &fnd); + temp_file_name->stat.is_file = true; + int len = 2*MAXPATHLEN + strlen(target->string_mb) + 2; + wchar_t *to = string = ALLOC_WC(len); + for (wchar_t *from = wcs_buffer; *from != (int) nul_char; ) { + if (*from == (int) space_char) { + *to++ = (int) backslash_char; + } + *to++ = *from++; + } + *to++ = (int) space_char; + MBSTOWCS(to, target->string_mb); + Name sprodep_name = getname_fn(string, FIND_LENGTH, false, &fnd); + (void) SETVAR(sunpro_dependencies, + sprodep_name, + false); + retmem(string); + } else { + temp_file_name = NULL; + } + + /* + * In case we are interrupted, we need to know what was going on. + */ + current_target = target; + /* + * We also need to be able to save an empty command instead of the + * interrupted one in .make.state. + */ + current_line = line; + if (remember_only) { + /* Empty block!!! */ + } else if (touch) { + result = touch_command(line, target, result); + if (posix) { + result = execute_parallel(line, true); + } + } else { + /* + * If this is not a touch run, we need to execute the + * proper command(s) for the target. + */ + if (parallel) { + if (!parallel_ok(target, true)) { + /* + * We are building in parallel, but + * this target must be built in serial. + */ + /* + * If nothing else is building, + * do this one, else wait. + */ + if (parallel_process_cnt == 0) { + result = execute_parallel(line, true, target->localhost); + } else { + current_target = NULL; + current_line = NULL; +/* + line->body.line.command_used = NULL; + */ + line->body.line.dont_rebuild_command_used = true; + return build_serial; + } + } else { + result = execute_parallel(line, false); + switch (result) { + case build_running: + return build_running; + case build_serial: + if (parallel_process_cnt == 0) { + result = execute_parallel(line, true, target->localhost); + } else { + current_target = NULL; + current_line = NULL; + target->parallel = false; + line->body.line.command_used = + NULL; + return build_serial; + } + } + } + } else { + result = execute_parallel(line, true, target->localhost); + } + } + temp_file_name = NULL; + if (report_dependencies_level == 0){ + update_target(line, result); + } + current_target = NULL; + current_line = NULL; + return result; +} + +/* + * execute_serial(line) + * + * Runs thru the command line for the target and + * executes the rules one by one. + * + * Return value: + * The result of the command build + * + * Parameters: + * line The command to execute + * + * Static variables used: + * + * Global variables used: + * continue_after_error -k flag + * do_not_exec_rule -n flag + * report_dependencies -P flag + * silent Don't echo commands before executing + * temp_file_name Temp file for auto dependencies + * vpath_defined If true, translate path for command + */ +Doname +execute_serial(Property line) +{ + int child_pid = 0; + Boolean printed_serial; + Doname result = build_ok; + Cmd_line rule, cmd_tail, command = NULL; + char mbstring[MAXPATHLEN]; + int filed; + Name target = line->body.line.target; + + target->has_recursive_dependency = false; + // We have to create a copy of the rules chain for processing because + // the original one can be destroyed during .make.state file rereading. + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (command == NULL) { + command = cmd_tail = ALLOC(Cmd_line); + } else { + cmd_tail->next = ALLOC(Cmd_line); + cmd_tail = cmd_tail->next; + } + *cmd_tail = *rule; + } + if (command) { + cmd_tail->next = NULL; + } + for (rule = command; rule != NULL; rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + /* Echo command line, maybe. */ + if ((rule->command_line->hash.length > 0) && + !silent && + (!rule->silent || do_not_exec_rule) && + (report_dependencies_level == 0)) { + (void) printf("%s\n", rule->command_line->string_mb); + } + if (rule->command_line->hash.length > 0) { + /* Do assignment if command line prefixed with "=" */ + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + } else if (report_dependencies_level == 0) { + /* Execute command line. */ + setvar_envvar(); + result = dosys(rule->command_line, + (Boolean) rule->ignore_error, + (Boolean) rule->make_refd, + /* ds 98.04.23 bug #4085164. make should always show error messages */ + false, + /* BOOLEAN(rule->silent && + rule->ignore_error), */ + (Boolean) rule->always_exec, + target); + check_state(temp_file_name); + } + } else { + result = build_ok; + } + if (result == build_failed) { + if (silent || rule->silent) { + (void) printf(gettext("The following command caused the error:\n%s\n"), + rule->command_line->string_mb); + } + if (!rule->ignore_error && !ignore_errors) { + if (!continue_after_error) { + fatal(gettext("Command failed for target `%s'"), + target->string_mb); + } + /* + * Make sure a failing command is not + * saved in .make.state. + */ + line->body.line.command_used = NULL; + break; + } else { + result = build_ok; + } + } + } + for (rule = command; rule != NULL; rule = cmd_tail) { + cmd_tail = rule->next; + free(rule); + } + command = NULL; + if (temp_file_name != NULL) { + free_name(temp_file_name); + } + temp_file_name = NULL; + + Property spro = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro != NULL) { + Name val = spro->body.macro.value; + if(val != NULL) { + free_name(val); + spro->body.macro.value = NULL; + } + } + spro = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro) { + char *val = spro->body.env_mem.value; + if(val != NULL) { + /* + * Do not return memory allocated for SUNPRO_DEPENDENCIES + * It will be returned in setvar_daemon() in macro.cc + */ + // retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + + return result; +} + + + +/* + * vpath_translation(cmd) + * + * Translates one command line by + * checking each word. If the word has an alias it is translated. + * + * Return value: + * The translated command + * + * Parameters: + * cmd Command to translate + * + * Global variables used: + */ +Name +vpath_translation(register Name cmd) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec new_cmd; + wchar_t *p; + wchar_t *start; + + if (!vpath_defined || (cmd == NULL) || (cmd->hash.length == 0)) { + return cmd; + } + INIT_STRING_FROM_STACK(new_cmd, buffer); + + Wstring wcb(cmd); + p = wcb.get_string(); + + while (*p != (int) nul_char) { + while (iswspace(*p) && (*p != (int) nul_char)) { + append_char(*p++, &new_cmd); + } + start = p; + while (!iswspace(*p) && (*p != (int) nul_char)) { + p++; + } + cmd = GETNAME(start, p - start); + if (cmd->has_vpath_alias_prop) { + cmd = get_prop(cmd->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + APPEND_NAME(cmd, + &new_cmd, + (int) cmd->hash.length); + } else { + append_string(start, &new_cmd, p - start); + } + } + cmd = GETNAME(new_cmd.buffer.start, FIND_LENGTH); + if (new_cmd.free_after_use) { + retmem(new_cmd.buffer.start); + } + return cmd; +} + +/* + * check_state(temp_file_name) + * + * Reads and checks the state changed by the previously executed command. + * + * Parameters: + * temp_file_name The auto dependency temp file + * + * Global variables used: + */ +void +check_state(Name temp_file_name) +{ + if (!keep_state) { + return; + } + + /* + * Then read the temp file that now might + * contain dependency reports from utilities + */ + read_dependency_file(temp_file_name); + + /* + * And reread .make.state if it + * changed (the command ran recursive makes) + */ + check_read_state_file(); + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } +} + +/* + * read_dependency_file(filename) + * + * Read the temp file used for reporting dependencies to make + * + * Parameters: + * filename The name of the file with the state info + * + * Global variables used: + * makefile_type The type of makefile being read + * read_trace_level Debug flag + * temp_file_number The always increasing number for unique files + * trace_reader Debug flag + */ +static void +read_dependency_file(register Name filename) +{ + register Makefile_type save_makefile_type; + + if (filename == NULL) { + return; + } + filename->stat.time = file_no_time; + if (exists(filename) > file_doesnt_exist) { + save_makefile_type = makefile_type; + makefile_type = reading_cpp_file; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(filename, + false, + false, + false, + false, + false, + false); + trace_reader = false; + makefile_type = save_makefile_type; + } +} + +/* + * check_read_state_file() + * + * Check if .make.state has changed + * If it has we reread it + * + * Parameters: + * + * Global variables used: + * make_state Make state file name + * makefile_type Type of makefile being read + * read_trace_level Debug flag + * trace_reader Debug flag + */ +static void +check_read_state_file(void) +{ + timestruc_t previous = make_state->stat.time; + register Makefile_type save_makefile_type; + register Property makefile; + + make_state->stat.time = file_no_time; + if ((exists(make_state) == file_doesnt_exist) || + (make_state->stat.time == previous)) { + return; + } + save_makefile_type = makefile_type; + makefile_type = rereading_statefile; + /* Make sure we clear the old cached contents of .make.state */ + makefile = maybe_append_prop(make_state, makefile_prop); + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = save_makefile_type; +} + +/* + * do_assign(line, target) + * + * Handles runtime assignments for command lines prefixed with "=". + * + * Parameters: + * line The command that contains an assignment + * target The Name of the target, used for error reports + * + * Global variables used: + * assign_done Set to indicate doname needs to reprocess + */ +static void +do_assign(register Name line, register Name target) +{ + Wstring wcb(line); + register wchar_t *string = wcb.get_string(); + register wchar_t *equal; + register Name name; + register Boolean append = false; + + /* + * If any runtime assignments are done, doname() must reprocess all + * targets in the future since the macro values used to build the + * command lines for the targets might have changed. + */ + assign_done = true; + /* Skip white space. */ + while (iswspace(*string)) { + string++; + } + equal = string; + /* Find "+=" or "=". */ + while (!iswspace(*equal) && + (*equal != (int) plus_char) && + (*equal != (int) equal_char)) { + equal++; + } + /* Internalize macro name. */ + name = GETNAME(string, equal - string); + /* Skip over "+=" "=". */ + while (!((*equal == (int) nul_char) || + (*equal == (int) equal_char) || + (*equal == (int) plus_char))) { + equal++; + } + switch (*equal) { + case nul_char: + fatal(gettext("= expected in rule `%s' for target `%s'"), + line->string_mb, + target->string_mb); + case plus_char: + append = true; + equal++; + break; + } + equal++; + /* Skip over whitespace in front of value. */ + while (iswspace(*equal)) { + equal++; + } + /* Enter new macro value. */ + enter_equal(name, + GETNAME(equal, wcb.get_string() + line->hash.length - equal), + append); +} + +/* + * build_command_strings(target, line) + * + * Builds the command string to used when + * building a target. If the string is different from the previous one + * is_out_of_date is set. + * + * Parameters: + * target Target to build commands for + * line Where to stuff result + * + * Global variables used: + * c_at The Name "@", used to set macro value + * command_changed Set if command is different from old + * debug_level Should we trace activities? + * do_not_exec_rule Always echo when running -n + * empty_name The Name "", used for empty rule + * funny Semantics of characters + * ignore_errors Used to init field for line + * is_conditional Set to false befor evaling macro, checked + * after expanding macros + * keep_state Indicates that .KEEP_STATE is on + * make_word_mentioned Set by macro eval, inits field for cmd + * query The Name "?", used to set macro value + * query_mentioned Set by macro eval, inits field for cmd + * recursion_level Used for tracing + * silent Used to init field for line + */ +static void +build_command_strings(Name target, register Property line) +{ + String_rec command_line; + register Cmd_line command_template = line->body.line.command_template; + register Cmd_line *insert = &line->body.line.command_used; + register Cmd_line used = *insert; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *start; + Name new_command_line; + register Boolean new_command_longer = false; + register Boolean ignore_all_command_dependency = true; + Property member; + static Name less_name; + static Name percent_name; + static Name star; + Name tmp_name; + + if (less_name == NULL) { + MBSTOWCS(wcs_buffer, "<"); + less_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "%"); + percent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "*"); + star = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* We have to check if a target depends on conditional macros */ + /* Targets that do must be reprocessed by doname() each time around */ + /* since the macro values used when building the target might have */ + /* changed */ + conditional_macro_used = false; + /* If we are building a lib.a(member) target $@ should be bound */ + /* to lib.a */ + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != NULL)) { + target = member->body.member.library; + } + /* If we are building a "::" help target $@ should be bound to */ + /* the real target name */ + /* A lib.a(member) target is never :: */ + if (target->has_target_prop) { + target = get_prop(target->prop, target_prop)-> + body.target.target; + } + /* Bind the magic macros that make supplies */ + tmp_name = target; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(c_at, tmp_name, false); + + tmp_name = line->body.line.star; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(star, tmp_name, false); + + tmp_name = line->body.line.less; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(less_name, tmp_name, false); + + tmp_name = line->body.line.percent; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(percent_name, tmp_name, false); + + /* $? is seldom used and it is expensive to build */ + /* so we store the list form and build the string on demand */ + Chain query_list = NULL; + Chain *query_list_tail = &query_list; + + for (Chain ch = line->body.line.query; ch != NULL; ch = ch->next) { + *query_list_tail = ALLOC(Chain); + (*query_list_tail)->name = ch->name; + if ((*query_list_tail)->name->has_vpath_alias_prop) { + (*query_list_tail)->name = + get_prop((*query_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + (*query_list_tail)->next = NULL; + query_list_tail = &(*query_list_tail)->next; + } + (void) setvar_daemon(query, + (Name) query_list, + false, + chain_daemon, + false, + debug_level); + + /* build $^ */ + Chain hat_list = NULL; + Chain *hat_list_tail = &hat_list; + + for (Dependency dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* skip automatic dependencies */ + if (!dependency->automatic) { + if ((dependency->name != force) && + (dependency->stale == false)) { + *hat_list_tail = ALLOC(Chain); + + if (dependency->name->is_member && + (get_prop(dependency->name->prop, member_prop) != NULL)) { + (*hat_list_tail)->name = + get_prop(dependency->name->prop, + member_prop)->body.member.member; + } else { + (*hat_list_tail)->name = dependency->name; + } + + if((*hat_list_tail)->name != NULL) { + if ((*hat_list_tail)->name->has_vpath_alias_prop) { + (*hat_list_tail)->name = + get_prop((*hat_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + } + + (*hat_list_tail)->next = NULL; + hat_list_tail = &(*hat_list_tail)->next; + } + } + } + (void) setvar_daemon(hat, + (Name) hat_list, + false, + chain_daemon, + false, + debug_level); + +/* We have two command sequences we need to handle */ +/* The old one that we probably read from .make.state */ +/* and the new one we are building that will replace the old one */ +/* Even when KEEP_STATE is not on we build a new command sequence and store */ +/* it in the line prop. This command sequence is then executed by */ +/* run_command(). If KEEP_STATE is on it is also later written to */ +/* .make.state. The routine replaces the old command line by line with the */ +/* new one trying to reuse Cmd_lines */ + + /* If there is no old command_used we have to start creating */ + /* Cmd_lines to keep the new cmd in */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = NULL; + insert = &used->next; + } + /* Run thru the template for the new command and build the expanded */ + /* new command lines */ + for (; + command_template != NULL; + command_template = command_template->next, insert = &used->next, used = *insert) { + /* If there is no old command_used Cmd_line we need to */ + /* create one and say that cmd consistency failed */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = empty_name; + } + /* Prepare the Cmd_line for the processing */ + /* The command line prefixes "@-=?" are stripped and that */ + /* information is saved in the Cmd_line */ + used->assign = false; + used->ignore_error = ignore_errors; + used->silent = silent; + used->always_exec = false; + /* Expand the macros in the command line */ + INIT_STRING_FROM_STACK(command_line, buffer); + make_word_mentioned = + query_mentioned = + false; + expand_value(command_template->command_line, &command_line, true); + /* If the macro $(MAKE) is mentioned in the command */ + /* "make -n" runs actually execute the command */ + used->make_refd = make_word_mentioned; + used->ignore_command_dependency = query_mentioned; + /* Strip the prefixes */ + start = command_line.buffer.start; + for (; + iswspace(*start) || + (get_char_semantics_value(*start) & (int) command_prefix_sem); + start++) { + switch (*start) { + case question_char: + used->ignore_command_dependency = true; + break; + case exclam_char: + used->ignore_command_dependency = false; + break; + case equal_char: + used->assign = true; + break; + case hyphen_char: + used->ignore_error = true; + break; + case at_char: + if (!do_not_exec_rule) { + used->silent = true; + } + break; + case plus_char: + if(posix) { + used->always_exec = true; + } + break; + } + } + /* If all command lines of the template are prefixed with "?"*/ + /* the VIRTUAL_ROOT is not used for cmd consistency checks */ + if (!used->ignore_command_dependency) { + ignore_all_command_dependency = false; + } + /* Internalize the expanded and stripped command line */ + new_command_line = GETNAME(start, FIND_LENGTH); + if ((used->command_line == NULL) && + (line->body.line.sccs_command)) { + used->command_line = new_command_line; + new_command_longer = false; + } + /* Compare it with the old one for command consistency */ + if (used->command_line != new_command_line) { + Name vpath_translated = vpath_translation(new_command_line); + if (keep_state && + !used->ignore_command_dependency && (vpath_translated != used->command_line)) { + if (debug_level > 0) { + if (used->command_line != NULL + && *used->command_line->string_mb != + '\0') { + (void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + "", + used-> + command_line-> + string_mb); + } else { + (void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + ""); + } + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + used->command_line = new_command_line; + } + if (command_line.free_after_use) { + retmem(command_line.buffer.start); + } + } + /* Check if the old command is longer than the new for */ + /* command consistency */ + if (used != NULL) { + *insert = NULL; + if (keep_state && + !ignore_all_command_dependency) { + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s because new command shorter than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + } + /* Check if the new command is longer than the old command for */ + /* command consistency */ + if (new_command_longer && + !ignore_all_command_dependency && + keep_state) { + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s because new command longer than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + /* Unbind the magic macros */ + (void) SETVAR(c_at, (Name) NULL, false); + (void) SETVAR(star, (Name) NULL, false); + (void) SETVAR(less_name, (Name) NULL, false); + (void) SETVAR(percent_name, (Name) NULL, false); + (void) SETVAR(query, (Name) NULL, false); + if (query_list != NULL) { + delete_query_chain(query_list); + } + (void) SETVAR(hat, (Name) NULL, false); + if (hat_list != NULL) { + delete_query_chain(hat_list); + } + + if (conditional_macro_used) { + target->conditional_macro_list = cond_macro_list; + cond_macro_list = NULL; + target->depends_on_conditional = true; + } +} + +/* + * touch_command(line, target, result) + * + * If this is an "make -t" run we do this. + * We touch all targets in the target group ("foo + fie:") if any. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to update + * target The target we are touching + * result Initial value for the result we return + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * silent Do not echo commands + */ +static Doname +touch_command(register Property line, register Name target, Doname result) +{ + Name name; + register Chain target_group; + String_rec touch_string; + wchar_t buffer[MAXPATHLEN]; + Name touch_cmd; + Cmd_line rule; + + for (name = target, target_group = NULL; name != NULL;) { + if (!name->is_member) { + /* + * Build a touch command that can be passed + * to dosys(). If KEEP_STATE is on, "make -t" + * will save the proper command, not the + * "touch" in .make.state. + */ + INIT_STRING_FROM_STACK(touch_string, buffer); + MBSTOWCS(wcs_buffer, "touch "); + append_string(wcs_buffer, &touch_string, FIND_LENGTH); + touch_cmd = name; + if (name->has_vpath_alias_prop) { + touch_cmd = get_prop(name->prop, + vpath_alias_prop)-> + body.vpath_alias.alias; + } + APPEND_NAME(touch_cmd, + &touch_string, + FIND_LENGTH); + touch_cmd = GETNAME(touch_string.buffer.start, + FIND_LENGTH); + if (touch_string.free_after_use) { + retmem(touch_string.buffer.start); + } + if (!silent || + do_not_exec_rule && + (target_group == NULL)) { + (void) printf("%s\n", touch_cmd->string_mb); + } + /* Run the touch command, or simulate it */ + if (!do_not_exec_rule) { + result = dosys(touch_cmd, + false, + false, + false, + false, + name); + } else { + result = build_ok; + } + } else { + result = build_ok; + } + if (target_group == NULL) { + target_group = line->body.line.target_group; + } else { + target_group = target_group->next; + } + if (target_group != NULL) { + name = target_group->name; + } else { + name = NULL; + } + } + return result; +} + +/* + * update_target(line, result) + * + * updates the status of a target after executing its commands. + * + * Parameters: + * line The command line block to update + * result Indicates that build is OK so can update + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * touch Fake the new timestamp if we are just touching + */ +void +update_target(Property line, Doname result) +{ + Name target; + Chain target_group; + Property line2; + timestruc_t old_stat_time; + Property member; + + /* + * [tolik] Additional fix for bug 1063790. It was fixed + * for serial make long ago, but DMake dumps core when + * target is a symlink and sccs file is newer then target. + * In this case, finish_children() calls update_target() + * with line==NULL. + */ + if(line == NULL) { + /* XXX. Should we do anything here? */ + return; + } + + target = line->body.line.target; + + if ((result == build_ok) && (line->body.line.command_used != NULL)) { + if (do_not_exec_rule || + touch || + (target->is_member && + (line->body.line.command_template != NULL) && + (line->body.line.command_template->command_line->string_mb[0] == 0) && + (line->body.line.command_template->next == NULL))) { + /* If we are simulating execution we need to fake a */ + /* new timestamp for the target we didnt build */ + target->stat.time = file_max_time; + } else { + /* + * If we really built the target we read the new + * timestamp. + * Fix for bug #1110906: if .c file is newer than + * the corresponding .o file which is in an archive + * file, make will compile the .c file but it won't + * update the object in the .a file. + */ + old_stat_time = target->stat.time; + target->stat.time = file_no_time; + (void) exists(target); + if ((target->is_member) && + (target->stat.time == old_stat_time)) { + member = get_prop(target->prop, member_prop); + if (member != NULL) { + target->stat.time = member->body.member.library->stat.time; + target->stat.time.tv_sec++; + } + } + } + /* If the target is part of a group we need to propagate the */ + /* result of the run to all members */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + target_group->name->stat.time = target->stat.time; + line2 = maybe_append_prop(target_group->name, + line_prop); + line2->body.line.command_used = + line->body.line.command_used; + line2->body.line.target = target_group->name; + } + } + target->has_built = true; +} + +/* + * sccs_get(target, command) + * + * Figures out if it possible to sccs get a file + * and builds the command to do it if it is. + * + * Return value: + * Indicates if sccs get failed or not + * + * Parameters: + * target Target to get + * command Where to deposit command to use + * + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + * sccs_get_rule The rule to used for sccs getting + */ +static Doname +sccs_get(register Name target, register Property *command) +{ + register int result; + char link[MAXPATHLEN]; + String_rec string; + wchar_t name[MAXPATHLEN]; + register wchar_t *p; + timestruc_t sccs_time; + register Property line; + int sym_link_depth = 0; + + /* For sccs, we need to chase symlinks. */ + while (target->stat.is_sym_link) { + if (sym_link_depth++ > 90) { + fatal(gettext("Can't read symbolic link `%s': Number of symbolic links encountered during path name traversal exceeds 90."), + target->string_mb); + } + /* Read the value of the link. */ + result = readlink_vroot(target->string_mb, + link, + sizeof(link), + NULL, + VROOT_DEFAULT); + if (result == -1) { + fatal(gettext("Can't read symbolic link `%s': %s"), + target->string_mb, errmsg(errno)); + } + link[result] = 0; + /* Use the value to build the proper filename. */ + INIT_STRING_FROM_STACK(string, name); + + Wstring wcb(target); + if ((link[0] != slash_char) && + ((p = (wchar_t *) wcsrchr(wcb.get_string(), slash_char)) != NULL)) { + append_string(wcb.get_string(), &string, p - wcb.get_string() + 1); + } + append_string(link, &string, result); + /* Replace the old name with the translated name. */ + target = normalize_name(string.buffer.start, string.text.p - string.buffer.start); + (void) exists(target); + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + + /* + * read_dir() also reads the ?/SCCS dir and saves information + * about which files have SCSC/s. files. + */ + if (target->stat.has_sccs == DONT_KNOW_SCCS) { + read_directory_of_file(target); + } + switch (target->stat.has_sccs) { + case DONT_KNOW_SCCS: + /* We dont know by now there is no SCCS/s.* */ + target->stat.has_sccs = NO_SCCS; + case NO_SCCS: + /* + * If there is no SCCS/s.* but the plain file exists, + * we say things are OK. + */ + if (target->stat.time > file_doesnt_exist) { + return build_ok; + } + /* If we cant find the plain file, we give up. */ + return build_dont_know; + case HAS_SCCS: + /* + * Pay dirt. We now need to figure out if the plain file + * is out of date relative to the SCCS/s.* file. + */ + sccs_time = exists(get_prop(target->prop, + sccs_prop)->body.sccs.file); + break; + } + + if ((!target->has_complained && + (sccs_time != file_doesnt_exist) && + (sccs_get_rule != NULL))) { + /* only checking */ + if (command == NULL) { + return build_ok; + } + /* + * We provide a command line for the target. The line is a + * "sccs get" command from default.mk. + */ + line = maybe_append_prop(target, line_prop); + *command = line; + if (sccs_time > target->stat.time) { + /* + * And only if the plain file is out of date do we + * request execution of the command. + */ + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(gettext("%*sSccs getting %s because s. file is younger than source file\n"), + recursion_level, + "", + target->string_mb); + } + } + line->body.line.sccs_command = true; + line->body.line.command_template = sccs_get_rule; + if(!svr4 && (!allrules_read || posix)) { + if((target->prop) && + (target->prop->body.sccs.file) && + (target->prop->body.sccs.file->string_mb)) { + if((strlen(target->prop->body.sccs.file->string_mb) == + strlen(target->string_mb) + 2) && + (target->prop->body.sccs.file->string_mb[0] == 's') && + (target->prop->body.sccs.file->string_mb[1] == '.')) { + + line->body.line.command_template = get_posix_rule; + } + } + } + line->body.line.target = target; + /* + * Also make sure the rule is build with $* and $< + * bound properly. + */ + line->body.line.star = NULL; + line->body.line.less = NULL; + line->body.line.percent = NULL; + return build_ok; + } + return build_dont_know; +} + +/* + * read_directory_of_file(file) + * + * Reads the directory the specified file lives in. + * + * Parameters: + * file The file we need to read dir for + * + * Global variables used: + * dot The Name ".", used as the default dir + */ +void +read_directory_of_file(register Name file) +{ + + Wstring file_string(file); + wchar_t * wcb = file_string.get_string(); + wchar_t usr_include_buf[MAXPATHLEN]; + wchar_t usr_include_sys_buf[MAXPATHLEN]; + + register Name directory = dot; + register wchar_t *p = (wchar_t *) wcsrchr(wcb, + (int) slash_char); + register int length = p - wcb; + static Name usr_include; + static Name usr_include_sys; + + if (usr_include == NULL) { + MBSTOWCS(usr_include_buf, "/usr/include"); + usr_include = GETNAME(usr_include_buf, FIND_LENGTH); + MBSTOWCS(usr_include_sys_buf, "/usr/include/sys"); + usr_include_sys = GETNAME(usr_include_sys_buf, FIND_LENGTH); + } + + /* + * If the filename contains a "/" we have to extract the path + * Else the path defaults to ".". + */ + if (p != NULL) { + /* + * Check some popular directories first to possibly + * save time. Compare string length first to gain speed. + */ + if ((usr_include->hash.length == length) && + IS_WEQUALN(usr_include_buf, + wcb, + length)) { + directory = usr_include; + } else if ((usr_include_sys->hash.length == length) && + IS_WEQUALN(usr_include_sys_buf, + wcb, + length)) { + directory = usr_include_sys; + } else { + directory = GETNAME(wcb, length); + } + } + (void) read_dir(directory, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); +} + +/* + * add_pattern_conditionals(target) + * + * Scan the list of conditionals defined for pattern targets and add any + * that match this target to its list of conditionals. + * + * Parameters: + * target The target we should add conditionals for + * + * Global variables used: + * conditionals The list of pattern conditionals + */ +static void +add_pattern_conditionals(register Name target) +{ + register Property conditional; + Property new_prop; + Property *previous; + Name_rec dummy; + wchar_t *pattern; + wchar_t *percent; + int length; + + Wstring wcb(target); + Wstring wcb1; + + for (conditional = get_prop(conditionals->prop, conditional_prop); + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop)) { + wcb1.init(conditional->body.conditional.target); + pattern = wcb1.get_string(); + if (pattern[1] != 0) { + percent = (wchar_t *) wcschr(pattern, (int) percent_char); + /* Check for possible buffer under-read */ + if ((length = wcb.length()-wcslen(percent+1)) <= 0) { + continue; + } + if (!wcb.equaln(pattern, percent-pattern) || + !IS_WEQUAL(wcb.get_string(length), percent+1)) { + continue; + } + } + for (previous = &target->prop; + *previous != NULL; + previous = &(*previous)->next) { + if (((*previous)->type == conditional_prop) && + ((*previous)->body.conditional.sequence > + conditional->body.conditional.sequence)) { + break; + } + } + if (*previous == NULL) { + new_prop = append_prop(target, conditional_prop); + } else { + dummy.prop = NULL; + new_prop = append_prop(&dummy, conditional_prop); + new_prop->next = *previous; + *previous = new_prop; + } + target->conditional_cnt++; + new_prop->body.conditional = conditional->body.conditional; + } +} + +/* + * set_locals(target, old_locals) + * + * Sets any conditional macros for the target. + * Each target carries a possibly empty set of conditional properties. + * + * Parameters: + * target The target to set conditional macros for + * old_locals Space to store old values in + * + * Global variables used: + * debug_level Should we trace activity? + * is_conditional We need to preserve this value + * recursion_level Used for tracing + */ +void +set_locals(register Name target, register Property old_locals) +{ + register Property conditional; + register int i; + register Boolean saved_conditional_macro_used; + Chain cond_name; + Chain cond_chain; + + if (target->dont_activate_cond_values) { + return; + } + + saved_conditional_macro_used = conditional_macro_used; + + /* Scan the list of conditional properties and apply each one */ + for (conditional = get_prop(target->prop, conditional_prop), i = 0; + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop), + i++) { + /* Save the old value */ + old_locals[i].body.macro = + maybe_append_prop(conditional->body.conditional.name, + macro_prop)->body.macro; + if (debug_level > 1) { + (void) printf(gettext("%*sActivating conditional value: "), + recursion_level, + ""); + } + /* Set the conditional value. Macros are expanded when the */ + /* macro is refd as usual */ + if ((conditional->body.conditional.name != virtual_root) || + (conditional->body.conditional.value != virtual_root)) { + (void) SETVAR(conditional->body.conditional.name, + conditional->body.conditional.value, + (Boolean) conditional->body.conditional.append); + } + cond_name = ALLOC(Chain); + cond_name->name = conditional->body.conditional.name; + } + /* Put this target on the front of the chain of conditional targets */ + cond_chain = ALLOC(Chain); + cond_chain->name = target; + cond_chain->next = conditional_targets; + conditional_targets = cond_chain; + conditional_macro_used = saved_conditional_macro_used; +} + +/* + * reset_locals(target, old_locals, conditional, index) + * + * Removes any conditional macros for the target. + * + * Parameters: + * target The target we are retoring values for + * old_locals The values to restore + * conditional The first conditional block for the target + * index into the old_locals vector + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + */ +void +reset_locals(register Name target, register Property old_locals, register Property conditional, register int index) +{ + register Property this_conditional; + Chain cond_chain; + + if (target->dont_activate_cond_values) { + return; + } + + /* Scan the list of conditional properties and restore the old value */ + /* to each one Reverse the order relative to when we assigned macros */ + this_conditional = get_prop(conditional->next, conditional_prop); + if (this_conditional != NULL) { + reset_locals(target, old_locals, this_conditional, index+1); + } else { + /* Remove conditional target from chain */ + if (conditional_targets == NULL || + conditional_targets->name != target) { + warning(gettext("Internal error: reset target not at head of condtional_targets chain")); + } else { + cond_chain = conditional_targets->next; + retmem_mb((caddr_t) conditional_targets); + conditional_targets = cond_chain; + } + } + get_prop(conditional->body.conditional.name->prop, + macro_prop)->body.macro = old_locals[index].body.macro; + if (conditional->body.conditional.name == virtual_root) { + (void) SETVAR(virtual_root, getvar(virtual_root), false); + } + if (debug_level > 1) { + if (old_locals[index].body.macro.value != NULL) { + (void) printf(gettext("%*sdeactivating conditional value: %s= %s\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb, + old_locals[index].body.macro.value-> + string_mb); + } else { + (void) printf(gettext("%*sdeactivating conditional value: %s =\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb); + } + } +} + +/* + * check_auto_dependencies(target, auto_count, automatics) + * + * Returns true if the target now has a dependency + * it didn't previously have (saved on automatics). + * + * Return value: + * true if new dependency found + * + * Parameters: + * target Target we check + * auto_count Number of old automatic vars + * automatics Saved old automatics + * + * Global variables used: + * keep_state Indicates that .KEEP_STATE is on + */ +Boolean +check_auto_dependencies(Name target, int auto_count, Name *automatics) +{ + Name *p; + int n; + Property line; + Dependency dependency; + + if (keep_state) { + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return false; + } + /* Go thru new list of automatic depes */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* And make sure that each one existed before we */ + /* built the target */ + if (dependency->automatic && !dependency->stale) { + for (n = auto_count, p = automatics; + n > 0; + n--) { + if (*p++ == dependency->name) { + /* If we can find it on the */ + /* saved list of autos we */ + /* are OK */ + goto not_new; + } + } + /* But if we scan over the old list */ + /* of auto. without finding it it is */ + /* new and we must check it */ + return true; + } + not_new:; + } + return false; + } else { + return false; + } +} + + +// Recursively delete each of the Chain struct on the chain. + +static void +delete_query_chain(Chain ch) +{ + if (ch == NULL) { + return; + } else { + delete_query_chain(ch->next); + retmem_mb((char *) ch); + } +} + +Doname +target_can_be_built(register Name target) { + Doname result = build_dont_know; + Name true_target = target; + Property line; + + if (target == wait_name) { + return(build_ok); + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + + (void) exists(true_target); + + if (true_target->state == build_running) { + return(build_running); + } + if (true_target->stat.time != file_doesnt_exist) { + result = build_ok; + } + + /* get line property for the target */ + line = get_prop(true_target->prop, line_prop); + + /* first check for explicit rule */ + if (line != NULL && line->body.line.command_template != NULL) { + result = build_ok; + } + /* try to find pattern rule */ + if (result == build_dont_know) { + result = find_percent_rule(target, NULL, false); + } + + /* try to find double suffix rule */ + if (result == build_dont_know) { + if (target->is_member) { + Property member = get_prop(target->prop, member_prop); + if (member != NULL && member->body.member.member != NULL) { + result = find_ar_suffix_rule(target, member->body.member.member, NULL, false); + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } + + /* try to find suffix rule */ + if ((result == build_dont_know) && second_pass) { + result = find_suffix_rule(target, target, empty_name, NULL, false); + } + + /* check for sccs */ + if (result == build_dont_know) { + result = sccs_get(target, NULL); + } + + /* try to find dyn target */ + if (result == build_dont_know) { + Name dtarg = find_dyntarget(target); + if (dtarg != NULL) { + result = target_can_be_built(dtarg); + } + } + + /* check whether target was mentioned in makefile */ + if (result == build_dont_know) { + if (target->colons != no_colon) { + result = build_ok; + } + } + + /* result */ + return result; +} diff --git a/bin/dosys.cc b/bin/dosys.cc new file mode 100644 index 0000000..ae7f4ef --- /dev/null +++ b/bin/dosys.cc @@ -0,0 +1,163 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * dosys.cc + * + * Execute one commandline + */ + +/* + * Included files + */ +#include /* open() */ +#include +#include /* doshell(), doexec() */ +#include /* getmem() */ +#include /* open() */ +#include /* getpid() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static int filter_file; +static char *filter_file_name; + +/* + * File table of contents + */ +static void redirect_stderr(void); + +/* + * dosys(command, ignore_error, call_make, silent_error, target) + * + * Check if command string contains meta chars and dispatch to + * the proper routine for executing one command line. + * + * Return value: + * Indicates if the command execution failed + * + * Parameters: + * command The command to run + * ignore_error Should make abort when an error is seen? + * call_make Did command reference $(MAKE) ? + * silent_error Should error messages be suppressed for pmake? + * target Target we are building + * + * Global variables used: + * do_not_exec_rule Is -n on? + * working_on_targets We started processing real targets + */ +Doname +dosys(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target) +{ + timestruc_t before; + register int length = command->hash.length; + Wstring wcb(command); + register wchar_t *p = wcb.get_string(); + register wchar_t *q; + Doname result; + + /* Strip spaces from head of command string */ + while (iswspace(*p)) { + p++, length--; + } + if (*p == (int) nul_char) { + return build_failed; + } + /* If we are faking it we just return */ + if (do_not_exec_rule && + working_on_targets && + !call_make && + !always_exec) { + return build_ok; + } + /* no_action_was_taken is used to print special message */ + no_action_was_taken = false; + + /* Copy string to make it OK to write it. */ + q = ALLOC_WC(length + 1); + (void) wcscpy(q, p); + /* Write the state file iff this command uses make. */ + if (call_make && command_changed) { + write_state_file(0, false); + } + make_state->stat.time = file_no_time; + (void)exists(make_state); + before = make_state->stat.time; + /* + * Run command directly if it contains no shell meta chars, + * else run it using the shell. + */ + if (await(ignore_error, + silent_error, + target, + wcb.get_string(), + command->meta ? + doshell(q, ignore_error, + stdout_file, stderr_file, 0) : + doexec(q, ignore_error, + stdout_file, stderr_file, + vroot_path, 0), + NULL, + -1 + )) { + result = build_ok; + } else { + result = build_failed; + } + retmem(q); + + if ((report_dependencies_level == 0) && + call_make) { + make_state->stat.time = file_no_time; + (void)exists(make_state); + if (before == make_state->stat.time) { + return result; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } + return result; +} diff --git a/bin/files.cc b/bin/files.cc new file mode 100644 index 0000000..1aefc4e --- /dev/null +++ b/bin/files.cc @@ -0,0 +1,711 @@ +/* + * 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. + */ + +/* + * files.c + * + * Various file related routines: + * Figure out if file exists + * Wildcard resolution for directory reader + * Directory reader + */ + + +/* + * Included files + */ +#include /* opendir() */ +#include /* errno */ +#include +#include /* getvar() */ +#include /* get_prop(), append_prop() */ +#include /* lstat() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& exists(register Name target); +extern void set_target_stat(register Name target, struct stat buf); +static timestruc_t& vpath_exists(register Name target); +static Name enter_file_name(wchar_t *name_string, wchar_t *library); +static Boolean star_match(register char *string, register char *pattern); +static Boolean amatch(register wchar_t *string, register wchar_t *pattern); + +/* + * exists(target) + * + * Figure out the timestamp for one target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * debug_level Should we trace the stat call? + * recursion_level Used for tracing + * vpath_defined Was the variable VPATH defined in environment? + */ +timestruc_t& +exists(register Name target) +{ + struct stat buf; + register int result; + + /* We cache stat information. */ + if (target->stat.time != file_no_time) { + return target->stat.time; + } + + /* + * If the target is a member, we have to extract the time + * from the archive. + */ + if (target->is_member && + (get_prop(target->prop, member_prop) != NULL)) { + return read_archive(target); + } + + if (debug_level > 1) { + (void) printf("%*sstat(%s)\n", + recursion_level, + "", + target->string_mb); + } + + result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) { + /* + * If the file is a symbolic link, we remember that + * and then we get the status for the refd file. + */ + target->stat.is_sym_link = true; + result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + } else { + target->stat.is_sym_link = false; + } + + if (result < 0) { + target->stat.time = file_doesnt_exist; + target->stat.stat_errno = errno; + if ((errno == ENOENT) && + vpath_defined && +/* azv, fixing bug 1262942, VPATH works with a leaf name + * but not a directory name. + */ + (target->string_mb[0] != (int) slash_char) ) { +/* BID_1214655 */ +/* azv */ + vpath_exists(target); + // return vpath_exists(target); + } + } else { + /* Save all the information we need about the file */ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ + target->stat.time = MAX(buf.st_mtim, file_min_time); + } + } + if ((target->colon_splits > 0) && + (get_prop(target->prop, time_prop) == NULL)) { + append_prop(target, time_prop)->body.time.time = + target->stat.time; + } + return target->stat.time; +} + +/* + * set_target_stat( target, buf) + * + * Called by exists() to set some stat fields in the Name structure + * to those read by the stat_vroot() call (from disk). + * + * Parameters: + * target The target whose stat field is set + * buf stat values (on disk) of the file + * represented by target. + */ +void +set_target_stat(register Name target, struct stat buf) +{ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ + target->stat.time = MAX(buf.st_mtim, file_min_time); + } +} + + +/* + * vpath_exists(target) + * + * Called if exists() discovers that there is a VPATH defined. + * This function stats the VPATH translation of the target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * vpath_name The Name "VPATH", used to get macro value + */ +static timestruc_t& +vpath_exists(register Name target) +{ + wchar_t *vpath; + wchar_t file_name[MAXPATHLEN]; + wchar_t *name_p; + Name alias; + + /* + * To avoid recursive search through VPATH when exists(alias) is called + */ + vpath_defined = false; + + Wstring wcb(getvar(vpath_name)); + Wstring wcb1(target); + + vpath = wcb.get_string(); + + while (*vpath != (int) nul_char) { + name_p = file_name; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + *name_p++ = *vpath++; + } + *name_p++ = (int) slash_char; + (void) wcscpy(name_p, wcb1.get_string()); + alias = GETNAME(file_name, FIND_LENGTH); + if (exists(alias) != file_doesnt_exist) { + target->stat.is_file = true; + target->stat.mode = alias->stat.mode; + target->stat.size = alias->stat.size; + target->stat.is_dir = alias->stat.is_dir; + target->stat.time = alias->stat.time; + maybe_append_prop(target, vpath_alias_prop)-> + body.vpath_alias.alias = alias; + target->has_vpath_alias_prop = true; + vpath_defined = true; + return alias->stat.time; + } + while ((*vpath != (int) nul_char) && + ((*vpath == (int) colon_char) || iswspace(*vpath))) { + vpath++; + } + } + /* + * Restore vpath_defined + */ + vpath_defined = true; + return target->stat.time; +} + +/* + * read_dir(dir, pattern, line, library) + * + * Used to enter the contents of directories into makes namespace. + * Presence of a file is important when scanning for implicit rules. + * read_dir() is also used to expand wildcards in dependency lists. + * + * Return value: + * Non-0 if we found files to match the pattern + * + * Parameters: + * dir Path to the directory to read + * pattern Pattern for that files should match or NULL + * line When we scan using a pattern we enter files + * we find as dependencies for this line + * library If we scan for "lib.a()" + * + * Global variables used: + * debug_level Should we trace the dir reading? + * dot The Name ".", compared against + * sccs_dir_path The path to the SCCS dir (from PROJECTDIR) + * vpath_defined Was the variable VPATH defined in environment? + * vpath_name The Name "VPATH", use to get macro value + */ +int +read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library) +{ + wchar_t file_name[MAXPATHLEN]; + wchar_t *file_name_p = file_name; + Name file; + wchar_t plain_file_name[MAXPATHLEN]; + wchar_t *plain_file_name_p; + Name plain_file; + wchar_t tmp_wcs_buffer[MAXPATHLEN]; + DIR *dir_fd; + int m_local_dependency=0; +#define d_fileno d_ino + register struct dirent *dp; + wchar_t *vpath = NULL; + wchar_t *p; + int result = 0; + + if(dir->hash.length >= MAXPATHLEN) { + return 0; + } + + Wstring wcb(dir); + Wstring vps; + + /* A directory is only read once unless we need to expand wildcards. */ + if (pattern == NULL) { + if (dir->has_read_dir) { + return 0; + } + dir->has_read_dir = true; + } + /* Check if VPATH is active and setup list if it is. */ + if (vpath_defined && (dir == dot)) { + vps.init(getvar(vpath_name)); + vpath = vps.get_string(); + } + + /* + * Prepare the string where we build the full name of the + * files in the directory. + */ + if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) { + (void) wcscpy(file_name, wcb.get_string()); + MBSTOWCS(wcs_buffer, "/"); + (void) wcscat(file_name, wcs_buffer); + file_name_p = file_name + wcslen(file_name); + } + + /* Open the directory. */ +vpath_loop: + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return 0; + } + + /* Read all the directory entries. */ + while ((dp = readdir(dir_fd)) != NULL) { + /* We ignore "." and ".." */ + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* + * Build the full name of the file using whatever + * path supplied to the function. + */ + MBSTOWCS(tmp_wcs_buffer, dp->d_name); + (void) wcscpy(file_name_p, tmp_wcs_buffer); + file = enter_file_name(file_name, library); + if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { + /* + * If we are expanding a wildcard pattern, we + * enter the file as a dependency for the target. + */ + if (debug_level > 0){ + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target->string_mb, + file->string_mb, + mbs_buffer); + } + enter_dependency(line, file, false); + result++; + } else { + /* + * If the file has an SCCS/s. file, + * we will detect that later on. + */ + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + plain_file_name_p = plain_file_name; + (void) wcscpy(plain_file_name_p, tmp_wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + } + (void) closedir(dir_fd); + if ((vpath != NULL) && (*vpath != (int) nul_char)) { + while ((*vpath != (int) nul_char) && + (iswspace(*vpath) || (*vpath == (int) colon_char))) { + vpath++; + } + p = vpath; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + vpath++; + } + if (vpath > p) { + dir = GETNAME(p, vpath - p); + goto vpath_loop; + } + } +/* + * look into SCCS directory only if it's not svr4. For svr4 dont do that. + */ + +/* + * Now read the SCCS directory. + * Files in the SCSC directory are considered to be part of the set of + * files in the plain directory. They are also entered in their own right. + * Prepare the string where we build the true name of the SCCS files. + */ + (void) wcsncpy(plain_file_name, + file_name, + file_name_p - file_name); + plain_file_name[file_name_p - file_name] = 0; + plain_file_name_p = plain_file_name + wcslen(plain_file_name); + + if(!svr4) { + + if (sccs_dir_path != NULL) { + wchar_t tmp_wchar; + wchar_t path[MAXPATHLEN]; + char mb_path[MAXPATHLEN]; + + if (file_name_p - file_name > 0) { + tmp_wchar = *file_name_p; + *file_name_p = 0; + WCSTOMBS(mbs_buffer, file_name); + (void) sprintf(mb_path, "%s/%s/SCCS", + sccs_dir_path, + mbs_buffer); + *file_name_p = tmp_wchar; + } else { + (void) sprintf(mb_path, "%s/SCCS", sccs_dir_path); + } + MBSTOWCS(path, mb_path); + (void) wcscpy(file_name, path); + } else { + MBSTOWCS(wcs_buffer, "SCCS"); + (void) wcscpy(file_name_p, wcs_buffer); + } + } else { + MBSTOWCS(wcs_buffer, "."); + (void) wcscpy(file_name_p, wcs_buffer); + } + /* Internalize the constructed SCCS dir name. */ + (void) exists(dir = GETNAME(file_name, FIND_LENGTH)); + /* Just give up if the directory file doesnt exist. */ + if (!dir->stat.is_file) { + return result; + } + /* Open the directory. */ + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return result; + } + MBSTOWCS(wcs_buffer, "/"); + (void) wcscat(file_name, wcs_buffer); + file_name_p = file_name + wcslen(file_name); + + while ((dp = readdir(dir_fd)) != NULL) { + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* Construct and internalize the true name of the SCCS file. */ + MBSTOWCS(wcs_buffer, dp->d_name); + (void) wcscpy(file_name_p, wcs_buffer); + file = GETNAME(file_name, FIND_LENGTH); + file->stat.is_file = true; + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(wcs_buffer, dp->d_name + 2); + (void) wcscpy(plain_file_name_p, wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* if sccs dependency is already set,skip */ + if(plain_file->prop) { + Property sprop = get_prop(plain_file->prop,sccs_prop); + if(sprop != NULL) { + if (sprop->body.sccs.file) { + goto try_pattern; + } + } + } + + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; +try_pattern: + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + (void) closedir(dir_fd); + + return result; +} + +/* + * enter_file_name(name_string, library) + * + * Helper function for read_dir(). + * + * Return value: + * The Name that was entered + * + * Parameters: + * name_string Name of the file we want to enter + * library The library it is a member of, if any + * + * Global variables used: + */ +static Name +enter_file_name(wchar_t *name_string, wchar_t *library) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec lib_name; + Name name; + Property prop; + + if (library == NULL) { + name = GETNAME(name_string, FIND_LENGTH); + name->stat.is_file = true; + return name; + } + + INIT_STRING_FROM_STACK(lib_name, buffer); + append_string(library, &lib_name, FIND_LENGTH); + append_char((int) parenleft_char, &lib_name); + append_string(name_string, &lib_name, FIND_LENGTH); + append_char((int) parenright_char, &lib_name); + + name = GETNAME(lib_name.buffer.start, FIND_LENGTH); + name->stat.is_file = true; + name->is_member = true; + prop = maybe_append_prop(name, member_prop); + prop->body.member.library = GETNAME(library, FIND_LENGTH); + prop->body.member.library->stat.is_file = true; + prop->body.member.entry = NULL; + prop->body.member.member = GETNAME(name_string, FIND_LENGTH); + prop->body.member.member->stat.is_file = true; + return name; +} + +/* + * star_match(string, pattern) + * + * This is a regular shell type wildcard pattern matcher + * It is used when xpanding wildcards in dependency lists + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +star_match(register wchar_t *string, register wchar_t *pattern) +{ + register int pattern_ch; + + switch (*pattern) { + case 0: + return succeeded; + case bracketleft_char: + case question_char: + case asterisk_char: + while (*string) { + if (amatch(string++, pattern)) { + return succeeded; + } + } + break; + default: + pattern_ch = (int) *pattern++; + while (*string) { + if ((*string++ == pattern_ch) && + amatch(string, pattern)) { + return succeeded; + } + } + break; + } + return failed; +} + +/* + * amatch(string, pattern) + * + * Helper function for shell pattern matching + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +amatch(register wchar_t *string, register wchar_t *pattern) +{ + register long lower_bound; + register long string_ch; + register long pattern_ch; + register int k; + +top: + for (; 1; pattern++, string++) { + lower_bound = 017777777777; + string_ch = *string; + switch (pattern_ch = *pattern) { + case bracketleft_char: + k = 0; + while ((pattern_ch = *++pattern) != 0) { + switch (pattern_ch) { + case bracketright_char: + if (!k) { + return failed; + } + string++; + pattern++; + goto top; + case hyphen_char: + k |= (lower_bound <= string_ch) && + (string_ch <= + (pattern_ch = pattern[1])); + default: + if (string_ch == + (lower_bound = pattern_ch)) { + k++; + } + } + } + return failed; + case asterisk_char: + return star_match(string, ++pattern); + case 0: + return BOOLEAN(!string_ch); + case question_char: + if (string_ch == 0) { + return failed; + } + break; + default: + if (pattern_ch != string_ch) { + return failed; + } + break; + } + } + /* NOTREACHED */ +} + diff --git a/bin/globals.cc b/bin/globals.cc new file mode 100644 index 0000000..b0c28c6 --- /dev/null +++ b/bin/globals.cc @@ -0,0 +1,181 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * globals.cc + * + * This declares all global variables + */ + +/* + * Included files + */ +#include +#include +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Global variables used by make only + */ + FILE *dependency_report_file; + +/* + * Global variables used by make + */ + Boolean allrules_read=false; + Name posix_name; + Name svr4_name; + Boolean sdot_target; /* used to identify s.m(/M)akefile */ + Boolean all_parallel; + Boolean assign_done; + int foo; + Boolean build_failed_seen; + Name built_last_make_run; + Name c_at; + Boolean cleanup; + Boolean close_report; + Boolean command_changed; + Boolean commands_done; + Chain conditional_targets; + Name conditionals; + Boolean continue_after_error; /* `-k' */ + Property current_line; + Name current_make_version; + Name current_target; + short debug_level; + Cmd_line default_rule; + Name default_rule_name; + Name default_target_to_build; + Name dmake_group; + Name dmake_max_jobs; + Name dmake_mode; + DMake_mode dmake_mode_type; + Name dmake_output_mode; + DMake_output_mode output_mode = txt1_mode; + Name dmake_odir; + Name dmake_rcfile; + Name done; + Name dot; + Name dot_keep_state; + Name dot_keep_state_file; + Name empty_name; + Boolean fatal_in_progress; + int file_number; +#if 0 + Boolean filter_stderr; /* `-X' */ +#endif + Name force; + Name ignore_name; + Boolean ignore_errors; /* `-i' */ + Boolean ignore_errors_all; /* `-i' */ + Name init; + int job_msg_id; + Boolean keep_state; + Name make_state; + timestruc_t make_state_before; + Dependency makefiles_used; + Name makeflags; +// Boolean make_state_locked; // Moved to lib/mksh + Name make_version; + char mbs_buffer2[(MAXPATHLEN * MB_LEN_MAX)]; + char *mbs_ptr; + char *mbs_ptr2; + Boolean depinfo_already_read = false; + Boolean no_action_was_taken = true; /* true if we've not ** + ** run any command */ + + Boolean no_parallel = false; + Name no_parallel_name; + Name not_auto; + Boolean only_parallel; + Boolean parallel; + Name parallel_name; + Name localhost_name; + int parallel_process_cnt; + Percent percent_list; + Dyntarget dyntarget_list; + Name plus; + Name pmake_machinesfile; + Name precious; + Name primary_makefile; + Boolean quest; /* `-q' */ + short read_trace_level; + Boolean reading_dependencies = false; + Name recursive_name; + int recursion_level; + short report_dependencies_level = 0; /* -P */ + Boolean report_pwd; + Boolean rewrite_statefile; + Running running_list; + char *sccs_dir_path; + Name sccs_get_name; + Name sccs_get_posix_name; + Cmd_line sccs_get_rule; + Cmd_line sccs_get_org_rule; + Cmd_line sccs_get_posix_rule; + Name get_name; + Cmd_line get_rule; + Name get_posix_name; + Cmd_line get_posix_rule; + Boolean all_precious; + Boolean silent_all; /* `-s' */ + Boolean report_cwd; /* `-w' */ + Boolean silent; /* `-s' */ + Name silent_name; + char *stderr_file = NULL; + char *stdout_file = NULL; + Boolean stdout_stderr_same; + Dependency suffixes; + Name suffixes_name; + Name sunpro_dependencies; + Boolean target_variants; + const char *tmpdir = "/tmp"; + const char *temp_file_directory = "."; + Name temp_file_name; + short temp_file_number; + time_t timing_start; + wchar_t *top_level_target; + Boolean touch; /* `-t' */ + Boolean trace_reader; /* `-D' */ + Boolean build_unconditional; /* `-u' */ + pathpt vroot_path = VROOT_DEFAULT; + Name wait_name; + wchar_t wcs_buffer2[MAXPATHLEN]; + wchar_t *wcs_ptr; + wchar_t *wcs_ptr2; + long int hostid; + +/* + * File table of contents + */ + diff --git a/bin/implicit.cc b/bin/implicit.cc new file mode 100644 index 0000000..184e5b6 --- /dev/null +++ b/bin/implicit.cc @@ -0,0 +1,1462 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * implicit.c + * + * Handle suffix and percent rules + */ + +/* + * Included files + */ +#include +#include /* expand_value() */ +#include /* retmem() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static wchar_t WIDE_NULL[1] = {(wchar_t) nul_char}; + +/* + * File table of contents + */ +extern Doname find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking); +extern Doname find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking); +extern Doname find_double_suffix_rule(register Name target, Property *command, Boolean rechecking); +extern void build_suffix_list(register Name target_suffix); +extern Doname find_percent_rule(register Name target, Property *command, Boolean rechecking); +static void create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent); +static Boolean match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf); +static void construct_string_from_pattern(Percent pat_rule, String percent, String result); +static Boolean dependency_exists(Name target, Property line); +extern Property maybe_append_prop(Name, Property_id); +extern void add_target_to_chain(Name target, Chain * query); + +/* + * find_suffix_rule(target, target_body, target_suffix, command, rechecking) + * + * Does the lookup for single and double suffix rules. + * It calls build_suffix_list() to build the list of possible suffixes + * for the given target. + * It then scans the list to find the first possible source file that + * exists. This is done by concatenating the body of the target name + * (target name less target suffix) and the source suffix and checking + * if the resulting file exists. + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * target_body The target name without the suffix + * target_suffix The suffix of the target + * command Pointer to slot to deposit cmd in if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + */ + +static Boolean actual_doname = false; + +/* /tolik/ + * fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * When make attemps to apply % rule it didn't look for a single suffix rule because + * if "doname" is called from "find_percent_rule" argument "implicit" is set to true + * and find_suffix_rule was not called. I've commented the checking of "implicit" + * in "doname" and make got infinite recursion for SVR4 tilde rules. + * Usage of "we_are_in_tilde" is intended to avoid this recursion. + */ + +static Boolean we_are_in_tilde = false; + +Doname +find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking) +{ + static wchar_t static_string_buf_3M [ 3 * MAXPATHLEN ]; + Name true_target = target; + wchar_t *sourcename = (wchar_t*)static_string_buf_3M; + register wchar_t *put_suffix; + register Property source_suffix; + register Name source; + Doname result; + register Property line; + extern Boolean tilde_rule; + Boolean name_found = true; + Boolean posix_tilde_attempt = true; + int src_len = MAXPATHLEN + strlen(target_body->string_mb); + + /* + * To avoid infinite recursion + */ + if(we_are_in_tilde) { + we_are_in_tilde = false; + return(build_dont_know); + } + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (debug_level > 1) { + (void) printf("%*sfind_suffix_rule(%s,%s,%s)\n", + recursion_level, + "", + true_target->string_mb, + target_body->string_mb, + target_suffix->string_mb); + } + if (command != NULL) { + if ((true_target->suffix_scan_done == true) && (*command == NULL)) { + return build_ok; + } + } + true_target->suffix_scan_done = true; + /* + * Enter all names from the directory where the target lives as + * files that makes sense. + * This will make finding the synthesized source possible. + */ + read_directory_of_file(target_body); + /* Cache the suffixes for this target suffix if not done. */ + if (!target_suffix->has_read_suffixes) { + build_suffix_list(target_suffix); + } + /* Preload the sourcename vector with the head of the target name. */ + if (src_len >= sizeof(static_string_buf_3M)) { + sourcename = ALLOC_WC(src_len); + } + (void) mbstowcs(sourcename, + target_body->string_mb, + (int) target_body->hash.length); + put_suffix = sourcename + target_body->hash.length; + /* Scan the suffix list for the target if one exists. */ + if (target_suffix->has_suffixes) { +posix_attempts: + for (source_suffix = get_prop(target_suffix->prop, + suffix_prop); + source_suffix != NULL; + source_suffix = get_prop(source_suffix->next, + suffix_prop)) { + /* Build the synthesized source name. */ + (void) mbstowcs(put_suffix, + source_suffix->body. + suffix.suffix->string_mb, + (int) source_suffix->body. + suffix.suffix->hash.length); + put_suffix[source_suffix->body. + suffix.suffix->hash.length] = + (int) nul_char; + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(gettext("%*sTrying %s\n"), + recursion_level, + "", + mbs_buffer); + } + source = getname_fn(sourcename, FIND_LENGTH, false, &name_found); + /* + * If the source file is not registered as + * a file, this source suffix did not match. + */ + if(vpath_defined && !posix && !svr4) { + (void) exists(source); + } + if (!source->stat.is_file) { + if(!(posix|svr4)) + { + if(!name_found) { + free_name(source); + } + continue; + } + + /* following code will ensure that the corresponding + ** tilde rules are executed when corresponding s. file + ** exists in the current directory. Though the current + ** target ends with a ~ character, there wont be any + ** any file in the current directory with that suffix + ** as it's fictitious. Even if it exists, it'll + ** execute all the rules for the ~ target. + */ + + if(source->string_mb[source->hash.length - 1] == '~' && + ( svr4 || posix_tilde_attempt ) ) + { + char *p, *np; + char *tmpbuf; + + tmpbuf = getmem(source->hash.length + 8); + /* + 8 to add "s." or "SCCS/s." */ + memset(tmpbuf,0,source->hash.length + 8); + source->string_mb[source->hash.length - 1] = '\0'; + if(p = (char *) memchr((char *)source->string_mb,'/',source->hash.length)) + { + while(1) { + if(np = (char *) memchr((char *)p+1,'/',source->hash.length - (p - source->string_mb))) { + p = np; + } else {break;} + } + /* copy everything including '/' */ + strncpy(tmpbuf, source->string_mb, p - source->string_mb + 1); + strcat(tmpbuf, "s."); + strcat(tmpbuf, p+1); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } else { + strcpy(tmpbuf, "s."); + strcat(tmpbuf, source->string_mb); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } + source->hash.length = strlen(source->string_mb); + if(exists(source) == file_doesnt_exist) + continue; + tilde_rule = true; + we_are_in_tilde = true; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } else { + if(posix && posix_tilde_attempt) { + if(exists(source) == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + } + } + + if (command != NULL) { + if(!name_found) { + store_name(source); + } + /* + * The source file is a file. + * Make sure it is up to date. + */ + if (dependency_exists(source, + get_prop(target->prop, + line_prop))) { + result = (Doname) source->state; + } else { +#if 0 /* with_squiggle sends false, which is buggy. : djay */ + result = doname(source, + (Boolean) source_suffix->body. + suffix.suffix->with_squiggle, + true); +#else + result = doname(source, + true, + true); +#endif + } + } else { + result = target_can_be_built(source); + + if (result == build_ok) { + return result; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } + + switch (result) { + case build_dont_know: + /* + * If we still can't build the source, + * this rule is not a match, + * try the next one. + */ + if (source->stat.time == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + case build_running: + if(!name_found) { + store_name(source); + } + true_target->suffix_scan_done = false; + line = maybe_append_prop(target, line_prop); + enter_dependency(line, source, false); + line->body.line.target = true_target; + return build_running; + case build_ok: + if(!name_found) { + store_name(source); + } + break; + case build_failed: + if(!name_found) { + store_name(source); + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_failed; + } + + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(gettext("%*sFound %s\n"), + recursion_level, + "", + mbs_buffer); + } + + if (source->depends_on_conditional) { + target->depends_on_conditional = true; + } +/* + * Since it is possible that the same target is built several times during + * the make run, we have to patch the target with all information we found + * here. Thus, the target will have an explicit rule the next time around. + */ + line = maybe_append_prop(target, line_prop); + if (*command == NULL) { + *command = line; + } + if ((source->stat.time > (*command)->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(gettext("%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + source->string_mb, + time_to_string(source-> + stat.time), + true_target->string_mb, + time_to_string((*command)-> + body.line. + dependency_time)); + } + /* + * Determine if this new dependency made the + * target out of date. + */ + (*command)->body.line.dependency_time = + MAX((*command)->body.line.dependency_time, + source->stat.time); + Boolean out_of_date; + if (target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, + (*command)->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, + (*command)->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using suffix rule for %s%s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + source_suffix->body.suffix.suffix->string_mb, + target_suffix->string_mb, + source->string_mb); + } + } + /* + * Add the implicit rule as the target's explicit + * rule if none actually given, and register + * dependency. + * The time checking above really should be + * conditional on actual use of implicit rule + * as well. + */ + line->body.line.sccs_command = false; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = + source_suffix->body.suffix.command_template; + } + enter_dependency(line, source, false); + line->body.line.target = true_target; + /* + * Also make sure the rule is built with + * $* and $< bound properly. + */ + line->body.line.star = target_body; + if(svr4|posix) { + char * p; + char tstr[256]; + extern Boolean dollarless_flag; + extern Name dollarless_value; + + if(tilde_rule) { + MBSTOWCS(wcs_buffer, source->string_mb); + dollarless_value = GETNAME(wcs_buffer,FIND_LENGTH); + } + else { + dollarless_flag = false; + } + } + line->body.line.less = source; + line->body.line.percent = NULL; + add_target_to_chain(source, &(line->body.line.query)); + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_ok; + } + if(posix && posix_tilde_attempt) { + posix_tilde_attempt = false; + goto posix_attempts; + } + if ((command != NULL) && + ((*command) != NULL) && + ((*command)->body.line.star == NULL)) { + (*command)->body.line.star = target_body; + } + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + /* Return here in case no rule matched the target */ + return build_dont_know; +} + +/* + * find_ar_suffix_rule(target, true_target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target member name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target using the suffix ".a". + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * true_target The proper name + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * dot_a The Name ".a", compared against + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking) +{ + wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Property line; + Name body; + static Name dot_a; + + Wstring targ_string(true_target); + Wstring suf_string; + + if (dot_a == NULL) { + MBSTOWCS(wcs_buffer, ".a"); + dot_a = GETNAME(wcs_buffer, FIND_LENGTH); + } + target_end = targ_string.get_string() + true_target->hash.length; + + /* + * We compare the tail of the target name with the suffixes + * from .SUFFIXES. + */ + if (debug_level > 1) { + (void) printf("%*sfind_ar_suffix_rule(%s)\n", + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches any of + * those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (!IS_WEQUALN(suf_string.get_string(), + target_end - suffix_length, + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + target->suffix_scan_done = false; + body = GETNAME(targ_string.get_string(), + (int)(true_target->hash.length - + suffix_length)); + we_are_in_tilde = false; + switch (find_suffix_rule(target, + body, + dot_a, + command, + rechecking)) { + case build_ok: + line = get_prop(target->prop, line_prop); + line->body.line.star = body; + return build_ok; + case build_running: + return build_running; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail, and so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + return build_dont_know; +} + +/* + * find_double_suffix_rule(target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target. + * + * Return value: + * Indicates if scan failed or not + * + * Parameters: + * target Target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_double_suffix_rule(register Name target, Property *command, Boolean rechecking) +{ + Name true_target = target; + Name target_body; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Boolean scanned_once = false; + Boolean name_found = true; + + Wstring targ_string; + Wstring suf_string; + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + targ_string.init(true_target); + + /* + * We compare the tail of the target name with the + * suffixes from .SUFFIXES. + */ + target_end = targ_string.get_string() + true_target->hash.length; + if (debug_level > 1) { + (void) printf("%*sfind_double_suffix_rule(%s)\n", + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches + * any of those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + target->suffix_scan_done = false; + true_target->suffix_scan_done = false; + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + /* Check the lengths, or else RTC will report rua. */ + if (true_target->hash.length < suffix_length) { + goto not_this_one; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + we_are_in_tilde = false; + target_body = GETNAME( + targ_string.get_string(), + (int)(true_target->hash.length - suffix_length) + ); + switch (find_suffix_rule(target, + target_body, + suffix->name, + command, + rechecking)) { + case build_ok: + return build_ok; + case build_running: + return build_running; + } + if (true_target->suffix_scan_done == true) { + scanned_once = true; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail. And so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + if (scanned_once) + true_target->suffix_scan_done = true; + return build_dont_know; +} + +/* + * build_suffix_list(target_suffix) + * + * Scans the .SUFFIXES list and figures out + * which suffixes this target can be derived from. + * The target itself is not know here, we just know the suffix of the + * target. For each suffix on the list the target can be derived iff + * a rule exists for the name "". + * A list of all possible building suffixes is built, with the rule for + * each, and tacked to the target suffix nameblock. + * + * Parameters: + * target_suffix The suffix we build a match list for + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + * working_on_targets Indicates that this is a real target + */ +void +build_suffix_list(register Name target_suffix) +{ + register Dependency source_suffix; + wchar_t rule_name[MAXPATHLEN]; + register Property line; + register Property suffix; + Name rule; + + /* If this is before default.mk has been read we just return to try */ + /* again later */ + if ((suffixes == NULL) || !working_on_targets) { + return; + } + if (debug_level > 1) { + (void) printf("%*sbuild_suffix_list(%s) ", + recursion_level, + "", + target_suffix->string_mb); + } + /* Mark the target suffix saying we cashed its list */ + target_suffix->has_read_suffixes = true; + /* Scan the .SUFFIXES list */ + for (source_suffix = suffixes; + source_suffix != NULL; + source_suffix = source_suffix->next) { + /* + * Build the name "". + * (a popular one would be ".c.o"). + */ + (void) mbstowcs(rule_name, + source_suffix->name->string_mb, + (int) source_suffix->name->hash.length); + (void) mbstowcs(rule_name + source_suffix->name->hash.length, + target_suffix->string_mb, + (int) target_suffix->hash.length); + /* + * Check if that name has a rule. If not, it cannot match + * any implicit rule scan and is ignored. + * The GETNAME() call only checks for presence, it will not + * enter the name if it is not defined. + */ + if (((rule = getname_fn(rule_name, + (int) (source_suffix->name-> + hash.length + + target_suffix->hash.length), + true)) != NULL) && + ((line = get_prop(rule->prop, line_prop)) != NULL)) { + if (debug_level > 1) { + (void) printf("%s ", rule->string_mb); + } + /* + * This makes it possible to quickly determine if + * it will pay to look for a suffix property. + */ + target_suffix->has_suffixes = true; + /* + * Add the suffix property to the target suffix + * and save the rule with it. + * All information the implicit rule scanner need + * is saved in the suffix property. + */ + suffix = append_prop(target_suffix, suffix_prop); + suffix->body.suffix.suffix = source_suffix->name; + suffix->body.suffix.command_template = + line->body.line.command_template; + } + } + if (debug_level > 1) { + (void) printf("\n"); + } +} + +/* + * find_percent_rule(target, command, rechecking) + * + * Tries to find a rule from the list of wildcard matched rules. + * It scans the list attempting to match the target. + * For each target match it checks if the corresponding source exists. + * If it does the match is returned. + * The percent_list is built at makefile read time. + * Each percent rule get one entry on the list. + * + * Return value: + * Indicates if the scan failed or not + * + * Parameters: + * target The target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * percent_list List of all percent rules + * recursion_level Used for tracing + * empty_name + */ +Doname +find_percent_rule(register Name target, Property *command, Boolean rechecking) +{ + register Percent pat_rule, pat_depe; + register Name depe_to_check; + register Dependency depe; + register Property line; + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + String_rec percent; + wchar_t percent_buf[STRING_BUFFER_LENGTH]; + Name true_target = target; + Name less; + Boolean nonpattern_less; + Boolean dep_name_found = false; + Doname result = build_dont_know; + Percent rule_candidate = NULL; + Boolean rule_maybe_ok; + Boolean is_pattern; + + /* If the target is constructed for a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (target->has_long_member_name) { + true_target = get_prop(target->prop, + long_member_name_prop)->body.long_member_name.member_name; + } + if (debug_level > 1) { + (void) printf(gettext("%*sLooking for %% rule for %s\n"), + recursion_level, + "", + true_target->string_mb); + } + for (pat_rule = percent_list; + pat_rule != NULL; + pat_rule = pat_rule->next) { + /* Avoid infinite recursion when expanding patterns */ + if (pat_rule->being_expanded == true) { + continue; + } + + /* Mark this pat_rule as "maybe ok". If no % rule is found + make will use this rule. The following algorithm is used: + 1) make scans all pattern rules in order to find the rule + where ALL dependencies, including nonpattern ones, exist or + can be built (GNU behaviour). If such rule is found make + will apply it. + 2) During this check make also remembers the first pattern rule + where all PATTERN dependencies can be build (no matter what + happens with nonpattern dependencies). + 3) If no rule satisfying 1) is found, make will apply the rule + remembered in 2) if there is one. + */ + rule_maybe_ok = true; + + /* used to track first percent dependency */ + less = NULL; + nonpattern_less = true; + + /* check whether pattern matches. + if it matches, percent string will contain matched percent part of pattern */ + if (!match_found_with_pattern(true_target, pat_rule, &percent, percent_buf)) { + continue; + } + if (pat_rule->dependencies != NULL) { + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + /* checking result for dependency */ + result = build_dont_know; + + dep_name_found = true; + if (pat_depe->name->percent) { + is_pattern = true; + /* build dependency name */ + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + + if ((less == NULL) || nonpattern_less) { + less = depe_to_check; + nonpattern_less = false; + } + } else { + /* nonpattern dependency */ + is_pattern = false; + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + } + if (less == NULL) { + less = depe_to_check; + } + } + + if (depe_to_check == empty_name) { + result = build_ok; + } else { + if (debug_level > 1) { + (void) printf(gettext("%*sTrying %s\n"), + recursion_level, + "", + depe_to_check->string_mb); + } + + pat_rule->being_expanded = true; + + /* suppress message output */ + int save_debug_level = debug_level; + debug_level = 0; + + /* check whether dependency can be built */ + if (dependency_exists(depe_to_check, + get_prop(target->prop, + line_prop))) + { + result = (Doname) depe_to_check->state; + } else { + if(actual_doname) { + result = doname(depe_to_check, true, true); + } else { + result = target_can_be_built(depe_to_check); + } + if(!dep_name_found) { + if(result != build_ok && result != build_running) { + free_name(depe_to_check); + } else { + store_name(depe_to_check); + } + } + } + if(result != build_ok && is_pattern) { + rule_maybe_ok = false; + } + + /* restore debug_level */ + debug_level = save_debug_level; + } + + if (pat_depe->name->percent) { + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + /* make can't figure out how to make this dependency */ + if (result != build_ok && result != build_running) { + pat_rule->being_expanded = false; + break; + } + } + } else { + result = build_ok; + } + + /* this pattern rule is the needed one since all dependencies could be built */ + if (result == build_ok || result == build_running) { + break; + } + + /* Make does not know how to build some of dependencies from this rule. + But if all "pattern" dependencies can be built, we remember this rule + as a candidate for the case if no other pattern rule found. + */ + if(rule_maybe_ok && rule_candidate == NULL) { + rule_candidate = pat_rule; + } + } + + /* if no pattern matching rule was found, use the remembered candidate + or return build_dont_know if there is no candidate. + */ + if (result != build_ok && result != build_running) { + if(rule_candidate) { + pat_rule = rule_candidate; + } else { + return build_dont_know; + } + } + + /* if we are performing only check whether dependency could be built with existing rules, + return success */ + if (command == NULL) { + if(pat_rule != NULL) { + pat_rule->being_expanded = false; + } + return result; + } + + if (debug_level > 1) { + (void) printf(gettext("%*sMatched %s:"), + recursion_level, + "", + target->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } else { + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } + } + + if (depe_to_check != empty_name) { + (void) printf(" %s", depe_to_check->string_mb); + } + } + + (void) printf(gettext(" from: %s:"), + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf("\n"); + } + + if (true_target->colons == no_colon) { + true_target->colons = one_colon; + } + + /* create deppendency list and target group from matched pattern rule */ + create_target_group_and_dependencies_list(target, pat_rule, &percent); + + /* save command */ + line = get_prop(target->prop, line_prop); + *command = line; + + /* free query chain if one exist */ + while(line->body.line.query != NULL) { + Chain to_free = line->body.line.query; + line->body.line.query = line->body.line.query->next; + retmem_mb((char *) to_free); + } + + if (line->body.line.dependencies != NULL) { + /* build all collected dependencies */ + for (depe = line->body.line.dependencies; + depe != NULL; + depe = depe->next) { + actual_doname = true; + result = doname_check(depe->name, true, true, depe->automatic); + + actual_doname = false; + if (result == build_failed) { + pat_rule->being_expanded = false; + return build_failed; + } + if (result == build_running) { + pat_rule->being_expanded = false; + return build_running; + } + + if ((depe->name->stat.time > line->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(gettext("%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + depe->name->string_mb, + time_to_string(depe->name->stat.time), + true_target->string_mb, + time_to_string(line->body.line.dependency_time)); + } + + line->body.line.dependency_time = + MAX(line->body.line.dependency_time, depe->name->stat.time); + + /* determine whether this dependency made target out of date */ + Boolean out_of_date; + if (target->is_member || depe->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, depe->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, depe->name->stat.time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + add_target_to_chain(depe->name, &(line->body.line.query)); + + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using pattern rule %s:"), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf(gettext(" because it is out of date relative to %s\n"), + depe->name->string_mb); + } + } + } + } else { + if ((true_target->stat.time <= file_doesnt_exist) || + (true_target->stat.time < line->body.line.dependency_time)) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using pattern rule %s: "), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb, + (target->stat.time > file_doesnt_exist) ? + gettext("because it is out of date") : + gettext("because it does not exist")); + } + } + } + + /* enter explicit rule from percent rule */ + Name lmn_target = true_target; + if (true_target->has_long_member_name) { + lmn_target = get_prop(true_target->prop, long_member_name_prop)->body.long_member_name.member_name; + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + line->body.line.command_template = pat_rule->command_template; + line->body.line.star = GETNAME(percent.buffer.start, FIND_LENGTH); + line->body.line.less = less; + + if (lmn_target->parenleft) { + Wstring lmn_string(lmn_target); + + wchar_t *left = (wchar_t *) wcschr(lmn_string.get_string(), (int) parenleft_char); + wchar_t *right = (wchar_t *) wcschr(lmn_string.get_string(), (int) parenright_char); + + if ((left == NULL) || (right == NULL)) { + line->body.line.percent = NULL; + } else { + line->body.line.percent = GETNAME(left + 1, right - left - 1); + } + } else { + line->body.line.percent = NULL; + } + pat_rule->being_expanded = false; + + return result; +} + +/* + * match_found_with_pattern + * ( target, pat_rule, percent, percent_buf) + * + * matches "target->string" with a % pattern. + * If pattern contains a MACRO definition, it's expanded first. + * + * Return value: + * true if a match was found + * + * Parameters: + * target The target we're trying to match + * pattern + * percent record that contains "percent_buf" below + * percent_buf This is where the patched % part of pattern is stored + * + */ + +static Boolean +match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + + /* construct prefix string and check whether prefix matches */ + Name prefix = pat_rule->patterns[0]; + int prefix_length; + + Wstring targ_string(target); + Wstring pref_string(prefix); + Wstring suf_string; + + if (prefix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(prefix, &string, false); + prefix_length = string.text.p - string.buffer.start; + if ((string.buffer.start[0] == (int) period_char) && + (string.buffer.start[1] == (int) slash_char)) { + string.buffer.start += 2; + prefix_length -= 2; + } + if (!targ_string.equaln(string.buffer.start, prefix_length)) { + return false; + } + } else { + prefix_length = prefix->hash.length; + if (!targ_string.equaln(&pref_string, prefix_length)) { + return false; + } + } + + /* do the same with pattern suffix */ + Name suffix = pat_rule->patterns[pat_rule->patterns_total - 1]; + suf_string.init(suffix); + + int suffix_length; + if (suffix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(suffix, &string, false); + suffix_length = string.text.p - string.buffer.start; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(string.buffer.start, target->hash.length - suffix_length)) { + return false; + } + } else { + suffix_length = (int) suffix->hash.length; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(&suf_string, target->hash.length - suffix_length)) { + return false; + } + } + + Boolean match_found = false; + int percent_length = target->hash.length - prefix_length - suffix_length; + + while (!match_found && (percent_length >= 0)) { + /* init result string */ + INIT_STRING_FROM_STACK(string, string_buf); + + /* init percent string */ + percent->buffer.start = percent_buf; + percent->text.p = percent_buf; + percent->text.end = NULL; + percent->free_after_use = false; + percent->buffer.end = percent_buf + STRING_BUFFER_LENGTH; + + /* construct percent and result strings */ + targ_string.append_to_str(percent, prefix_length, percent_length); + construct_string_from_pattern(pat_rule, percent, &string); + + /* check for match */ + if (targ_string.equal(string.buffer.start, 0)) { + match_found = true; + } else { + percent_length--; + } + } + + /* result */ + return match_found; +} + + +/* + * create_target_group_and_dependencies_list + * (target, pat_rule, percent) + * + * constructs dependency list and a target group from pattern. + * + * If we have the lines + * %/%.a + %/%.b + C%/CC%.c: yyy %.d bb%/BB%.e + * commands + * + * and we have matched the pattern xx/xx.a with %/%.a, then we + * construct a target group that looks like this: + * xx/xx.a + xx/xx.b + Cxx/CCxx.c: dependencies + * + * and construct dependency list that looks like this: + * yyy xx.d bbxx/BBxx.e + already existed dependencies + * + * Return value: + * none + * + * Parameters: + * target The target we are building, in the previous + * example, this is xx/xx.a + * pat_rule the % pattern that matched "target", here %/%.a + * percent string containing matched % part. In the example=xx. + * + * Global variables used: + * empty_name + */ + +static void +create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + Percent pat_depe; + Name depe; + Property line = maybe_append_prop(target, line_prop); + Chain new_target_group = NULL; + Chain *new_target_group_tail = &new_target_group; + Chain group_member; + + /* create and append dependencies from rule */ + for (pat_depe = pat_rule->dependencies; pat_depe != NULL; pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, percent, &string); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + if (depe != empty_name) { + enter_dependency(line, depe, false); + } + } else { + depe = pat_depe->name; + if(depe->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe, &string, false); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + } + enter_dependency(line, depe, false); + } + } + + /* if matched pattern is a group member, create new target group */ + for (group_member = pat_rule->target_group; group_member != NULL; group_member = group_member->next) { + Name new_target = group_member->name; + if (group_member->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(group_member->percent_member, percent, &string); + new_target = GETNAME(string.buffer.start, FIND_LENGTH); + if (new_target == empty_name) { + continue; + } + } + + /* check for duplicates */ + Chain tgm; + for (tgm = new_target_group; tgm != NULL; tgm = tgm->next) { + if (new_target == tgm->name) { + break; + } + } + if (tgm != NULL) { + continue; + } + + /* insert it into the targets list */ + (*new_target_group_tail) = ALLOC(Chain); + (*new_target_group_tail)->name = new_target; + (*new_target_group_tail)->next = NULL; + new_target_group_tail = &(*new_target_group_tail)->next; + } + + /* now we gathered all dependencies and created target group */ + line->body.line.target_group = new_target_group; + + /* update properties for group members */ + for (group_member = new_target_group; group_member != NULL; group_member = group_member->next) { + if (group_member->name != target) { + group_member->name->prop = target->prop; + group_member->name->conditional_cnt = target->conditional_cnt; + } + } +} + +/* + * construct_string_from_pattern + * (pat_rule, percent, result) + * + * after pattern matched a target this routine is called to construct targets and dependencies + * strings from this matched pattern rule and a string (percent) with substitutes % sign in pattern. + * + * Return value: + * none + * + * Parameters: + * pat_rule matched pattern rule + * percent string containing matched % sign part. + * result holds the result of string construction. + * + */ +static void +construct_string_from_pattern(Percent pat_rule, String percent, String result) { + for (int i = 0; i < pat_rule->patterns_total; i++) { + if (pat_rule->patterns[i]->dollar) { + expand_value(pat_rule->patterns[i], + result, + false); + + } else { + append_string(pat_rule->patterns[i]->string_mb, + result, + pat_rule->patterns[i]->hash.length); + } + + if (i < pat_rule->patterns_total - 1) { + append_string(percent->buffer.start, + result, + percent->text.p - percent->buffer.start); + } + } + + if ((result->buffer.start[0] == (int) period_char) && + (result->buffer.start[1] == (int) slash_char)) { + result->buffer.start += 2; + } +} + +/* + * dependency_exists(target, line) + * + * Returns true if the target exists in the + * dependency list of the line. + * + * Return value: + * True if target is on dependency list + * + * Parameters: + * target Target we scan for + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +dependency_exists(Name target, Property line) +{ + Dependency dp; + + if (line == NULL) { + return false; + } + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + if (dp->name == target) { + return true; + } + } + return false; +} + +void +add_target_to_chain(Name target, Chain * query) +{ + if (target->is_member && (get_prop(target->prop, member_prop) != NULL)) { + target = get_prop(target->prop, member_prop)->body.member.member; + } + Chain *query_tail; + for (query_tail = query; *query_tail != NULL; query_tail = &(*query_tail)->next) { + if ((*query_tail)->name == target) { + return; + } + } + *query_tail = ALLOC(Chain); + (*query_tail)->name = target; + (*query_tail)->next = NULL; +} + diff --git a/bin/macro.cc b/bin/macro.cc new file mode 100644 index 0000000..a33484a --- /dev/null +++ b/bin/macro.cc @@ -0,0 +1,167 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * macro.cc + * + * Handle expansion of make macros + */ + +/* + * Included files + */ +#include +#include /* getvar(), expand_value() */ +#include /* getmem() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ + +void +setvar_append(register Name name, register Name value) +{ + register Property macro_apx = get_prop(name->prop, macro_append_prop); + register Property macro = get_prop(name->prop, macro_prop); + int length; + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Chain chain; + Name val = NULL; + + if(macro_apx == NULL) { + macro_apx = append_prop(name, macro_append_prop); + if(macro != NULL) { + macro_apx->body.macro_appendix.value = macro->body.macro.value; + } + } + + val = macro_apx->body.macro_appendix.value_to_append; + + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if (val != NULL) { + APPEND_NAME(val, + &destination, + (int) val->hash.length); + if (value != NULL) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + macro_apx->body.macro_appendix.value_to_append = value; + + SETVAR(name, empty_name, true); +} + +/* + * setvar_envvar() + * + * This function scans the list of environment variables that have + * dynamic values and sets them. + * + * Parameters: + * + * Global variables used: + * envvar A list of environment vars with $ in value + */ +void +setvar_envvar(void) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + int length; + register char *mbs, *tmp_mbs_buffer = NULL; + register char *env, *tmp_mbs_buffer2 = NULL; + Envvar p; + String_rec value; + + for (p = envvar; p != NULL; p = p->next) { + if (p->already_put + ) { + continue; + } + INIT_STRING_FROM_STACK(value, buffer); + expand_value(p->value, &value, false); + if ((length = wcslen(value.buffer.start)) >= MAXPATHLEN) { + mbs = tmp_mbs_buffer = getmem((length + 1) * MB_LEN_MAX); + (void) wcstombs(mbs, + value.buffer.start, + (length + 1) * MB_LEN_MAX); + } else { + mbs = mbs_buffer; + WCSTOMBS(mbs, value.buffer.start); + } + length = 2 + strlen(p->name->string_mb) + strlen(mbs); + if (!p->already_put || length > (MAXPATHLEN * MB_LEN_MAX)) { + env = tmp_mbs_buffer2 = getmem(length); + } else { + env = mbs_buffer2; + } + (void) sprintf(env, + "%s=%s", + p->name->string_mb, + mbs); + if (!p->already_put) { + (void) putenv(env); + p->already_put = true; + if (p->env_string) { + retmem_mb(p->env_string); + } + p->env_string = env; + tmp_mbs_buffer2 = NULL; // We should not return this memory now + } + if (tmp_mbs_buffer2) { + retmem_mb(tmp_mbs_buffer2); + tmp_mbs_buffer2 = NULL; + } + if (tmp_mbs_buffer) { + retmem_mb(tmp_mbs_buffer); + tmp_mbs_buffer = NULL; + } + } +} + + diff --git a/bin/main.cc b/bin/main.cc new file mode 100644 index 0000000..ed0afc0 --- /dev/null +++ b/bin/main.cc @@ -0,0 +1,3215 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * main.cc + * + * make program main routine plus some helper routines + */ + +/* + * Included files + */ +#include /* bsd_signal() */ + + +#include /* setlocale() */ +#include +#include +#include /* getvar() */ +#include /* getmem(), setup_char_semantics() */ + +#include /* getpwnam() */ +#include +#include +#include +#include /* ENOENT */ +#include /* fstat() */ +#include /* open() */ + +# include /* sysinfo() */ + +#include /* stat() */ +#include /* wait() */ +#include /* execv(), unlink(), access() */ +#include /* report_dependency(), get_report_file() */ + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + +extern void job_adjust_fini(); + + +/* + * Defined macros + */ +#define LD_SUPPORT_ENV_VAR "SGS_SUPPORT_32" +#define LD_SUPPORT_ENV_VAR_32 "SGS_SUPPORT_32" +#define LD_SUPPORT_ENV_VAR_64 "SGS_SUPPORT_64" +#define LD_SUPPORT_MAKE_LIB "libmakestate.so.1" +#ifdef __i386 +#define LD_SUPPORT_MAKE_ARCH "i386" +#elif __sparc +#define LD_SUPPORT_MAKE_ARCH "sparc" +#else +#error "Unsupported architecture" +#endif + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char *argv_zero_string; +static Boolean build_failed_ever_seen; +static Boolean continue_after_error_ever_seen; /* `-k' */ +static Boolean dmake_group_specified; /* `-g' */ +static Boolean dmake_max_jobs_specified; /* `-j' */ +static Boolean dmake_mode_specified; /* `-m' */ +static Boolean dmake_add_mode_specified; /* `-x' */ +static Boolean dmake_output_mode_specified; /* `-x DMAKE_OUTPUT_MODE=' */ +static Boolean dmake_compat_mode_specified; /* `-x SUN_MAKE_COMPAT_MODE=' */ +static Boolean dmake_odir_specified; /* `-o' */ +static Boolean dmake_rcfile_specified; /* `-c' */ +static Boolean env_wins; /* `-e' */ +static Boolean ignore_default_mk; /* `-r' */ +static Boolean list_all_targets; /* `-T' */ +static int mf_argc; +static char **mf_argv; +static Dependency_rec not_auto_depen_struct; +static Dependency not_auto_depen = ¬_auto_depen_struct; +static Boolean pmake_cap_r_specified; /* `-R' */ +static Boolean pmake_machinesfile_specified; /* `-M' */ +static Boolean stop_after_error_ever_seen; /* `-S' */ +static Boolean trace_status; /* `-p' */ + +#ifdef DMAKE_STATISTICS +static Boolean getname_stat = false; +#endif + + static time_t start_time; + static int g_argc; + static char **g_argv; + +/* + * File table of contents + */ + extern "C" void cleanup_after_exit(void); + +extern "C" { + extern void dmake_exit_callback(void); + extern void dmake_message_callback(char *); +} + +extern Name normalize_name(register wchar_t *name_string, register int length); + +extern int main(int, char * []); + +static void append_makeflags_string(Name, String); +static void doalarm(int); +static void enter_argv_values(int , char **, ASCII_Dyn_Array *); +static void make_targets(int, char **, Boolean); +static int parse_command_option(char); +static void read_command_options(int, char **); +static void read_environment(Boolean); +static void read_files_and_state(int, char **); +static Boolean read_makefile(Name, Boolean, Boolean, Boolean); +static void report_recursion(Name); +static void set_sgs_support(void); +static void setup_for_projectdir(void); +static void setup_makeflags_argv(void); +static void report_dir_enter_leave(Boolean entering); + +extern void expand_value(Name, register String , Boolean); + +static const char verstring[] = "illumos make"; + +jmp_buf jmpbuffer; + +/* + * main(argc, argv) + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * list_all_targets make -T seen + * trace_status make -p seen + * + * Global variables used: + * debug_level Should we trace make actions? + * keep_state Set if .KEEP_STATE seen + * makeflags The Name "MAKEFLAGS", used to get macro + * remote_command_name Name of remote invocation cmd ("on") + * running_list List of parallel running processes + * stdout_stderr_same true if stdout and stderr are the same + * auto_dependencies The Name "SUNPRO_DEPENDENCIES" + * temp_file_directory Set to the dir where we create tmp file + * trace_reader Set to reflect tracing status + * working_on_targets Set when building user targets + */ +int +main(int argc, char *argv[]) +{ + /* + * cp is a -> to the value of the MAKEFLAGS env var, + * which has to be regular chars. + */ + register char *cp; + char make_state_dir[MAXPATHLEN]; + Boolean parallel_flag = false; + char *prognameptr; + char *slash_ptr; + mode_t um; + int i; + struct itimerval value; + char def_dmakerc_path[MAXPATHLEN]; + Name dmake_name, dmake_name2; + Name dmake_value, dmake_value2; + Property prop, prop2; + struct stat statbuf; + int statval; + + struct stat out_stat, err_stat; + hostid = gethostid(); + bsd_signals(); + + (void) setlocale(LC_ALL, ""); + + +#ifdef DMAKE_STATISTICS + if (getenv("DMAKE_STATISTICS")) { + getname_stat = true; + } +#endif + +#ifndef TEXT_DOMAIN +#define TEXT_DOMAIN "SYS_TEST" +#endif + textdomain(TEXT_DOMAIN); + + g_argc = argc; + g_argv = (char **) malloc((g_argc + 1) * sizeof(char *)); + for (i = 0; i < argc; i++) { + g_argv[i] = argv[i]; + } + g_argv[i] = NULL; + + /* + * Set argv_zero_string to some form of argv[0] for + * recursive MAKE builds. + */ + + if (*argv[0] == (int) slash_char) { + /* argv[0] starts with a slash */ + argv_zero_string = strdup(argv[0]); + } else if (strchr(argv[0], (int) slash_char) == NULL) { + /* argv[0] contains no slashes */ + argv_zero_string = strdup(argv[0]); + } else { + /* + * argv[0] contains at least one slash, + * but doesn't start with a slash + */ + char *tmp_current_path; + char *tmp_string; + + tmp_current_path = get_current_path(); + tmp_string = getmem(strlen(tmp_current_path) + 1 + + strlen(argv[0]) + 1); + (void) sprintf(tmp_string, + "%s/%s", + tmp_current_path, + argv[0]); + argv_zero_string = strdup(tmp_string); + retmem_mb(tmp_string); + } + + /* + * The following flags are reset if we don't have the + * (.nse_depinfo or .make.state) files locked and only set + * AFTER the file has been locked. This ensures that if the user + * interrupts the program while file_lock() is waiting to lock + * the file, the interrupt handler doesn't remove a lock + * that doesn't belong to us. + */ + make_state_lockfile = NULL; + make_state_locked = false; + + + /* + * look for last slash char in the path to look at the binary + * name. This is to resolve the hard link and invoke make + * in svr4 mode. + */ + + /* Sun OS make standart */ + svr4 = false; + posix = false; + if(!strcmp(argv_zero_string, "/usr/xpg4/bin/make")) { + svr4 = false; + posix = true; + } else { + prognameptr = strrchr(argv[0], '/'); + if(prognameptr) { + prognameptr++; + } else { + prognameptr = argv[0]; + } + if(!strcmp(prognameptr, "svr4.make")) { + svr4 = true; + posix = false; + } + } + if (getenv(USE_SVR4_MAKE) || getenv("USE_SVID")){ + svr4 = true; + posix = false; + } + + /* + * Find the dmake_compat_mode: posix, sun, svr4, or gnu_style, . + */ + char * dmake_compat_mode_var = getenv("SUN_MAKE_COMPAT_MODE"); + if (dmake_compat_mode_var != NULL) { + if (0 == strcasecmp(dmake_compat_mode_var, "GNU")) { + gnu_style = true; + } + //svr4 = false; + //posix = false; + } + + /* + * Temporary directory set up. + */ + char * tmpdir_var = getenv("TMPDIR"); + if (tmpdir_var != NULL && *tmpdir_var == '/' && strlen(tmpdir_var) < MAXPATHLEN) { + strcpy(mbs_buffer, tmpdir_var); + for (tmpdir_var = mbs_buffer+strlen(mbs_buffer); + *(--tmpdir_var) == '/' && tmpdir_var > mbs_buffer; + *tmpdir_var = '\0'); + if (strlen(mbs_buffer) + 32 < MAXPATHLEN) { /* 32 = strlen("/dmake.stdout.%d.%d.XXXXXX") */ + sprintf(mbs_buffer2, "%s/dmake.tst.%d.XXXXXX", + mbs_buffer, getpid()); + int fd = mkstemp(mbs_buffer2); + if (fd >= 0) { + close(fd); + unlink(mbs_buffer2); + tmpdir = strdup(mbs_buffer); + } + } + } + + /* find out if stdout and stderr point to the same place */ + if (fstat(1, &out_stat) < 0) { + fatal(gettext("fstat of standard out failed: %s"), errmsg(errno)); + } + if (fstat(2, &err_stat) < 0) { + fatal(gettext("fstat of standard error failed: %s"), errmsg(errno)); + } + if ((out_stat.st_dev == err_stat.st_dev) && + (out_stat.st_ino == err_stat.st_ino)) { + stdout_stderr_same = true; + } else { + stdout_stderr_same = false; + } + /* Make the vroot package scan the path using shell semantics */ + set_path_style(0); + + setup_char_semantics(); + + setup_for_projectdir(); + + /* + * If running with .KEEP_STATE, curdir will be set with + * the connected directory. + */ + (void) atexit(cleanup_after_exit); + + load_cached_names(); + +/* + * Set command line flags + */ + setup_makeflags_argv(); + read_command_options(mf_argc, mf_argv); + read_command_options(argc, argv); + if (debug_level > 0) { + cp = getenv(makeflags->string_mb); + (void) printf(gettext("MAKEFLAGS value: %s\n"), cp == NULL ? "" : cp); + } + + setup_interrupt(handle_interrupt); + + read_files_and_state(argc, argv); + + /* + * Find the dmake_output_mode: TXT1, TXT2 or HTML1. + */ + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + if (prop2 == NULL) { + /* DMAKE_OUTPUT_MODE not defined, default to TXT1 mode */ + output_mode = txt1_mode; + } else { + dmake_value2 = prop2->body.macro.value; + if ((dmake_value2 == NULL) || + (IS_EQUAL(dmake_value2->string_mb, "TXT1"))) { + output_mode = txt1_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, "TXT2")) { + output_mode = txt2_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, "HTML1")) { + output_mode = html1_mode; + } else { + warning(gettext("Unsupported value `%s' for DMAKE_OUTPUT_MODE after -x flag (ignored)"), + dmake_value2->string_mb); + } + } + /* + * Find the dmake_mode: parallel, or serial. + */ + if ((!pmake_cap_r_specified) && + (!pmake_machinesfile_specified)) { + char *s, *b; + + if ((s = strdup(argv[0])) == NULL) + fatal(gettext("Out of memory")); + + b = basename(s); + + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + // If we're invoked as 'make' run serially, regardless of DMAKE_MODE + // If we're invoked as 'make' but passed -j, run parallel + // If we're invoked as 'dmake', without DMAKE_MODE, default parallel + // If we're invoked as 'dmake' and DMAKE_MODE is set, honour it. + if ((strcmp(b, "make") == 0) && + !dmake_max_jobs_specified) { + dmake_mode_type = serial_mode; + no_parallel = true; + } else if (prop2 == NULL) { + /* DMAKE_MODE not defined, default based on our name */ + if (strcmp(b, "dmake") == 0) { + dmake_mode_type = parallel_mode; + no_parallel = false; + } + } else { + dmake_value2 = prop2->body.macro.value; + if (IS_EQUAL(dmake_value2->string_mb, "parallel")) { + dmake_mode_type = parallel_mode; + no_parallel = false; + } else if (IS_EQUAL(dmake_value2->string_mb, "serial")) { + dmake_mode_type = serial_mode; + no_parallel = true; + } else { + fatal(gettext("Unknown dmake mode argument `%s' after -m flag"), dmake_value2->string_mb); + } + } + free(s); + } + + parallel_flag = true; + putenv(strdup("DMAKE_CHILD=TRUE")); + +// +// If dmake is running with -t option, set dmake_mode_type to serial. +// This is done because doname() calls touch_command() that runs serially. +// If we do not do that, maketool will have problems. +// + if(touch) { + dmake_mode_type = serial_mode; + no_parallel = true; + } + + /* + * Check whether stdout and stderr are physically same. + * This is in order to decide whether we need to redirect + * stderr separately from stdout. + * This check is performed only if __DMAKE_SEPARATE_STDERR + * is not set. This variable may be used in order to preserve + * the 'old' behaviour. + */ + out_err_same = true; + char * dmake_sep_var = getenv("__DMAKE_SEPARATE_STDERR"); + if (dmake_sep_var == NULL || (0 != strcasecmp(dmake_sep_var, "NO"))) { + struct stat stdout_stat; + struct stat stderr_stat; + if( (fstat(1, &stdout_stat) == 0) + && (fstat(2, &stderr_stat) == 0) ) + { + if( (stdout_stat.st_dev != stderr_stat.st_dev) + || (stdout_stat.st_ino != stderr_stat.st_ino) ) + { + out_err_same = false; + } + } + } + + +/* + * Enable interrupt handler for alarms + */ + (void) bsd_signal(SIGALRM, (SIG_PF)doalarm); + +/* + * Check if make should report + */ + if (getenv(sunpro_dependencies->string_mb) != NULL) { + FILE *report_file; + + report_dependency(""); + report_file = get_report_file(); + if ((report_file != NULL) && (report_file != (FILE*)-1)) { + (void) fprintf(report_file, "\n"); + } + } + +/* + * Make sure SUNPRO_DEPENDENCIES is exported (or not) properly. + */ + if (keep_state) { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = true; + } else { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = false; + } + + working_on_targets = true; + if (trace_status) { + dump_make_state(); + fclose(stdout); + fclose(stderr); + exit_status = 0; + exit(0); + } + if (list_all_targets) { + dump_target_list(); + fclose(stdout); + fclose(stderr); + exit_status = 0; + exit(0); + } + trace_reader = false; + + /* + * Set temp_file_directory to the directory the .make.state + * file is written to. + */ + if ((slash_ptr = strrchr(make_state->string_mb, (int) slash_char)) == NULL) { + temp_file_directory = strdup(get_current_path()); + } else { + *slash_ptr = (int) nul_char; + (void) strcpy(make_state_dir, make_state->string_mb); + *slash_ptr = (int) slash_char; + /* when there is only one slash and it's the first + ** character, make_state_dir should point to '/'. + */ + if(make_state_dir[0] == '\0') { + make_state_dir[0] = '/'; + make_state_dir[1] = '\0'; + } + if (make_state_dir[0] == (int) slash_char) { + temp_file_directory = strdup(make_state_dir); + } else { + char tmp_current_path2[MAXPATHLEN]; + + (void) sprintf(tmp_current_path2, + "%s/%s", + get_current_path(), + make_state_dir); + temp_file_directory = strdup(tmp_current_path2); + } + } + + + report_dir_enter_leave(true); + + make_targets(argc, argv, parallel_flag); + + report_dir_enter_leave(false); + + if (build_failed_ever_seen) { + if (posix) { + exit_status = 1; + } + exit(1); + } + exit_status = 0; + exit(0); + /* NOTREACHED */ +} + +/* + * cleanup_after_exit() + * + * Called from exit(), performs cleanup actions. + * + * Parameters: + * status The argument exit() was called with + * arg Address of an argument vector to + * cleanup_after_exit() + * + * Global variables used: + * command_changed Set if we think .make.state should be rewritten + * current_line Is set we set commands_changed + * do_not_exec_rule + * True if -n flag on + * done The Name ".DONE", rule we run + * keep_state Set if .KEEP_STATE seen + * parallel True if building in parallel + * quest If -q is on we do not run .DONE + * report_dependencies + * True if -P flag on + * running_list List of parallel running processes + * temp_file_name The temp file is removed, if any + */ +extern "C" void +cleanup_after_exit(void) +{ + Running rp; + +extern long getname_bytes_count; +extern long getname_names_count; +extern long getname_struct_count; +extern long freename_bytes_count; +extern long freename_names_count; +extern long freename_struct_count; +extern long other_alloc; + +extern long env_alloc_num; +extern long env_alloc_bytes; + + +#ifdef DMAKE_STATISTICS +if(getname_stat) { + printf(">>> Getname statistics:\n"); + printf(" Allocated:\n"); + printf(" Names: %ld\n", getname_names_count); + printf(" Strings: %ld Kb (%ld bytes)\n", getname_bytes_count/1000, getname_bytes_count); + printf(" Structs: %ld Kb (%ld bytes)\n", getname_struct_count/1000, getname_struct_count); + printf(" Total bytes: %ld Kb (%ld bytes)\n", getname_struct_count/1000 + getname_bytes_count/1000, getname_struct_count + getname_bytes_count); + + printf("\n Unallocated: %ld\n", freename_names_count); + printf(" Names: %ld\n", freename_names_count); + printf(" Strings: %ld Kb (%ld bytes)\n", freename_bytes_count/1000, freename_bytes_count); + printf(" Structs: %ld Kb (%ld bytes)\n", freename_struct_count/1000, freename_struct_count); + printf(" Total bytes: %ld Kb (%ld bytes)\n", freename_struct_count/1000 + freename_bytes_count/1000, freename_struct_count + freename_bytes_count); + + printf("\n Total used: %ld Kb (%ld bytes)\n", (getname_struct_count/1000 + getname_bytes_count/1000) - (freename_struct_count/1000 + freename_bytes_count/1000), (getname_struct_count + getname_bytes_count) - (freename_struct_count + freename_bytes_count)); + + printf("\n>>> Other:\n"); + printf( + " Env (%ld): %ld Kb (%ld bytes)\n", + env_alloc_num, + env_alloc_bytes/1000, + env_alloc_bytes + ); + +} +#endif + + parallel = false; + /* If we used the SVR4_MAKE, don't build .DONE or .FAILED */ + if (!getenv(USE_SVR4_MAKE)){ + /* Build the target .DONE or .FAILED if we caught an error */ + if (!quest && !list_all_targets) { + Name failed_name; + + MBSTOWCS(wcs_buffer, ".FAILED"); + failed_name = GETNAME(wcs_buffer, FIND_LENGTH); + if ((exit_status != 0) && (failed_name->prop != NULL)) { + /* + * [tolik] switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; + (void) doname(failed_name, false, true); + } else { + if (!trace_status) { + /* + * Switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; + (void) doname(done, false, true); + } + } + } + } + /* + * Remove the temp file utilities report dependencies thru if it + * is still around + */ + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } + /* + * Do not save the current command in .make.state if make + * was interrupted. + */ + if (current_line != NULL) { + command_changed = true; + current_line->body.line.command_used = NULL; + } + /* + * For each parallel build process running, remove the temp files + * and zap the command line so it won't be put in .make.state + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->temp_file != NULL) { + (void) unlink(rp->temp_file->string_mb); + } + if (rp->stdout_file != NULL) { + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } + if (rp->stderr_file != NULL) { + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } + command_changed = true; +/* + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + */ + } + /* Remove the statefile lock file if the file has been locked */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + (void) unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + /* Write .make.state */ + write_state_file(1, (Boolean) 1); + + job_adjust_fini(); +} + +/* + * handle_interrupt() + * + * This is where C-C traps are caught. + * + * Parameters: + * + * Global variables used (except DMake 1.0): + * current_target Sometimes the current target is removed + * do_not_exec_rule But not if -n is on + * quest or -q + * running_list List of parallel running processes + * touch Current target is not removed if -t on + */ +void +handle_interrupt(int) +{ + Property member; + Running rp; + + (void) fflush(stdout); + if (childPid > 0) { + kill(childPid, SIGTERM); + childPid = -1; + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->pid > 0) { + kill(rp->pid, SIGTERM); + rp->pid = -1; + } + } + if (getpid() == getpgrp()) { + bsd_signal(SIGTERM, SIG_IGN); + kill (-getpid(), SIGTERM); + } + /* Clean up all parallel children already finished */ + finish_children(false); + + /* Make sure the processes running under us terminate first */ + + while (wait((int *) NULL) != -1); + /* Delete the current targets unless they are precious */ + if ((current_target != NULL) && + current_target->is_member && + ((member = get_prop(current_target->prop, member_prop)) != NULL)) { + current_target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + (current_target != NULL) && + !(current_target->stat.is_precious || all_precious)) { + +/* BID_1030811 */ +/* azv 16 Oct 95 */ + current_target->stat.time = file_no_time; + + if (exists(current_target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + current_target->string_mb); + if (current_target->stat.is_dir) { + (void) fprintf(stderr, + gettext("not removed.\n"), + current_target->string_mb); + } else if (unlink(current_target->string_mb) == 0) { + (void) fprintf(stderr, + gettext("removed.\n"), + current_target->string_mb); + } else { + (void) fprintf(stderr, + gettext("could not be removed: %s.\n"), + current_target->string_mb, + errmsg(errno)); + } + } + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->target->is_member && + ((member = get_prop(rp->target->prop, member_prop)) != + NULL)) { + rp->target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + !(rp->target->stat.is_precious || all_precious)) { + + rp->target->stat.time = file_no_time; + if (exists(rp->target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + rp->target->string_mb); + if (rp->target->stat.is_dir) { + (void) fprintf(stderr, + gettext("not removed.\n"), + rp->target->string_mb); + } else if (unlink(rp->target->string_mb) == 0) { + (void) fprintf(stderr, + gettext("removed.\n"), + rp->target->string_mb); + } else { + (void) fprintf(stderr, + gettext("could not be removed: %s.\n"), + rp->target->string_mb, + errmsg(errno)); + } + } + } + } + + + /* Have we locked .make.state or .nse_depinfo? */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + /* + * Re-read .make.state file (it might be changed by recursive make) + */ + check_state(NULL); + + report_dir_enter_leave(false); + + exit_status = 2; + exit(2); +} + +/* + * doalarm(sig, ...) + * + * Handle the alarm interrupt but do nothing. Side effect is to + * cause return from wait3. + * + * Parameters: + * sig + * + * Global variables used: + */ +/*ARGSUSED*/ +static void +doalarm(int) +{ + return; +} + + +/* + * read_command_options(argc, argv) + * + * Scan the cmd line options and process the ones that start with "-" + * + * Return value: + * -M argument, if any + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Global variables used: + */ +static void +read_command_options(register int argc, register char **argv) +{ + register int ch; + int current_optind = 1; + int last_optind_with_double_hyphen = 0; + int last_optind; + int last_current_optind; + register int i; + register int j; + register int k; + register int makefile_next = 0; /* + * flag to note options: + * -c, f, g, j, m, o + */ + const char *tptr; + const char *CMD_OPTS; + + extern char *optarg; + extern int optind, opterr, optopt; + +#define SUNPRO_CMD_OPTS "-~Bbc:Ddef:g:ij:K:kM:m:NnO:o:PpqRrSsTtuVvwx:" + +# define SVR4_CMD_OPTS "-c:ef:g:ij:km:nO:o:pqrsTtVv" + + /* + * Added V in SVR4_CMD_OPTS also, which is going to be a hidden + * option, just to make sure that the getopt doesn't fail when some + * users leave their USE_SVR4_MAKE set and try to use the makefiles + * that are designed to issue commands like $(MAKE) -V. Anyway it + * sets the same flag but ensures that getopt doesn't fail. + */ + + opterr = 0; + optind = 1; + while (1) { + last_optind=optind; /* Save optind and current_optind values */ + last_current_optind=current_optind; /* in case we have to repeat this round. */ + if (svr4) { + CMD_OPTS=SVR4_CMD_OPTS; + ch = getopt(argc, argv, SVR4_CMD_OPTS); + } else { + CMD_OPTS=SUNPRO_CMD_OPTS; + ch = getopt(argc, argv, SUNPRO_CMD_OPTS); + } + if (ch == EOF) { + if(optind < argc) { + /* + * Fixing bug 4102537: + * Strange behaviour of command make using -- option. + * Not all argv have been processed + * Skip non-flag argv and continue processing. + */ + optind++; + current_optind++; + continue; + } else { + break; + } + + } + if (ch == '?') { + if (optopt == '-') { + /* Bug 5060758: getopt() changed behavior (s10_60), + * and now we have to deal with cases when options + * with double hyphen appear here, from -$(MAKEFLAGS) + */ + i = current_optind; + if (argv[i][0] == '-') { + if (argv[i][1] == '-') { + if (argv[i][2] != '\0') { + /* Check if this option is allowed */ + tptr = strchr(CMD_OPTS, argv[i][2]); + if (tptr) { + if (last_optind_with_double_hyphen != current_optind) { + /* This is first time we are trying to fix "--" + * problem with this option. If we come here second + * time, we will go to fatal error. + */ + last_optind_with_double_hyphen = current_optind; + + /* Eliminate first hyphen character */ + for (j=0; argv[i][j] != '\0'; j++) { + argv[i][j] = argv[i][j+1]; + } + + /* Repeat the processing of this argument */ + optind=last_optind; + current_optind=last_current_optind; + continue; + } + } + } + } + } + } + } + + if (ch == '?') { + if (svr4) { + fprintf(stderr, + gettext("Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + gettext(" [ -j dmake_max_jobs ][ -m dmake_mode ][ -o dmake_odir ]...\n")); + fprintf(stderr, + gettext(" [ -e ][ -i ][ -k ][ -n ][ -p ][ -q ][ -r ][ -s ][ -t ][ -v ]\n")); + tptr = strchr(SVR4_CMD_OPTS, optopt); + } else { + fprintf(stderr, + gettext("Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + gettext(" [ -j dmake_max_jobs ][ -K statefile ][ -m dmake_mode ][ -x MODE_NAME=VALUE ][ -o dmake_odir ]...\n")); + fprintf(stderr, + gettext(" [ -d ][ -dd ][ -D ][ -DD ][ -e ][ -i ][ -k ][ -n ][ -p ][ -P ][ -u ][ -w ]\n")); + fprintf(stderr, + gettext(" [ -q ][ -r ][ -s ][ -S ][ -t ][ -v ][ -V ][ target... ][ macro=value... ][ \"macro +=value\"... ]\n")); + tptr = strchr(SUNPRO_CMD_OPTS, optopt); + } + if (!tptr) { + fatal(gettext("Unknown option `-%c'"), optopt); + } else { + fatal(gettext("Missing argument after `-%c'"), optopt); + } + } + + + + makefile_next |= parse_command_option(ch); + /* + * If we're done processing all of the options of + * ONE argument string... + */ + if (current_optind < optind) { + i = current_optind; + k = 0; + /* If there's an argument for an option... */ + if ((optind - current_optind) > 1) { + k = i + 1; + } + switch (makefile_next) { + case 0: + argv[i] = NULL; + /* This shouldn't happen */ + if (k) { + argv[k] = NULL; + } + break; + case 1: /* -f seen */ + argv[i] = (char *)"-f"; + break; + case 2: /* -c seen */ + argv[i] = (char *)"-c"; + break; + case 4: /* -g seen */ + argv[i] = (char *)"-g"; + break; + case 8: /* -j seen */ + argv[i] = (char *)"-j"; + break; + case 16: /* -M seen */ + argv[i] = (char *)"-M"; + break; + case 32: /* -m seen */ + argv[i] = (char *)"-m"; + break; + case 128: /* -O seen */ + argv[i] = (char *)"-O"; + break; + case 256: /* -K seen */ + argv[i] = (char *)"-K"; + break; + case 512: /* -o seen */ + argv[i] = (char *)"-o"; + break; + case 1024: /* -x seen */ + argv[i] = (char *)"-x"; + break; + default: /* > 1 of -c, f, g, j, K, M, m, O, o, x seen */ + fatal(gettext("Illegal command line. More than one option requiring\nan argument given in the same argument group")); + } + + makefile_next = 0; + current_optind = optind; + } + } +} + +static void +quote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + switch (*from) { + case ';': /* End of command */ + case '(': /* Start group */ + case ')': /* End group */ + case '{': /* Start group */ + case '}': /* End group */ + case '[': /* Reg expr - any of a set of chars */ + case ']': /* End of set of chars */ + case '|': /* Pipe or logical-or */ + case '^': /* Old-fashioned pipe */ + case '&': /* Background or logical-and */ + case '<': /* Redirect stdin */ + case '>': /* Redirect stdout */ + case '*': /* Reg expr - any sequence of chars */ + case '?': /* Reg expr - any single char */ + case '$': /* Variable substitution */ + case '\'': /* Singe quote - turn off all magic */ + case '"': /* Double quote - span whitespace */ + case '`': /* Backquote - run a command */ + case '#': /* Comment */ + case ' ': /* Space (for MACRO=value1 value2 */ + case '\\': /* Escape char - turn off magic of next char */ + *to++ = '\\'; + break; + + default: + break; + } + *to++ = *from; + } + *to = '\0'; +} + +static void +unquote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + if (*from == '\\') { + from++; + } + *to++ = *from; + } + *to = '\0'; +} + +/* + * Convert the MAKEFLAGS string value into a vector of char *, similar + * to argv. + */ +static void +setup_makeflags_argv() +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *cp_orig; + Boolean add_hyphen; + int i; + char tmp_char; + + mf_argc = 1; + cp = getenv(makeflags->string_mb); + cp_orig = cp; + + if (cp) { + /* + * If new MAKEFLAGS format, no need to add hyphen. + * If old MAKEFLAGS format, add hyphen before flags. + */ + + if ((strchr(cp, (int) hyphen_char) != NULL) || + (strchr(cp, (int) equal_char) != NULL)) { + + /* New MAKEFLAGS format */ + + add_hyphen = false; + + /* Check if MAKEFLAGS value begins with multiple + * hyphen characters, and remove all duplicates. + * Usually it happens when the next command is + * used: $(MAKE) -$(MAKEFLAGS) + * + * This was a workaround for BugID 5060758, but + * appears to have survived as a fix in make. + */ + while (*cp) { + if (*cp != (int) hyphen_char) { + break; + } + cp++; + if (*cp == (int) hyphen_char) { + /* There are two hyphens. Skip one */ + cp_orig = cp; + cp++; + } + if (!(*cp)) { + /* There are hyphens only. Skip all */ + cp_orig = cp; + break; + } + } + } else { + + /* Old MAKEFLAGS format */ + + add_hyphen = true; + } + } + + /* Find the number of arguments in MAKEFLAGS */ + while (cp && *cp) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + /* Increment arg count */ + mf_argc++; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + } + } + /* Allocate memory for the new MAKEFLAGS argv */ + mf_argv = (char **) malloc((mf_argc + 1) * sizeof(char *)); + mf_argv[0] = (char *)"MAKEFLAGS"; + /* + * Convert the MAKEFLAGS string value into a vector of char *, + * similar to argv. + */ + cp = cp_orig; + for (i = 1; i < mf_argc; i++) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + cp_orig = cp; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + tmp_char = *cp; + *cp = (int) nul_char; + if (add_hyphen) { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + mf_argv[i][0] = '\0'; + (void) strcat(mf_argv[i], "-"); + // (void) strcat(mf_argv[i], cp_orig); + unquote_str(cp_orig, mf_argv[i]+1); + } else { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + //mf_argv[i] = strdup(cp_orig); + unquote_str(cp_orig, mf_argv[i]); + } + *cp = tmp_char; + } + } + mf_argv[i] = NULL; +} + +/* + * parse_command_option(ch) + * + * Parse make command line options. + * + * Return value: + * Indicates if any -f -c or -M were seen + * + * Parameters: + * ch The character to parse + * + * Static variables used: + * dmake_group_specified Set for make -g + * dmake_max_jobs_specified Set for make -j + * dmake_mode_specified Set for make -m + * dmake_add_mode_specified Set for make -x + * dmake_compat_mode_specified Set for make -x SUN_MAKE_COMPAT_MODE= + * dmake_output_mode_specified Set for make -x DMAKE_OUTPUT_MODE= + * dmake_odir_specified Set for make -o + * dmake_rcfile_specified Set for make -c + * env_wins Set for make -e + * ignore_default_mk Set for make -r + * trace_status Set for make -p + * + * Global variables used: + * .make.state path & name set for make -K + * continue_after_error Set for make -k + * debug_level Set for make -d + * do_not_exec_rule Set for make -n + * filter_stderr Set for make -X + * ignore_errors_all Set for make -i + * no_parallel Set for make -R + * quest Set for make -q + * read_trace_level Set for make -D + * report_dependencies Set for make -P + * silent_all Set for make -s + * touch Set for make -t + */ +static int +parse_command_option(register char ch) +{ + static int invert_next = 0; + int invert_this = invert_next; + + invert_next = 0; + switch (ch) { + case '-': /* Ignore "--" */ + return 0; + case '~': /* Invert next option */ + invert_next = 1; + return 0; + case 'B': /* Obsolete */ + return 0; + case 'b': /* Obsolete */ + return 0; + case 'c': /* Read alternative dmakerc file */ + if (invert_this) { + dmake_rcfile_specified = false; + } else { + dmake_rcfile_specified = true; + } + return 2; + case 'D': /* Show lines read */ + if (invert_this) { + read_trace_level--; + } else { + read_trace_level++; + } + return 0; + case 'd': /* Debug flag */ + if (invert_this) { + debug_level--; + } else { + debug_level++; + } + return 0; + case 'e': /* Environment override flag */ + if (invert_this) { + env_wins = false; + } else { + env_wins = true; + } + return 0; + case 'f': /* Read alternative makefile(s) */ + return 1; + case 'g': /* Use alternative DMake group */ + if (invert_this) { + dmake_group_specified = false; + } else { + dmake_group_specified = true; + } + return 4; + case 'i': /* Ignore errors */ + if (invert_this) { + ignore_errors_all = false; + } else { + ignore_errors_all = true; + } + return 0; + case 'j': /* Use alternative DMake max jobs */ + if (invert_this) { + dmake_max_jobs_specified = false; + } else { + dmake_mode_type = parallel_mode; + no_parallel = false; + dmake_max_jobs_specified = true; + } + return 8; + case 'K': /* Read alternative .make.state */ + return 256; + case 'k': /* Keep making even after errors */ + if (invert_this) { + continue_after_error = false; + } else { + continue_after_error = true; + continue_after_error_ever_seen = true; + } + return 0; + case 'M': /* Read alternative make.machines file */ + if (invert_this) { + pmake_machinesfile_specified = false; + } else { + pmake_machinesfile_specified = true; + dmake_mode_type = parallel_mode; + no_parallel = false; + } + return 16; + case 'm': /* Use alternative DMake build mode */ + if (invert_this) { + dmake_mode_specified = false; + } else { + dmake_mode_specified = true; + } + return 32; + case 'x': /* Use alternative DMake mode */ + if (invert_this) { + dmake_add_mode_specified = false; + } else { + dmake_add_mode_specified = true; + } + return 1024; + case 'N': /* Reverse -n */ + if (invert_this) { + do_not_exec_rule = true; + } else { + do_not_exec_rule = false; + } + return 0; + case 'n': /* Print, not exec commands */ + if (invert_this) { + do_not_exec_rule = false; + } else { + do_not_exec_rule = true; + } + return 0; + case 'O': /* Integrate with maketool, obsolete */ + return 0; + case 'o': /* Use alternative dmake output dir */ + if (invert_this) { + dmake_odir_specified = false; + } else { + dmake_odir_specified = true; + } + return 512; + case 'P': /* Print for selected targets */ + if (invert_this) { + report_dependencies_level--; + } else { + report_dependencies_level++; + } + return 0; + case 'p': /* Print description */ + if (invert_this) { + trace_status = false; + do_not_exec_rule = false; + } else { + trace_status = true; + do_not_exec_rule = true; + } + return 0; + case 'q': /* Question flag */ + if (invert_this) { + quest = false; + } else { + quest = true; + } + return 0; + case 'R': /* Don't run in parallel */ + if (invert_this) { + pmake_cap_r_specified = false; + no_parallel = false; + } else { + pmake_cap_r_specified = true; + dmake_mode_type = serial_mode; + no_parallel = true; + } + return 0; + case 'r': /* Turn off internal rules */ + if (invert_this) { + ignore_default_mk = false; + } else { + ignore_default_mk = true; + } + return 0; + case 'S': /* Reverse -k */ + if (invert_this) { + continue_after_error = true; + } else { + continue_after_error = false; + stop_after_error_ever_seen = true; + } + return 0; + case 's': /* Silent flag */ + if (invert_this) { + silent_all = false; + } else { + silent_all = true; + } + return 0; + case 'T': /* Print target list */ + if (invert_this) { + list_all_targets = false; + do_not_exec_rule = false; + } else { + list_all_targets = true; + do_not_exec_rule = true; + } + return 0; + case 't': /* Touch flag */ + if (invert_this) { + touch = false; + } else { + touch = true; + } + return 0; + case 'u': /* Unconditional flag */ + if (invert_this) { + build_unconditional = false; + } else { + build_unconditional = true; + } + return 0; + case 'V': /* SVR4 mode */ + svr4 = true; + return 0; + case 'v': /* Version flag */ + if (invert_this) { + } else { + fprintf(stdout, "%s: %s\n", getprogname(), verstring); + exit_status = 0; + exit(0); + } + return 0; + case 'w': /* Unconditional flag */ + if (invert_this) { + report_cwd = false; + } else { + report_cwd = true; + } + return 0; +#if 0 + case 'X': /* Filter stdout */ + if (invert_this) { + filter_stderr = false; + } else { + filter_stderr = true; + } + return 0; +#endif + default: + break; + } + return 0; +} + +/* + * setup_for_projectdir() + * + * Read the PROJECTDIR variable, if defined, and set the sccs path + * + * Parameters: + * + * Global variables used: + * sccs_dir_path Set to point to SCCS dir to use + */ +static void +setup_for_projectdir(void) +{ +static char path[MAXPATHLEN]; +char cwdpath[MAXPATHLEN]; +uid_t uid; +int done=0; + + /* Check if we should use PROJECTDIR when reading the SCCS dir. */ + sccs_dir_path = getenv("PROJECTDIR"); + if ((sccs_dir_path != NULL) && + (sccs_dir_path[0] != (int) slash_char)) { + struct passwd *pwent; + + { + uid = getuid(); + pwent = getpwuid(uid); + if (pwent == NULL) { + fatal(gettext("Bogus USERID ")); + } + if ((pwent = getpwnam(sccs_dir_path)) == NULL) { + /*empty block : it'll go & check cwd */ + } + else { + (void) sprintf(path, "%s/src", pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + (void) sprintf(path, "%s/source", pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } + } + } + if (!done) { + if (getcwd(cwdpath, MAXPATHLEN - 1 )) { + + (void) sprintf(path, "%s/%s", cwdpath,sccs_dir_path); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + fatal(gettext("Bogus PROJECTDIR '%s'"), sccs_dir_path); + } + } + } + } + } +} + +char * +make_install_prefix(void) +{ + int ret; + char origin[PATH_MAX]; + char *dir; + + if ((ret = readlink("/proc/self/path/a.out", origin, + PATH_MAX - 1)) < 0) + fatal("failed to read origin from /proc\n"); + + + origin[ret] = '\0'; + return strdup(dirname(origin)); +} + +static char * +add_to_env(const char *var, const char *value, const char *fallback) +{ + const char *oldpath; + char *newpath; + + oldpath = getenv(var); + if (oldpath == NULL) { + if (value != NULL) { + asprintf(&newpath, "%s=%s", + var, value); + } else { + asprintf(&newpath, "%s=%s", + var, fallback); + } + } else { + if (value != NULL) { + asprintf(&newpath, "%s=%s:%s", + var, oldpath, value); + } else { + asprintf(&newpath, "%s=%s:%s", + var, oldpath, fallback); + } + } + + return (newpath); +} + +/* + * set_sgs_support() + * + * Add the libmakestate.so.1 lib to the env var SGS_SUPPORT + * if it's not already in there. + * The SGS_SUPPORT env var and libmakestate.so.1 is used by + * the linker ld to report .make.state info back to make. + * + * In the new world we always will set the 32-bit and 64-bit versions of this + * variable explicitly so that we can take into account the correct isa and our + * prefix. So say that the prefix was /opt/local. Then we would want to search + * /opt/local/lib/libmakestate.so.1:libmakestate.so.1. We still want to search + * the original location just as a safety measure. + */ +static void +set_sgs_support() +{ + int len; + char *newpath, *newpath64; + char *lib32, *lib64; + static char *prev_path, *prev_path64; + char *origin = make_install_prefix(); + struct stat st; + + asprintf(&lib32, "%s/%s/%s", origin, "../lib", + LD_SUPPORT_MAKE_LIB); + + if (stat(lib32, &st) != 0) { + free(lib32); + // Try the tools path + asprintf(&lib32, "%s/%s/%s/%s", origin, "../../lib/", + LD_SUPPORT_MAKE_ARCH, LD_SUPPORT_MAKE_LIB); + + if (stat(lib32, &st) != 0) { + free(lib32); + lib32 = NULL; + } + } + + asprintf(&lib64, "%s/%s/64/%s", origin, "../lib", + LD_SUPPORT_MAKE_LIB); + + if (stat(lib64, &st) != 0) { + free(lib64); + // Try the tools path + asprintf(&lib64, "%s/%s/%s/64/%s", origin, "../../lib/", + LD_SUPPORT_MAKE_ARCH, LD_SUPPORT_MAKE_LIB); + + if (stat(lib64, &st) != 0) { + free(lib64); + lib64 = NULL; + } + } + + newpath = add_to_env(LD_SUPPORT_ENV_VAR_32, lib32, LD_SUPPORT_MAKE_LIB); + newpath64 = add_to_env(LD_SUPPORT_ENV_VAR_64, lib64, LD_SUPPORT_MAKE_LIB); + + putenv(newpath); + if (prev_path) { + free(prev_path); + } + prev_path = newpath; + + putenv(newpath64); + if (prev_path64) { + free(prev_path64); + } + prev_path64 = newpath64; + free(lib32); + free(lib64); + free(origin); +} + +/* + * read_files_and_state(argc, argv) + * + * Read the makefiles we care about and the environment + * Also read the = style command line options + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * env_wins make -e, determines if env vars are RO + * ignore_default_mk make -r, determines if make.rules is read + * not_auto_depen dwight + * + * Global variables used: + * default_target_to_build Set to first proper target from file + * do_not_exec_rule Set to false when makfile is made + * dot The Name ".", used to read current dir + * empty_name The Name "", use as macro value + * keep_state Set if KEEP_STATE is in environment + * make_state The Name ".make.state", used to read file + * makefile_type Set to type of file being read + * makeflags The Name "MAKEFLAGS", used to set macro value + * not_auto dwight + * read_trace_level Checked to se if the reader should trace + * report_dependencies If -P is on we do not read .make.state + * trace_reader Set if reader should trace + * virtual_root The Name "VIRTUAL_ROOT", used to check value + */ +static void +read_files_and_state(int argc, char **argv) +{ + wchar_t buffer[1000]; + wchar_t buffer_posix[1000]; + register char ch; + register char *cp; + Property def_make_macro = NULL; + Name def_make_name; + Name default_makefile; + String_rec dest; + wchar_t destbuffer[STRING_BUFFER_LENGTH]; + register int i; + register int j; + Name keep_state_name; + int length; + Name Makefile; + register Property macro; + struct stat make_state_stat; + Name makefile_name; + register int makefile_next = 0; + register Boolean makefile_read = false; + String_rec makeflags_string; + String_rec makeflags_string_posix; + String_rec * makeflags_string_current; + Name makeflags_value_saved; + register Name name; + Name new_make_value; + Boolean save_do_not_exec_rule; + Name sdotMakefile; + Name sdotmakefile_name; + static wchar_t state_file_str; + static char state_file_str_mb[MAXPATHLEN]; + static struct _Name state_filename; + Boolean temp; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + ASCII_Dyn_Array makeflags_and_macro; + Boolean is_xpg4; + +/* + * Remember current mode. It may be changed after reading makefile + * and we will have to correct MAKEFLAGS variable. + */ + is_xpg4 = posix; + + MBSTOWCS(wcs_buffer, "KEEP_STATE"); + keep_state_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "Makefile"); + Makefile = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "makefile"); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "s.makefile"); + sdotmakefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "s.Makefile"); + sdotMakefile = GETNAME(wcs_buffer, FIND_LENGTH); + +/* + * initialize global dependency entry for .NOT_AUTO + */ + not_auto_depen->next = NULL; + not_auto_depen->name = not_auto; + not_auto_depen->automatic = not_auto_depen->stale = false; + +/* + * Read internal definitions and rules. + */ + if (read_trace_level > 1) { + trace_reader = true; + } + if (!ignore_default_mk) { + if (svr4) { + MBSTOWCS(wcs_buffer, "svr4.make.rules"); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } else { + MBSTOWCS(wcs_buffer, "make.rules"); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } + default_makefile->stat.is_file = true; + + (void) read_makefile(default_makefile, + true, + false, + true); + } + + /* + * If the user did not redefine the MAKE macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of MAKE to be some form + * of argv[0] for recursive MAKE builds. + */ + MBSTOWCS(wcs_buffer, "MAKE"); + def_make_name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + def_make_macro = get_prop(def_make_name->prop, macro_prop); + if ((def_make_macro != NULL) && + (IS_EQUAL(def_make_macro->body.macro.value->string_mb, + "make"))) { + MBSTOWCS(wcs_buffer, argv_zero_string); + new_make_value = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + (void) SETVAR(def_make_name, + new_make_value, + false); + } + + default_target_to_build = NULL; + trace_reader = false; + +/* + * Read environment args. Let file args which follow override unless + * -e option seen. If -e option is not mentioned. + */ + read_environment(env_wins); + if (getvar(virtual_root)->hash.length == 0) { + maybe_append_prop(virtual_root, macro_prop) + ->body.macro.exported = true; + MBSTOWCS(wcs_buffer, "/"); + (void) SETVAR(virtual_root, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + +/* + * We now scan mf_argv and argv to see if we need to set + * any of the DMake-added options/variables in MAKEFLAGS. + */ + + makeflags_and_macro.start = 0; + makeflags_and_macro.size = 0; + enter_argv_values(mf_argc, mf_argv, &makeflags_and_macro); + enter_argv_values(argc, argv, &makeflags_and_macro); + +/* + * Set MFLAGS and MAKEFLAGS + * + * Before reading makefile we do not know exactly which mode + * (posix or not) is used. So prepare two MAKEFLAGS strings + * for both posix and solaris modes because they are different. + */ + INIT_STRING_FROM_STACK(makeflags_string, buffer); + INIT_STRING_FROM_STACK(makeflags_string_posix, buffer_posix); + append_char((int) hyphen_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string_posix); + + switch (read_trace_level) { + case 2: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + case 1: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + } + switch (debug_level) { + case 2: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + case 1: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + } + if (env_wins) { + append_char('e', &makeflags_string); + append_char('e', &makeflags_string_posix); + } + if (ignore_errors_all) { + append_char('i', &makeflags_string); + append_char('i', &makeflags_string_posix); + } + if (continue_after_error) { + if (stop_after_error_ever_seen) { + append_char('S', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + } + append_char('k', &makeflags_string); + append_char('k', &makeflags_string_posix); + } else { + if (stop_after_error_ever_seen + && continue_after_error_ever_seen) { + append_char('k', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('S', &makeflags_string_posix); + } + } + if (do_not_exec_rule) { + append_char('n', &makeflags_string); + append_char('n', &makeflags_string_posix); + } + switch (report_dependencies_level) { + case 4: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 3: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 2: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 1: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + } + if (trace_status) { + append_char('p', &makeflags_string); + append_char('p', &makeflags_string_posix); + } + if (quest) { + append_char('q', &makeflags_string); + append_char('q', &makeflags_string_posix); + } + if (silent_all) { + append_char('s', &makeflags_string); + append_char('s', &makeflags_string_posix); + } + if (touch) { + append_char('t', &makeflags_string); + append_char('t', &makeflags_string_posix); + } + if (build_unconditional) { + append_char('u', &makeflags_string); + append_char('u', &makeflags_string_posix); + } + if (report_cwd) { + append_char('w', &makeflags_string); + append_char('w', &makeflags_string_posix); + } + /* -c dmake_rcfile */ + if (dmake_rcfile_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_RCFILE"); + dmake_rcfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_rcfile, &makeflags_string); + append_makeflags_string(dmake_rcfile, &makeflags_string_posix); + } + /* -g dmake_group */ + if (dmake_group_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_GROUP"); + dmake_group = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_group, &makeflags_string); + append_makeflags_string(dmake_group, &makeflags_string_posix); + } + /* -j dmake_max_jobs */ + if (dmake_max_jobs_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + dmake_max_jobs = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_max_jobs, &makeflags_string); + append_makeflags_string(dmake_max_jobs, &makeflags_string_posix); + } + /* -m dmake_mode */ + if (dmake_mode_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + dmake_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_mode, &makeflags_string); + append_makeflags_string(dmake_mode, &makeflags_string_posix); + } + /* -x dmake_compat_mode */ +// if (dmake_compat_mode_specified) { +// MBSTOWCS(wcs_buffer, "SUN_MAKE_COMPAT_MODE"); +// dmake_compat_mode = GETNAME(wcs_buffer, FIND_LENGTH); +// append_makeflags_string(dmake_compat_mode, &makeflags_string); +// append_makeflags_string(dmake_compat_mode, &makeflags_string_posix); +// } + /* -x dmake_output_mode */ + if (dmake_output_mode_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + dmake_output_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_output_mode, &makeflags_string); + append_makeflags_string(dmake_output_mode, &makeflags_string_posix); + } + /* -o dmake_odir */ + if (dmake_odir_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_ODIR"); + dmake_odir = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_odir, &makeflags_string); + append_makeflags_string(dmake_odir, &makeflags_string_posix); + } + /* -M pmake_machinesfile */ + if (pmake_machinesfile_specified) { + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + pmake_machinesfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(pmake_machinesfile, &makeflags_string); + append_makeflags_string(pmake_machinesfile, &makeflags_string_posix); + } + /* -R */ + if (pmake_cap_r_specified) { + append_char((int) space_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string); + append_char('R', &makeflags_string); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('R', &makeflags_string_posix); + } + +/* + * Make sure MAKEFLAGS is exported + */ + maybe_append_prop(makeflags, macro_prop)-> + body.macro.exported = true; + + if (makeflags_string.buffer.start[1] != (int) nul_char) { + if (makeflags_string.buffer.start[1] != (int) space_char) { + MBSTOWCS(wcs_buffer, "MFLAGS"); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start, + FIND_LENGTH), + false); + } else { + MBSTOWCS(wcs_buffer, "MFLAGS"); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start + 2, + FIND_LENGTH), + false); + } + } + +/* + * Add command line macro to POSIX makeflags_string + */ + if (makeflags_and_macro.start) { + tmp_char = (char) space_char; + cp = makeflags_and_macro.start; + do { + append_char(tmp_char, &makeflags_string_posix); + } while ( tmp_char = *cp++ ); + retmem_mb(makeflags_and_macro.start); + } + +/* + * Now set the value of MAKEFLAGS macro in accordance + * with current mode. + */ + macro = maybe_append_prop(makeflags, macro_prop); + temp = (Boolean) macro->body.macro.read_only; + macro->body.macro.read_only = false; + if(posix || gnu_style) { + makeflags_string_current = &makeflags_string_posix; + } else { + makeflags_string_current = &makeflags_string; + } + if (makeflags_string_current->buffer.start[1] == (int) nul_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 1 + , FIND_LENGTH + ); + } else { + if (makeflags_string_current->buffer.start[1] != (int) space_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + , FIND_LENGTH + ); + } else { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 2 + , FIND_LENGTH + ); + } + } + (void) SETVAR( makeflags + , makeflags_value_saved + , false + ); + macro->body.macro.read_only = temp; + +/* + * Read command line "-f" arguments and ignore -c, g, j, K, M, m, O and o args. + */ + save_do_not_exec_rule = do_not_exec_rule; + do_not_exec_rule = false; + if (read_trace_level > 0) { + trace_reader = true; + } + + for (i = 1; i < argc; i++) { + if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'f') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; /* Remove -f */ + if (i >= argc - 1) { + fatal(gettext("No filename argument after -f flag")); + } + MBSTOWCS(wcs_buffer, argv[++i]); + primary_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + (void) read_makefile(primary_makefile, true, true, true); + argv[i] = NULL; /* Remove filename */ + makefile_read = true; + } else if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'c' || + argv[i][1] == 'g' || + argv[i][1] == 'j' || + argv[i][1] == 'K' || + argv[i][1] == 'M' || + argv[i][1] == 'm' || + argv[i][1] == 'O' || + argv[i][1] == 'o') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; + argv[++i] = NULL; + } + } + +/* + * If no command line "-f" args then look for "makefile", and then for + * "Makefile" if "makefile" isn't found. + */ + if (!makefile_read) { + (void) read_dir(dot, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); + if (!posix) { + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(gettext("Both `makefile' and `Makefile' exist")); + } + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } else { + + enum sccs_stat save_m_has_sccs = NO_SCCS; + enum sccs_stat save_M_has_sccs = NO_SCCS; + + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(gettext("Both `makefile' and `Makefile' exist")); + } + } + if (makefile_name->stat.is_file) { + if (makefile_name->stat.has_sccs == NO_SCCS) { + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } else { + save_m_has_sccs = makefile_name->stat.has_sccs; + makefile_name->stat.has_sccs = NO_SCCS; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + } + if (!makefile_read && + Makefile->stat.is_file) { + if (Makefile->stat.has_sccs == NO_SCCS) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } else { + save_M_has_sccs = Makefile->stat.has_sccs; + Makefile->stat.has_sccs = NO_SCCS; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + if (!makefile_read && + makefile_name->stat.is_file) { + makefile_name->stat.has_sccs = save_m_has_sccs; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + Makefile->stat.has_sccs = save_M_has_sccs; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + } + do_not_exec_rule = save_do_not_exec_rule; + allrules_read = makefile_read; + trace_reader = false; + +/* + * Now get current value of MAKEFLAGS and compare it with + * the saved value we set before reading makefile. + * If they are different then MAKEFLAGS is subsequently set by + * makefile, just leave it there. Otherwise, if make mode + * is changed by using .POSIX target in makefile we need + * to correct MAKEFLAGS value. + */ + Name mf_val = getvar(makeflags); + if( (posix != is_xpg4) + && (!strcmp(mf_val->string_mb, makeflags_value_saved->string_mb))) + { + if (makeflags_string_posix.buffer.start[1] == (int) nul_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 1, + FIND_LENGTH), + false); + } else { + if (makeflags_string_posix.buffer.start[1] != (int) space_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start, + FIND_LENGTH), + false); + } else { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 2, + FIND_LENGTH), + false); + } + } + } + + if (makeflags_string.free_after_use) { + retmem(makeflags_string.buffer.start); + } + if (makeflags_string_posix.free_after_use) { + retmem(makeflags_string_posix.buffer.start); + } + makeflags_string.buffer.start = NULL; + makeflags_string_posix.buffer.start = NULL; + + if (posix) { + /* + * If the user did not redefine the ARFLAGS macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of ARFLAGS to be in accordance + * with "POSIX" requirements. + */ + MBSTOWCS(wcs_buffer, "ARFLAGS"); + name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + macro = get_prop(name->prop, macro_prop); + if ((macro != NULL) && /* Maybe (macro == NULL) || ? */ + (IS_EQUAL(macro->body.macro.value->string_mb, + "rv"))) { + MBSTOWCS(wcs_buffer, "-rv"); + value = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + (void) SETVAR(name, + value, + false); + } + } + + if (!posix && !svr4) { + set_sgs_support(); + } + + +/* + * Make sure KEEP_STATE is in the environment if KEEP_STATE is on. + */ + macro = get_prop(keep_state_name->prop, macro_prop); + if ((macro != NULL) && + macro->body.macro.exported) { + keep_state = true; + } + if (keep_state) { + if (macro == NULL) { + macro = maybe_append_prop(keep_state_name, + macro_prop); + } + macro->body.macro.exported = true; + (void) SETVAR(keep_state_name, + empty_name, + false); + + /* + * Read state file + */ + + /* Before we read state, let's make sure we have + ** right state file. + */ + /* just in case macro references are used in make_state file + ** name, we better expand them at this stage using expand_value. + */ + INIT_STRING_FROM_STACK(dest, destbuffer); + expand_value(make_state, &dest, false); + + make_state = GETNAME(dest.buffer.start, FIND_LENGTH); + + if(!stat(make_state->string_mb, &make_state_stat)) { + if(!(make_state_stat.st_mode & S_IFREG) ) { + /* copy the make_state structure to the other + ** and then let make_state point to the new + ** one. + */ + memcpy(&state_filename, make_state,sizeof(state_filename)); + state_filename.string_mb = state_file_str_mb; + /* Just a kludge to avoid two slashes back to back */ + if((make_state->hash.length == 1)&& + (make_state->string_mb[0] == '/')) { + make_state->hash.length = 0; + make_state->string_mb[0] = '\0'; + } + sprintf(state_file_str_mb,"%s%s", + make_state->string_mb,"/.make.state"); + make_state = &state_filename; + /* adjust the length to reflect the appended string */ + make_state->hash.length += 12; + } + } else { /* the file doesn't exist or no permission */ + char tmp_path[MAXPATHLEN]; + char *slashp; + + if (slashp = strrchr(make_state->string_mb, '/')) { + strncpy(tmp_path, make_state->string_mb, + (slashp - make_state->string_mb)); + tmp_path[slashp - make_state->string_mb]=0; + if(strlen(tmp_path)) { + if(stat(tmp_path, &make_state_stat)) { + warning(gettext("directory %s for .KEEP_STATE_FILE does not exist"),tmp_path); + } + if (access(tmp_path, F_OK) != 0) { + warning(gettext("can't access dir %s"),tmp_path); + } + } + } + } + if (report_dependencies_level != 1) { + Makefile_type makefile_type_temp = makefile_type; + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = makefile_type_temp; + } + } +} + +/* + * Scan the argv for options and "=" type args and make them readonly. + */ +static void +enter_argv_values(int argc, char *argv[], ASCII_Dyn_Array *makeflags_and_macro) +{ + register char *cp; + register int i; + int length; + register Name name; + int opt_separator = argc; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + Boolean append = false; + Property macro; + struct stat statbuf; + + + /* Read argv options and "=" type args and make them readonly. */ + makefile_type = reading_nothing; + for (i = 1; i < argc; ++i) { + append = false; + if (argv[i] == NULL) { + continue; + } else if (((argv[i][0] == '-') && (argv[i][1] == '-')) || + ((argv[i][0] == (int) ' ') && + (argv[i][1] == (int) '-') && + (argv[i][2] == (int) ' ') && + (argv[i][3] == (int) '-'))) { + argv[i] = NULL; + opt_separator = i; + continue; + } else if ((i < opt_separator) && (argv[i][0] == (int) hyphen_char)) { + switch (parse_command_option(argv[i][1])) { + case 1: /* -f seen */ + ++i; + continue; + case 2: /* -c seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake rcfile argument after -c flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_RCFILE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 4: /* -g seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake group argument after -g flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_GROUP"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 8: /* -j seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake max jobs argument after -j flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 16: /* -M seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No pmake machinesfile argument after -M flag")); + } + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 32: /* -m seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake mode argument after -m flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 256: /* -K seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No makestate filename argument after -K flag")); + } + MBSTOWCS(wcs_buffer, argv[i+1]); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + keep_state = true; + argv[i] = NULL; + argv[i+1] = NULL; + continue; + case 512: /* -o seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake output dir argument after -o flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_ODIR"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 1024: /* -x seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No argument after -x flag")); + } + length = strlen( "SUN_MAKE_COMPAT_MODE="); + if (strncmp(argv[i+1], "SUN_MAKE_COMPAT_MODE=", length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, "SUN_MAKE_COMPAT_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_compat_mode_specified = dmake_add_mode_specified; + break; + } + length = strlen( "DMAKE_OUTPUT_MODE="); + if (strncmp(argv[i+1], "DMAKE_OUTPUT_MODE=", length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_output_mode_specified = dmake_add_mode_specified; + } else { + warning(gettext("Unknown argument `%s' after -x flag (ignored)"), + argv[i+1]); + argv[i] = argv[i + 1] = NULL; + continue; + } + break; + default: /* Shouldn't reach here */ + argv[i] = NULL; + continue; + } + argv[i] = NULL; + if (i == (argc - 1)) { + break; + } + if ((length = strlen(argv[i+1])) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, argv[i+1], length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, argv[i+1]); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i+1] = NULL; + } else if ((cp = strchr(argv[i], (int) equal_char)) != NULL) { +/* + * Combine all macro in dynamic array + */ + if(*(cp-1) == (int) plus_char) + { + if(isspace(*(cp-2))) { + append = true; + cp--; + } + } + if(!append) + append_or_replace_macro_in_dyn_array(makeflags_and_macro, argv[i]); + + while (isspace(*(cp-1))) { + cp--; + } + tmp_char = *cp; + *cp = (int) nul_char; + MBSTOWCS(wcs_buffer, argv[i]); + *cp = tmp_char; + name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + while (*cp != (int) equal_char) { + cp++; + } + cp++; + while (isspace(*cp) && (*cp != (int) nul_char)) { + cp++; + } + if ((length = strlen(cp)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, cp, length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, cp); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i] = NULL; + } else { + /* Illegal MAKEFLAGS argument */ + continue; + } + if(append) { + setvar_append(name, value); + append = false; + } else { + macro = maybe_append_prop(name, macro_prop); + macro->body.macro.exported = true; + SETVAR(name, value, false)->body.macro.read_only = true; + } + } +} + +/* + * Append the DMake option and value to the MAKEFLAGS string. + */ +static void +append_makeflags_string(Name name, register String makeflags_string) +{ + const char *option; + + if (strcmp(name->string_mb, "DMAKE_GROUP") == 0) { + option = " -g "; + } else if (strcmp(name->string_mb, "DMAKE_MAX_JOBS") == 0) { + option = " -j "; + } else if (strcmp(name->string_mb, "DMAKE_MODE") == 0) { + option = " -m "; + } else if (strcmp(name->string_mb, "DMAKE_ODIR") == 0) { + option = " -o "; + } else if (strcmp(name->string_mb, "DMAKE_RCFILE") == 0) { + option = " -c "; + } else if (strcmp(name->string_mb, "PMAKE_MACHINESFILE") == 0) { + option = " -M "; + } else if (strcmp(name->string_mb, "DMAKE_OUTPUT_MODE") == 0) { + option = " -x DMAKE_OUTPUT_MODE="; + } else if (strcmp(name->string_mb, "SUN_MAKE_COMPAT_MODE") == 0) { + option = " -x SUN_MAKE_COMPAT_MODE="; + } else { + fatal(gettext("Internal error: name not recognized in append_makeflags_string()")); + } + Property prop = maybe_append_prop(name, macro_prop); + if( prop == 0 || prop->body.macro.value == 0 || + prop->body.macro.value->string_mb == 0 ) { + return; + } + char mbs_value[MAXPATHLEN + 100]; + strcpy(mbs_value, option); + strcat(mbs_value, prop->body.macro.value->string_mb); + MBSTOWCS(wcs_buffer, mbs_value); + append_string(wcs_buffer, makeflags_string, FIND_LENGTH); +} + +/* + * read_environment(read_only) + * + * This routine reads the process environment when make starts and enters + * it as make macros. The environment variable SHELL is ignored. + * + * Parameters: + * read_only Should we make env vars read only? + * + * Global variables used: + * report_pwd Set if this make was started by other make + */ +static void +read_environment(Boolean read_only) +{ + register char **environment; + int length; + wchar_t *tmp_wcs_buffer; + Boolean alloced_tmp_wcs_buffer = false; + register wchar_t *name; + register wchar_t *value; + register Name macro; + Property val; + Boolean read_only_saved; + + reading_environment = true; + environment = environ; + for (; *environment; environment++) { + read_only_saved = read_only; + if ((length = strlen(*environment)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + alloced_tmp_wcs_buffer = true; + (void) mbstowcs(tmp_wcs_buffer, *environment, length + 1); + name = tmp_wcs_buffer; + } else { + MBSTOWCS(wcs_buffer, *environment); + name = wcs_buffer; + } + value = (wchar_t *) wcschr(name, (int) equal_char); + + /* + * Looks like there's a bug in the system, but sometimes + * you can get blank lines in *environment. + */ + if (!value) { + continue; + } + MBSTOWCS(wcs_buffer2, "SHELL="); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + continue; + } + MBSTOWCS(wcs_buffer2, "MAKEFLAGS="); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + report_pwd = true; + /* + * In POSIX mode we do not want MAKEFLAGS to be readonly. + * If the MAKEFLAGS macro is subsequently set by the makefile, + * it replaces the MAKEFLAGS variable currently found in the + * environment. + * See Assertion 50 in section 6.2.5.3 of standard P1003.3.2/D8. + */ + if(posix) { + read_only_saved = false; + } + } + + /* + * We ignore SUNPRO_DEPENDENCIES. This environment variable is + * set by make and read by cpp which then writes info to + * .make.dependency.xxx. When make is invoked by another make + * (recursive make), we don't want to read this because then + * the child make will end up writing to the parent + * directory's .make.state and clobbering them. + */ + MBSTOWCS(wcs_buffer2, "SUNPRO_DEPENDENCIES"); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + continue; + } + + macro = GETNAME(name, value - name); + maybe_append_prop(macro, macro_prop)->body.macro.exported = + true; + if ((value == NULL) || ((value + 1)[0] == (int) nul_char)) { + val = setvar_daemon(macro, + (Name) NULL, + false, no_daemon, false, debug_level); + } else { + val = setvar_daemon(macro, + GETNAME(value + 1, FIND_LENGTH), + false, no_daemon, false, debug_level); + } + val->body.macro.read_only = read_only_saved; + if (alloced_tmp_wcs_buffer) { + retmem(tmp_wcs_buffer); + alloced_tmp_wcs_buffer = false; + } + } + reading_environment = false; +} + +/* + * read_makefile(makefile, complain, must_exist, report_file) + * + * Read one makefile and check the result + * + * Return value: + * false is the read failed + * + * Parameters: + * makefile The file to read + * complain Passed thru to read_simple_file() + * must_exist Passed thru to read_simple_file() + * report_file Passed thru to read_simple_file() + * + * Global variables used: + * makefile_type Set to indicate we are reading main file + * recursion_level Initialized + */ +static Boolean +read_makefile(register Name makefile, Boolean complain, Boolean must_exist, Boolean report_file) +{ + Boolean b; + + makefile_type = reading_makefile; + recursion_level = 0; + reading_dependencies = true; + b = read_simple_file(makefile, true, true, complain, + must_exist, report_file, false); + reading_dependencies = false; + return b; +} + +/* + * make_targets(argc, argv, parallel_flag) + * + * Call doname on the specified targets + * + * Parameters: + * argc You know what this is + * argv You know what this is + * parallel_flag True if building in parallel + * + * Global variables used: + * build_failed_seen Used to generated message after failed -k + * commands_done Used to generate message "Up to date" + * default_target_to_build First proper target in makefile + * init The Name ".INIT", use to run command + * parallel Global parallel building flag + * quest make -q, suppresses messages + * recursion_level Initialized, used for tracing + * report_dependencies make -P, regroves whole process + */ +static void +make_targets(int argc, char **argv, Boolean parallel_flag) +{ + int i; + char *cp; + Doname result; + register Boolean target_to_make_found = false; + + (void) doname(init, true, true); + recursion_level = 1; + parallel = parallel_flag; +/* + * make remaining args + */ +/* + if ((report_dependencies_level == 0) && parallel) { + */ + if (parallel) { + /* + * If building targets in parallel, start all of the + * remaining args to build in parallel. + */ + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + commands_done = false; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + //default_target_to_build = GETNAME(wcs_buffer, + // FIND_LENGTH); + default_target_to_build = normalize_name(wcs_buffer, + wcslen(wcs_buffer)); + if (default_target_to_build == wait_name) { + if (parallel_process_cnt > 0) { + finish_running(); + } + continue; + } + top_level_target = get_wstring(default_target_to_build->string_mb); + /* + * If we can't execute the current target in + * parallel, hold off the target processing + * to preserve the order of the targets as they appeared + * in command line. + */ + if (!parallel_ok(default_target_to_build, false) + && parallel_process_cnt > 0) { + finish_running(); + } + result = doname_check(default_target_to_build, + true, + false, + false); + gather_recursive_deps(); + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + default_target_to_build->stat.time = file_no_time; + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + /* Now wait for all of the targets to finish running */ + finish_running(); + // setjmp(jmpbuffer); + + } + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + target_to_make_found = true; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + default_target_to_build = normalize_name(wcs_buffer, wcslen(wcs_buffer)); + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + commands_done = false; + if (parallel) { + result = (Doname) default_target_to_build->state; + } else { + result = doname_check(default_target_to_build, + true, + false, + false); + } + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(gettext("Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build->prop, + line_prop)); + } + default_target_to_build->stat.time = + file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = + build_dont_know; + } + if (!parallel && + /* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + +/* + * If no file arguments have been encountered, + * make the first name encountered that doesnt start with a dot + */ + if (!target_to_make_found) { + if (default_target_to_build == NULL) { + fatal(gettext("No arguments to build")); + } + commands_done = false; + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + + + if (getenv("SPRO_EXPAND_ERRORS")){ + (void) printf("::(%s)\n", + default_target_to_build->string_mb); + } + + + result = doname_parallel(default_target_to_build, true, false); + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(gettext("Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build-> + prop, + line_prop)); + } + default_target_to_build->stat.time = file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = build_dont_know; + } + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } +} + +/* + * report_recursion(target) + * + * If this is a recursive make and the parent make has KEEP_STATE on + * this routine reports the dependency to the parent make + * + * Parameters: + * target Target to report + * + * Global variables used: + * makefiles_used List of makefiles read + * recursive_name The Name ".RECURSIVE", printed + * report_dependency dwight + */ +static void +report_recursion(register Name target) +{ + register FILE *report_file = get_report_file(); + + if ((report_file == NULL) || (report_file == (FILE*)-1)) { + return; + } + if (primary_makefile == NULL) { + /* + * This can happen when there is no makefile and + * only implicit rules are being used. + */ + return; + } + (void) fprintf(report_file, + "%s: %s ", + get_target_being_reported_for(), + recursive_name->string_mb); + report_dependency(get_current_path()); + report_dependency(target->string_mb); + report_dependency(primary_makefile->string_mb); + (void) fprintf(report_file, "\n"); +} + +/* Next function "append_or_replace_macro_in_dyn_array" must be in "misc.cc". */ +/* NIKMOL */ +extern void +append_or_replace_macro_in_dyn_array(ASCII_Dyn_Array *Ar, char *macro) +{ + register char *cp0; /* work pointer in macro */ + register char *cp1; /* work pointer in array */ + register char *cp2; /* work pointer in array */ + register char *cp3; /* work pointer in array */ + register char *name; /* macro name */ + register char *value; /* macro value */ + register int len_array; + register int len_macro; + + char * esc_value = NULL; + int esc_len; + + if (!(len_macro = strlen(macro))) return; + name = macro; + while (isspace(*(name))) { + name++; + } + if (!(value = strchr(name, (int) equal_char))) { + /* no '=' in macro */ + goto ERROR_MACRO; + } + cp0 = value; + value++; + while (isspace(*(value))) { + value++; + } + while (isspace(*(cp0-1))) { + cp0--; + } + if (cp0 <= name) goto ERROR_MACRO; /* no name */ + if (!(Ar->size)) goto ALLOC_ARRAY; + cp1 = Ar->start; + +LOOK_FOR_NAME: + if (!(cp1 = strchr(cp1, name[0]))) goto APPEND_MACRO; + if (!(cp2 = strchr(cp1, (int) equal_char))) goto APPEND_MACRO; + if (strncmp(cp1, name, (size_t)(cp0-name))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + if (cp1 != Ar->start) { + if (!isspace(*(cp1-1))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + } + for (cp3 = cp1 + (cp0-name); cp3 < cp2; cp3++) { + if (isspace(*cp3)) continue; + /* else: another name */ + cp1++; + goto LOOK_FOR_NAME; + } + /* Look for the next macro name in array */ + cp3 = cp2+1; + if (*cp3 != (int) doublequote_char) { + /* internal error */ + goto ERROR_MACRO; + } + if (!(cp3 = strchr(cp3+1, (int) doublequote_char))) { + /* internal error */ + goto ERROR_MACRO; + } + cp3++; + while (isspace(*cp3)) { + cp3++; + } + + cp2 = cp1; /* remove old macro */ + if ((*cp3) && (cp3 < Ar->start + Ar->size)) { + for (; cp3 < Ar->start + Ar->size; cp3++) { + *cp2++ = *cp3; + } + } + for (; cp2 < Ar->start + Ar->size; cp2++) { + *cp2 = 0; + } + if (*cp1) { + /* check next name */ + goto LOOK_FOR_NAME; + } + goto APPEND_MACRO; + +ALLOC_ARRAY: + if (Ar->size) { + cp1 = Ar->start; + } else { + cp1 = 0; + } + Ar->size += 128; + Ar->start = getmem(Ar->size); + for (len_array=0; len_array < Ar->size; len_array++) { + Ar->start[len_array] = 0; + } + if (cp1) { + strcpy(Ar->start, cp1); + retmem((wchar_t *) cp1); + } + +APPEND_MACRO: + len_array = strlen(Ar->start); + esc_value = (char*)malloc(strlen(value)*2 + 1); + quote_str(value, esc_value); + esc_len = strlen(esc_value) - strlen(value); + if (len_array + len_macro + esc_len + 5 >= Ar->size) goto ALLOC_ARRAY; + strcat(Ar->start, " "); + strncat(Ar->start, name, cp0-name); + strcat(Ar->start, "="); + strncat(Ar->start, esc_value, strlen(esc_value)); + free(esc_value); + return; +ERROR_MACRO: + /* Macro without '=' or with invalid left/right part */ + return; +} + +static void +report_dir_enter_leave(Boolean entering) +{ + char rcwd[MAXPATHLEN]; +static char * mlev = NULL; + char * make_level_str = NULL; + int make_level_val = 0; + + make_level_str = getenv("MAKELEVEL"); + if(make_level_str) { + make_level_val = atoi(make_level_str); + } + if(mlev == NULL) { + mlev = (char*) malloc(MAXPATHLEN); + } + if(entering) { + sprintf(mlev, "MAKELEVEL=%d", make_level_val + 1); + } else { + make_level_val--; + sprintf(mlev, "MAKELEVEL=%d", make_level_val); + } + putenv(mlev); + + if(report_cwd) { + if(make_level_val <= 0) { + if(entering) { + sprintf(rcwd, + gettext("%s: Entering directory `%s'\n"), + getprogname(), + get_current_path()); + } else { + sprintf(rcwd, + gettext("%s: Leaving directory `%s'\n"), + getprogname(), + get_current_path()); + } + } else { + if(entering) { + sprintf(rcwd, + gettext("%s[%d]: Entering directory `%s'\n"), + getprogname(), + make_level_val, get_current_path()); + } else { + sprintf(rcwd, + gettext("%s[%d]: Leaving directory `%s'\n"), + getprogname(), + make_level_val, get_current_path()); + } + } + printf("%s", rcwd); + } +} diff --git a/bin/make.rules.file b/bin/make.rules.file new file mode 100644 index 0000000..098a448 --- /dev/null +++ b/bin/make.rules.file @@ -0,0 +1,498 @@ +# +# 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. +# + +SUFFIXES = .o .c .c~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S~ .i .ln \ + .h .h~ .f .f~ .for .for~ .F .F~ .f90 .f90~ .ftn .ftn~ .mod .mod~ \ + .sym .def .def~ .p .p~ .r .r~ .cps .cps~ .C .C~ .Y .Y~ .L .L~ \ + .java .java~ .class + +.SUFFIXES: $(SUFFIXES) + +# OUTPUT_OPTION should be defined to "-o $@" when +# the default rules are used for non-local files. +OUTPUT_OPTION= + +# C language section. +CC=cc +CFLAGS= +CPPFLAGS= +LINT=lint +LINTFLAGS= +COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c +LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) +LINT.c=$(LINT) $(LINTFLAGS) $(CPPFLAGS) +.c: + $(LINK.c) -o $@ $< $(LDLIBS) +.c~: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c +.c.o: + $(COMPILE.c) $(OUTPUT_OPTION) $< +.c~.o: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) -c $*.c +.c.i: + $(CC) $(CFLAGS) $(CPPFLAGS) -P $< +.c~.i: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(CPPFLAGS) -P $*.c +.c.ln: + $(LINT.c) $(OUTPUT_OPTION) -c $< +.c~.ln: + $(GET) $(GFLAGS) -p $< > $*.c + $(LINT.c) $(OUTPUT_OPTION) -c $*.c +.c.a: + $(COMPILE.c) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.c~.a: + $(GET) $(GFLAGS) -p $< > $*.c + $(COMPILE.c) -o $% $*.c + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# C language section. yacc. +YACC=yacc +YFLAGS= +YACC.y=$(YACC) $(YFLAGS) +.y: + $(YACC.y) $< + $(LINK.c) -o $@ y.tab.c $(LDLIBS) + $(RM) y.tab.c +.y~: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c + +.y.c: + $(YACC.y) $< + mv y.tab.c $@ +.y~.c: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $@ +.y.ln: + $(YACC.y) $< + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y~.ln: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC.y) $*.y + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y.o: + $(YACC.y) $< + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c +.y~.o: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + rm -f y.tab.c + mv y.tab.o $@ + +# C language section. lex. +LEX=lex +LFLAGS= +LEX.l=$(LEX) $(LFLAGS) -t +.l: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINK.c) -o $@ $*.c -ll $(LDLIBS) + $(RM) $*.c +.l~: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +.l.c : + $(RM) $@ + $(LEX.l) $< > $@ +.l~.c: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ +.l.ln: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l~.ln: + $(GET) $(GFLAGS) -p $< > $*.l + $(RM) $*.c + $(LEX.l) $*.l > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l.o: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(COMPILE.c) -o $@ $*.c + $(RM) $*.c +.l~.o: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +# C++ language section. +CCC=CC +CCFLAGS= +COMPILE.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +COMPILE.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +.cc: + $(LINK.cc) -o $@ $< $(LDLIBS) +.cc~: + $(GET) $(GFLAGS) -p $< > $*.cc + $(LINK.cc) -o $@ $*.cc $(LDLIBS) +.cc.o: + $(COMPILE.cc) $(OUTPUT_OPTION) $< +.cc~.o: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) $(OUTPUT_OPTION) $*.cc +.cc.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.cc~.i: + $(GET) $(GFLAGS) -p $< > $*.cc + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.cc +.cc.a: + $(COMPILE.cc) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.cc~.a: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) -o $% $*.cc + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +.C: + $(LINK.C) -o $@ $< $(LDLIBS) +.C~: + $(GET) $(GFLAGS) -p $< > $*.C + $(LINK.C) -o $@ $*.C $(LDLIBS) +.C.o: + $(COMPILE.C) $(OUTPUT_OPTION) $< +.C~.o: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) $(OUTPUT_OPTION) $*.C +.C.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.C~.i: + $(GET) $(GFLAGS) -p $< > $*.C + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.C +.C.a: + $(COMPILE.C) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.C~.a: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) -o $% $*.C + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. +FC=f77 +FFLAGS= +COMPILE.f=$(FC) $(FFLAGS) -c +LINK.f=$(FC) $(FFLAGS) $(LDFLAGS) +COMPILE.F=$(FC) $(FFLAGS) $(CPPFLAGS) -c +LINK.F=$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) +.f: + $(LINK.f) -o $@ $< $(LDLIBS) +.f~: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.f +.f.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.f~.o: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) -c $*.f +.f.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f~.a: + $(GET) $(GFLAGS) -p $< > $*.f + $(COMPILE.f) -o $% $*.f + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for: + $(LINK.f) -o $@ $< $(LDLIBS) +.for~: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.for +.for.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.for~.o: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) -c $*.for +.for.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for~.a: + $(GET) $(GFLAGS) -p $< > $*.for + $(COMPILE.f) -o $% $*.for + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F: + $(LINK.F) -o $@ $< $(LDLIBS) +.F~: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.F +.F.o: + $(COMPILE.F) $(OUTPUT_OPTION) $< +.F~.o: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) -c $*.F +.F.a: + $(COMPILE.F) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F~.a: + $(GET) $(GFLAGS) -p $< > $*.F + $(COMPILE.F) -o $% $*.F + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. ratfor. +RFLAGS= +COMPILE.r=$(FC) $(FFLAGS) $(RFLAGS) -c +LINK.r=$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) +.r: + $(LINK.r) -o $@ $< $(LDLIBS) +.r~: + $(GET) $(GFLAGS) -p $< > $*.r + $(LINK.r) -o $@ $*.r $(LDLIBS) +.r.o: + $(COMPILE.r) $(OUTPUT_OPTION) $< +.r~.o: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) $(OUTPUT_OPTION) $*.r +.r.a: + $(COMPILE.r) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.r~.a: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) -o $% $*.r + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN 90 section. +F90C=f90 +F90FLAGS= +COMPILE.f90=$(F90C) $(F90FLAGS) -c +LINK.f90=$(F90C) $(F90FLAGS) $(LDFLAGS) +COMPILE.ftn=$(F90C) $(F90FLAGS) -c +LINK.ftn=$(F90C) $(F90FLAGS) $(LDFLAGS) +.f90: + $(LINK.f90) -o $@ $< $(LDLIBS) +.f90~: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(LINK.f90) -o $@ $*.f90 $(LDLIBS) +.f90.o: + $(COMPILE.f90) $(OUTPUT_OPTION) $< +.f90~.o: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) $(OUTPUT_OPTION) $*.f90 +.f90.a: + $(COMPILE.f90) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f90~.a: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) -o $% $*.f90 + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn: + $(LINK.ftn) -o $@ $< $(LDLIBS) +.ftn~: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(LINK.ftn) -o $@ $*.ftn $(LDLIBS) +.ftn.o: + $(COMPILE.ftn) $(OUTPUT_OPTION) $< +.ftn~.o: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) $(OUTPUT_OPTION) $*.ftn +.ftn.a: + $(COMPILE.ftn) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn~.a: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) -o $% $*.ftn + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Modula-2 section. +M2C=m2c +M2FLAGS= +MODFLAGS= +DEFFLAGS= +COMPILE.def=$(M2C) $(M2FLAGS) $(DEFFLAGS) +COMPILE.mod=$(M2C) $(M2FLAGS) $(MODFLAGS) +.def.sym: + $(COMPILE.def) -o $@ $< +.def~.sym: + $(GET) $(GFLAGS) -p $< > $*.def + $(COMPILE.def) -o $@ $*.def +.mod: + $(COMPILE.mod) -o $@ -e $@ $< +.mod~: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ -e $@ $*.mod +.mod.o: + $(COMPILE.mod) -o $@ $< +.mod~.o: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ $*.mod +.mod.a: + $(COMPILE.mod) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.mod~.a: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $% $*.mod + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Pascal section. +PC=pc +PFLAGS= +COMPILE.p=$(PC) $(PFLAGS) $(CPPFLAGS) -c +LINK.p=$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) +.p: + $(LINK.p) -o $@ $< $(LDLIBS) +.p~: + $(GET) $(GFLAGS) -p $< > $*.p + $(LINK.p) -o $@ $*.p $(LDLIBS) +.p.o: + $(COMPILE.p) $(OUTPUT_OPTION) $< +.p~.o: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) $(OUTPUT_OPTION) $*.p +.p.a: + $(COMPILE.p) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.p~.a: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) -o $% $*.p + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Assembly section. +AS=as +ASFLAGS= +COMPILE.s=$(AS) $(ASFLAGS) +COMPILE.S=$(CC) $(ASFLAGS) $(CPPFLAGS) -c +.s.o: + $(COMPILE.s) -o $@ $< +.s~.o: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $@ $*.s +.s.a: + $(COMPILE.s) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.s~.a: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $% $*.s + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S.o: + $(COMPILE.S) -o $@ $< +.S~.o: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $@ $*.S +.S.a: + $(COMPILE.S) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S~.a: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $% $*.S + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Shell section. +.sh: + $(RM) $@ + cat $< > $@ + chmod +x $@ +.sh~: + $(GET) $(GFLAGS) -p $< > $*.sh + cp $*.sh $@ + chmod a+x $@ + +# NeWS section +CPS=cps +CPSFLAGS= +.cps.h: + $(CPS) $(CPSFLAGS) $*.cps +.cps~.h: + $(GET) $(GFLAGS) -p $< > $*.cps + $(CPS) $(CPSFLAGS) $*.cps + +# JAVA section +JAVAC=javac +JAVACFLAGS= +.java.class: + $(JAVAC) $(JAVACFLAGS) $< +.java~.class: + $(GET) $(GFLAGS) -p $< > $*.java + $(JAVAC) $(JAVACFLAGS) $< + +# Miscellaneous section. +LD=ld +LDFLAGS= +LDLIBS= +MAKE=make +RM=rm -f +AR=ar +ARFLAGS=rv +GET=get +GFLAGS= + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + cc -c markfile.c + $(RM) markfile.c + +SCCSFLAGS= +SCCSGETFLAGS=-s +.SCCS_GET: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@ + +.SCCS_GET_POSIX: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ + +.GET_POSIX: + $(GET) $(GFLAGS) s.$@ diff --git a/bin/misc.cc b/bin/misc.cc new file mode 100644 index 0000000..25bad0a --- /dev/null +++ b/bin/misc.cc @@ -0,0 +1,736 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * misc.cc + * + * This file contains various unclassified routines. Some main groups: + * getname + * Memory allocation + * String handling + * Property handling + * Error message handling + * Make internal state dumping + * main routine support + */ + +/* + * Included files + */ +#include +#include +#include /* SETVAR() */ +#include /* enable_interrupt() */ +#include /* va_list, va_start(), va_end() */ +#include /* SUNPRO_DEPENDENCIES */ +#include + +extern void job_adjust_fini(); + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static void print_rule(register Name target); +static void print_target_n_deps(register Name target); + +/***************************************** + * + * getname + */ + +/***************************************** + * + * Memory allocation + */ + +/* + * free_chain() + * + * frees a chain of Name_vector's + * + * Parameters: + * ptr Pointer to the first element in the chain + * to be freed. + * + * Global variables used: + */ +void +free_chain(Name_vector ptr) +{ + if (ptr != NULL) { + if (ptr->next != NULL) { + free_chain(ptr->next); + } + free((char *) ptr); + } +} + +/***************************************** + * + * String manipulation + */ + +/***************************************** + * + * Nameblock property handling + */ + +/***************************************** + * + * Error message handling + */ + +/* + * fatal(format, args...) + * + * Print a message and die + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * fatal_in_progress Indicates if this is a recursive call + * parallel_process_cnt Do we need to wait for anything? + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +fatal(const char *message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Fatal error: "), getprogname()); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); + if (fatal_in_progress) { + exit_status = 1; + exit(1); + } + fatal_in_progress = true; + /* Let all parallel children finish */ + if ((dmake_mode_type == parallel_mode) && + (parallel_process_cnt > 0)) { + (void) fprintf(stderr, + gettext("Waiting for %d %s to finish\n"), + parallel_process_cnt, + parallel_process_cnt == 1 ? + gettext("job") : gettext("jobs")); + (void) fflush(stderr); + } + + while (parallel_process_cnt > 0) { + await_parallel(true); + finish_children(false); + } + + job_adjust_fini(); + + exit_status = 1; + exit(1); +} + +/* + * warning(format, args...) + * + * Print a message and continue. + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +warning(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Warning: "), getprogname()); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); +} + +/* + * time_to_string(time) + * + * Take a numeric time value and produce + * a proper string representation. + * + * Return value: + * The string representation of the time + * + * Parameters: + * time The time we need to translate + * + * Global variables used: + */ +char * +time_to_string(const timestruc_t &time) +{ + struct tm *tm; + char buf[128]; + + if (time == file_doesnt_exist) { + return gettext("File does not exist"); + } + if (time == file_max_time) { + return gettext("Younger than any file"); + } + tm = localtime(&time.tv_sec); + strftime(buf, sizeof (buf), "%c %Z", tm); + buf[127] = (int) nul_char; + return strdup(buf); +} + +/* + * get_current_path() + * + * Stuff current_path with the current path if it isnt there already. + * + * Parameters: + * + * Global variables used: + */ +char * +get_current_path(void) +{ + char pwd[(MAXPATHLEN * MB_LEN_MAX)]; + static char *current_path; + + if (current_path == NULL) { + getcwd(pwd, sizeof(pwd)); + if (pwd[0] == (int) nul_char) { + pwd[0] = (int) slash_char; + pwd[1] = (int) nul_char; + } + current_path = strdup(pwd); + } + return current_path; +} + +/***************************************** + * + * Make internal state dumping + * + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ + +/* + * dump_make_state() + * + * Dump make's internal state to stdout + * + * Parameters: + * + * Global variables used: + * svr4 Was ".SVR4" seen in makefile? + * svr4_name The Name ".SVR4", printed + * posix Was ".POSIX" seen in makefile? + * posix_name The Name ".POSIX", printed + * default_rule Points to the .DEFAULT rule + * default_rule_name The Name ".DEFAULT", printed + * default_target_to_build The first target to print + * dot_keep_state The Name ".KEEP_STATE", printed + * dot_keep_state_file The Name ".KEEP_STATE_FILE", printed + * hashtab The make hash table for Name blocks + * ignore_errors Was ".IGNORE" seen in makefile? + * ignore_name The Name ".IGNORE", printed + * keep_state Was ".KEEP_STATE" seen in makefile? + * percent_list The list of % rules + * precious The Name ".PRECIOUS", printed + * sccs_get_name The Name ".SCCS_GET", printed + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", printed + * get_name The Name ".GET", printed + * get_posix_name The Name ".GET_POSIX", printed + * sccs_get_rule Points to the ".SCCS_GET" rule + * silent Was ".SILENT" seen in makefile? + * silent_name The Name ".SILENT", printed + * suffixes The suffix list from ".SUFFIXES" + * suffixes_name The Name ".SUFFIX", printed + */ +void +dump_make_state(void) +{ + Name_set::iterator p, e; + register Property prop; + register Dependency dep; + register Cmd_line rule; + Percent percent, percent_depe; + + /* Default target */ + if (default_target_to_build != NULL) { + print_rule(default_target_to_build); + } + (void) printf("\n"); + + /* .POSIX */ + if (posix) { + (void) printf("%s:\n", posix_name->string_mb); + } + + /* .DEFAULT */ + if (default_rule != NULL) { + (void) printf("%s:\n", default_rule_name->string_mb); + for (rule = default_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .IGNORE */ + if (ignore_errors) { + (void) printf("%s:\n", ignore_name->string_mb); + } + + /* .KEEP_STATE: */ + if (keep_state) { + (void) printf("%s:\n\n", dot_keep_state->string_mb); + } + + /* .PRECIOUS */ + (void) printf("%s:", precious->string_mb); + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if ((p->stat.is_precious) || (all_precious)) { + (void) printf(" %s", p->string_mb); + } + } + (void) printf("\n"); + + /* .SCCS_GET */ + if (sccs_get_rule != NULL) { + (void) printf("%s:\n", sccs_get_name->string_mb); + for (rule = sccs_get_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .SILENT */ + if (silent) { + (void) printf("%s:\n", silent_name->string_mb); + } + + /* .SUFFIXES: */ + (void) printf("%s:", suffixes_name->string_mb); + for (dep = suffixes; dep != NULL; dep = dep->next) { + (void) printf(" %s", dep->name->string_mb); + build_suffix_list(dep->name); + } + (void) printf("\n\n"); + + /* % rules */ + for (percent = percent_list; + percent != NULL; + percent = percent->next) { + (void) printf("%s:", + percent->name->string_mb); + + for (percent_depe = percent->dependencies; + percent_depe != NULL; + percent_depe = percent_depe->next) { + (void) printf(" %s", percent_depe->name->string_mb); + } + + (void) printf("\n"); + + for (rule = percent->command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* Suffix rules */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + Wstring wcb(p); + if (wcb.get_string()[0] == (int) period_char) { + print_rule(p); + } + } + + /* Macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (((prop = get_prop(p->prop, macro_prop)) != NULL) && + (prop->body.macro.value != NULL)) { + (void) printf("%s", p->string_mb); + print_value(prop->body.macro.value, + (Daemon) prop->body.macro.daemon); + } + } + (void) printf("\n"); + + /* Conditional macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + for (prop = get_prop(p->prop, conditional_prop); + prop != NULL; + prop = get_prop(prop->next, conditional_prop)) { + (void) printf("%s := %s", + p->string_mb, + prop->body.conditional.name-> + string_mb); + if (prop->body.conditional.append) { + printf(" +"); + } + else { + printf(" "); + } + print_value(prop->body.conditional.value, + no_daemon); + } + } + (void) printf("\n"); + + /* All other dependencies */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (p->colons != no_colon) { + print_rule(p); + } + } + (void) printf("\n"); +} + +/* + * print_rule(target) + * + * Print the rule for one target + * + * Parameters: + * target Target we print rule for + * + * Global variables used: + */ +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed || + ((line = get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) { + return; + } + target->dependency_printed = true; + + (void) printf("%s:", target->string_mb); + + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + (void) printf(" %s", dependency->name->string_mb); + } + + (void) printf("\n"); + + for (rule = line->body.line.command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } +} + +void +dump_target_list(void) +{ + Name_set::iterator p, e; + Wstring str; + + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + str.init(p); + wchar_t * wcb = str.get_string(); + if ((p->colons != no_colon) && + ((wcb[0] != (int) period_char) || + ((wcb[0] == (int) period_char) && + (wcschr(wcb, (int) slash_char))))) { + print_target_n_deps(p); + } + } +} + +static void +print_target_n_deps(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed) { + return; + } + target->dependency_printed = true; + + (void) printf("%s\n", target->string_mb); + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->automatic) { + print_target_n_deps(dependency->name); + } + } +} + +/***************************************** + * + * main() support + */ + +/* + * load_cached_names() + * + * Load the vector of cached names + * + * Parameters: + * + * Global variables used: + * Many many pointers to Name blocks. + */ +void +load_cached_names(void) +{ + char *cp; + Name dollar; + + /* Load the cached_names struct */ + MBSTOWCS(wcs_buffer, ".BUILT_LAST_MAKE_RUN"); + built_last_make_run = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "@"); + c_at = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, " *conditionals* "); + conditionals = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * A version of make was released with NSE 1.0 that used + * VERSION-1.1 but this version is identical to VERSION-1.0. + * The version mismatch code makes a special case for this + * situation. If the version number is changed from 1.0 + * it should go to 1.2. + */ + MBSTOWCS(wcs_buffer, "VERSION-1.0"); + current_make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SVR4"); + svr4_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".POSIX"); + posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".DEFAULT"); + default_rule_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "$"); + dollar = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".DONE"); + done = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "."); + dot = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".KEEP_STATE"); + dot_keep_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".KEEP_STATE_FILE"); + dot_keep_state_file = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ""); + empty_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, " FORCE"); + force = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "HOST_ARCH"); + host_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "HOST_MACH"); + host_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".IGNORE"); + ignore_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".INIT"); + init = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".LOCAL"); + localhost_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".make.state"); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "MAKEFLAGS"); + makeflags = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".MAKE_VERSION"); + make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".NO_PARALLEL"); + no_parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".NOT_AUTO"); + not_auto = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".PARALLEL"); + parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "PATH"); + path_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "+"); + plus = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".PRECIOUS"); + precious = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "?"); + query = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "^"); + hat = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".RECURSIVE"); + recursive_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SCCS_GET"); + sccs_get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SCCS_GET_POSIX"); + sccs_get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".GET"); + get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".GET_POSIX"); + get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "SHELL"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SILENT"); + silent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SUFFIXES"); + suffixes_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, SUNPRO_DEPENDENCIES); + sunpro_dependencies = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "TARGET_ARCH"); + target_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "TARGET_MACH"); + target_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "VIRTUAL_ROOT"); + virtual_root = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "VPATH"); + vpath_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".WAIT"); + wait_name = GETNAME(wcs_buffer, FIND_LENGTH); + + wait_name->state = build_ok; + + /* Mark special targets so that the reader treats them properly */ + svr4_name->special_reader = svr4_special; + posix_name->special_reader = posix_special; + built_last_make_run->special_reader = built_last_make_run_special; + default_rule_name->special_reader = default_special; + dot_keep_state->special_reader = keep_state_special; + dot_keep_state_file->special_reader = keep_state_file_special; + ignore_name->special_reader = ignore_special; + make_version->special_reader = make_version_special; + no_parallel_name->special_reader = no_parallel_special; + parallel_name->special_reader = parallel_special; + localhost_name->special_reader = localhost_special; + precious->special_reader = precious_special; + sccs_get_name->special_reader = sccs_get_special; + sccs_get_posix_name->special_reader = sccs_get_posix_special; + get_name->special_reader = get_special; + get_posix_name->special_reader = get_posix_special; + silent_name->special_reader = silent_special; + suffixes_name->special_reader = suffixes_special; + + /* The value of $$ is $ */ + (void) SETVAR(dollar, dollar, false); + dollar->dollar = false; + + /* Set the value of $(SHELL) */ + if (posix) { + MBSTOWCS(wcs_buffer, "/usr/xpg4/bin/sh"); + } else { + MBSTOWCS(wcs_buffer, "/bin/sh"); + } + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + + /* + * Use " FORCE" to simulate a FRC dependency for :: type + * targets with no dependencies. + */ + (void) append_prop(force, line_prop); + force->stat.time = file_max_time; + + /* Make sure VPATH is defined before current dir is read */ + if ((cp = getenv(vpath_name->string_mb)) != NULL) { + MBSTOWCS(wcs_buffer, cp); + (void) SETVAR(vpath_name, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + + /* Check if there is NO PATH variable. If not we construct one. */ + if (getenv(path_name->string_mb) == NULL) { + vroot_path = NULL; + add_dir_to_path(".", &vroot_path, -1); + add_dir_to_path("/bin", &vroot_path, -1); + add_dir_to_path("/usr/bin", &vroot_path, -1); + } +} + +/* + * iterate on list of conditional macros in np, and place them in + * a String_rec starting with, and separated by the '$' character. + */ +void +cond_macros_into_string(Name np, String_rec *buffer) +{ + Macro_list macro_list; + + /* + * Put the version number at the start of the string + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + append_string(wcs_buffer, buffer, FIND_LENGTH); + /* + * Add the rest of the conditional macros to the buffer + */ + if (np->depends_on_conditional){ + for (macro_list = np->conditional_macro_list; + macro_list != NULL; macro_list = macro_list->next){ + append_string(macro_list->macro_name, buffer, + FIND_LENGTH); + append_char((int) equal_char, buffer); + append_string(macro_list->value, buffer, FIND_LENGTH); + append_char((int) dollar_char, buffer); + } + } +} + diff --git a/bin/nse_printdep.cc b/bin/nse_printdep.cc new file mode 100644 index 0000000..df6ec1b --- /dev/null +++ b/bin/nse_printdep.cc @@ -0,0 +1,365 @@ +/* + * 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. + */ + +/* + * Included files + */ +#include +#include /* get_prop() */ + +/* + * File table of contents + */ +void print_dependencies(register Name target, register Property line); +static void print_deps(register Name target, register Property line); +static void print_more_deps(Name target, Name name); +static void print_filename(Name name); +static Boolean should_print_dep(Property line); +static void print_forest(Name target); +static void print_deplist(Dependency head); +void print_value(register Name value, Daemon daemon); +static void print_rule(register Name target); +static void print_rec_info(Name target); +static Boolean is_out_of_date(Property line); +extern void depvar_print_results (void); + +/* + * print_dependencies(target, line) + * + * Print all the dependencies of a target. First print all the Makefiles. + * Then print all the dependencies. Finally, print all the .INIT + * dependencies. + * + * Parameters: + * target The target we print dependencies for + * line We get the dependency list from here + * + * Global variables used: + * done The Name ".DONE" + * init The Name ".INIT" + * makefiles_used List of all makefiles read + */ +void +print_dependencies(register Name target, register Property line) +{ + Dependency dp; + static Boolean makefiles_printed = false; + + if (target_variants) { + depvar_print_results(); + } + + if (!makefiles_printed) { + /* + * Search the makefile list for the primary makefile, + * then print it and its inclusions. After that go back + * and print the default.mk file and its inclusions. + */ + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + } + if (dp) { + print_deplist(dp); + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + (void)printf(" %s", dp->name->string_mb); + } + } + (void) printf("\n"); + makefiles_printed = true; + } + print_deps(target, line); +/* + print_more_deps(target, init); + print_more_deps(target, done); + */ + if (target_variants) { + print_forest(target); + } +} + +/* + * print_more_deps(target, name) + * + * Print some special dependencies. + * These are the dependencies for the .INIT and .DONE targets. + * + * Parameters: + * target Target built during make run + * name Special target to print dependencies for + * + * Global variables used: + */ +static void +print_more_deps(Name target, Name name) +{ + Property line; + register Dependency dependencies; + + line = get_prop(name->prop, line_prop); + if (line != NULL && line->body.line.dependencies != NULL) { + (void) printf("%s:\t", target->string_mb); + print_deplist(line->body.line.dependencies); + (void) printf("\n"); + for (dependencies= line->body.line.dependencies; + dependencies != NULL; + dependencies= dependencies->next) { + print_deps(dependencies->name, + get_prop(dependencies->name->prop, line_prop)); + } + } +} + +/* + * print_deps(target, line, go_recursive) + * + * Print a regular dependency list. Append to this information which + * indicates whether or not the target is recursive. + * + * Parameters: + * target target to print dependencies for + * line We get the dependency list from here + * go_recursive Should we show all dependencies recursively? + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", printed + */ +static void +print_deps(register Name target, register Property line) +{ + register Dependency dep; + + if ((target->dependency_printed) || + (target == force)) { + return; + } + target->dependency_printed = true; + + /* only print entries that are actually derived and are not leaf + * files and are not the result of sccs get. + */ + if (should_print_dep(line)) { + if ((report_dependencies_level == 2) || + (report_dependencies_level == 4)) { + if (is_out_of_date(line)) { + (void) printf("1 "); + } else { + (void) printf("0 "); + } + } + print_filename(target); + (void) printf(":\t"); + print_deplist(line->body.line.dependencies); + print_rec_info(target); + (void) printf("\n"); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + print_deps(dep->name, + get_prop(dep->name->prop, line_prop)); + } + } +} + +static Boolean +is_out_of_date(Property line) +{ + Dependency dep; + Property line2; + + if (line == NULL) { + return false; + } + if (line->body.line.is_out_of_date) { + return true; + } + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + line2 = get_prop(dep->name->prop, line_prop); + if (is_out_of_date(line2)) { + line->body.line.is_out_of_date = true; + return true; + } + } + return false; +} + +/* + * Given a dependency print it and all its siblings. + */ +static void +print_deplist(Dependency head) +{ + Dependency dp; + + for (dp = head; dp != NULL; dp = dp->next) { + if ((report_dependencies_level != 2) || + ((!dp->automatic) || + (dp->name->is_double_colon))) { + if (dp->name != force) { + putwchar(' '); + print_filename(dp->name); + } + } + } +} + +/* + * Print the name of a file for the -P option. + * If the file is a directory put on a trailing slash. + */ +static void +print_filename(Name name) +{ + (void) printf("%s", name->string_mb); +/* + if (name->stat.is_dir) { + putwchar('/'); + } + */ +} + +/* + * should_print_dep(line) + * + * Test if we should print the dependencies of this target. + * The line must exist and either have children dependencies + * or have a command that is not an SCCS command. + * + * Return value: + * true if the dependencies should be printed + * + * Parameters: + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +should_print_dep(Property line) +{ + if (line == NULL) { + return false; + } + if (line->body.line.dependencies != NULL) { + return true; + } + if (line->body.line.sccs_command) { + return false; + } + return true; +} + +/* + * Print out the root nodes of all the dependency trees + * in this makefile. + */ +static void +print_forest(Name target) +{ + Name_set::iterator np, e; + Property line; + + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + if (np->is_target && !np->has_parent && np != target) { + (void) doname_check(np, true, false, false); + line = get_prop(np->prop, line_prop); + printf("-\n"); + print_deps(np, line); + } + } +} + + +/* + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ +void +print_value(register Name value, Daemon daemon) + + +{ + Chain cp; + + if (value == NULL) + (void)printf("=\n"); + else + switch (daemon) { + case no_daemon: + (void)printf("= %s\n", value->string_mb); + break; + case chain_daemon: + for (cp= (Chain) value; cp != NULL; cp= cp->next) + (void)printf(cp->next == NULL ? "%s" : "%s ", + cp->name->string_mb); + (void)printf("\n"); + break; + }; +} + +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + + if (((line= get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) + return; + print_dependencies(target, line); + for (rule= line->body.line.command_template; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); +} + + +/* + * If target is recursive, print the following to standard out: + * .RECURSIVE subdir targ Makefile + */ +static void +print_rec_info(Name target) +{ + Recursive_make rp; + wchar_t *colon; + + report_recursive_init(); + + rp = find_recursive_target(target); + + if (rp) { + /* + * if found, print starting with the space after the ':' + */ + colon = (wchar_t *) wcschr(rp->oldline, (int) colon_char); + (void) printf("%s", colon + 1); + } +} + diff --git a/bin/parallel.cc b/bin/parallel.cc new file mode 100644 index 0000000..c7712df --- /dev/null +++ b/bin/parallel.cc @@ -0,0 +1,1892 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * parallel.cc + * + * Deal with the parallel processing + */ + +/* + * Included files + */ +#include /* errno */ +#include +#include +#include /* redirect_io() */ +#include /* expand_value() */ +#include /* getmem() */ +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Defined macros + */ +#define MAXRULES 100 + +/* + * This const should be in avo_dms/include/AvoDmakeCommand.h + */ +const int local_host_mask = 0x20; + + +/* + * typedefs & structs + */ + + +/* + * Static variables + */ +static Boolean just_did_subtree = false; +static char local_host[MAXNAMELEN] = ""; +static char user_name[MAXNAMELEN] = ""; +static int pmake_max_jobs = 0; +static pid_t process_running = -1; +static Running *running_tail = &running_list; +static Name subtree_conflict; +static Name subtree_conflict2; + + +/* + * File table of contents + */ +static void delete_running_struct(Running rp); +static Boolean dependency_conflict(Name target); +static Doname distribute_process(char **commands, Property line); +static void doname_subtree(Name target, Boolean do_get, Boolean implicit); +static void dump_out_file(char *filename, Boolean err); +static void finish_doname(Running rp); +static void maybe_reread_make_state(void); +static void process_next(void); +static void reset_conditionals(int cnt, Name *targets, Property *locals); +static pid_t run_rule_commands(char *host, char **commands); +static Property *set_conditionals(int cnt, Name *targets); +static void store_conditionals(Running rp); + + +/* + * execute_parallel(line, waitflg) + * + * DMake 2.x: + * parallel mode: spawns a parallel process to execute the command group. + * + * Return value: + * The result of the execution + * + * Parameters: + * line The command group to execute + */ +Doname +execute_parallel(Property line, Boolean waitflg, Boolean local) +{ + int argcnt; + int cmd_options = 0; + char *commands[MAXRULES + 5]; + char *cp; + Name dmake_name; + Name dmake_value; + int ignore; + Name make_machines_name; + char **p; + Property prop; + Doname result = build_ok; + Cmd_line rule; + Boolean silent_flag; + Name target = line->body.line.target; + Boolean wrote_state_file = false; + + if ((pmake_max_jobs == 0) && + (dmake_mode_type == parallel_mode)) { + if (local_host[0] == '\0') { + (void) gethostname(local_host, MAXNAMELEN); + } + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + pmake_max_jobs = atoi(dmake_value->string_mb); + if (pmake_max_jobs <= 0) { + warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero.")); + warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS); + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } else { + /* + * For backwards compatibility w/ PMake 1.x, when + * DMake 2.x is being run in parallel mode, DMake + * should parse the PMake startup file + * $(HOME)/.make.machines to get the pmake_max_jobs. + */ + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + make_machines_name = dmake_value; + } else { + make_machines_name = NULL; + } + if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) { + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } + } + + if ((dmake_mode_type == serial_mode) || + ((dmake_mode_type == parallel_mode) && (waitflg))) { + return (execute_serial(line)); + } + + { + p = commands; + } + + argcnt = 0; + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + + silent_flag = false; + ignore = 0; + + if (rule->command_line->hash.length > 0) { + if (++argcnt == MAXRULES) { + return build_serial; + } + { + if (rule->silent && !silent) { + silent_flag = true; + } + if (rule->ignore_error) { + ignore++; + } + /* XXX - need to add support for + prefix */ + if (silent_flag || ignore) { + *p = getmem((silent_flag ? 1 : 0) + + ignore + + (strlen(rule-> + command_line-> + string_mb)) + + 1); + cp = *p++; + if (silent_flag) { + *cp++ = (int) at_char; + } + if (ignore) { + *cp++ = (int) hyphen_char; + } + (void) strcpy(cp, rule->command_line->string_mb); + } else { + *p++ = rule->command_line->string_mb; + } + } + } + } + if ((argcnt == 0) || + (report_dependencies_level > 0)) { + return build_ok; + } + { + *p = NULL; + + Doname res = distribute_process(commands, line); + if (res == build_running) { + parallel_process_cnt++; + } + + /* + * Return only those memory that were specially allocated + * for part of commands. + */ + for (int i = 0; commands[i] != NULL; i++) { + if ((commands[i][0] == (int) at_char) || + (commands[i][0] == (int) hyphen_char)) { + retmem_mb(commands[i]); + } + } + return res; + } +} + + + +#include /* sysconf(_SC_NPROCESSORS_ONLN) */ +#include /* ftok() */ +#include /* shmget(), shmat(), shmdt(), shmctl() */ +#include /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */ +#include /* getloadavg() */ + +/* + * adjust_pmake_max_jobs (int pmake_max_jobs) + * + * Parameters: + * pmake_max_jobs - max jobs limit set by user + * + * External functions used: + * sysconf() + * getloadavg() + */ +static int +adjust_pmake_max_jobs (int pmake_max_jobs) +{ + static int ncpu = 0; + double loadavg[3]; + int adjustment; + int adjusted_max_jobs; + + if (ncpu <= 0) { + if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { + ncpu = 1; + } + } + if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs); + adjustment = ((int)loadavg[LOADAVG_1MIN]); + if (adjustment < 2) return(pmake_max_jobs); + if (ncpu > 1) { + adjustment = adjustment / ncpu; + } + adjusted_max_jobs = pmake_max_jobs - adjustment; + if (adjusted_max_jobs < 1) adjusted_max_jobs = 1; + return(adjusted_max_jobs); +} + +/* + * M2 adjust mode data and functions + * + * m2_init() - initializes M2 shared semaphore + * m2_acquire_job() - decrements M2 semaphore counter + * m2_release_job() - increments M2 semaphore counter + * m2_fini() - destroys M2 semaphore and shared memory* + * + * Environment variables: + * __DMAKE_M2_FILE__ + * + * External functions: + * ftok(), shmget(), shmat(), shmdt(), shmctl() + * sem_init(), sem_trywait(), sem_post(), sem_destroy() + * creat(), close(), unlink() + * getenv(), putenv() + * + * Static variables: + * m2_file - tmp file name to create ipc key for shared memory + * m2_shm_id - shared memory id + * m2_shm_sem - shared memory semaphore + */ + +static char m2_file[MAXPATHLEN]; +static int m2_shm_id = -1; +static sem_t* m2_shm_sem = 0; + +static int +m2_init() { + char *var; + key_t key; + + if ((var = getenv("__DMAKE_M2_FILE__")) == 0) { + /* compose tmp file name */ + sprintf(m2_file, "%s/dmake.m2.%d.XXXXXX", tmpdir, getpid()); + + /* create tmp file */ + int fd = mkstemp(m2_file); + if (fd < 0) { + return -1; + } else { + close(fd); + } + } else { + /* using existing semaphore */ + strcpy(m2_file, var); + } + + /* combine IPC key */ + if ((key = ftok(m2_file, 38)) == (key_t) -1) { + return -1; + } + + /* create shared memory */ + if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) { + return -1; + } + + /* attach shared memory */ + if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) { + return -1; + } + + /* root process */ + if (var == 0) { + /* initialize semaphore */ + if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) { + return -1; + } + + /* alloc memory for env variable */ + if ((var = (char*) malloc(MAXPATHLEN)) == 0) { + return -1; + } + + /* put key to env */ + sprintf(var, "__DMAKE_M2_FILE__=%s", m2_file); + if (putenv(var)) { + return -1; + } + } + return 0; +} + +static void +m2_fini() { + if (m2_shm_id >= 0) { + struct shmid_ds stat; + + /* determine the number of attached processes */ + if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) { + if (stat.shm_nattch <= 1) { + /* destroy semaphore */ + if (m2_shm_sem != 0) { + (void) sem_destroy(m2_shm_sem); + } + + /* destroy shared memory */ + (void) shmctl(m2_shm_id, IPC_RMID, &stat); + + /* remove tmp file created for the key */ + (void) unlink(m2_file); + } else { + /* detach shared memory */ + if (m2_shm_sem != 0) { + (void) shmdt((char*) m2_shm_sem); + } + } + } + + m2_shm_id = -1; + m2_shm_sem = 0; + } +} + +static int +m2_acquire_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_trywait(m2_shm_sem) == 0) { + return 1; + } + if (errno == EAGAIN) { + return 0; + } + } + return -1; +} + +static int +m2_release_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_post(m2_shm_sem) == 0) { + return 0; + } + } + return -1; +} + +/* + * job adjust mode + * + * Possible values: + * ADJUST_M1 - adjustment by system load (default) + * ADJUST_M2 - fixed limit of jobs for the group of nested dmakes + * ADJUST_NONE - no adjustment - fixed limit of jobs for the current dmake + */ +static enum { + ADJUST_UNKNOWN, + ADJUST_M1, + ADJUST_M2, + ADJUST_NONE +} job_adjust_mode = ADJUST_UNKNOWN; + +/* + * void job_adjust_fini() + * + * Description: + * Cleans up job adjust data. + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +void +job_adjust_fini() { + if (job_adjust_mode == ADJUST_M2) { + m2_fini(); + } +} + +/* + * void job_adjust_error() + * + * Description: + * Prints warning message, cleans up job adjust data, and disables job adjustment + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * + * External functions: + * putenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_error() { + if (job_adjust_mode != ADJUST_NONE) { + /* cleanup internals */ + job_adjust_fini(); + + /* warning message for the user */ + warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment.")); + + /* switch off job adjustment for the children */ + putenv(strdup("DMAKE_ADJUST_MAX_JOBS=NO")); + + /* and for this dmake */ + job_adjust_mode = ADJUST_NONE; + } +} + +/* + * void job_adjust_init() + * + * Description: + * Parses DMAKE_ADJUST_MAX_JOBS env variable + * and performs appropriate initializations. + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment + * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode + * other - M1 adjust mode + * + * External functions: + * getenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_init() { + if (job_adjust_mode == ADJUST_UNKNOWN) { + /* default mode */ + job_adjust_mode = ADJUST_M1; + + /* determine adjust mode */ + if (char *var = getenv("DMAKE_ADJUST_MAX_JOBS")) { + if (strcasecmp(var, "NO") == 0) { + job_adjust_mode = ADJUST_NONE; + } else if (strcasecmp(var, "M2") == 0) { + job_adjust_mode = ADJUST_M2; + } + } + + /* M2 specific initialization */ + if (job_adjust_mode == ADJUST_M2) { + if (m2_init()) { + job_adjust_error(); + } + } + } +} + + +/* + * distribute_process(char **commands, Property line) + * + * Parameters: + * commands argv vector of commands to execute + * + * Return value: + * The result of the execution + * + * Static variables used: + * process_running Set to the pid of the process set running + * job_adjust_mode Current job adjust mode + */ +static Doname +distribute_process(char **commands, Property line) +{ + static unsigned file_number = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; + int res; + int tmp_index; + char *tmp_index_str_ptr; + + /* initialize adjust mode, if not initialized */ + if (job_adjust_mode == ADJUST_UNKNOWN) { + job_adjust_init(); + } + + /* actions depend on adjust mode */ + switch (job_adjust_mode) { + case ADJUST_M1: + while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) { + await_parallel(false); + finish_children(true); + } + break; + case ADJUST_M2: + if ((res = m2_acquire_job()) == 0) { + if (parallel_process_cnt > 0) { + await_parallel(false); + finish_children(true); + + if ((res = m2_acquire_job()) == 0) { + return build_serial; + } + } else { + return build_serial; + } + } + if (res < 0) { + /* job adjustment error */ + job_adjust_error(); + + /* no adjustment */ + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + break; + default: + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + + setvar_envvar(); + /* + * Tell the user what DMake is doing. + */ + if (!silent && output_mode != txt2_mode) { + /* + * Print local_host --> x job(s). + */ + (void) fprintf(stdout, + gettext("%s --> %d %s\n"), + local_host, + parallel_process_cnt + 1, + (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs")); + + /* Print command line(s). */ + tmp_index = 0; + while (commands[tmp_index] != NULL) { + /* No @ char. */ + /* XXX - need to add [2] when + prefix is added */ + if ((commands[tmp_index][0] != (int) at_char) && + (commands[tmp_index][1] != (int) at_char)) { + tmp_index_str_ptr = commands[tmp_index]; + if (*tmp_index_str_ptr == (int) hyphen_char) { + tmp_index_str_ptr++; + } + (void) fprintf(stdout, "%s\n", tmp_index_str_ptr); + } + tmp_index++; + } + (void) fflush(stdout); + } + + (void) sprintf(mbstring, + "%s/dmake.stdout.%d.%d.XXXXXX", + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stdout_file = strdup(mbstring); + stderr_file = NULL; + + if (!out_err_same) { + (void) sprintf(mbstring, + "%s/dmake.stderr.%d.%d.XXXXXX", + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stderr_file = strdup(mbstring); + } + + process_running = run_rule_commands(local_host, commands); + + return build_running; +} + +/* + * doname_parallel(target, do_get, implicit) + * + * Processes the given target and finishes up any parallel + * processes left running. + * + * Return value: + * Result of target build + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + */ +Doname +doname_parallel(Name target, Boolean do_get, Boolean implicit) +{ + Doname result; + + result = doname_check(target, do_get, implicit, false); + if (result == build_ok || result == build_failed) { + return result; + } + finish_running(); + return (Doname) target->state; +} + +/* + * doname_subtree(target, do_get, implicit) + * + * Completely computes an object and its dependents for a + * serial subtree build. + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + * + * Static variables used: + * running_tail Tail of the list of running processes + * + * Global variables used: + * running_list The list of running processes + */ +static void +doname_subtree(Name target, Boolean do_get, Boolean implicit) +{ + Running save_running_list; + Running *save_running_tail; + + save_running_list = running_list; + save_running_tail = running_tail; + running_list = NULL; + running_tail = &running_list; + target->state = build_subtree; + target->checking_subtree = true; + while(doname_check(target, do_get, implicit, false) == build_running) { + target->checking_subtree = false; + finish_running(); + target->state = build_subtree; + } + target->checking_subtree = false; + running_list = save_running_list; + running_tail = save_running_tail; +} + +/* + * finish_running() + * + * Keeps processing until the running_list is emptied out. + * + * Parameters: + * + * Global variables used: + * running_list The list of running processes + */ +void +finish_running(void) +{ + while (running_list != NULL) { + { + await_parallel(false); + finish_children(true); + } + if (running_list != NULL) { + process_next(); + } + } +} + +/* + * process_next() + * + * Searches the running list for any targets which can start processing. + * This can be a pending target, a serial target, or a subtree target. + * + * Parameters: + * + * Static variables used: + * running_tail The end of the list of running procs + * subtree_conflict A target which conflicts with a subtree + * subtree_conflict2 The other target which conflicts + * + * Global variables used: + * commands_done True if commands executed + * debug_level Controls debug output + * parallel_process_cnt Number of parallel process running + * recursion_level Indentation for debug output + * running_list List of running processes + */ +static void +process_next(void) +{ + Running rp; + Running *rp_prev; + Property line; + Chain target_group; + Dependency dep; + Boolean quiescent = true; + Running *subtree_target; + Boolean saved_commands_done; + Property *conditionals; + + subtree_target = NULL; + subtree_conflict = NULL; + subtree_conflict2 = NULL; + /* + * If nothing currently running, build a serial target, if any. + */ +start_loop_1: + for (rp_prev = &running_list, rp = running_list; + rp != NULL && parallel_process_cnt == 0; + rp = rp->next) { + if (rp->state == build_serial) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = build_pending; + (void) doname_check(rp->target, + rp->do_get, + rp->implicit, + false); + quiescent = false; + delete_running_struct(rp); + goto start_loop_1; + } else { + rp_prev = &rp->next; + } + } + /* + * Find a target to build. The target must be pending, have all + * its dependencies built, and not be in a target group with a target + * currently building. + */ +start_loop_2: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (!(rp->state == build_pending || + rp->state == build_subtree)) { + quiescent = false; + rp_prev = &rp->next; + } else if (rp->state == build_pending) { + line = get_prop(rp->target->prop, line_prop); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + if (dep->name->state == build_running || + dep->name->state == build_pending || + dep->name->state == build_serial) { + break; + } + } + if (dep == NULL) { + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + break; + } + } + if (target_group == NULL) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = rp->redo ? + build_dont_know : build_pending; + saved_commands_done = commands_done; + conditionals = + set_conditionals + (rp->conditional_cnt, + rp->conditional_targets); + rp->target->dont_activate_cond_values = true; + if ((doname_check(rp->target, + rp->do_get, + rp->implicit, + rp->target->has_target_prop ? true : false) != + build_running) && + !commands_done) { + commands_done = + saved_commands_done; + } + rp->target->dont_activate_cond_values = false; + reset_conditionals + (rp->conditional_cnt, + rp->conditional_targets, + conditionals); + quiescent = false; + delete_running_struct(rp); + goto start_loop_2; + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + /* + * If nothing has been found to build and there exists a subtree + * target with no dependency conflicts, build it. + */ + if (quiescent) { +start_loop_3: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (rp->state == build_subtree) { + if (!dependency_conflict(rp->target)) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, + rp->do_get, + rp->implicit); + quiescent = false; + delete_running_struct(rp); + goto start_loop_3; + } else { + subtree_target = rp_prev; + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + } + /* + * If still nothing found to build, we either have a deadlock + * or a subtree with a dependency conflict with something waiting + * to build. + */ + if (quiescent) { + if (subtree_target == NULL) { + fatal(gettext("Internal error: deadlock detected in process_next")); + } else { + rp = *subtree_target; + if (debug_level > 0) { + warning(gettext("Conditional macro conflict encountered for %s between %s and %s"), + subtree_conflict2->string_mb, + rp->target->string_mb, + subtree_conflict->string_mb); + } + *subtree_target = (*subtree_target)->next; + if (rp->next == NULL) { + running_tail = subtree_target; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, rp->do_get, rp->implicit); + delete_running_struct(rp); + } + } +} + +/* + * set_conditionals(cnt, targets) + * + * Sets the conditional macros for the targets given in the array of + * targets. The old macro values are returned in an array of + * Properties for later resetting. + * + * Return value: + * Array of conditional macro settings + * + * Parameters: + * cnt Number of targets + * targets Array of targets + */ +static Property * +set_conditionals(int cnt, Name *targets) +{ + Property *locals, *lp; + Name *tp; + + locals = (Property *) getmem(cnt * sizeof(Property)); + for (lp = locals, tp = targets; + cnt > 0; + cnt--, lp++, tp++) { + *lp = (Property) getmem((*tp)->conditional_cnt * + sizeof(struct _Property)); + set_locals(*tp, *lp); + } + return locals; +} + +/* + * reset_conditionals(cnt, targets, locals) + * + * Resets the conditional macros as saved in the given array of + * Properties. The resets are done in reverse order. Afterwards the + * data structures are freed. + * + * Parameters: + * cnt Number of targets + * targets Array of targets + * locals Array of dependency macro settings + */ +static void +reset_conditionals(int cnt, Name *targets, Property *locals) +{ + Name *tp; + Property *lp; + + for (tp = targets + (cnt - 1), lp = locals + (cnt - 1); + cnt > 0; + cnt--, tp--, lp--) { + reset_locals(*tp, + *lp, + get_prop((*tp)->prop, conditional_prop), + 0); + retmem_mb((caddr_t) *lp); + } + retmem_mb((caddr_t) locals); +} + +/* + * dependency_conflict(target) + * + * Returns true if there is an intersection between + * the subtree of the target and any dependents of the pending targets. + * + * Return value: + * True if conflict found + * + * Parameters: + * target Subtree target to check + * + * Static variables used: + * subtree_conflict Target conflict found + * subtree_conflict2 Second conflict found + * + * Global variables used: + * running_list List of running processes + * wait_name .WAIT, not a real dependency + */ +static Boolean +dependency_conflict(Name target) +{ + Property line; + Property pending_line; + Dependency dp; + Dependency pending_dp; + Running rp; + + /* Return if we are already checking this target */ + if (target->checking_subtree) { + return false; + } + target->checking_subtree = true; + line = get_prop(target->prop, line_prop); + if (line == NULL) { + target->checking_subtree = false; + return false; + } + /* Check each dependency of the target for conflicts */ + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + /* Ignore .WAIT dependency */ + if (dp->name == wait_name) { + continue; + } + /* + * For each pending target, look for a dependency which + * is the same as a dependency of the subtree target. Since + * we can't build the subtree until all pending targets have + * finished which depend on the same dependency, this is + * a conflict. + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state == build_pending) { + pending_line = get_prop(rp->target->prop, + line_prop); + if (pending_line == NULL) { + continue; + } + for(pending_dp = pending_line-> + body.line.dependencies; + pending_dp != NULL; + pending_dp = pending_dp->next) { + if (dp->name == pending_dp->name) { + target->checking_subtree + = false; + subtree_conflict = rp->target; + subtree_conflict2 = dp->name; + return true; + } + } + } + } + if (dependency_conflict(dp->name)) { + target->checking_subtree = false; + return true; + } + } + target->checking_subtree = false; + return false; +} + +/* + * await_parallel(waitflg) + * + * Waits for parallel children to exit and finishes their processing. + * If waitflg is false, the function returns after update_delay. + * + * Parameters: + * waitflg dwight + */ +void +await_parallel(Boolean waitflg) +{ + Boolean nohang; + pid_t pid; + int status; + Running rp; + int waiterr; + + nohang = false; + for ( ; ; ) { + if (!nohang) { + (void) alarm((int) update_delay); + } + pid = waitpid((pid_t)-1, + &status, + nohang ? WNOHANG : 0); + waiterr = errno; + if (!nohang) { + (void) alarm(0); + } + if (pid <= 0) { + if (waiterr == EINTR) { + if (waitflg) { + continue; + } else { + return; + } + } else { + return; + } + } + for (rp = running_list; + (rp != NULL) && (rp->pid != pid); + rp = rp->next) { + ; + } + if (rp == NULL) { + fatal(gettext("Internal error: returned child pid not in running_list")); + } else { + rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed; + } + nohang = true; + parallel_process_cnt--; + + if (job_adjust_mode == ADJUST_M2) { + if (m2_release_job()) { + job_adjust_error(); + } + } + } +} + +/* + * finish_children(docheck) + * + * Finishes the processing for all targets which were running + * and have now completed. + * + * Parameters: + * docheck Completely check the finished target + * + * Static variables used: + * running_tail The tail of the running list + * + * Global variables used: + * continue_after_error -k flag + * fatal_in_progress True if we are finishing up after fatal err + * running_list List of running processes + */ +void +finish_children(Boolean docheck) +{ + int cmds_length; + Property line; + Property line2; + struct stat out_buf; + Running rp; + Running *rp_prev; + Cmd_line rule; + Boolean silent_flag; + + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { +bypass_for_loop_inc_4: + /* + * If the state is ok or failed, then this target has + * finished building. + * In parallel_mode, output the accumulated stdout/stderr. + * Read the auto dependency stuff, handle a failed build, + * update the target, then finish the doname process for + * that target. + */ + if (rp->state == build_ok || rp->state == build_failed) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + if ((line2 = rp->command) == NULL) { + line2 = get_prop(rp->target->prop, line_prop); + } + + + /* + * Check if there were any job output + * from the parallel build. + */ + if (rp->stdout_file != NULL) { + if (stat(rp->stdout_file, &out_buf) < 0) { + fatal(gettext("stat of %s failed: %s"), + rp->stdout_file, + errmsg(errno)); + } + + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + cmds_length = 0; + for (rule = line2->body.line.command_used, + silent_flag = silent; + rule != NULL; + rule = rule->next) { + cmds_length += rule->command_line->hash.length + 1; + silent_flag = BOOLEAN(silent_flag || rule->silent); + } + if (out_buf.st_size != cmds_length || silent_flag || + output_mode == txt2_mode) { + dump_out_file(rp->stdout_file, false); + } + } + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } + + if (!out_err_same && (rp->stderr_file != NULL)) { + if (stat(rp->stderr_file, &out_buf) < 0) { + fatal(gettext("stat of %s failed: %s"), + rp->stderr_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + dump_out_file(rp->stderr_file, true); + } + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } + + check_state(rp->temp_file); + if (rp->temp_file != NULL) { + free_name(rp->temp_file); + } + rp->temp_file = NULL; + if (rp->state == build_failed) { + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + if (continue_after_error || + fatal_in_progress || + !docheck) { + warning(gettext("Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + build_failed_seen = true; + } else { + /* + * XXX??? - DMake needs to exit(), + * but shouldn't call fatal(). + */ +#ifdef PRINT_EXIT_STATUS + warning("I'm in finish_children. rp->state == build_failed."); +#endif + + fatal(gettext("Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + } + } + if (!docheck) { + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } + update_target(get_prop(rp->target->prop, line_prop), + rp->state); + finish_doname(rp); + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } else { + rp_prev = &rp->next; + } + } +} + +/* + * dump_out_file(filename, err) + * + * Write the contents of the file to stdout, then unlink the file. + * + * Parameters: + * filename Name of temp file containing output + * + * Global variables used: + */ +static void +dump_out_file(char *filename, Boolean err) +{ + int chars_read; + char copybuf[BUFSIZ]; + int fd; + int out_fd = (err ? 2 : 1); + + if ((fd = open(filename, O_RDONLY)) < 0) { + fatal(gettext("open failed for output file %s: %s"), + filename, + errmsg(errno)); + } + if (!silent && output_mode != txt2_mode) { + (void) fprintf(err ? stderr : stdout, + err ? + gettext("%s --> Job errors\n") : + gettext("%s --> Job output\n"), + local_host); + (void) fflush(err ? stderr : stdout); + } + for (chars_read = read(fd, copybuf, BUFSIZ); + chars_read > 0; + chars_read = read(fd, copybuf, BUFSIZ)) { + /* + * Read buffers from the source file until end or error. + */ + if (write(out_fd, copybuf, chars_read) < 0) { + fatal(gettext("write failed for output file %s: %s"), + filename, + errmsg(errno)); + } + } + (void) close(fd); + (void) unlink(filename); +} + +/* + * finish_doname(rp) + * + * Completes the processing for a target which was left running. + * + * Parameters: + * rp Running list entry for target + * + * Global variables used: + * debug_level Debug flag + * recursion_level Indentation for debug output + */ +static void +finish_doname(Running rp) +{ + int auto_count = rp->auto_count; + Name *automatics = rp->automatics; + Doname result = rp->state; + Name target = rp->target; + Name true_target = rp->true_target; + Property *conditionals; + + recursion_level = rp->recursion_level; + if (result == build_ok) { + if (true_target == NULL) { + (void) printf("Target = %s\n", target->string_mb); + (void) printf(" State = %d\n", result); + fatal("Internal error: NULL true_target in finish_doname"); + } + /* If all went OK, set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + } + target->state = result; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member */ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = +/* + exists(member->body.member.member); + */ + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + target->rechecking_target = true; + target->state = build_running; + + /* [tolik, Tue Mar 25 1997] + * Fix for bug 4038824: + * command line options set by conditional macros get dropped + * rp->conditional_cnt and rp->conditional_targets must be copied + * to new 'rp' during add_pending(). Set_conditionals() stores + * rp->conditional_targets to the global variable 'conditional_targets' + * Add_pending() will use this variable to set up 'rp'. + */ + conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets); + add_pending(target, + recursion_level, + rp->do_get, + rp->implicit, + true); + reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals); + } +} + +/* + * new_running_struct() + * + * Constructor for Running struct. Creates a structure and initializes + * its fields. + * + */ +static Running new_running_struct() +{ + Running rp; + + rp = ALLOC(Running); + rp->target = NULL; + rp->true_target = NULL; + rp->command = NULL; + rp->sprodep_value = NULL; + rp->sprodep_env = NULL; + rp->auto_count = 0; + rp->automatics = NULL; + rp->pid = -1; + rp->job_msg_id = -1; + rp->stdout_file = NULL; + rp->stderr_file = NULL; + rp->temp_file = NULL; + rp->next = NULL; + return rp; +} + +/* + * add_running(target, true_target, command, recursion_level, auto_count, + * automatics, do_get, implicit) + * + * Adds a record on the running list for this target, which + * was just spawned and is running. + * + * Parameters: + * target Target being built + * true_target True target for target + * command Running command. + * recursion_level Debug indentation level + * auto_count Count of automatic dependencies + * automatics List of automatic dependencies + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + * process_running PID of process + * + * Global variables used: + * current_line Current line for target + * current_target Current target being built + * stderr_file Temporary file for stdout + * stdout_file Temporary file for stdout + * temp_file_name Temporary file for auto dependencies + */ +void +add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit) +{ + Running rp; + Name *p; + + rp = new_running_struct(); + rp->state = build_running; + rp->target = target; + rp->true_target = true_target; + rp->command = command; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->auto_count = auto_count; + if (auto_count > 0) { + rp->automatics = (Name *) getmem(auto_count * sizeof (Name)); + for (p = rp->automatics; auto_count > 0; auto_count--) { + *p++ = *automatics++; + } + } else { + rp->automatics = NULL; + } + { + rp->pid = process_running; + process_running = -1; + childPid = -1; + } + rp->job_msg_id = job_msg_id; + rp->stdout_file = stdout_file; + rp->stderr_file = stderr_file; + rp->temp_file = temp_file_name; + rp->redo = false; + rp->next = NULL; + store_conditionals(rp); + stdout_file = NULL; + stderr_file = NULL; + temp_file_name = NULL; + current_target = NULL; + current_line = NULL; + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_pending(target, recursion_level, do_get, implicit, redo) + * + * Adds a record on the running list for a pending target + * (waiting for its dependents to finish running). + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * redo True if this target is being redone + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo) +{ + Running rp; + rp = new_running_struct(); + rp->state = build_pending; + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->redo = redo; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_serial(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in serial after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_serial; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_subtree(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in isolation after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_subtree; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * store_conditionals(rp) + * + * Creates an array of the currently active targets with conditional + * macros (found in the chain conditional_targets) and puts that + * array in the Running struct. + * + * Parameters: + * rp Running struct for storing chain + * + * Global variables used: + * conditional_targets Chain of current dynamic conditionals + */ +static void +store_conditionals(Running rp) +{ + int cnt; + Chain cond_name; + + if (conditional_targets == NULL) { + rp->conditional_cnt = 0; + rp->conditional_targets = NULL; + return; + } + cnt = 0; + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + cnt++; + } + rp->conditional_cnt = cnt; + rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name)); + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + rp->conditional_targets[--cnt] = cond_name->name; + } +} + +/* + * parallel_ok(target, line_prop_must_exists) + * + * Returns true if the target can be run in parallel + * + * Return value: + * True if can run in parallel + * + * Parameters: + * target Target being tested + * + * Global variables used: + * all_parallel True if all targets default to parallel + * only_parallel True if no targets default to parallel + */ +Boolean +parallel_ok(Name target, Boolean line_prop_must_exists) +{ + Boolean assign; + Boolean make_refd; + Property line; + Cmd_line rule; + + assign = make_refd = false; + if (((line = get_prop(target->prop, line_prop)) == NULL) && + line_prop_must_exists) { + return false; + } + if (line != NULL) { + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (rule->assign) { + assign = true; + } else if (rule->make_refd) { + make_refd = true; + } + } + } + if (assign) { + return false; + } else if (target->parallel) { + return true; + } else if (target->no_parallel) { + return false; + } else if (all_parallel) { + return true; + } else if (only_parallel) { + return false; + } else if (make_refd) { + return false; + } else { + return true; + } +} + +/* + * is_running(target) + * + * Returns true if the target is running. + * + * Return value: + * True if target is running + * + * Parameters: + * target Target to check + * + * Global variables used: + * running_list List of running processes + */ +Boolean +is_running(Name target) +{ + Running rp; + + if (target->state != build_running) { + return false; + } + for (rp = running_list; + rp != NULL && target != rp->target; + rp = rp->next); + if (rp == NULL) { + return false; + } else { + return (rp->state == build_running) ? true : false; + } +} + +/* + * This function replaces the makesh binary. + */ + + +static pid_t +run_rule_commands(char *host, char **commands) +{ + Boolean always_exec; + Name command; + Boolean ignore; + int length; + Doname result; + Boolean silent_flag; + wchar_t *tmp_wcs_buffer; + + childPid = fork(); + switch (childPid) { + case -1: /* Error */ + fatal(gettext("Could not fork child process for dmake job: %s"), + errmsg(errno)); + break; + case 0: /* Child */ + /* To control the processed targets list is not the child's business */ + running_list = NULL; + if(out_err_same) { + redirect_io(stdout_file, (char*)NULL); + } else { + redirect_io(stdout_file, stderr_file); + } + for (commands = commands; + (*commands != (char *)NULL); + commands++) { + silent_flag = silent; + ignore = false; + always_exec = false; + while ((**commands == (int) at_char) || + (**commands == (int) hyphen_char) || + (**commands == (int) plus_char)) { + if (**commands == (int) at_char) { + silent_flag = true; + } + if (**commands == (int) hyphen_char) { + ignore = true; + } + if (**commands == (int) plus_char) { + always_exec = true; + } + (*commands)++; + } + if ((length = strlen(*commands)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1); + command = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, *commands); + command = GETNAME(wcs_buffer, FIND_LENGTH); + } + if ((command->hash.length > 0) && + !silent_flag) { + (void) printf("%s\n", command->string_mb); + } + result = dosys(command, + ignore, + false, + false, /* bugs #4085164 & #4990057 */ + /* BOOLEAN(silent_flag && ignore), */ + always_exec, + (Name) NULL); + if (result == build_failed) { + if (silent_flag) { + (void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb); + } + if (!ignore) { + _exit(1); + } + } + } + _exit(0); + break; + default: + break; + } + return childPid; +} + +static void +maybe_reread_make_state(void) +{ + /* Copying dosys()... */ + if (report_dependencies_level == 0) { + make_state->stat.time = file_no_time; + (void) exists(make_state); + if (make_state_before == make_state->stat.time) { + return; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } +} + + +static void +delete_running_struct(Running rp) +{ + if ((rp->conditional_cnt > 0) && + (rp->conditional_targets != NULL)) { + retmem_mb((char *) rp->conditional_targets); + } +/**/ + if ((rp->auto_count > 0) && + (rp->automatics != NULL)) { + retmem_mb((char *) rp->automatics); + } +/**/ + if(rp->sprodep_value) { + free_name(rp->sprodep_value); + } + if(rp->sprodep_env) { + retmem_mb(rp->sprodep_env); + } + retmem_mb((char *) rp); + +} + + diff --git a/bin/pmake.cc b/bin/pmake.cc new file mode 100644 index 0000000..660d8c0 --- /dev/null +++ b/bin/pmake.cc @@ -0,0 +1,420 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Included files + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* host2netname(), netname2host() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static int get_max(wchar_t **ms_address, wchar_t *hostname); +static Boolean pskip_comment(wchar_t **cp_address); +static void pskip_till_next_word(wchar_t **cp); +static Boolean pskip_white_space(wchar_t **cp_address); + + +/* + * read_make_machines(Name make_machines_name) + * + * For backwards compatibility w/ PMake 1.x, when DMake 2.x is + * being run in parallel mode, DMake should parse the PMake startup + * file $(HOME)/.make.machines to get the PMake max jobs. + * + * Return value: + * int of PMake max jobs + * + * Parameters: + * make_machines_name Name of .make.machines file + * + */ +int +read_make_machines(Name make_machines_name) +{ + wchar_t c; + Boolean default_make_machines; + struct hostent *hp; + wchar_t local_host[MAX_HOSTNAMELEN + 1]; + char local_host_mb[MAX_HOSTNAMELEN + 1] = ""; + int local_host_wslen; + wchar_t full_host[MAXNETNAMELEN + 1]; + int full_host_wslen = 0; + char *homedir; + Name MAKE_MACHINES; + struct stat make_machines_buf; + FILE *make_machines_file; + wchar_t *make_machines_list = NULL; + char *make_machines_list_mb = NULL; + wchar_t make_machines_path[MAXPATHLEN]; + char mb_make_machines_path[MAXPATHLEN]; + wchar_t *mp; + wchar_t *ms; + int pmake_max_jobs = 0; + struct utsname uts_info; + + + MBSTOWCS(wcs_buffer, "MAKE_MACHINES"); + MAKE_MACHINES = GETNAME(wcs_buffer, FIND_LENGTH); + /* Did the user specify a .make.machines file on the command line? */ + default_make_machines = false; + if (make_machines_name == NULL) { + /* Try reading the default .make.machines file, in $(HOME). */ + homedir = getenv("HOME"); + if ((homedir != NULL) && (strlen(homedir) < (sizeof(mb_make_machines_path) - 16))) { + sprintf(mb_make_machines_path, + "%s/.make.machines", homedir); + MBSTOWCS(make_machines_path, mb_make_machines_path); + make_machines_name = GETNAME(make_machines_path, FIND_LENGTH); + default_make_machines = true; + } + if (make_machines_name == NULL) { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + } +/* + make_machines_list_mb = getenv(MAKE_MACHINES->string_mb); + */ + /* Open the .make.machines file. */ + if ((make_machines_file = fopen(make_machines_name->string_mb, "r")) == NULL) { + if (!default_make_machines) { + /* Error opening .make.machines file. */ + fatal(gettext("Open of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + /* Stat the .make.machines file to get the size of the file. */ + } else if (fstat(fileno(make_machines_file), &make_machines_buf) < 0) { + /* Error stat'ing .make.machines file. */ + fatal(gettext("Stat of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* Allocate memory for "MAKE_MACHINES=" */ + make_machines_list_mb = + (char *) getmem((int) (strlen(MAKE_MACHINES->string_mb) + + 2 + + make_machines_buf.st_size)); + sprintf(make_machines_list_mb, + "%s=", + MAKE_MACHINES->string_mb); + /* Read in the .make.machines file. */ + if (fread(make_machines_list_mb + strlen(MAKE_MACHINES->string_mb) + 1, + sizeof(char), + (int) make_machines_buf.st_size, + make_machines_file) != make_machines_buf.st_size) { + /* + * Error reading .make.machines file. + * Return 0 for PMake max jobs. + */ + warning(gettext("Unable to read %s"), + make_machines_name->string_mb); + (void) fclose(make_machines_file); + retmem_mb((caddr_t) make_machines_list_mb); + return(0); + } else { + (void) fclose(make_machines_file); + /* putenv "MAKE_MACHINES=" */ + *(make_machines_list_mb + + strlen(MAKE_MACHINES->string_mb) + + 1 + + make_machines_buf.st_size) = (int) nul_char; + if (putenv(make_machines_list_mb) != 0) { + warning(gettext("Couldn't put contents of %s in environment"), + make_machines_name->string_mb); + } else { + make_machines_list_mb += strlen(MAKE_MACHINES->string_mb) + 1; + make_machines_list = ALLOC_WC(strlen(make_machines_list_mb) + 1); + (void) mbstowcs(make_machines_list, + make_machines_list_mb, + (strlen(make_machines_list_mb) + 1)); + } + } + } + + uname(&uts_info); + strcpy(local_host_mb, &uts_info.nodename[0]); + MBSTOWCS(local_host, local_host_mb); + local_host_wslen = wcslen(local_host); + + // There is no getdomainname() function on Solaris. + // And netname2host() function does not work on Linux. + // So we have to use different APIs. + if (host2netname(mbs_buffer, NULL, NULL) && + netname2host(mbs_buffer, mbs_buffer2, MAXNETNAMELEN+1)) { + MBSTOWCS(full_host, mbs_buffer2); + full_host_wslen = wcslen(full_host); + } + + for (ms = make_machines_list; + (ms) && (*ms ); + ) { + /* + * Skip white space and comments till you reach + * a machine name. + */ + pskip_till_next_word(&ms); + + /* + * If we haven't reached the end of file, process the + * machine name. + */ + if (*ms) { + /* + * If invalid machine name decrement counter + * and skip line. + */ + mp = ms; + SKIPWORD(ms); + c = *ms; + *ms++ = '\0'; /* Append null to machine name. */ + /* + * If this was the beginning of a comment + * (we overwrote a # sign) and it's not + * end of line yet, shift the # sign. + */ + if ((c == '#') && (*ms != '\n') && (*ms)) { + *ms = '#'; + } + WCSTOMBS(mbs_buffer, mp); + /* + * Print "Ignoring unknown host" if: + * 1) hostname is longer than MAX_HOSTNAMELEN, or + * 2) hostname is unknown + */ + if ((wcslen(mp) > MAX_HOSTNAMELEN) || + ((hp = gethostbyname(mbs_buffer)) == NULL)) { + warning(gettext("Ignoring unknown host %s"), + mbs_buffer); + SKIPTOEND(ms); + /* Increment ptr if not end of file. */ + if (*ms) { + ms++; + } + } else { + /* Compare current hostname with local_host. */ + if (wcslen(mp) == local_host_wslen && + IS_WEQUALN(mp, local_host, local_host_wslen)) { + /* + * Bingo, local_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + /* Compare current hostname with full_host. */ + } else if (wcslen(mp) == full_host_wslen && + IS_WEQUALN(mp, full_host, full_host_wslen)) { + /* + * Bingo, full_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } else { + if (c != '\n') { + SKIPTOEND(ms); + if (*ms) { + ms++; + } + } + continue; + } + /* If we get here, local_host is in .make.machines. */ + if (c != '\n') { + /* Now look for keyword 'max'. */ + MBSTOWCS(wcs_buffer, "max"); + SKIPSPACE(ms); + while ((*ms != '\n') && (*ms)) { + if (*ms == '#') { + pskip_comment(&ms); + } else if (IS_WEQUALN(ms, wcs_buffer, 3)) { + /* Skip "max". */ + ms += 3; + pmake_max_jobs = get_max(&ms, mp); + SKIPSPACE(ms); + } else { + warning(gettext("unknown option for host %s"), mbs_buffer); + SKIPTOEND(ms); + break; + } + } + } + break; /* out of outermost for() loop. */ + } + } + } + retmem(make_machines_list); + return(pmake_max_jobs); +} + +/* + * pskip_till_next_word(cp) + * + * Parameters: + * cp the address of the string pointer. + * + * On return: + * cp points to beginning of machine name. + * + */ +static void +pskip_till_next_word(wchar_t **cp) +{ + /* + * Keep recursing until all combinations of white spaces + * and comments have been skipped. + */ + if (pskip_white_space(cp) || pskip_comment(cp)) { + pskip_till_next_word(cp); + } +} + +/* + * pskip_white_space(cp_address) + * + * Advances the string pointer so that it points to the first + * non white character (space/tab/linefeed). + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_white_space(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + while (*cp && iswspace(*cp)) { + cp++; + } + /* Have we skipped any characters? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +/* + * pskip_comment(cp_address) + * + * If cp_address is pointing to '#' (the beginning of a comment), + * increment the pointer till you reach end of line. + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_comment(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + /* Is this the beginning of a comment? Skip till end of line. */ + if (*cp == '#') { + SKIPTOEND(cp); + } + /* Have we skipped a comment line? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +static int +get_max(wchar_t **ms_address, wchar_t *hostname) +{ + wchar_t *ms = *ms_address; + int limit = PMAKE_DEF_MAX_JOBS; /* Default setting. */ + + WCSTOMBS(mbs_buffer, hostname); + /* Look for `='. */ + SKIPSPACE(ms); + if ((!*ms) || (*ms == '\n') || (*ms != '=')) { + SKIPTOEND(ms); + warning(gettext("expected `=' after max, ignoring rest of line for host %s"), + mbs_buffer); + *ms_address = ms; + return((int) limit); + } else { + ms++; + SKIPSPACE(ms); + if ((*ms != '\n') && (*ms != '\0')) { + /* We've found, hopefully, a valid "max" value. */ + limit = (int) wcstol(ms, &ms, 10); + if (limit < 1) { + limit = PMAKE_DEF_MAX_JOBS; + warning(gettext("max value cannot be less than or equal to zero for host %s"), mbs_buffer); + } + } else { + /* No "max" value after "max=". */ + warning(gettext("no max value specified for host %s"), mbs_buffer); + } + *ms_address = ms; + return(limit); + } +} + + diff --git a/bin/read.cc b/bin/read.cc new file mode 100644 index 0000000..0d47f5b --- /dev/null +++ b/bin/read.cc @@ -0,0 +1,2148 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include /* alloca() */ +#include /* errno */ +#include /* fcntl() */ +#include +#include /* expand_value(), expand_macro() */ +#include /* getmem() */ +#include /* get_next_block_fn() */ +#include /* read() */ +#include /* read(), unlink() */ +#include + + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +static int line_started_with_space=0; // Used to diagnose spaces instead of tabs + +/* + * File table of contents + */ +static void parse_makefile(register Name true_makefile_name, register Source source); +static Source push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source); +extern void enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen); +extern Name normalize_name(register wchar_t *name_string, register int length); + +/* + * read_simple_file(makefile_name, chase_path, doname_it, + * complain, must_exist, report_file, lock_makefile) + * + * Make the makefile and setup to read it. Actually read it if it is stdio + * + * Return value: + * false if the read failed + * + * Parameters: + * makefile_name Name of the file to read + * chase_path Use the makefile path when opening file + * doname_it Call doname() to build the file first + * complain Print message if doname/open fails + * must_exist Generate fatal if file is missing + * report_file Report file when running -P + * lock_makefile Lock the makefile when reading + * + * Static variables used: + * + * Global variables used: + * do_not_exec_rule Is -n on? + * file_being_read Set to the name of the new file + * line_number The number of the current makefile line + * makefiles_used A list of all makefiles used, appended to + */ + + +Boolean +read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile) +{ + static short max_include_depth; + register Property makefile = maybe_append_prop(makefile_name, + makefile_prop); + Boolean forget_after_parse = false; + static pathpt makefile_path; + register int n; + char *path; + register Source source = ALLOC(Source); + Property orig_makefile = makefile; + Dependency *dpp; + Dependency dp; + register int length; + wchar_t *previous_file_being_read = file_being_read; + int previous_line_number = line_number; + wchar_t previous_current_makefile[MAXPATHLEN]; + Makefile_type save_makefile_type; + Name normalized_makefile_name; + register wchar_t *string_start; + register wchar_t *string_end; + + + + wchar_t * wcb = get_wstring(makefile_name->string_mb); + + if (max_include_depth++ >= 40) { + fatal(gettext("Too many nested include statements")); + } + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + } + source->inp_buf = + source->inp_buf_ptr = + source->inp_buf_end = NULL; + source->error_converting = false; + makefile->body.makefile.contents = NULL; + makefile->body.makefile.size = 0; + if ((makefile_name->hash.length != 1) || + (wcb[0] != (int) hyphen_char)) { + if ((makefile->body.makefile.contents == NULL) && + (doname_it)) { + if (makefile_path == NULL) { + char *pfx = make_install_prefix(); + char *path; + + add_dir_to_path(".", + &makefile_path, + -1); + + // As regularly installed + asprintf(&path, "%s/../share/lib/make", pfx); + add_dir_to_path(path, &makefile_path, -1); + free(path); + + // Tools build + asprintf(&path, "%s/../../share/", pfx); + add_dir_to_path(path, &makefile_path, -1); + free(path); + + add_dir_to_path("/usr/share/lib/make", + &makefile_path, + -1); + add_dir_to_path("/etc/default", + &makefile_path, + -1); + + free(pfx); + } + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if (doname(makefile_name, true, false) == build_dont_know) { + /* Try normalized filename */ + string_start=get_wstring(makefile_name->string_mb); + for (string_end=string_start+1; *string_end != L'\0'; string_end++); + normalized_makefile_name=normalize_name(string_start, string_end - string_start); + if ((strcmp(makefile_name->string_mb, normalized_makefile_name->string_mb) == 0) || + (doname(normalized_makefile_name, true, false) == build_dont_know)) { + n = access_vroot(makefile_name->string_mb, + 4, + chase_path ? + makefile_path : NULL, + VROOT_DEFAULT); + if (n == 0) { + get_vroot_path((char **) NULL, + &path, + (char **) NULL); + if ((path[0] == (int) period_char) && + (path[1] == (int) slash_char)) { + path += 2; + } + MBSTOWCS(wcs_buffer, path); + makefile_name = GETNAME(wcs_buffer, + FIND_LENGTH); + } + } + retmem(string_start); + /* + * Commented out: retmem_mb(normalized_makefile_name->string_mb); + * We have to return this memory, but it seems to trigger a bug + * in dmake or in Sun C++ 5.7 compiler (it works ok if this code + * is compiled using Sun C++ 5.6). + */ + // retmem_mb(normalized_makefile_name->string_mb); + } + makefile_type = save_makefile_type; + } + source->string.free_after_use = false; + source->previous = NULL; + source->already_expanded = false; + /* Lock the file for read, but not when -n. */ + if (lock_makefile && + !do_not_exec_rule) { + + make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1); + (void) sprintf(make_state_lockfile, + "%s.lock", + make_state->string_mb); + (void) file_lock(make_state->string_mb, + make_state_lockfile, + (int *) &make_state_locked, + 0); + if(!make_state_locked) { + printf("-- NO LOCKING for read\n"); + retmem_mb(make_state_lockfile); + make_state_lockfile = 0; + return failed; + } + } + if (makefile->body.makefile.contents == NULL) { + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if ((doname_it) && + (doname(makefile_name, true, false) == build_failed)) { + if (complain) { + (void) fprintf(stderr, + gettext("%s: Couldn't make `%s'\n"), + getprogname(), + makefile_name->string_mb); + } + max_include_depth--; + makefile_type = save_makefile_type; + return failed; + } + makefile_type = save_makefile_type; + // + // Before calling exists() make sure that we have the right timestamp + // + makefile_name->stat.time = file_no_time; + + if (exists(makefile_name) == file_doesnt_exist) { + if (complain || + (makefile_name->stat.stat_errno != ENOENT)) { + if (must_exist) { + fatal(gettext("Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } else { + warning(gettext("Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } + } + max_include_depth--; + if(make_state_locked && (make_state_lockfile != NULL)) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + retmem(wcb); + retmem_mb((char *)source); + return failed; + } + /* + * These values are the size and bytes of + * the MULTI-BYTE makefile. + */ + orig_makefile->body.makefile.size = + makefile->body.makefile.size = + source->bytes_left_in_file = + makefile_name->stat.size; + if (report_file) { + for (dpp = &makefiles_used; + *dpp != NULL; + dpp = &(*dpp)->next); + dp = ALLOC(Dependency); + dp->next = NULL; + dp->name = makefile_name; + dp->automatic = false; + dp->stale = false; + dp->built = false; + *dpp = dp; + } + source->fd = open_vroot(makefile_name->string_mb, + O_RDONLY, + 0, + NULL, + VROOT_DEFAULT); + if (source->fd < 0) { + if (complain || (errno != ENOENT)) { + if (must_exist) { + fatal(gettext("Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } else { + warning(gettext("Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } + } + max_include_depth--; + return failed; + } + (void) fcntl(source->fd, F_SETFD, 1); + orig_makefile->body.makefile.contents = + makefile->body.makefile.contents = + source->string.text.p = + source->string.buffer.start = + ALLOC_WC((int) (makefile_name->stat.size + 2)); + if (makefile_type == reading_cpp_file) { + forget_after_parse = true; + } + source->string.text.end = source->string.text.p; + source->string.buffer.end = + source->string.text.p + makefile_name->stat.size; + } else { + /* Do we ever reach here? */ + source->fd = -1; + source->string.text.p = + source->string.buffer.start = + makefile->body.makefile.contents; + source->string.text.end = + source->string.buffer.end = + source->string.text.p + makefile->body.makefile.size; + source->bytes_left_in_file = + makefile->body.makefile.size; + } + file_being_read = wcb; + } else { + char *stdin_text_p; + char *stdin_text_end; + char *stdin_buffer_start; + char *stdin_buffer_end; + char *p_mb; + int num_mb_chars; + size_t num_wc_chars; + + MBSTOWCS(wcs_buffer, "Standard in"); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * Memory to read standard in, then convert it + * to wide char strings. + */ + stdin_buffer_start = + stdin_text_p = getmem(length = 1024); + stdin_buffer_end = stdin_text_p + length; + MBSTOWCS(wcs_buffer, "standard input"); + file_being_read = (wchar_t *) wcsdup(wcs_buffer); + line_number = 0; + while ((n = read(fileno(stdin), + stdin_text_p, + length)) > 0) { + length -= n; + stdin_text_p += n; + if (length == 0) { + p_mb = getmem(length = 1024 + + (stdin_buffer_end - + stdin_buffer_start)); + (void) strncpy(p_mb, + stdin_buffer_start, + (stdin_buffer_end - + stdin_buffer_start)); + retmem_mb(stdin_buffer_start); + stdin_text_p = p_mb + + (stdin_buffer_end - stdin_buffer_start); + stdin_buffer_start = p_mb; + stdin_buffer_end = + stdin_buffer_start + length; + length = 1024; + } + } + if (n < 0) { + fatal(gettext("Error reading standard input: %s"), + errmsg(errno)); + } + stdin_text_p = stdin_buffer_start; + stdin_text_end = stdin_buffer_end - length; + num_mb_chars = stdin_text_end - stdin_text_p; + + /* + * Now, convert the sequence of multibyte chars into + * a sequence of corresponding wide character codes. + */ + source->string.free_after_use = false; + source->previous = NULL; + source->bytes_left_in_file = 0; + source->fd = -1; + source->already_expanded = false; + source->string.buffer.start = + source->string.text.p = ALLOC_WC(num_mb_chars + 1); + source->string.buffer.end = + source->string.text.p + num_mb_chars; + num_wc_chars = mbstowcs(source->string.text.p, + stdin_text_p, + num_mb_chars); + if ((int) num_wc_chars >= 0) { + source->string.text.end = + source->string.text.p + num_wc_chars; + } + (void) retmem_mb(stdin_text_p); + } + line_number = 1; + if (trace_reader) { + (void) printf(gettext(">>>>>>>>>>>>>>>> Reading makefile %s\n"), + makefile_name->string_mb); + } + parse_makefile(makefile_name, source); + if (trace_reader) { + (void) printf(gettext(">>>>>>>>>>>>>>>> End of makefile %s\n"), + makefile_name->string_mb); + } + if(file_being_read) { + retmem(file_being_read); + } + file_being_read = previous_file_being_read; + line_number = previous_line_number; + makefile_type = reading_nothing; + max_include_depth--; + if (make_state_locked) { + /* Unlock .make.state. */ + unlink(make_state_lockfile); + make_state_locked = false; + retmem_mb(make_state_lockfile); + } + if (forget_after_parse) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + retmem_mb((char *)source); + return succeeded; +} + +/* + * parse_makefile(true_makefile_name, source) + * + * Strings are read from Sources. + * When macros are found, their values are represented by a + * Source that is pushed on a stack. At end of string + * (that is returned from GET_CHAR() as 0), the block is popped. + * + * Parameters: + * true_makefile_name The name of makefile we are parsing + * source The source block to read from + * + * Global variables used: + * do_not_exec_rule Is -n on? + * line_number The number of the current makefile line + * makefile_type What kind of makefile are we reading? + * empty_name The Name "" + */ +static void +parse_makefile(register Name true_makefile_name, register Source source) +{ +/* + char mb_buffer[MB_LEN_MAX]; + */ + register wchar_t *source_p; + register wchar_t *source_end; + register wchar_t *string_start; + wchar_t *string_end; + register Boolean macro_seen_in_string; + Boolean append; + String_rec name_string; + wchar_t name_buffer[STRING_BUFFER_LENGTH]; + register int distance; + register int paren_count; + int brace_count; + int char_number; + Cmd_line command; + Cmd_line command_tail; + Name macro_value; + + Name_vector_rec target; + Name_vector_rec depes; + Name_vector_rec extra_name_vector; + Name_vector current_names; + Name_vector extra_names = &extra_name_vector; + Name_vector nvp; + Boolean target_group_seen; + + register Reader_state state; + register Reader_state on_eoln_state; + register Separator separator; + + wchar_t buffer[4 * STRING_BUFFER_LENGTH]; + Source extrap; + + Boolean save_do_not_exec_rule = do_not_exec_rule; + Name makefile_name; + + static Name sh_name; + static Name shell_name; + int i; + + static wchar_t include_space[10]; + static wchar_t include_tab[10]; + int tmp_bytes_left_in_string; + Boolean tmp_maybe_include = false; + int emptycount = 0; + Boolean first_target; + + String_rec include_name; + wchar_t include_buffer[STRING_BUFFER_LENGTH]; + + target.next = depes.next = NULL; + /* Move some values from their struct to register declared locals */ + CACHE_SOURCE(0); + + start_new_line: + /* + * Read whitespace on old line. Leave pointer on first char on + * next line. + */ + first_target = true; + on_eoln_state = exit_state; +/* + for (WCTOMB(mb_buffer, GET_CHAR()); + 1; + source_p++, WCTOMB(mb_buffer, GET_CHAR())) + switch (mb_buffer[0]) { + */ + for (char_number=0; 1; source_p++,char_number++) switch (GET_CHAR()) { + case nul_char: + /* End of this string. Pop it and return to the previous one */ + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + end_of_line: + source_p++; + if (source->fd >= 0) { + line_number++; + } + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + /* Go back to the top of this loop */ + goto start_new_line; + case newline_char: + case numbersign_char: + case dollar_char: + case space_char: + case tab_char: + /* + * Go back to the top of this loop since the + * new line does not start with a regular char. + */ + goto start_new_line; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + case space_char: + if (char_number == 0) + line_started_with_space=line_number; + case tab_char: + /* Whitespace. Just keep going in this loop */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + /* Comments can be continued */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + /* + * After we skip the comment we go to + * the end of line handler since end of + * line terminates comments. + */ + goto end_of_line; + } + } + case dollar_char: + /* Macro reference */ + if (source->already_expanded) { + /* + * If we are reading from the expansion of a + * macro we already expanded everything enough. + */ + goto start_new_line_no_skip; + } + /* + * Expand the value and push the Source on the stack of + * things being read. + */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + + /* + * We found the first normal char (one that starts an identifier) + * on the newline. + */ +start_new_line_no_skip: + /* Inspect that first char to see if it maybe is special anyway */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + goto start_new_line_no_skip; + case newline_char: + /* Just in case */ + goto start_new_line; + case exclam_char: + /* Evaluate the line before it is read */ + string_start = source_p + 1; + macro_seen_in_string = false; + /* Stuff the line in a string so we can eval it. */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_1; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK( + name_string, name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_1: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + if (*source_p == (int) nul_char) { + source_p++; + } + /* Eval the macro */ + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + /* And push the value */ + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto line_evald; + } + } + default: + goto line_evald; + } + + /* We now have a line we can start reading */ + line_evald: + if (source == NULL) { + GOTO_STATE(exit_state); + } + /* Check if this is an include command */ + if ((makefile_type == reading_makefile) && + !source->already_expanded) { + if (include_space[0] == (int) nul_char) { + MBSTOWCS(include_space, "include "); + MBSTOWCS(include_tab, "include\t"); + } + if ((IS_WEQUALN(source_p, include_space, 8)) || + (IS_WEQUALN(source_p, include_tab, 8))) { + source_p += 7; + if (iswspace(*source_p)) { + Makefile_type save_makefile_type; + wchar_t *name_start; + int name_length; + + /* + * Yes, this is an include. + * Skip spaces to get to the filename. + */ + while (iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + string_start = source_p; + /* Find the end of the filename */ + macro_seen_in_string = false; + while (!iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + source->string.text.p = source_p; + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + name_start = name_string.buffer.start; + name_length = name_string.text.p - name_start; + } else { + name_start = string_start; + name_length = source_p - string_start; + } + + /* Strip "./" from the head of the name */ + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + /* if include file name is surrounded by double quotes */ + if ((name_start[0] == (int) doublequote_char) && + (name_start[name_length - 1] == (int) doublequote_char)) { + name_start += 1; + name_length -= 2; + + /* if name does not begin with a slash char */ + if (name_start[0] != (int) slash_char) { + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + + INIT_STRING_FROM_STACK(include_name, include_buffer); + APPEND_NAME(true_makefile_name, + &include_name, + true_makefile_name->hash.length); + + wchar_t *slash = wcsrchr(include_name.buffer.start, (int) slash_char); + if (slash != NULL) { + include_name.text.p = slash + 1; + append_string(name_start, + &include_name, + name_length); + + name_start = include_name.buffer.start; + name_length = include_name.text.p - name_start; + } + } + } + + /* Even when we run -n we want to create makefiles */ + do_not_exec_rule = false; + makefile_name = GETNAME(name_start, name_length); + if (makefile_name->dollar) { + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *p; + wchar_t *q; + + INIT_STRING_FROM_STACK(destination, buffer); + expand_value(makefile_name, + &destination, + false); + for (p = destination.buffer.start; + (*p != (int) nul_char) && iswspace(*p); + p++); + for (q = p; + (*q != (int) nul_char) && !iswspace(*q); + q++); + makefile_name = GETNAME(p, q-p); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } + source_p++; + UNCACHE_SOURCE(); + /* Read the file */ + save_makefile_type = makefile_type; + if (read_simple_file(makefile_name, + true, + true, + true, + false, + true, + false) == failed) { + fatal_reader(gettext("Read of include file `%s' failed"), + makefile_name->string_mb); + } + makefile_type = save_makefile_type; + do_not_exec_rule = save_do_not_exec_rule; + CACHE_SOURCE(0); + goto start_new_line; + } else { + source_p -= 7; + } + } else { + /* Check if the word include was split across 8K boundary. */ + + tmp_bytes_left_in_string = source->string.text.end - source_p; + if (tmp_bytes_left_in_string < 8) { + tmp_maybe_include = false; + if (IS_WEQUALN(source_p, + include_space, + tmp_bytes_left_in_string)) { + tmp_maybe_include = true; + } + if (tmp_maybe_include) { + GET_NEXT_BLOCK(source); + tmp_maybe_include = false; + goto line_evald; + } + } + } + } + + /* Reset the status in preparation for the new line */ + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + for (nvp = &depes; nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + target_group_seen = false; + command = command_tail = NULL; + macro_value = NULL; + append = false; + current_names = ⌖ + SET_STATE(scan_name_state); + on_eoln_state = illegal_eoln_state; + separator = none_seen; + + /* The state machine starts here */ + enter_state: + while (1) switch (state) { + +/**************************************************************** + * Scan name state + */ +case scan_name_state: + /* Scan an identifier. We skip over chars until we find a break char */ + /* First skip white space. */ + for (; 1; source_p++) switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + /* We found the end of the line. */ + /* Do postprocessing or return error */ + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Continuation */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } else { + source_p--; + } + break; + case tab_char: + case space_char: + /* Whitespace is skipped */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + } + } + case dollar_char: + /* Macro reference. Expand and push value */ + if (source->already_expanded) { + goto scan_name; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* End of white space */ + goto scan_name; + } + + /* First proper identifier character */ + scan_name: + + string_start = source_p; + paren_count = brace_count = 0; + macro_seen_in_string = false; + resume_name_scan: + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + /* Save what we have seen so far of the identifier */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Get more text to read */ + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + if (paren_count > 0) { + fatal_reader(gettext("Unmatched `(' on line")); + } + if (brace_count > 0) { + fatal_reader(gettext("Unmatched `{' on line")); + } + source_p++; + /* Enter name */ + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p - 1, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + /* Do postprocessing or return error */ + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Check if this is a quoting backslash */ + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + macro_seen_in_string = true; + } + append_string(string_start, + &name_string, + source_p - string_start); + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + *source_p = (int) space_char; + string_start = source_p; + goto resume_name_scan; + } else { + string_start = source_p; + break; + } + break; + case numbersign_char: + if (paren_count + brace_count > 0) { + break; + } + fatal_reader(gettext("Unexpected comment seen")); + case dollar_char: + if (source->already_expanded) { + break; + } + /* Save the identifier so far */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Eval and push the macro */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = + (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + string_start = source_p + 1; + break; + case parenleft_char: + paren_count++; + break; + case parenright_char: + if (--paren_count < 0) { + fatal_reader(gettext("Unmatched `)' on line")); + } + break; + case braceleft_char: + brace_count++; + break; + case braceright_char: + if (--brace_count < 0) { + fatal_reader(gettext("Unmatched `}' on line")); + } + break; + case ampersand_char: + case greater_char: + case bar_char: + if (paren_count + brace_count == 0) { + source_p++; + } + /* Fall into */ + case tab_char: + case space_char: + if (paren_count + brace_count > 0) { + break; + } + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + goto enter_state; + case colon_char: + if (paren_count + brace_count > 0) { + break; + } + if (separator == conditional_seen) { + break; + } +/** POSIX **/ +#if 0 + if(posix) { + emptycount = 0; + } +#endif +/** END POSIX **/ + /* End of the target list. We now start reading */ + /* dependencies or a conditional assignment */ + if (separator != none_seen) { + fatal_reader(gettext("Extra `:', `::', or `:=' on dependency line")); + } + /* Enter the last target */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + /* Check if it is ":" "::" or ":=" */ + scan_colon_label: + switch (*++source_p) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + goto scan_colon_label; + case equal_char: + if(svr4) { + fatal_reader(gettext("syntax error")); + } + separator = conditional_seen; + source_p++; + current_names = &depes; + GOTO_STATE(scan_name_state); + case colon_char: + separator = two_colon; + source_p++; + break; + default: + separator = one_colon; + } + current_names = &depes; + on_eoln_state = enter_dependencies_state; + GOTO_STATE(scan_name_state); + case semicolon_char: + if (paren_count + brace_count > 0) { + break; + } + /* End of reading names. Start reading the rule */ + if ((separator != one_colon) && + (separator != two_colon)) { + fatal_reader(gettext("Unexpected command seen")); + } + /* Enter the last dependency */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + source_p++; + /* Make sure to enter a rule even if the is */ + /* no text here */ + command = command_tail = ALLOC(Cmd_line); + command->next = NULL; + command->command_line = empty_name; + command->make_refd = false; + command->ignore_command_dependency = false; + command->assign = false; + command->ignore_error = false; + command->silent = false; + + GOTO_STATE(scan_command_state); + case plus_char: + /* + ** following code drops the target separator plus char if it starts + ** a line. + */ + if(first_target && !macro_seen_in_string && + source_p == string_start) { + for (; 1; source_p++) + switch (GET_CHAR()) { + case nul_char: + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case plus_char: + source_p++; + while (*source_p == (int) nul_char) { + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) tab_char || + *source_p == (int) space_char) { + macro_seen_in_string = false; + string_start = source_p + 1; + } else { + goto resume_name_scan; + } + break; + case tab_char: + case space_char: + string_start = source_p + 1; + break; + default: + goto resume_name_scan; + } + } + if (paren_count + brace_count > 0) { + break; + } + /* We found "+=" construct */ + if (source_p != string_start) { + /* "+" is not a break char. */ + /* Ignore it if it is part of an identifier */ + source_p++; + goto resume_name_scan; + } + /* Make sure the "+" is followed by a "=" */ + scan_append: + switch (*++source_p) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + source_p--; + string_start = source_p; + if (source == NULL) { + GOTO_STATE(illegal_eoln_state); + } + goto scan_append; + case equal_char: + if(!svr4) { + append = true; + } else { + fatal_reader(gettext("Must be a separator on rules")); + } + break; + default: + /* The "+" just starts a regular name. */ + /* Start reading that name */ + goto resume_name_scan; + } + /* Fall into */ + case equal_char: + if (paren_count + brace_count > 0) { + break; + } + /* We found macro assignment. */ + /* Check if it is legal and if it is appending */ + switch (separator) { + case none_seen: + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + case conditional_seen: + on_eoln_state = enter_conditional_state; + break; + default: + /* Reader must special check for "MACRO:sh=" */ + /* notation */ + if (sh_name == NULL) { + MBSTOWCS(wcs_buffer, "sh"); + sh_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "shell"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + } + + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start + ); + + if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == sh_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == sh_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(sh_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } else if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == shell_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == shell_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(shell_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } + if(svr4) { + fatal_reader(gettext("syntax error")); + } + else { + fatal_reader(gettext("Macro assignment on dependency line")); + } + } + if (append) { + source_p--; + } + /* Enter the macro name */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + if (append) { + source_p++; + } + macro_value = NULL; + source_p++; + distance = 0; + /* Skip whitespace to the start of the value */ + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p != (int) newline_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_char((int) + backslash_char, + &name_string); + append_char(*source_p, + &name_string); + string_start = source_p+1; + goto macro_value_start; + } else { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + case numbersign_char: + string_start = source_p; + goto macro_value_end; + case tab_char: + case space_char: + break; + default: + string_start = source_p; + goto macro_value_start; + } + } + macro_value_start: + /* Find the end of the value */ + for (; 1; source_p++) { + if (distance != 0) { + *source_p = *(source_p + distance); + } + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + source_p++; + if (distance != 0) { + *source_p = + *(source_p + distance); + } + if (*source_p == (int) nul_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + +/* BID_1225561 */ + *(source_p - 1) = (int) space_char; + append_string(string_start, + &name_string, + source_p - + string_start - 1); + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (distance != 0) { + *source_p = + *(source_p + + distance); + } + if (*source_p == (int) newline_char) { + append_char((int) space_char, &name_string); + } else { + append_char((int) backslash_char, &name_string); + } +/****************/ + } + if (*source_p == (int) newline_char) { + source_p--; + line_number++; + distance++; + *source_p = (int) space_char; + while ((*(source_p + + distance + 1) == + (int) tab_char) || + (*(source_p + + distance + 1) == + (int) space_char)) { + distance++; + } + } + break; + case newline_char: + case numbersign_char: + goto macro_value_end; + } + } + macro_value_end: + /* Complete the value in the string */ + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + if (name_string.buffer.start != name_string.text.p) { + macro_value = + GETNAME(name_string.buffer.start, + FIND_LENGTH); + } + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + for (; distance > 0; distance--) { + *source_p++ = (int) space_char; + } + GOTO_STATE(on_eoln_state); + } + } + +/**************************************************************** + * enter dependencies state + */ + case enter_dependencies_state: + enter_dependencies_label: +/* Expects pointer on first non whitespace char after last dependency. (On */ +/* next line.) We end up here after having read a "targets : dependencies" */ +/* line. The state checks if there is a rule to read and if so dispatches */ +/* to scan_command_state scan_command_state reads one rule line and the */ +/* returns here */ + + /* First check if the first char on the next line is special */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + break; + } + goto enter_dependencies_label; + case exclam_char: + /* The line should be evaluate before it is read */ + macro_seen_in_string = false; + string_start = source_p + 1; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_2; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_2: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto enter_dependencies_label; + } + } + case dollar_char: + if (source->already_expanded) { + break; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(0); + goto enter_dependencies_label; + case numbersign_char: + if (makefile_type != reading_makefile) { + source_p++; + GOTO_STATE(scan_command_state); + } + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + goto enter_dependencies_label; + } + } + + case tab_char: + GOTO_STATE(scan_command_state); + } + + /* We read all the command lines for the target/dependency line. */ + /* Enter the stuff */ + enter_target_groups_and_dependencies( &target, &depes, command, + separator, target_group_seen); + + goto start_new_line; + +/**************************************************************** + * scan command state + */ +case scan_command_state: + /* We need to read one rule line. Do that and return to */ + /* the enter dependencies state */ + string_start = source_p; + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case backslash_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + append_char((int) backslash_char, &name_string); + append_char(*source_p, &name_string); + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + if (*source_p == (int) tab_char) { + source_p++; + } + } else { + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + } + string_start = source_p; + if ((*source_p == (int) newline_char) || + (*source_p == (int) backslash_char) || + (*source_p == (int) nul_char)) { + source_p--; + } + break; + case newline_char: + command_newline: + if ((string_start != source_p) || + macro_seen_in_string) { + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + string_start = + name_string.buffer.start; + string_end = name_string.text.p; + } else { + string_end = source_p; + } + while ((*string_start != (int) newline_char) && + iswspace(*string_start)){ + string_start++; + } + if ((string_end > string_start) || + (makefile_type == reading_statefile)) { + if (command_tail == NULL) { + command = + command_tail = + ALLOC(Cmd_line); + } else { + command_tail->next = + ALLOC(Cmd_line); + command_tail = + command_tail->next; + } + command_tail->next = NULL; + command_tail->make_refd = false; + command_tail->ignore_command_dependency = false; + command_tail->assign = false; + command_tail->ignore_error = false; + command_tail->silent = false; + command_tail->command_line = + GETNAME(string_start, + string_end - string_start); + if (macro_seen_in_string && + name_string.free_after_use) { + retmem(name_string. + buffer.start); + } + } + } + do { + if ((source != NULL) && (source->fd >= 0)) { + line_number++; + } + if ((source != NULL) && + (*++source_p == (int) nul_char)) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + } while (*source_p == (int) newline_char); + + GOTO_STATE(enter_dependencies_state); + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + break; + } + } + +/**************************************************************** + * enter equal state + */ +case enter_equal_state: + if (target.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + enter_equal(target.names[0], macro_value, append); + goto start_new_line; + +/**************************************************************** + * enter conditional state + */ +case enter_conditional_state: + if (depes.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + enter_conditional(nvp->names[i], + depes.names[0], + macro_value, + append); + } + } + goto start_new_line; + +/**************************************************************** + * Error states + */ +case illegal_bytes_state: + fatal_reader(gettext("Invalid byte sequence")); +case illegal_eoln_state: + if (line_number > 1) { + if (line_started_with_space == (line_number - 1)) { + line_number--; + fatal_reader(gettext("Unexpected end of line seen\n\t*** missing separator (did you mean TAB instead of 8 spaces?)")); + } + } + fatal_reader(gettext("Unexpected end of line seen")); +case poorly_formed_macro_state: + fatal_reader(gettext("Badly formed macro assignment")); +case exit_state: + return; +default: + fatal_reader(gettext("Internal error. Unknown reader state")); +} +} + +/* + * push_macro_value(bp, buffer, size, source) + * + * Macro and function that evaluates one macro + * and makes the reader read from the value of it + * + * Return value: + * The source block to read the macro from + * + * Parameters: + * bp The new source block to fill in + * buffer Buffer to read from + * size size of the buffer + * source The old source block + * + * Global variables used: + */ +static Source +push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source) +{ + bp->string.buffer.start = bp->string.text.p = buffer; + bp->string.text.end = NULL; + bp->string.buffer.end = buffer + (size/SIZEOFWCHAR_T); + bp->string.free_after_use = false; + bp->inp_buf = + bp->inp_buf_ptr = + bp->inp_buf_end = NULL; + bp->error_converting = false; + expand_macro(source, &bp->string, (wchar_t *) NULL, false); + bp->string.text.p = bp->string.buffer.start; + + /* 4209588: 'make' doesn't understand a macro with whitespaces in the head as target. + * strip whitespace from the begining of the macro value + */ + while (iswspace(*bp->string.text.p)) { + bp->string.text.p++; + } + + bp->fd = -1; + bp->already_expanded = true; + bp->previous = source; + return bp; +} + +/* + * enter_target_groups_and_dependencies(target, depes, command, separator, + * target_group_seen) + * + * Parameters: + * target Structure that shows the target(s) on the line + * we are currently parsing. This can looks like + * target1 .. targetN : dependencies + * commands + * or + * target1 + .. + targetN : dependencies + * commands + * depes Dependencies + * command Points to the command(s) to be executed for + * this target. + * separator : or :: or := + * target_group_seen Set if we have target1 + .. + targetN + * + * + * After reading the command lines for a target, this routine + * is called to setup the dependencies and the commands for it. + * If the target is a % pattern or part of a target group, then + * the appropriate routines are called. + */ + +void +enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen) +{ + int i; + Boolean reset= true; + Chain target_group_member; + Percent percent_ptr; + + for (; target != NULL; target = target->next) { + for (i = 0; i < target->used; i++) { + if (target->names[i] != NULL) { + if (target_group_seen) { + target_group_member = + find_target_groups(target, i, reset); + if(target_group_member == NULL) { + fatal_reader(gettext("Unexpected '+' on dependency line")); + } + } + reset = false; + + /* If we saw it in the makefile it must be + * a file */ + target->names[i]->stat.is_file = true; + /* Make sure that we use dependencies + * entered for makefiles */ + target->names[i]->state = build_dont_know; + + /* If the target is special we delegate + * the processing */ + if (target->names[i]->special_reader + != no_special) { + special_reader(target->names[i], + depes, + command); + } + /* Check if this is a "a%b : x%y" type rule */ + else if (target->names[i]->percent) { + percent_ptr = + enter_percent(target->names[i], + target->target_group[i], + depes, command); + if (target_group_seen) { + target_group_member->percent_member = + percent_ptr; + } + } else if (target->names[i]->dollar) { + enter_dyntarget(target->names[i]); + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } else { + if (target_group_seen) { + target_group_member->percent_member = + NULL; + } + + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } + } + } + } +} + + diff --git a/bin/read2.cc b/bin/read2.cc new file mode 100644 index 0000000..50324de --- /dev/null +++ b/bin/read2.cc @@ -0,0 +1,1898 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include +#include /* sh_command2string() */ +#include /* expand_value() */ +#include /* retmem() */ +#include /* va_list, va_start(), va_end() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static Boolean built_last_make_run_seen; + +/* + * File table of contents + */ +static Name_vector enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names); +extern Name normalize_name(register wchar_t *name_string, register int length); +static void read_suffixes_list(register Name_vector depes); +static void make_relative(wchar_t *to, wchar_t *result); +static void print_rule(register Cmd_line command); +static void sh_transform(Name *name, Name *value); + + +/* + * enter_name(string, tail_present, string_start, string_end, + * current_names, extra_names, target_group_seen) + * + * Take one string and enter it as a name. The string is passed in + * two parts. A make string and possibly a C string to append to it. + * The result is stuffed in the vector current_names. + * extra_names points to a vector that is used if current_names overflows. + * This is allocad in the calling routine. + * Here we handle the "lib.a[members]" notation. + * + * Return value: + * The name vector that was used + * + * Parameters: + * tail_present Indicates if both C and make string was passed + * string_start C string + * string_end Pointer to char after last in C string + * string make style string with head of name + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * target_group_seen Pointer to boolean that is set if "+" is seen + * + * Global variables used: + * makefile_type When we read a report file we normalize paths + * plus Points to the Name "+" + */ + +Name_vector +enter_name(String string, Boolean tail_present, register wchar_t *string_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen) +{ + Name name; + register wchar_t *cp; + wchar_t ch; + + /* If we were passed a separate tail of the name we append it to the */ + /* make string with the rest of it */ + if (tail_present) { + append_string(string_start, string, string_end - string_start); + string_start = string->buffer.start; + string_end = string->text.p; + } + ch = *string_end; + *string_end = (int) nul_char; + /* + * Check if there are any ( or [ that are not prefixed with $. + * If there are, we have to deal with the lib.a(members) format. + */ + for (cp = (wchar_t *) wcschr(string_start, (int) parenleft_char); + cp != NULL; + cp = (wchar_t *) wcschr(cp + 1, (int) parenleft_char)) { + if (*(cp - 1) != (int) dollar_char) { + *string_end = ch; + return enter_member_name(string_start, + cp, + string_end, + current_names, + extra_names); + } + } + *string_end = ch; + + if (makefile_type == reading_cpp_file) { + /* Remove extra ../ constructs if we are reading from a report file */ + name = normalize_name(string_start, string_end - string_start); + } else { + /* + * /tolik, fix bug 1197477/ + * Normalize every target name before entering. + * ..//obj/a.o and ../obj//a.o are not two different targets. + * There is only one target ../obj/a.o + */ + /*name = GETNAME(string_start, string_end - string_start);*/ + name = normalize_name(string_start, string_end - string_start); + } + + /* Internalize the name. Detect the name "+" (target group here) */ +if(current_names->used != 0 && current_names->names[current_names->used-1] == plus) { + if(name == plus) { + return current_names; + } +} + /* If the current_names vector is full we patch in the one from */ + /* extra_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + current_names->next = *extra_names; + *extra_names = NULL; + current_names = current_names->next; + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + if (name == plus) { + *target_group_seen = true; + } + if (tail_present && string->free_after_use) { + retmem(string->buffer.start); + } + return current_names; +} + +/* + * enter_member_name(lib_start, member_start, string_end, + * current_names, extra_names) + * + * A string has been found to contain member names. + * (The "lib.a[members]" and "lib.a(members)" notation) + * Handle it pretty much as enter_name() does for simple names. + * + * Return value: + * The name vector that was used + * + * Parameters: + * lib_start Points to the of start of "lib.a(member.o)" + * member_start Points to "member.o" from above string. + * string_end Points to char after last of above string. + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * + * Global variables used: + */ +static Name_vector +enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names) +{ + register Boolean entry = false; + wchar_t buffer[STRING_BUFFER_LENGTH]; + Name lib; + Name member; + Name name; + Property prop; + wchar_t *memberp; + wchar_t *q; + register int paren_count; + register Boolean has_dollar; + register wchar_t *cq; + Name long_member_name = NULL; + + /* Internalize the name of the library */ + lib = GETNAME(lib_start, member_start - lib_start); + lib->is_member = true; + member_start++; + if (*member_start == (int) parenleft_char) { + /* This is really the "lib.a((entries))" format */ + entry = true; + member_start++; + } + /* Move the library name to the buffer where we intend to build the */ + /* "lib.a(member)" for each member */ + (void) wcsncpy(buffer, lib_start, member_start - lib_start); + memberp = buffer + (member_start-lib_start); + while (1) { + long_member_name = NULL; + /* Skip leading spaces */ + for (; + (member_start < string_end) && iswspace(*member_start); + member_start++); + /* Find the end of the member name. Allow nested (). Detect $*/ + for (cq = memberp, has_dollar = false, paren_count = 0; + (member_start < string_end) && + ((*member_start != (int) parenright_char) || + (paren_count > 0)) && + !iswspace(*member_start); + *cq++ = *member_start++) { + switch (*member_start) { + case parenleft_char: + paren_count++; + break; + case parenright_char: + paren_count--; + break; + case dollar_char: + has_dollar = true; + } + } + /* Internalize the member name */ + member = GETNAME(memberp, cq - memberp); + *cq = 0; + if ((q = (wchar_t *) wcsrchr(memberp, (int) slash_char)) == NULL) { + q = memberp; + } + if ((cq - q > (int) ar_member_name_len) && + !has_dollar) { + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + long_member_name = GETNAME(buffer, cq - buffer); + cq = q + (int) ar_member_name_len; + } + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + /* Internalize the "lib.a(member)" notation for this member */ + name = GETNAME(buffer, cq - buffer); + name->is_member = lib->is_member; + if (long_member_name != NULL) { + prop = append_prop(name, long_member_name_prop); + name->has_long_member_name = true; + prop->body.long_member_name.member_name = + long_member_name; + } + /* And add the member prop */ + prop = append_prop(name, member_prop); + prop->body.member.library = lib; + if (entry) { + /* "lib.a((entry))" notation */ + prop->body.member.entry = member; + prop->body.member.member = NULL; + } else { + /* "lib.a(member)" Notation */ + prop->body.member.entry = NULL; + prop->body.member.member = member; + } + /* Handle overflow of current_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + if (*extra_names == NULL) { + current_names = + current_names->next = + ALLOC(Name_vector); + } else { + current_names = + current_names->next = + *extra_names; + *extra_names = NULL; + } + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + while (iswspace(*member_start)) { + member_start++; + } + /* Check if there are more members */ + if ((*member_start == (int) parenright_char) || + (member_start >= string_end)) { + return current_names; + } + } + /* NOTREACHED */ +} + +/* + * normalize_name(name_string, length) + * + * Take a namestring and remove redundant ../, // and ./ constructs + * + * Return value: + * The normalized name + * + * Parameters: + * name_string Path string to normalize + * length Length of that string + * + * Global variables used: + * dot The Name ".", compared against + * dotdot The Name "..", compared against + */ +Name +normalize_name(register wchar_t *name_string, register int length) +{ + static Name dotdot; + register wchar_t *string = ALLOC_WC(length + 1); + register wchar_t *string2; + register wchar_t *cdp; + wchar_t *current_component; + Name name; + register int count; + + if (dotdot == NULL) { + MBSTOWCS(wcs_buffer, ".."); + dotdot = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* + * Copy string removing ./ and //. + * First strip leading ./ + */ + while ((length > 1) && + (name_string[0] == (int) period_char) && + (name_string[1] == (int) slash_char)) { + name_string += 2; + length -= 2; + while ((length > 0) && (name_string[0] == (int) slash_char)) { + name_string++; + length--; + } + } + /* Then copy the rest of the string removing /./ & // */ + cdp = string; + while (length > 0) { + if (((length > 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) slash_char)) || + ((length == 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char))) { + name_string += 2; + length -= 2; + continue; + } + if ((length > 1) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) slash_char)) { + name_string++; + length--; + continue; + } + *cdp++ = *name_string++; + length--; + } + *cdp = (int) nul_char; + /* + * Now scan for /../ and remove such combinations iff + * is not another .. + * Each time something is removed, the whole process is restarted. + */ +removed_one: + name_string = string; + string2 = name_string; /*save for free*/ + current_component = + cdp = + string = + ALLOC_WC((length = wcslen(name_string)) + 1); + while (length > 0) { + if (((length > 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char) && + (name_string[3] == (int) slash_char)) || + ((length == 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char))) { + /* Positioned on the / that starts a /.. sequence */ + if (((count = cdp - current_component) != 0) && + (exists(name = GETNAME(string, cdp - string)) > file_doesnt_exist) && + (!name->stat.is_sym_link)) { + name = GETNAME(current_component, count); + if(name != dotdot) { + cdp = current_component; + name_string += 3; + length -= 3; + if (length > 0) { + name_string++; /* skip slash */ + length--; + while (length > 0) { + *cdp++ = *name_string++; + length--; + } + } + *cdp = (int) nul_char; + retmem(string2); + goto removed_one; + } + } + } + if ((*cdp++ = *name_string++) == (int) slash_char) { + current_component = cdp; + } + length--; + } + *cdp = (int) nul_char; + if (string[0] == (int) nul_char) { + name = dot; + } else { + name = GETNAME(string, FIND_LENGTH); + } + retmem(string); + retmem(string2); + return name; +} + +/* + * find_target_groups(target_list) + * + * If a "+" was seen when the target list was scanned we need to extract + * the groups. Each target in the name vector that is a member of a + * group gets a pointer to a chain of all the members stuffed in its + * target_group vector slot + * + * Parameters: + * target_list The list of targets that contains "+" + * + * Global variables used: + * plus The Name "+", compared against + */ +Chain +find_target_groups(register Name_vector target_list, register int i, Boolean reset) +{ + static Chain target_group = NULL; + static Chain tail_target_group = NULL; + static Name *next; + static Boolean clear_target_group = false; + + if (reset) { + target_group = NULL; + tail_target_group = NULL; + clear_target_group = false; + } + + /* Scan the list of targets */ + /* If the previous target terminated a group */ + /* we flush the pointer to that member chain */ + if (clear_target_group) { + clear_target_group = false; + target_group = NULL; + } + /* Pick up a pointer to the cell with */ + /* the next target */ + if (i + 1 != target_list->used) { + next = &target_list->names[i + 1]; + } else { + next = (target_list->next != NULL) ? + &target_list->next->names[0] : NULL; + } + /* We have four states here : + * 0: No target group started and next element is not "+" + * This is not interesting. + * 1: A target group is being built and the next element + * is not "+". This terminates the group. + * 2: No target group started and the next member is "+" + * This is the first target in a group. + * 3: A target group started and the next member is a "+" + * The group continues. + */ + switch ((target_group ? 1 : 0) + + (next && (*next == plus) ? + 2 : 0)) { + case 0: /* Not target_group */ + break; + case 1: /* Last group member */ + /* We need to keep this pointer so */ + /* we can stuff it for last member */ + clear_target_group = true; + /* fall into */ + case 3: /* Middle group member */ + /* Add this target to the */ + /* current chain */ + tail_target_group->next = ALLOC(Chain); + tail_target_group = tail_target_group->next; + tail_target_group->next = NULL; + tail_target_group->name = target_list->names[i]; + break; + case 2: /* First group member */ + /* Start a new chain */ + target_group = tail_target_group = ALLOC(Chain); + target_group->next = NULL; + target_group->name = target_list->names[i]; + break; + } + /* Stuff the current chain, if any, in the */ + /* targets group slot */ + target_list->target_group[i] = target_group; + if ((next != NULL) && + (*next == plus)) { + *next = NULL; + } + return (tail_target_group); +} + +/* + * enter_dependencies(target, target_group, depes, command, separator) + * + * Take one target and a list of dependencies and process the whole thing. + * The target might be special in some sense in which case that is handled + * + * Parameters: + * target The target we want to enter + * target_group Non-NULL if target is part of a group this time + * depes A list of dependencies for the target + * command The command the target should be entered with + * separator Indicates if this is a ":" or a "::" rule + * + * Static variables used: + * built_last_make_run_seen If the previous target was + * .BUILT_LAST_MAKE_RUN we say to rewrite + * the state file later on + * + * Global variables used: + * command_changed Set to indicate if .make.state needs rewriting + * default_target_to_build Set to the target if reading makefile + * and this is the first regular target + * force The Name " FORCE", used with "::" targets + * makefile_type We do different things for makefile vs. report + * not_auto The Name ".NOT_AUTO", compared against + * recursive_name The Name ".RECURSIVE", compared against + * temp_file_number Used to figure out when to clear stale + * automatic dependencies + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_dependencies(register Name target, Chain target_group, register Name_vector depes, register Cmd_line command, register Separator separator) +{ + register int i; + register Property line; + Name name; + Name directory; + wchar_t *namep; + char *mb_namep; + Dependency dp; + Dependency *dpp; + Property line2; + wchar_t relative[MAXPATHLEN]; + register int recursive_state; + Boolean register_as_auto; + Boolean not_auto_found; + char *slash; + Wstring depstr; + + /* Check if this is a .RECURSIVE line */ + if ((depes->used >= 3) && + (depes->names[0] == recursive_name)) { + target->has_recursive_dependency = true; + depes->names[0] = NULL; + recursive_state = 0; + dp = NULL; + dpp = &dp; + /* Read the dependencies. They are " */ + /* *" */ + for (; depes != NULL; depes = depes->next) { + for (i = 0; i < depes->used; i++) { + if (depes->names[i] != NULL) { + switch (recursive_state++) { + case 0: /* Directory */ + { + depstr.init(depes->names[i]); + make_relative(depstr.get_string(), + relative); + directory = + GETNAME(relative, + FIND_LENGTH); + } + break; + case 1: /* Target */ + name = depes->names[i]; + break; + default: /* Makefiles */ + *dpp = ALLOC(Dependency); + (*dpp)->next = NULL; + (*dpp)->name = depes->names[i]; + (*dpp)->automatic = false; + (*dpp)->stale = false; + (*dpp)->built = false; + dpp = &((*dpp)->next); + break; + } + } + } + } + /* Check if this recursion already has been reported else */ + /* enter the recursive prop for the target */ + /* The has_built flag is used to tell if this .RECURSIVE */ + /* was discovered from this run (read from a tmp file) */ + /* or was from discovered from the original .make.state */ + /* file */ + for (line = get_prop(target->prop, recursive_prop); + line != NULL; + line = get_prop(line->next, recursive_prop)) { + if ((line->body.recursive.directory == directory) && + (line->body.recursive.target == name)) { + line->body.recursive.makefiles = dp; + line->body.recursive.has_built = + (Boolean) + (makefile_type == reading_cpp_file); + return; + } + } + line2 = append_prop(target, recursive_prop); + line2->body.recursive.directory = directory; + line2->body.recursive.target = name; + line2->body.recursive.makefiles = dp; + line2->body.recursive.has_built = + (Boolean) (makefile_type == reading_cpp_file); + line2->body.recursive.in_depinfo = false; + return; + } + /* If this is the first target that doesnt start with a "." in the */ + /* makefile we remember that */ + Wstring tstr(target); + wchar_t * wcb = tstr.get_string(); + if ((makefile_type == reading_makefile) && + (default_target_to_build == NULL) && + ((wcb[0] != (int) period_char) || + wcschr(wcb, (int) slash_char))) { + +/* BID 1181577: $(EMPTY_MACRO) + $(EMPTY_MACRO): +** The target with empty name cannot be default_target_to_build +*/ + if (target->hash.length != 0) + default_target_to_build = target; + } + /* Check if the line is ":" or "::" */ + if (makefile_type == reading_makefile) { + if (target->colons == no_colon) { + target->colons = separator; + } else { + if (target->colons != separator) { + fatal_reader(gettext(":/:: conflict for target `%s'"), + target->string_mb); + } + } + if (target->colons == two_colon) { + if (depes->used == 0) { + /* If this is a "::" type line with no */ + /* dependencies we add one "FRC" type */ + /* dependency for free */ + depes->used = 1; /* Force :: targets with no + * depes to always run */ + depes->names[0] = force; + } + /* Do not delete "::" type targets when interrupted */ + target->stat.is_precious = true; + /* + * Build a synthetic target "%target" + * for "target". + */ + mb_namep = getmem((int) (strlen(target->string_mb) + 10)); + namep = ALLOC_WC((int) (target->hash.length + 10)); + slash = strrchr(target->string_mb, (int) slash_char); + if (slash == NULL) { + (void) sprintf(mb_namep, + "%d@%s", + target->colon_splits++, + target->string_mb); + } else { + *slash = 0; + (void) sprintf(mb_namep, + "%s/%d@%s", + target->string_mb, + target->colon_splits++, + slash + 1); + *slash = (int) slash_char; + } + MBSTOWCS(namep, mb_namep); + retmem_mb(mb_namep); + name = GETNAME(namep, FIND_LENGTH); + retmem(namep); + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Make "target" depend on "%target */ + line2 = maybe_append_prop(target, line_prop); + enter_dependency(line2, name, true); + line2->body.line.target = target; + /* Put a prop on "%target that makes */ + /* appear as "target" */ + /* when it is processed */ + maybe_append_prop(name, target_prop)-> + body.target.target = target; + target->is_double_colon_parent = true; + name->is_double_colon = true; + name->has_target_prop = true; + if (trace_reader) { + (void) printf("\n"); + } + (target = name)->stat.is_file = true; + } + } + /* This really is a regular dependency line. Just enter it */ + line = maybe_append_prop(target, line_prop); + line->body.line.target = target; + /* Depending on what kind of makefile we are reading we have to */ + /* treat things differently */ + switch (makefile_type) { + case reading_makefile: + /* Reading regular makefile. Just notice whether this */ + /* redefines the rule for the target */ + if (command != NULL) { + if (line->body.line.command_template != NULL) { + line->body.line.command_template_redefined = + true; + if ((wcb[0] == (int) period_char) && + !wcschr(wcb, (int) slash_char)) { + line->body.line.command_template = + command; + } + } else { + line->body.line.command_template = command; + } + } else { + if ((wcb[0] == (int) period_char) && + !wcschr(wcb, (int) slash_char)) { + line->body.line.command_template = command; + } + } + break; + case rereading_statefile: + /* Rereading the statefile. We only enter thing that changed */ + /* since the previous time we read it */ + if (!built_last_make_run_seen) { + for (Cmd_line next, cmd = command; cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + return; + } + built_last_make_run_seen = false; + command_changed = true; + target->ran_command = true; + case reading_statefile: + /* Reading the statefile for the first time. Enter the rules */ + /* as "Commands used" not "templates to use" */ + if (command != NULL) { + for (Cmd_line next, cmd = line->body.line.command_used; + cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + line->body.line.command_used = command; + } + case reading_cpp_file: + /* Reading report file from programs that reports */ + /* dependencies. If this is the first time the target is */ + /* read from this reportfile we clear all old */ + /* automatic depes */ + if (target->temp_file_number == temp_file_number) { + break; + } + target->temp_file_number = temp_file_number; + command_changed = true; + if (line != NULL) { + for (dp = line->body.line.dependencies; + dp != NULL; + dp = dp->next) { + if (dp->automatic) { + dp->stale = true; + } + } + } + break; + default: + fatal_reader(gettext("Internal error. Unknown makefile type %d"), + makefile_type); + } + /* A target may only be involved in one target group */ + if (line->body.line.target_group != NULL) { + if (target_group != NULL) { + fatal_reader(gettext("Too many target groups for target `%s'"), + target->string_mb); + } + } else { + line->body.line.target_group = target_group; + } + + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Enter the dependencies */ + register_as_auto = BOOLEAN(makefile_type != reading_makefile); + not_auto_found = false; + for (; + (depes != NULL) && !not_auto_found; + depes = depes->next) { + for (i = 0; i < depes->used; i++) { + /* the dependency .NOT_AUTO signals beginning of + * explicit dependancies which were put at end of + * list in .make.state file - we stop entering + * dependencies at this point + */ + if (depes->names[i] == not_auto) { + not_auto_found = true; + break; + } + enter_dependency(line, + depes->names[i], + register_as_auto); + } + } + if (trace_reader) { + (void) printf("\n"); + print_rule(command); + } +} + +/* + * enter_dependency(line, depe, automatic) + * + * Enter one dependency. Do not enter duplicates. + * + * Parameters: + * line The line block that the dependeny is + * entered for + * depe The dependency to enter + * automatic Used to set the field "automatic" + * + * Global variables used: + * makefile_type We do different things for makefile vs. report + * trace_reader Indicates that we should echo stuff we read + * wait_name The Name ".WAIT", compared against + */ +void +enter_dependency(Property line, register Name depe, Boolean automatic) +{ + register Dependency dp; + register Dependency *insert; + + if (trace_reader) { + (void) printf("%s ", depe->string_mb); + } + /* Find the end of the list and check for duplicates */ + for (insert = &line->body.line.dependencies, dp = *insert; + dp != NULL; + insert = &dp->next, dp = *insert) { + if ((dp->name == depe) && (depe != wait_name)) { + if (dp->automatic) { + dp->automatic = automatic; + if (automatic) { + dp->built = false; + depe->stat.is_file = true; + } + } + dp->stale = false; + return; + } + } + /* Insert the new dependency since we couldnt find it */ + dp = *insert = ALLOC(Dependency); + dp->name = depe; + dp->next = NULL; + dp->automatic = automatic; + dp->stale = false; + dp->built = false; + depe->stat.is_file = true; + + if ((makefile_type == reading_makefile) && + (line != NULL) && + (line->body.line.target != NULL)) { + line->body.line.target->has_regular_dependency = true; + } +} + +/* + * enter_percent(target, depes, command) + * + * Enter "x%y : a%b" type lines + * % patterns are stored in four parts head and tail for target and source + * + * Parameters: + * target Left hand side of pattern + * depes The dependency list with the rh pattern + * command The command for the pattern + * + * Global variables used: + * empty_name The Name "", compared against + * percent_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Percent +enter_percent(register Name target, Chain target_group, register Name_vector depes, Cmd_line command) +{ + register Percent result = ALLOC(Percent); + register Percent depe; + register Percent *depe_tail = &result->dependencies; + register Percent *insert; + register wchar_t *cp, *cp1; + Name_vector nvp; + int i; + int pattern; + + result->next = NULL; + result->patterns = NULL; + result->patterns_total = 0; + result->command_template = command; + result->being_expanded = false; + result->name = target; + result->dependencies = NULL; + result->target_group = target_group; + + /* get patterns count */ + Wstring wcb(target); + cp = wcb.get_string(); + while (true) { + cp = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp != NULL) { + result->patterns_total++; + cp++; + } else { + break; + } + } + result->patterns_total++; + + /* allocate storage for patterns */ + result->patterns = (Name *) getmem(sizeof(Name) * result->patterns_total); + + /* then create patterns */ + cp = wcb.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp1 != NULL) { + result->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + result->patterns[pattern] = GETNAME(cp, (int) target->hash.length - (cp - wcb.get_string())); + break; + } + } + + Wstring wcb1; + + /* build dependencies list */ + for (nvp = depes; nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + depe = ALLOC(Percent); + depe->next = NULL; + depe->patterns = NULL; + depe->patterns_total = 0; + depe->name = nvp->names[i]; + depe->dependencies = NULL; + depe->command_template = NULL; + depe->being_expanded = false; + depe->target_group = NULL; + + *depe_tail = depe; + depe_tail = &depe->next; + + if (depe->name->percent) { + /* get patterns count */ + wcb1.init(depe->name); + cp = wcb1.get_string(); + while (true) { + cp = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp != NULL) { + depe->patterns_total++; + cp++; + } else { + break; + } + } + depe->patterns_total++; + + /* allocate storage for patterns */ + depe->patterns = (Name *) getmem(sizeof(Name) * depe->patterns_total); + + /* then create patterns */ + cp = wcb1.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp1 != NULL) { + depe->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + depe->patterns[pattern] = GETNAME(cp, (int) depe->name->hash.length - (cp - wcb1.get_string())); + break; + } + } + } + } + } + + /* Find the end of the percent list and append the new pattern */ + for (insert = &percent_list; (*insert) != NULL; insert = &(*insert)->next); + *insert = result; + + if (trace_reader) { + (void) printf("%s:", result->name->string_mb); + + for (depe = result->dependencies; depe != NULL; depe = depe->next) { + (void) printf(" %s", depe->name->string_mb); + } + + (void) printf("\n"); + + print_rule(command); + } + + return result; +} + +/* + * enter_dyntarget(target) + * + * Enter "$$(MACRO) : b" type lines + * + * Parameters: + * target Left hand side of pattern + * + * Global variables used: + * dyntarget_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Dyntarget +enter_dyntarget(register Name target) +{ + register Dyntarget result = ALLOC(Dyntarget); + Dyntarget p; + Dyntarget *insert; + int i; + + result->next = NULL; + result->name = target; + + + /* Find the end of the dyntarget list and append the new pattern */ + for (insert = &dyntarget_list, p = *insert; + p != NULL; + insert = &p->next, p = *insert); + *insert = result; + + if (trace_reader) { + (void) printf("Dynamic target %s:\n", result->name->string_mb); + } + return( result); +} + + +/* + * special_reader(target, depes, command) + * + * Read the pseudo targets make knows about + * This handles the special targets that should not be entered as regular + * target/dependency sets. + * + * Parameters: + * target The special target + * depes The list of dependencies it was entered with + * command The command it was entered with + * + * Static variables used: + * built_last_make_run_seen Set to indicate .BUILT_LAST... seen + * + * Global variables used: + * all_parallel Set to indicate that everything runs parallel + * svr4 Set when ".SVR4" target is read + * svr4_name The Name ".SVR4" + * posix Set when ".POSIX" target is read + * posix_name The Name ".POSIX" + * current_make_version The Name "" + * default_rule Set when ".DEFAULT" target is read + * default_rule_name The Name ".DEFAULT", used for tracing + * dot_keep_state The Name ".KEEP_STATE", used for tracing + * ignore_errors Set if ".IGNORE" target is read + * ignore_name The Name ".IGNORE", used for tracing + * keep_state Set if ".KEEP_STATE" target is read + * no_parallel_name The Name ".NO_PARALLEL", used for tracing + * only_parallel Set to indicate only some targets runs parallel + * parallel_name The Name ".PARALLEL", used for tracing + * precious The Name ".PRECIOUS", used for tracing + * sccs_get_name The Name ".SCCS_GET", used for tracing + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", used for tracing + * get_name The Name ".GET", used for tracing + * sccs_get_rule Set when ".SCCS_GET" target is read + * silent Set when ".SILENT" target is read + * silent_name The Name ".SILENT", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +void +special_reader(Name target, register Name_vector depes, Cmd_line command) +{ + register int n; + + switch (target->special_reader) { + + case svr4_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + svr4 = true; + posix = false; + keep_state = false; + all_parallel = false; + only_parallel = false; + if (trace_reader) { + (void) printf("%s:\n", svr4_name->string_mb); + } + break; + + case posix_special: + if(svr4) + break; + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + posix = true; + /* with posix on, use the posix get rule */ + sccs_get_rule = sccs_get_posix_rule; + /* turn keep state off being SunPro make specific */ + keep_state = false; + /* Use /usr/xpg4/bin/sh on Solaris */ + MBSTOWCS(wcs_buffer, "/usr/xpg4/bin/sh"); + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + if (trace_reader) { + (void) printf("%s:\n", posix_name->string_mb); + } + break; + + case built_last_make_run_special: + built_last_make_run_seen = true; + break; + + case default_special: + if (depes->used != 0) { + warning(gettext("Illegal dependency list for target `%s'"), + target->string_mb); + } + default_rule = command; + if (trace_reader) { + (void) printf("%s:\n", + default_rule_name->string_mb); + print_rule(command); + } + break; + + + case ignore_special: + if ((depes->used != 0) &&(!posix)){ + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + ignore_errors_all = true; + } + if(svr4) { + ignore_errors_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->ignore_error_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", ignore_name->string_mb); + } + break; + + case keep_state_special: + if(svr4) + break; + /* ignore keep state, being SunPro make specific */ + if(posix) + break; + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + keep_state = true; + if (trace_reader) { + (void) printf("%s:\n", + dot_keep_state->string_mb); + } + break; + + case keep_state_file_special: + if(svr4) + break; + if(posix) + break; + /* it's not necessary to specify KEEP_STATE, if this + ** is given, so set the keep_state. + */ + keep_state = true; + if (depes->used != 0) { + if((!make_state) ||(!strcmp(make_state->string_mb,".make.state"))) { + make_state = depes->names[0]; + } + } + break; + case make_version_special: + if(svr4) + break; + if (depes->used != 1) { + fatal_reader(gettext("Illegal dependency list for target `%s'"), + target->string_mb); + } + if (depes->names[0] != current_make_version) { + /* + * Special case the fact that version 1.0 and 1.1 + * are identical. + */ + if (!IS_EQUAL(depes->names[0]->string_mb, + "VERSION-1.1") || + !IS_EQUAL(current_make_version->string_mb, + "VERSION-1.0")) { + /* + * Version mismatches should cause the + * .make.state file to be skipped. + * This is currently not true - it is read + * anyway. + */ + warning(gettext("Version mismatch between current version `%s' and `%s'"), + current_make_version->string_mb, + depes->names[0]->string_mb); + } + } + break; + + case no_parallel_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + no_parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + } + } + break; + + case parallel_special: + if(svr4) + break; + if (depes->used == 0) { + /* everything runs in parallel */ + all_parallel = true; + only_parallel = false; + } + /* Set the parallel bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->parallel = true; + depes->names[n]->no_parallel = false; + } + } + break; + + case localhost_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + localhost_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + depes->names[n]->localhost = true; + } + } + break; + + case precious_special: + if (depes->used == 0) { + /* everything is precious */ + all_precious = true; + } else { + all_precious = false; + } + if(svr4) { + all_precious = true; + break; + } + /* Set the precious bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + precious->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->stat.is_precious = true; + } + } + break; + + case sccs_get_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_rule = command; + sccs_get_org_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_name->string_mb); + print_rule(command); + } + break; + + case sccs_get_posix_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_posix_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_special: + if(!svr4) { + break; + } + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + get_rule = command; + sccs_get_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_name->string_mb); + print_rule(command); + } + break; + + case silent_special: + if ((depes->used != 0) && (!posix)){ + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + silent_all = true; + } + if(svr4) { + silent_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->silent_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", silent_name->string_mb); + } + break; + + case suffixes_special: + read_suffixes_list(depes); + break; + + default: + + fatal_reader(gettext("Internal error: Unknown special reader")); + } +} + +/* + * read_suffixes_list(depes) + * + * Read the special list .SUFFIXES. If it is empty the old list is + * cleared. Else the new one is appended. Suffixes with ~ are extracted + * and marked. + * + * Parameters: + * depes The list of suffixes + * + * Global variables used: + * hashtab The central hashtable for Names. + * suffixes The list of suffixes, set or appended to + * suffixes_name The Name ".SUFFIXES", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +static void +read_suffixes_list(register Name_vector depes) +{ + register int n; + register Dependency dp; + register Dependency *insert_dep; + register Name np; + Name np2; + register Boolean first = true; + + if (depes->used == 0) { + /* .SUFFIXES with no dependency list clears the */ + /* suffixes list */ + for (Name_set::iterator np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + np->with_squiggle = + np->without_squiggle = + false; + } + suffixes = NULL; + if (trace_reader) { + (void) printf("%s:\n", suffixes_name->string_mb); + } + return; + } + Wstring str; + /* Otherwise we append to the list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + np = depes->names[n]; + /* Find the end of the list and check if the */ + /* suffix already has been entered */ + for (insert_dep = &suffixes, dp = *insert_dep; + dp != NULL; + insert_dep = &dp->next, dp = *insert_dep) { + if (dp->name == np) { + goto duplicate_suffix; + } + } + if (trace_reader) { + if (first) { + (void) printf("%s:\t", + suffixes_name->string_mb); + first = false; + } + (void) printf("%s ", depes->names[n]->string_mb); + } + if(!(posix|svr4)) { + /* If the suffix is suffixed with "~" we */ + /* strip that and mark the suffix nameblock */ + str.init(np); + wchar_t * wcb = str.get_string(); + if (wcb[np->hash.length - 1] == + (int) tilde_char) { + np2 = GETNAME(wcb, + (int)(np->hash.length - 1)); + np2->with_squiggle = true; + if (np2->without_squiggle) { + continue; + } + np = np2; + } + } + np->without_squiggle = true; + /* Add the suffix to the list */ + dp = *insert_dep = ALLOC(Dependency); + insert_dep = &dp->next; + dp->next = NULL; + dp->name = np; + dp->built = false; + duplicate_suffix:; + } + } + if (trace_reader) { + (void) printf("\n"); + } +} + +/* + * make_relative(to, result) + * + * Given a file name compose a relative path name from it to the + * current directory. + * + * Parameters: + * to The path we want to make relative + * result Where to put the resulting relative path + * + * Global variables used: + */ +static void +make_relative(wchar_t *to, wchar_t *result) +{ + wchar_t *from; + wchar_t *allocated; + wchar_t *cp; + wchar_t *tocomp; + int ncomps; + int i; + int len; + + /* Check if the path is already relative. */ + if (to[0] != (int) slash_char) { + (void) wcscpy(result, to); + return; + } + + MBSTOWCS(wcs_buffer, get_current_path()); + from = allocated = (wchar_t *) wcsdup(wcs_buffer); + + /* + * Find the number of components in the from name. + * ncomp = number of slashes + 1. + */ + ncomps = 1; + for (cp = from; *cp != (int) nul_char; cp++) { + if (*cp == (int) slash_char) { + ncomps++; + } + } + + /* + * See how many components match to determine how many "..", + * if any, will be needed. + */ + result[0] = (int) nul_char; + tocomp = to; + while ((*from != (int) nul_char) && (*from == *to)) { + if (*from == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + from++; + to++; + } + + /* + * Now for some special cases. Check for exact matches and + * for either name terminating exactly. + */ + if (*from == (int) nul_char) { + if (*to == (int) nul_char) { + MBSTOWCS(wcs_buffer, "."); + (void) wcscpy(result, wcs_buffer); + retmem(allocated); + return; + } + if (*to == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + } else if ((*from == (int) slash_char) && (*to == (int) nul_char)) { + ncomps--; + tocomp = to; + } + /* Add on the ".."s. */ + for (i = 0; i < ncomps; i++) { + MBSTOWCS(wcs_buffer, "../"); + (void) wcscat(result, wcs_buffer); + } + + /* Add on the remainder of the to name, if any. */ + if (*tocomp == (int) nul_char) { + len = wcslen(result); + result[len - 1] = (int) nul_char; + } else { + (void) wcscat(result, tocomp); + } + retmem(allocated); + return; +} + +/* + * print_rule(command) + * + * Used when tracing the reading of rules + * + * Parameters: + * command Command to print + * + * Global variables used: + */ +static void +print_rule(register Cmd_line command) +{ + for (; command != NULL; command = command->next) { + (void) printf("\t%s\n", command->command_line->string_mb); + } +} + +/* + * enter_conditional(target, name, value, append) + * + * Enter "target := MACRO= value" constructs + * + * Parameters: + * target The target the macro is for + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * conditionals A special Name that stores all conditionals + * where the target is a % pattern + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_conditional(register Name target, Name name, Name value, register Boolean append) +{ + register Property conditional; + static int sequence; + Name orig_target = target; + + if (name == target_arch) { + enter_conditional(target, virtual_root, virtual_root, false); + } + + if (target->percent) { + target = conditionals; + } + + if (name->colon) { + sh_transform(&name, &value); + } + + /* Count how many conditionals we must activate before building the */ + /* target */ + if (target->percent) { + target = conditionals; + } + + target->conditional_cnt++; + maybe_append_prop(name, macro_prop)->body.macro.is_conditional = true; + /* Add the property for the target */ + conditional = append_prop(target, conditional_prop); + conditional->body.conditional.target = orig_target; + conditional->body.conditional.name = name; + conditional->body.conditional.value = value; + conditional->body.conditional.sequence = sequence++; + conditional->body.conditional.append = append; + if (trace_reader) { + if (value == NULL) { + (void) printf("%s := %s %c=\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s := %s %c= %s\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * enter_equal(name, value, append) + * + * Enter "MACRO= value" constructs + * + * Parameters: + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_equal(Name name, Name value, register Boolean append) +{ + wchar_t *string; + Name temp; + + if (name->colon) { + sh_transform(&name, &value); + } + (void) SETVAR(name, value, append); + + /* if we're setting FC, we want to set F77 to the same value. */ + Wstring nms(name); + wchar_t * wcb = nms.get_string(); + string = wcb; + if (string[0]=='F' && + string[1]=='C' && + string[2]=='\0') { + MBSTOWCS(wcs_buffer, "F77"); + temp = GETNAME(wcs_buffer, FIND_LENGTH); + (void) SETVAR(temp, value, append); +/* + fprintf(stderr, gettext("warning: FC is obsolete, use F77 instead\n")); + */ + } + + if (trace_reader) { + if (value == NULL) { + (void) printf("%s %c=\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s %c= %s\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * sh_transform(name, value) + * + * Parameters: + * name The name of the macro we might transform + * value The value to transform + * + */ +static void +sh_transform(Name *name, Name *value) +{ + /* Check if we need :sh transform */ + wchar_t *colon; + String_rec command; + String_rec destination; + wchar_t buffer[1000]; + wchar_t buffer1[1000]; + + static wchar_t colon_sh[4]; + static wchar_t colon_shell[7]; + + if (colon_sh[0] == (int) nul_char) { + MBSTOWCS(colon_sh, ":sh"); + MBSTOWCS(colon_shell, ":shell"); + } + Wstring nms((*name)); + wchar_t * wcb = nms.get_string(); + + colon = (wchar_t *) wcsrchr(wcb, (int) colon_char); + if ((colon != NULL) && (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell))) { + INIT_STRING_FROM_STACK(destination, buffer); + + if(*value == NULL) { + buffer[0] = 0; + } else { + Wstring wcb1((*value)); + if (IS_WEQUAL(colon, colon_shell)) { + INIT_STRING_FROM_STACK(command, buffer1); + expand_value(*value, &command, false); + } else { + command.text.p = wcb1.get_string() + (*value)->hash.length; + command.text.end = command.text.p; + command.buffer.start = wcb1.get_string(); + command.buffer.end = command.text.p; + } + sh_command2string(&command, &destination); + } + + (*value) = GETNAME(destination.buffer.start, FIND_LENGTH); + *colon = (int) nul_char; + (*name) = GETNAME(wcb, FIND_LENGTH); + *colon = (int) colon_char; + } +} + +/* + * fatal_reader(format, args...) + * + * Parameters: + * format printf style format string + * args arguments to match the format + * + * Global variables used: + * file_being_read Name of the makefile being read + * line_number Line that is being read + * report_pwd Indicates whether current path should be shown + * temp_file_name When reading tempfile we report that name + */ +/*VARARGS*/ +void +fatal_reader(char * pattern, ...) +{ + va_list args; + char message[1000]; + + va_start(args, pattern); + if (file_being_read != NULL) { + WCSTOMBS(mbs_buffer, file_being_read); + if (line_number != 0) { + (void) sprintf(message, + gettext("%s, line %d: %s"), + mbs_buffer, + line_number, + pattern); + } else { + (void) sprintf(message, + "%s: %s", + mbs_buffer, + pattern); + } + pattern = message; + } + + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Fatal error in reader: "), + getprogname()); + (void) vfprintf(stderr, pattern, args); + (void) fprintf(stderr, "\n"); + va_end(args); + + if (temp_file_name != NULL) { + (void) fprintf(stderr, + gettext("%s: Temp-file %s not removed\n"), + getprogname(), + temp_file_name->string_mb); + temp_file_name = NULL; + } + + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); + exit_status = 1; + exit(1); +} + diff --git a/bin/rep.cc b/bin/rep.cc new file mode 100644 index 0000000..f0cb9e1 --- /dev/null +++ b/bin/rep.cc @@ -0,0 +1,394 @@ +/* + * 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 +#include /* retmem() */ +#include /* 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]; + register 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); + } + } +} + diff --git a/bin/state.cc b/bin/state.cc new file mode 100644 index 0000000..57a0150 --- /dev/null +++ b/bin/state.cc @@ -0,0 +1,444 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * state.c + * + * This file contains the routines that write the .make.state file + */ + +/* + * Included files + */ +#include +#include /* errmsg() */ +#include /* setjmp() */ +#include /* getpid() */ +#include /* errno */ +#include /* MB_CUR_MAX */ + +/* + * Defined macros + */ +#define LONGJUMP_VALUE 17 +#define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \ + longjmp(long_jump, LONGJUMP_VALUE);} +#define XPUTC(ch, fd) { \ + if (putc((int) ch, fd) == EOF) \ + longjmp(long_jump, LONGJUMP_VALUE); \ + } +#define XFPUTS(string, fd) fputs(string, fd) + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static char * escape_target_name(Name np) +{ + if(np->dollar) { + int len = strlen(np->string_mb); + char * buff = (char*)malloc(2 * len); + int pos = 0; + wchar_t wc; + int pp = 0; + while(pos < len) { + int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX); + if(n < 0) { // error - this shouldn't happen + (void)free(buff); + return strdup(np->string_mb); + } + if(wc == dollar_char) { + buff[pp] = '\\'; pp++; + buff[pp] = '$'; pp++; + } else { + for(int j=0;jstring_mb[pos+j]; pp++; + } + } + pos += n; + } + buff[pp] = '\0'; + return buff; + } else { + return strdup(np->string_mb); + } +} + +static void print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump); + +/* + * write_state_file(report_recursive, exiting) + * + * Write a new version of .make.state + * + * Parameters: + * report_recursive Should only be done at end of run + * exiting true if called from the exit handler + * + * Global variables used: + * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written + * command_changed If no command changed we do not need to write + * current_make_version The Name "", written + * do_not_exec_rule If -n is on we do not write statefile + * hashtab The hashtable that contains all names + * keep_state If .KEEP_STATE is no on we do not write file + * make_state The Name ".make.state", used for opening file + * make_version The Name ".MAKE_VERSION", written + * recursive_name The Name ".RECURSIVE", written + * rewrite_statefile Indicates that something changed + */ + +void +write_state_file(int, Boolean exiting) +{ + register FILE *fd; + int lock_err; + char buffer[MAXPATHLEN]; + char make_state_tempfile[MAXPATHLEN]; + jmp_buf long_jump; + register int attempts = 0; + Name_set::iterator np, e; + register Property lines; + register int m; + Dependency dependency; + register Boolean name_printed; + Boolean built_this_run = false; + char *target_name; + int line_length; + register Cmd_line cp; + + + if (!rewrite_statefile || + !command_changed || + !keep_state || + do_not_exec_rule || + (report_dependencies_level > 0)) { + return; + } + /* Lock the file for writing. */ + make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1); + (void) sprintf(make_state_lockfile, + "%s.lock", + make_state->string_mb); + if (lock_err = file_lock(make_state->string_mb, + make_state_lockfile, + (int *) &make_state_locked, 0)) { + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + + /* + * We need to make sure that we are not being + * called by the exit handler so we don't call + * it again. + */ + + if (exiting) { + (void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid()); + report_pwd = true; + warning(gettext("Writing to %s"), buffer); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fprintf(stderr, + gettext("Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + return; + } + } else { + report_pwd = true; + fatal(gettext("Can't lock .make.state")); + } + } + + (void) sprintf(make_state_tempfile, + "%s.tmp", + make_state->string_mb); + /* Delete old temporary statefile (in case it exists) */ + (void) unlink(make_state_tempfile); + if ((fd = fopen(make_state_tempfile, "w")) == NULL) { + lock_err = errno; /* Save it! unlink() can change errno */ + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not open temporary statefile `%s': %s"), + make_state_tempfile, + errmsg(lock_err)); + } + /* + * Set a trap for failed writes. If a write fails, the routine + * will try saving the .make.state file under another name in /tmp. + */ + if (setjmp(long_jump)) { + (void) fclose(fd); + if (attempts++ > 5) { + if ((make_state_lockfile != NULL) && + make_state_locked) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + fatal(gettext("Giving up on writing statefile")); + } + sleep(10); + (void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid()); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fatal(gettext("Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + } + warning(gettext("Initial write of statefile failed. Trying again on %s"), + buffer); + } + + /* Write the version stamp. */ + XFWRITE(make_version->string_mb, + strlen(make_version->string_mb), + fd); + XPUTC(colon_char, fd); + XPUTC(tab_char, fd); + XFWRITE(current_make_version->string_mb, + strlen(current_make_version->string_mb), + fd); + XPUTC(newline_char, fd); + + /* + * Go through all the targets, dump their dependencies and + * command used. + */ + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + /* + * If the target has no command used nor dependencies, + * we can go to the next one. + */ + if ((lines = get_prop(np->prop, line_prop)) == NULL) { + continue; + } + /* If this target is a special target, don't print. */ + if (np->special_reader != no_special) { + continue; + } + /* + * Find out if any of the targets dependencies should + * be written to .make.state. + */ + for (m = 0, dependency = lines->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (m = !dependency->stale + && (dependency->name != force) +#ifndef PRINT_EXPLICIT_DEPEN + && dependency->automatic +#endif + ) { + break; + } + } + /* Only print if dependencies listed. */ + if (m || (lines->body.line.command_used != NULL)) { + name_printed = false; + /* + * If this target was built during this make run, + * we mark it. + */ + built_this_run = false; + if (np->has_built) { + built_this_run = true; + XFWRITE(built_last_make_run->string_mb, + strlen(built_last_make_run->string_mb), + fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + /* If the target has dependencies, we dump them. */ + target_name = escape_target_name(np); + if (np->has_long_member_name) { + target_name = + get_prop(np->prop, long_member_name_prop) + ->body.long_member_name.member_name-> + string_mb; + } + if (m) { + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XFPUTS("\t", fd); + name_printed = true; + line_length = 0; + for (dependency = + lines->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + print_auto_depes(dependency, + fd, + built_this_run, + &line_length, + target_name, + long_jump); + } + XFPUTS("\n", fd); + } + /* If there is a command used, we dump it. */ + if (lines->body.line.command_used != NULL) { + /* + * Only write the target name if it + * wasn't done for the dependencies. + */ + if (!name_printed) { + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + /* + * Write the command lines. + * Prefix each textual line with a tab. + */ + for (cp = lines->body.line.command_used; + cp != NULL; + cp = cp->next) { + char *csp; + int n; + + XPUTC(tab_char, fd); + if (cp->command_line != NULL) { + for (csp = cp-> + command_line-> + string_mb, + n = strlen(cp-> + command_line-> + string_mb); + n > 0; + n--, csp++) { + XPUTC(*csp, fd); + if (*csp == + (int) newline_char) { + XPUTC(tab_char, + fd); + } + } + } + XPUTC(newline_char, fd); + } + } + (void)free(target_name); + } + } + if (fclose(fd) == EOF) { + longjmp(long_jump, LONGJUMP_VALUE); + } + if (attempts == 0) { + if (unlink(make_state->string_mb) != 0 && errno != ENOENT) { + lock_err = errno; /* Save it! unlink() can change errno */ + /* Delete temporary statefile */ + (void) unlink(make_state_tempfile); + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not delete old statefile `%s': %s"), + make_state->string_mb, + errmsg(lock_err)); + } + if (rename(make_state_tempfile, make_state->string_mb) != 0) { + lock_err = errno; /* Save it! unlink() can change errno */ + /* Delete temporary statefile */ + (void) unlink(make_state_tempfile); + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not rename `%s' to `%s': %s"), + make_state_tempfile, + make_state->string_mb, + errmsg(lock_err)); + } + } + if ((make_state_lockfile != NULL) && make_state_locked) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } +} + +/* + * print_auto_depes(dependency, fd, built_this_run, + * line_length, target_name, long_jump) + * + * Will print a dependency list for automatic entries. + * + * Parameters: + * dependency The dependency to print + * fd The file to print it to + * built_this_run If on we prefix each line with .BUILT_THIS... + * line_length Pointer to line length var that we update + * target_name We need this when we restart line + * long_jump setjmp/longjmp buffer used for IO error action + * + * Global variables used: + * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written + * force The Name " FORCE", compared against + */ +static void +print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump) +{ + if (!dependency->automatic || + dependency->stale || + (dependency->name == force)) { + return; + } + XFWRITE(dependency->name->string_mb, + strlen(dependency->name->string_mb), + fd); + /* + * Check if the dependency line is too long. + * If so, break it and start a new one. + */ + if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) { + *line_length = 0; + XPUTC(newline_char, fd); + if (built_this_run) { + XFPUTS(built_last_make_run->string_mb, fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XPUTC(tab_char, fd); + } else { + XFPUTS(" ", fd); + } + return; +} + + diff --git a/bin/svr4.make.rules.file b/bin/svr4.make.rules.file new file mode 100644 index 0000000..cda4edb --- /dev/null +++ b/bin/svr4.make.rules.file @@ -0,0 +1,241 @@ +# +# 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 1994 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +.SUFFIXES: .o .c .c~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~ .f .f~ \ +.C .C~ .Y .Y~ .L .L~ + +MAKE=make +BUILD=build +AR=ar +ARFLAGS=rv +AS=as +ASFLAGS= +CC=cc +CFLAGS=-O +F77=f77 +FFLAGS=-O +GET=get +GFLAGS= +LD=ld +LDFLAGS= +LEX=lex +LFLAGS= +YACC=yacc +YFLAGS= +C++C=CC +C++FLAGS=-O + + +.c: + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +.c~: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) $*.c -o $@ $(LDFLAGS) + -rm -f $*.c +.f: + $(F77) $(FFLAGS) $< -o $@ $(LDFLAGS) +.f~: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) $*.f -o $@ $(LDFLAGS) + -rm -f $*.f +.s: + $(AS) $(ASFLAGS) $< -o $@ $(LDFLAGS) +.s~: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) $*.s -o $* $(LDFLAGS) + -rm -f $*.s +.sh: + cp $< $@; chmod 0777 $@ +.sh~: + $(GET) $(GFLAGS) $< + cp $*.sh $*; chmod 0777 $@ + -rm -f $*.sh +.C: + $(C++C) $(C++FLAGS) $< -o $@ $(LDFLAGS) +.C~: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) $*.C -o $@ $(LDFLAGS) + -rm -f $*.C + +.c.a: + $(CC) $(CFLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.c.o: + $(CC) $(CFLAGS) -c $< +.c~.a: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[co] +.c~.c: + $(GET) $(GFLAGS) $< +.c~.o: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + -rm -f $*.c +.f.a: + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.f.o: + $(F77) $(FFLAGS) -c $*.f +.f~.a: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[fo] +.f~.f: + $(GET) $(GFLAGS) $< +.f~.o: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + -rm -f $*.f +.h~.h: + $(GET) $(GFLAGS) $< +.l.c: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.l.o: + $(LEX) $(LFLAGS) $< + $(CC) $(CFLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.l~.c: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ + -rm -f $*.l +.l~.l: + $(GET) $(GFLAGS) $< +.l~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.l + mv lex.yy.o $@ +.s.a: + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o +.s.o: + $(AS) $(ASFLAGS) -o $@ $< +.s~.a: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[so] +.s~.o: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + -rm -f $*.s +.s~.s: + $(GET) $(GFLAGS) $< +.sh~.sh: + $(GET) $(GFLAGS) $< +.y.c: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.y.o: + $(YACC) $(YFLAGS) $< + $(CC) $(CFLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.y~.c: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $*.c + -rm -f $*.y +.y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + -rm -f y.tab.c $*.y + mv y.tab.o $*.o +.y~.y : + $(GET) $(GFLAGS) $< +.C.a: + $(C++C) $(C++FLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.C.o: + $(C++C) $(C++FLAGS) -c $< +.C~.a: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[Co] +.C~.C: + $(GET) $(GFLAGS) $< +.C~.o: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + -rm -f $*.C +.L.C: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.L.o: + $(LEX) $(LFLAGS) $< + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.L~.C: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + mv lex.yy.c $@ + -rm -f $*.L +.L~.L: + $(GET) $(GFLAGS) $< +.L~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.L + mv lex.yy.c $@ +.Y.C: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.Y.o: + $(YACC) $(YFLAGS) $< + $(C++C) $(C++FLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.Y~.C: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + mv y.tab.c $*.C + -rm -f $*.Y +.Y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + $(C++C) $(C++FLAGS) -c y.tab.c + -rm -f y.tab.c $*.Y + mv y.tab.o $*.o +.Y~.Y : + $(GET) $(GFLAGS) $< + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + $(CC) -c markfile.c + -rm -f markfile.c + +.SCCS_GET: + $(GET) $(GFLAGS) s.$@ diff --git a/include/bsd/bsd.h b/include/bsd/bsd.h new file mode 100644 index 0000000..2893d62 --- /dev/null +++ b/include/bsd/bsd.h @@ -0,0 +1,47 @@ +/* + * 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. + */ + +/* + * bsd/bsd.h: Interface definitions to BSD compatibility functions for SVR4. + */ + +#ifndef _BSD_BSD_H +#define _BSD_BSD_H + +#include + + +#ifndef __cplusplus +typedef void (*SIG_PF) (int); +#endif + +#ifdef __cplusplus +extern "C" SIG_PF bsd_signal(int a, SIG_PF b); +#else +extern void (*bsd_signal(int, void (*) (int))) (int); +#endif +extern void bsd_signals(void); + +#endif diff --git a/include/mk/defs.h b/include/mk/defs.h new file mode 100644 index 0000000..5f5beab --- /dev/null +++ b/include/mk/defs.h @@ -0,0 +1,396 @@ +#ifndef _MK_DEFS_H +#define _MK_DEFS_H +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Included files + */ + +#include + + + +/* + * Defined macros + */ + +#define SKIPSPACE(x) while (*x && \ + ((*x == (int) space_char) || \ + (*x == (int) tab_char) || \ + (*x == (int) comma_char))) { \ + x++; \ + } + +#define SKIPWORD(x) while (*x && \ + (*x != (int) space_char) && \ + (*x != (int) tab_char) && \ + (*x != (int) newline_char) && \ + (*x != (int) comma_char) && \ + (*x != (int) equal_char)) { \ + x++; \ + } + +#define SKIPTOEND(x) while (*x && \ + (*x != (int) newline_char)) { \ + x++; \ + } + +#define PMAKE_DEF_MAX_JOBS 2 /* Default number of parallel jobs. */ + +#define OUT_OF_DATE(a,b) \ + (((a) < (b)) || (((a) == file_doesnt_exist) && ((b) == file_doesnt_exist))) + +#define OUT_OF_DATE_SEC(a,b) \ + (((a).tv_sec < (b).tv_sec) || (((a).tv_sec == file_doesnt_exist.tv_sec) && ((b).tv_sec == file_doesnt_exist.tv_sec))) + +#define SETVAR(name, value, append) \ + setvar_daemon(name, value, append, no_daemon, \ + true, debug_level) +#define MAX(a,b) (((a)>(b))?(a):(b)) +/* + * New feature added to SUN5_0 make, invoke the vanilla svr4 make when + * the USE_SVR4_MAKE environment variable is set. + */ +#define SVR4_MAKE "/usr/ccs/lib/svr4.make" +#define USE_SVR4_MAKE "USE_SVR4_MAKE" +/* + * The standard MAXHOSTNAMELEN is 64. We want 32. + */ +#define MAX_HOSTNAMELEN 32 + + +/* + * typedefs & structs + */ +typedef enum { + no_state, + scan_name_state, + scan_command_state, + enter_dependencies_state, + enter_conditional_state, + enter_equal_state, + illegal_bytes_state, + illegal_eoln_state, + poorly_formed_macro_state, + exit_state +} Reader_state; + +struct _Name_vector { + struct _Name *names[64]; + struct _Chain *target_group[64]; + short used; + struct _Name_vector *next; +}; + +struct _Running { + struct _Running *next; + Doname state; + struct _Name *target; + struct _Name *true_target; + struct _Property *command; + struct _Name *sprodep_value; + char *sprodep_env; + int recursion_level; + Boolean do_get; + Boolean implicit; + Boolean redo; + int auto_count; + struct _Name **automatics; + pid_t pid; + int job_msg_id; + char *stdout_file; + char *stderr_file; + struct _Name *temp_file; + int conditional_cnt; + struct _Name **conditional_targets; + Boolean make_refd; +}; + +typedef enum { + serial_mode, + parallel_mode +} DMake_mode; + +typedef enum { + txt1_mode, + txt2_mode, + html1_mode +} DMake_output_mode; + +struct _Recursive_make { + struct _Recursive_make *next; /* Linked list */ + wchar_t *target;/* Name of target */ + wchar_t *oldline;/* Original line in .nse_depinfo */ + wchar_t *newline;/* New line in .nse_depinfo */ + wchar_t *cond_macrostring; + /* string built from value of + * conditional macros used by + * this target + */ + Boolean removed;/* This target is no longer recursive*/ +}; + +struct _Dyntarget { + struct _Dyntarget *next; + struct _Name *name; +}; + + +/* + * Typedefs for all structs + */ +typedef struct _Cmd_line *Cmd_line, Cmd_line_rec; +typedef struct _Dependency *Dependency, Dependency_rec; +typedef struct _Macro *Macro, Macro_rec; +typedef struct _Name_vector *Name_vector, Name_vector_rec; +typedef struct _Percent *Percent, Percent_rec; +typedef struct _Dyntarget *Dyntarget; +typedef struct _Recursive_make *Recursive_make, Recursive_make_rec; +typedef struct _Running *Running, Running_rec; + + +/* + * extern declarations for all global variables. + * The actual declarations are in globals.cc + */ +extern Boolean allrules_read; +extern Name posix_name; +extern Name svr4_name; +extern Boolean sdot_target; +extern Boolean all_parallel; +extern Boolean assign_done; +extern Boolean build_failed_seen; +extern Name built_last_make_run; +extern Name c_at; +extern Boolean command_changed; +extern Boolean commands_done; +extern Chain conditional_targets; +extern Name conditionals; +extern Boolean continue_after_error; +extern Property current_line; +extern Name current_make_version; +extern Name current_target; +extern short debug_level; +extern Cmd_line default_rule; +extern Name default_rule_name; +extern Name default_target_to_build; +extern Boolean depinfo_already_read; +extern Name dmake_group; +extern Name dmake_max_jobs; +extern Name dmake_mode; +extern DMake_mode dmake_mode_type; +extern Name dmake_output_mode; +extern DMake_output_mode output_mode; +extern Name dmake_odir; +extern Name dmake_rcfile; +extern Name done; +extern Name dot; +extern Name dot_keep_state; +extern Name dot_keep_state_file; +extern Name empty_name; +extern Boolean fatal_in_progress; +extern int file_number; +extern Name force; +extern Name ignore_name; +extern Boolean ignore_errors; +extern Boolean ignore_errors_all; +extern Name init; +extern int job_msg_id; +extern Boolean keep_state; +extern Name make_state; +extern timestruc_t make_state_before; +extern Boolean make_state_locked; +extern Dependency makefiles_used; +extern Name makeflags; +extern Name make_version; +extern char mbs_buffer2[]; +extern char *mbs_ptr; +extern char *mbs_ptr2; +extern Boolean no_action_was_taken; +extern Boolean no_parallel; +extern Name no_parallel_name; +extern Name not_auto; +extern Boolean only_parallel; +extern Boolean parallel; +extern Name parallel_name; +extern Name localhost_name; +extern int parallel_process_cnt; +extern Percent percent_list; +extern Dyntarget dyntarget_list; +extern Name plus; +extern Name pmake_machinesfile; +extern Name precious; +extern Name primary_makefile; +extern Boolean quest; +extern short read_trace_level; +extern Boolean reading_dependencies; +extern int recursion_level; +extern Name recursive_name; +extern short report_dependencies_level; +extern Boolean report_pwd; +extern Boolean rewrite_statefile; +extern Running running_list; +extern char *sccs_dir_path; +extern Name sccs_get_name; +extern Name sccs_get_posix_name; +extern Cmd_line sccs_get_rule; +extern Cmd_line sccs_get_org_rule; +extern Cmd_line sccs_get_posix_rule; +extern Name get_name; +extern Name get_posix_name; +extern Cmd_line get_rule; +extern Cmd_line get_posix_rule; +extern Boolean all_precious; +extern Boolean report_cwd; +extern Boolean silent_all; +extern Boolean silent; +extern Name silent_name; +extern char *stderr_file; +extern char *stdout_file; +extern Boolean stdout_stderr_same; +extern Dependency suffixes; +extern Name suffixes_name; +extern Name sunpro_dependencies; +extern Boolean target_variants; +extern const char *tmpdir; +extern const char *temp_file_directory; +extern Name temp_file_name; +extern short temp_file_number; +extern wchar_t *top_level_target; +extern Boolean touch; +extern Boolean trace_reader; +extern Boolean build_unconditional; +extern pathpt vroot_path; +extern Name wait_name; +extern wchar_t wcs_buffer2[]; +extern wchar_t *wcs_ptr; +extern wchar_t *wcs_ptr2; +extern long int hostid; + +/* + * Declarations of system defined variables + */ +/* On linux this variable is defined in 'signal.h' */ +extern char *sys_siglist[]; + +/* + * Declarations of system supplied functions + */ +extern int file_lock(char *, char *, int *, int); + +/* + * Declarations of functions declared and used by make + */ +extern void add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo); +extern void add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit); +extern void add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit); +extern void add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit); +extern void append_or_replace_macro_in_dyn_array(ASCII_Dyn_Array *Ar, char *macro); +extern void await_parallel(Boolean waitflg); +extern void build_suffix_list(Name target_suffix); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +extern void check_state(Name temp_file_name); +extern void cond_macros_into_string(Name np, String_rec *buffer); +extern void construct_target_string(); +extern void create_xdrs_ptr(void); +extern void depvar_add_to_list (Name name, Boolean cmdline); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic = false); +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname_parallel(Name target, Boolean do_get, Boolean implicit); +extern Doname dosys(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target); +extern void dump_make_state(void); +extern void dump_target_list(void); +extern void enter_conditional(register Name target, Name name, Name value, register Boolean append); +extern void enter_dependencies(register Name target, Chain target_group, register Name_vector depes, register Cmd_line command, register Separator separator); +extern void enter_dependency(Property line, register Name depe, Boolean automatic); +extern void enter_equal(Name name, Name value, register Boolean append); +extern Percent enter_percent(register Name target, Chain target_group, register Name_vector depes, Cmd_line command); +extern Dyntarget enter_dyntarget(register Name target); +extern Name_vector enter_name(String string, Boolean tail_present, register wchar_t *string_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen); +extern Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error); +extern Doname execute_parallel(Property line, Boolean waitflg, Boolean local = false); +extern Doname execute_serial(Property line); +extern timestruc_t& exists(register Name target); +extern void fatal(const char *, ...); +extern void fatal_reader(char *, ...); +extern Doname find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking); +extern Doname find_double_suffix_rule(register Name target, Property *command, Boolean rechecking); +extern Doname find_percent_rule(register Name target, Property *command, Boolean rechecking); +extern int find_run_directory (char *cmd, char *cwd, char *dir, char **pgm, char **run, char *path); +extern Doname find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking); +extern Chain find_target_groups(register Name_vector target_list, register int i, Boolean reset); +extern void finish_children(Boolean docheck); +extern void finish_running(void); +extern void free_chain(Name_vector ptr); +extern void gather_recursive_deps(void); +extern char *get_current_path(void); +extern int get_job_msg_id(void); +extern wchar_t *getmem_wc(register int size); +/* On linux getwd(char *) is defined in 'unistd.h' */ +#ifdef __cplusplus +extern "C" { +#endif +extern char *getwd(char *); +#ifdef __cplusplus +} +#endif +extern void handle_interrupt(int); +extern Boolean is_running(Name target); +extern void load_cached_names(void); +extern Boolean parallel_ok(Name target, Boolean line_prop_must_exists); +extern void print_dependencies(register Name target, register Property line); +extern void send_job_start_msg(Property line); +extern void send_rsrc_info_msg(int max_jobs, char *hostname, char *username); +extern void print_value(register Name value, Daemon daemon); +extern timestruc_t& read_archive(register Name target); +extern int read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library); +extern void read_directory_of_file(register Name file); +extern int read_make_machines(Name make_machines_name); +extern Boolean read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile); +extern void remove_recursive_dep(Name target); +extern void report_recursive_dep(Name target, char *line); +extern void report_recursive_done(void); +extern void report_recursive_init(void); +extern Recursive_make find_recursive_target(Name target); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern void set_locals(register Name target, register Property old_locals); +extern void setvar_append(register Name name, register Name value); +extern void setvar_envvar(void); +extern void special_reader(Name target, register Name_vector depes, Cmd_line command); +extern void startup_rxm(); +extern Doname target_can_be_built(register Name target); +extern char *time_to_string(const timestruc_t &time); +extern void update_target(Property line, Doname result); +extern void warning(char *, ...); +extern void write_state_file(int report_recursive, Boolean exiting); +extern Name vpath_translation(register Name cmd); +extern char *make_install_prefix(void); + +#define DEPINFO_FMT_VERSION "VERS2$" +#define VER_LEN strlen(DEPINFO_FMT_VERSION) + + +#endif diff --git a/include/mksh/defs.h b/include/mksh/defs.h new file mode 100644 index 0000000..9ad58c4 --- /dev/null +++ b/include/mksh/defs.h @@ -0,0 +1,945 @@ +#ifndef _MKSH_DEFS_H +#define _MKSH_DEFS_H +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include /* MB_LEN_MAX */ +#include +#include /* wchar_t */ +#include /* strcmp() */ +#include /* MAXPATHLEN */ +#include /* time_t, caddr_t */ +#include /* pathpt */ +#include /* timestruc_t */ +#include /* errno */ + +#include + +/* + * A type and some utilities for boolean values + */ + +#define false BOOLEAN_false +#define true BOOLEAN_true + +typedef enum { + false = 0, + true = 1, + failed = 0, + succeeded = 1 +} Boolean; +#define BOOLEAN(expr) ((expr) ? true : false) + +/* + * Some random constants (in an enum so dbx knows their values) + */ +enum { + update_delay = 30, /* time between rstat checks */ + ar_member_name_len = 1024, + hashsize = 2048 /* size of hash table */ +}; + + +/* + * Symbols that defines all the different char constants make uses + */ +enum { + ampersand_char = '&', + asterisk_char = '*', + at_char = '@', + backquote_char = '`', + backslash_char = '\\', + bar_char = '|', + braceleft_char = '{', + braceright_char = '}', + bracketleft_char = '[', + bracketright_char = ']', + colon_char = ':', + comma_char = ',', + dollar_char = '$', + doublequote_char = '"', + equal_char = '=', + exclam_char = '!', + greater_char = '>', + hat_char = '^', + hyphen_char = '-', + less_char = '<', + newline_char = '\n', + nul_char = '\0', + numbersign_char = '#', + parenleft_char = '(', + parenright_char = ')', + percent_char = '%', + period_char = '.', + plus_char = '+', + question_char = '?', + quote_char = '\'', + semicolon_char = ';', + slash_char = '/', + space_char = ' ', + tab_char = '\t', + tilde_char = '~' +}; + +/* + * For make i18n. Codeset independent. + * Setup character semantics by identifying all the special characters + * of make, and assigning each an entry in the char_semantics[] vector. + */ +enum { + ampersand_char_entry = 0, /* 0 */ + asterisk_char_entry, /* 1 */ + at_char_entry, /* 2 */ + backquote_char_entry, /* 3 */ + backslash_char_entry, /* 4 */ + bar_char_entry, /* 5 */ + bracketleft_char_entry, /* 6 */ + bracketright_char_entry, /* 7 */ + colon_char_entry, /* 8 */ + dollar_char_entry, /* 9 */ + doublequote_char_entry, /* 10 */ + equal_char_entry, /* 11 */ + exclam_char_entry, /* 12 */ + greater_char_entry, /* 13 */ + hat_char_entry, /* 14 */ + hyphen_char_entry, /* 15 */ + less_char_entry, /* 16 */ + newline_char_entry, /* 17 */ + numbersign_char_entry, /* 18 */ + parenleft_char_entry, /* 19 */ + parenright_char_entry, /* 20 */ + percent_char_entry, /* 21 */ + plus_char_entry, /* 22 */ + question_char_entry, /* 23 */ + quote_char_entry, /* 24 */ + semicolon_char_entry, /* 25 */ + no_semantics_entry /* 26 */ +}; + +/* + * CHAR_SEMANTICS_ENTRIES should be the number of entries above. + * The last entry in char_semantics[] should be blank. + */ +#define CHAR_SEMANTICS_ENTRIES 27 +/* +#define CHAR_SEMANTICS_STRING "&*@`\\|[]:$=!>-\n#()%+?;^<'\"" + */ + +/* + * Some utility macros + */ +#define ALLOC(x) ((struct _##x *)getmem(sizeof (struct _##x))) +#define ALLOC_WC(x) ((wchar_t *)getmem((x) * SIZEOFWCHAR_T)) +#define FIND_LENGTH -1 +#define GETNAME(a,b) getname_fn((a), (b), false) +#define IS_EQUAL(a,b) (!strcmp((a), (b))) +#define IS_EQUALN(a,b,n) (!strncmp((a), (b), (n))) +#define IS_WEQUAL(a,b) (!wcscmp((a), (b))) +#define IS_WEQUALN(a,b,n) (!wcsncmp((a), (b), (n))) +#define MBLEN(a) mblen((a), MB_LEN_MAX) +#define MBSTOWCS(a,b) (void) mbstowcs_with_check((a), (b), MAXPATHLEN) +#define MBTOWC(a,b) mbtowc((a), (b), MB_LEN_MAX) +#define SIZEOFWCHAR_T (sizeof (wchar_t)) +#define VSIZEOF(v) (sizeof (v) / sizeof ((v)[0])) +#define WCSTOMBS(a,b) (void) wcstombs((a), (b), (MAXPATHLEN * MB_LEN_MAX)) +#define WCTOMB(a,b) (void) wctomb((a), (b)) +#define HASH(v, c) (v = (v)*31 + (unsigned int)(c)) + +extern void mbstowcs_with_check(wchar_t *pwcs, const char *s, size_t n); + +/* + * Bits stored in funny vector to classify chars + */ +enum { + dollar_sem = 0001, + meta_sem = 0002, + percent_sem = 0004, + wildcard_sem = 0010, + command_prefix_sem = 0020, + special_macro_sem = 0040, + colon_sem = 0100, + parenleft_sem = 0200 +}; + +/* + * Type returned from doname class functions + */ +typedef enum { + build_dont_know = 0, + build_failed, + build_ok, + build_in_progress, + build_running, /* PARALLEL & DISTRIBUTED */ + build_pending, /* PARALLEL & DISTRIBUTED */ + build_serial, /* PARALLEL & DISTRIBUTED */ + build_subtree /* PARALLEL & DISTRIBUTED */ +} Doname; + +/* + * The String struct defines a string with the following layout + * "xxxxxxxxxxxxxxxCxxxxxxxxxxxxxxx________" + * ^ ^ ^ ^ + * | | | | + * buffer.start text.p text.end buffer.end + * text.p points to the next char to read/write. + */ +struct _String { + struct Text { + wchar_t *p; /* Read/Write pointer */ + wchar_t *end; /* Read limit pointer */ + } text; + struct Physical_buffer { + wchar_t *start; /* Points to start of buffer */ + wchar_t *end; /* End of physical buffer */ + } buffer; + Boolean free_after_use:1; +}; + +#define STRING_BUFFER_LENGTH 1024 +#define INIT_STRING_FROM_STACK(str, buf) { \ + str.buffer.start = (buf); \ + str.text.p = (buf); \ + str.text.end = NULL; \ + str.buffer.end = (buf) \ + + (sizeof (buf)/SIZEOFWCHAR_T); \ + str.free_after_use = false; \ + } + +#define APPEND_NAME(np, dest, len) append_string((np)->string_mb, (dest), (len)); + +class Wstring { + public: + struct _String string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + + public: + Wstring(); + Wstring(struct _Name * name); + ~Wstring(); + + void init(struct _Name * name); + void init(wchar_t * name, unsigned length); + unsigned length() { + return wcslen(string.buffer.start); + }; + void append_to_str(struct _String * str, unsigned off, unsigned length); + + wchar_t * get_string() { + return string.buffer.start; + }; + + wchar_t * get_string(unsigned off) { + return string.buffer.start + off; + }; + + Boolean equaln(wchar_t * str, unsigned length); + Boolean equal(wchar_t * str); + Boolean equal(wchar_t * str, unsigned off); + Boolean equal(wchar_t * str, unsigned off, unsigned length); + + Boolean equaln(Wstring * str, unsigned length); + Boolean equal(Wstring * str); + Boolean equal(Wstring * str, unsigned off); + Boolean equal(Wstring * str, unsigned off, unsigned length); +}; + + +/* + * Used for storing the $? list and also for the "target + target:" + * construct. + */ +struct _Chain { + struct _Chain *next; + struct _Name *name; + struct _Percent *percent_member; +}; + +/* + * Stores one command line for a rule + */ +struct _Cmd_line { + struct _Cmd_line *next; + struct _Name *command_line; + Boolean make_refd:1; /* $(MAKE) referenced? */ + /* + * Remember any command line prefixes given + */ + Boolean ignore_command_dependency:1; /* `?' */ + Boolean assign:1; /* `=' */ + Boolean ignore_error:1; /* `-' */ + Boolean silent:1; /* `@' */ + Boolean always_exec:1; /* `+' */ +}; + +/* + * Linked list of targets/files + */ +struct _Dependency { + struct _Dependency *next; + struct _Name *name; + Boolean automatic:1; + Boolean stale:1; + Boolean built:1; +}; + +/* + * The specials are markers for targets that the reader should special case + */ +typedef enum { + no_special, + built_last_make_run_special, + default_special, + get_posix_special, + get_special, + ignore_special, + keep_state_file_special, + keep_state_special, + make_version_special, + no_parallel_special, + parallel_special, + posix_special, + precious_special, + sccs_get_posix_special, + sccs_get_special, + silent_special, + suffixes_special, + svr4_special, + localhost_special +} Special; + +typedef enum { + no_colon, + one_colon, + two_colon, + equal_seen, + conditional_seen, + none_seen +} Separator; + +/* + * Magic values for the timestamp stored with each name object + */ + + +extern const timestruc_t file_no_time; +extern const timestruc_t file_doesnt_exist; +extern const timestruc_t file_is_dir; +extern const timestruc_t file_min_time; +extern const timestruc_t file_max_time; + +/* + * Each Name has a list of properties + * The properties are used to store information that only + * a subset of the Names need + */ +typedef enum { + no_prop, + conditional_prop, + line_prop, + macro_prop, + makefile_prop, + member_prop, + recursive_prop, + sccs_prop, + suffix_prop, + target_prop, + time_prop, + vpath_alias_prop, + long_member_name_prop, + macro_append_prop, + env_mem_prop +} Property_id; + +typedef enum { + no_daemon = 0, + chain_daemon +} Daemon; + +struct _Env_mem { + char *value; +}; + +struct _Macro_appendix { + struct _Name *value; + struct _Name *value_to_append; +}; + +struct _Macro { + /* + * For "ABC = xyz" constructs + * Name "ABC" get one macro prop + */ + struct _Name *value; + Boolean exported:1; + Boolean read_only:1; + /* + * This macro is defined conditionally + */ + Boolean is_conditional:1; + /* + * The list for $? is stored as a structured list that + * is translated into a string iff it is referenced. + * This is why some macro values need a daemon. + */ + Daemon daemon:2; +}; + +struct _Macro_list { + struct _Macro_list *next; + char *macro_name; + char *value; +}; + +enum sccs_stat { + DONT_KNOW_SCCS = 0, + NO_SCCS, + HAS_SCCS +}; + +struct _Name { + struct _Property *prop; /* List of properties */ + char *string_mb; /* Multi-byte name string */ + struct { + unsigned int length; + } hash; + struct { + timestruc_t time; /* Modification */ + int stat_errno; /* error from "stat" */ + off_t size; /* Of file */ + mode_t mode; /* Of file */ + Boolean is_file:1; + Boolean is_dir:1; + Boolean is_sym_link:1; + Boolean is_precious:1; + enum sccs_stat has_sccs:2; + } stat; + /* + * Count instances of :: definitions for this target + */ + short colon_splits; + /* + * We only clear the automatic depes once per target per report + */ + short temp_file_number; + /* + * Count how many conditional macros this target has defined + */ + short conditional_cnt; + /* + * A conditional macro was used when building this target + */ + Boolean depends_on_conditional:1; + /* + * Pointer to list of conditional macros which were used to build + * this target + */ + struct _Macro_list *conditional_macro_list; + Boolean has_member_depe:1; + Boolean is_member:1; + /* + * This target is a directory that has been read + */ + Boolean has_read_dir:1; + /* + * This name is a macro that is now being expanded + */ + Boolean being_expanded:1; + /* + * This name is a magic name that the reader must know about + */ + Special special_reader:5; + Doname state:3; + Separator colons:3; + Boolean has_depe_list_expanded:1; + Boolean suffix_scan_done:1; + Boolean has_complained:1; /* For sccs */ + /* + * This target has been built during this make run + */ + Boolean ran_command:1; + Boolean with_squiggle:1; /* for .SUFFIXES */ + Boolean without_squiggle:1; /* for .SUFFIXES */ + Boolean has_read_suffixes:1; /* Suffix list cached*/ + Boolean has_suffixes:1; + Boolean has_target_prop:1; + Boolean has_vpath_alias_prop:1; + Boolean dependency_printed:1; /* For dump_make_state() */ + Boolean dollar:1; /* In namestring */ + Boolean meta:1; /* In namestring */ + Boolean percent:1; /* In namestring */ + Boolean wildcard:1; /* In namestring */ + Boolean has_parent:1; + Boolean is_target:1; + Boolean has_built:1; + Boolean colon:1; /* In namestring */ + Boolean parenleft:1; /* In namestring */ + Boolean has_recursive_dependency:1; + Boolean has_regular_dependency:1; + Boolean is_double_colon:1; + Boolean is_double_colon_parent:1; + Boolean has_long_member_name:1; + /* + * allowed to run in parallel + */ + Boolean parallel:1; + /* + * not allowed to run in parallel + */ + Boolean no_parallel:1; + /* + * used in dependency_conflict + */ + Boolean checking_subtree:1; + Boolean added_pattern_conditionals:1; + /* + * rechecking target for possible rebuild + */ + Boolean rechecking_target:1; + /* + * build this target in silent mode + */ + Boolean silent_mode:1; + /* + * build this target in ignore error mode + */ + Boolean ignore_error_mode:1; + Boolean dont_activate_cond_values:1; + /* + * allowed to run serially on local host + */ + Boolean localhost:1; +}; + +/* + * Stores the % matched default rules + */ +struct _Percent { + struct _Percent *next; + struct _Name **patterns; + struct _Name *name; + struct _Percent *dependencies; + struct _Cmd_line *command_template; + struct _Chain *target_group; + int patterns_total; + Boolean being_expanded; +}; + +struct Conditional { + /* + * For "foo := ABC [+]= xyz" constructs + * Name "foo" gets one conditional prop + */ + struct _Name *target; + struct _Name *name; + struct _Name *value; + int sequence; + Boolean append:1; +}; + +struct Line { + /* + * For "target : dependencies" constructs + * Name "target" gets one line prop + */ + struct _Cmd_line *command_template; + struct _Cmd_line *command_used; + struct _Dependency *dependencies; + timestruc_t dependency_time; + struct _Chain *target_group; + Boolean is_out_of_date:1; + Boolean sccs_command:1; + Boolean command_template_redefined:1; + Boolean dont_rebuild_command_used:1; + /* + * Values for the dynamic macros + */ + struct _Name *target; + struct _Name *star; + struct _Name *less; + struct _Name *percent; + struct _Chain *query; +}; + +struct Makefile { + /* + * Names that reference makefiles gets one prop + */ + wchar_t *contents; + off_t size; +}; + +struct Member { + /* + * For "lib(member)" and "lib((entry))" constructs + * Name "lib(member)" gets one member prop + * Name "lib((entry))" gets one member prop + * The member field is filled in when the prop is refd + */ + struct _Name *library; + struct _Name *entry; + struct _Name *member; +}; + +struct Recursive { + /* + * For "target: .RECURSIVE dir makefiles" constructs + * Used to keep track of recursive calls to make + * Name "target" gets one recursive prop + */ + struct _Name *directory; + struct _Name *target; + struct _Dependency *makefiles; + Boolean has_built; + Boolean in_depinfo; +}; + +struct Sccs { + /* + * Each file that has a SCCS s. file gets one prop + */ + struct _Name *file; +}; + +struct Suffix { + /* + * Cached list of suffixes that can build this target + * suffix is built from .SUFFIXES + */ + struct _Name *suffix; + struct _Cmd_line *command_template; +}; + +struct Target { + /* + * For "target:: dependencies" constructs + * The "::" construct is handled by converting it to + * "foo: 1@foo" + "1@foo: dependecies" + * "1@foo" gets one target prop + * This target prop cause $@ to be bound to "foo" + * not "1@foo" when the rule is evaluated + */ + struct _Name *target; +}; + +struct STime { + /* + * Save the original time for :: targets + */ + timestruc_t time; +}; + +struct Vpath_alias { + /* + * If a file was found using the VPATH it gets + * a vpath_alias prop + */ + struct _Name *alias; +}; + +struct Long_member_name { + /* + * Targets with a truncated member name carries + * the full lib(member) name for the state file + */ + struct _Name *member_name; +}; + +union Body { + struct _Macro macro; + struct Conditional conditional; + struct Line line; + struct Makefile makefile; + struct Member member; + struct Recursive recursive; + struct Sccs sccs; + struct Suffix suffix; + struct Target target; + struct STime time; + struct Vpath_alias vpath_alias; + struct Long_member_name long_member_name; + struct _Macro_appendix macro_appendix; + struct _Env_mem env_mem; +}; + +#define PROPERTY_HEAD_SIZE (sizeof (struct _Property)-sizeof (union Body)) +struct _Property { + struct _Property *next; + Property_id type:4; + union Body body; +}; + +/* Structure for dynamic "ascii" arrays */ +struct ASCII_Dyn_Array { + char *start; + size_t size; +}; + +struct _Envvar { + struct _Name *name; + struct _Name *value; + struct _Envvar *next; + char *env_string; + Boolean already_put:1; +}; + +/* + * Macros for the reader + */ +#define GOTO_STATE(new_state) { \ + SET_STATE(new_state); \ + goto enter_state; \ + } +#define SET_STATE(new_state) state = (new_state) + +#define UNCACHE_SOURCE() if (source != NULL) { \ + source->string.text.p = source_p; \ + } +#define CACHE_SOURCE(comp) if (source != NULL) { \ + source_p = source->string.text.p - \ + (comp); \ + source_end = source->string.text.end; \ + } +#define GET_NEXT_BLOCK_NOCHK(source) { UNCACHE_SOURCE(); \ + source = get_next_block_fn(source); \ + CACHE_SOURCE(0) \ + } +#define GET_NEXT_BLOCK(source) { GET_NEXT_BLOCK_NOCHK(source); \ + if (source != NULL && source->error_converting) { \ + GOTO_STATE(illegal_bytes_state); \ + } \ + } +#define GET_CHAR() ((source == NULL) || \ + (source_p >= source_end) ? 0 : *source_p) + +struct _Source { + struct _String string; + struct _Source *previous; + off_t bytes_left_in_file; + short fd; + Boolean already_expanded:1; + Boolean error_converting:1; + char *inp_buf; + char *inp_buf_end; + char *inp_buf_ptr; +}; + +typedef enum { + reading_nothing, + reading_makefile, + reading_statefile, + rereading_statefile, + reading_cpp_file +} Makefile_type; + +/* + * Typedefs for all structs + */ +typedef struct _Chain *Chain, Chain_rec; +typedef struct _Envvar *Envvar, Envvar_rec; +typedef struct _Macro_list *Macro_list, Macro_list_rec; +typedef struct _Name *Name, Name_rec; +typedef struct _Property *Property, Property_rec; +typedef struct _Source *Source, Source_rec; +typedef struct _String *String, String_rec; + +/* + * name records hash table. + */ +struct Name_set { +private: + // single node in a tree + struct entry { + entry(Name name_, entry *parent_) : + name(name_), + parent(parent_), + left(0), + right(0), + depth(1) + {} + + Name name; + + entry *parent; + entry *left; + entry *right; + unsigned depth; + + void setup_depth() { + unsigned rdepth = (right != 0) ? right->depth : 0; + unsigned ldepth = (left != 0) ? left->depth : 0; + depth = 1 + ((ldepth > rdepth) ? ldepth : rdepth); + } + }; + +public: + // make iterator a friend of Name_set to have access to struct entry + struct iterator; + friend struct Name_set::iterator; + + // iterator over tree nodes + struct iterator { + public: + // constructors + iterator() : node(0) {} + iterator(entry *node_) : node(node_) {} + + // dereference operator + Name operator->() const { return node->name; } + + // conversion operator + operator Name() { return node->name; } + + // assignment operator + iterator& operator=(const iterator &o) { node = o.node; return *this; } + + // equality/inequality operators + int operator==(const iterator &o) const { return (node == o.node); } + int operator!=(const iterator &o) const { return (node != o.node); } + + // pre/post increment operators + iterator& operator++(); + iterator operator++(int) { iterator it = *this; ++*this; return it; } + + private: + // the node iterator points to + entry *node; + }; + +public: + // constructor + Name_set() : root(0) {} + + // lookup, insert and remove operations + Name lookup(const char *key); + Name insert(const char *key, Boolean &found); + void insert(Name name); + + // begin/end iterators + iterator begin() const; + iterator end() const { return iterator(); } + +private: + // rebalance given node + void rebalance(entry *node); + +private: + // tree root + entry *root; +}; + +/* + * extern declarations for all global variables. + * The actual declarations are in globals.cc + */ +extern char char_semantics[]; +extern wchar_t char_semantics_char[]; +extern Macro_list cond_macro_list; +extern Boolean conditional_macro_used; +extern Boolean do_not_exec_rule; /* `-n' */ +extern Boolean dollarget_seen; +extern Boolean dollarless_flag; +extern Name dollarless_value; +extern char **environ; +extern Envvar envvar; +extern int exit_status; +extern wchar_t *file_being_read; +/* Variable gnu_style=true if env. var. SUN_MAKE_COMPAT_MODE=GNU (RFE 4866328) */ +extern Boolean gnu_style; +extern Name_set hashtab; +extern Name host_arch; +extern Name host_mach; +extern int line_number; +extern char *make_state_lockfile; +extern Boolean make_word_mentioned; +extern Makefile_type makefile_type; +extern char mbs_buffer[]; +extern Name path_name; +extern Boolean posix; +extern Name query; +extern Boolean query_mentioned; +extern Name hat; +extern Boolean reading_environment; +extern Name shell_name; +extern Boolean svr4; +extern Name target_arch; +extern Name target_mach; +extern Boolean tilde_rule; +extern wchar_t wcs_buffer[]; +extern Boolean working_on_targets; +extern Name virtual_root; +extern Boolean vpath_defined; +extern Name vpath_name; +extern Boolean make_state_locked; +extern Boolean out_err_same; +extern pid_t childPid; + +/* + * RFE 1257407: make does not use fine granularity time info available from stat. + * High resolution time comparison. + */ + +inline int +operator==(const timestruc_t &t1, const timestruc_t &t2) { + return ((t1.tv_sec == t2.tv_sec) && (t1.tv_nsec == t2.tv_nsec)); +} + +inline int +operator!=(const timestruc_t &t1, const timestruc_t &t2) { + return ((t1.tv_sec != t2.tv_sec) || (t1.tv_nsec != t2.tv_nsec)); +} + +inline int +operator>(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec > t2.tv_nsec); + } + return (t1.tv_sec > t2.tv_sec); +} + +inline int +operator>=(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec >= t2.tv_nsec); + } + return (t1.tv_sec > t2.tv_sec); +} + +inline int +operator<(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec < t2.tv_nsec); + } + return (t1.tv_sec < t2.tv_sec); +} + +inline int +operator<=(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec <= t2.tv_nsec); + } + return (t1.tv_sec < t2.tv_sec); +} + +#endif diff --git a/include/mksh/dosys.h b/include/mksh/dosys.h new file mode 100644 index 0000000..18e3392 --- /dev/null +++ b/include/mksh/dosys.h @@ -0,0 +1,37 @@ +#ifndef _MKSH_DOSYS_H +#define _MKSH_DOSYS_H +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include + +extern Boolean await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs, int job_msg_id); +extern int doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio); +extern int doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio); +extern void redirect_io(char *stdout_file, char *stderr_file); +extern void sh_command2string(register String command, register String destination); + +#endif diff --git a/include/mksh/globals.h b/include/mksh/globals.h new file mode 100644 index 0000000..98a8766 --- /dev/null +++ b/include/mksh/globals.h @@ -0,0 +1,30 @@ +#ifndef _MKSH_GLOBALS_H +#define _MKSH_GLOBALS_H +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +#endif diff --git a/include/mksh/i18n.h b/include/mksh/i18n.h new file mode 100644 index 0000000..08f7e61 --- /dev/null +++ b/include/mksh/i18n.h @@ -0,0 +1,33 @@ +#ifndef _MKSH_I18N_H +#define _MKSH_I18N_H +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern int get_char_semantics_entry(wchar_t ch); +extern char get_char_semantics_value(wchar_t ch); + +#endif diff --git a/include/mksh/libmksh_init.h b/include/mksh/libmksh_init.h new file mode 100644 index 0000000..b10b5e0 --- /dev/null +++ b/include/mksh/libmksh_init.h @@ -0,0 +1,31 @@ +#ifndef _MKSH_INIT_H +#define _MKSH_INIT_H +/* + * 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 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +int libmksh_init() +void libmksh_fini(); + +#endif diff --git a/include/mksh/macro.h b/include/mksh/macro.h new file mode 100644 index 0000000..2068d48 --- /dev/null +++ b/include/mksh/macro.h @@ -0,0 +1,36 @@ +#ifndef _MKSH_MACRO_H +#define _MKSH_MACRO_H +/* + * 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 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern void expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd); +extern void expand_value(Name value, register String destination, Boolean cmd); +extern Name getvar(register Name name); + +extern Property setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level); + +#endif diff --git a/include/mksh/misc.h b/include/mksh/misc.h new file mode 100644 index 0000000..f3504bb --- /dev/null +++ b/include/mksh/misc.h @@ -0,0 +1,55 @@ +#ifndef _MKSH_MISC_H +#define _MKSH_MISC_H +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern void append_char(wchar_t from, register String to); +extern Property append_prop(register Name target, register Property_id type); +extern void append_string(register wchar_t *from, register String to, register int length); +extern void enable_interrupt(register void (*handler) (int)); +extern char *errmsg(int errnum); +extern void fatal_mksh(const char *message, ...); +extern void fatal_reader_mksh(const char *pattern, ...); +extern char *get_current_path_mksh(void); +extern Property get_prop(register Property start, register Property_id type); +extern char *getmem(register int size); +extern Name getname_fn(wchar_t *name, register int len, register Boolean dont_enter, register Boolean * foundp = NULL); +extern void store_name(Name name); +extern void free_name(Name name); +extern void handle_interrupt_mksh(int); +extern Property maybe_append_prop(register Name target, register Property_id type); +extern void retmem(wchar_t *p); +extern void retmem_mb(caddr_t p); +extern void setup_char_semantics(void); +extern void setup_interrupt(register void (*handler) (int)); +extern void warning_mksh(char * message, ...); + +extern void append_string(register char *from, register String to, register int length); +extern wchar_t *get_wstring(char * from); + + +#endif diff --git a/include/mksh/mksh.h b/include/mksh/mksh.h new file mode 100644 index 0000000..5c0804e --- /dev/null +++ b/include/mksh/mksh.h @@ -0,0 +1,37 @@ +#ifndef _MKSH_MKSH_H +#define _MKSH_MKSH_H +/* + * 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. + */ + + +/* + * Included files + */ + +#include +#include + + +#endif diff --git a/include/mksh/read.h b/include/mksh/read.h new file mode 100644 index 0000000..7866d3d --- /dev/null +++ b/include/mksh/read.h @@ -0,0 +1,33 @@ +#ifndef _MKSH_READ_H +#define _MKSH_READ_H +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern Source get_next_block_fn(register Source source); + +#endif diff --git a/include/vroot/args.h b/include/vroot/args.h new file mode 100644 index 0000000..09bb4b4 --- /dev/null +++ b/include/vroot/args.h @@ -0,0 +1,63 @@ +/* + * 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 1999 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _ARGS_H_ +#define _ARGS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { rw_read, rw_write} rwt, *rwpt; + +extern void translate_with_thunk(register char *filename, int (*thunk) (char *), pathpt path_vector, pathpt vroot_vector, rwt rw); + +union Args { + struct { int mode;} access; + struct { int mode;} chmod; + struct { int user; int group;} chown; + struct { int mode;} creat; + struct { char **argv; char **environ;} execve; + struct { struct stat *buffer;} lstat; + struct { int mode;} mkdir; + struct { char *name; int mode;} mount; + struct { int flags; int mode;} open; + struct { char *buffer; int buffer_size;} readlink; + struct { struct stat *buffer;} stat; + struct { int length;} truncate; + struct { struct timeval *time;} utimes; +}; + +extern union Args vroot_args; +extern int vroot_result; + +#endif diff --git a/include/vroot/report.h b/include/vroot/report.h new file mode 100644 index 0000000..6edd863 --- /dev/null +++ b/include/vroot/report.h @@ -0,0 +1,48 @@ +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _REPORT_H_ +#define _REPORT_H_ + +#include + +extern FILE *get_report_file(void); +extern char *get_target_being_reported_for(void); +extern void report_dependency(const char *name); +extern int file_lock(char *name, char *lockname, int *file_locked, int timeout); + +#define SUNPRO_DEPENDENCIES "SUNPRO_DEPENDENCIES" +#define LD "LD" +#define COMP "COMP" + +/* + * These relate to Sun's ancient source control system that predated TeamWare, + * named NSE. They appear to be used regardless of its presence, however, and + * so linger. + */ +#define NSE_DEPINFO ".nse_depinfo" +#define NSE_DEPINFO_LOCK ".nse_depinfo.lock" + +#endif diff --git a/include/vroot/vroot.h b/include/vroot/vroot.h new file mode 100644 index 0000000..cf84681 --- /dev/null +++ b/include/vroot/vroot.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + + +#ifndef _VROOT_H_ +#define _VROOT_H_ + +#include +#include + +#define VROOT_DEFAULT ((pathpt)-1) + +typedef struct { + char *path; + short length; +} pathcellt, *pathcellpt, patht; +typedef patht *pathpt; + +extern void add_dir_to_path(const char *path, register pathpt *pointer, register int position); +extern void flush_path_cache(void); +extern void flush_vroot_cache(void); +extern const char *get_path_name(void); +extern char *get_vroot_path(register char **vroot, register char **path, register char **filename); +extern const char *get_vroot_name(void); +extern int open_vroot(char *path, int flags, int mode, pathpt vroot_path, pathpt vroot_vroot); +extern pathpt parse_path_string(register char *string, register int remove_slash); +extern void scan_path_first(void); +extern void scan_vroot_first(void); +extern void set_path_style(int style); + +extern int access_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot); + +extern int execve_vroot(char *path, char **argv, char **environ, pathpt vroot_path, pathpt vroot_vroot); + +extern int lstat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot); +extern int stat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot); +extern int readlink_vroot(char *path, char *buffer, int buffer_size, pathpt vroot_path, pathpt vroot_vroot); + +#endif diff --git a/lib/bsd/bsd.cc b/lib/bsd/bsd.cc new file mode 100644 index 0000000..931a528 --- /dev/null +++ b/lib/bsd/bsd.cc @@ -0,0 +1,73 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +#include + +/* External references. + */ + +/* Forward references. + */ + +/* Static data. + */ + +extern SIG_PF +bsd_signal (int Signal, SIG_PF Handler) +{ + auto SIG_PF previous_handler; +#ifdef sun + previous_handler = sigset (Signal, Handler); +#else + auto struct sigaction new_action; + auto struct sigaction old_action; + + new_action.sa_flags = SA_SIGINFO; + new_action.sa_handler = (void (*) ()) Handler; + (void) sigemptyset (&new_action.sa_mask); + (void) sigaddset (&new_action.sa_mask, Signal); + + (void) sigaction (Signal, &new_action, &old_action); + + previous_handler = (SIG_PF) old_action.sa_handler; +#endif + return previous_handler; +} + +extern void +bsd_signals (void) +{ + static int initialized = 0; + + if (initialized == 0) + { + initialized = 1; + } + + return; +} diff --git a/lib/makestate/ld_file.c b/lib/makestate/ld_file.c new file mode 100644 index 0000000..b491b2a --- /dev/null +++ b/lib/makestate/ld_file.c @@ -0,0 +1,188 @@ +/* + * 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 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma init(ld_support_init) + +#include +#include +#include +#include +#include +#include +#include + +#define SUNPRO_DEPENDENCIES "SUNPRO_DEPENDENCIES" + +/* + * Linked list of strings - used to keep lists of names + * of directories or files. + */ + +struct Stritem { + char * str; + void * next; +}; + +typedef struct Stritem Stritem; + +static char * depend_file = NULL; +static Stritem * list = NULL; + + +void mk_state_init() +{ + depend_file = getenv(SUNPRO_DEPENDENCIES); +} /* mk_state_init() */ + + + +static void +prepend_str(Stritem **list, const char * str) +{ + Stritem * new; + char * newstr; + + if (!(new = calloc(1, sizeof (Stritem)))) { + perror("libmakestate.so"); + return; + } /* if */ + + if (!(newstr = malloc(strlen(str) + 1))) { + perror("libmakestate.so"); + return; + } /* if */ + + new->str = strcpy(newstr, str); + new->next = *list; + *list = new; + +} /* prepend_str() */ + + +void +mk_state_collect_dep(const char * file) +{ + /* + * SUNPRO_DEPENDENCIES wasn't set, we don't collect .make.state + * information. + */ + if (!depend_file) + return; + + prepend_str(&list, file); + +} /* mk_state_collect_dep() */ + + +void +mk_state_update_exit() +{ + Stritem * cur; + char lockfile[MAXPATHLEN], * err, * space, * target; + FILE * ofp; + extern char * file_lock(char *, char *, int); + + if (!depend_file) + return; + + if ((space = strchr(depend_file, ' ')) == NULL) + return; + *space = '\0'; + target = &space[1]; + + (void) sprintf(lockfile, "%s.lock", depend_file); + if ((err = file_lock(depend_file, lockfile, 0))) { + (void) fprintf(stderr, "%s\n", err); + return; + } /* if */ + + if (!(ofp = fopen(depend_file, "a"))) + return; + + if (list) + (void) fprintf(ofp, "%s: ", target); + + for (cur = list; cur; cur = cur->next) + (void) fprintf(ofp, " %s", cur->str); + + (void) fputc('\n', ofp); + + (void) fclose(ofp); + (void) unlink(lockfile); + *space = ' '; + +} /* mk_state_update_exit() */ + +static void +/* LINTED static unused */ +ld_support_init() +{ + mk_state_init(); + +} /* ld_support_init() */ + +/* ARGSUSED */ +void +ld_file(const char * file, const Elf_Kind ekind, int flags, Elf *elf) +{ + if(! ((flags & LD_SUP_DERIVED) && !(flags & LD_SUP_EXTRACTED))) + return; + + mk_state_collect_dep(file); + +} /* ld_file */ + +void +ld_atexit(int exit_code) +{ + if (exit_code) + return; + + mk_state_update_exit(); + +} /* ld_atexit() */ + +/* + * Supporting 64-bit objects + */ +void +ld_file64(const char * file, const Elf_Kind ekind, int flags, Elf *elf) +{ + if(! ((flags & LD_SUP_DERIVED) && !(flags & LD_SUP_EXTRACTED))) + return; + + mk_state_collect_dep(file); + +} /* ld_file64 */ + +void +ld_atexit64(int exit_code) +{ + if (exit_code) + return; + + mk_state_update_exit(); + +} /* ld_atexit64() */ diff --git a/lib/makestate/lock.c b/lib/makestate/lock.c new file mode 100644 index 0000000..38c30e3 --- /dev/null +++ b/lib/makestate/lock.c @@ -0,0 +1,172 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* errno */ + +#if defined(_LP64) +/* + * The symbols _sys_errlist and _sys_nerr are not visible in the + * LP64 libc. Use strerror(3C) instead. + */ +#else /* #_LP64 */ +extern char * sys_errlist[]; +extern int sys_nerr; +#endif /* #_LP64 */ + +static void file_lock_error(); + +/* + * This code stolen from the NSE library and changed to not depend + * upon any NSE routines or header files. + * + * Simple file locking. + * Create a symlink to a file. The "test and set" will be + * atomic as creating the symlink provides both functions. + * + * The timeout value specifies how long to wait for stale locks + * to disappear. If the lock is more than 'timeout' seconds old + * then it is ok to blow it away. This part has a small window + * of vunerability as the operations of testing the time, + * removing the lock and creating a new one are not atomic. + * It would be possible for two processes to both decide to blow + * away the lock and then have process A remove the lock and establish + * its own, and then then have process B remove the lock which accidentily + * removes A's lock rather than the stale one. + * + * A further complication is with the NFS. If the file in question is + * being served by an NFS server, then its time is set by that server. + * We can not use the time on the client machine to check for a stale + * lock. Therefore, a temp file on the server is created to get + * the servers current time. + * + * Returns an error message. NULL return means the lock was obtained. + * + */ +char * +file_lock(char * name, char * lockname, int timeout) +{ + int r; + int fd; + struct stat statb; + struct stat fs_statb; + char tmpname[MAXPATHLEN]; + static char msg[MAXPATHLEN]; + + if (timeout <= 0) { + timeout = 15; + } + for (;;) { + r = symlink(name, lockname); + if (r == 0) { + return (NULL); + } + if (errno != EEXIST) { + file_lock_error(msg, name, + (const char *)"symlink(%s, %s)", name, lockname); + return (msg); + } + for (;;) { + (void) sleep(1); + r = lstat(lockname, &statb); + if (r == -1) { + /* + * The lock must have just gone away - try + * again. + */ + break; + } + + /* + * With the NFS the time given a file is the time on + * the file server. This time may vary from the + * client's time. Therefore, we create a tmpfile in + * the same directory to establish the time on the + * server and use this time to see if the lock has + * expired. + */ + (void) sprintf(tmpname, "%s.XXXXXX", lockname); + (void) mktemp(tmpname); + fd = creat(tmpname, 0666); + if (fd != -1) { + (void) close(fd); + } else { + file_lock_error(msg, name, + (const char *)"creat(%s)", tmpname); + return (msg); + } + if (stat(tmpname, &fs_statb) == -1) { + file_lock_error(msg, name, + (const char *)"stat(%s)", tmpname); + return (msg); + } + (void) unlink(tmpname); + if (statb.st_mtime + timeout < fs_statb.st_mtime) { + /* + * The lock has expired - blow it away. + */ + (void) unlink(lockname); + break; + } + } + } + /* NOTREACHED */ +} + +/* + * Format a message telling why the lock could not be created. + */ +/* VARARGS4 */ +static void +file_lock_error(char * msg, char * file, const char * str, char * arg1, + char * arg2) +{ + int len; + + (void) sprintf(msg, "Could not lock file `%s'; ", file); + len = strlen(msg); + (void) sprintf(&msg[len], str, arg1, arg2); + (void) strcat(msg, " failed - "); +#if defined(_LP64) + /* Needs to be changed to use strerror(3C) instead. */ + len = strlen(msg); + (void) sprintf(&msg[len], "errno %d", errno); +#else /* #_LP64 */ + if (errno < sys_nerr) { + (void) strcat(msg, sys_errlist[errno]); + } else { + len = strlen(msg); + (void) sprintf(&msg[len], "errno %d", errno); + } +#endif /* #_LP64 */ +} diff --git a/lib/mksh/dosys.cc b/lib/mksh/dosys.cc new file mode 100644 index 0000000..5ff0ab7 --- /dev/null +++ b/lib/mksh/dosys.cc @@ -0,0 +1,577 @@ +/* + * 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 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + + +/* + * dosys.cc + * + * Execute one commandline + */ + +/* + * Included files + */ +#include /* WIFEXITED(status) */ +#include /* alloca() */ + +#include /* errno */ +#include /* errno */ +#include /* open() */ +#include +#include /* getvar() */ +#include /* getmem(), fatal_mksh(), errmsg() */ +#include /* SIG_DFL */ +#include /* open() */ +#include /* wait() */ +#include /* ulimit() */ +#include /* close(), dup2() */ +#include /* closefrom() */ +#include + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path); + +/* + * Workaround for NFS bug. Sometimes, when running 'open' on a remote + * dmake server, it fails with "Stale NFS file handle" error. + * The second attempt seems to work. + */ +int +my_open(const char *path, int oflag, mode_t mode) { + int res = open(path, oflag, mode); + if (res < 0 && (errno == ESTALE || errno == EAGAIN)) { + /* Stale NFS file handle. Try again */ + res = open(path, oflag, mode); + } + return res; +} + +/* + * void + * redirect_io(char *stdout_file, char *stderr_file) + * + * Redirects stdout and stderr for a child mksh process. + */ +void +redirect_io(char *stdout_file, char *stderr_file) +{ + int i; + + (void) closefrom(3); + if ((i = my_open(stdout_file, + O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, + S_IREAD | S_IWRITE)) < 0) { + fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"), + stdout_file, + errmsg(errno)); + } else { + if (dup2(i, 1) == -1) { + fatal_mksh("*** Error: dup2(3, 1) failed: %s", + errmsg(errno)); + } + close(i); + } + if (stderr_file == NULL) { + if (dup2(1, 2) == -1) { + fatal_mksh("*** Error: dup2(1, 2) failed: %s", + errmsg(errno)); + } + } else if ((i = my_open(stderr_file, + O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, + S_IREAD | S_IWRITE)) < 0) { + fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"), + stderr_file, + errmsg(errno)); + } else { + if (dup2(i, 2) == -1) { + fatal_mksh("*** Error: dup2(3, 2) failed: %s", + errmsg(errno)); + } + close(i); + } +} + +/* + * doshell(command, ignore_error) + * + * Used to run command lines that include shell meta-characters. + * The make macro SHELL is supposed to contain a path to the shell. + * + * Return value: + * The pid of the process we started + * + * Parameters: + * command The command to run + * ignore_error Should we abort on error? + * + * Global variables used: + * filter_stderr If -X is on we redirect stderr + * shell_name The Name "SHELL", used to get the path to shell + */ +int +doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio) +{ + char *argv[6]; + int argv_index = 0; + int cmd_argv_index; + int length; + char nice_prio_buf[MAXPATHLEN]; + register Name shell = getvar(shell_name); + register char *shellname; + char *tmp_mbs_buffer; + + + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) { + shellname = shell->string_mb; + } else { + shellname++; + } + + /* + * Only prepend the /usr/bin/nice command to the original command + * if the nice priority, nice_prio, is NOT zero (0). + * Nice priorities can be a positive or a negative number. + */ + if (nice_prio != 0) { + argv[argv_index++] = (char *)"nice"; + (void) sprintf(nice_prio_buf, "-%d", nice_prio); + argv[argv_index++] = strdup(nice_prio_buf); + } + argv[argv_index++] = shellname; + argv[argv_index++] = (char*)(ignore_error ? "-c" : "-ce"); + if ((length = wcslen(command)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); + cmd_argv_index = argv_index; + argv[argv_index++] = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, command); + cmd_argv_index = argv_index; + argv[argv_index++] = strdup(mbs_buffer); + } + argv[argv_index] = NULL; + (void) fflush(stdout); + if ((childPid = fork()) == 0) { + enable_interrupt((void (*) (int)) SIG_DFL); +#if 0 + if (filter_stderr) { + redirect_stderr(); + } +#endif + if (nice_prio != 0) { + (void) execve("/usr/bin/nice", argv, environ); + fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"), + errmsg(errno)); + } else { + (void) execve(shell->string_mb, argv, environ); + fatal_mksh(gettext("Could not load Shell from `%s': %s"), + shell->string_mb, + errmsg(errno)); + } + } + if (childPid == -1) { + fatal_mksh(gettext("fork failed: %s"), + errmsg(errno)); + } + retmem_mb(argv[cmd_argv_index]); + return childPid; +} + +/* + * exec_vp(name, argv, envp, ignore_error) + * + * Like execve, but does path search. + * This starts command when make invokes it directly (without a shell). + * + * Return value: + * Returns false if the exec failed + * + * Parameters: + * name The name of the command to run + * argv Arguments for the command + * envp The environment for it + * ignore_error Should we abort on error? + * + * Global variables used: + * shell_name The Name "SHELL", used to get the path to shell + * vroot_path The path used by the vroot package + */ +static Boolean +exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path) +{ + register Name shell = getvar(shell_name); + register char *shellname; + char *shargv[4]; + Name tmp_shell; + + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + + for (int i = 0; i < 5; i++) { + (void) execve_vroot(name, + argv + 1, + envp, + vroot_path, + VROOT_DEFAULT); + switch (errno) { + case ENOEXEC: + case ENOENT: + /* That failed. Let the shell handle it */ + shellname = strrchr(shell->string_mb, (int) slash_char); + if (shellname == NULL) { + shellname = shell->string_mb; + } else { + shellname++; + } + shargv[0] = shellname; + shargv[1] = (char*)(ignore_error ? "-c" : "-ce"); + shargv[2] = argv[0]; + shargv[3] = NULL; + tmp_shell = getvar(shell_name); + if (IS_EQUAL(tmp_shell->string_mb, "")) { + tmp_shell = shell_name; + } + (void) execve_vroot(tmp_shell->string_mb, + shargv, + envp, + vroot_path, + VROOT_DEFAULT); + return failed; + case ETXTBSY: + /* + * The program is busy (debugged?). + * Wait and then try again. + */ + (void) sleep((unsigned) i); + case EAGAIN: + break; + default: + return failed; + } + } + return failed; +} + +/* + * doexec(command, ignore_error) + * + * Will scan an argument string and split it into words + * thus building an argument list that can be passed to exec_ve() + * + * Return value: + * The pid of the process started here + * + * Parameters: + * command The command to run + * ignore_error Should we abort on error? + * + * Global variables used: + * filter_stderr If -X is on we redirect stderr + */ +int +doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio) +{ + int arg_count = 5; + char **argv; + int length; + char nice_prio_buf[MAXPATHLEN]; + register char **p; + wchar_t *q; + register wchar_t *t; + char *tmp_mbs_buffer; + + /* + * Only prepend the /usr/bin/nice command to the original command + * if the nice priority, nice_prio, is NOT zero (0). + * Nice priorities can be a positive or a negative number. + */ + if (nice_prio != 0) { + arg_count += 2; + } + for (t = command; *t != (int) nul_char; t++) { + if (iswspace(*t)) { + arg_count++; + } + } + argv = (char **)alloca(arg_count * (sizeof(char *))); + /* + * Reserve argv[0] for sh in case of exec_vp failure. + * Don't worry about prepending /usr/bin/nice command to argv[0]. + * In fact, doing it may cause the sh command to fail! + */ + p = &argv[1]; + if ((length = wcslen(command)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); + argv[0] = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, command); + argv[0] = strdup(mbs_buffer); + } + + if (nice_prio != 0) { + *p++ = strdup("/usr/bin/nice"); + (void) sprintf(nice_prio_buf, "-%d", nice_prio); + *p++ = strdup(nice_prio_buf); + } + /* Build list of argument words. */ + for (t = command; *t;) { + if (p >= &argv[arg_count]) { + /* This should never happen, right? */ + WCSTOMBS(mbs_buffer, command); + fatal_mksh(gettext("Command `%s' has more than %d arguments"), + mbs_buffer, + arg_count); + } + q = t; + while (!iswspace(*t) && (*t != (int) nul_char)) { + t++; + } + if (*t) { + for (*t++ = (int) nul_char; iswspace(*t); t++); + } + if ((length = wcslen(q)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1); + *p++ = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, q); + *p++ = strdup(mbs_buffer); + } + } + *p = NULL; + + /* Then exec the command with that argument list. */ + (void) fflush(stdout); + if ((childPid = fork()) == 0) { + enable_interrupt((void (*) (int)) SIG_DFL); +#if 0 + if (filter_stderr) { + redirect_stderr(); + } +#endif + (void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path); + fatal_mksh(gettext("Cannot load command `%s': %s"), argv[1], errmsg(errno)); + } + if (childPid == -1) { + fatal_mksh(gettext("fork failed: %s"), + errmsg(errno)); + } + for (int i = 0; argv[i] != NULL; i++) { + retmem_mb(argv[i]); + } + return childPid; +} + +/* + * await(ignore_error, silent_error, target, command, running_pid) + * + * Wait for one child process and analyzes + * the returned status when the child process terminates. + * + * Return value: + * Returns true if commands ran OK + * + * Parameters: + * ignore_error Should we abort on error? + * silent_error Should error messages be suppressed for dmake? + * target The target we are building, for error msgs + * command The command we ran, for error msgs + * running_pid The pid of the process we are waiting for + * + * Static variables used: + * filter_file The fd for the filter file + * filter_file_name The name of the filter file + * + * Global variables used: + * filter_stderr Set if -X is on + */ +Boolean +await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id) +{ + int status; + char *buffer; + int core_dumped; + int exit_status; + FILE *outfp; + register pid_t pid; + struct stat stat_buff; + int termination_signal; + char tmp_buf[MAXPATHLEN]; + + while ((pid = wait(&status)) != running_pid) { + if (pid == -1) { + fatal_mksh(gettext("wait() failed: %s"), errmsg(errno)); + } + } + (void) fflush(stdout); + (void) fflush(stderr); + + if (status == 0) { + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and status is 0."); +#endif + + return succeeded; + } + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and status is *NOT* 0."); +#endif + + + exit_status = WEXITSTATUS(status); + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and exit_status is %d.", exit_status); +#endif + + termination_signal = WTERMSIG(status); + core_dumped = WCOREDUMP(status); + + /* + * If the child returned an error, we now try to print a + * nice message about it. + */ + + tmp_buf[0] = (int) nul_char; + if (!silent_error) { + if (exit_status != 0) { + (void) fprintf(stdout, + gettext("*** Error code %d"), + exit_status); + } else { + (void) fprintf(stdout, + gettext("*** Signal %d"), + termination_signal); + if (core_dumped) { + (void) fprintf(stdout, + gettext(" - core dumped")); + } + } + if (ignore_error) { + (void) fprintf(stdout, + gettext(" (ignored)")); + } + (void) fprintf(stdout, "\n"); + (void) fflush(stdout); + } + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), returning failed."); +#endif + + return failed; +} + +/* + * sh_command2string(command, destination) + * + * Run one sh command and capture the output from it. + * + * Return value: + * + * Parameters: + * command The command to run + * destination Where to deposit the output from the command + * + * Static variables used: + * + * Global variables used: + */ +void +sh_command2string(register String command, register String destination) +{ + register FILE *fd; + register int chr; + int status; + Boolean command_generated_output = false; + + command->text.p = (int) nul_char; + WCSTOMBS(mbs_buffer, command->buffer.start); + if ((fd = popen(mbs_buffer, "r")) == NULL) { + WCSTOMBS(mbs_buffer, command->buffer.start); + fatal_mksh(gettext("Could not run command `%s' for :sh transformation"), + mbs_buffer); + } + while ((chr = getc(fd)) != EOF) { + if (chr == (int) newline_char) { + chr = (int) space_char; + } + command_generated_output = true; + append_char(chr, destination); + } + + /* + * We don't want to keep the last LINE_FEED since usually + * the output of the 'sh:' command is used to evaluate + * some MACRO. ( /bin/sh and other shell add a line feed + * to the output so that the prompt appear in the right place. + * We don't need that + */ + if (command_generated_output){ + if ( *(destination->text.p-1) == (int) space_char) { + * (-- destination->text.p) = '\0'; + } + } else { + /* + * If the command didn't generate any output, + * set the buffer to a null string. + */ + *(destination->text.p) = '\0'; + } + + status = pclose(fd); + if (status != 0) { + WCSTOMBS(mbs_buffer, command->buffer.start); + fatal_mksh(gettext("The command `%s' returned status `%d'"), + mbs_buffer, + WEXITSTATUS(status)); + } +} + + diff --git a/lib/mksh/globals.cc b/lib/mksh/globals.cc new file mode 100644 index 0000000..255985e --- /dev/null +++ b/lib/mksh/globals.cc @@ -0,0 +1,128 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * globals.cc + * + * This declares all global variables + */ + +/* + * Included files + */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Global variables + */ +char char_semantics[CHAR_SEMANTICS_ENTRIES]; +wchar_t char_semantics_char[] = { + ampersand_char, + asterisk_char, + at_char, + backquote_char, + backslash_char, + bar_char, + bracketleft_char, + bracketright_char, + colon_char, + dollar_char, + doublequote_char, + equal_char, + exclam_char, + greater_char, + hat_char, + hyphen_char, + less_char, + newline_char, + numbersign_char, + parenleft_char, + parenright_char, + percent_char, + plus_char, + question_char, + quote_char, + semicolon_char, + nul_char +}; +Macro_list cond_macro_list; +Boolean conditional_macro_used; +Boolean do_not_exec_rule; /* `-n' */ +Boolean dollarget_seen; +Boolean dollarless_flag; +Name dollarless_value; +Envvar envvar; +#ifdef lint +char **environ; +#endif +int exit_status; +wchar_t *file_being_read; +/* Variable gnu_style=true if env. var. SUN_MAKE_COMPAT_MODE=GNU (RFE 4866328) */ +Boolean gnu_style = false; +Name_set hashtab; +Name host_arch; +Name host_mach; +int line_number; +char *make_state_lockfile; +Boolean make_word_mentioned; +Makefile_type makefile_type = reading_nothing; +char mbs_buffer[(MAXPATHLEN * MB_LEN_MAX)]; +Name path_name; +Boolean posix = true; +Name hat; +Name query; +Boolean query_mentioned; +Boolean reading_environment; +Name shell_name; +Boolean svr4 = false; +Name target_arch; +Name target_mach; +Boolean tilde_rule; +Name virtual_root; +Boolean vpath_defined; +Name vpath_name; +wchar_t wcs_buffer[MAXPATHLEN]; +Boolean working_on_targets; +Boolean out_err_same; +pid_t childPid = -1; // This variable is used for killing child's process + // Such as qrsh, running command, etc. + +/* + * timestamps defined in defs.h + */ +const timestruc_t file_no_time = { -1, 0 }; +const timestruc_t file_doesnt_exist = { 0, 0 }; +const timestruc_t file_is_dir = { 1, 0 }; +const timestruc_t file_min_time = { 2, 0 }; +const timestruc_t file_max_time = { INT_MAX, 0 }; diff --git a/lib/mksh/i18n.cc b/lib/mksh/i18n.cc new file mode 100644 index 0000000..acc0e0c --- /dev/null +++ b/lib/mksh/i18n.cc @@ -0,0 +1,91 @@ +/* + * 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. + */ + + +/* + * i18n.cc + * + * Deal with internationalization conversions + */ + +/* + * Included files + */ +#include +#include /* setup_char_semantics() */ + +/* + * get_char_semantics_value(ch) + * + * Return value: + * The character semantics of ch. + * + * Parameters: + * ch character we want semantics for. + * + */ +char +get_char_semantics_value(wchar_t ch) +{ + static Boolean char_semantics_setup; + + if (!char_semantics_setup) { + setup_char_semantics(); + char_semantics_setup = true; + } + return char_semantics[get_char_semantics_entry(ch)]; +} + +/* + * get_char_semantics_entry(ch) + * + * Return value: + * The slot number in the array for special make chars, + * else the slot number of the last array entry. + * + * Parameters: + * ch The wide character + * + * Global variables used: + * char_semantics_char[] array of special wchar_t chars + * "&*@`\\|[]:$=!>-\n#()%?;^<'\"" + */ +int +get_char_semantics_entry(wchar_t ch) +{ + wchar_t *char_sem_char; + + char_sem_char = (wchar_t *) wcschr(char_semantics_char, ch); + if (char_sem_char == NULL) { + /* + * Return the integer entry for the last slot, + * whose content is empty. + */ + return (CHAR_SEMANTICS_ENTRIES - 1); + } else { + return (char_sem_char - char_semantics_char); + } +} + diff --git a/lib/mksh/macro.cc b/lib/mksh/macro.cc new file mode 100644 index 0000000..b815105 --- /dev/null +++ b/lib/mksh/macro.cc @@ -0,0 +1,1335 @@ +/* + * 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 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * macro.cc + * + * Handle expansion of make macros + */ + +/* + * Included files + */ +#include /* sh_command2string() */ +#include /* get_char_semantics_value() */ +#include +#include /* retmem() */ +#include /* get_next_block_fn() */ + +#include + +/* + * File table of contents + */ +static void add_macro_to_global_list(Name macro_to_add); +static void expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd); + +static void init_arch_macros(void); +static void init_mach_macros(void); +static Boolean init_arch_done = false; +static Boolean init_mach_done = false; + + +long env_alloc_num = 0; +long env_alloc_bytes = 0; + +/* + * getvar(name) + * + * Return expanded value of macro. + * + * Return value: + * The expanded value of the macro + * + * Parameters: + * name The name of the macro we want the value for + * + * Global variables used: + */ +Name +getvar(register Name name) +{ + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Name result; + + if ((name == host_arch) || (name == target_arch)) { + if (!init_arch_done) { + init_arch_done = true; + init_arch_macros(); + } + } + if ((name == host_mach) || (name == target_mach)) { + if (!init_mach_done) { + init_mach_done = true; + init_mach_macros(); + } + } + + INIT_STRING_FROM_STACK(destination, buffer); + expand_value(maybe_append_prop(name, macro_prop)->body.macro.value, + &destination, + false); + result = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + return result; +} + +/* + * expand_value(value, destination, cmd) + * + * Recursively expands all macros in the string value. + * destination is where the expanded value should be appended. + * + * Parameters: + * value The value we are expanding + * destination Where to deposit the expansion + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + */ +void +expand_value(Name value, register String destination, Boolean cmd) +{ + Source_rec sourceb; + register Source source = &sourceb; + register wchar_t *source_p = NULL; + register wchar_t *source_end = NULL; + wchar_t *block_start = NULL; + int quote_seen = 0; + + if (value == NULL) { + /* + * Make sure to get a string allocated even if it + * will be empty. + */ + MBSTOWCS(wcs_buffer, ""); + append_string(wcs_buffer, destination, FIND_LENGTH); + destination->text.end = destination->text.p; + return; + } + if (!value->dollar) { + /* + * If the value we are expanding does not contain + * any $, we don't have to parse it. + */ + APPEND_NAME(value, + destination, + (int) value->hash.length + ); + destination->text.end = destination->text.p; + return; + } + + if (value->being_expanded) { + fatal_reader_mksh(gettext("Loop detected when expanding macro value `%s'"), + value->string_mb); + } + value->being_expanded = true; + /* Setup the structure we read from */ + Wstring vals(value); + sourceb.string.text.p = sourceb.string.buffer.start = wcsdup(vals.get_string()); + sourceb.string.free_after_use = true; + sourceb.string.text.end = + sourceb.string.buffer.end = + sourceb.string.text.p + value->hash.length; + sourceb.previous = NULL; + sourceb.fd = -1; + sourceb.inp_buf = + sourceb.inp_buf_ptr = + sourceb.inp_buf_end = NULL; + sourceb.error_converting = false; + /* Lift some pointers from the struct to local register variables */ + CACHE_SOURCE(0); +/* We parse the string in segments */ +/* We read chars until we find a $, then we append what we have read so far */ +/* (since last $ processing) to the destination. When we find a $ we call */ +/* expand_macro() and let it expand that particular $ reference into dest */ + block_start = source_p; + quote_seen = 0; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case backslash_char: + /* Quote $ in macro value */ + if (!cmd) { + quote_seen = ~quote_seen; + } + continue; + case dollar_char: + /* Save the plain string we found since */ + /* start of string or previous $ */ + if (quote_seen) { + append_string(block_start, + destination, + source_p - block_start - 1); + block_start = source_p; + break; + } + append_string(block_start, + destination, + source_p - block_start); + source->string.text.p = ++source_p; + UNCACHE_SOURCE(); + /* Go expand the macro reference */ + expand_macro(source, destination, sourceb.string.buffer.start, cmd); + CACHE_SOURCE(1); + block_start = source_p + 1; + break; + case nul_char: + /* The string ran out. Get some more */ + append_string(block_start, + destination, + source_p - block_start); + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + destination->text.end = destination->text.p; + value->being_expanded = false; + return; + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_value()"); + } + block_start = source_p; + source_p--; + continue; + } + quote_seen = 0; + } + retmem(sourceb.string.buffer.start); +} + +/* + * expand_macro(source, destination, current_string, cmd) + * + * Should be called with source->string.text.p pointing to + * the first char after the $ that starts a macro reference. + * source->string.text.p is returned pointing to the first char after + * the macro name. + * It will read the macro name, expanding any macros in it, + * and get the value. The value is then expanded. + * destination is a String that is filled in with the expanded macro. + * It may be passed in referencing a buffer to expand the macro into. + * Note that most expansions are done on demand, e.g. right + * before the command is executed and not while the file is + * being parsed. + * + * Parameters: + * source The source block that references the string + * to expand + * destination Where to put the result + * current_string The string we are expanding, for error msg + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + * funny Vector of semantic tags for characters + * is_conditional Set if a conditional macro is refd + * make_word_mentioned Set if the word "MAKE" is mentioned + * makefile_type We deliver extra msg when reading makefiles + * query The Name "?", compared against + * query_mentioned Set if the word "?" is mentioned + */ +void +expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd) +{ + static Name make = (Name)NULL; + static wchar_t colon_sh[4]; + static wchar_t colon_shell[7]; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register wchar_t *source_p = source->string.text.p; + register wchar_t *source_end = source->string.text.end; + register int closer = 0; + wchar_t *block_start = (wchar_t *)NULL; + int quote_seen = 0; + register int closer_level = 1; + Name name = (Name)NULL; + wchar_t *colon = (wchar_t *)NULL; + wchar_t *percent = (wchar_t *)NULL; + wchar_t *eq = (wchar_t *) NULL; + Property macro = NULL; + wchar_t *p = (wchar_t*)NULL; + String_rec extracted; + wchar_t extracted_string[MAXPATHLEN]; + wchar_t *left_head = NULL; + wchar_t *left_tail = NULL; + wchar_t *right_tail = NULL; + int left_head_len = 0; + int left_tail_len = 0; + int tmp_len = 0; + wchar_t *right_hand[128]; + int i = 0; + enum { + no_extract, + dir_extract, + file_extract + } extraction = no_extract; + enum { + no_replace, + suffix_replace, + pattern_replace, + sh_replace + } replacement = no_replace; + + if (make == NULL) { + MBSTOWCS(wcs_buffer, "MAKE"); + make = GETNAME(wcs_buffer, FIND_LENGTH); + + MBSTOWCS(colon_sh, ":sh"); + MBSTOWCS(colon_shell, ":shell"); + } + + right_hand[0] = NULL; + + /* First copy the (macro-expanded) macro name into string. */ + INIT_STRING_FROM_STACK(string, buffer); +recheck_first_char: + /* Check the first char of the macro name to figure out what to do. */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + WCSTOMBS(mbs_buffer, current_string); + fatal_reader_mksh(gettext("'$' at end of string `%s'"), + mbs_buffer); + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); + } + goto recheck_first_char; + case parenleft_char: + /* Multi char name. */ + closer = (int) parenright_char; + break; + case braceleft_char: + /* Multi char name. */ + closer = (int) braceright_char; + break; + case newline_char: + fatal_reader_mksh(gettext("'$' at end of line")); + default: + /* Single char macro name. Just suck it up */ + append_char(*source_p, &string); + source->string.text.p = source_p + 1; + goto get_macro_value; + } + + /* Handle multi-char macro names */ + block_start = ++source_p; + quote_seen = 0; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + append_string(block_start, + &string, + source_p - block_start); + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + if (current_string != NULL) { + WCSTOMBS(mbs_buffer, current_string); + fatal_reader_mksh(gettext("Unmatched `%c' in string `%s'"), + closer == + (int) braceright_char ? + (int) braceleft_char : + (int) parenleft_char, + mbs_buffer); + } else { + fatal_reader_mksh(gettext("Premature EOF")); + } + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); + } + block_start = source_p; + source_p--; + continue; + case newline_char: + fatal_reader_mksh(gettext("Unmatched `%c' on line"), + closer == (int) braceright_char ? + (int) braceleft_char : + (int) parenleft_char); + case backslash_char: + /* Quote dollar in macro value. */ + if (!cmd) { + quote_seen = ~quote_seen; + } + continue; + case dollar_char: + /* + * Macro names may reference macros. + * This expands the value of such macros into the + * macro name string. + */ + if (quote_seen) { + append_string(block_start, + &string, + source_p - block_start - 1); + block_start = source_p; + break; + } + append_string(block_start, + &string, + source_p - block_start); + source->string.text.p = ++source_p; + UNCACHE_SOURCE(); + expand_macro(source, &string, current_string, cmd); + CACHE_SOURCE(0); + block_start = source_p; + source_p--; + break; + case parenleft_char: + /* Allow nested pairs of () in the macro name. */ + if (closer == (int) parenright_char) { + closer_level++; + } + break; + case braceleft_char: + /* Allow nested pairs of {} in the macro name. */ + if (closer == (int) braceright_char) { + closer_level++; + } + break; + case parenright_char: + case braceright_char: + /* + * End of the name. Save the string in the macro + * name string. + */ + if ((*source_p == closer) && (--closer_level <= 0)) { + source->string.text.p = source_p + 1; + append_string(block_start, + &string, + source_p - block_start); + goto get_macro_value; + } + break; + } + quote_seen = 0; + } + /* + * We got the macro name. We now inspect it to see if it + * specifies any translations of the value. + */ +get_macro_value: + name = NULL; + /* First check if we have a $(@D) type translation. */ + if ((get_char_semantics_value(string.buffer.start[0]) & + (int) special_macro_sem) && + (string.text.p - string.buffer.start >= 2) && + ((string.buffer.start[1] == 'D') || + (string.buffer.start[1] == 'F'))) { + switch (string.buffer.start[1]) { + case 'D': + extraction = dir_extract; + break; + case 'F': + extraction = file_extract; + break; + default: + WCSTOMBS(mbs_buffer, string.buffer.start); + fatal_reader_mksh(gettext("Illegal macro reference `%s'"), + mbs_buffer); + } + /* Internalize the macro name using the first char only. */ + name = GETNAME(string.buffer.start, 1); + (void) wcscpy(string.buffer.start, string.buffer.start + 2); + } + /* Check for other kinds of translations. */ + if ((colon = (wchar_t *) wcschr(string.buffer.start, + (int) colon_char)) != NULL) { + /* + * We have a $(FOO:.c=.o) type translation. + * Get the name of the macro proper. + */ + if (name == NULL) { + name = GETNAME(string.buffer.start, + colon - string.buffer.start); + } + /* Pickup all the translations. */ + if (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell)) { + replacement = sh_replace; + } else if ((svr4) || + ((percent = (wchar_t *) wcschr(colon + 1, + (int) percent_char)) == NULL)) { + while (colon != NULL) { + if ((eq = (wchar_t *) wcschr(colon + 1, + (int) equal_char)) == NULL) { + fatal_reader_mksh(gettext("= missing from replacement macro reference")); + } + left_tail_len = eq - colon - 1; + if(left_tail) { + retmem(left_tail); + } + left_tail = ALLOC_WC(left_tail_len + 1); + (void) wcsncpy(left_tail, + colon + 1, + eq - colon - 1); + left_tail[eq - colon - 1] = (int) nul_char; + replacement = suffix_replace; + if ((colon = (wchar_t *) wcschr(eq + 1, + (int) colon_char)) != NULL) { + tmp_len = colon - eq; + if(right_tail) { + retmem(right_tail); + } + right_tail = ALLOC_WC(tmp_len); + (void) wcsncpy(right_tail, + eq + 1, + colon - eq - 1); + right_tail[colon - eq - 1] = + (int) nul_char; + } else { + if(right_tail) { + retmem(right_tail); + } + right_tail = ALLOC_WC(wcslen(eq) + 1); + (void) wcscpy(right_tail, eq + 1); + } + } + } else { + if ((eq = (wchar_t *) wcschr(colon + 1, + (int) equal_char)) == NULL) { + fatal_reader_mksh(gettext("= missing from replacement macro reference")); + } + if ((percent = (wchar_t *) wcschr(colon + 1, + (int) percent_char)) == NULL) { + fatal_reader_mksh(gettext("%% missing from replacement macro reference")); + } + if (eq < percent) { + fatal_reader_mksh(gettext("%% missing from replacement macro reference")); + } + + if (percent > (colon + 1)) { + tmp_len = percent - colon; + if(left_head) { + retmem(left_head); + } + left_head = ALLOC_WC(tmp_len); + (void) wcsncpy(left_head, + colon + 1, + percent - colon - 1); + left_head[percent-colon-1] = (int) nul_char; + left_head_len = percent-colon-1; + } else { + left_head = NULL; + left_head_len = 0; + } + + if (eq > percent+1) { + tmp_len = eq - percent; + if(left_tail) { + retmem(left_tail); + } + left_tail = ALLOC_WC(tmp_len); + (void) wcsncpy(left_tail, + percent + 1, + eq - percent - 1); + left_tail[eq-percent-1] = (int) nul_char; + left_tail_len = eq-percent-1; + } else { + left_tail = NULL; + left_tail_len = 0; + } + + if ((percent = (wchar_t *) wcschr(++eq, + (int) percent_char)) == NULL) { + + right_hand[0] = ALLOC_WC(wcslen(eq) + 1); + right_hand[1] = NULL; + (void) wcscpy(right_hand[0], eq); + } else { + i = 0; + do { + right_hand[i] = ALLOC_WC(percent-eq+1); + (void) wcsncpy(right_hand[i], + eq, + percent - eq); + right_hand[i][percent-eq] = + (int) nul_char; + if (i++ >= VSIZEOF(right_hand)) { + fatal_mksh(gettext("Too many %% in pattern")); + } + eq = percent + 1; + if (eq[0] == (int) nul_char) { + MBSTOWCS(wcs_buffer, ""); + right_hand[i] = (wchar_t *) wcsdup(wcs_buffer); + i++; + break; + } + } while ((percent = (wchar_t *) wcschr(eq, (int) percent_char)) != NULL); + if (eq[0] != (int) nul_char) { + right_hand[i] = ALLOC_WC(wcslen(eq) + 1); + (void) wcscpy(right_hand[i], eq); + i++; + } + right_hand[i] = NULL; + } + replacement = pattern_replace; + } + } + if (name == NULL) { + /* + * No translations found. + * Use the whole string as the macro name. + */ + name = GETNAME(string.buffer.start, + string.text.p - string.buffer.start); + } + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (name == make) { + make_word_mentioned = true; + } + if (name == query) { + query_mentioned = true; + } + if ((name == host_arch) || (name == target_arch)) { + if (!init_arch_done) { + init_arch_done = true; + init_arch_macros(); + } + } + if ((name == host_mach) || (name == target_mach)) { + if (!init_mach_done) { + init_mach_done = true; + init_mach_macros(); + } + } + /* Get the macro value. */ + macro = get_prop(name->prop, macro_prop); + if ((macro != NULL) && macro->body.macro.is_conditional) { + conditional_macro_used = true; + /* + * Add this conditional macro to the beginning of the + * global list. + */ + add_macro_to_global_list(name); + if (makefile_type == reading_makefile) { + warning_mksh(gettext("Conditional macro `%s' referenced in file `%ws', line %d"), + name->string_mb, file_being_read, line_number); + } + } + /* Macro name read and parsed. Expand the value. */ + if ((macro == NULL) || (macro->body.macro.value == NULL)) { + /* If the value is empty, we just get out of here. */ + goto exit; + } + if (replacement == sh_replace) { + /* If we should do a :sh transform, we expand the command + * and process it. + */ + INIT_STRING_FROM_STACK(string, buffer); + /* Expand the value into a local string buffer and run cmd. */ + expand_value_with_daemon(name, macro, &string, cmd); + sh_command2string(&string, destination); + } else if ((replacement != no_replace) || (extraction != no_extract)) { + /* + * If there were any transforms specified in the macro + * name, we deal with them here. + */ + INIT_STRING_FROM_STACK(string, buffer); + /* Expand the value into a local string buffer. */ + expand_value_with_daemon(name, macro, &string, cmd); + /* Scan the expanded string. */ + p = string.buffer.start; + while (*p != (int) nul_char) { + wchar_t chr; + + /* + * First skip over any white space and append + * that to the destination string. + */ + block_start = p; + while ((*p != (int) nul_char) && iswspace(*p)) { + p++; + } + append_string(block_start, + destination, + p - block_start); + /* Then find the end of the next word. */ + block_start = p; + while ((*p != (int) nul_char) && !iswspace(*p)) { + p++; + } + /* If we cant find another word we are done */ + if (block_start == p) { + break; + } + /* Then apply the transforms to the word */ + INIT_STRING_FROM_STACK(extracted, extracted_string); + switch (extraction) { + case dir_extract: + /* + * $(@D) type transform. Extract the + * path from the word. Deliver "." if + * none is found. + */ + if (p != NULL) { + chr = *p; + *p = (int) nul_char; + } + eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); + if (p != NULL) { + *p = chr; + } + if ((eq == NULL) || (eq > p)) { + MBSTOWCS(wcs_buffer, "."); + append_string(wcs_buffer, &extracted, 1); + } else { + append_string(block_start, + &extracted, + eq - block_start); + } + break; + case file_extract: + /* + * $(@F) type transform. Remove the path + * from the word if any. + */ + if (p != NULL) { + chr = *p; + *p = (int) nul_char; + } + eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); + if (p != NULL) { + *p = chr; + } + if ((eq == NULL) || (eq > p)) { + append_string(block_start, + &extracted, + p - block_start); + } else { + append_string(eq + 1, + &extracted, + p - eq - 1); + } + break; + case no_extract: + append_string(block_start, + &extracted, + p - block_start); + break; + } + switch (replacement) { + case suffix_replace: + /* + * $(FOO:.o=.c) type transform. + * Maybe replace the tail of the word. + */ + if (((extracted.text.p - + extracted.buffer.start) >= + left_tail_len) && + IS_WEQUALN(extracted.text.p - left_tail_len, + left_tail, + left_tail_len)) { + append_string(extracted.buffer.start, + destination, + (extracted.text.p - + extracted.buffer.start) + - left_tail_len); + append_string(right_tail, + destination, + FIND_LENGTH); + } else { + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + } + break; + case pattern_replace: + /* $(X:a%b=c%d) type transform. */ + if (((extracted.text.p - + extracted.buffer.start) >= + left_head_len+left_tail_len) && + IS_WEQUALN(left_head, + extracted.buffer.start, + left_head_len) && + IS_WEQUALN(left_tail, + extracted.text.p - left_tail_len, + left_tail_len)) { + i = 0; + while (right_hand[i] != NULL) { + append_string(right_hand[i], + destination, + FIND_LENGTH); + i++; + if (right_hand[i] != NULL) { + append_string(extracted.buffer. + start + + left_head_len, + destination, + (extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len); + } + } + } else { + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + } + break; + case no_replace: + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + break; + case sh_replace: + break; + } + } + if (string.free_after_use) { + retmem(string.buffer.start); + } + } else { + /* + * This is for the case when the macro name did not + * specify transforms. + */ + if (!strncmp(name->string_mb, "GET", 3)) { + dollarget_seen = true; + } + dollarless_flag = false; + if (!strncmp(name->string_mb, "<", 1) && + dollarget_seen) { + dollarless_flag = true; + dollarget_seen = false; + } + expand_value_with_daemon(name, macro, destination, cmd); + } +exit: + if(left_tail) { + retmem(left_tail); + } + if(right_tail) { + retmem(right_tail); + } + if(left_head) { + retmem(left_head); + } + i = 0; + while (right_hand[i] != NULL) { + retmem(right_hand[i]); + i++; + } + *destination->text.p = (int) nul_char; + destination->text.end = destination->text.p; +} + +static void +add_macro_to_global_list(Name macro_to_add) +{ + Macro_list new_macro; + Macro_list macro_on_list; + char *name_on_list = (char*)NULL; + char *name_to_add = macro_to_add->string_mb; + char *value_on_list = (char*)NULL; + const char *value_to_add = (char*)NULL; + + if (macro_to_add->prop->body.macro.value != NULL) { + value_to_add = macro_to_add->prop->body.macro.value->string_mb; + } else { + value_to_add = ""; + } + + /* + * Check if this macro is already on list, if so, do nothing + */ + for (macro_on_list = cond_macro_list; + macro_on_list != NULL; + macro_on_list = macro_on_list->next) { + + name_on_list = macro_on_list->macro_name; + value_on_list = macro_on_list->value; + + if (IS_EQUAL(name_on_list, name_to_add)) { + if (IS_EQUAL(value_on_list, value_to_add)) { + return; + } + } + } + new_macro = (Macro_list) malloc(sizeof(Macro_list_rec)); + new_macro->macro_name = strdup(name_to_add); + new_macro->value = strdup(value_to_add); + new_macro->next = cond_macro_list; + cond_macro_list = new_macro; +} + +/* + * init_arch_macros(void) + * + * Set the magic macros TARGET_ARCH, HOST_ARCH, + * + * Parameters: + * + * Global variables used: + * host_arch Property for magic macro HOST_ARCH + * target_arch Property for magic macro TARGET_ARCH + * + * Return value: + * The function does not return a value, but can + * call fatal() in case of error. + */ +static void +init_arch_macros(void) +{ + String_rec result_string; + wchar_t wc_buf[STRING_BUFFER_LENGTH]; + char mb_buf[STRING_BUFFER_LENGTH]; + FILE *pipe; + Name value; + int set_host, set_target; + const char *mach_command = "/bin/mach"; + + set_host = (get_prop(host_arch->prop, macro_prop) == NULL); + set_target = (get_prop(target_arch->prop, macro_prop) == NULL); + + if (set_host || set_target) { + INIT_STRING_FROM_STACK(result_string, wc_buf); + append_char((int) hyphen_char, &result_string); + + if ((pipe = popen(mach_command, "r")) == NULL) { + fatal_mksh(gettext("Execute of %s failed"), mach_command); + } + while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { + MBSTOWCS(wcs_buffer, mb_buf); + append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); + } + if (pclose(pipe) != 0) { + fatal_mksh(gettext("Execute of %s failed"), mach_command); + } + + value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); + + if (set_host) { + (void) setvar_daemon(host_arch, value, false, no_daemon, true, 0); + } + if (set_target) { + (void) setvar_daemon(target_arch, value, false, no_daemon, true, 0); + } + } +} + +/* + * init_mach_macros(void) + * + * Set the magic macros TARGET_MACH, HOST_MACH, + * + * Parameters: + * + * Global variables used: + * host_mach Property for magic macro HOST_MACH + * target_mach Property for magic macro TARGET_MACH + * + * Return value: + * The function does not return a value, but can + * call fatal() in case of error. + */ +static void +init_mach_macros(void) +{ + String_rec result_string; + wchar_t wc_buf[STRING_BUFFER_LENGTH]; + char mb_buf[STRING_BUFFER_LENGTH]; + FILE *pipe; + Name value; + int set_host, set_target; + const char *arch_command = "/bin/arch"; + + set_host = (get_prop(host_mach->prop, macro_prop) == NULL); + set_target = (get_prop(target_mach->prop, macro_prop) == NULL); + + if (set_host || set_target) { + INIT_STRING_FROM_STACK(result_string, wc_buf); + append_char((int) hyphen_char, &result_string); + + if ((pipe = popen(arch_command, "r")) == NULL) { + fatal_mksh(gettext("Execute of %s failed"), arch_command); + } + while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { + MBSTOWCS(wcs_buffer, mb_buf); + append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); + } + if (pclose(pipe) != 0) { + fatal_mksh(gettext("Execute of %s failed"), arch_command); + } + + value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); + + if (set_host) { + (void) setvar_daemon(host_mach, value, false, no_daemon, true, 0); + } + if (set_target) { + (void) setvar_daemon(target_mach, value, false, no_daemon, true, 0); + } + } +} + +/* + * expand_value_with_daemon(name, macro, destination, cmd) + * + * Checks for daemons and then maybe calls expand_value(). + * + * Parameters: + * name Name of the macro (Added by the NSE) + * macro The property block with the value to expand + * destination Where the result should be deposited + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + */ +static void +expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd) +{ + register Chain chain; + + + switch (macro->body.macro.daemon) { + case no_daemon: + if (!svr4 && !posix) { + expand_value(macro->body.macro.value, destination, cmd); + } else { + if (dollarless_flag && tilde_rule) { + expand_value(dollarless_value, destination, cmd); + dollarless_flag = false; + tilde_rule = false; + } else { + expand_value(macro->body.macro.value, destination, cmd); + } + } + return; + case chain_daemon: + /* If this is a $? value we call the daemon to translate the */ + /* list of names to a string */ + for (chain = (Chain) macro->body.macro.value; + chain != NULL; + chain = chain->next) { + APPEND_NAME(chain->name, + destination, + (int) chain->name->hash.length); + if (chain->next != NULL) { + append_char((int) space_char, destination); + } + } + return; + } +} + +/* + * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. + */ +char *sunpro_dependencies_buf = NULL; +char *sunpro_dependencies_oldbuf = NULL; +int sunpro_dependencies_buf_size = 0; + +/* + * setvar_daemon(name, value, append, daemon, strip_trailing_spaces) + * + * Set a macro value, possibly supplying a daemon to be used + * when referencing the value. + * + * Return value: + * The property block with the new value + * + * Parameters: + * name Name of the macro to set + * value The value to set + * append Should we reset or append to the current value? + * daemon Special treatment when reading the value + * strip_trailing_spaces from the end of value->string + * debug_level Indicates how much tracing we should do + * + * Global variables used: + * makefile_type Used to check if we should enforce read only + * path_name The Name "PATH", compared against + * virtual_root The Name "VIRTUAL_ROOT", compared against + * vpath_defined Set if the macro VPATH is set + * vpath_name The Name "VPATH", compared against + * envvar A list of environment vars with $ in value + */ +Property +setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level) +{ + register Property macro = maybe_append_prop(name, macro_prop); + register Property macro_apx = get_prop(name->prop, macro_append_prop); + int length = 0; + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Chain chain; + Name val; + wchar_t *val_string = (wchar_t*)NULL; + Wstring wcb; + + + if ((makefile_type != reading_nothing) && + macro->body.macro.read_only) { + return macro; + } + /* Strip spaces from the end of the value */ + if (daemon == no_daemon) { + if(value != NULL) { + wcb.init(value); + length = wcb.length(); + val_string = wcb.get_string(); + } + if ((length > 0) && iswspace(val_string[length-1])) { + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + append_string(val_string, &destination, length); + if (strip_trailing_spaces) { + while ((length > 0) && + iswspace(destination.buffer.start[length-1])) { + destination.buffer.start[--length] = 0; + } + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + } + } + + if(macro_apx != NULL) { + val = macro_apx->body.macro_appendix.value; + } else { + val = macro->body.macro.value; + } + + if (append) { + /* + * If we are appending, we just tack the new value after + * the old one with a space in between. + */ + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if ((macro != NULL) && (val != NULL)) { + APPEND_NAME(val, + &destination, + (int) val->hash.length); + if (value != NULL) { + wcb.init(value); + if(wcb.length() > 0) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + } + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + wcb.init(value); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } + + /* Debugging trace */ + if (debug_level > 1) { + if (value != NULL) { + switch (daemon) { + case chain_daemon: + (void) printf("%s =", name->string_mb); + for (chain = (Chain) value; + chain != NULL; + chain = chain->next) { + (void) printf(" %s", chain->name->string_mb); + } + (void) printf("\n"); + break; + case no_daemon: + (void) printf("%s= %s\n", + name->string_mb, + value->string_mb); + break; + } + } else { + (void) printf("%s =\n", name->string_mb); + } + } + /* Set the new values in the macro property block */ +/**/ + if(macro_apx != NULL) { + macro_apx->body.macro_appendix.value = value; + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + if (macro_apx->body.macro_appendix.value_to_append != NULL) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + if (macro_apx->body.macro_appendix.value_to_append != NULL) { + APPEND_NAME(macro_apx->body.macro_appendix.value_to_append, + &destination, + (int) macro_apx->body.macro_appendix.value_to_append->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } +/**/ + macro->body.macro.value = value; + macro->body.macro.daemon = daemon; + /* + * If the user changes the VIRTUAL_ROOT, we need to flush + * the vroot package cache. + */ + if (name == path_name) { + flush_path_cache(); + } + if (name == virtual_root) { + flush_vroot_cache(); + } + /* If this sets the VPATH we remember that */ + if ((name == vpath_name) && + (value != NULL) && + (value->hash.length > 0)) { + vpath_defined = true; + } + /* + * For environment variables we also set the + * environment value each time. + */ + if (macro->body.macro.exported) { + static char *env; + + if (!reading_environment && (value != NULL)) { + Envvar p; + + for (p = envvar; p != NULL; p = p->next) { + if (p->name == name) { + p->value = value; + p->already_put = false; + goto found_it; + } + } + p = ALLOC(Envvar); + p->name = name; + p->value = value; + p->next = envvar; + p->env_string = NULL; + p->already_put = false; + envvar = p; +found_it:; + } if (reading_environment || (value == NULL) || !value->dollar) { + length = 2 + strlen(name->string_mb); + if (value != NULL) { + length += strlen(value->string_mb); + } + Property env_prop = maybe_append_prop(name, env_mem_prop); + /* + * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. + */ + if (!strncmp(name->string_mb, "SUNPRO_DEPENDENCIES", 19)) { + if (length >= sunpro_dependencies_buf_size) { + sunpro_dependencies_buf_size=length*2; + if (sunpro_dependencies_buf_size < 4096) + sunpro_dependencies_buf_size = 4096; // Default minimum size + if (sunpro_dependencies_buf) + sunpro_dependencies_oldbuf = sunpro_dependencies_buf; + sunpro_dependencies_buf=getmem(sunpro_dependencies_buf_size); + } + env = sunpro_dependencies_buf; + } else { + env = getmem(length); + } + env_alloc_num++; + env_alloc_bytes += length; + (void) sprintf(env, + "%s=%s", + name->string_mb, + value == NULL ? + "" : value->string_mb); + (void) putenv(env); + env_prop->body.env_mem.value = env; + if (sunpro_dependencies_oldbuf) { + /* Return old buffer */ + retmem_mb(sunpro_dependencies_oldbuf); + sunpro_dependencies_oldbuf = NULL; + } + } + } + if (name == target_arch) { + Name ha = getvar(host_arch); + Name ta = getvar(target_arch); + Name vr = getvar(virtual_root); + int length; + wchar_t *new_value; + wchar_t *old_vr; + Boolean new_value_allocated = false; + + Wstring ha_str(ha); + Wstring ta_str(ta); + Wstring vr_str(vr); + + wchar_t * wcb_ha = ha_str.get_string(); + wchar_t * wcb_ta = ta_str.get_string(); + wchar_t * wcb_vr = vr_str.get_string(); + + length = 32 + + wcslen(wcb_ha) + + wcslen(wcb_ta) + + wcslen(wcb_vr); + old_vr = wcb_vr; + MBSTOWCS(wcs_buffer, "/usr/arch/"); + if (IS_WEQUALN(old_vr, + wcs_buffer, + wcslen(wcs_buffer))) { + old_vr = (wchar_t *) wcschr(old_vr, (int) colon_char) + 1; + } + if ( (ha == ta) || (wcslen(wcb_ta) == 0) ) { + new_value = old_vr; + } else { + new_value = ALLOC_WC(length); + new_value_allocated = true; + WCSTOMBS(mbs_buffer, old_vr); + (void) swprintf(new_value, length * SIZEOFWCHAR_T, + L"/usr/arch/%s/%s:%s", + ha->string_mb + 1, + ta->string_mb + 1, + mbs_buffer); + } + if (new_value[0] != 0) { + (void) setvar_daemon(virtual_root, + GETNAME(new_value, FIND_LENGTH), + false, + no_daemon, + true, + debug_level); + } + if (new_value_allocated) { + retmem(new_value); + } + } + return macro; +} diff --git a/lib/mksh/misc.cc b/lib/mksh/misc.cc new file mode 100644 index 0000000..4765102 --- /dev/null +++ b/lib/mksh/misc.cc @@ -0,0 +1,1113 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * misc.cc + * + * This file contains various unclassified routines. Some main groups: + * getname + * Memory allocation + * String handling + * Property handling + * Error message handling + * Make internal state dumping + * main routine support + */ + +/* + * Included files + */ +#include /* bsd_signal() */ +#include /* get_char_semantics_value() */ +#include +#include /* va_list, va_start(), va_end() */ +#include /* mbstowcs() */ +#include /* SIG_DFL */ +#include /* wait() */ + +#include /* strerror() */ +#include + + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +extern "C" { + void (*sigivalue)(int) = SIG_DFL; + void (*sigqvalue)(int) = SIG_DFL; + void (*sigtvalue)(int) = SIG_DFL; + void (*sighvalue)(int) = SIG_DFL; +} + +long getname_bytes_count = 0; +long getname_names_count = 0; +long getname_struct_count = 0; + +long freename_bytes_count = 0; +long freename_names_count = 0; +long freename_struct_count = 0; + +long expandstring_count = 0; +long getwstring_count = 0; + +/* + * File table of contents + */ +static void expand_string(register String string, register int length); + +#define FATAL_ERROR_MSG_SIZE 200 + +/* + * getmem(size) + * + * malloc() version that checks the returned value. + * + * Return value: + * The memory chunk we allocated + * + * Parameters: + * size The size of the chunk we need + * + * Global variables used: + */ +char * +getmem(register int size) +{ + register char *result = (char *) malloc((unsigned) size); + if (result == NULL) { + char buf[FATAL_ERROR_MSG_SIZE]; + sprintf(buf, "*** Error: malloc(%d) failed: %s\n", size, strerror(errno)); + strcat(buf, gettext("mksh: Fatal error: Out of memory\n")); + fputs(buf, stderr); + exit_status = 1; + exit(1); + } + return result; +} + +/* + * retmem(p) + * + * Cover funtion for free() to make it possible to insert advises. + * + * Parameters: + * p The memory block to free + * + * Global variables used: + */ +void +retmem(wchar_t *p) +{ + (void) free((char *) p); +} + +void +retmem_mb(caddr_t p) +{ + (void) free(p); +} + +/* + * getname_fn(name, len, dont_enter) + * + * Hash a name string to the corresponding nameblock. + * + * Return value: + * The Name block for the string + * + * Parameters: + * name The string we want to internalize + * len The length of that string + * dont_enter Don't enter the name if it does not exist + * + * Global variables used: + * funny The vector of semantic tags for characters + * hashtab The hashtable used for the nametable + */ +Name +getname_fn(wchar_t *name, register int len, register Boolean dont_enter, register Boolean * foundp) +{ + register int length; + register wchar_t *cap = name; + register Name np; + static Name_rec empty_Name; + char *tmp_mbs_buffer = NULL; + char *mbs_name = mbs_buffer; + + /* + * First figure out how long the string is. + * If the len argument is -1 we count the chars here. + */ + if (len == FIND_LENGTH) { + length = wcslen(name); + } else { + length = len; + } + + Wstring ws; + ws.init(name, length); + if (length >= MAXPATHLEN) { + mbs_name = tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + } + (void) wcstombs(mbs_name, ws.get_string(), (length * MB_LEN_MAX) + 1); + + /* Look for the string */ + if (dont_enter || (foundp != 0)) { + np = hashtab.lookup(mbs_name); + if (foundp != 0) { + *foundp = (np != 0) ? true : false; + } + if ((np != 0) || dont_enter) { + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + } + return np; + } else { + np = ALLOC(Name); + } + } else { + Boolean found; + np = hashtab.insert(mbs_name, found); + if (found) { + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + } + return np; + } + } + getname_struct_count += sizeof(struct _Name); + *np = empty_Name; + + np->string_mb = strdup(mbs_name); + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + mbs_name = tmp_mbs_buffer = NULL; + } + getname_bytes_count += strlen(np->string_mb) + 1; + /* Fill in the new Name */ + np->stat.time = file_no_time; + np->hash.length = length; + /* Scan the namestring to classify it */ + for (cap = name, len = 0; --length >= 0;) { + len |= get_char_semantics_value(*cap++); + } + np->dollar = BOOLEAN((len & (int) dollar_sem) != 0); + np->meta = BOOLEAN((len & (int) meta_sem) != 0); + np->percent = BOOLEAN((len & (int) percent_sem) != 0); + np->wildcard = BOOLEAN((len & (int) wildcard_sem) != 0); + np->colon = BOOLEAN((len & (int) colon_sem) != 0); + np->parenleft = BOOLEAN((len & (int) parenleft_sem) != 0); + getname_names_count++; + return np; +} + +void +store_name(Name name) +{ + hashtab.insert(name); +} + +void +free_name(Name name) +{ + freename_names_count++; + freename_struct_count += sizeof(struct _Name); + freename_bytes_count += strlen(name->string_mb) + 1; + retmem_mb(name->string_mb); + for (Property next, p = name->prop; p != NULL; p = next) { + next = p->next; + free(p); + } + free(name); +} + +/* + * enable_interrupt(handler) + * + * This routine sets a new interrupt handler for the signals make + * wants to deal with. + * + * Parameters: + * handler The function installed as interrupt handler + * + * Static variables used: + * sigivalue The original signal handler + * sigqvalue The original signal handler + * sigtvalue The original signal handler + * sighvalue The original signal handler + */ +void +enable_interrupt(register void (*handler) (int)) +{ + if (sigivalue != SIG_IGN) { + (void) bsd_signal(SIGINT, (SIG_PF) handler); + } + if (sigqvalue != SIG_IGN) { + (void) bsd_signal(SIGQUIT, (SIG_PF) handler); + } + if (sigtvalue != SIG_IGN) { + (void) bsd_signal(SIGTERM, (SIG_PF) handler); + } + if (sighvalue != SIG_IGN) { + (void) bsd_signal(SIGHUP, (SIG_PF) handler); + } +} + +/* + * setup_char_semantics() + * + * Load the vector char_semantics[] with lexical markers + * + * Parameters: + * + * Global variables used: + * char_semantics The vector of character semantics that we set + */ +void +setup_char_semantics(void) +{ + const char *s; + wchar_t wc_buffer[1]; + int entry; + + if (svr4) { + s = "@-"; + } else { + s = "=@-?!+"; + } + for (s; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) command_prefix_sem; + } + char_semantics[dollar_char_entry] |= (int) dollar_sem; + for (s = "#|=^();&<>*?[]:$`'\"\\\n"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) meta_sem; + } + char_semantics[percent_char_entry] |= (int) percent_sem; + for (s = "@*<%?^"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) special_macro_sem; + } + for (s = "?[*"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) wildcard_sem; + } + char_semantics[colon_char_entry] |= (int) colon_sem; + char_semantics[parenleft_char_entry] |= (int) parenleft_sem; +} + +/* + * errmsg(errnum) + * + * Return the error message for a system call error + * + * Return value: + * An error message string + * + * Parameters: + * errnum The number of the error we want to describe + * + * Global variables used: + * sys_errlist A vector of error messages + * sys_nerr The size of sys_errlist + */ +char * +errmsg(int errnum) +{ + + extern int sys_nerr; + char *errbuf; + + if ((errnum < 0) || (errnum > sys_nerr)) { + errbuf = getmem(6+1+11+1); + (void) sprintf(errbuf, gettext("Error %d"), errnum); + return errbuf; + } else { + return strerror(errnum); + + } +} + +static char static_buf[MAXPATHLEN*3]; + +/* + * fatal_mksh(format, args...) + * + * Print a message and die + * + * Parameters: + * format printf type format string + * args Arguments to match the format + */ +/*VARARGS*/ +void +fatal_mksh(const char *message, ...) +{ + va_list args; + char *buf = static_buf; + char *mksh_fat_err = gettext("mksh: Fatal error: "); + char *cur_wrk_dir = gettext("Current working directory: "); + int mksh_fat_err_len = strlen(mksh_fat_err); + + va_start(args, message); + (void) fflush(stdout); + (void) strcpy(buf, mksh_fat_err); + size_t buf_len = vsnprintf(static_buf + mksh_fat_err_len, + sizeof(static_buf) - mksh_fat_err_len, + message, args) + + mksh_fat_err_len + + strlen(cur_wrk_dir) + + strlen(get_current_path_mksh()) + + 3; // "\n\n" + va_end(args); + if (buf_len >= sizeof(static_buf)) { + buf = getmem(buf_len); + (void) strcpy(buf, mksh_fat_err); + va_start(args, message); + (void) vsprintf(buf + mksh_fat_err_len, message, args); + va_end(args); + } + (void) strcat(buf, "\n"); +/* + if (report_pwd) { + */ + if (1) { + (void) strcat(buf, cur_wrk_dir); + (void) strcat(buf, get_current_path_mksh()); + (void) strcat(buf, "\n"); + } + (void) fputs(buf, stderr); + (void) fflush(stderr); + if (buf != static_buf) { + retmem_mb(buf); + } + exit_status = 1; + exit(1); +} + +/* + * fatal_reader_mksh(format, args...) + * + * Parameters: + * format printf style format string + * args arguments to match the format + */ +/*VARARGS*/ +void +fatal_reader_mksh(const char * pattern, ...) +{ + va_list args; + char message[1000]; + + va_start(args, pattern); +/* + if (file_being_read != NULL) { + WCSTOMBS(mbs_buffer, file_being_read); + if (line_number != 0) { + (void) sprintf(message, + gettext("%s, line %d: %s"), + mbs_buffer, + line_number, + pattern); + } else { + (void) sprintf(message, + "%s: %s", + mbs_buffer, + pattern); + } + pattern = message; + } + */ + + (void) fflush(stdout); + (void) fprintf(stderr, gettext("mksh: Fatal error in reader: ")); + (void) vfprintf(stderr, pattern, args); + (void) fprintf(stderr, "\n"); + va_end(args); + +/* + if (temp_file_name != NULL) { + (void) fprintf(stderr, + gettext("mksh: Temp-file %s not removed\n"), + temp_file_name->string_mb); + temp_file_name = NULL; + } + */ + +/* + if (report_pwd) { + */ + if (1) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path_mksh()); + } + (void) fflush(stderr); + exit_status = 1; + exit(1); +} + +/* + * warning_mksh(format, args...) + * + * Print a message and continue. + * + * Parameters: + * format printf type format string + * args Arguments to match the format + */ +/*VARARGS*/ +void +warning_mksh(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("mksh: Warning: ")); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); +/* + if (report_pwd) { + */ + if (1) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path_mksh()); + } + (void) fflush(stderr); +} + +/* + * get_current_path_mksh() + * + * Stuff current_path with the current path if it isnt there already. + * + * Parameters: + * + * Global variables used: + */ +char * +get_current_path_mksh(void) +{ + char pwd[(MAXPATHLEN * MB_LEN_MAX)]; + static char *current_path; + + if (current_path == NULL) { + getcwd(pwd, sizeof(pwd)); + if (pwd[0] == (int) nul_char) { + pwd[0] = (int) slash_char; + pwd[1] = (int) nul_char; + } + current_path = strdup(pwd); + } + return current_path; +} + +/* + * append_prop(target, type) + * + * Create a new property and append it to the property list of a Name. + * + * Return value: + * A new property block for the target + * + * Parameters: + * target The target that wants a new property + * type The type of property being requested + * + * Global variables used: + */ +Property +append_prop(register Name target, register Property_id type) +{ + register Property *insert = &target->prop; + register Property prop = *insert; + register int size; + + switch (type) { + case conditional_prop: + size = sizeof (struct Conditional); + break; + case line_prop: + size = sizeof (struct Line); + break; + case macro_prop: + size = sizeof (struct _Macro); + break; + case makefile_prop: + size = sizeof (struct Makefile); + break; + case member_prop: + size = sizeof (struct Member); + break; + case recursive_prop: + size = sizeof (struct Recursive); + break; + case sccs_prop: + size = sizeof (struct Sccs); + break; + case suffix_prop: + size = sizeof (struct Suffix); + break; + case target_prop: + size = sizeof (struct Target); + break; + case time_prop: + size = sizeof (struct STime); + break; + case vpath_alias_prop: + size = sizeof (struct Vpath_alias); + break; + case long_member_name_prop: + size = sizeof (struct Long_member_name); + break; + case macro_append_prop: + size = sizeof (struct _Macro_appendix); + break; + case env_mem_prop: + size = sizeof (struct _Env_mem); + break; + default: + fatal_mksh(gettext("Internal error. Unknown prop type %d"), type); + } + for (; prop != NULL; insert = &prop->next, prop = *insert); + size += PROPERTY_HEAD_SIZE; + *insert = prop = (Property) getmem(size); + memset((char *) prop, 0, size); + prop->type = type; + prop->next = NULL; + return prop; +} + +/* + * maybe_append_prop(target, type) + * + * Append a property to the Name if none of this type exists + * else return the one already there + * + * Return value: + * A property of the requested type for the target + * + * Parameters: + * target The target that wants a new property + * type The type of property being requested + * + * Global variables used: + */ +Property +maybe_append_prop(register Name target, register Property_id type) +{ + register Property prop; + + if ((prop = get_prop(target->prop, type)) != NULL) { + return prop; + } + return append_prop(target, type); +} + +/* + * get_prop(start, type) + * + * Scan the property list of a Name to find the next property + * of a given type. + * + * Return value: + * The first property of the type, if any left + * + * Parameters: + * start The first property block to check for type + * type The type of property block we need + * + * Global variables used: + */ +Property +get_prop(register Property start, register Property_id type) +{ + for (; start != NULL; start = start->next) { + if (start->type == type) { + return start; + } + } + return NULL; +} + +/* + * append_string(from, to, length) + * + * Append a C string to a make string expanding it if nessecary + * + * Parameters: + * from The source (C style) string + * to The destination (make style) string + * length The length of the from string + * + * Global variables used: + */ +void +append_string(register wchar_t *from, register String to, register int length) +{ + if (length == FIND_LENGTH) { + length = wcslen(from); + } + if (to->buffer.start == NULL) { + expand_string(to, 32 + length); + } + if (to->buffer.end - to->text.p <= length) { + expand_string(to, + (to->buffer.end - to->buffer.start) * 2 + + length); + } + if (length > 0) { + (void) wcsncpy(to->text.p, from, length); + to->text.p += length; + } + *(to->text.p) = (int) nul_char; +} + +wchar_t * get_wstring(char *from) { + if(from == NULL) { + return NULL; + } + getwstring_count++; + wchar_t * wcbuf = ALLOC_WC(strlen(from) + 1); + mbstowcs(wcbuf, from, strlen(from)+1); + return wcbuf; +} + +void +append_string(register char *from, register String to, register int length) +{ + if (length == FIND_LENGTH) { + length = strlen(from); + } + if (to->buffer.start == NULL) { + expand_string(to, 32 + length); + } + if (to->buffer.end - to->text.p <= length) { + expand_string(to, + (to->buffer.end - to->buffer.start) * 2 + + length); + } + if (length > 0) { + (void) mbstowcs(to->text.p, from, length); + to->text.p += length; + } + *(to->text.p) = (int) nul_char; +} + +/* + * expand_string(string, length) + * + * Allocate more memory for strings that run out of space. + * + * Parameters: + * string The make style string we want to expand + * length The new length we need + * + * Global variables used: + */ +static void +expand_string(register String string, register int length) +{ + register wchar_t *p; + + if (string->buffer.start == NULL) { + /* For strings that have no memory allocated */ + string->buffer.start = + string->text.p = + string->text.end = + ALLOC_WC(length); + string->buffer.end = string->buffer.start + length; + string->text.p[0] = (int) nul_char; + string->free_after_use = true; + expandstring_count++; + return; + } + if (string->buffer.end - string->buffer.start >= length) { + /* If we really don't need more memory. */ + return; + } + /* + * Get more memory, copy the string and free the old buffer if + * it is was malloc()'ed. + */ + expandstring_count++; + p = ALLOC_WC(length); + (void) wcscpy(p, string->buffer.start); + string->text.p = p + (string->text.p - string->buffer.start); + string->text.end = p + (string->text.end - string->buffer.start); + string->buffer.end = p + length; + if (string->free_after_use) { + retmem(string->buffer.start); + } + string->buffer.start = p; + string->free_after_use = true; +} + +/* + * append_char(from, to) + * + * Append one char to a make string expanding it if nessecary + * + * Parameters: + * from Single character to append to string + * to The destination (make style) string + * + * Global variables used: + */ +void +append_char(wchar_t from, register String to) +{ + if (to->buffer.start == NULL) { + expand_string(to, 32); + } + if (to->buffer.end - to->text.p <= 2) { + expand_string(to, to->buffer.end - to->buffer.start + 32); + } + *(to->text.p)++ = from; + *(to->text.p) = (int) nul_char; +} + +/* + * handle_interrupt_mksh() + * + * This is where C-C traps are caught. + */ +void +handle_interrupt_mksh(int) +{ + (void) fflush(stdout); + /* Make sure the processes running under us terminate first. */ + if (childPid > 0) { + kill(childPid, SIGTERM); + childPid = -1; + } + while (wait((int *) NULL) != -1); + exit_status = 2; + exit(2); +} + +/* + * setup_interrupt() + * + * This routine saves the original interrupt handler pointers + * + * Parameters: + * + * Static variables used: + * sigivalue The original signal handler + * sigqvalue The original signal handler + * sigtvalue The original signal handler + * sighvalue The original signal handler + */ +void +setup_interrupt(register void (*handler) (int)) +{ + sigivalue = bsd_signal(SIGINT, SIG_IGN); + sigqvalue = bsd_signal(SIGQUIT, SIG_IGN); + sigtvalue = bsd_signal(SIGTERM, SIG_IGN); + sighvalue = bsd_signal(SIGHUP, SIG_IGN); + enable_interrupt(handler); +} + + +void +mbstowcs_with_check(wchar_t *pwcs, const char *s, size_t n) +{ + if(mbstowcs(pwcs, s, n) == -1) { + fatal_mksh(gettext("The string `%s' is not valid in current locale"), s); + } +} + + + +Wstring::Wstring() +{ + INIT_STRING_FROM_STACK(string, string_buf); +} + +Wstring::Wstring(struct _Name * name) +{ + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name->string_mb, &string, name->hash.length); +} + +Wstring::~Wstring() +{ + if(string.free_after_use) { + retmem(string.buffer.start); + } +} + +void +Wstring::init(struct _Name * name) +{ + if(string.free_after_use) { + retmem(string.buffer.start); + } + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name->string_mb, &string, name->hash.length); +} + +void +Wstring::init(wchar_t * name, unsigned length) +{ + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name, &string, length); + string.buffer.start[length] = 0; +} + +Boolean +Wstring::equaln(wchar_t * str, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start, str, length); +} + +Boolean +Wstring::equaln(Wstring * str, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start, str->string.buffer.start, length); +} + +Boolean +Wstring::equal(wchar_t * str, unsigned off, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start + off, str, length); +} + +Boolean +Wstring::equal(wchar_t * str, unsigned off) +{ + return (Boolean)IS_WEQUAL(string.buffer.start + off, str); +} + +Boolean +Wstring::equal(wchar_t * str) +{ + return equal(str, 0); +} + +Boolean +Wstring::equal(Wstring * str, unsigned off, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start + off, str->string.buffer.start, length); +} + +Boolean +Wstring::equal(Wstring * str) +{ + return equal(str, 0); +} + +Boolean +Wstring::equal(Wstring * str, unsigned off) +{ + return (Boolean)IS_WEQUAL(string.buffer.start + off, str->string.buffer.start); +} + +void +Wstring::append_to_str(struct _String * str, unsigned off, unsigned length) +{ + append_string(string.buffer.start + off, str, length); +} + +Name +Name_set::lookup(const char *key) +{ + for (entry *node = root; node != 0;) { + int res = strcmp(key, node->name->string_mb); + if (res < 0) { + node = node->left; + } else if (res > 0) { + node = node->right; + } else { + return node->name; + } + } + return 0; +} + +Name +Name_set::insert(const char *key, Boolean &found) +{ + Name name = 0; + + if (root != 0) { + for (entry *node = root; name == 0;) { + int res = strcmp(key, node->name->string_mb); + if (res < 0) { + if (node->left != 0) { + node = node->left; + } else { + found = false; + name = ALLOC(Name); + + node->left = new entry(name, node); + rebalance(node); + } + } else if (res > 0) { + if (node->right != 0) { + node = node->right; + } else { + found = false; + name = ALLOC(Name); + + node->right = new entry(name, node); + rebalance(node); + } + } else { + found = true; + name = node->name; + } + } + } else { + found = false; + name = ALLOC(Name); + + root = new entry(name, 0); + } + return name; +} + +void +Name_set::insert(Name name) { + if (root != 0) { + for (entry *node = root;;) { + int res = strcmp(name->string_mb, node->name->string_mb); + if (res < 0) { + if (node->left != 0) { + node = node->left; + } else { + node->left = new entry(name, node); + rebalance(node); + break; + } + } else if (res > 0) { + if (node->right != 0) { + node = node->right; + } else { + node->right = new entry(name, node); + rebalance(node); + break; + } + } else { + // should be an error: inserting already existing name + break; + } + } + } else { + root = new entry(name, 0); + } +} + +void +Name_set::rebalance(Name_set::entry *node) { + for (; node != 0; node = node->parent) { + entry *right = node->right; + entry *left = node->left; + + unsigned rdepth = (right != 0) ? right->depth : 0; + unsigned ldepth = (left != 0) ? left->depth : 0; + + if (ldepth > rdepth + 1) { + if ((node->left = left->right) != 0) { + left->right->parent = node; + } + if ((left->parent = node->parent) != 0) { + if (node == node->parent->right) { + node->parent->right = left; + } else { + node->parent->left = left; + } + } else { + root = left; + } + left->right = node; + node->parent = left; + + node->setup_depth(); + node = left; + } else if (rdepth > ldepth + 1) { + if ((node->right = right->left) != 0) { + right->left->parent = node; + } + if ((right->parent = node->parent) != 0) { + if (node == node->parent->right) { + node->parent->right = right; + } else { + node->parent->left = right; + } + } else { + root = right; + } + right->left = node; + node->parent = right; + + node->setup_depth(); + node = right; + } + node->setup_depth(); + } +} + +Name_set::iterator +Name_set::begin() const { + for (entry *node = root; node != 0; node = node->left) { + if (node->left == 0) { + return iterator(node); + } + } + return iterator(); +} + +Name_set::iterator& +Name_set::iterator::operator++() { + if (node != 0) { + if (node->right != 0) { + node = node->right; + while (node->left != 0) { + node = node->left; + } + } else { + while ((node->parent != 0) && (node->parent->right == node)) { + node = node->parent; + } + node = node->parent; + } + } + return *this; +} diff --git a/lib/mksh/mksh.cc b/lib/mksh/mksh.cc new file mode 100644 index 0000000..9c0ec13 --- /dev/null +++ b/lib/mksh/mksh.cc @@ -0,0 +1,131 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * mksh.cc + * + * Execute the command(s) of one Make or DMake rule + */ + +/* + * Included files + */ +#include /* redirect_io() */ +#include /* retmem() */ +#include +#include +#include + + +/* + * Workaround for NFS bug. Sometimes, when running 'chdir' on a remote + * dmake server, it fails with "Stale NFS file handle" error. + * The second attempt seems to work. + */ +int +my_chdir(char * dir) { + int res = chdir(dir); + if (res != 0 && (errno == ESTALE || errno == EAGAIN)) { + /* Stale NFS file handle. Try again */ + res = chdir(dir); + } + return res; +} + + +/* + * File table of contents + */ +static void change_sunpro_dependencies_value(char *oldpath, char *newpath); +static void init_mksh_globals(char *shell); +static void set_env_vars(char *env_list[]); + + +static void +set_env_vars(char *env_list[]) +{ + char **env_list_p; + + for (env_list_p = env_list; + *env_list_p != (char *) NULL; + env_list_p++) { + putenv(*env_list_p); + } +} + +static void +init_mksh_globals(char *shell) +{ +/* + MBSTOWCS(wcs_buffer, "SHELL"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, shell); + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + */ + char * dmake_shell; + if ((dmake_shell = getenv("DMAKE_SHELL")) == NULL) { + dmake_shell = shell; + } + MBSTOWCS(wcs_buffer, dmake_shell); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); +} + +/* + * Change the pathname in the value of the SUNPRO_DEPENDENCIES env variable + * from oldpath to newpath. + */ +static void +change_sunpro_dependencies_value(char *oldpath, char *newpath) +{ + char buf[MAXPATHLEN]; + static char *env; + int length; + int oldpathlen; + char *sp_dep_value; + + /* check if SUNPRO_DEPENDENCIES is set in the environment */ + if ((sp_dep_value = getenv("SUNPRO_DEPENDENCIES")) != NULL) { + oldpathlen = strlen(oldpath); + /* check if oldpath is indeed in the value of SUNPRO_DEPENDENCIES */ + if (strncmp(oldpath, sp_dep_value, oldpathlen) == 0) { + (void) sprintf(buf, + "%s%s", + newpath, + sp_dep_value + oldpathlen); + length = 2 + + strlen("SUNPRO_DEPENDENCIES") + + strlen(buf); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + "SUNPRO_DEPENDENCIES", + buf); + (void) putenv(env); + } + } +} + + diff --git a/lib/mksh/read.cc b/lib/mksh/read.cc new file mode 100644 index 0000000..6a6a261 --- /dev/null +++ b/lib/mksh/read.cc @@ -0,0 +1,170 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include /* retmem() */ +#include +#include /* read() */ +#include /* close(), unlink(), read() */ +#include + +#define STRING_LEN_TO_CONVERT (8*1024) + +/* + * get_next_block_fn(source) + * + * Will get the next block of text to read either + * by popping one source bVSIZEOFlock of the stack of Sources + * or by reading some more from the makefile. + * + * Return value: + * The new source block to read from + * + * Parameters: + * source The old source block + * + * Global variables used: + * file_being_read The name of the current file, error msg + */ +Boolean make_state_locked; +Source +get_next_block_fn(register Source source) +{ + register off_t to_read; + register int length; + register size_t num_wc_chars; + char ch_save; + char *ptr; + + if (source == NULL) { + return NULL; + } + if ((source->fd < 0) || + ((source->bytes_left_in_file <= 0) && + (source->inp_buf_ptr >= source->inp_buf_end))) { + /* We can't read from the makefile, so pop the source block */ + if (source->fd > 2) { + (void) close(source->fd); + if (make_state_lockfile != NULL) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + } + if (source->string.free_after_use && + (source->string.buffer.start != NULL)) { + retmem(source->string.buffer.start); + source->string.buffer.start = NULL; + } + if (source->inp_buf != NULL) { + retmem_mb(source->inp_buf); + source->inp_buf = NULL; + } + source = source->previous; + if (source != NULL) { + source->error_converting = false; + } + return source; + } + if (source->bytes_left_in_file > 0) { + /* + * Read the whole makefile. + * Hopefully the kernel managed to prefetch the stuff. + */ + to_read = source->bytes_left_in_file; + source->inp_buf_ptr = source->inp_buf = getmem(to_read + 1); + source->inp_buf_end = source->inp_buf + to_read; + length = read(source->fd, source->inp_buf, (unsigned int) to_read); + if (length != to_read) { + WCSTOMBS(mbs_buffer, file_being_read); + if (length == 0) { + fatal_mksh(gettext("Error reading `%s': Premature EOF"), + mbs_buffer); + } else { + fatal_mksh(gettext("Error reading `%s': %s"), + mbs_buffer, + errmsg(errno)); + } + } + *source->inp_buf_end = nul_char; + source->bytes_left_in_file = 0; + } + /* + * Try to convert the next piece. + */ + ptr = source->inp_buf_ptr + STRING_LEN_TO_CONVERT; + if (ptr > source->inp_buf_end) { + ptr = source->inp_buf_end; + } + for (num_wc_chars = 0; ptr > source->inp_buf_ptr; ptr--) { + ch_save = *ptr; + *ptr = nul_char; + num_wc_chars = mbstowcs(source->string.text.end, + source->inp_buf_ptr, + STRING_LEN_TO_CONVERT); + *ptr = ch_save; + if (num_wc_chars != (size_t)-1) { + break; + } + } + + if ((int) num_wc_chars == (size_t)-1) { + source->error_converting = true; + return source; + } + + source->error_converting = false; + source->inp_buf_ptr = ptr; + source->string.text.end += num_wc_chars; + *source->string.text.end = 0; + + if (source->inp_buf_ptr >= source->inp_buf_end) { + if (*(source->string.text.end - 1) != (int) newline_char) { + WCSTOMBS(mbs_buffer, file_being_read); + warning_mksh(gettext("newline is not last character in file %s"), + mbs_buffer); + *source->string.text.end++ = (int) newline_char; + *source->string.text.end = (int) nul_char; + *source->string.buffer.end++; + } + if (source->inp_buf != NULL) { + retmem_mb(source->inp_buf); + source->inp_buf = NULL; + } + } + return source; +} + + diff --git a/lib/vroot/access.cc b/lib/vroot/access.cc new file mode 100644 index 0000000..7161c10 --- /dev/null +++ b/lib/vroot/access.cc @@ -0,0 +1,42 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +static int access_thunk(char *path) +{ + vroot_result= access(path, vroot_args.access.mode); + return((vroot_result == 0) || (errno != ENOENT)); +} + +int access_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.access.mode= mode; + translate_with_thunk(path, access_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/args.cc b/lib/vroot/args.cc new file mode 100644 index 0000000..0f8155a --- /dev/null +++ b/lib/vroot/args.cc @@ -0,0 +1,31 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +union Args vroot_args; +int vroot_result; diff --git a/lib/vroot/chdir.cc b/lib/vroot/chdir.cc new file mode 100644 index 0000000..fb4aaa3 --- /dev/null +++ b/lib/vroot/chdir.cc @@ -0,0 +1,41 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +static int chdir_thunk(char *path) +{ + vroot_result= chdir(path); + return(vroot_result == 0); +} + +int chdir_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, chdir_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chmod.cc b/lib/vroot/chmod.cc new file mode 100644 index 0000000..7217462 --- /dev/null +++ b/lib/vroot/chmod.cc @@ -0,0 +1,46 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int chmod(const char *path, mode_t mode); + +#include +#include + +static int chmod_thunk(char *path) +{ + vroot_result= chmod(path, vroot_args.chmod.mode); + return(vroot_result == 0); +} + +int chmod_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.chmod.mode= mode; + translate_with_thunk(path, chmod_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chown.cc b/lib/vroot/chown.cc new file mode 100644 index 0000000..00018ba --- /dev/null +++ b/lib/vroot/chown.cc @@ -0,0 +1,47 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int chown(const char *path, uid_t owner, gid_t group); + +#include +#include + +static int chown_thunk(char *path) +{ + vroot_result= chown(path, vroot_args.chown.user, vroot_args.chown.group); + return(vroot_result == 0); +} + +int chown_vroot(char *path, int user, int group, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.chown.user= user; + vroot_args.chown.group= group; + translate_with_thunk(path, chown_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chroot.cc b/lib/vroot/chroot.cc new file mode 100644 index 0000000..0561e1e --- /dev/null +++ b/lib/vroot/chroot.cc @@ -0,0 +1,44 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int chroot(const char *path); + +#include +#include + +static int chroot_thunk(char *path) +{ + vroot_result= chroot(path); + return(vroot_result == 0); +} + +int chroot_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, chroot_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/creat.cc b/lib/vroot/creat.cc new file mode 100644 index 0000000..9bf7fad --- /dev/null +++ b/lib/vroot/creat.cc @@ -0,0 +1,47 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern int creat(const char *path, mode_t mode); + +#include +#include + +static int creat_thunk(char *path) +{ + vroot_result= creat(path, vroot_args.creat.mode); + return(vroot_result >= 0); +} + +int creat_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.creat.mode= mode; + translate_with_thunk(path, creat_thunk, vroot_path, vroot_vroot, rw_write); + return(vroot_result); +} diff --git a/lib/vroot/execve.cc b/lib/vroot/execve.cc new file mode 100644 index 0000000..cf23e66 --- /dev/null +++ b/lib/vroot/execve.cc @@ -0,0 +1,50 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int execve (const char *path, char *const argv[], char *const envp[]); + +#include +#include + +static int execve_thunk(char *path) +{ + execve(path, vroot_args.execve.argv, vroot_args.execve.environ); + switch (errno) { + case ETXTBSY: + case ENOEXEC: return 1; + default: return 0; + } +} + +int execve_vroot(char *path, char **argv, char **environ, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.execve.argv= argv; + vroot_args.execve.environ= environ; + translate_with_thunk(path, execve_thunk, vroot_path, vroot_vroot, rw_read); + return(-1); +} diff --git a/lib/vroot/lock.cc b/lib/vroot/lock.cc new file mode 100644 index 0000000..d3389ce --- /dev/null +++ b/lib/vroot/lock.cc @@ -0,0 +1,175 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* errno */ +#include + +extern char *sys_errlist[]; +extern int sys_nerr; + +static void file_lock_error(char *msg, char *file, char *str, int arg1, int arg2); + +#define BLOCK_INTERUPTS sigfillset(&newset) ; \ + sigprocmask(SIG_SETMASK, &newset, &oldset) + +#define UNBLOCK_INTERUPTS \ + sigprocmask(SIG_SETMASK, &oldset, &newset) + +/* + * This code stolen from the NSE library and changed to not depend + * upon any NSE routines or header files. + * + * Simple file locking. + * Create a symlink to a file. The "test and set" will be + * atomic as creating the symlink provides both functions. + * + * The timeout value specifies how long to wait for stale locks + * to disappear. If the lock is more than 'timeout' seconds old + * then it is ok to blow it away. This part has a small window + * of vunerability as the operations of testing the time, + * removing the lock and creating a new one are not atomic. + * It would be possible for two processes to both decide to blow + * away the lock and then have process A remove the lock and establish + * its own, and then then have process B remove the lock which accidentily + * removes A's lock rather than the stale one. + * + * A further complication is with the NFS. If the file in question is + * being served by an NFS server, then its time is set by that server. + * We can not use the time on the client machine to check for a stale + * lock. Therefore, a temp file on the server is created to get + * the servers current time. + * + * Returns an error message. NULL return means the lock was obtained. + * + * 12/6/91 Added the parameter "file_locked". Before this parameter + * was added, the calling procedure would have to wait for file_lock() + * to return before it sets the flag. If the user interrupted "make" + * between the time the lock was acquired and the time file_lock() + * returns, make wouldn't know that the file has been locked, and therefore + * it wouldn' remove the lock. Setting the flag right after locking the file + * makes this window much smaller. + */ + +int +file_lock(char *name, char *lockname, int *file_locked, int timeout) +{ + int counter = 0; + static char msg[MAXPATHLEN+1]; + int printed_warning = 0; + int r; + struct stat statb; + sigset_t newset; + sigset_t oldset; + + *file_locked = 0; + if (timeout <= 0) { + timeout = 120; + } + for (;;) { + BLOCK_INTERUPTS; + r = symlink(name, lockname); + if (r == 0) { + *file_locked = 1; + UNBLOCK_INTERUPTS; + return 0; /* success */ + } + UNBLOCK_INTERUPTS; + + if (errno != EEXIST) { + file_lock_error(msg, name, (char *)"symlink(%s, %s)", + (int) name, (int) lockname); + fprintf(stderr, "%s", msg); + return errno; + } + + counter = 0; + for (;;) { + sleep(1); + r = lstat(lockname, &statb); + if (r == -1) { + /* + * The lock must have just gone away - try + * again. + */ + break; + } + + if ((counter > 5) && (!printed_warning)) { + /* Print waiting message after 5 secs */ + (void) getcwd(msg, MAXPATHLEN); + fprintf(stderr, + gettext("file_lock: file %s is already locked.\n"), + name); + fprintf(stderr, + gettext("file_lock: will periodically check the lockfile %s for two minutes.\n"), + lockname); + fprintf(stderr, + gettext("Current working directory %s\n"), + msg); + + printed_warning = 1; + } + + if (++counter > timeout ) { + /* + * Waited enough - return an error.. + */ + return EEXIST; + } + } + } + /* NOTREACHED */ +} + +/* + * Format a message telling why the lock could not be created. + */ +static void +file_lock_error(char *msg, char *file, char *str, int arg1, int arg2) +{ + int len; + + sprintf(msg, gettext("Could not lock file `%s'; "), file); + len = strlen(msg); + sprintf(&msg[len], str, arg1, arg2); + strcat(msg, gettext(" failed - ")); + if (errno < sys_nerr) { + strcat(msg, strerror(errno)); + } else { + len = strlen(msg); + sprintf(&msg[len], "errno %d", errno); + } +} + diff --git a/lib/vroot/lstat.cc b/lib/vroot/lstat.cc new file mode 100644 index 0000000..e5a8b2d --- /dev/null +++ b/lib/vroot/lstat.cc @@ -0,0 +1,46 @@ +/* + * 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 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int lstat(const char *path, struct stat *buf); + +#include +#include + +static int lstat_thunk(char *path) +{ + vroot_result= lstat(path, vroot_args.lstat.buffer); + return(vroot_result == 0); +} + +int lstat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.lstat.buffer= buffer; + translate_with_thunk(path, lstat_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/mkdir.cc b/lib/vroot/mkdir.cc new file mode 100644 index 0000000..d64c6ef --- /dev/null +++ b/lib/vroot/mkdir.cc @@ -0,0 +1,46 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int mkdir(const char *path, mode_t mode); + +#include +#include + +static int mkdir_thunk(char *path) +{ + vroot_result= mkdir(path, vroot_args.mkdir.mode); + return(vroot_result == 0); +} + +int mkdir_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.mkdir.mode= mode; + translate_with_thunk(path, mkdir_thunk, vroot_path, vroot_vroot, rw_write); + return(vroot_result); +} diff --git a/lib/vroot/mount.cc b/lib/vroot/mount.cc new file mode 100644 index 0000000..6ffc67f --- /dev/null +++ b/lib/vroot/mount.cc @@ -0,0 +1,47 @@ +/* + * 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 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int mount(const char *spec, const char *dir, int mflag, ...); + +#include +#include + +static int mount_thunk(char *path) +{ + vroot_result= mount(path, vroot_args.mount.name, vroot_args.mount.mode); + return(vroot_result == 0); +} + +int mount_vroot(char *target, char *name, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.mount.name= name; + vroot_args.mount.mode= mode; + translate_with_thunk(target, mount_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/open.cc b/lib/vroot/open.cc new file mode 100644 index 0000000..8780baf --- /dev/null +++ b/lib/vroot/open.cc @@ -0,0 +1,49 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern int open(const char *path, int oflag, ...); + +#include +#include + +static int open_thunk(char *path) +{ + vroot_result= open(path, vroot_args.open.flags, vroot_args.open.mode); + return(vroot_result >= 0); +} + +int open_vroot(char *path, int flags, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.open.flags= flags; + vroot_args.open.mode= mode; + translate_with_thunk(path, open_thunk, vroot_path, vroot_vroot, + ((flags & (O_CREAT|O_APPEND)) != 0) ? rw_write : rw_read); + return(vroot_result); +} diff --git a/lib/vroot/readlink.cc b/lib/vroot/readlink.cc new file mode 100644 index 0000000..06ca253 --- /dev/null +++ b/lib/vroot/readlink.cc @@ -0,0 +1,46 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int readlink(const char *path, void *buf, size_t bufsiz); + +#include +#include + +static int readlink_thunk(char *path) +{ + vroot_result= readlink(path, vroot_args.readlink.buffer, vroot_args.readlink.buffer_size); + return(vroot_result >= 0); +} + +int readlink_vroot(char *path, char *buffer, int buffer_size, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.readlink.buffer= buffer; + vroot_args.readlink.buffer_size= buffer_size; + translate_with_thunk(path, readlink_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/report.cc b/lib/vroot/report.cc new file mode 100644 index 0000000..9def773 --- /dev/null +++ b/lib/vroot/report.cc @@ -0,0 +1,333 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* for tmpdir */ + +static FILE *report_file; +static FILE *command_output_fp; +static char *target_being_reported_for; +static char *search_dir; +static char command_output_tmpfile[30]; +static int is_path = 0; +static char sfile[MAXPATHLEN]; +extern "C" { +static void (*warning_ptr) (char *, ...) = (void (*) (char *, ...)) NULL; +} + +FILE * +get_report_file(void) +{ + return(report_file); +} + +char * +get_target_being_reported_for(void) +{ + return(target_being_reported_for); +} + +extern "C" { +static void +close_report_file(void) +{ + (void)fputs("\n", report_file); + (void)fclose(report_file); +} +} // extern "C" + +static void +clean_up(FILE *nse_depinfo_fp, FILE *merge_fp, char *nse_depinfo_file, char *merge_file, int unlinkf) +{ + fclose(nse_depinfo_fp); + fclose(merge_fp); + fclose(command_output_fp); + unlink(command_output_tmpfile); + if (unlinkf) + unlink(merge_file); + else + rename(merge_file, nse_depinfo_file); +} + + +/* + * Update the file, if necessary. We don't want to rewrite + * the file if we don't have to because we don't want the time of the file + * to change in that case. + */ + +extern "C" { +static void +close_file(void) +{ + char line[MAXPATHLEN+2]; + char buf[MAXPATHLEN+2]; + FILE *nse_depinfo_fp; + FILE *merge_fp; + char nse_depinfo_file[MAXPATHLEN]; + char merge_file[MAXPATHLEN]; + char lock_file[MAXPATHLEN]; + int err; + int len; + int changed = 0; + int file_locked; + + fprintf(command_output_fp, "\n"); + fclose(command_output_fp); + if ((command_output_fp = fopen(command_output_tmpfile, "r")) == NULL) { + return; + } + sprintf(nse_depinfo_file, "%s/%s", search_dir, NSE_DEPINFO); + sprintf(merge_file, "%s/.tmp%s.%d", search_dir, NSE_DEPINFO, getpid()); + sprintf(lock_file, "%s/%s", search_dir, NSE_DEPINFO_LOCK); + err = file_lock(nse_depinfo_file, lock_file, &file_locked, 0); + if (err) { + if (warning_ptr != (void (*) (char *, ...)) NULL) { + (*warning_ptr)(gettext("Couldn't write to %s"), nse_depinfo_file); + } + unlink(command_output_tmpfile); + return; + } + /* If .nse_depinfo file doesn't exist */ + if ((nse_depinfo_fp = fopen(nse_depinfo_file, "r+")) == NULL) { + if (is_path) { + if ((nse_depinfo_fp = + fopen(nse_depinfo_file, "w")) == NULL) { + fprintf(stderr, gettext("Cannot open `%s' for writing\n"), + nse_depinfo_file); + unlink(command_output_tmpfile); + + unlink(lock_file); + return; + } + while (fgets(line, MAXPATHLEN+2, command_output_fp) + != NULL) { + fprintf(nse_depinfo_fp, "%s", line); + } + fclose(command_output_fp); + } + fclose(nse_depinfo_fp); + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } + if ((merge_fp = fopen(merge_file, "w")) == NULL) { + fprintf(stderr, gettext("Cannot open %s for writing\n"), merge_file); + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } + len = strlen(sfile); + while (fgets(line, MAXPATHLEN+2, nse_depinfo_fp) != NULL) { + if (strncmp(line, sfile, len) == 0 && line[len] == ':') { + while (fgets(buf, MAXPATHLEN+2, command_output_fp) + != NULL) { + if (is_path) { + fprintf(merge_fp, "%s", buf); + if (strcmp(line, buf)) { + /* changed */ + changed = 1; + } + } + if (buf[strlen(buf)-1] == '\n') { + break; + } + } + if (changed || !is_path) { + while (fgets(line, MAXPATHLEN, nse_depinfo_fp) + != NULL) { + fputs(line, merge_fp); + } + clean_up(nse_depinfo_fp, merge_fp, + nse_depinfo_file, merge_file, 0); + } else { + clean_up(nse_depinfo_fp, merge_fp, + nse_depinfo_file, merge_file, 1); + } + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } /* entry found */ + fputs(line, merge_fp); + } + /* Entry never found. Add it if there is a search path */ + if (is_path) { + while (fgets(line, MAXPATHLEN+2, command_output_fp) != NULL) { + fprintf(nse_depinfo_fp, "%s", line); + } + } + clean_up(nse_depinfo_fp, merge_fp, nse_depinfo_file, merge_file, 1); + if (file_locked) { + unlink(lock_file); + } +} + +} // extern "C" + +static void +report_dep(char *iflag, char *filename) +{ + + if (command_output_fp == NULL) { + sprintf(command_output_tmpfile, + "%s/%s.%d.XXXXXX", tmpdir, NSE_DEPINFO, getpid()); + int fd = mkstemp(command_output_tmpfile); + if ((fd < 0) || (command_output_fp = fdopen(fd, "w")) == NULL) { + return; + } + if ((search_dir = getenv("NSE_DEP")) == NULL) { + return; + } + atexit(close_file); + strcpy(sfile, filename); + if (iflag == NULL || *iflag == '\0') { + return; + } + fprintf(command_output_fp, "%s:", sfile); + } + fprintf(command_output_fp, " "); + fprintf(command_output_fp, iflag); + if (iflag != NULL) { + is_path = 1; + } +} + +void +report_libdep(char *lib, char *flag) +{ + char *ptr; + char filename[MAXPATHLEN]; + char *p; + + if ((p= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + return; + } + ptr = strchr(p, ' '); + if(ptr) { + sprintf(filename, "%s-%s", ptr+1, flag); + is_path = 1; + report_dep(lib, filename); + } +} + +void +report_search_path(char *iflag) +{ + char curdir[MAXPATHLEN]; + char *sdir; + char *newiflag; + char filename[MAXPATHLEN]; + char *p, *ptr; + + if ((sdir = getenv("NSE_DEP")) == NULL) { + return; + } + if ((p= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + return; + } + ptr = strchr(p, ' '); + if( ! ptr ) { + return; + } + sprintf(filename, "%s-CPP", ptr+1); + getcwd(curdir, sizeof(curdir)); + if (strcmp(curdir, sdir) != 0 && strlen(iflag) > 2 && + iflag[2] != '/') { + /* Makefile must have had an "cd xx; cc ..." */ + /* Modify the -I path to be relative to the cd */ + newiflag = (char *)malloc(strlen(iflag) + strlen(curdir) + 2); + sprintf(newiflag, "-%c%s/%s", iflag[1], curdir, &iflag[2]); + report_dep(newiflag, filename); + } else { + report_dep(iflag, filename); + } +} + +void +report_dependency(const char *name) +{ + register char *filename; + char buffer[MAXPATHLEN+1]; + register char *p; + register char *p2; + char nse_depinfo_file[MAXPATHLEN]; + + if (report_file == NULL) { + if ((filename= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + report_file = (FILE *)-1; + return; + } + if (strlen(filename) == 0) { + report_file = (FILE *)-1; + return; + } + (void)strcpy(buffer, name); + name = buffer; + p = strchr(filename, ' '); + if(p) { + *p= 0; + } else { + report_file = (FILE *)-1; + return; + } + if ((report_file= fopen(filename, "a")) == NULL) { + if ((report_file= fopen(filename, "w")) == NULL) { + report_file= (FILE *)-1; + return; + } + } + atexit(close_report_file); + if ((p2= strchr(p+1, ' ')) != NULL) + *p2= 0; + target_being_reported_for= (char *)malloc((unsigned)(strlen(p+1)+1)); + (void)strcpy(target_being_reported_for, p+1); + (void)fputs(p+1, report_file); + (void)fputs(":", report_file); + *p= ' '; + if (p2 != NULL) + *p2= ' '; + } + if (report_file == (FILE *)-1) + return; + (void)fputs(name, report_file); + (void)fputs(" ", report_file); +} + + diff --git a/lib/vroot/rmdir.cc b/lib/vroot/rmdir.cc new file mode 100644 index 0000000..f297aac --- /dev/null +++ b/lib/vroot/rmdir.cc @@ -0,0 +1,44 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int rmdir(const char *path); + +#include +#include + +static int rmdir_thunk(char *path) +{ + vroot_result= rmdir(path); + return(vroot_result == 0); +} + +int rmdir_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, rmdir_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/setenv.cc b/lib/vroot/setenv.cc new file mode 100644 index 0000000..881be5a --- /dev/null +++ b/lib/vroot/setenv.cc @@ -0,0 +1,61 @@ +/* + * 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 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern char **environ; + +static short setenv_made_new_vector= 0; + +char *setenv(char *name, char *value) +{ char *p= NULL, **q; + int length= 0, vl; + + if ((p= getenv(name)) == NULL) { /* Allocate new vector */ + for (q= environ; *q != NULL; q++, length++); + q= (char **)malloc((unsigned)(sizeof(char *)*(length+2))); + memcpy(((char *)q)+sizeof(char *), (char *)environ, sizeof(char *)*(length+1)); + if (setenv_made_new_vector++) + free((char *)environ); + length= strlen(name); + environ= q;} + else { /* Find old slot */ + length= strlen(name); + for (q= environ; *q != NULL; q++) + if (!strncmp(*q, name, length)) + break;}; + vl= strlen(value); + if (!p || (length+vl+1 > strlen(p))) + *q= p= (char *) malloc((unsigned)(length+vl+2)); + else + p= *q; + (void)strcpy(p, name); p+= length; + *p++= '='; + (void)strcpy(p, value); + return(value); +} diff --git a/lib/vroot/stat.cc b/lib/vroot/stat.cc new file mode 100644 index 0000000..4cd6a08 --- /dev/null +++ b/lib/vroot/stat.cc @@ -0,0 +1,46 @@ +/* + * 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 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int stat(const char *path, struct stat *buf); + +#include +#include + +static int stat_thunk(char *path) +{ + vroot_result= stat(path, vroot_args.stat.buffer); + return(vroot_result == 0); +} + +int stat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.stat.buffer= buffer; + translate_with_thunk(path, stat_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/truncate.cc b/lib/vroot/truncate.cc new file mode 100644 index 0000000..d9d118d --- /dev/null +++ b/lib/vroot/truncate.cc @@ -0,0 +1,45 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int truncate(const char *path, off_t length); + +#include +#include + +static int truncate_thunk(char *path) +{ + vroot_result= truncate(path, vroot_args.truncate.length); + return(vroot_result == 0); +} + +int truncate_vroot(char *path, int length, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.truncate.length= length; + translate_with_thunk(path, truncate_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/unlink.cc b/lib/vroot/unlink.cc new file mode 100644 index 0000000..51134d1 --- /dev/null +++ b/lib/vroot/unlink.cc @@ -0,0 +1,44 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int unlink(const char *path); + +#include +#include + +static int unlink_thunk(char *path) +{ + vroot_result= unlink(path); + return(vroot_result == 0); +} + +int unlink_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, unlink_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/utimes.cc b/lib/vroot/utimes.cc new file mode 100644 index 0000000..9cddab3 --- /dev/null +++ b/lib/vroot/utimes.cc @@ -0,0 +1,46 @@ +/* + * 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 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int utimes(char *file, struct timeval *tvp); + +#include +#include + +static int utimes_thunk(char *path) +{ + vroot_result= utimes(path, vroot_args.utimes.time); + return(vroot_result == 0); +} + +int utimes_vroot(char *path, struct timeval *time, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.utimes.time= time; + translate_with_thunk(path, utimes_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/vroot.cc b/lib/vroot/vroot.cc new file mode 100644 index 0000000..785a0bc --- /dev/null +++ b/lib/vroot/vroot.cc @@ -0,0 +1,338 @@ +/* + * 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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +#include +#include + +#include +#include +#include + +typedef struct { + short init; + pathpt vector; + const char *env_var; +} vroot_patht; + +typedef struct { + vroot_patht vroot; + vroot_patht path; + char full_path[MAXPATHLEN+1]; + char *vroot_start; + char *path_start; + char *filename_start; + int scan_vroot_first; + int cpp_style_path; +} vroot_datat, *vroot_datapt; + +static vroot_datat vroot_data= { + { 0, NULL, "VIRTUAL_ROOT"}, + { 0, NULL, "PATH"}, + "", NULL, NULL, NULL, 0, 1}; + +void +add_dir_to_path(const char *path, register pathpt *pointer, register int position) +{ + register int size= 0; + register int length; + register char *name; + register pathcellpt p; + pathpt new_path; + + if (*pointer != NULL) { + for (p= &((*pointer)[0]); p->path != NULL; p++, size++); + if (position < 0) + position= size;} + else + if (position < 0) + position= 0; + if (position >= size) { + new_path= (pathpt)calloc((unsigned)(position+2), sizeof(pathcellt)); + if (*pointer != NULL) { + memcpy((char *)new_path,(char *)(*pointer), size*sizeof(pathcellt)); + free((char *)(*pointer));}; + *pointer= new_path;}; + length= strlen(path); + name= (char *)malloc((unsigned)(length+1)); + (void)strcpy(name, path); + if ((*pointer)[position].path != NULL) + free((*pointer)[position].path); + (*pointer)[position].path= name; + (*pointer)[position].length= length; +} + +pathpt +parse_path_string(register char *string, register int remove_slash) +{ + register char *p; + pathpt result= NULL; + + if (string != NULL) + for (; 1; string= p+1) { + if (p= strchr(string, ':')) *p= 0; + if ((remove_slash == 1) && !strcmp(string, "/")) + add_dir_to_path("", &result, -1); + else + add_dir_to_path(string, &result, -1); + if (p) *p= ':'; + else return(result);}; + return((pathpt)NULL); +} + +const char * +get_vroot_name(void) +{ + return(vroot_data.vroot.env_var); +} + +const char * +get_path_name(void) +{ + return(vroot_data.path.env_var); +} + +void +flush_path_cache(void) +{ + vroot_data.path.init= 0; +} + +void +flush_vroot_cache(void) +{ + vroot_data.vroot.init= 0; +} + +void +scan_path_first(void) +{ + vroot_data.scan_vroot_first= 0; +} + +void +scan_vroot_first(void) +{ + vroot_data.scan_vroot_first= 1; +} + +void +set_path_style(int style) +{ + vroot_data.cpp_style_path= style; +} + +char * +get_vroot_path(register char **vroot, register char **path, register char **filename) +{ + if (vroot != NULL) { + if ((*vroot= vroot_data.vroot_start) == NULL) + if ((*vroot= vroot_data.path_start) == NULL) + *vroot= vroot_data.filename_start;}; + if (path != NULL) { + if ((*path= vroot_data.path_start) == NULL) + *path= vroot_data.filename_start;}; + if (filename != NULL) + *filename= vroot_data.filename_start; + return(vroot_data.full_path); +} + +void +translate_with_thunk(register char *filename, int (*thunk) (char *), pathpt path_vector, pathpt vroot_vector, rwt rw) +{ + register pathcellt *vp; + pathcellt *pp; + register pathcellt *pp1; + register char *p; + int flags[256]; + +/* Setup path to use */ + if (rw == rw_write) + pp1= NULL; /* Do not use path when writing */ + else { + if (path_vector == VROOT_DEFAULT) { + if (!vroot_data.path.init) { + vroot_data.path.init= 1; + vroot_data.path.vector= parse_path_string(getenv(vroot_data.path.env_var), 0);}; + path_vector= vroot_data.path.vector;}; + pp1= path_vector == NULL ? NULL : &(path_vector)[0];}; + +/* Setup vroot to use */ + if (vroot_vector == VROOT_DEFAULT) { + if (!vroot_data.vroot.init) { + vroot_data.vroot.init= 1; + vroot_data.vroot.vector= parse_path_string(getenv(vroot_data.vroot.env_var), 1);}; + vroot_vector= vroot_data.vroot.vector;}; + vp= vroot_vector == NULL ? NULL : &(vroot_vector)[0]; + +/* Setup to remember pieces */ + vroot_data.vroot_start= NULL; + vroot_data.path_start= NULL; + vroot_data.filename_start= NULL; + + int flen = strlen(filename); + if(flen >= MAXPATHLEN) { + errno = ENAMETOOLONG; + return; + } + + switch ((vp ?1:0) + (pp1 ? 2:0)) { + case 0: /* No path. No vroot. */ + use_name: + (void)strcpy(vroot_data.full_path, filename); + vroot_data.filename_start= vroot_data.full_path; + (void)(*thunk)(vroot_data.full_path); + return; + case 1: /* No path. Vroot */ + if (filename[0] != '/') goto use_name; + for (; vp->path != NULL; vp++) { + if((1 + flen + vp->length) >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + p= vroot_data.full_path; + (void)strcpy(vroot_data.vroot_start= p, vp->path); + p+= vp->length; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;}; + (void)strcpy(vroot_data.full_path, filename); + return; + case 2: /* Path. No vroot. */ + if (vroot_data.cpp_style_path) { + if (filename[0] == '/') goto use_name; + } else { + if (strchr(filename, '/') != NULL) goto use_name; + }; + for (; pp1->path != NULL; pp1++) { + p= vroot_data.full_path; + if((1 + flen + pp1->length) >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + if (vroot_data.cpp_style_path) { + (void)strcpy(vroot_data.path_start= p, pp1->path); + p+= pp1->length; + *p++= '/'; + } else { + if (pp1->length != 0) { + (void)strcpy(vroot_data.path_start= p, + pp1->path); + p+= pp1->length; + *p++= '/'; + }; + }; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;}; + (void)strcpy(vroot_data.full_path, filename); + return; + case 3: { /* Path. Vroot. */ + int *rel_path, path_len= 1; + if (vroot_data.scan_vroot_first == 0) { + for (pp= pp1; pp->path != NULL; pp++) path_len++; + rel_path= flags; + for (path_len-= 2; path_len >= 0; path_len--) rel_path[path_len]= 0; + for (; vp->path != NULL; vp++) + for (pp= pp1, path_len= 0; pp->path != NULL; pp++, path_len++) { + int len = 0; + if (rel_path[path_len] == 1) continue; + if (pp->path[0] != '/') rel_path[path_len]= 1; + p= vroot_data.full_path; + if ((filename[0] == '/') || (pp->path[0] == '/')) { + if(vp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.vroot_start= p, vp->path); p+= vp->length; + len += vp->length; + }; + if (vroot_data.cpp_style_path) { + if (filename[0] != '/') { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + }; + } else { + if (strchr(filename, '/') == NULL) { + if (pp->length != 0) { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, + pp->path); + p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } + }; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;};} + else { pathcellt *vp1= vp; + for (pp= pp1, path_len= 0; pp->path != NULL; pp++, path_len++) + for (vp= vp1; vp->path != NULL; vp++) { + int len = 0; + p= vroot_data.full_path; + if ((filename[0] == '/') || (pp->path[0] == '/')) { + if(vp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.vroot_start= p, vp->path); p+= vp->length; + len += vp->length; + } + if (vroot_data.cpp_style_path) { + if (filename[0] != '/') { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } else { + if (strchr(filename, '/') == NULL) { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;};}; + (void)strcpy(vroot_data.full_path, filename); + return;};}; +}