# # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. SUBDIRS= bin \ lib all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber _msg : TARGET = _msg .KEEP_STATE: bin: lib all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. MAKE_INCLUDE= $(SRC)/cmd/make/include CFLAGS += $(CCVERBOSE) CPPFLAGS += -I$(MAKE_INCLUDE) $(MAKE_DEFS) # So that it's set even for the libraries we build TEXT_DOMAIN = SUNW_OST_OSCMD $(POFILE): $(POFILES) $(CAT) $(POFILES) > $@ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. # Copyright 2019 RackTop Systems. PROG= make POFILE= make.po OBJS= ar.o \ depvar.o \ doname.o \ dosys.o \ files.o \ globals.o \ implicit.o \ macro.o \ main.o \ misc.o \ nse_printdep.o \ parallel.o \ pmake.o \ read.o \ read2.o \ rep.o \ state.o POFILES= $(OBJS:%.o=%.po) include ../../Makefile.cmd include ../Makefile.com LDLIBS += ../lib/mksh/libmksh.a ../lib/vroot/libvroot.a LDLIBS += ../lib/bsd/libbsd.a -lc -lnsl -lumem CPPFLAGS += -D_FILE_OFFSET_BITS=64 CCERRWARN += -Wno-unused-variable CCERRWARN += -Wno-switch CCERRWARN += -Wno-parentheses CCERRWARN += -Wno-unused-function CCERRWARN += -Wno-clobbered CCERRWARN += -Wno-uninitialized ROOTLINKS = $(ROOTCCSBIN)/make $(ROOTXPG4BIN)/make $(ROOTBIN)/dmake $(ROOTCCSLIB)/svr4.make \ $(ROOTLIB)/svr4.make ROOTRULES = $(ROOTSHLIB)/make/make.rules $(ROOTSHLIB)/make/svr4.make.rules all: $(PROG) install: all $(ROOTPROG) $(ROOTLINKS) $(ROOTRULES) $(PROG): $(OBJS) $(LINK.cc) $(OBJS) -o $@ $(LDLIBS) $(POST_PROCESS) $(ROOTCCSBIN)/make: -$(RM) $@; $(SYMLINK) ../../bin/make $@ $(ROOTCCSLIB)/svr4.make: -$(RM) $@; $(SYMLINK) ../../bin/make $@ $(ROOTLIB)/svr4.make: -$(RM) $@; $(SYMLINK) ../bin/make $@ $(ROOTXPG4BIN)/make: -$(RM) $@; $(SYMLINK) ../../bin/make $@ $(ROOTBIN)/dmake: -$(RM) $@; $(SYMLINK) ./make $@ $(ROOTRULES) : FILEMODE = 0444 $(ROOTRULES): $(ROOTSHLIB)/make $(ROOTSHLIB)/make: FRC $(INS.dir) $(ROOTSHLIB)/make/%: %.file $(INS.rename) clean: $(RM) $(OBJS) FRC: include ../../Makefile.targ /* * 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(Name target); static Boolean open_archive(char *filename, Ar *arp); static void close_archive(Ar *arp); static Boolean read_archive_dir(Ar *arp, Name library, char **long_names_table); static void translate_entry(Ar *arp, Name target, 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 (Ar *arp, char **long_names_table, char *filename); timestruc_t& read_archive(Name target) { Property member; wchar_t *slash; String_rec true_member_name; wchar_t buffer[STRING_BUFFER_LENGTH]; 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, 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(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(Ar *arp, Name library, char **long_names_table) { wchar_t *name_string; wchar_t *member_string; long len; wchar_t *p; char *q; 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(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(Ar *arp, Name target, Property member, char **long_names_table) { int len; 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; wchar_t *ap; 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(char *buffer) { long w = 0; 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; } /* * 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")); } } /* * 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 (c) 2016 by Delphix. All rights reserved. * Copyright 2019 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic); extern Doname doname(Name target, Boolean do_get, Boolean implicit, 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(Property line, Boolean print_machine); extern Doname execute_serial(Property line); extern Name vpath_translation(Name cmd); extern void check_state(Name temp_file_name); static void read_dependency_file(Name filename); static void check_read_state_file(void); static void do_assign(Name line, Name target); static void build_command_strings(Name target, Property line); static Doname touch_command(Property line, Name target, Doname result); extern void update_target(Property line, Doname result); static Doname sccs_get(Name target, Property *command); extern void read_directory_of_file(Name file); static void add_pattern_conditionals(Name target); extern void set_locals(Name target, Property old_locals); extern void reset_locals(Name target, Property old_locals, Property conditional, 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(wchar_t *name_string, 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic) { int first_time = 1; Doname rv = build_failed; (void) fflush(stdout); try_again: switch (doname(target, do_get, implicit, automatic)) { case build_ok: second_pass = 0; rv = build_ok; break; case build_running: second_pass = 0; rv = build_running; break; 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; rv = build_failed; break; 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; rv = build_dont_know; break; } 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; rv = build_failed; break; } fatal(gettext("Don't know how to make target `%s'"), target->string_mb); break; } return (rv); } 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic) { Doname result = build_dont_know; Chain out_of_date_list = NULL; Chain target_group; Property old_locals = NULL; Property line; Property command = NULL; Dependency dependency; Name less = NULL; Name true_target = target; Name *automatics = NULL; 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; 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; wchar_t *target_end; Dependency suffix; 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]; wchar_t *p; Property line; Dependency dependency; Dependency *remove; String_rec string; wchar_t buffer[MAXPATHLEN]; Boolean set_at = false; wchar_t *start; Dependency new_depe; 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(Property line, Boolean) { Doname result = build_ok; Boolean remember_only = false; 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(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(Name filename) { 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; Makefile_type save_makefile_type; 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(Name line, Name target) { Wstring wcb(line); wchar_t *string = wcb.get_string(); wchar_t *equal; Name name; 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, Property line) { String_rec command_line; Cmd_line command_template = line->body.line.command_template; Cmd_line *insert = &line->body.line.command_used; Cmd_line used = *insert; wchar_t buffer[STRING_BUFFER_LENGTH]; wchar_t *start; Name new_command_line; Boolean new_command_longer = false; 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(Property line, Name target, Doname result) { Name name; 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(Name target, Property *command) { int result; char link[MAXPATHLEN]; String_rec string; wchar_t name[MAXPATHLEN]; wchar_t *p; timestruc_t sccs_time; 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; /* FALLTHROUGH */ 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(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]; Name directory = dot; wchar_t *p = (wchar_t *) wcsrchr(wcb, (int) slash_char); 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(Name target) { 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(Name target, Property old_locals) { Property conditional; int i; 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(Name target, Property old_locals, Property conditional, int index) { 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(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; } /* * 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(Name command, Boolean ignore_error, Boolean call_make, Boolean silent_error, Boolean always_exec, Name target) { timestruc_t before; int length = command->hash.length; Wstring wcb(command); wchar_t *p = wcb.get_string(); 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; } /* * 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. * * Copyright 2019 RackTop Systems. */ /* * 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(Name target); extern void set_target_stat(Name target, struct stat buf); static timestruc_t& vpath_exists(Name target); static Name enter_file_name(wchar_t *name_string, wchar_t *library); static Boolean star_match(char *string, char *pattern); static Boolean amatch(wchar_t *string, 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(Name target) { struct stat buf; 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(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(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 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(wchar_t *string, wchar_t *pattern) { 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(wchar_t *string, wchar_t *pattern) { long lower_bound; long string_ch; long pattern_ch; 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])); /* FALLTHROUGH */ 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 */ } /* * 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. * * Copyright 2019, Joyent, Inc. */ /* * 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; Boolean path_reset = false; Boolean rebuild_arg0 = false; /* * File table of contents */ /* * 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. * * Copyright 2019 RackTop Systems. */ /* * 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(Name target, Name true_target, Property *command, Boolean rechecking); extern Doname find_double_suffix_rule(Name target, Property *command, Boolean rechecking); extern void build_suffix_list(Name target_suffix); extern Doname find_percent_rule(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; wchar_t *put_suffix; Property source_suffix; Name source; Doname result; 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; } /* FALLTHROUGH */ 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(Name target, Name true_target, Property *command, Boolean rechecking) { wchar_t *target_end; Dependency suffix; 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(Name target, Property *command, Boolean rechecking) { Name true_target = target; Name target_body; wchar_t *target_end; Dependency suffix; 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(Name target_suffix) { Dependency source_suffix; wchar_t rule_name[MAXPATHLEN]; Property line; 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(Name target, Property *command, Boolean rechecking) { Percent pat_rule, pat_depe; Name depe_to_check; Dependency depe; 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; } /* * 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(Name name, Name value) { Property macro_apx = get_prop(name->prop, macro_append_prop); Property macro = get_prop(name->prop, macro_prop); int length; String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; 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; char *mbs, *tmp_mbs_buffer = NULL; 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; } } } /* * 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 2019, Joyent, Inc. * Copyright 2019 RackTop Systems. * Copyright 2019 OmniOS Community Edition (OmniOSce) Association. */ /* * 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(wchar_t *name_string, 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 __x86 #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(wchar_t *name_string, 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, 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. */ char *cp; char make_state_dir[MAXPATHLEN]; Boolean parallel_flag = false; Boolean argv_zero_relative = 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); argv_zero_relative = true; } /* * 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 standard */ 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(); /* * 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); } /* * Reset argv_zero_string if it was built from a relative path and the * -C option was specified. */ if (argv_zero_relative && rebuild_arg0) { char *tmp_current_path; char *tmp_string; free(argv_zero_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); } setup_for_projectdir(); 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: */ static void doalarm(int sig __attribute__((unused))) { 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(int argc, char **argv) { int ch; int current_optind = 1; int last_optind_with_double_hyphen = 0; int last_optind; int last_current_optind; int i; int j; int k; 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:C:Ddef:g:ij:K:kM:m:NnO:o:PpqRrSsTtuVvwx:" # define SVR4_CMD_OPTS "-c: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 ][-C directory]\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 ][-C directory]\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; case 2048: argv[i] = (char *)"-C"; if (chdir(argv[k]) != 0) { fatal(gettext("failed to change to directory %s: %s"), argv[k], strerror(errno)); } path_reset = true; rebuild_arg0 = true; (void) get_current_path(); 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(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 'C': /* Change directory */ return 2048; 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]; char ch; char *cp; Property def_make_macro = NULL; Name def_make_name; Name default_makefile; String_rec dest; wchar_t destbuffer[STRING_BUFFER_LENGTH]; int i; int j; Name keep_state_name; int length; Name Makefile; Property macro; struct stat make_state_stat; Name makefile_name; int makefile_next = 0; Boolean makefile_read = false; String_rec makeflags_string; String_rec makeflags_string_posix; String_rec * makeflags_string_current; Name makeflags_value_saved; Name name; Name new_make_value; Boolean save_do_not_exec_rule; 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; 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); /* * 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. * * Note: cw depends on short flags with no arguments appearing * first in MAKEFLAGS. If this behavior changes, cw will need to * be updated. */ 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); /* FALLTHROUGH */ 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); /* FALLTHROUGH */ 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); /* FALLTHROUGH */ case 3: append_char('P', &makeflags_string); append_char('P', &makeflags_string_posix); /* FALLTHROUGH */ case 2: append_char('P', &makeflags_string); append_char('P', &makeflags_string_posix); /* FALLTHROUGH */ 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) { char *cp; int i; int length; Name name; int opt_separator = argc; char tmp_char; wchar_t *tmp_wcs_buffer; 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; case 2048: /* -C seen */ /* * The chdir() will have been performed * in read_command_options(). * Just set DMAKE_CDIR here. */ if (argv[i + 1] == NULL) { fatal(gettext( "No argument after -C flag")); } MBSTOWCS(wcs_buffer, "DMAKE_CDIR"); name = GETNAME(wcs_buffer, FIND_LENGTH); 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, 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) { char **environment; int length; wchar_t *tmp_wcs_buffer; Boolean alloced_tmp_wcs_buffer = false; wchar_t *name; wchar_t *value; 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(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; 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(Name target) { 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) { char *cp0; /* work pointer in macro */ char *cp1; /* work pointer in array */ char *cp2; /* work pointer in array */ char *cp3; /* work pointer in array */ char *name; /* macro name */ char *value; /* macro value */ int len_array; 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); } } # # 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.$@ /* * 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 2019, Joyent, Inc. */ /* * 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(Name target); static void print_target_n_deps(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 = NULL; /* * When we hit this with path_reset to true, we do not free the older * version of current_path at this time, as we don't have confidence * that we've properly caught all users of it and they haven't cached * the pointer somewhere. As such, since this is only currently set with * the -C option is passed in, it seems OK to just let that bit go. */ if (current_path == NULL || path_reset == true) { getcwd(pwd, sizeof(pwd)); if (pwd[0] == (int) nul_char) { pwd[0] = (int) slash_char; pwd[1] = (int) nul_char; } current_path = strdup(pwd); path_reset = false; } 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; Property prop; Dependency dep; 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(Name target) { Cmd_line rule; Property line; 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(Name target) { Cmd_line rule; Property line; 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); } } } /* * 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(Name target, Property line); static void print_deps(Name target, 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(Name value, Daemon daemon); static void print_rule(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(Name target, 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; 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(Name target, Property line) { 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(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(Name target) { Cmd_line rule; 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); } } /* * 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); } /* * 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); } } /* * 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 2019 RackTop Systems. */ /* * 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(Name true_makefile_name, Source source); static Source push_macro_value(Source bp, wchar_t *buffer, int size, 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(wchar_t *name_string, 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(Name makefile_name, Boolean chase_path, Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile) { static short max_include_depth; Property makefile = maybe_append_prop(makefile_name, makefile_prop); Boolean forget_after_parse = false; static pathpt makefile_path; int n; char *path; Source source = ALLOC(Source); Property orig_makefile = makefile; Dependency *dpp; Dependency dp; 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; wchar_t *string_start; 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(Name true_makefile_name, Source source) { /* char mb_buffer[MB_LEN_MAX]; */ wchar_t *source_p; wchar_t *source_end; wchar_t *string_start; wchar_t *string_end; Boolean macro_seen_in_string; Boolean append; String_rec name_string; wchar_t name_buffer[STRING_BUFFER_LENGTH]; int distance; 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; Reader_state state; Reader_state on_eoln_state; 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++; } /* FALLTHROUGH */ 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; } /* FALLTHROUGH */ 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(Source bp, wchar_t *buffer, int size, 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); } } } } } /* * 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 2019 RackTop Systems. */ /* * 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(wchar_t *lib_start, wchar_t *member_start, wchar_t *string_end, Name_vector current_names, Name_vector *extra_names); extern Name normalize_name(wchar_t *name_string, int length); static void read_suffixes_list(Name_vector depes); static void make_relative(wchar_t *to, wchar_t *result); static void print_rule(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, wchar_t *string_start, wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen) { Name name; 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(wchar_t *lib_start, wchar_t *member_start, wchar_t *string_end, Name_vector current_names, Name_vector *extra_names) { Boolean entry = false; wchar_t buffer[STRING_BUFFER_LENGTH]; Name lib; Name member; Name name; Property prop; wchar_t *memberp; wchar_t *q; int paren_count; Boolean has_dollar; 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(wchar_t *name_string, int length) { static Name dotdot; wchar_t *string = ALLOC_WC(length + 1); wchar_t *string2; wchar_t *cdp; wchar_t *current_component; Name name; 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(Name_vector target_list, 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; /* FALLTHROUGH */ 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(Name target, Chain target_group, Name_vector depes, Cmd_line command, Separator separator) { int i; Property line; Name name; Name directory; wchar_t *namep; char *mb_namep; Dependency dp; Dependency *dpp; Property line2; wchar_t relative[MAXPATHLEN]; 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; /* FALLTHROUGH */ 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; } /* FALLTHROUGH */ 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, Name depe, Boolean automatic) { Dependency dp; 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(Name target, Chain target_group, Name_vector depes, Cmd_line command) { Percent result = ALLOC(Percent); Percent depe; Percent *depe_tail = &result->dependencies; Percent *insert; 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(Name target) { 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, Name_vector depes, Cmd_line command) { 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(Name_vector depes) { int n; Dependency dp; Dependency *insert_dep; Name np; Name np2; 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(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(Name target, Name name, Name value, Boolean append) { 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, 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); } /* * 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]; 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); } } } /* * 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(Dependency dependency, FILE *fd, Boolean built_this_run, int *line_length, 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) { FILE *fd; int lock_err; char buffer[MAXPATHLEN]; char make_state_tempfile[MAXPATHLEN]; jmp_buf long_jump; int attempts = 0; Name_set::iterator np, e; Property lines; int m; Dependency dependency; Boolean name_printed; Boolean built_this_run = false; char *target_name; int line_length; 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(Dependency dependency, FILE *fd, Boolean built_this_run, int *line_length, 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; } # # 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.$@ /* * 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 /* * 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. * * Copyright 2019, Joyent, Inc. * Copyright 2019 RackTop Systems. */ #ifndef _MK_DEFS_H #define _MK_DEFS_H /* * 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 */ /* * string built from value of * conditional macros used by * this target */ wchar_t *cond_macrostring; /* This target is no longer recursive */ Boolean removed; }; 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; extern Boolean path_reset; extern Boolean rebuild_arg0; /* * 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(Name target, Boolean do_get, Boolean implicit, Boolean automatic = false); extern Doname doname_check(Name target, Boolean do_get, Boolean implicit, Boolean automatic); extern Doname doname_parallel(Name target, Boolean do_get, Boolean implicit); extern Doname dosys(Name command, Boolean ignore_error, 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(Name target, Name name, Name value, Boolean append); extern void enter_dependencies(Name target, Chain target_group, Name_vector depes, Cmd_line command, Separator separator); extern void enter_dependency(Property line, Name depe, Boolean automatic); extern void enter_equal(Name name, Name value, Boolean append); extern Percent enter_percent(Name target, Chain target_group, Name_vector depes, Cmd_line command); extern Dyntarget enter_dyntarget(Name target); extern Name_vector enter_name(String string, Boolean tail_present, wchar_t *string_start, wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen); extern Boolean exec_vp(char *name, char **argv, char **envp, Boolean ignore_error); extern Doname execute_parallel(Property line, Boolean waitflg, Boolean local = false); extern Doname execute_serial(Property line); extern timestruc_t& exists(Name target); extern void fatal(const char *, ...) __NORETURN; extern void fatal_reader(char *, ...) __NORETURN; extern Doname find_ar_suffix_rule(Name target, Name true_target, Property *command, Boolean rechecking); extern Doname find_double_suffix_rule(Name target, Property *command, Boolean rechecking); extern Doname find_percent_rule(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(Name_vector target_list, 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(int size); 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(Name target, 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(Name value, Daemon daemon); extern timestruc_t& read_archive(Name target); extern int read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library); extern void read_directory_of_file(Name file); extern int read_make_machines(Name make_machines_name); extern Boolean read_simple_file(Name makefile_name, Boolean chase_path, 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(Name target, Property old_locals, Property conditional, int index); extern void set_locals(Name target, Property old_locals); extern void setvar_append(Name name, Name value); extern void setvar_envvar(void); extern void special_reader(Name target, Name_vector depes, Cmd_line command); extern void startup_rxm(); extern Doname target_can_be_built(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(Name cmd); extern char *make_install_prefix(void); #define DEPINFO_FMT_VERSION "VERS2$" #define VER_LEN strlen(DEPINFO_FMT_VERSION) #endif /* _MK_DEFS_H */ #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_) {} iterator(const iterator&) = default; // 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 /* * 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. */ #ifndef _MKSH_DOSYS_H #define _MKSH_DOSYS_H #include #include extern Boolean await(Boolean, Boolean, Name, wchar_t *, pid_t, void *, int); extern int doexec(wchar_t *, Boolean, char *, char *, pathpt, int); extern int doshell(wchar_t *, Boolean, char *, char *, int); extern void redirect_io(char *, char *); extern void sh_command2string(String, String); #endif /* _MKSH_DOSYS_H */ #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 #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 #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 /* * 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. */ #ifndef _MKSH_MACRO_H #define _MKSH_MACRO_H #include extern void expand_macro(Source, String, wchar_t *, Boolean); extern void expand_value(Name, String, Boolean); extern Name getvar(Name); extern Property setvar_daemon(Name, Name, Boolean, Daemon, Boolean, short); #endif /* _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 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Copyright 2019 RackTop Systems. */ #ifndef _MKSH_MISC_H #define _MKSH_MISC_H #include extern void append_char(wchar_t, String); extern Property append_prop(Name, Property_id); extern void append_string(wchar_t *, String, int); extern void enable_interrupt(void (*) (int)); extern char *errmsg(int); extern void fatal_mksh(const char *, ...) __NORETURN; extern void fatal_reader_mksh(const char *, ...) __NORETURN; extern char *get_current_path_mksh(void); extern Property get_prop(Property, Property_id); extern char *getmem(size_t); extern Name getname_fn(wchar_t *name, int len, Boolean dont_enter, Boolean *foundp = NULL); extern void store_name(Name); extern void free_name(Name); extern void handle_interrupt_mksh(int); extern Property maybe_append_prop(Name, Property_id); extern void retmem(wchar_t *); extern void retmem_mb(caddr_t); extern void setup_char_semantics(void); extern void setup_interrupt(void (*) (int)); extern void warning_mksh(char *, ...); extern void append_string(char *, String, int); extern wchar_t *get_wstring(char *); #endif /* _MKSH_MISC_H */ #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 /* * 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 _MKSH_READ_H #define _MKSH_READ_H #include extern Source get_next_block_fn(Source source); #endif /* _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 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(char *, int (*) (char *), pathpt, pathpt, rwt); 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 /* _ARGS_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. */ #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 /* * 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 *, pathpt *, int); extern void flush_path_cache(void); extern void flush_vroot_cache(void); extern const char *get_path_name(void); extern char *get_vroot_path(char **, char **, char **); extern const char *get_vroot_name(void); extern int open_vroot(char *, int, int, pathpt, pathpt); extern pathpt parse_path_string(char *, int); extern void scan_path_first(void); extern void scan_vroot_first(void); extern void set_path_style(int); extern int access_vroot(char *, int, pathpt, pathpt); extern int execve_vroot(char *, char **, char **, pathpt, pathpt); extern int lstat_vroot(char *, struct stat *, pathpt, pathpt); extern int stat_vroot(char *, struct stat *, pathpt, pathpt); extern int readlink_vroot(char *, char *, int, pathpt, pathpt); #endif /* _VROOT_H_ */ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. SUBDIRS=bsd \ makestate \ mksh \ vroot all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber _msg : TARGET = _msg .KEEP_STATE: all clean clobber install _msg: $(SUBDIRS) $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. LIBRARY = libbsd.a VERS = .1 OBJECTS = bsd.o include $(SRC)/lib/Makefile.lib include ../../Makefile.com LIBS = $(LIBRARY) SRCDIR = ../ CPPFLAGS += -D_FILE_OFFSET_BITS=64 all: $(LIBS) install: all _msg: include $(SRC)/lib/Makefile.targ /* * 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) { SIG_PF previous_handler; #ifdef sun previous_handler = sigset (Signal, Handler); #else struct sigaction new_action; 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; } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. include $(SRC)/Makefile.master SUBDIRS = $(MACH64) all : TARGET = all install : TARGET = install clean : TARGET = clean clobber : TARGET = clobber .KEEP_STATE: all clean clobber install: $(SUBDIRS) _msg: $(SUBDIRS): FRC @cd $@; pwd; $(MAKE) $(TARGET) FRC: # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. LIBRARY = libmakestate.a VERS = .1 OBJECTS = ld_file.o \ lock.o include $(SRC)/lib/Makefile.lib include ../../../Makefile.com LIBS = $(DYNLIB) SRCDIR = ../ MAPFILES = $(SRCDIR)/mapfile-vers LDLIBS += -lc all: $(LIBS) include $(SRC)/lib/Makefile.targ # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. include ../Makefile.com include $(SRC)/lib/Makefile.lib.64 install: all $(ROOTLIBS64) /* * 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(void) { 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 ld_support_init() { mk_state_init(); } /* ld_support_init() */ 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() */ /* * 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 */ } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. # # MAPFILE HEADER START # # WARNING: STOP NOW. DO NOT MODIFY THIS FILE. # Object versioning must comply with the rules detailed in # # usr/src/lib/README.mapfiles # # You should not be making modifications here until you've read the most current # copy of that file. If you need help, contact a gatekeeper for guidance. # # MAPFILE HEADER END # $mapfile_version 2 SYMBOL_VERSION ILLUMOSprivate { global: ld_atexit; ld_atexit64; ld_file; ld_file64; local: *; }; # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. # Copyright 2019 RackTop Systems. LIBRARY = libmksh.a VERS = .1 OBJECTS = dosys.o \ globals.o \ i18n.o \ macro.o \ misc.o \ mksh.o \ read.o include $(SRC)/lib/Makefile.lib include ../../Makefile.com POFILE = libmksh.po MSGFILES = $(OBJECTS:%.o=%.cc) LIBS = $(LIBRARY) SRCDIR = ../ MAPFILES= CPPFLAGS += -D_FILE_OFFSET_BITS=64 CCERRWARN += -Wno-unused-variable CCERRWARN += -Wno-unused-function CCERRWARN += -Wno-unused-value CCERRWARN += -Wno-uninitialized all: $(LIBS) install: all $(POFILE): pofile_MSGFILES _msg: $(MSGDOMAINPOFILE) include $(SRC)/lib/Makefile.targ include $(SRC)/Makefile.msg.targ /* * 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. * Copyright 2019 RackTop Systems. */ /* * 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(char *name, char **argv, char **envp, 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, 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]; Name shell = getvar(shell_name); 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(char *name, char **argv, char **envp, Boolean ignore_error, pathpt vroot_path) { Name shell = getvar(shell_name); 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(wchar_t *command, 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]; char **p; wchar_t *q; 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(Boolean ignore_error, 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; pid_t pid; struct stat stat_buff; int termination_signal; 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. */ 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(String command, String destination) { FILE *fd; int chr; int status; Boolean command_generated_output = false; command->text.p = NULL; 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)); } } /* * 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; 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 }; /* * 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); } } /* * 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, Property macro, 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(Name name) { String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; 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, String destination, Boolean cmd) { Source_rec sourceb; Source source = &sourceb; wchar_t *source_p = NULL; 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(Source source, 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]; wchar_t *source_p = source->string.text.p; wchar_t *source_end = source->string.text.end; int closer = 0; wchar_t *block_start = (wchar_t *)NULL; int quote_seen = 0; 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, Property macro, String destination, Boolean cmd) { 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(Name name, Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level) { Property macro = maybe_append_prop(name, macro_prop); Property macro_apx = get_prop(name->prop, macro_append_prop); int length = 0; String_rec destination; wchar_t buffer[STRING_BUFFER_LENGTH]; 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; } /* * 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(String string, 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(size_t size) { char *result = (char *)malloc(size); if (result == NULL) { (void) fprintf(stderr, "*** Error: malloc(%d) failed: %s\n%s", size, strerror(errno), gettext("mksh: Fatal error: Out of memory\n")); 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, int len, Boolean dont_enter, Boolean * foundp) { int length; wchar_t *cap = name; 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(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 */ char * errmsg(int errnum) { char *msg; char *errbuf; errno = 0; msg = strerror(errnum); if (errno == EINVAL) { size_t size = 6 + 1 + 11 + 1; errbuf = getmem(size); (void) snprintf(errbuf, size, gettext("Error %d"), errnum); return (errbuf); } return (msg); } 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(Name target, Property_id type) { Property *insert = &target->prop; Property prop = *insert; 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(Name target, Property_id type) { 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(Property start, 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(wchar_t *from, String to, 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(char *from, String to, 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(String string, int length) { 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, 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(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; } /* * 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); } } } /* * 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(Source source) { off_t to_read; int length; 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; } # # This file and its contents are supplied under the terms of the # Common Development and Distribution License ("CDDL"), version 1.0. # You may only use this file in accordance with the terms of version # 1.0 of the CDDL. # # A full copy of the text of the CDDL should have accompanied this # source. A copy of the CDDL is also available via the Internet at # http://www.illumos.org/license/CDDL. # # Copyright 2015, Richard Lowe. # Copyright 2019 RackTop Systems. LIBRARY = libvroot.a VERS = .1 OBJECTS = access.o \ args.o \ chdir.o \ chmod.o \ chown.o \ chroot.o \ creat.o \ execve.o \ lock.o \ lstat.o \ mkdir.o \ mount.o \ open.o \ readlink.o \ report.o \ rmdir.o \ stat.o \ truncate.o \ unlink.o \ utimes.o \ vroot.o \ setenv.o include $(SRC)/lib/Makefile.lib include ../../Makefile.com POFILE = libvroot.po POFILES = $(OBJECTS:%.o=%.po) LIBS = $(LIBRARY) SRCDIR = ../ MAPFILES= CPPFLAGS += -D_FILE_OFFSET_BITS=64 CCERRWARN += -Wno-unused-variable CCERRWARN += -Wno-parentheses all: $(LIBS) install: all $(POFILE): $(POFILES) $(CAT) $(POFILES) > $@ _msg: $(MSGDOMAIN) $(POFILE) $(RM) $(MSGDOMAIN)/$(POFILE) $(CP) $(POFILE) $(MSGDOMAIN) include $(SRC)/lib/Makefile.targ /* * 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); } /* * 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; /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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 static void file_lock_error(char *msg, char *file, const char *str, char *arg1, char * 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, "symlink(%s, %s)", name, 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, const char *str, char *arg1, char *arg2) { int len, err; char *ptr; sprintf(msg, gettext("Could not lock file `%s'; "), file); len = strlen(msg); sprintf(&msg[len], str, arg1, arg2); strcat(msg, gettext(" failed - ")); err = errno; errno = 0; ptr = strerror(err); if (errno != EINVAL) { strcat(msg, ptr); } else { len = strlen(msg); sprintf(&msg[len], "errno %d", err); } } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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) { char *filename; char buffer[MAXPATHLEN+1]; char *p; 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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); } /* * 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, pathpt *pointer, int position) { int size= 0; int length; char *name; 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(char *string, int remove_slash) { 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(char **vroot, char **path, 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(char *filename, int (*thunk) (char *), pathpt path_vector, pathpt vroot_vector, rwt rw) { pathcellt *vp; pathcellt *pp; pathcellt *pp1; 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;};}; }