From cd9f4eb76cab44d8add9d2b88b916b42ff132fcb Mon Sep 17 00:00:00 2001 From: Georg Sauthoff Date: Sun, 21 Aug 2016 11:10:30 +0200 Subject: [PATCH 1/3] import OpenSolaris CDDL license Source: curl -o COPYING 'https://raw.githubusercontent.com/illumos/illumos-gate/cbf5bfd0563daa9bd1e66ff129333add3c9ca2ea/usr/src/OPENSOLARIS.LICENSE' --- COPYING | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100644 COPYING diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..da23621 --- /dev/null +++ b/COPYING @@ -0,0 +1,384 @@ +Unless otherwise noted, all files in this distribution are released +under the Common Development and Distribution License (CDDL). +Exceptions are noted within the associated source files. + +-------------------------------------------------------------------- + + +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE Version 1.0 + +1. Definitions. + + 1.1. "Contributor" means each individual or entity that creates + or contributes to the creation of Modifications. + + 1.2. "Contributor Version" means the combination of the Original + Software, prior Modifications used by a Contributor (if any), + and the Modifications made by that particular Contributor. + + 1.3. "Covered Software" means (a) the Original Software, or (b) + Modifications, or (c) the combination of files containing + Original Software with files containing Modifications, in + each case including portions thereof. + + 1.4. "Executable" means the Covered Software in any form other + than Source Code. + + 1.5. "Initial Developer" means the individual or entity that first + makes Original Software available under this License. + + 1.6. "Larger Work" means a work which combines Covered Software or + portions thereof with code not governed by the terms of this + License. + + 1.7. "License" means this document. + + 1.8. "Licensable" means having the right to grant, to the maximum + extent possible, whether at the time of the initial grant or + subsequently acquired, any and all of the rights conveyed + herein. + + 1.9. "Modifications" means the Source Code and Executable form of + any of the following: + + A. Any file that results from an addition to, deletion from or + modification of the contents of a file containing Original + Software or previous Modifications; + + B. Any new file that contains any part of the Original + Software or previous Modifications; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. "Original Software" means the Source Code and Executable + form of computer software code that is originally released + under this License. + + 1.11. "Patent Claims" means any patent claim(s), now owned or + hereafter acquired, including without limitation, method, + process, and apparatus claims, in any patent Licensable by + grantor. + + 1.12. "Source Code" means (a) the common form of computer software + code in which modifications are made and (b) associated + documentation included in or with such code. + + 1.13. "You" (or "Your") means an individual or a legal entity + exercising rights under, and complying with all of the terms + of, this License. For legal entities, "You" includes any + entity which controls, is controlled by, or is under common + control with You. For purposes of this definition, + "control" means (a) the power, direct or indirect, to cause + the direction or management of such entity, whether by + contract or otherwise, or (b) ownership of more than fifty + percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the Initial + Developer hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Initial Developer, to use, + reproduce, modify, display, perform, sublicense and + distribute the Original Software (or portions thereof), + with or without Modifications, and/or as part of a Larger + Work; and + + (b) under Patent Claims infringed by the making, using or + selling of Original Software, to make, have made, use, + practice, sell, and offer for sale, and/or otherwise + dispose of the Original Software (or portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) are + effective on the date Initial Developer first distributes + or otherwise makes the Original Software available to a + third party under the terms of this License. + + (d) Notwithstanding Section 2.1(b) above, no patent license is + granted: (1) for code that You delete from the Original + Software, or (2) for infringements caused by: (i) the + modification of the Original Software, or (ii) the + combination of the Original Software with other software + or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than patent or + trademark) Licensable by Contributor to use, reproduce, + modify, display, perform, sublicense and distribute the + Modifications created by such Contributor (or portions + thereof), either on an unmodified basis, with other + Modifications, as Covered Software and/or as part of a + Larger Work; and + + (b) under Patent Claims infringed by the making, using, or + selling of Modifications made by that Contributor either + alone and/or in combination with its Contributor Version + (or portions of such combination), to make, use, sell, + offer for sale, have made, and/or otherwise dispose of: + (1) Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications made by + that Contributor with its Contributor Version (or portions + of such combination). + + (c) The licenses granted in Sections 2.2(a) and 2.2(b) are + effective on the date Contributor first distributes or + otherwise makes the Modifications available to a third + party. + + (d) Notwithstanding Section 2.2(b) above, no patent license is + granted: (1) for any code that Contributor has deleted + from the Contributor Version; (2) for infringements caused + by: (i) third party modifications of Contributor Version, + or (ii) the combination of Modifications made by that + Contributor with other software (except as part of the + Contributor Version) or other devices; or (3) under Patent + Claims infringed by Covered Software in the absence of + Modifications made by that Contributor. + +3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in Source + Code form and that Source Code form must be distributed only under + the terms of this License. You must include a copy of this + License with every copy of the Source Code form of the Covered + Software You distribute or otherwise make available. You must + inform recipients of any such Covered Software in Executable form + as to how they can obtain such Covered Software in Source Code + form in a reasonable manner on or through a medium customarily + used for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You contribute are + governed by the terms of this License. You represent that You + believe Your Modifications are Your original creation(s) and/or + You have sufficient rights to grant the rights conveyed by this + License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications that + identifies You as the Contributor of the Modification. You may + not remove or alter any copyright, patent or trademark notices + contained within the Covered Software, or any notices of licensing + or any descriptive text giving attribution to any Contributor or + the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered Software in + Source Code form that alters or restricts the applicable version + of this License or the recipients' rights hereunder. You may + choose to offer, and to charge a fee for, warranty, support, + indemnity or liability obligations to one or more recipients of + Covered Software. However, you may do so only on Your own behalf, + and not on behalf of the Initial Developer or any Contributor. + You must make it absolutely clear that any such warranty, support, + indemnity or liability obligation is offered by You alone, and You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial Developer or + such Contributor as a result of warranty, support, indemnity or + liability terms You offer. + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered Software + under the terms of this License or under the terms of a license of + Your choice, which may contain terms different from this License, + provided that You are in compliance with the terms of this License + and that the license for the Executable form does not attempt to + limit or alter the recipient's rights in the Source Code form from + the rights set forth in this License. If You distribute the + Covered Software in Executable form under a different license, You + must make it absolutely clear that any terms which differ from + this License are offered by You alone, not by the Initial + Developer or Contributor. You hereby agree to indemnify the + Initial Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of any + such terms You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software with + other code not governed by the terms of this License and + distribute the Larger Work as a single product. In such a case, + You must make sure the requirements of this License are fulfilled + for the Covered Software. + +4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and may + publish revised and/or new versions of this License from time to + time. Each version will be given a distinguishing version number. + Except as provided in Section 4.3, no one other than the license + steward has the right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise make the + Covered Software available under the terms of the version of the + License under which You originally received the Covered Software. + If the Initial Developer includes a notice in the Original + Software prohibiting it from being distributed or otherwise made + available under any subsequent version of the License, You must + distribute and make the Covered Software available under the terms + of the version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to use, + distribute or otherwise make the Covered Software available under + the terms of any subsequent version of the License published by + the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a new + license for Your Original Software, You may create and use a + modified version of this License if You: (a) rename the license + and remove any references to the name of the license steward + (except to note that the license differs from this License); and + (b) otherwise make it clear that the license contains terms which + differ from this License. + +5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY + NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + +6. TERMINATION. + + 6.1. This License and the rights granted hereunder will terminate + automatically if You fail to comply with terms herein and fail to + cure such breach within 30 days of becoming aware of the breach. + Provisions which, by their nature, must remain in effect beyond + the termination of this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or a + Contributor (the Initial Developer or Contributor against whom You + assert such claim is referred to as "Participant") alleging that + the Participant Software (meaning the Contributor Version where + the Participant is a Contributor or the Original Software where + the Participant is the Initial Developer) directly or indirectly + infringes any patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial Developer (if + the Initial Developer is not the Participant) and all Contributors + under Sections 2.1 and/or 2.2 of this License shall, upon 60 days + notice from Participant terminate prospectively and automatically + at the expiration of such 60 day notice period, unless if within + such 60 day period You withdraw Your claim with respect to the + Participant Software against such Participant either unilaterally + or pursuant to a written agreement with Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 above, + all end user licenses that have been validly granted by You or any + distributor hereunder prior to termination (excluding licenses + granted to You by any distributor) shall survive termination. + +7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + +8. U.S. GOVERNMENT END USERS. + + The Covered Software is a "commercial item," as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial + computer software" (as that term is defined at 48 + C.F.R. 252.227-7014(a)(1)) and "commercial computer software + documentation" as such terms are used in 48 C.F.R. 12.212 + (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 + C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all + U.S. Government End Users acquire Covered Software with only those + rights set forth herein. This U.S. Government Rights clause is in + lieu of, and supersedes, any other FAR, DFAR, or other clause or + provision that addresses Government rights in computer software + under this License. + +9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the extent + necessary to make it enforceable. This License shall be governed + by the law of the jurisdiction specified in a notice contained + within the Original Software (except to the extent applicable law, + if any, provides otherwise), excluding such jurisdiction's + conflict-of-law provisions. Any litigation relating to this + License shall be subject to the jurisdiction of the courts located + in the jurisdiction and venue specified in a notice contained + within the Original Software, with the losing party responsible + for costs, including, without limitation, court costs and + reasonable attorneys' fees and expenses. The application of the + United Nations Convention on Contracts for the International Sale + of Goods is expressly excluded. Any law or regulation which + provides that the language of a contract shall be construed + against the drafter shall not apply to this License. You agree + that You alone are responsible for compliance with the United + States export administration regulations (and the export control + laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + +10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. + +-------------------------------------------------------------------- + +NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND +DISTRIBUTION LICENSE (CDDL) + +For Covered Software in this distribution, this License shall +be governed by the laws of the State of California (excluding +conflict-of-law provisions). + +Any litigation relating to this License shall be subject to the +jurisdiction of the Federal Courts of the Northern District of +California and the state courts of the State of California, with +venue lying in Santa Clara County, California. From c2f5d8001b6a37959d987e5d8d3d85db597d44e8 Mon Sep 17 00:00:00 2001 From: Georg Sauthoff Date: Sun, 21 Aug 2016 11:15:00 +0200 Subject: [PATCH 2/3] Import man page Source: curl -OL https://github.com/illumos/illumos-gate/raw/10d63b7db37a83b39c7f511cf9426c9d03ea0760/usr/src/man/man1/make.1 --- man/man1/make.1 | 2788 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2788 insertions(+) create mode 100644 man/man1/make.1 diff --git a/man/man1/make.1 b/man/man1/make.1 new file mode 100644 index 0000000..d0cbd29 --- /dev/null +++ b/man/man1/make.1 @@ -0,0 +1,2788 @@ +'\" te +.\" Copyright 1989 AT&T +.\" Portions Copyright (c) 1992, X/Open Company Limited All Rights Reserved +.\" Copyright (c) 2008, Sun Microsystems, Inc. All Rights Reserved +.\" Sun Microsystems, Inc. gratefully acknowledges The Open Group for permission to reproduce portions of its copyrighted documentation. Original documentation from The Open Group can be obtained online at +.\" http://www.opengroup.org/bookstore/. +.\" The Institute of Electrical and Electronics Engineers and The Open Group, have given us permission to reprint portions of their documentation. In the following statement, the phrase "this text" refers to portions of the system documentation. Portions of this text are reprinted and reproduced in electronic form in the Sun OS Reference Manual, from IEEE Std 1003.1, 2004 Edition, Standard for Information Technology -- Portable Operating System Interface (POSIX), The Open Group Base Specifications Issue 6, Copyright (C) 2001-2004 by the Institute of Electrical and Electronics Engineers, Inc and The Open Group. In the event of any discrepancy between these versions and the original IEEE and The Open Group Standard, the original IEEE and The Open Group Standard is the referee document. The original Standard can be obtained online at http://www.opengroup.org/unix/online.html. +.\" This notice shall appear on any product containing this material. +.\" 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] +.TH MAKE 1 "Jun 24, 2015" +.SH NAME +make \- maintain, update, and regenerate related programs and files +.SH SYNOPSIS +.LP +.nf +\fB/usr/bin/make\fR [\fB-d\fR] [\fB-dd\fR] [\fB-D\fR] [\fB-DD\fR] [\fB-e\fR] + [\fB-i\fR] [\fB-j\fR \FImaxjobs\fR] [\fB-k\fR] [\fB-m\fR \fI{serial | parallel}\fR] + [\fB-n\fR] [\fB-p\fR] [\fB-P\fR] [\fB-q\fR] [\fB-r\fR] [\fB-s\fR] [\fB-S\fR] [\fB-t\fR] [\fB-V\fR] + [\fB-f\fR \fImakefile\fR]... [\fB-K\fR \fIstatefile\fR]... [\fItarget\fR]... + [\fImacro\fR = \fIvalue\fR...] +.fi + +.nf +\fB/usr/bin/dmake\fR [\fB-d\fR] [\fB-dd\fR] [\fB-D\fR] [\fB-DD\fR] [\fB-e\fR] + [\fB-i\fR] [\fB-j\fR \FImaxjobs\fR] [\fB-k\fR] [\fB-m\fR \fI{serial | parallel}\fR] + [\fB-n\fR] [\fB-p\fR] [\fB-P\fR] [\fB-q\fR] [\fB-r\fR] [\fB-s\fR] [\fB-S\fR] [\fB-t\fR] [\fB-V\fR] + [\fB-f\fR \fImakefile\fR]... [\fB-K\fR \fIstatefile\fR]... [\fItarget\fR]... + [\fImacro\fR = \fIvalue\fR...] +.fi + +.LP +.nf +\fB/usr/xpg4/bin/make\fR [\fB-d\fR] [\fB-dd\fR] [\fB-D\fR] [\fB-DD\fR] + [\fB-e\fR] [\fB-i\fR] [\fB-j\fR \FImaxjobs\fR] [\fB-k\fR] [\fB-m\fR \fI{serial | parallel}\fR] + [\fB-n\fR] [\fB-p\fR] [\fB-P\fR] [\fB-q\fR] [\fB-r\fR] [\fB-s\fR] [\fB-S\fR] [\fB-t\fR] [\fB-V\fR] + [\fB-f\fR \fImakefile\fR]... [\fItarget\fR]... [\fImacro\fR = \fIvalue\fR...] +.fi + +.SH DESCRIPTION +.LP +The \fBmake\fR utility executes a list of shell commands associated with each +\fItarget\fR, typically to create or update a file of the same name. +\fImakefile\fR contains entries that describe how to bring a target up to date +with respect to those on which it depends, which are called \fIdependencies\fR. +Since each dependency is a target, it can have dependencies of its own. +Targets, dependencies, and sub-dependencies comprise a tree structure that +\fBmake\fR traces when deciding whether or not to rebuild a \fItarget\fR. +.sp +.LP +The \fBmake\fR utility recursively checks each \fItarget\fR against its +dependencies, beginning with the first target entry in \fImakefile\fR if no +\fItarget\fR argument is supplied on the command line. If, after processing all +of its dependencies, a target file is found either to be missing, or to be +older than any of its dependencies, \fBmake\fR rebuilds it. Optionally with +this version of \fBmake\fR, a target can be treated as out-of-date when the +commands used to generate it have changed since the last time the target was +built. +.sp +.LP +To build a given target, \fBmake\fR executes the list of commands, called a +\fIrule\fR. This rule can be listed explicitly in the target's makefile entry, +or it can be supplied implicitly by \fBmake\fR. +.sp +.LP +If no \fItarget\fR is specified on the command line, \fBmake\fR uses the first +target defined in \fImakefile\fR. +.sp +.LP +If a \fItarget\fR has no makefile entry, or if its entry has no rule, +\fBmake\fR attempts to derive a rule by each of the following methods, in turn, +until a suitable rule is found. Each method is described under Usage below. +.RS +4 +.TP +.ie t \(bu +.el o +Pattern matching rules. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +Implicit rules, read in from a user-supplied makefile. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +Standard implicit rules (also known as suffix rules), typically read in from +the file \fB/usr/share/lib/make/make.rules\fR. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +\fBSCCS\fR retrieval. \fBmake\fR retrieves the most recent version from the +\fBSCCS\fR history file (if any). See the description of the +\fB\&.SCCS_GET:\fR special-function target for details. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +The rule from the \fB\&.DEFAULT:\fR target entry, if there is such an entry in +the makefile. +.RE +.sp +.LP +If there is no makefile entry for a \fItarget\fR, if no rule can be derived for +building it, and if no file by that name is present, \fBmake\fR issues an error +message and halts. +.SH OPTIONS +.LP +The following options are supported: +.sp +.ne 2 +.na +\fB\fB-d\fR\fR +.ad +.RS 16n +Displays the reasons why \fBmake\fR chooses to rebuild a target. \fBmake\fR +displays any and all dependencies that are newer. In addition, \fBmake\fR +displays options read in from the \fBMAKEFLAGS\fR environment variable. +.RE + +.sp +.ne 2 +.na +\fB\fB-dd\fR\fR +.ad +.RS 16n +Displays the dependency check and processing in vast detail. +.RE + +.sp +.ne 2 +.na +\fB\fB-D\fR\fR +.ad +.RS 16n +Displays the text of the makefiles read in. +.RE + +.sp +.ne 2 +.na +\fB\fB-DD\fR\fR +.ad +.RS 16n +Displays the text of the makefiles, \fBmake.rules\fR file, the state file, and +all hidden-dependency reports. +.RE + +.sp +.ne 2 +.na +\fB\fB-e\fR\fR +.ad +.RS 16n +Environment variables override assignments within makefiles. +.RE + +.sp +.ne 2 +.na +\fB\fB-f\fR \fImakefile\fR\fR +.ad +.RS 16n +Uses the description file \fImakefile\fR. A \fB\(mi\fR as the \fImakefile\fR +argument denotes the standard input. The contents of \fImakefile\fR, when +present, override the standard set of implicit rules and predefined macros. +When more than one \fB-f\fR \fImakefile\fR argument pair appears, \fBmake\fR +uses the concatenation of those files, in order of appearance. +.sp +When no \fImakefile\fR is specified, \fB/usr/bin/make\fR tries the following in +sequence, except when in POSIX mode (see \fB\&.POSIX\fR in Usage): +.RS +4 +.TP +.ie t \(bu +.el o +If there is a file named \fBmakefile\fR in the working directory, \fBmake\fR +uses that file. If, however, there is an \fBSCCS\fR history file +(\fBSCCS/s.makefile\fR) which is newer, \fBmake\fR attempts to retrieve and use +the most recent version. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +In the absence of the above file(s), if a file named \fBMakefile\fR is present +in the working directory, \fBmake\fR attempts to use it. If there is an +\fBSCCS\fR history file (\fBSCCS/s.Makefile\fR) that is newer, \fBmake\fR +attempts to retrieve and use the most recent version. +.RE +When no \fImakefile\fR is specified, \fB/usr/bin/make\fR in POSIX mode and +\fB/usr/xpg4/bin/make\fR try the following files in sequence: +.RS +4 +.TP +.ie t \(bu +.el o +\fB\&./makefile\fR, \fB\&./Makefile\fR +.RE +.RS +4 +.TP +.ie t \(bu +.el o +\fBs.makefile\fR, \fBSCCS/s.makefile\fR +.RE +.RS +4 +.TP +.ie t \(bu +.el o +\fBs.Makefile\fR, \fBSCCS/s.Makefile\fR +.RE +.RE + +.sp +.ne 2 +.na +\fB\fB-i\fR\fR +.ad +.RS 16n +Ignores error codes returned by commands. Equivalent to the special-function +target \fB\&.IGNORE:\fR. +.RE + +.sp +.ne 2 +.na +\fB-j\fR \fImaxjobs\fR +.ad +.RS 16n +Specify the maximum number of jobs that each instance of \fBmake\fR will invoke. +.RE + +.sp +.ne 2 +.na +\fB\fB-k\fR\fR +.ad +.RS 16n +When a nonzero error status is returned by a rule, or when \fBmake\fR cannot +find a rule, abandons work on the current target, but continues with other +dependency branches that do not depend on it. +.RE + +.sp +.ne 2 +.na +\fB\fB-K\fR \fIstatefile\fR\fR +.ad +.RS 16n +Uses the state file \fIstatefile\fR. A \fB\(mi\fR as the \fIstatefile\fR +argument denotes the standard input. The contents of \fIstatefile\fR, when +present, override the standard set of implicit rules and predefined macros. +When more than one \fB-K\fR \fIstatefile\fR argument pair appears, \fBmake\fR +uses the concatenation of those files, in order of appearance. (See also +\fB\&.KEEP_STATE\fR and \fB\&.KEEP_STATE_FILE\fR in the Special-Function +Targets section). +.RE + +.sp +.ne 2 +.na +\fB-m\fR \fI{serial | parallel}\fR +.ad +.RS 16n +Specify whether \fBmake\fR should execute jobs serially or in parallel. The +default for \fBmake\fR is to run serially unless \fB-j\fR is specified. The +default is for \fBdmake\fR is to run in parallel. +.RE + +.sp +.ne 2 +.na +\fB\fB-n\fR\fR +.ad +.RS 16n +No execution mode. Prints commands, but does not execute them. Even lines +beginning with an \fB@\fR are printed. However, if a command line contains a +reference to the \fB$(MAKE)\fR macro, that line is always executed (see the +discussion of \fBMAKEFLAGS\fR in Reading Makefiles and the Environment). When +in POSIX mode, lines beginning with a "\fB+\fR" are executed. +.RE + +.sp +.ne 2 +.na +\fB\fB-p\fR\fR +.ad +.RS 16n +Prints out the complete set of macro definitions and target descriptions. +.RE + +.sp +.ne 2 +.na +\fB\fB-P\fR\fR +.ad +.RS 16n +Merely reports dependencies, rather than building them. +.RE + +.sp +.ne 2 +.na +\fB\fB-q\fR\fR +.ad +.RS 16n +Question mode. \fBmake\fR returns a zero or nonzero status code depending on +whether or not the target file is up to date. When in POSIX mode, lines +beginning with a "\fB+\fR" are executed. +.RE + +.sp +.ne 2 +.na +\fB\fB-r\fR\fR +.ad +.RS 16n +Does not read in the default makefile \fB/usr/share/lib/make/make.rules\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB-s\fR\fR +.ad +.RS 16n +Silent mode. Does not print command lines before executing them. Equivalent to +the special-function target \fB\&.SILENT:\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB-S\fR\fR +.ad +.RS 16n +Undoes the effect of the \fB-k\fR option. Stops processing when a non-zero exit +status is returned by a command. +.RE + +.sp +.ne 2 +.na +\fB\fB-t\fR\fR +.ad +.RS 16n +Touches the target files (bringing them up to date) rather than performing +their rules. \fBWarning:\fR This can be \fBdangerous\fR when files are +maintained by more than one person. When the \fB\&.KEEP_STATE:\fR target +appears in the makefile, this option updates the state file just as if the +rules had been performed. When in POSIX mode, lines beginning with a "\fB+\fR" +are executed. +.RE + +.sp +.ne 2 +.na +\fB\fB-V\fR\fR +.ad +.RS 16n +Puts \fBmake\fR into SysV mode. Refer to \fBsysV-make\fR(1) for respective +details. +.RE + +.SH OPERANDS +.LP +The following operands are supported: +.sp +.ne 2 +.na +\fB\fItarget\fR\fR +.ad +.RS 15n +Target names, as defined in Usage. +.RE + +.sp +.ne 2 +.na +\fB\fImacro\fR\fB=\fR\fIvalue\fR\fR +.ad +.RS 15n +Macro definition. This definition overrides any regular definition for the +specified macro within the makefile itself, or in the environment. However, +this definition can still be overridden by conditional macro assignments. +.RE + +.SH USAGE +.LP +The usage of \fBmake\fR is described below: +.SS "Reading Makefiles and the Environment" +.LP +When \fBmake\fR first starts, it reads the \fBMAKEFLAGS\fR environment variable +to obtain any of the following options specified present in its value: +\fB-d\fR, \fB-D\fR, \fB-e\fR, \fB-i\fR, \fB-k\fR, \fB-n\fR, \fB-p\fR, \fB-q\fR, +\fB-r\fR, \fB-s\fR, \fB-S\fR, or \fB-t\fR. Due to the implementation of POSIX.2 +(see \fBPOSIX.2\fR(5), the \fBMAKEFLAGS\fR values contains a leading \fB\(mi\fR +character. The \fBmake\fR utility then reads the command line for additional +options, which also take effect. +.sp +.LP +Next, \fBmake\fR reads in a default makefile that typically contains predefined +macro definitions, target entries for implicit rules, and additional rules, +such as the rule for retrieving \fBSCCS\fR files. If present, \fBmake\fR uses +the file \fBmake.rules\fR in the current directory; otherwise it reads the file +\fB/usr/share/lib/make/make.rules\fR, which contains the standard definitions +and rules. Use the directive: +.sp +.in +2 +.nf +\fBinclude /usr/share/lib/make/make.rules\fR +.fi +.in -2 +.sp + +.sp +.LP +in your local \fBmake.rules\fR file to include them. +.sp +.LP +Next, \fBmake\fR imports variables from the environment (unless the \fB-e\fR +option is in effect), and treats them as defined macros. Because \fBmake\fR +uses the most recent definition it encounters, a macro definition in the +makefile normally overrides an environment variable of the same name. When +\fB-e\fR is in effect, however, environment variables are read in \fBafter\fR +all makefiles have been read. In that case, the environment variables take +precedence over definitions in the makefile. +.sp +.LP +Next, \fBmake\fR reads any makefiles you specify with \fB-f\fR, or one of +\fBmakefile\fR or \fBMakefile\fR as described above and then the state file, in +the local directory if it exists. If the makefile contains a +\fB\&.KEEP_STATE_FILE\fR target, then it reads the state file that follows the +target. Refer to special target \fB\&.KEEP_STATE_FILE\fR for details. +.sp +.LP +Next (after reading the environment if \fB-e\fR is in effect), \fBmake\fR reads +in any macro definitions supplied as command line arguments. These override +macro definitions in the makefile and the environment both, but only for the +\fBmake\fR command itself. +.sp +.LP +\fBmake\fR exports environment variables, using the most recently defined +value. Macro definitions supplied on the command line are not normally +exported, unless the macro is also an environment variable. +.sp +.LP +\fBmake\fR does not export macros defined in the makefile. If an environment +variable is set, and a macro with the same name is defined on the command line, +\fBmake\fR exports its value as defined on the command line. Unless \fB-e\fR is +in effect, macro definitions within the makefile take precedence over those +imported from the environment. +.sp +.LP +The macros \fBMAKEFLAGS\fR, \fBMAKE\fR, \fBSHELL\fR, \fBHOST_ARCH\fR, +\fBHOST_MACH\fR, and \fBTARGET_MACH\fR are special cases. See Special-Purpose +Macros below for details. +.SS "Makefile Target Entries" +.LP +A target entry has the following format: +.sp +.in +2 +.nf + \fItarget\fR [\fB:\fR|\fB::\fR] [\fIdependency\fR] ... [\fB;\fR \fBcommand\fR] ... + [\fBcommand\fR] + ... +.fi +.in -2 + +.sp +.LP +The first line contains the name of a target, or a space-separated list of +target names, terminated with a colon or double colon. If a list of targets is +given, this is equivalent to having a separate entry of the same form for each +target. The colon(s) can be followed by a \fIdependency\fR, or a dependency +list. \fBmake\fR checks this list before building the target. The dependency +list can be terminated with a semicolon (\fB;\fR), which in turn can be +followed by a single Bourne shell command. Subsequent lines in the target entry +begin with a \fBTAB\fR and contain Bourne shell commands. These commands +comprise the rule for building the target. +.sp +.LP +Shell commands can be continued across input lines by escaping the +\fBNEWLINE\fR with a backslash (\fB\e\fR). The continuing line must also start +with a \fBTAB\fR. +.sp +.LP +To rebuild a target, \fBmake\fR expands macros, strips off initial \fBTAB\fR +characters and either executes the command directly (if it contains no shell +metacharacters), or passes each command line to a Bourne shell for execution. +.sp +.LP +The first \fInon-empty\fR line that does not begin with a \fBTAB\fR or \fB#\fR +begins another target or macro definition. +.SS "Special Characters" +.LP +Special characters are defined below. +.SS "\fIGlobal\fR" +.ne 2 +.na +\fB\fB#\fR\fR +.ad +.RS 20n +Start a comment. The comment ends at the next \fBNEWLINE\fR. If the \fB#\fR +follows the \fBTAB\fR in a command line, that line is passed to the shell +(which also treats \fB#\fR as the start of a comment). +.RE + +.sp +.ne 2 +.na +\fB\fBinclude\fR \fIfilename\fR\fR +.ad +.RS 20n +If the word \fBinclude\fR appears as the first seven letters of a line and is +followed by a \fBSPACE\fR or \fBTAB\fR, the string that follows is taken as a +filename to interpolate at that line. \fBinclude\fR files can be nested to a +depth of no more than 38 nested makefiles . If \fIfilename\fR is a macro +reference, it is expanded. +.RE + +.SS "\fITargets and Dependencies\fR" +.ne 2 +.na +\fB\fB:\fR\fR +.ad +.sp .6 +.RS 4n +Target list terminator. Words following the colon are added to the dependency +list for the target or targets. If a target is named in more than one +colon-terminated target entry, the dependencies for all its entries are added +to form that target's complete dependency list. +.RE + +.sp +.ne 2 +.na +\fB\fB::\fR\fR +.ad +.sp .6 +.RS 4n +Target terminator for alternate dependencies. When used in place of a \fB:\fR +the double-colon allows a target to be checked and updated with respect to +alternate dependency lists. When the target is out-of-date with respect to +dependencies listed in the first alternate, it is built according to the rule +for that entry. When out-of-date with respect to dependencies in another +alternate, it is built according the rule in that other entry. Implicit rules +do not apply to double-colon targets; you must supply a rule for each entry. If +no dependencies are specified, the rule is always performed. +.RE + +.sp +.ne 2 +.na +\fB\fItarget\fR [\fB+\fR \fItarget\fR.\|.\|.\|] \fB:\fR\fR +.ad +.sp .6 +.RS 4n +Target group. The rule in the target entry builds all the indicated targets as +a group. It is normally performed only once per \fBmake\fR run, but is checked +for command dependencies every time a target in the group is encountered in the +dependency scan. +.RE + +.sp +.ne 2 +.na +\fB\fB%\fR\fR +.ad +.sp .6 +.RS 4n +Pattern matching wild card metacharacter. Like the \fB*\fR shell wild card, +\fB%\fR matches any string of zero or more characters in a target name or +dependency, in the target portion of a conditional macro definition, or within +a pattern replacement macro reference. Notice that only one \fB%\fR can appear +in a target, dependency-name, or pattern-replacement macro reference. +.RE + +.sp +.ne 2 +.na +\fB\fB\&./\fR\fIpathname\fR\fR +.ad +.sp .6 +.RS 4n +\fBmake\fR ignores the leading \fB\&./\fR characters from targets with names +given as pathnames relative to "dot," the working directory. +.RE + +.SS "\fIMacros\fR" +.ne 2 +.na +\fB\fB=\fR\fR +.ad +.RS 9n +Macro definition. The word to the left of this character is the macro name; +words to the right comprise its value. Leading and trailing white space +characters are stripped from the value. A word break following the \fB=\fR is +implied. +.RE + +.sp +.ne 2 +.na +\fB\fB$\fR\fR +.ad +.RS 9n +Macro reference. The following character, or the parenthesized or bracketed +string, is interpreted as a macro reference: \fBmake\fR expands the reference +(including the \fB$\fR) by replacing it with the macro's value. +.RE + +.sp +.ne 2 +.na +\fB\fB( )\fR\fR +.ad +.br +.na +\fB\fB{ }\fR\fR +.ad +.RS 9n +Macro-reference name delimiters. A parenthesized or bracketed word appended to +a \fB$\fR is taken as the name of the macro being referred to. Without the +delimiters, \fBmake\fR recognizes only the first character as the macro name. +.RE + +.sp +.ne 2 +.na +\fB\fB$$\fR\fR +.ad +.RS 9n +A reference to the dollar-sign macro, the value of which is the character +\fB$\fR. Used to pass variable expressions beginning with \fB$\fR to the shell, +to refer to environment variables which are expanded by the shell, or to delay +processing of dynamic macros within the dependency list of a target, until that +target is actually processed. +.RE + +.sp +.ne 2 +.na +\fB\fB\e$\fR\fR +.ad +.RS 9n +Escaped dollar-sign character. Interpreted as a literal dollar sign within a +rule. +.RE + +.sp +.ne 2 +.na +\fB\fB+=\fR\fR +.ad +.RS 9n +When used in place of \fB=\fR, appends a string to a macro definition (must be +surrounded by white space, unlike \fB=\fR). +.RE + +.sp +.ne 2 +.na +\fB\fB:=\fR\fR +.ad +.RS 9n +Conditional macro assignment. When preceded by a list of targets with explicit +target entries, the macro definition that follows takes effect when processing +only those targets, and their dependencies. +.RE + +.sp +.ne 2 +.na +\fB\fB:sh\fR \fB=\fR\fR +.ad +.RS 9n +Define the value of a macro to be the output of a command (see Command +Substitutions below). +.RE + +.sp +.ne 2 +.na +\fB\fB:sh\fR\fR +.ad +.RS 9n +In a macro reference, execute the command stored in the macro, and replace the +reference with the output of that command (see \fBCommand Substitutions\fR +below). +.RE + +.SS "\fIRules\fR" +.ne 2 +.na +\fB\fB+\fR\fR +.ad +.RS 8n +\fBmake\fR always executes the commands preceded by a "\fB+\fR", even when +\fB-n\fR is specified. +.RE + +.sp +.ne 2 +.na +\fB\fB\(mi\fR\fR +.ad +.RS 8n +\fBmake\fR ignores any nonzero error code returned by a command line for which +the first non-\fBTAB\fR character is a \fB\(mi\fR\&. This character is not +passed to the shell as part of the command line. \fBmake\fR normally terminates +when a command returns nonzero status, unless the \fB-i\fR or \fB-k\fR options, +or the \fB\&.IGNORE:\fR special-function target is in effect. +.RE + +.sp +.ne 2 +.na +\fB\fB@\fR\fR +.ad +.RS 8n +If the first non-\fBTAB\fR character is a \fB@\fR, \fBmake\fR does not print +the command line before executing it. This character is not passed to the +shell. +.RE + +.sp +.ne 2 +.na +\fB\fB?\fR\fR +.ad +.RS 8n +Escape command-dependency checking. Command lines starting with this character +are not subject to command dependency checking. +.RE + +.sp +.ne 2 +.na +\fB\fB!\fR\fR +.ad +.RS 8n +Force command-dependency checking. Command-dependency checking is applied to +command lines for which it would otherwise be suppressed. This checking is +normally suppressed for lines that contain references to the \fB?\fR dynamic +macro (for example, \fB$?\fR). +.sp +When any combination of \fB+\fR, \fB\(mi\fR, \fB@\fR, \fB?\fR, or \fB!\fR +appear as the first characters after the \fBTAB\fR, all that are present apply. +None are passed to the shell. +.RE + +.SS "Special-Function Targets" +.LP +When incorporated in a makefile, the following target names perform +special-functions: +.sp +.ne 2 +.na +\fB\fB\&.DEFAULT:\fR\fR +.ad +.RS 23n +If it has an entry in the makefile, the rule for this target is used to process +a target when there is no other entry for it, no rule for building it, and no +\fBSCCS\fR history file from which to retrieve a current version. \fBmake\fR +ignores any dependencies for this target. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.DONE:\fR\fR +.ad +.RS 23n +If defined in the makefile, \fBmake\fR processes this target and its +dependencies after all other targets are built. This target is also performed +when \fBmake\fR halts with an error, unless the \fB\&.FAILED\fR target is +defined. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.FAILED:\fR\fR +.ad +.RS 23n +This target, along with its dependencies, is performed instead of \fB\&.DONE\fR +when defined in the makefile and \fBmake\fR halts with an error. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.GET_POSIX:\fR\fR +.ad +.RS 23n +This target contains the rule for retrieving the current version of an +\fBSCCS\fR file from its history file in the current working directory. +\fBmake\fR uses this rule when it is running in POSIX mode. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.IGNORE:\fR\fR +.ad +.RS 23n +Ignore errors. When this target appears in the makefile, \fBmake\fR ignores +non-zero error codes returned from commands. When used in POSIX mode, +\fB\&.IGNORE\fR could be followed by target names only, for which the errors is +ignored. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.INIT:\fR\fR +.ad +.RS 23n +If defined in the makefile, this target and its dependencies are built before +any other targets are processed. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.KEEP_STATE:\fR\fR +.ad +.RS 23n +If this target is in effect, \fBmake\fR updates the state file, +\fB\&.make.state\fR, in the current directory. This target also activates +command dependencies, and hidden dependency checks. If either the +\fB\&.KEEP_STATE:\fR target appears in the makefile, or the environment +variable \fBKEEP_STATE\fR is set (\fBsetenv KEEP_STATE\fR), \fBmake\fR rebuilds +everything in order to collect dependency information, even if all the targets +were up to date due to previous \fBmake\fR runs. See also the Environment +Variables section. This target has no effect if used in POSIX mode. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.KEEP_STATE_FILE:\fR\fR +.ad +.RS 23n +This target has no effect if used in POSIX mode. This target implies +\fB\&.KEEP_STATE\fR. If the target is followed by a filename, \fBmake\fR uses +it as the state file. If the target is followed by a directory name, \fBmake\fR +looks for a \fB\&.make.state\fR file in that directory. If the target is not +followed by any name, \fBmake\fR looks for \fB\&.make.state\fR file in the +current working directory. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.MAKE_VERSION:\fR\fR +.ad +.RS 23n +A target-entry of the form: +.sp +.in +2 +.nf +\&.MAKE_VERSION: VERSION\(mi\fInumber\fR +.fi +.in -2 + +enables version checking. If the version of \fBmake\fR differs from the version +indicated by a string like \fBVERSION-1.0\fR, \fBmake\fR issues a warning +message. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.NO_PARALLEL:\fR\fR +.ad +.RS 23n +Dependencies of this target will be executed serially. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.PARALLEL:\fR\fR +.ad +.RS 23n +Dependencies of this target will be executed in parallel. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.POSIX:\fR\fR +.ad +.RS 23n +This target enables POSIX mode. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.PRECIOUS:\fR\fR +.ad +.RS 23n +List of files not to delete. \fBmake\fR does not remove any of the files listed +as dependencies for this target when interrupted. \fBmake\fR normally removes +the current target when it receives an interrupt. When used in POSIX mode, if +the target is not followed by a list of files, all the file are assumed +precious. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.SCCS_GET:\fR\fR +.ad +.RS 23n +This target contains the rule for retrieving the current version of an +\fBSCCS\fR file from its history file. To suppress automatic retrieval, add an +entry for this target with an empty rule to your makefile. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.SCCS_GET_POSIX:\fR\fR +.ad +.RS 23n +This target contains the rule for retrieving the current version of an +\fBSCCS\fR file from its history file. \fBmake\fR uses this rule when it is +running in POSIX mode. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.SILENT:\fR\fR +.ad +.RS 23n +Run silently. When this target appears in the makefile, \fBmake\fR does not +echo commands before executing them. When used in POSIX mode, it could be +followed by target names, and only those are executed silently. +.RE + +.sp +.ne 2 +.na +\fB\fB\&.SUFFIXES:\fR\fR +.ad +.RS 23n +The suffixes list for selecting implicit rules (see The Suffixes List). +.RE + +.sp +.ne 2 +.na +\fB\fB\&.WAIT:\fR\fR +.ad +.RS 23n +In a dependency list, wait until any dependency preceding this has completed +before moving on to any dependency following it. +.RE + +.SS "\fIClearing Special Targets\fR" +.LP +In this version of \fBmake\fR, you can clear the definition of the following +special targets by supplying entries for them with no dependencies and no rule: +.sp +.LP +\fB\&.DEFAULT, .SCCS_GET, and .SUFFIXES\fR +.SS "Command Dependencies" +.LP +When the \fB\&.KEEP_STATE:\fR target is effective, \fBmake\fR checks the +command for building a target against the state file. If the command has +changed since the last \fBmake\fR run, \fBmake\fR rebuilds the target. +.SS "Hidden Dependencies" +.LP +When the \fB\&.KEEP_STATE:\fR target is effective, \fBmake\fR reads reports +from \fBcpp\fR(1) and other compilation processors for any "hidden" files, such +as \fB#include\fR files. If the target is out of date with respect to any of +these files, \fBmake\fR rebuilds it. +.SS "Macros" +.LP +Entries of the form +.sp +.in +2 +.nf +\fImacro\fR\fB=\fR\fIvalue\fR +.fi +.in -2 + +.sp +.LP +define macros. \fImacro\fR is the name of the macro, and \fIvalue\fR, which +consists of all characters up to a comment character or unescaped +\fBNEWLINE\fR, is the value. \fBmake\fR strips both leading and trailing white +space in accepting the value. +.sp +.LP +Subsequent references to the macro, of the forms: \fB$(\fR\fIname\fR\fB)\fR or +\fB${\fR\fIname\fR\fB}\fR are replaced by \fIvalue\fR. The parentheses or +brackets can be omitted in a reference to a macro with a single-character name. +.sp +.LP +Macro references can contain references to other macros, in which case nested +references are expanded first. +.SS "\fISuffix Replacement Macro References\fR" +.LP +Substitutions within macros can be made as follows: +.sp +.in +2 +.nf + \fB$(\fR\fIname\fR\fB:\fR\fIstring1\fR\fB=\fR\fIstring2\fR\fB)\fR +.fi +.in -2 + +.sp +.LP +where \fIstring1\fR is either a suffix, or a word to be replaced in the macro +definition, and \fIstring2\fR is the replacement suffix or word. Words in a +macro value are separated by \fBSPACE\fR, \fBTAB\fR, and escaped \fBNEWLINE\fR +characters. +.SS "\fIPattern Replacement Macro References\fR" +.LP +Pattern matching replacements can also be applied to macros, with a reference +of the form: +.sp +.in +2 +.nf + \fB$(\fR\fIname\fR\fB:\fR \fIop\fR\fB%\fR\fIos\fR\fB=\fR \fInp\fR\fB%\fR\fIns\fR\fB)\fR +.fi +.in -2 + +.sp +.LP +where \fIop\fR is the existing (old) prefix and \fIos\fR is the existing (old) +suffix, \fInp\fR and \fIns\fR are the new prefix and new suffix, respectively, +and the pattern matched by \fB%\fR (a string of zero or more characters), is +carried forward from the value being replaced. For example: +.sp +.in +2 +.nf +\fBPROGRAM=fabricate +DEBUG= $(PROGRAM:%=tmp/%\(mig)\fR +.fi +.in -2 +.sp + +.sp +.LP +sets the value of \fBDEBUG\fR to \fBtmp/fabricate\(mig\fR. +.sp +.LP +Notice that pattern replacement macro references cannot be used in the +dependency list of a pattern matching rule; the \fB%\fR characters are not +evaluated independently. Also, any number of \fB%\fR metacharacters can appear +after the equal-sign. +.SS "\fIAppending to a Macro\fR" +.LP +Words can be appended to macro values as follows: +.sp +.in +2 +.nf + \fImacro\fR \fB+=\fR \fIword .\|.\|.\fR +.fi +.in -2 + +.SS "Special-Purpose Macros" +.LP +When the \fBMAKEFLAGS\fR variable is present in the environment, \fBmake\fR +takes options from it, in combination with options entered on the command line. +\fBmake\fR retains this combined value as the \fBMAKEFLAGS\fR macro, and +exports it automatically to each command or shell it invokes. +.sp +.LP +Notice that flags passed by way of \fBMAKEFLAGS\fR are only displayed when the +\fB-d\fR, or \fB-dd\fR options are in effect. +.sp +.LP +The \fBMAKE\fR macro is another special case. It has the value \fBmake\fR by +default, and temporarily overrides the \fB-n\fR option for any line in which it +is referred to. This allows nested invocations of \fBmake\fR written as: +.sp +.in +2 +.nf + \fB$(MAKE)\fR .\|.\|. +.fi +.in -2 + +.sp +.LP +to run recursively, with the \fB-n\fR flag in effect for all commands but +\fBmake\fR. This lets you use \fBmake\fR \fB-n\fR to test an entire hierarchy +of makefiles. +.sp +.LP +For compatibility with the 4.2 \fBBSD\fR \fBmake\fR, the \fBMFLAGS\fR macro is +set from the \fBMAKEFLAGS\fR variable by prepending a \fB-\fR\&. \fBMFLAGS\fR +is not exported automatically. +.sp +.LP +The \fBSHELL\fR macro, when set to a single-word value such as +\fB/usr/bin/csh\fR, indicates the name of an alternate shell to use. The +default is \fB/bin/sh\fR. Notice that \fBmake\fR executes commands that contain +no shell metacharacters itself. Built-in commands, such as \fBdirs\fR in the C +shell, are not recognized unless the command line includes a metacharacter (for +instance, a semicolon). This macro is neither imported from, nor exported to +the environment, regardless of \fB-e\fR. To be sure it is set properly, you +must define this macro within every makefile that requires it. +.sp +.LP +The syntax of the \fBVPATH\fR macro is: +.sp +.in +2 +.nf + \fBVPATH\fR = [ \fIpathname\fR [ : \fIpathname\fR ] ... ] +.fi +.in -2 + +.sp +.LP +\fBVPATH\fR specifies a list of directories to search for the files, which are +targets or dependencies, when \fBmake\fR is executed. \fBVPATH\fR is also used +in order to search for the \fBinclude\fR files mentioned in the particular +makefile. +.sp +.LP +When processing a target or a dependency or an include directive, \fBmake\fR +checks the existence of the file with the same name in the current directory. +If the file is found to be missing, \fBmake\fR searches for this file in the +list of directories presented in \fBVPATH\fR (like the \fBPATH\fR variable in +the shell). Unlike the \fBPATH\fR variable, \fBVPATH\fR is used in order to +search for the files with relative pathnames. When \fBmake\fR attempts to apply +implicit rules to the target, it also searches for the dependency files using +\fBVPATH\fR. +.sp +.LP +When the file is found using \fBVPATH\fR, internal macros \fB$@\fR, \fB@<\fR, +\fB$?\fR, \fB$*\fR, and their alternative forms (with \fBD\fR or \fBF\fR +appended) are set in accordance with the name derived from \fBVPATH\fR. For +instance, if the target \fBsubdir/foo.o\fR is found in the directory +\fB/aaa/bbb\fR using \fBVPATH\fR, then the value of the internal macro \fB$@\fR +for this target is \fB/aaa/bbb/subdir/foo.o\fR. +.sp +.LP +If a target or a dependency file is found using \fBVPATH\fR, then any +occurrences of the word that is the same as the target name in the subsequent +rules are replaced with the actual name of the target derived from \fBVPATH\fR. +.sp +.LP +For example: +.sp +.in +2 +.nf +\fBVPATH=./subdir +file.o : file.c + cc -c file.c -o file.o\fR +.fi +.in -2 +.sp + +.sp +.LP +If \fBfile.c\fR is found in \fB\&./subdir\fR, then the command +.sp +.in +2 +.nf +\fBcc -c ./subdir/file.c -o file.o\fR +.fi +.in -2 +.sp + +.sp +.LP +are executed. +.sp +.LP +The following macros are provided for use with cross-compilation: +.sp +.ne 2 +.na +\fB\fBHOST_ARCH\fR\fR +.ad +.RS 15n +The processor type of the host system. By default, this is the output of the +\fBmach\fR(1) command, prepended with \fB-\fR\&. Under normal circumstances, +this value should never be altered by the user. +.RE + +.sp +.ne 2 +.na +\fB\fBHOST_MACH\fR\fR +.ad +.RS 15n +The machine architecture of the host system. By default, this is the output of +the \fBarch\fR(1) command, prepended with \fB\(mi\fR\&. Under normal +circumstances, this value should never be altered by the user. +.RE + +.sp +.ne 2 +.na +\fB\fBTARGET_ARCH\fR\fR +.ad +.RS 15n +The processor type of the target system. By default, the output of \fBmach\fR, +prepended with \fB\(mi\fR\&. +.RE + +.SS "Dynamic Macros" +.LP +There are several dynamically maintained macros that are useful as +abbreviations within rules. They are shown here as references; if you were to +define them, \fBmake\fR would simply override the definition. +.sp +.ne 2 +.na +\fB\fB$*\fR\fR +.ad +.RS 6n +The basename of the current target, derived as if selected for use with an +implicit rule. +.RE + +.sp +.ne 2 +.na +\fB\fB$<\fR\fR +.ad +.RS 6n +The name of a dependency file, derived as if selected for use with an implicit +rule. +.RE + +.sp +.ne 2 +.na +\fB\fB$@\fR\fR +.ad +.RS 6n +The name of the current target. This is the only dynamic macro whose value is +strictly determined when used in a dependency list. (In which case it takes the +form \fB$$@\fR.) +.RE + +.sp +.ne 2 +.na +\fB\fB$?\fR\fR +.ad +.RS 6n +The list of dependencies that are newer than the target. Command-dependency +checking is automatically suppressed for lines that contain this macro, just as +if the command had been prefixed with a \fB?\fR. See the description of +\fB?\fR, under \fBSpecial Character\fR Rules above. You can force this check +with the \fB!\fR command-line prefix. +.RE + +.sp +.ne 2 +.na +\fB\fB$%\fR\fR +.ad +.RS 6n +The name of the library member being processed. (See Library Maintenance +below.) +.RE + +.sp +.LP +To refer to the \fB$@\fR dynamic macro within a dependency list, precede the +reference with an additional \fB$\fR character (as in, \fB$$@\fR). Because +\fBmake\fR assigns \fB$<\fR and \fB$*\fR as it would for implicit rules +(according to the suffixes list and the directory contents), they can be +unreliable when used within explicit target entries. +.sp +.LP +These macros can be modified to apply either to the filename part, or the +directory part of the strings they stand for, by adding an upper case \fBF\fR +or \fBD\fR, respectively (and enclosing the resulting name in parentheses or +braces). Thus, \fB$(@D)\fR refers to the directory part of the string \fB$@\fR; +if there is no directory part, \fB\&.\fR is assigned. \fB$(@F)\fR refers to the +filename part. +.SS "Conditional Macro Definitions" +.LP +A macro definition of the form: +.sp +.in +2 +.nf +\fItarget-list\fR \fB:=\fR \fImacro\fR \fB=\fR \fIvalue\fR +.fi +.in -2 + +.sp +.LP +indicates that when processing any of the targets listed \fIand their +dependencies\fR, \fImacro\fR is to be set to the \fIvalue\fR supplied. Notice +that if a conditional macro is referred to in a dependency list, the \fB$\fR +must be delayed (use \fB$$\fR instead). Also, \fItarget-list\fR can contain a +\fB%\fR pattern, in which case the macro is conditionally defined for all +targets encountered that match the pattern. A pattern replacement reference can +be used within the \fIvalue\fR. +.sp +.LP +You can temporarily append to a macros value with a conditional definition of +the form: +.sp +.in +2 +.nf +\fItarget-list\fR \fB:=\fR \fImacro\fR \fB+=\fR \fIvalue\fR +.fi +.in -2 + +.SS "Predefined Macros" +.LP +\fBmake\fR supplies the macros shown in the table that follows for compilers +and their options, host architectures, and other commands. Unless these macros +are read in as environment variables, their values are not exported by +\fBmake\fR. If you run \fBmake\fR with any of these set in the environment, it +is a good idea to add commentary to the makefile to indicate what value each is +expected to take. If \fB-r\fR is in effect, \fBmake\fR does not read the +default makefile (\fB\&./make.rules\fR or \fB/usr/share/lib/make/make.rules\fR) +in which these macro definitions are supplied. +.sp + +.sp +.TS +box; +cw(1i) | cw(1.17i) | cw(3.33i) +cw(1i) | cw(1.17i) | cw(3.33i) . +\fITable of Predefined Macros\fR +_ +\fIUse\fR \fIMacro\fR \fIDefault Value\fR +_ +Library \fBAR\fR \fBar\fR +Archives \fBARFLAGS\fR \fBrv\fR +_ + +Assembler \fBAS\fR \fBas\fR +Commands \fBASFLAGS\fR + \fBCOMPILE.s\fR \fB$(AS) $(ASFLAGS)\fR + \fBCOMPILE.S\fR \fB$(CC) $(ASFLAGS) $(CPPFLAGS)\fR \fB-c\fR +_ + +C \fBCC\fR \fBcc\fR +Compiler \fBCFLAGS\fR +Commands \fBCPPFLAGS\fR + \fBCOMPILE.c\fR \fB$(CC) $(CFLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.c\fR \fB$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS)\fR +_ + +C++ \fBCCC\fR \fBCC\fR +Compiler \fBCCFLAGS\fR \fBCFLAGS\fR +Commands \fBCPPFLAGS\fR + \fBCOMPILE.cc\fR \fB$(CCC) $(CCFLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.cc\fR \fB$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS)\fR + \fBCOMPILE.C\fR \fB$(CCC) $(CCFLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.C\fR \fB$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS)\fR +_ + +FORTRAN 77 \fBFC\fR \fBf77\fR +Compiler \fBFFLAGS\fR +Commands \fBCOMPILE.f\fR \fB$(FC) $(FFLAGS)\fR \fB-c\fR + \fBLINK.f\fR \fB$(FC) $(FFLAGS) $(LDFLAGS)\fR + \fBCOMPILE.F\fR \fB$(FC) $(FFLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.F\fR \fB$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS)\fR +_ + +FORTRAN 90 \fBFC\fR \fBf90\fR +Compiler \fBF90FLAGS\fR +Commands \fBCOMPILE.f90\fR \fB$(F90C) $(F90FLAGS)\fR \fB-c\fR + \fBLINK.f90\fR \fB$(F90C) $(F90FLAGS) $(LDFLAGS)\fR + \fBCOMPILE.ftn\fR \fB$(F90C) $(F90FLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.ftn\fR T{ +\fB$(F90C) $(F90FLAGS) $(CPPFLAGS) $(LDFLAGS)\fR +T} +_ + +Link Editor \fBLD\fR \fBld\fR +Command \fBLDFLAGS\fR +_ + +lex \fBLEX\fR \fBlex\fR +Command \fBLFLAGS\fR + \fBLEX.l\fR \fB$(LEX) $(LFLAGS)\fR \fB-t\fR +_ + +lint \fBLINT\fR \fBlint\fR +Command \fBLINTFLAGS\fR + \fBLINT.c\fR \fB$(LINT) $(LINTFLAGS) $(CPPFLAGS)\fR +_ + +Modula 2 \fBM2C\fR \fBm2c\fR +Commands \fBM2FLAGS\fR + \fBMODFLAGS\fR + \fBDEFFLAGS\fR + \fBCOMPILE.def\fR \fB$(M2C) $(M2FLAGS) $(DEFFLAGS)\fR + \fBCOMPILE.mod\fR \fB$(M2C) $(M2FLAGS) $(MODFLAGS)\fR +_ + +Pascal \fBPC\fR \fBpc\fR +Compiler \fBPFLAGS\fR +Commands \fBCOMPILE.p\fR \fB$(PC) $(PFLAGS) $(CPPFLAGS)\fR \fB-c\fR + \fBLINK.p\fR \fB$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS)\fR +_ + +Ratfor \fBRFLAGS\fR +Compilation \fBCOMPILE.r\fR \fB$(FC) $(FFLAGS) $(RFLAGS)\fR \fB-c\fR +Commands \fBLINK.r\fR \fB$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS)\fR +_ + +rm Command \fBRM\fR \fBrm\fR \fB-f\fR +_ + +sccs \fBSCCSFLAGS\fR +Command \fBSCCSGETFLAGS\fR \fB-s\fR +_ + +yacc \fBYACC\fR \fByacc\fR +Command \fBYFLAGS\fR + \fBYACC.y\fR \fB$(YACC) $(YFLAGS)\fR +_ + +Suffixes List \fBSUFFIXES\fR T{ +\fB\&.o .c .c~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S~ .ln .h .h~ .f .f~ .F .F~ .mod .mod~ .sym .def .def~ .p .p~ .r .r~ .cps .cps~ .C .C~ .Y .Y~ .L .L .f90 .f90~ .ftn .ftn~\fR +T} +.TE + +.SS "Implicit Rules" +.LP +When a target has no entry in the makefile, \fBmake\fR attempts to determine +its class (if any) and apply the rule for that class. An implicit rule +describes how to build any target of a given class, from an associated +dependency file. The class of a target can be determined either by a pattern, +or by a suffix; the corresponding dependency file (with the same basename) from +which such a target might be built. In addition to a predefined set of implicit +rules, \fBmake\fR allows you to define your own, either by pattern, or by +suffix. +.SS "\fIPattern Matching Rules\fR" +.LP +A target entry of the form: +.sp +.in +2 +.nf +\fItp\fR\fB%\fR\fIts\fR:\|\fIdp\fR\fB%\fR\fIds\fR + \fIrule\fR +.fi +.in -2 +.sp + +.sp +.LP +is a pattern matching rule, in which \fItp\fR is a target prefix, \fIts\fR is a +target suffix, \fIdp\fR is a dependency prefix, and \fIds\fR is a dependency +suffix (any of which can be null). The \fB%\fR stands for a basename of zero or +more characters that is matched in the target, and is used to construct the +name of a dependency. When \fBmake\fR encounters a match in its search for an +implicit rule, it uses the rule in that target entry to build the target from +the dependency file. Pattern-matching implicit rules typically make use of the +\fB$@\fR and \fB$<\fR dynamic macros as placeholders for the target and +dependency names. Other, regular dependencies can occur in the dependency list; +however, none of the regular dependencies can contain \fB%\fR. An entry of the +form: +.sp +.in +2 +.nf +\fItp\fR%\fIts\fR:\|[\fIdependency .\|.\|.\fR\|] \fIdp\fR%\fIds\fR\|[\fIdependency .\|.\|.\fR\|] + \fIrule\fR +.fi +.in -2 +.sp + +.sp +.LP +is a valid pattern matching rule. +.SS "\fISuffix Rules\fR" +.LP +When no pattern matching rule applies, \fBmake\fR checks the target name to see +if it ends with a suffix in the known suffixes list. If so, \fBmake\fR checks +for any suffix rules, as well as a dependency file with same root and another +recognized suffix, from which to build it. +.sp +.LP +The target entry for a suffix rule takes the form: +.sp +.in +2 +.nf +\fIDsTs\fR: \fIrule\fR +.fi +.in -2 +.sp + +.sp +.LP +where \fITs\fR is the suffix of the target, \fIDs\fR is the suffix of the +dependency file, and \fIrule\fR is the rule for building a target in the class. +Both \fIDs\fR and \fITs\fR must appear in the suffixes list. (A suffix need not +begin with a \fB\&.\fR to be recognized.) +.sp +.LP +A suffix rule with only one suffix describes how to build a target having a +null (or no) suffix from a dependency file with the indicated suffix. For +instance, the \fB\&.c\fR rule could be used to build an executable program +named \fBfile\fR from a C source file named \fBfile.c\fR. If a target with a +null suffix has an explicit dependency, \fBmake\fR omits the search for a +suffix rule. +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for Assembly Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.s.o\fR \fB$(COMPILE.s)\fR \fB-o\fR \fB$@ $<\fR +_ + +\fB\&.s.a\fR \fB$(COMPILE.s)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.s~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.s\fR + \fB$(COMPILE.s)\fR \fB-o\fR \fB$@ $*.s\fR +_ + +\fB\&.S.o\fR \fB$(COMPILE.S)\fR \fB-o\fR \fB$@ $<\fR +_ + +\fB\&.S.a\fR \fB$(COMPILE.S)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.S~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.S\fR + \fB$(COMPILE.S)\fR \fB-o\fR \fB$@ $*.S\fR +_ + +\fB\&.S~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.S\fR + \fB$(COMPILE.S)\fR \fB-o\fR \fB$% $*.S\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for C Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.c\fR \fB$(LINK.c)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.c.ln\fR \fB$(LINT.c) $(OUTPUT_OPTION)\fR \fB-i\fR \fB$<\fR +_ + +\fB\&.c.o\fR \fB$(COMPILE.c) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.c.a\fR \fB$(COMPILE.c)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.c~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.c\fR + \fB$(CC) $(CFLAGS) $(LDFLAGS)\fR \fB-o\fR \fB$@ $*.c\fR +_ + +\fB\&.c~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.c\fR + \fB$(CC) $(CFLAGS)\fR \fB-c\fR \fB$*.c\fR +_ + +\fB\&.c~.ln\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.c\fR + \fB$(LINT.c) $(OUTPUT_OPTION)\fR \fB-c\fR \fB$*.c\fR +_ + +\fB\&.c~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.c\fR + \fB$(COMPILE.c)\fR \fB-o\fR \fB$% $*.c\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for C++ Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.cc\fR \fB$(LINK.cc)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.cc.o\fR \fB$(COMPILE.cc) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.cc.a\fR \fB$(COMPILE.cc)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.cc~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.cc\fR + \fB$(LINK.cc)\fR \fB-o\fR \fB$@ $*.cc $(LDLIBS)\fR +_ + +\fB\&.cc.o\fR \fB$(COMPILE.cc) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.cc~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.cc\fR + \fB$(COMPILE.cc) $(OUTPUT_OPTION) $*.cc\fR +_ + +\fB\&.cc.a\fR \fB$(COMPILE.cc)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.cc~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.cc\fR + \fB$(COMPILE.cc)\fR \fB-o\fR \fB$% $*.cc\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.C\fR \fB$(LINK.C)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.C~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.C\fR + \fB$(LINK.C)\fR \fB-o\fR \fB$@ $*.C $(LDLIBS)\fR +_ + +\fB\&.C.o\fR \fB$(COMPILE.C) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.C~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.C\fR + \fB$(COMPILE.C) $(OUTPUT_OPTION) $*.C\fR +_ + +\fB\&.C.a\fR \fB$(COMPILE.C)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.C~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.C\fR + \fB$(COMPILE.C)\fR \fB-o\fR \fB$% $*.C\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for FORTRAN 77 Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.f\fR \fB$(LINK.f)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.f.o\fR \fB$(COMPILE.f) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.f.a\fR \fB$(COMPILE.f)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.f\fR \fB$(LINK.f)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.f~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f\fR + \fB$(FC) $(FFLAGS) $(LDFLAGS)\fR \fB-o\fR \fB$@ $*.f\fR +_ + +\fB\&.f~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f\fR + \fB$(FC) $(FFLAGS)\fR \fB-c\fR \fB$*.f\fR +_ + +\fB\&.f~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f\fR + \fB$(COMPILE.f)\fR \fB-o\fR \fB$% $*.f\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.F\fR \fB$(LINK.F)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.F.o\fR \fB$(COMPILE.F) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.F.a\fR \fB$(COMPILE.F)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.F~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.F\fR + \fB$(FC) $(FFLAGS) $(LDFLAGS)\fR \fB-o\fR \fB$@ $*.F\fR +_ + +\fB\&.F~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.F\fR + \fB$(FC) $(FFLAGS)\fR \fB-c\fR \fB$*.F\fR +_ + +\fB\&.F~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.F\fR + \fB$(COMPILE.F)\fR \fB-o\fR \fB$% $*.F\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for FORTRAN 90 Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.f90\fR \fB$(LINK.f90)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.f90~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f90\fR + \fB$(LINK.f90)\fR \fB-o\fR \fB$@ $*.f90 $(LDLIBS)\fR +_ + +\fB\&.f90.o\fR \fB$(COMPILE.f90) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.f90~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f90\fR + \fB$(COMPILE.f90) $(OUTPUT_OPTION) $*.f90\fR +_ + +\fB\&.f90.a\fR \fB$(COMPILE.f90)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.f90~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.f90\fR + \fB$(COMPILE.f90)\fR \fB-o\fR \fB$% $*.f90\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.ftn\fR \fB$(LINK.ftn)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.ftn~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.ftn\fR + \fB$(LINK.ftn)\fR \fB-o\fR \fB$@ $*.ftn $(LDLIBS)\fR +_ + +\fB\&.ftn.o\fR \fB$(COMPILE.ftn) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.ftn~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.ftn\fR + \fB$(COMPILE.ftn) $(OUTPUT_OPTION) $*.ftn\fR +_ + +\fB\&.ftn.a\fR \fB$(COMPILE.ftn)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.ftn~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.ftn\fR + \fB$(COMPILE.ftn)\fR \fB-o\fR \fB$% $*.ftn\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for lex Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.l\fR \fB$(RM) $*.c\fR + \fB$(LEX.l) $< > $*.c\fR + \fB$(LINK.c)\fR \fB-o\fR \fB$@ $*.c $(LDLIBS)\fR + \fB$(RM) $*.c\fR +_ + +\fB\&.l.c\fR \fB$(RM) $@\fR + \fB$(LEX.l) $< > $@\fR +_ + +\fB\&.l.ln\fR \fB$(RM) $*.c\fR + \fB$(LEX.l) $< > $*.c\fR + \fB$(LINT.c)\fR \fB-o\fR \fB$@\fR \fB-i\fR \fB$*.c\fR + \fB$(RM) $*.c\fR +_ + +\fB\&.l.o\fR \fB$(RM) $*.c\fR + \fB$(LEX.l) $< > $*.c\fR + \fB$(COMPILE.c)\fR \fB-o\fR \fB$@ $*.c\fR + \fB$(RM) $*.c\fR +_ + +\fB\&.l~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.l\fR + \fB$(LEX) $(LFLAGS) $*.l\fR + \fB$(CC) $(CFLAGS)\fR \fB-c\fR \fBlex.yy.c\fR + \fBrm\fR \fB-f\fR \fBlex.yy.c\fR + \fBmv lex.yy.c $@\fR +_ + +\fB\&.l~.c\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.l\fR + \fB$(LEX) $(LFLAGS) $*.l\fR + \fBmv lex.yy.c $@\fR +_ + +\fB\&.l~.ln\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.l\fR + \fB$(RM) $*.c\fR + \fB$(LEX.l) $*.l > $*.c\fR + \fB$(LINT.c)\fR \fB-o\fR \fB$@\fR \fB-i\fR \fB$*.c\fR + \fB$(RM) $*.c\fR +_ + +\fB\&.l~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.l\fR + \fB$(LEX) $(LFLAGS) $*.l\fR + \fB$(CC) $(CFLAGS)\fR \fB-c\fR \fBlex.yy.c\fR + \fBrm\fR \fB-f\fR \fBlex.yy.c\fR + \fBmv lex.yy.c $@\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for Modula 2 Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.mod\fR \fB$(COMPILE.mod)\fR \fB-o\fR \fB$@\fR \fB-e\fR \fB$@ $<\fR +_ + +\fB\&.mod.o\fR \fB$(COMPILE.mod)\fR \fB-o\fR \fB$@ $<\fR +_ + +\fB\&.def.sym\fR \fB$(COMPILE.def)\fR \fB-o\fR \fB$@ $<\fR +_ + +\fB\&.def~.sym\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.def\fR + \fB$(COMPILE.def)\fR \fB-o\fR\fB$@ $*.def\fR +_ + +\fB\&.mod~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.mod\fR + \fB$(COMPILE.mod)\fR \fB-o\fR \fB$@\fR \fB-e\fR \fB$@ $*.mod\fR +_ + +\fB\&.mod~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.mod\fR + \fB$(COMPILE.mod)\fR \fB-o\fR \fB$@ $*.mod\fR +_ + +\fB\&.mod~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.mod\fR + \fB$(COMPILE.mod)\fR \fB-o\fR \fB$% $*.mod\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for NeWS Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.cps.h\fR \fBcps $*.cps\fR +_ +\fB\&.cps~.h\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.cps\fR + \fB$(CPS) $(CPSFLAGS) $*.cps\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for Pascal Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.p\fR \fB$(LINK.p)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.p.o\fR \fB$(COMPILE.p) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.p~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.p\fR + \fB$(LINK.p)\fR \fB-o\fR \fB$@ $*.p $(LDLIBS)\fR +_ + +\fB\&.p~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.p\fR + \fB$(COMPILE.p) $(OUTPUT_OPTION) $*.p\fR +_ + +\fB\&.p~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.p\fR + \fB$(COMPILE.p)\fR \fB-o\fR \fB$% $*.p\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for Ratfor Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.r\fR \fB$(LINK.r)\fR \fB-o\fR \fB$@ $< $(LDLIBS)\fR +_ + +\fB\&.r.o\fR \fB$(COMPILE.r) $(OUTPUT_OPTION) $<\fR +_ + +\fB\&.r.a\fR \fB$(COMPILE.r)\fR \fB-o\fR \fB$% $<\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +_ + +\fB\&.r~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.r\fR + \fB$(LINK.r)\fR \fB-o\fR \fB$@ $*.r $(LDLIBS)\fR +_ + +\fB\&.r~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.r\fR + \fB$(COMPILE.r) $(OUTPUT_OPTION) $*.r\fR +_ + +\fB\&.r~.a\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.r\fR + \fB$(COMPILE.r)\fR \fB-o\fR \fB$% $*.r\fR + \fB$(AR) $(ARFLAGS) $@ $%\fR + \fB$(RM) $%\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for SCCS Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.SCCS_GET\fR T{ +\fBsccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@\fR \fB-G\fR\fB$@\fR +T} + +_ + +\fB\&.SCCS_GET_POSIX\fR \fBsccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@\fR +_ + +\fB\&.GET_POSIX\fR \fB$(GET) $(GFLAGS) s.$@\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for Shell Scripts\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.sh\fR \fBcat $< >$@\fR + \fBchmod +x $@\fR +_ + +\fB\&.sh~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.sh\fR + \fBcp $*.sh $@\fR + \fBchmod a+x $@\fR +.TE + +.sp + +.sp +.TS +box; +cw(1.57i) |cw(3.93i) +cw(1.57i) |cw(3.93i) . +T{ +\fITable of Standard Implicit (Suffix) Rules for yacc Files\fR +T} +_ +\fIImplicit Rule Name\fR \fICommand Line\fR +_ +\fB\&.y\fR \fB$(YACC.y) $<\fR + \fB$(LINK.c)\fR \fB-o\fR \fB$@ y.tab.c $(LDLIBS)\fR + \fB$(RM) y.tab.c\fR +_ + +\fB\&.y.c\fR \fB$(YACC.y) $<\fR + \fBmv y.tab.c $@\fR +_ + +\fB\&.y.ln\fR \fB$(YACC.y) $<\fR + \fB$(LINT.c)\fR \fB-o\fR \fB$@\fR \fB-i\fR \fBy.tab.c\fR + \fB$(RM) y.tab.c\fR +_ + +\fB\&.y.o\fR \fB$(YACC.y) $<\fR + \fB$(COMPILE.c)\fR \fB-o\fR \fB$@ y.tab.c\fR + \fB$(RM) y.tab.c\fR +_ + +\fB\&.y~\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.y\fR + \fB$(YACC) $(YFLAGS) $*.y\fR + \fB$(COMPILE.c)\fR \fB-o\fR \fB$@ y.tab.c\fR + \fB$(RM) y.tab.c\fR +_ + +\fB\&.y~.c\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.y\fR + \fB$(YACC) $(YFLAGS) $*.y\fR + \fBmv y.tab.c $@\fR +_ + +\fB\&.y~.ln\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.y\fR + \fB$(YACC.y) $*.y\fR + \fB$(LINT.c)\fR \fB-o\fR \fB$@\fR \fB-i\fR \fBy.tab.c\fR + \fB$(RM) y.tab.c\fR +_ + +\fB\&.y~.o\fR \fB$(GET) $(GFLAGS)\fR \fB-p\fR \fB$< > $*.y\fR + \fB$(YACC) $(YFLAGS) $*.y\fR + \fB$(CC) $(CFLAGS)\fR \fB-c\fR \fBy.tab.c\fR + \fBrm\fR \fB-f\fR \fBy.tab.c\fR + \fBmv y.tab.o $@\fR +.TE + +.sp +.LP +\fBmake\fR reads in the standard set of implicit rules from the file +\fB/usr/share/lib/make/make.rules\fR, unless \fB-r\fR is in effect, or there is +a \fBmake.rules\fR file in the local directory that does not \fBinclude\fR that +file. +.SS "The Suffixes List" +.LP +The suffixes list is given as the list of dependencies for the +\fB\&.SUFFIXES:\fR special-function target. The default list is contained in +the \fBSUFFIXES\fR macro (See \fITable of Predefined Macros\fR for the standard +list of suffixes). You can define additional \fB\&.SUFFIXES:\fR targets; a +\fB\&.SUFFIXES\fR target with no dependencies clears the list of suffixes. +Order is significant within the list; \fBmake\fR selects a rule that +corresponds to the target's suffix and the first dependency-file suffix found +in the list. To place suffixes at the head of the list, clear the list and +replace it with the new suffixes, followed by the default list: +.sp +.in +2 +.nf + .SUFFIXES: + .SUFFIXES: \fIsuffixes\fR $(SUFFIXES) +.fi +.in -2 + +.sp +.LP +A tilde (\fB~\fR) indicates that if a dependency file with the indicated suffix +(minus the ~) is under \fBSCCS\fR its most recent version should be retrieved, +if necessary, before the target is processed. +.SS "Library Maintenance" +.LP +A target name of the form: +.sp +.in +2 +.nf + \fIlib\fR(\fImember ...\fR) +.fi +.in -2 + +.sp +.LP +refers to a member, or a space-separated list of members, in an \fBar\fR(1) +library. +.sp +.LP +The dependency of the library member on the corresponding file must be given as +an explicit entry in the makefile. This can be handled by a pattern matching +rule of the form: +.sp +.in +2 +.nf + \fIlib\fR(%\fI\&.s\fR): %\fI\&.s\fR +.fi +.in -2 + +.sp +.LP +where \fI\&.s\fR is the suffix of the member; this suffix is typically +\fB\&.o\fR for object libraries. +.sp +.LP +A target name of the form: +.sp +.in +2 +.nf + \fIlib\fR((\fIsymbol\fR)) +.fi +.in -2 + +.sp +.LP +refers to the member of a randomized object library that defines the entry +point named \fIsymbol\fR. +.SS "Command Execution" +.LP +Command lines are executed one at a time, \fIeach by its own process or +shell\fR. Shell commands, notably \fBcd\fR, are ineffectual across an unescaped +\fBNEWLINE\fR in the makefile. A line is printed (after macro expansion) just +before being executed. This is suppressed if it starts with a \fB@\fR, if there +is a \fB\&.SILENT:\fR entry in the makefile, or if \fBmake\fR is run with the +\fB-s\fR option. Although the \fB-n\fR option specifies printing without +execution, lines containing the macro \fB$(MAKE)\fR are executed regardless, +and lines containing the \fB@\fR special character are printed. The \fB-t\fR +(touch) option updates the modification date of a file without executing any +rules. This can be dangerous when sources are maintained by more than one +person. +.sp +.LP +\fBmake\fR invokes the shell with the \fB-e\fR (exit-on-errors) argument. Thus, +with semicolon-separated command sequences, execution of the later commands +depends on the success of the former. This behavior can be overridden by +starting the command line with a \fB\|-\fR, or by writing a shell script that +returns a non-zero status only as it finds appropriate. +.SS "Bourne Shell Constructs" +.LP +To use the Bourne shell \fBif\fR control structure for branching, use a command +line of the form: +.sp +.in +2 +.nf +if \fIexpression\fR ; \e +then \fIcommand\fR ; \e + ... ; \e +else \fIcommand\fR; \e + ... ; \e +fi +.fi +.in -2 + +.sp +.LP +Although composed of several input lines, the escaped \fBNEWLINE\fR characters +insure that \fBmake\fR treats them all as one (shell) command line. +.sp +.LP +To use the Bourne shell \fBfor\fR control structure for loops, use a command +line of the form: +.sp +.in +2 +.nf +for \fIvar\fR in \fIlist\fR ; \e +do \fIcommand\fR; \e + ... \fB; \e\fRdone +.fi +.in -2 + +.sp +.LP +To refer to a shell variable, use a double-dollar-sign (\fB$$\fR). This +prevents expansion of the dollar-sign by \fBmake\fR. +.SS "Command Substitutions" +.LP +To incorporate the standard output of a shell command in a macro, use a +definition of the form: +.sp +.in +2 +.nf + \fIMACRO\|\fR:sh =\fIcommand\fR +.fi +.in -2 + +.sp +.LP +The command is executed only once, standard error output is discarded, and +\fBNEWLINE\fR characters are replaced with \fBSPACE\fRs. If the command has a +non-zero exit status, \fBmake\fR halts with an error. +.sp +.LP +To capture the output of a shell command in a macro reference, use a reference +of the form: +.sp +.in +2 +.nf +$(\fIMACRO\|\fR:sh) +.fi +.in -2 + +.sp +.LP +where \fIMACRO\fR is the name of a macro containing a valid Bourne shell +command line. In this case, the command is executed whenever the reference is +evaluated. As with shell command substitutions, the reference is replaced with +the standard output of the command. If the command has a non-zero exit status, +\fBmake\fR halts with an error. +.sp +.LP +In contrast to commands in rules, the command is not subject for macro +substitution; therefore, a dollar sign (\fB$\fR) need not be replaced with a +double dollar sign (\fB$$\fR). +.SS "\fISignals\fR" +.LP +\fBINT\fR, \fBSIGTERM\fR, and \fBQUIT\fR signals received from the keyboard +halt \fBmake\fR and remove the target file being processed unless that target +is in the dependency list for \fB\&.PRECIOUS:\fR. +.SH EXAMPLES +.LP +\fBExample 1 \fRDefining dependencies +.sp +.LP +This makefile says that \fBpgm\fR depends on two files \fBa.o\fR and \fBb.o\fR, +and that they in turn depend on their corresponding source files (\fBa.c\fR and +\fBb.c\fR) along with a common file \fBincl.h\fR: + +.sp +.in +2 +.nf +pgm: a.o b.o + $(LINK.c) -o $@a.o b.o +a.o: incl.h a.c + cc -c a.c +b.o: incl.h b.c + cc -c b.c +.fi +.in -2 + +.LP +\fBExample 2 \fRUsing implicit rules +.sp +.LP +The following makefile uses implicit rules to express the same dependencies: + +.sp +.in +2 +.nf +pgm: a.o b.o + cc a.o b.o -o pgm +a.o b.o: incl.h +.fi +.in -2 + +.SH ENVIRONMENT VARIABLES +.LP +See \fBenviron\fR(5) for descriptions of the following environment variables +that affect the execution of \fBmake\fR: \fBLANG\fR, \fBLC_ALL\fR, +\fBLC_CTYPE\fR, \fBLC_MESSAGES\fR, and \fBNLSPATH\fR. + +.sp +.ne 2 +.na +\fBDMAKE_MAX_JOBS\fR +.ad +.RS 17n +The maximum number of jobs that each instance of \fBmake\fR should run. +.RE + +.sp +.ne 2 +.na +\fBDMAKE_ADJUST_MAX_JOBS\fR +.ad +.RS 17n +If \fBYES\fR adjust the maximum number of jobs each instance of \fBmake\fR +runs based on system load. If \fBNO\fR do not +.RE + +.sp +.ne 2 +.na +\fBDMAKE_OUTPUT_MODE\fR +.ad +.RS 17n +Adjusts the format of output from \fBmake\fR when executing jobs in parallel. + +If the value is \fBTEXT1\fR \fBmake\fR will print the hostname and the command +executed once when the job begins, and again preceding any output from that +job. + +If the value is \fBTEXT2\fR \fBmake\fR will only print the command and its +output only once, when execution is complete. +.RE + +.sp +.ne 2 +.na +\fBDMAKE_MODE\fR +.ad +.RS 17n +Specify whether jobs should be executed in serial, or parallel. Equivalent to +passing the \fB-m\FR option. +.RE + +.sp +.ne 2 +.na +\fB\fBKEEP_STATE\fR\fR +.ad +.RS 17n +This environment variable has the same effect as the .KEEP_STATE: +special-function target. It enables command dependencies, hidden dependencies +and writing of the state file. +.RE + +.sp +.ne 2 +.na +\fB\fBUSE_SVR4_MAKE\fR\fR +.ad +.RS 17n +This environment variable causes \fBmake\fR to invoke the generic System V +version of \fBmake\fR (\fB/usr/lib/svr4.make\fR). See \fBsysV-make\fR(1). +.RE + +.sp +.ne 2 +.na +\fB\fBMAKEFLAGS\fR\fR +.ad +.RS 17n +This variable is interpreted as a character string representing a series of +option characters to be used as the default options. The implementation accepts +both of the following formats (but need not accept them when intermixed): +.RS +4 +.TP +1. +The characters are option letters without the leading hyphens or blank +character separation used on a command line. +.RE +.RS +4 +.TP +2. +The characters are formatted in a manner similar to a portion of the +\fBmake\fR command line: options are preceded by hyphens and +blank-character-separated. The \fImacro=name\fR macro definition operands can +also be included. The difference between the contents of \fBMAKEFLAGS\fR and +the command line is that the contents of the variable is not subjected to the +word expansions associated with parsing the command line values. See +\fBwordexp\fR(3C). +.sp +When the command-line options \fB-f\fR or \fB-p\fR are used, they take effect +regardless of whether they also appear in \fBMAKEFLAGS\fR. If they otherwise +appear in \fBMAKEFLAGS\fR, the result is undefined. +.RE +The \fBMAKEFLAGS\fR variable is accessed from the environment before the +makefile is read. At that time, all of the options (except \fB-f\fR and +\fB-p\fR) and command-line macros not already included in \fBMAKEFLAGS\fR are +added to the \fBMAKEFLAGS\fR macro. The \fBMAKEFLAGS\fR macro is passed into +the environment as an environment variable for all child processes. If the +\fBMAKEFLAGS\fR macro is subsequently set by the makefile, it replaces the +\fBMAKEFLAGS\fR variable currently found in the environment. +.RE + +.sp +.ne 2 +.na +\fB\fBPROJECTDIR\fR\fR +.ad +.RS 17n +Provides a directory to be used to search for SCCS files not found in the +current directory. In all of the following cases, the search for SCCS files is +made in the directory SCCS in the identified directory. If the value of +\fBPROJECTDIR\fR begins with a slash, it shall be considered an absolute +pathname. Otherwise, the value of \fBPROJECTDIR\fR is treated as a user name +and that user's initial working directory shall be examined for a subdirectory +\fBsrc\fR or \fBsource\fR. If such a directory is found, it shall be used. +Otherwise, the value is used as a relative pathname. +.sp +If \fBPROJECTDIR\fR is not set or has a null value, the search for SCCS files +shall be made in the directory SCCS in the current directory. The setting of +\fBPROJECTDIR\fR affects all files listed in the remainder of this utility +description for files with a component named SCCS. +.RE + +.SH EXIT STATUS +.LP +When the \fB-q\fR option is specified, the \fBmake\fR utility exits with one of +the following values: +.sp +.ne 2 +.na +\fB\fB0\fR\fR +.ad +.RS 6n +Successful completion. +.RE + +.sp +.ne 2 +.na +\fB\fB1\fR\fR +.ad +.RS 6n +The target was not up-to-date. +.RE + +.sp +.ne 2 +.na +\fB\fB>1\fR\fR +.ad +.RS 6n +An error occurred. +.RE + +.sp +.LP +When the \fB-q\fR option is not specified, the \fBmake\fR utility exits with +one of the following values: +.sp +.ne 2 +.na +\fB\fB0\fR\fR +.ad +.RS 6n +Successful completion +.RE + +.sp +.ne 2 +.na +\fB\fB>0\fR\fR +.ad +.RS 6n +An error occurred +.RE + +.SH FILES +.ne 2 +.na +\fB\fBmakefile\fR\fR +.ad +.br +.na +\fB\fBMakefile\fR\fR +.ad +.sp .6 +.RS 4n +current version(s) of \fBmake\fR description file +.RE + +.sp +.ne 2 +.na +\fB\fBs.makefile\fR\fR +.ad +.br +.na +\fB\fBs.Makefile\fR\fR +.ad +.sp .6 +.RS 4n +\fBSCCS\fR history files for the above makefile(s) in the current directory +.RE + +.sp +.ne 2 +.na +\fB\fBSCCS/s.makefile\fR\fR +.ad +.br +.na +\fB\fBSCCS/s.Makefile\fR\fR +.ad +.sp .6 +.RS 4n +\fBSCCS\fR history files for the above makefile(s) +.RE + +.sp +.ne 2 +.na +\fB\fBmake.rules\fR\fR +.ad +.sp .6 +.RS 4n +default file for user-defined targets, macros, and implicit rules +.RE + +.sp +.ne 2 +.na +\fB\fB/usr/share/lib/make/make.rules\fR\fR +.ad +.sp .6 +.RS 4n +makefile for standard implicit rules and macros (not read if \fBmake.rules\fR +is) +.RE + +.sp +.ne 2 +.na +\fB\fB\&.make.state\fR\fR +.ad +.sp .6 +.RS 4n +state file in the local directory +.RE + +.SH ATTRIBUTES +.LP +See \fBattributes\fR(5) for descriptions of the following attributes: +.SS "/usr/xpg4/bin/make" +.TS +box; +cw(2.75i) |cw(2.75i) +lw(2.75i) |lw(2.75i) . +ATTRIBUTE TYPE ATTRIBUTE VALUE +_ +Interface Stability Committed +_ +Standard See \fBstandards\fR(5). +.TE + +.SH SEE ALSO +.LP +\fBar\fR(1), \fBarch\fR(1), \fBcd\fR(1), \fBcpp\fR(1), \fBlex\fR(1), +\fBmach\fR(1), \fBsccs-get\fR(1), \fBsh\fR(1), \fBsysV-make\fR(1), +\fByacc\fR(1), \fBwordexp\fR(3C), \fBpasswd\fR(4), \fBattributes\fR(5), +\fBenviron\fR(5), \fBPOSIX.2\fR(5), \fBstandards\fR(5) +.sp +.LP +\fISolaris Advanced User\&'s Guide\fR +.SH DIAGNOSTICS +.ne 2 +.na +\fB\fBDon't know how to make target \fItarget\fR\fR\fR +.ad +.sp .6 +.RS 4n +There is no makefile entry for \fItarget\fR, and none of \fBmake\fR's implicit +rules apply (there is no dependency file with a suffix in the suffixes list, or +the target's suffix is not in the list). +.RE + +.sp +.ne 2 +.na +\fB\fB***\fR \fItarget\fR \fBremoved.\fR\fR +.ad +.sp .6 +.RS 4n +\fBmake\fR was interrupted while building \fItarget\fR. Rather than leaving a +partially-completed version that is newer than its dependencies, \fBmake\fR +removes the file named \fItarget\fR. +.RE + +.sp +.ne 2 +.na +\fB\fB***\fR \fItarget\fR \fBnot removed.\fR\fR +.ad +.sp .6 +.RS 4n +\fBmake\fR was interrupted while building \fItarget\fR and \fItarget\fR was not +present in the directory. +.RE + +.sp +.ne 2 +.na +\fB\fB***\fR \fItarget\fR \fBcould not be removed,\fR \fIreason\fR\fR +.ad +.sp .6 +.RS 4n +\fBmake\fR was interrupted while building \fItarget\fR, which was not removed +for the indicated reason. +.RE + +.sp +.ne 2 +.na +\fB\fBRead of include file\fR \fBfile\fR \fBfailed\fR\fR +.ad +.sp .6 +.RS 4n +The makefile indicated in an \fBinclude\fR directive was not found, or was +inaccessible. +.RE + +.sp +.ne 2 +.na +\fB\fBLoop detected when expanding macro value\fR \fImacro\fR\fB\&'\fR\fR +.ad +.sp .6 +.RS 4n +A reference to the macro being defined was found in the definition. +.RE + +.sp +.ne 2 +.na +\fB\fBCould not write state file\fR \fIfile\fR\fB\fR\fR +.ad +.sp .6 +.RS 4n +You used the \fB\&.KEEP_STATE:\fR target, but do not have write permission on +the state file. +.RE + +.sp +.ne 2 +.na +\fB\fB***Error code\fR \fIn\fR\fR +.ad +.sp .6 +.RS 4n +The previous shell command returned a nonzero error code. +.RE + +.sp +.ne 2 +.na +\fB\fB***\fR \fIsignal message\fR\fR +.ad +.sp .6 +.RS 4n +The previous shell command was aborted due to a signal. If \fB- core dumped\fR +appears after the message, a \fBcore\fR file was created. +.RE + +.sp +.ne 2 +.na +\fB\fBConditional macro conflict encountered\fR\fR +.ad +.sp .6 +.RS 4n +Displayed only when \fB-d\fR is in effect, this message indicates that two or +more parallel targets currently being processed depend on a target which is +built differently for each by virtue of conditional macros. Since the target +cannot simultaneously satisfy both dependency relationships, it is conflicted. +.RE + +.SH BUGS +.LP +Some commands return nonzero status inappropriately; to overcome this +difficulty, prefix the offending command line in the rule with a \fB\(mi\fR\&. +.sp +.LP +Filenames with the characters \fB=\fR, \fB:\fR, or \fB@\fR, do not work. +.sp +.LP +You cannot build \fBfile.o\fR from \fBlib(file.o)\fR. +.sp +.LP +Options supplied by \fBMAKEFLAGS\fR should be reported for nested \fBmake\fR +commands. Use the \fB-d\fR option to find out what options the nested command +picks up from \fBMAKEFLAGS\fR. +.sp +.LP +This version of \fBmake\fR is incompatible in certain respects with previous +versions: +.RS +4 +.TP +.ie t \(bu +.el o +The \fB-d\fR option output is much briefer in this version. \fB-dd\fR now +produces the equivalent voluminous output. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +\fBmake\fR attempts to derive values for the dynamic macros \fB$*\fR, \fB$<\fR, +and \fB$?\fR, while processing explicit targets. It uses the same method as for +implicit rules; in some cases this can lead either to unexpected values, or to +an empty value being assigned. (Actually, this was true for earlier versions as +well, even though the documentation stated otherwise.) +.RE +.RS +4 +.TP +.ie t \(bu +.el o +\fBmake\fR no longer searches for \fBSCCS\fR history (\fBs\fR.) files. +.RE +.RS +4 +.TP +.ie t \(bu +.el o +Suffix replacement in macro references are now applied after the macro is +expanded. +.RE +.sp +.LP +There is no guarantee that makefiles created for this version of \fBmake\fR +work with earlier versions. +.sp +.LP +If there is no \fBmake.rules\fR file in the current directory, and the file +\fB/usr/share/lib/make/make.rules\fR is missing, \fBmake\fR stops before +processing any targets. To force \fBmake\fR to run anyway, create an empty +\fBmake.rules\fR file in the current directory. +.sp +.LP +Once a dependency is made, \fBmake\fR assumes the dependency file is present +for the remainder of the run. If a rule subsequently removes that file and +future targets depend on its existence, unexpected errors can result. +.sp +.LP +When hidden dependency checking is in effect, the \fB$?\fR macro's value +includes the names of hidden dependencies. This can lead to improper filename +arguments to commands when \fB$?\fR is used in a rule. +.sp +.LP +Pattern replacement macro references cannot be used in the dependency list of a +pattern matching rule. +.sp +.LP +Unlike previous versions, this version of \fBmake\fR strips a leading +\fB\&./\fR from the value of the \fB$@\fR dynamic macro. +.sp +.LP +With automatic \fBSCCS\fR retrieval, this version of \fBmake\fR does not +support tilde suffix rules. +.sp +.LP +The only dynamic macro whose value is strictly determined when used in a +dependency list is \fB$@\fR (takes the form \fB$$@\fR). +.sp +.LP +\fBmake\fR invokes the shell with the \fB-e\fR argument. This cannot be +inferred from the syntax of the rule alone. From 6804ba69d600db117d13b218dc8ad54fd64ebd6b Mon Sep 17 00:00:00 2001 From: Georg Sauthoff Date: Sun, 21 Aug 2016 14:32:02 +0200 Subject: [PATCH 3/3] import Sun's dmake from illumos Source: svn export https://github.com/illumos/illumos-gate/trunk/usr/src/cmd/make/ Yes, github also has a Subversion compatible interface - which seems to be useful when one is only interested in a set of files. The Github Subversion server printed: Exported revision 15816. This corresponds to: https://github.com/illumos/illumos-gate/tree/67c3092ccd4e8c261df7eded9df072ff9c4e330b/usr/src/cmd/make --- bin/ar.cc | 846 +++++++++ bin/depvar.cc | 108 ++ bin/doname.cc | 3209 ++++++++++++++++++++++++++++++++++ bin/dosys.cc | 163 ++ bin/files.cc | 711 ++++++++ bin/globals.cc | 181 ++ bin/implicit.cc | 1462 ++++++++++++++++ bin/macro.cc | 167 ++ bin/main.cc | 3215 +++++++++++++++++++++++++++++++++++ bin/make.rules.file | 498 ++++++ bin/misc.cc | 736 ++++++++ bin/nse_printdep.cc | 365 ++++ bin/parallel.cc | 1892 +++++++++++++++++++++ bin/pmake.cc | 420 +++++ bin/read.cc | 2148 +++++++++++++++++++++++ bin/read2.cc | 1898 +++++++++++++++++++++ bin/rep.cc | 394 +++++ bin/state.cc | 444 +++++ bin/svr4.make.rules.file | 241 +++ include/bsd/bsd.h | 47 + include/mk/defs.h | 396 +++++ include/mksh/defs.h | 945 ++++++++++ include/mksh/dosys.h | 37 + include/mksh/globals.h | 30 + include/mksh/i18n.h | 33 + include/mksh/libmksh_init.h | 31 + include/mksh/macro.h | 36 + include/mksh/misc.h | 55 + include/mksh/mksh.h | 37 + include/mksh/read.h | 33 + include/vroot/args.h | 63 + include/vroot/report.h | 48 + include/vroot/vroot.h | 61 + lib/bsd/bsd.cc | 73 + lib/makestate/ld_file.c | 188 ++ lib/makestate/lock.c | 172 ++ lib/mksh/dosys.cc | 577 +++++++ lib/mksh/globals.cc | 128 ++ lib/mksh/i18n.cc | 91 + lib/mksh/macro.cc | 1335 +++++++++++++++ lib/mksh/misc.cc | 1113 ++++++++++++ lib/mksh/mksh.cc | 131 ++ lib/mksh/read.cc | 170 ++ lib/vroot/access.cc | 42 + lib/vroot/args.cc | 31 + lib/vroot/chdir.cc | 41 + lib/vroot/chmod.cc | 46 + lib/vroot/chown.cc | 47 + lib/vroot/chroot.cc | 44 + lib/vroot/creat.cc | 47 + lib/vroot/execve.cc | 50 + lib/vroot/lock.cc | 175 ++ lib/vroot/lstat.cc | 46 + lib/vroot/mkdir.cc | 46 + lib/vroot/mount.cc | 47 + lib/vroot/open.cc | 49 + lib/vroot/readlink.cc | 46 + lib/vroot/report.cc | 333 ++++ lib/vroot/rmdir.cc | 44 + lib/vroot/setenv.cc | 61 + lib/vroot/stat.cc | 46 + lib/vroot/truncate.cc | 45 + lib/vroot/unlink.cc | 44 + lib/vroot/utimes.cc | 46 + lib/vroot/vroot.cc | 338 ++++ 65 files changed, 26642 insertions(+) create mode 100644 bin/ar.cc create mode 100644 bin/depvar.cc create mode 100644 bin/doname.cc create mode 100644 bin/dosys.cc create mode 100644 bin/files.cc create mode 100644 bin/globals.cc create mode 100644 bin/implicit.cc create mode 100644 bin/macro.cc create mode 100644 bin/main.cc create mode 100644 bin/make.rules.file create mode 100644 bin/misc.cc create mode 100644 bin/nse_printdep.cc create mode 100644 bin/parallel.cc create mode 100644 bin/pmake.cc create mode 100644 bin/read.cc create mode 100644 bin/read2.cc create mode 100644 bin/rep.cc create mode 100644 bin/state.cc create mode 100644 bin/svr4.make.rules.file create mode 100644 include/bsd/bsd.h create mode 100644 include/mk/defs.h create mode 100644 include/mksh/defs.h create mode 100644 include/mksh/dosys.h create mode 100644 include/mksh/globals.h create mode 100644 include/mksh/i18n.h create mode 100644 include/mksh/libmksh_init.h create mode 100644 include/mksh/macro.h create mode 100644 include/mksh/misc.h create mode 100644 include/mksh/mksh.h create mode 100644 include/mksh/read.h create mode 100644 include/vroot/args.h create mode 100644 include/vroot/report.h create mode 100644 include/vroot/vroot.h create mode 100644 lib/bsd/bsd.cc create mode 100644 lib/makestate/ld_file.c create mode 100644 lib/makestate/lock.c create mode 100644 lib/mksh/dosys.cc create mode 100644 lib/mksh/globals.cc create mode 100644 lib/mksh/i18n.cc create mode 100644 lib/mksh/macro.cc create mode 100644 lib/mksh/misc.cc create mode 100644 lib/mksh/mksh.cc create mode 100644 lib/mksh/read.cc create mode 100644 lib/vroot/access.cc create mode 100644 lib/vroot/args.cc create mode 100644 lib/vroot/chdir.cc create mode 100644 lib/vroot/chmod.cc create mode 100644 lib/vroot/chown.cc create mode 100644 lib/vroot/chroot.cc create mode 100644 lib/vroot/creat.cc create mode 100644 lib/vroot/execve.cc create mode 100644 lib/vroot/lock.cc create mode 100644 lib/vroot/lstat.cc create mode 100644 lib/vroot/mkdir.cc create mode 100644 lib/vroot/mount.cc create mode 100644 lib/vroot/open.cc create mode 100644 lib/vroot/readlink.cc create mode 100644 lib/vroot/report.cc create mode 100644 lib/vroot/rmdir.cc create mode 100644 lib/vroot/setenv.cc create mode 100644 lib/vroot/stat.cc create mode 100644 lib/vroot/truncate.cc create mode 100644 lib/vroot/unlink.cc create mode 100644 lib/vroot/utimes.cc create mode 100644 lib/vroot/vroot.cc diff --git a/bin/ar.cc b/bin/ar.cc new file mode 100644 index 0000000..898f7aa --- /dev/null +++ b/bin/ar.cc @@ -0,0 +1,846 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * ar.c + * + * Deal with the lib.a(member.o) and lib.a((entry-point)) notations + * + * Look inside archives for notations a(b) and a((b)) + * a(b) is file member b in archive a + * a((b)) is entry point b in object archive a + * + * For 6.0, create a make which can understand all archive + * formats. This is kind of tricky, and isnt any help. + */ + +/* + * Included files + */ +#include /* alloca() */ +#include +#include /* errno */ +#include /* open() */ +#include +#include +#include /* retmem_mb() */ + +struct ranlib { + union { + off_t ran_strx; /* string table index of */ + char *ran_name; /* symbol defined by */ + } ran_un; + off_t ran_off; /* library member at this offset */ +}; + +#include /* close() */ + + +/* + * Defined macros + */ +#ifndef S5EMUL +#undef BITSPERBYTE +#define BITSPERBYTE 8 +#endif + +/* + * Defines for all the different archive formats. See next comment + * block for justification for not using s versions. + */ +#define AR_5_MAGIC "" /* 5.0 format magic string */ +#define AR_5_MAGIC_LENGTH 4 /* 5.0 format string length */ + +#define AR_PORT_MAGIC "!\n" /* Port. (6.0) magic string */ +#define AR_PORT_MAGIC_LENGTH 8 /* Port. (6.0) string length */ +#define AR_PORT_END_MAGIC "`\n" /* Port. (6.0) end of header */ +#define AR_PORT_WORD 4 /* Port. (6.0) 'word' length */ + +/* + * typedefs & structs + */ +/* + * These are the archive file headers for the formats. Note + * that it really doesnt matter if these structures are defined + * here. They are correct as of the respective archive format + * releases. If the archive format is changed, then since backwards + * compatability is the desired behavior, a new structure is added + * to the list. + */ +typedef struct { /* 5.0 ar header format: vax family; 3b family */ + char ar_magic[AR_5_MAGIC_LENGTH]; /* AR_5_MAGIC*/ + char ar_name[16]; /* Space terminated */ + char ar_date[AR_PORT_WORD]; /* sgetl() accessed */ + char ar_syms[AR_PORT_WORD]; /* sgetl() accessed */ +} Arh_5; + +typedef struct { /* 5.0 ar symbol format: vax family; 3b family */ + char sym_name[8]; /* Space terminated */ + char sym_ptr[AR_PORT_WORD]; /* sgetl() accessed */ +} Ars_5; + +typedef struct { /* 5.0 ar member format: vax family; 3b family */ + char arf_name[16]; /* Space terminated */ + char arf_date[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_uid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_gid[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_mode[AR_PORT_WORD]; /* sgetl() accessed */ + char arf_size[AR_PORT_WORD]; /* sgetl() accessed */ +} Arf_5; + +typedef struct { /* Portable (6.0) ar format: vax family; 3b family */ + char ar_name[16]; /* Space terminated */ + /* left-adjusted fields; decimal ascii; blank filled */ + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; /* octal ascii */ + char ar_size[10]; + /* special end-of-header string (AR_PORT_END_MAGIC) */ + char ar_fmag[2]; +} Ar_port; + +enum ar_type { + AR_5, + AR_PORT +}; + +typedef unsigned int ar_port_word; // must be 4-bytes long + +typedef struct { + FILE *fd; + /* to distiguish ar format */ + enum ar_type type; + /* where first ar member header is at */ + long first_ar_mem; + /* where the symbol lookup starts */ + long sym_begin; + /* the number of symbols available */ + long num_symbols; + /* length of symbol directory file */ + long sym_size; + Arh_5 arh_5; + Ars_5 ars_5; + Arf_5 arf_5; + Ar_port ar_port; +} Ar; + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& read_archive(register Name target); +static Boolean open_archive(char *filename, register Ar *arp); +static void close_archive(register Ar *arp); +static Boolean read_archive_dir(register Ar *arp, Name library, char **long_names_table); +static void translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table); +static long sgetl(char *); + +/* + * read_archive(target) + * + * Read the contents of an ar file. + * + * Return value: + * The time the member was created + * + * Parameters: + * target The member to find time for + * + * Global variables used: + * empty_name The Name "" + */ + +int read_member_header (Ar_port *header, FILE *fd, char* filename); +int process_long_names_member (register Ar *arp, char **long_names_table, char *filename); + +timestruc_t& +read_archive(register Name target) +{ + register Property member; + wchar_t *slash; + String_rec true_member_name; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Name true_member = NULL; + Ar ar; + char *long_names_table = NULL; /* Table of long + member names */ + + member = get_prop(target->prop, member_prop); + /* + * Check if the member has directory component. + * If so, remove the dir and see if we know the date. + */ + if (member->body.member.member != NULL) { + Wstring member_string(member->body.member.member); + wchar_t * wcb = member_string.get_string(); + if((slash = (wchar_t *) wcsrchr(wcb, (int) slash_char)) != NULL) { + INIT_STRING_FROM_STACK(true_member_name, buffer); + append_string(member->body.member.library->string_mb, + &true_member_name, + FIND_LENGTH); + append_char((int) parenleft_char, &true_member_name); + append_string(slash + 1, &true_member_name, FIND_LENGTH); + append_char((int) parenright_char, &true_member_name); + true_member = GETNAME(true_member_name.buffer.start, + FIND_LENGTH); + if (true_member->stat.time != file_no_time) { + target->stat.time = true_member->stat.time; + return target->stat.time; + } + } + } + if (open_archive(member->body.member.library->string_mb, &ar) == failed) { + if (errno == ENOENT) { + target->stat.stat_errno = ENOENT; + close_archive(&ar); + if (member->body.member.member == NULL) { + member->body.member.member = empty_name; + } + return target->stat.time = file_doesnt_exist; + } else { + fatal(gettext("Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (target->stat.time == file_no_time) { + if (read_archive_dir(&ar, member->body.member.library, + &long_names_table) + == failed){ + fatal(gettext("Can't access archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } + } + if (member->body.member.entry != NULL) { + translate_entry(&ar, target, member,&long_names_table); + } + close_archive(&ar); + if (long_names_table) { + retmem_mb(long_names_table); + } + if (true_member != NULL) { + target->stat.time = true_member->stat.time; + } + if (target->stat.time == file_no_time) { + target->stat.time = file_doesnt_exist; + } + return target->stat.time; +} + +/* + * open_archive(filename, arp) + * + * Return value: + * Indicates if open failed or not + * + * Parameters: + * filename The name of the archive we need to read + * arp Pointer to ar file description block + * + * Global variables used: + */ +static Boolean +open_archive(char *filename, register Ar *arp) +{ + int fd; + char mag_5[AR_5_MAGIC_LENGTH]; + char mag_port[AR_PORT_MAGIC_LENGTH]; + char buffer[4]; + + arp->fd = NULL; + fd = open_vroot(filename, O_RDONLY, 0, NULL, VROOT_DEFAULT); + if ((fd < 0) || ((arp->fd = fdopen(fd, "r")) == NULL)) { + return failed; + } + (void) fcntl(fileno(arp->fd), F_SETFD, 1); + + if (fread(mag_port, AR_PORT_MAGIC_LENGTH, 1, arp->fd) != 1) { + return failed; + } + if (IS_EQUALN(mag_port, AR_PORT_MAGIC, AR_PORT_MAGIC_LENGTH)) { + arp->type = AR_PORT; + /* + * Read in first member header to find out if there is + * a symbol definition table. + */ + + int ret = read_member_header(&arp->ar_port, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd); + return succeeded; + } + /* + * The following values are the default if there is + * no symbol directory and long member names. + */ + arp->sym_size = arp->num_symbols = arp->sym_begin = 0L; + arp->first_ar_mem = ftell(arp->fd) - (long) sizeof (Ar_port); + + /* + * Do we have a symbol table? A symbol table is always + * the first member in an archive. In 4.1.x it has the + * name __.SYMDEF, in SVr4, it has the name "/ " + */ +/* + MBSTOWCS(wcs_buffer, "/ "); + if (IS_WEQUALN(arp->ar_port.ar_name, wcs_buffer, 16)) { + */ + if (IS_EQUALN(arp->ar_port.ar_name, + "/ ", + 16)) { + if (sscanf(arp->ar_port.ar_size, + "%ld", + &arp->sym_size) != 1) { + return failed; + } + arp->sym_size += (arp->sym_size & 1); /* round up */ + if (fread(buffer, sizeof buffer, 1, arp->fd) != 1) { + return failed; + } + arp->num_symbols = sgetl(buffer); + arp->sym_begin = ftell(arp->fd); + arp->first_ar_mem = arp->sym_begin + + arp->sym_size - sizeof buffer; + } + return succeeded; + } + fatal(gettext("`%s' is not an archive"), filename); + /* NOTREACHED */ + return failed; +} + + +/* + * close_archive(arp) + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +static void +close_archive(register Ar *arp) +{ + if (arp->fd != NULL) { + (void) fclose(arp->fd); + } +} + +/* + * read_archive_dir(arp, library, long_names_table) + * + * Reads the directory of an archive and enters all + * the members into the make symboltable in lib(member) format + * with their dates. + * + * Parameters: + * arp Pointer to ar file description block + * library Name of lib to enter members for. + * Used to form "lib(member)" string. + * long_names_table table that contains list of members + * with names > 15 characters long + * + * Global variables used: + */ +static Boolean +read_archive_dir(register Ar *arp, Name library, char **long_names_table) +{ + wchar_t *name_string; + wchar_t *member_string; + register long len; + register wchar_t *p; + register char *q; + register Name name; + Property member; + long ptr; + long date; + + int offset; + + /* + * If any of the members has a name > 15 chars, + * it will be found here. + */ + if (process_long_names_member(arp, long_names_table, library->string_mb) == failed) { + return failed; + } + name_string = ALLOC_WC((int) (library->hash.length + + (int) ar_member_name_len * 2)); + (void) mbstowcs(name_string, library->string_mb, (int) library->hash.length); + member_string = name_string + library->hash.length; + *member_string++ = (int) parenleft_char; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + goto read_error; + } + /* Read the directory using the appropriate format */ + switch (arp->type) { + case AR_5: + for (;;) { + if (fread((char *) &arp->arf_5, sizeof arp->arf_5, 1, arp->fd) + != 1) { + if (feof(arp->fd)) { + return succeeded; + } + break; + } + len = sizeof arp->arf_5.arf_name; + for (p = member_string, q = arp->arf_5.arf_name; + (len > 0) && (*q != (int) nul_char) && !isspace(*q); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + /* + * [tolik] Fix for dmake bug 1234018. + * If name->stat.time is already set, then it should not + * be changed. (D)make propogates time stamp for one + * member, and when it calls exists() for another member, + * the first one may be changed. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + name->stat.time.tv_nsec = LONG_MAX; + } + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + ptr = sgetl(arp->arf_5.arf_size); + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + case AR_PORT: + for (;;) { + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + if (feof(arp->fd)) { + return succeeded; + } + fatal( + gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), + library->string_mb, + ftell(arp->fd) + ); + } + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + /* + * "len" is used for hashing the string. + * We're using "ar_member_name_len" instead of + * the actual name length since it's the longest + * string the "ar" command can handle at this + * point. + */ + len = ar_member_name_len; + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + q = *long_names_table + offset; + } else { + q = arp->ar_port.ar_name; + len = sizeof arp->ar_port.ar_name; + } + + for (p = member_string; + (len > 0) && + (*q != (int) nul_char) && + !isspace(*q) && + (*q != (int) slash_char); + ) { + MBTOWC(p, q); + p++; + q++; + } + *p++ = (int) parenright_char; + *p = (int) nul_char; + name = GETNAME(name_string, FIND_LENGTH); + name->is_member = library->is_member; + member = maybe_append_prop(name, member_prop); + member->body.member.library = library; + *--p = (int) nul_char; + if (member->body.member.member == NULL) { + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + } + if (sscanf(arp->ar_port.ar_date, "%ld", &date) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(gettext("Bad date field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + /* + * [tolik] Fix for dmake bug 1234018. + */ + if(name->stat.time == file_no_time) { + name->stat.time.tv_sec = date; + name->stat.time.tv_nsec = LONG_MAX; + } + if (sscanf(arp->ar_port.ar_size, "%ld", &ptr) != 1) { + WCSTOMBS(mbs_buffer, name_string); + fatal(gettext("Bad size field for member `%s' in archive `%s'"), + mbs_buffer, + library->string_mb); + } + ptr += (ptr & 1); + if (fseek(arp->fd, ptr, 1) != 0) { + goto read_error; + } + } + break; + } + + /* Only here if fread() [or IS_EQUALN()] failed and not at EOF */ +read_error: + fatal(gettext("Read error in archive `%s': %s"), + library->string_mb, + errmsg(errno)); + /* NOTREACHED */ +} + + +/* + * process_long_names_member(arp) + * + * If the archive contains members with names longer + * than 15 characters, then it has a special member + * with the name "// " that contains a table + * of null-terminated long names. This member + * is always the first member, after the symbol table + * if it exists. + * + * Parameters: + * arp Pointer to ar file description block + * + * Global variables used: + */ +int +process_long_names_member(register Ar *arp, char **long_names_table, char *filename) +{ + Ar_port *ar_member_header; + int table_size; + + if (fseek(arp->fd, arp->first_ar_mem, 0) != 0) { + return failed; + } + if ((ar_member_header = + (Ar_port *) alloca((int) sizeof(Ar_port))) == NULL){ + perror(gettext("memory allocation failure")); + return failed; + } + int ret = read_member_header(ar_member_header, arp->fd, filename); + if (ret == failed) { + return failed; + } else if(ret == -1) { + /* There is no member header - empty archive */ + return succeeded; + } + /* Do we have special member containing long names? */ + if (IS_EQUALN(ar_member_header->ar_name, + "// ", + 16)){ + if (sscanf(ar_member_header->ar_size, + "%ld", + &table_size) != 1) { + return failed; + } + *long_names_table = (char *) malloc(table_size); + /* Read the list of long member names into the table */ + if (fread(*long_names_table, table_size, 1, arp->fd) != 1) { + return failed; + } + arp->first_ar_mem = ftell(arp->fd); + } + return succeeded; +} + +/* + * translate_entry(arp, target, member) + * + * Finds the member for one lib.a((entry)) + * + * Parameters: + * arp Pointer to ar file description block + * target Target to find member name for + * member Property to fill in with info + * + * Global variables used: + */ +static void +translate_entry(register Ar *arp, Name target, register Property member, char **long_names_table) +{ + register int len; + register int i; + wchar_t *member_string; + ar_port_word *offs; + int strtablen; + char *syms; /* string table */ + char *csym; /* string table */ + ar_port_word *offend; /* end of offsets table */ + int date; + register wchar_t *ap; + register char *hp; + int maxs; + int offset; + char buffer[4]; + + if (arp->sym_begin == 0L || arp->num_symbols == 0L) { + fatal(gettext("Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + } + + if (fseek(arp->fd, arp->sym_begin, 0) != 0) { + goto read_error; + } + member_string = ALLOC_WC((int) ((int) ar_member_name_len * 2)); + + switch (arp->type) { + case AR_5: + if ((len = member->body.member.entry->hash.length) > 8) { + len = 8; + } + for (i = 0; i < arp->num_symbols; i++) { + if (fread((char *) &arp->ars_5, + sizeof arp->ars_5, + 1, + arp->fd) != 1) { + goto read_error; + } + if (IS_EQUALN(arp->ars_5.sym_name, + member->body.member.entry->string_mb, + len)) { + if ((fseek(arp->fd, + sgetl(arp->ars_5.sym_ptr), + 0) != 0) || + (fread((char *) &arp->arf_5, + sizeof arp->arf_5, + 1, + arp->fd) != 1)) { + goto read_error; + } + MBSTOWCS(wcs_buffer, arp->arf_5.arf_name); + (void) wcsncpy(member_string, + wcs_buffer, + wcslen(wcs_buffer)); + member_string[sizeof(arp->arf_5.arf_name)] = + (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = sgetl(arp->arf_5.arf_date); + target->stat.time.tv_nsec = LONG_MAX; + return; + } + } + break; + case AR_PORT: + offs = (ar_port_word *) alloca((int) (arp->num_symbols * AR_PORT_WORD)); + if (fread((char *) offs, + AR_PORT_WORD, + (int) arp->num_symbols, + arp->fd) != arp->num_symbols) { + goto read_error; + } + + for(i=0;inum_symbols;i++) { + *((int*)buffer)=offs[i]; + offs[i]=(ar_port_word)sgetl(buffer); + } + + strtablen=arp->sym_size-4-(int) (arp->num_symbols * AR_PORT_WORD); + syms = (char *) alloca(strtablen); + if (fread(syms, + sizeof (char), + strtablen, + arp->fd) != strtablen) { + goto read_error; + } + offend = &offs[arp->num_symbols]; + while (offs < offend) { + maxs = strlen(member->body.member.entry->string_mb); + if(strlen(syms) > maxs) + maxs = strlen(syms); + if (IS_EQUALN(syms, + member->body.member.entry->string_mb, + maxs)) { + if (fseek(arp->fd, + (long) *offs, + 0) != 0) { + goto read_error; + } + if ((fread((char *) &arp->ar_port, + sizeof arp->ar_port, + 1, + arp->fd) != 1) || + !IS_EQUALN(arp->ar_port.ar_fmag, + AR_PORT_END_MAGIC, + sizeof arp->ar_port.ar_fmag)) { + goto read_error; + } + if (sscanf(arp->ar_port.ar_date, + "%ld", + &date) != 1) { + fatal(gettext("Bad date field for member `%s' in archive `%s'"), + arp->ar_port.ar_name, + target->string_mb); + } + /* If it's a long name, retrieve it from long name table */ + if (arp->ar_port.ar_name[0] == '/') { + sscanf(arp->ar_port.ar_name + 1, + "%ld", + &offset); + len = ar_member_name_len; + hp = *long_names_table + offset; + } else { + len = sizeof arp->ar_port.ar_name; + hp = arp->ar_port.ar_name; + } + ap = member_string; + while (*hp && + (*hp != (int) slash_char) && + (ap < &member_string[len])) { + MBTOWC(ap, hp); + ap++; + hp++; + } + *ap = (int) nul_char; + member->body.member.member = + GETNAME(member_string, FIND_LENGTH); + target->stat.time.tv_sec = date; + target->stat.time.tv_nsec = LONG_MAX; + return; + } + offs++; + while(*syms!='\0') syms++; + syms++; + } + } + fatal(gettext("Cannot find symbol `%s' in archive `%s'"), + member->body.member.entry->string_mb, + member->body.member.library->string_mb); + /*NOTREACHED*/ + +read_error: + if (ferror(arp->fd)) { + fatal(gettext("Read error in archive `%s': %s"), + member->body.member.library->string_mb, + errmsg(errno)); + } else { + fatal(gettext("Read error in archive `%s': Premature EOF"), + member->body.member.library->string_mb); + } +} + +/* + * sgetl(buffer) + * + * The intent here is to provide a means to make the value of + * bytes in an io-buffer correspond to the value of a long + * in the memory while doing the io a long at a time. + * Files written and read in this way are machine-independent. + * + * Return value: + * Long int read from buffer + * Parameters: + * buffer buffer we need to read long int from + * + * Global variables used: + */ +static long +sgetl(register char *buffer) +{ + register long w = 0; + register int i = BITSPERBYTE * AR_PORT_WORD; + + while ((i -= BITSPERBYTE) >= 0) { + w |= (long) ((unsigned char) *buffer++) << i; + } + return w; +} + + +/* + * read_member_header(header, fd, filename) + * + * reads the member header for the 4.1.x and SVr4 archives. + * + * Return value: + * fails if read error or member + * header is not the right format + * Parameters: + * header There's one before each archive member + * fd file descriptor for the archive file. + * + * Global variables used: + */ +int +read_member_header(Ar_port *header, FILE *fd, char* filename) +{ + int num = fread((char *) header, sizeof (Ar_port), 1, fd); + if (num != 1 && feof(fd)) { + /* There is no member header - empty archive */ + return -1; + } + if ((num != 1) || + !IS_EQUALN( + AR_PORT_END_MAGIC, + header->ar_fmag, + sizeof (header->ar_fmag) + ) + ) { + fatal( + gettext("Read error in archive `%s': invalid archive file member header at 0x%x"), + filename, + ftell(fd) + ); + } + return succeeded; +} + diff --git a/bin/depvar.cc b/bin/depvar.cc new file mode 100644 index 0000000..4f348a5 --- /dev/null +++ b/bin/depvar.cc @@ -0,0 +1,108 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Included files + */ +#include + +#include +#include /* getmem() */ + +/* + * This file deals with "Dependency Variables". + * The "-V var" command line option is used to indicate + * that var is a dependency variable. Used in conjunction with + * the -P option the user is asking if the named variables affect + * the dependencies of the given target. + */ + +struct _Depvar { + Name name; /* Name of variable */ + struct _Depvar *next; /* Linked list */ + Boolean cmdline; /* Macro defined on the cmdline? */ +}; + +typedef struct _Depvar *Depvar; + +static Depvar depvar_list; +static Depvar *bpatch = &depvar_list; +static Boolean variant_deps; + +/* + * Add a name to the list. + */ + +void +depvar_add_to_list(Name name, Boolean cmdline) +{ + Depvar dv; + + dv = ALLOC(Depvar); + dv->name = name; + dv->next = NULL; + dv->cmdline = cmdline; + *bpatch = dv; + bpatch = &dv->next; +} + +/* + * The macro `name' has been used in either the left-hand or + * right-hand side of a dependency. See if it is in the + * list. Two things are looked for. Names given as args + * to the -V list are checked so as to set the same/differ + * output for the -P option. Names given as macro=value + * command-line args are checked and, if found, an NSE + * warning is produced. + */ +void +depvar_dep_macro_used(Name name) +{ + Depvar dv; + + for (dv = depvar_list; dv != NULL; dv = dv->next) { + if (name == dv->name) { + variant_deps = true; + break; + } + } +} + + +/* + * Print the results. If any of the Dependency Variables + * affected the dependencies then the dependencies potentially + * differ because of these variables. + */ +void +depvar_print_results(void) +{ + if (variant_deps) { + printf(gettext("differ\n")); + } else { + printf(gettext("same\n")); + } +} + diff --git a/bin/doname.cc b/bin/doname.cc new file mode 100644 index 0000000..85be203 --- /dev/null +++ b/bin/doname.cc @@ -0,0 +1,3209 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + * + * Copyright 2016 RackTop Systems. + */ + +/* + * doname.c + * + * Figure out which targets are out of date and rebuild them + */ + +/* + * Included files + */ +#include /* alloca() */ +#include +#include +#include /* get_char_semantics_value() */ +#include /* getvar(), expand_value() */ +#include /* getmem() */ +#include +#include +#include +#include +#include +#include +#include +#include /* uname() */ +#include +#include /* close() */ + +/* + * Defined macros + */ +# define LOCALHOST "localhost" + +#define MAXRULES 100 + +// Sleep for .1 seconds between stat()'s +const int STAT_RETRY_SLEEP_TIME = 100000; + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char hostName[MAXNAMELEN] = ""; +static char userName[MAXNAMELEN] = ""; + + +static int second_pass = 0; + +/* + * File table of contents + */ +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +static Boolean check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals); +void dynamic_dependencies(Name target); +static Doname run_command(register Property line, Boolean print_machine); +extern Doname execute_serial(Property line); +extern Name vpath_translation(register Name cmd); +extern void check_state(Name temp_file_name); +static void read_dependency_file(register Name filename); +static void check_read_state_file(void); +static void do_assign(register Name line, register Name target); +static void build_command_strings(Name target, register Property line); +static Doname touch_command(register Property line, register Name target, Doname result); +extern void update_target(Property line, Doname result); +static Doname sccs_get(register Name target, register Property *command); +extern void read_directory_of_file(register Name file); +static void add_pattern_conditionals(register Name target); +extern void set_locals(register Name target, register Property old_locals); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +static void delete_query_chain(Chain ch); + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + + + +/* + * DONE. + * + * doname_check(target, do_get, implicit, automatic) + * + * Will call doname() and then inspect the return value + * + * Return value: + * Indication if the build failed or not + * + * Parameters: + * target The target to build + * do_get Passed thru to doname() + * implicit Passed thru to doname() + * automatic Are we building a hidden dependency? + * + * Global variables used: + * build_failed_seen Set if -k is on and error occurs + * continue_after_error Indicates that -k is on + * report_dependencies No error msg if -P is on + */ +Doname +doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + int first_time = 1; + (void) fflush(stdout); +try_again: + switch (doname(target, do_get, implicit, automatic)) { + case build_ok: + second_pass = 0; + return build_ok; + case build_running: + second_pass = 0; + return build_running; + case build_failed: + if (!continue_after_error) { + fatal(gettext("Target `%s' not remade because of errors"), + target->string_mb); + } + build_failed_seen = true; + second_pass = 0; + return build_failed; + case build_dont_know: + /* + * If we can't figure out how to build an automatic + * (hidden) dependency, we just ignore it. + * We later declare the target to be out of date just in + * case something changed. + * Also, don't complain if just reporting the dependencies + * and not building anything. + */ + if (automatic || (report_dependencies_level > 0)) { + second_pass = 0; + return build_dont_know; + } + if(first_time) { + first_time = 0; + second_pass = 1; + goto try_again; + } + second_pass = 0; + if (continue_after_error && !svr4) { + warning(gettext("Don't know how to make target `%s'"), + target->string_mb); + build_failed_seen = true; + return build_failed; + } + fatal(gettext("Don't know how to make target `%s'"), target->string_mb); + break; + } +#ifdef lint + return build_failed; +#endif +} + + +void +enter_explicit_rule_from_dynamic_rule(Name target, Name source) +{ + Property line, source_line; + Dependency dependency; + + source_line = get_prop(source->prop, line_prop); + line = maybe_append_prop(target, line_prop); + line->body.line.sccs_command = false; + line->body.line.target = target; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = source_line->body.line.command_template; + for (dependency = source_line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + enter_dependency(line, dependency->name, false); + } + line->body.line.less = target; + } + line->body.line.percent = NULL; +} + + + +Name +find_dyntarget(Name target) +{ + Dyntarget p; + int i; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *pp, * bufend; + wchar_t tbuffer[MAXPATHLEN]; + Wstring wcb(target); + + for (p = dyntarget_list; p != NULL; p = p->next) { + INIT_STRING_FROM_STACK(string, buffer); + expand_value(p->name, &string, false); + i = 0; + pp = string.buffer.start; + bufend = pp + STRING_BUFFER_LENGTH; + while((*pp != nul_char) && (pp < bufend)) { + if(iswspace(*pp)) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + pp++; + i = 0; + continue; + } + tbuffer[i] = *pp; + i++; + pp++; + if(*pp == nul_char) { + tbuffer[i] = nul_char; + if(i > 0) { + if (wcb.equal(tbuffer)) { + enter_explicit_rule_from_dynamic_rule(target, p->name); + return(target); + } + } + break; + } + } + } + return(NULL); +} + +/* + * DONE. + * + * doname(target, do_get, implicit) + * + * Chases all files the target depends on and builds any that + * are out of date. If the target is out of date it is then rebuilt. + * + * Return value: + * Indiates if build failed or nt + * + * Parameters: + * target Target to build + * do_get Run sccs get is nessecary + * implicit doname is trying to find an implicit rule + * + * Global variables used: + * assign_done True if command line assgnment has happened + * commands_done Preserved for the case that we need local value + * debug_level Should we trace make's actions? + * default_rule The rule for ".DEFAULT", used as last resort + * empty_name The Name "", used when looking for single sfx + * keep_state Indicates that .KEEP_STATE is on + * parallel True if building in parallel + * recursion_level Used for tracing + * report_dependencies make -P is on + */ +Doname +doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic) +{ + Doname result = build_dont_know; + Chain out_of_date_list = NULL; + Chain target_group; + Property old_locals = NULL; + register Property line; + Property command = NULL; + register Dependency dependency; + Name less = NULL; + Name true_target = target; + Name *automatics = NULL; + register int auto_count; + Boolean rechecking_target = false; + Boolean saved_commands_done; + Boolean restart = false; + Boolean save_parallel = parallel; + Boolean doing_subtree = false; + + Boolean recheck_conditionals = false; + + if (target->state == build_running) { + return build_running; + } + line = get_prop(target->prop, line_prop); + if (line != NULL) { + /* + * If this target is a member of target group and one of the + * other members of the group is running, mark this target + * as running. + */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + target->state = build_running; + add_pending(target, + recursion_level, + do_get, + implicit, + false); + return build_running; + } + } + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + if (true_target->colon_splits > 0) { + /* Make sure we have a valid time for :: targets */ + Property time; + + time = get_prop(true_target->prop, time_prop); + if (time != NULL) { + true_target->stat.time = time->body.time.time; + } + } + } + (void) exists(true_target); + /* + * If the target has been processed, we don't need to do it again, + * unless it depends on conditional macros or a delayed assignment, + * or it has been done when KEEP_STATE is on. + */ + if (target->state == build_ok) { + if((!keep_state || (!target->depends_on_conditional && !assign_done))) { + return build_ok; + } else { + recheck_conditionals = true; + } + } + if (target->state == build_subtree) { + /* A dynamic macro subtree is being built */ + target->state = build_dont_know; + doing_subtree = true; + if (!target->checking_subtree) { + /* + * This target has been started before and therefore + * not all dependencies have to be built. + */ + restart = true; + } + } else if (target->state == build_pending) { + target->state = build_dont_know; + restart = true; +/* + } else if (parallel && + keep_state && + (target->conditional_cnt > 0)) { + if (!parallel_ok(target, false)) { + add_subtree(target, recursion_level, do_get, implicit); + target->state = build_running; + return build_running; + } + */ + } + /* + * If KEEP_STATE is on, we have to rebuild the target if the + * building of it caused new automatic dependencies to be reported. + * This is where we restart the build. + */ + if (line != NULL) { + line->body.line.percent = NULL; + } +recheck_target: + /* Init all local variables */ + result = build_dont_know; + out_of_date_list = NULL; + command = NULL; + less = NULL; + auto_count = 0; + if (!restart && line != NULL) { + /* + * If this target has never been built before, mark all + * of the dependencies as never built. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + dependency->built = false; + } + } + /* Save the set of automatic depes defined for this target */ + if (keep_state && + (line != NULL) && + (line->body.line.dependencies != NULL)) { + Name *p; + + /* + * First run thru the dependency list to see how many + * autos there are. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + auto_count++; + } + } + /* Create vector to hold the current autos */ + automatics = + (Name *) alloca((int) (auto_count * sizeof (Name))); + /* Copy them */ + for (p = automatics, dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (dependency->automatic && !dependency->stale) { + *p++ = dependency->name; + } + } + } + if (debug_level > 1) { + (void) printf("%*sdoname(%s)\n", + recursion_level, + "", + target->string_mb); + } + recursion_level++; + /* Avoid infinite loops */ + if (target->state == build_in_progress) { + warning(gettext("Infinite loop: Target `%s' depends on itself"), + target->string_mb); + return build_ok; + } + target->state = build_in_progress; + + /* Activate conditional macros for the target */ + if (!target->added_pattern_conditionals) { + add_pattern_conditionals(target); + target->added_pattern_conditionals = true; + } + if (target->conditional_cnt > 0) { + old_locals = (Property) alloca(target->conditional_cnt * + sizeof (Property_rec)); + set_locals(target, old_locals); + } + +/* + * after making the call to dynamic_dependecies unconditional we can handle + * target names that are same as file name. In this case $$@ in the + * dependencies did not mean anything. WIth this change it expands it + * as expected. + */ + if (!target->has_depe_list_expanded) + { + dynamic_dependencies(target); + } + +/* + * FIRST SECTION -- GO THROUGH DEPENDENCIES AND COLLECT EXPLICIT + * COMMANDS TO RUN + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + + +/* + * If the target is a :: type, do not try to find the rule for the target, + * all actions will be taken by separate branches. + * Else, we try to find an implicit rule using various methods, + * we quit as soon as one is found. + * + * [tolik, 12 Sep 2002] Do not try to find implicit rule for the target + * being rechecked - the target is being rechecked means that it already + * has explicit dependencies derived from an implicit rule found + * in previous step. + */ + if (target->colon_splits == 0 && !rechecking_target) { + /* Look for percent matched rule */ + if ((result == build_dont_know) && + (command == NULL)) { + switch (find_percent_rule( + target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_ok: + result = build_ok; + break; + } + } + /* Look for double suffix rule */ + if (result == build_dont_know) { + Property member; + + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + switch (find_ar_suffix_rule(target, + member->body. + member.member, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + default: + /* ALWAYS bind $% for old style */ + /* ar rules */ + if (line == NULL) { + line = + maybe_append_prop(target, + line_prop); + } + line->body.line.percent = + member->body.member.member; + break; + } + } else { + switch (find_double_suffix_rule(target, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target-> + prop, + conditional_prop), + 0); + } + return build_running; + } + } + } + /* Look for single suffix rule */ + +/* /tolik/ + * I commented !implicit to fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * This caused problem with SVR4 tilde rules (infinite recursion). So I made some changes in "implicit.cc" + */ +/* /tolik, 06.21.96/ + * Regression! See BugId 1255360 + * If more than one percent rules are defined for the same target then + * the behaviour of 'make' with my previous fix may be different from one + * of the 'old make'. + * The global variable second_pass (maybe it should be an argument to doname()) + * is intended to avoid this regression. It is set in doname_check(). + * First, 'make' will work as it worked before. Only when it is + * going to say "don't know how to make target" it sets second_pass to true and + * run 'doname' again but now trying to use Single Suffix Rules. + */ + if ((result == build_dont_know) && !automatic && (!implicit || second_pass) && + ((line == NULL) || + ((line->body.line.target != NULL) && + !line->body.line.target->has_regular_dependency))) { + switch (find_suffix_rule(target, + target, + empty_name, + &command, + recheck_conditionals)) { + case build_failed: + result = build_failed; + break; + case build_running: + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + } + } + /* Try to sccs get */ + if ((command == NULL) && + (result == build_dont_know) && + do_get) { + result = sccs_get(target, &command); + } + + /* Use .DEFAULT rule if it is defined. */ + if ((command == NULL) && + (result == build_dont_know) && + (true_target->colons == no_colon) && + default_rule && + !implicit) { + /* Make sure we have a line prop */ + line = maybe_append_prop(target, line_prop); + command = line; + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using .DEFAULT because it is out of date\n"), + recursion_level, + "", + true_target->string_mb); + } + } + line->body.line.sccs_command = false; + line->body.line.command_template = default_rule; + line->body.line.target = true_target; + line->body.line.star = NULL; + line->body.line.less = true_target; + line->body.line.percent = NULL; + } + } + + /* We say "target up to date" if no cmd were executed for the target */ + if (!target->is_double_colon_parent) { + commands_done = false; + } + + silent = silent_all; + ignore_errors = ignore_errors_all; + if (posix) + { + if (!silent) + { + silent = (Boolean) target->silent_mode; + } + if (!ignore_errors) + { + ignore_errors = (Boolean) target->ignore_error_mode; + } + } + + int doname_dyntarget = 0; +r_command: + /* Run commands if any. */ + if ((command != NULL) && + (command->body.line.command_template != NULL)) { + if (result != build_failed) { + result = run_command(command, + (Boolean) ((parallel || save_parallel) && !silent)); + } + switch (result) { + case build_running: + add_running(target, + true_target, + command, + --recursion_level, + auto_count, + automatics, + do_get, + implicit); + target->state = build_running; + if ((line = get_prop(target->prop, + line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_serial: + add_serial(target, + --recursion_level, + do_get, + implicit); + target->state = build_running; + line = get_prop(target->prop, line_prop); + if (line != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return build_running; + case build_ok: + /* If all went OK set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + break; + } + } else { + /* + * If no command was found for the target, and it doesn't + * exist, and it is mentioned as a target in the makefile, + * we say it is extremely new and that it is OK. + */ + if (target->colons != no_colon) { + if (true_target->stat.time == file_doesnt_exist){ + true_target->stat.time = file_max_time; + } + result = build_ok; + } + /* + * Trying dynamic targets. + */ + if(!doname_dyntarget) { + doname_dyntarget = 1; + Name dtarg = find_dyntarget(target); + if(dtarg!=NULL) { + if (!target->has_depe_list_expanded) { + dynamic_dependencies(target); + } + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (check_dependencies(&result, + line, + do_get, + target, + true_target, + doing_subtree, + &out_of_date_list, + old_locals, + implicit, + &command, + less, + rechecking_target, + recheck_conditionals)) + { + return build_running; + } + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = out_of_date_list; + } + goto r_command; + } + } + /* + * If the file exists, it is OK that we couldnt figure + * out how to build it. + */ + (void) exists(target); + if ((target->stat.time != file_doesnt_exist) && + (result == build_dont_know)) { + result = build_ok; + } + } + + /* + * Some of the following is duplicated in the function finish_doname. + * If anything is changed here, check to see if it needs to be + * changed there. + */ + if ((line = get_prop(target->prop, line_prop)) != NULL) { + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + } + target->state = result; + parallel = save_parallel; + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, conditional_prop), + 0); + } + recursion_level--; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member*/ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, rechecking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + rechecking_target = true; + saved_commands_done = commands_done; + goto recheck_target; + } + + if (rechecking_target && !commands_done) { + commands_done = saved_commands_done; + } + + return result; +} + +/* + * DONE. + * + * check_dependencies(result, line, do_get, + * target, true_target, doing_subtree, out_of_date_tail, + * old_locals, implicit, command, less, rechecking_target) + * + * Return value: + * True returned if some dependencies left running + * + * Parameters: + * result Pointer to cell we update if build failed + * line We get the dependencies from here + * do_get Allow use of sccs get in recursive doname() + * target The target to chase dependencies for + * true_target The real one for :: and lib(member) + * doing_subtree True if building a conditional macro subtree + * out_of_date_tail Used to set the $? list + * old_locals Used for resetting the local macros + * implicit Called when scanning for implicit rules? + * command Place to stuff command + * less Set to $< value + * + * Global variables used: + * command_changed Set if we suspect .make.state needs rewrite + * debug_level Should we trace actions? + * force The Name " FORCE", compared against + * recursion_level Used for tracing + * rewrite_statefile Set if .make.state needs rewriting + * wait_name The Name ".WAIT", compared against + */ +static Boolean +check_dependencies(Doname *result, Property line, Boolean do_get, Name target, Name true_target, Boolean doing_subtree, Chain *out_of_date_tail, Property old_locals, Boolean implicit, Property *command, Name less, Boolean rechecking_target, Boolean recheck_conditionals) +{ + Boolean dependencies_running; + register Dependency dependency; + Doname dep_result; + Boolean dependency_changed = false; + + line->body.line.dependency_time = file_doesnt_exist; + if (line->body.line.query != NULL) { + delete_query_chain(line->body.line.query); + } + line->body.line.query = NULL; + line->body.line.is_out_of_date = false; + dependencies_running = false; + /* + * Run thru all the dependencies and call doname() recursively + * on each of them. + */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Boolean this_dependency_changed = false; + + if (!dependency->automatic && + (rechecking_target || target->rechecking_target)) { + /* + * We only bother with the autos when rechecking + */ + continue; + } + + if (dependency->name == wait_name) { + /* + * The special target .WAIT means finish all of + * the prior dependencies before continuing. + */ + if (dependencies_running) { + break; + } + } else if ((!parallel_ok(dependency->name, false)) && + (dependencies_running)) { + /* + * If we can't execute the current dependency in + * parallel, hold off the dependency processing + * to preserve the order of the dependencies. + */ + break; + } else { + timestruc_t depe_time = file_doesnt_exist; + + + if (true_target->is_member) { + depe_time = exists(dependency->name); + } + if (dependency->built || + (dependency->name->state == build_failed)) { + dep_result = (Doname) dependency->name->state; + } else { + dep_result = doname_check(dependency->name, + do_get, + false, + (Boolean) dependency->automatic); + } + if (true_target->is_member || dependency->name->is_member) { + /* should compare only secs, cause lib members does not have nsec time resolution */ + if (depe_time.tv_sec != dependency->name->stat.time.tv_sec) { + this_dependency_changed = + dependency_changed = + true; + } + } else { + if (depe_time != dependency->name->stat.time) { + this_dependency_changed = + dependency_changed = + true; + } + } + dependency->built = true; + switch (dep_result) { + case build_running: + dependencies_running = true; + continue; + case build_failed: + *result = build_failed; + break; + case build_dont_know: +/* + * If make can't figure out how to make a dependency, maybe the dependency + * is out of date. In this case, we just declare the target out of date + * and go on. If we really need the dependency, the make'ing of the target + * will fail. This will only happen for automatic (hidden) dependencies. + */ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + /* + * Make sure the dependency is not saved + * in the state file. + */ + dependency->stale = true; + rewrite_statefile = + command_changed = + true; + if (debug_level > 0) { + (void) printf(gettext("Target %s rebuilt because dependency %s does not exist\n"), + true_target->string_mb, + dependency->name->string_mb); + } + break; + } + if (dependency->name->depends_on_conditional) { + target->depends_on_conditional = true; + } + if (dependency->name == force) { + target->stat.time = + dependency->name->stat.time; + } + /* + * Propagate new timestamp from "member" to + * "lib.a(member)". + */ + (void) exists(dependency->name); + + /* Collect the timestamp of the youngest dependency */ + line->body.line.dependency_time = + MAX(dependency->name->stat.time, + line->body.line.dependency_time); + + /* Correction: do not consider nanosecs for members */ + if(true_target->is_member || dependency->name->is_member) { + line->body.line.dependency_time.tv_nsec = 0; + } + + if (debug_level > 1) { + (void) printf(gettext("%*sDate(%s)=%s \n"), + recursion_level, + "", + dependency->name->string_mb, + time_to_string(dependency->name-> + stat.time)); + if (dependency->name->stat.time > line->body.line.dependency_time) { + (void) printf(gettext("%*sDate-dependencies(%s) set to %s\n"), + recursion_level, + "", + true_target->string_mb, + time_to_string(line->body.line. + dependency_time)); + } + } + + /* Build the $? list */ + if (true_target->is_member) { + if (this_dependency_changed == true) { + true_target->stat.time = dependency->name->stat.time; + true_target->stat.time.tv_sec--; + } else { + /* Dina: + * The next statement is commented + * out as a fix for bug #1051032. + * if dependency hasn't changed + * then there's no need to invalidate + * true_target. This statemnt causes + * make to take much longer to process + * an already-built archive. Soren + * said it was a quick fix for some + * problem he doesn't remember. + true_target->stat.time = file_no_time; + */ + (void) exists(true_target); + } + } else { + (void) exists(true_target); + } + Boolean out_of_date; + if (true_target->is_member || dependency->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + dependency->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + dependency->name->stat.time); + } + if ((build_unconditional || out_of_date) && + (dependency->name != force) && + (dependency->stale == false)) { + *out_of_date_tail = ALLOC(Chain); + if (dependency->name->is_member && + (get_prop(dependency->name->prop, + member_prop) != NULL)) { + (*out_of_date_tail)->name = + get_prop(dependency->name->prop, + member_prop)-> + body.member.member; + } else { + (*out_of_date_tail)->name = + dependency->name; + } + (*out_of_date_tail)->next = NULL; + out_of_date_tail = &(*out_of_date_tail)->next; + if (debug_level > 0) { + if (dependency->name->stat.time == file_max_time) { + (void) printf(gettext("%*sBuilding %s because %s does not exist\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } else { + (void) printf(gettext("%*sBuilding %s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + dependency->name->string_mb); + } + } + } + if (dependency->name == force) { + force->stat.time = + file_max_time; + force->state = build_dont_know; + } + } + } + if (dependencies_running) { + if (doing_subtree) { + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } else { + target->state = build_running; + add_pending(target, + --recursion_level, + do_get, + implicit, + false); + if (target->conditional_cnt > 0) { + reset_locals(target, + old_locals, + get_prop(target->prop, + conditional_prop), + 0); + } + return true; + } + } + /* + * Collect the timestamp of the youngest double colon target + * dependency. + */ + if (target->is_double_colon_parent) { + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + Property tmp_line; + + if ((tmp_line = get_prop(dependency->name->prop, line_prop)) != NULL) { + if(tmp_line->body.line.dependency_time != file_max_time) { + target->stat.time = + MAX(tmp_line->body.line.dependency_time, + target->stat.time); + } + } + } + } + if ((true_target->is_member) && (dependency_changed == true)) { + true_target->stat.time = file_no_time; + } + /* + * After scanning all the dependencies, we check the rule + * if we found one. + */ + if (line->body.line.command_template != NULL) { + if (line->body.line.command_template_redefined) { + warning(gettext("Too many rules defined for target %s"), + target->string_mb); + } + *command = line; + /* Check if the target is out of date */ + Boolean out_of_date; + if (true_target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(true_target->stat.time, + line->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(true_target->stat.time, + line->body.line.dependency_time); + } + if (build_unconditional || out_of_date){ + if(!recheck_conditionals) { + line->body.line.is_out_of_date = true; + } + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + if(gnu_style) { + + // set $< for explicit rule + if(line->body.line.dependencies != NULL) { + less = line->body.line.dependencies->name; + } + + // set $* for explicit rule + Name target_body; + Name tt = true_target; + Property member; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Wstring targ_string; + Wstring suf_string; + + if (true_target->is_member && + ((member = get_prop(target->prop, member_prop)) != + NULL)) { + tt = member->body.member.member; + } + targ_string.init(tt); + target_end = targ_string.get_string() + tt->hash.length; + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (tt->hash.length < suffix_length) { + continue; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + continue; + } + target_body = GETNAME( + targ_string.get_string(), + (int)(tt->hash.length - suffix_length) + ); + line->body.line.star = target_body; + } + + // set result = build_ok so that implicit rules are not used. + if(*result == build_dont_know) { + *result = build_ok; + } + } + if (less != NULL) { + line->body.line.less = less; + } + } + + return false; +} + +/* + * dynamic_dependencies(target) + * + * Checks if any dependency contains a macro ref + * If so, it replaces the dependency with the expanded version. + * Here, "$@" gets translated to target->string. That is + * the current name on the left of the colon in the + * makefile. Thus, + * xyz: s.$@.c + * translates into + * xyz: s.xyz.c + * + * Also, "$(@F)" translates to the same thing without a preceeding + * directory path (if one exists). + * Note, to enter "$@" on a dependency line in a makefile + * "$$@" must be typed. This is because make expands + * macros in dependency lists upon reading them. + * dynamic_dependencies() also expands file wildcards. + * If there are any Shell meta characters in the name, + * search the directory, and replace the dependency + * with the set of files the pattern matches + * + * Parameters: + * target Target to sanitize dependencies for + * + * Global variables used: + * c_at The Name "@", used to set macro value + * debug_level Should we trace actions? + * dot The Name ".", used to read directory + * recursion_level Used for tracing + */ +void +dynamic_dependencies(Name target) +{ + wchar_t pattern[MAXPATHLEN]; + register wchar_t *p; + Property line; + register Dependency dependency; + register Dependency *remove; + String_rec string; + wchar_t buffer[MAXPATHLEN]; + register Boolean set_at = false; + register wchar_t *start; + Dependency new_depe; + register Boolean reuse_cell; + Dependency first_member; + Name directory; + Name lib; + Name member; + Property prop; + Name true_target = target; + wchar_t *library; + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + /* If the target is constructed from a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + /* Scan all dependencies and process the ones that contain "$" chars */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->name->dollar) { + continue; + } + target->has_depe_list_expanded = true; + + /* The make macro $@ is bound to the target name once per */ + /* invocation of dynamic_dependencies() */ + if (!set_at) { + (void) SETVAR(c_at, true_target, false); + set_at = true; + } + /* Expand this dependency string */ + INIT_STRING_FROM_STACK(string, buffer); + expand_value(dependency->name, &string, false); + /* Scan the expanded string. It could contain whitespace */ + /* which mean it expands to several dependencies */ + start = string.buffer.start; + while (iswspace(*start)) { + start++; + } + /* Remove the cell (later) if the macro was empty */ + if (start[0] == (int) nul_char) { + dependency->name = NULL; + } + +/* azv 10/26/95 to fix bug BID_1170218 */ + if ((start[0] == (int) period_char) && + (start[1] == (int) slash_char)) { + start += 2; + } +/* azv */ + + first_member = NULL; + /* We use the original dependency cell for the first */ + /* dependency from the expansion */ + reuse_cell = true; + /* We also have to deal with dependencies that expand to */ + /* lib.a(members) notation */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) parenleft_char)) { + lib = GETNAME(start, p - start); + lib->is_member = true; + first_member = dependency; + start = p + 1; + while (iswspace(*start)) { + start++; + } + break; + } + } + do { + /* First skip whitespace */ + for (p = start; *p != (int) nul_char; p++) { + if ((*p == (int) nul_char) || + iswspace(*p) || + (*p == (int) parenright_char)) { + break; + } + } + /* Enter dependency from expansion */ + if (p != start) { + /* Create new dependency cell if */ + /* this is not the first dependency */ + /* picked from the expansion */ + if (!reuse_cell) { + new_depe = ALLOC(Dependency); + new_depe->next = dependency->next; + new_depe->automatic = false; + new_depe->stale = false; + new_depe->built = false; + dependency->next = new_depe; + dependency = new_depe; + } + reuse_cell = false; + /* Internalize the dependency name */ + // tolik. Fix for bug 4110429: inconsistent expansion for macros that + // include "//" and "/./" + //dependency->name = GETNAME(start, p - start); + dependency->name = normalize_name(start, p - start); + if ((debug_level > 0) && + (first_member == NULL)) { + (void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + dependency->name->string_mb, + true_target->string_mb); + } + for (start = p; iswspace(*start); start++); + p = start; + } + } while ((*p != (int) nul_char) && + (*p != (int) parenright_char)); + /* If the expansion was of lib.a(members) format we now */ + /* enter the proper member cells */ + if (first_member != NULL) { + /* Scan the new dependencies and transform them from */ + /* "foo" to "lib.a(foo)" */ + for (; 1; first_member = first_member->next) { + /* Build "lib.a(foo)" name */ + INIT_STRING_FROM_STACK(string, buffer); + APPEND_NAME(lib, + &string, + (int) lib->hash.length); + append_char((int) parenleft_char, &string); + APPEND_NAME(first_member->name, + &string, + FIND_LENGTH); + append_char((int) parenright_char, &string); + member = first_member->name; + /* Replace "foo" with "lib.a(foo)" */ + first_member->name = + GETNAME(string.buffer.start, FIND_LENGTH); + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (debug_level > 0) { + (void) printf(gettext("%*sDynamic dependency `%s' for target `%s'\n"), + recursion_level, + "", + first_member->name-> + string_mb, + true_target->string_mb); + } + first_member->name->is_member = lib->is_member; + /* Add member property to member */ + prop = maybe_append_prop(first_member->name, + member_prop); + prop->body.member.library = lib; + prop->body.member.entry = NULL; + prop->body.member.member = member; + if (first_member == dependency) { + break; + } + } + } + } + Wstring wcb; + /* Then scan all the dependencies again. This time we want to expand */ + /* shell file wildcards */ + for (remove = &line->body.line.dependencies, dependency = *remove; + dependency != NULL; + dependency = *remove) { + if (dependency->name == NULL) { + dependency = *remove = (*remove)->next; + continue; + } + /* If dependency name string contains shell wildcards */ + /* replace the name with the expansion */ + if (dependency->name->wildcard) { + wcb.init(dependency->name); + if ((start = (wchar_t *) wcschr(wcb.get_string(), + (int) parenleft_char)) != NULL) { + /* lib(*) type pattern */ + library = buffer; + (void) wcsncpy(buffer, + wcb.get_string(), + start - wcb.get_string()); + buffer[start-wcb.get_string()] = + (int) nul_char; + (void) wcsncpy(pattern, + start + 1, +(int) (dependency->name->hash.length-(start-wcb.get_string())-2)); + pattern[dependency->name->hash.length - + (start-wcb.get_string()) - 2] = + (int) nul_char; + } else { + library = NULL; + (void) wcsncpy(pattern, + wcb.get_string(), + (int) dependency->name->hash.length); + pattern[dependency->name->hash.length] = + (int) nul_char; + } + start = (wchar_t *) wcsrchr(pattern, (int) slash_char); + if (start == NULL) { + directory = dot; + p = pattern; + } else { + directory = GETNAME(pattern, start-pattern); + p = start+1; + } + /* The expansion is handled by the read_dir() routine*/ + if (read_dir(directory, p, line, library)) { + *remove = (*remove)->next; + } else { + remove = &dependency->next; + } + } else { + remove = &dependency->next; + } + } + + /* Then unbind $@ */ + (void) SETVAR(c_at, (Name) NULL, false); +} + +/* + * DONE. + * + * run_command(line) + * + * Takes one Cmd_line and runs the commands from it. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to run + * + * Global variables used: + * commands_done Set if we do run command + * current_line Set to the line we run a command from + * current_target Set to the target we run a command for + * file_number Used to form temp file name + * keep_state Indicates that .KEEP_STATE is on + * make_state The Name ".make.state", used to check timestamp + * parallel True if currently building in parallel + * parallel_process_cnt Count of parallel processes running + * quest Indicates that make -q is on + * rewrite_statefile Set if we do run a command + * sunpro_dependencies The Name "SUNPRO_DEPENDENCIES", set value + * temp_file_directory Used to form temp fie name + * temp_file_name Set to the name of the temp file + * touch Indicates that make -t is on + */ +static Doname +run_command(register Property line, Boolean) +{ + register Doname result = build_ok; + register Boolean remember_only = false; + register Name target = line->body.line.target; + wchar_t *string; + char tmp_file_path[MAXPATHLEN]; + + if (!line->body.line.is_out_of_date && target->rechecking_target) { + target->rechecking_target = false; + return build_ok; + } + + /* + * Build the command if we know the target is out of date, + * or if we want to check cmd consistency. + */ + if (line->body.line.is_out_of_date || keep_state) { + /* Hack for handling conditional macros in DMake. */ + if (!line->body.line.dont_rebuild_command_used) { + build_command_strings(target, line); + } + } + /* Never mind */ + if (!line->body.line.is_out_of_date) { + return build_ok; + } + /* If quest, then exit(1) because the target is out of date */ + if (quest) { + if (posix) { + result = execute_parallel(line, true); + } + exit_status = 1; + exit(1); + } + /* We actually had to do something this time */ + rewrite_statefile = commands_done = true; + /* + * If this is an sccs command, we have to do some extra checking + * and possibly complain. If the file can't be gotten because it's + * checked out, we complain and behave as if the command was + * executed eventhough we ignored the command. + */ + if (!touch && + line->body.line.sccs_command && + (target->stat.time != file_doesnt_exist) && + ((target->stat.mode & 0222) != 0)) { + fatal(gettext("%s is writable so it cannot be sccs gotten"), + target->string_mb); + target->has_complained = remember_only = true; + } + /* + * If KEEP_STATE is on, we make sure we have the timestamp for + * .make.state. If .make.state changes during the command run, + * we reread .make.state after the command. We also setup the + * environment variable that asks utilities to report dependencies. + */ + if (!touch && + keep_state && + !remember_only) { + (void) exists(make_state); + if((strlen(temp_file_directory) == 1) && + (temp_file_directory[0] == '/')) { + tmp_file_path[0] = '\0'; + } else { + strcpy(tmp_file_path, temp_file_directory); + } + sprintf(mbs_buffer, + "%s/.make.dependency.%08x.%d.%d", + tmp_file_path, + hostid, + getpid(), + file_number++); + MBSTOWCS(wcs_buffer, mbs_buffer); + Boolean fnd; + temp_file_name = getname_fn(wcs_buffer, FIND_LENGTH, false, &fnd); + temp_file_name->stat.is_file = true; + int len = 2*MAXPATHLEN + strlen(target->string_mb) + 2; + wchar_t *to = string = ALLOC_WC(len); + for (wchar_t *from = wcs_buffer; *from != (int) nul_char; ) { + if (*from == (int) space_char) { + *to++ = (int) backslash_char; + } + *to++ = *from++; + } + *to++ = (int) space_char; + MBSTOWCS(to, target->string_mb); + Name sprodep_name = getname_fn(string, FIND_LENGTH, false, &fnd); + (void) SETVAR(sunpro_dependencies, + sprodep_name, + false); + retmem(string); + } else { + temp_file_name = NULL; + } + + /* + * In case we are interrupted, we need to know what was going on. + */ + current_target = target; + /* + * We also need to be able to save an empty command instead of the + * interrupted one in .make.state. + */ + current_line = line; + if (remember_only) { + /* Empty block!!! */ + } else if (touch) { + result = touch_command(line, target, result); + if (posix) { + result = execute_parallel(line, true); + } + } else { + /* + * If this is not a touch run, we need to execute the + * proper command(s) for the target. + */ + if (parallel) { + if (!parallel_ok(target, true)) { + /* + * We are building in parallel, but + * this target must be built in serial. + */ + /* + * If nothing else is building, + * do this one, else wait. + */ + if (parallel_process_cnt == 0) { + result = execute_parallel(line, true, target->localhost); + } else { + current_target = NULL; + current_line = NULL; +/* + line->body.line.command_used = NULL; + */ + line->body.line.dont_rebuild_command_used = true; + return build_serial; + } + } else { + result = execute_parallel(line, false); + switch (result) { + case build_running: + return build_running; + case build_serial: + if (parallel_process_cnt == 0) { + result = execute_parallel(line, true, target->localhost); + } else { + current_target = NULL; + current_line = NULL; + target->parallel = false; + line->body.line.command_used = + NULL; + return build_serial; + } + } + } + } else { + result = execute_parallel(line, true, target->localhost); + } + } + temp_file_name = NULL; + if (report_dependencies_level == 0){ + update_target(line, result); + } + current_target = NULL; + current_line = NULL; + return result; +} + +/* + * execute_serial(line) + * + * Runs thru the command line for the target and + * executes the rules one by one. + * + * Return value: + * The result of the command build + * + * Parameters: + * line The command to execute + * + * Static variables used: + * + * Global variables used: + * continue_after_error -k flag + * do_not_exec_rule -n flag + * report_dependencies -P flag + * silent Don't echo commands before executing + * temp_file_name Temp file for auto dependencies + * vpath_defined If true, translate path for command + */ +Doname +execute_serial(Property line) +{ + int child_pid = 0; + Boolean printed_serial; + Doname result = build_ok; + Cmd_line rule, cmd_tail, command = NULL; + char mbstring[MAXPATHLEN]; + int filed; + Name target = line->body.line.target; + + target->has_recursive_dependency = false; + // We have to create a copy of the rules chain for processing because + // the original one can be destroyed during .make.state file rereading. + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (command == NULL) { + command = cmd_tail = ALLOC(Cmd_line); + } else { + cmd_tail->next = ALLOC(Cmd_line); + cmd_tail = cmd_tail->next; + } + *cmd_tail = *rule; + } + if (command) { + cmd_tail->next = NULL; + } + for (rule = command; rule != NULL; rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + /* Echo command line, maybe. */ + if ((rule->command_line->hash.length > 0) && + !silent && + (!rule->silent || do_not_exec_rule) && + (report_dependencies_level == 0)) { + (void) printf("%s\n", rule->command_line->string_mb); + } + if (rule->command_line->hash.length > 0) { + /* Do assignment if command line prefixed with "=" */ + if (rule->assign) { + result = build_ok; + do_assign(rule->command_line, target); + } else if (report_dependencies_level == 0) { + /* Execute command line. */ + setvar_envvar(); + result = dosys(rule->command_line, + (Boolean) rule->ignore_error, + (Boolean) rule->make_refd, + /* ds 98.04.23 bug #4085164. make should always show error messages */ + false, + /* BOOLEAN(rule->silent && + rule->ignore_error), */ + (Boolean) rule->always_exec, + target); + check_state(temp_file_name); + } + } else { + result = build_ok; + } + if (result == build_failed) { + if (silent || rule->silent) { + (void) printf(gettext("The following command caused the error:\n%s\n"), + rule->command_line->string_mb); + } + if (!rule->ignore_error && !ignore_errors) { + if (!continue_after_error) { + fatal(gettext("Command failed for target `%s'"), + target->string_mb); + } + /* + * Make sure a failing command is not + * saved in .make.state. + */ + line->body.line.command_used = NULL; + break; + } else { + result = build_ok; + } + } + } + for (rule = command; rule != NULL; rule = cmd_tail) { + cmd_tail = rule->next; + free(rule); + } + command = NULL; + if (temp_file_name != NULL) { + free_name(temp_file_name); + } + temp_file_name = NULL; + + Property spro = get_prop(sunpro_dependencies->prop, macro_prop); + if(spro != NULL) { + Name val = spro->body.macro.value; + if(val != NULL) { + free_name(val); + spro->body.macro.value = NULL; + } + } + spro = get_prop(sunpro_dependencies->prop, env_mem_prop); + if(spro) { + char *val = spro->body.env_mem.value; + if(val != NULL) { + /* + * Do not return memory allocated for SUNPRO_DEPENDENCIES + * It will be returned in setvar_daemon() in macro.cc + */ + // retmem_mb(val); + spro->body.env_mem.value = NULL; + } + } + + return result; +} + + + +/* + * vpath_translation(cmd) + * + * Translates one command line by + * checking each word. If the word has an alias it is translated. + * + * Return value: + * The translated command + * + * Parameters: + * cmd Command to translate + * + * Global variables used: + */ +Name +vpath_translation(register Name cmd) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec new_cmd; + wchar_t *p; + wchar_t *start; + + if (!vpath_defined || (cmd == NULL) || (cmd->hash.length == 0)) { + return cmd; + } + INIT_STRING_FROM_STACK(new_cmd, buffer); + + Wstring wcb(cmd); + p = wcb.get_string(); + + while (*p != (int) nul_char) { + while (iswspace(*p) && (*p != (int) nul_char)) { + append_char(*p++, &new_cmd); + } + start = p; + while (!iswspace(*p) && (*p != (int) nul_char)) { + p++; + } + cmd = GETNAME(start, p - start); + if (cmd->has_vpath_alias_prop) { + cmd = get_prop(cmd->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + APPEND_NAME(cmd, + &new_cmd, + (int) cmd->hash.length); + } else { + append_string(start, &new_cmd, p - start); + } + } + cmd = GETNAME(new_cmd.buffer.start, FIND_LENGTH); + if (new_cmd.free_after_use) { + retmem(new_cmd.buffer.start); + } + return cmd; +} + +/* + * check_state(temp_file_name) + * + * Reads and checks the state changed by the previously executed command. + * + * Parameters: + * temp_file_name The auto dependency temp file + * + * Global variables used: + */ +void +check_state(Name temp_file_name) +{ + if (!keep_state) { + return; + } + + /* + * Then read the temp file that now might + * contain dependency reports from utilities + */ + read_dependency_file(temp_file_name); + + /* + * And reread .make.state if it + * changed (the command ran recursive makes) + */ + check_read_state_file(); + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } +} + +/* + * read_dependency_file(filename) + * + * Read the temp file used for reporting dependencies to make + * + * Parameters: + * filename The name of the file with the state info + * + * Global variables used: + * makefile_type The type of makefile being read + * read_trace_level Debug flag + * temp_file_number The always increasing number for unique files + * trace_reader Debug flag + */ +static void +read_dependency_file(register Name filename) +{ + register Makefile_type save_makefile_type; + + if (filename == NULL) { + return; + } + filename->stat.time = file_no_time; + if (exists(filename) > file_doesnt_exist) { + save_makefile_type = makefile_type; + makefile_type = reading_cpp_file; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(filename, + false, + false, + false, + false, + false, + false); + trace_reader = false; + makefile_type = save_makefile_type; + } +} + +/* + * check_read_state_file() + * + * Check if .make.state has changed + * If it has we reread it + * + * Parameters: + * + * Global variables used: + * make_state Make state file name + * makefile_type Type of makefile being read + * read_trace_level Debug flag + * trace_reader Debug flag + */ +static void +check_read_state_file(void) +{ + timestruc_t previous = make_state->stat.time; + register Makefile_type save_makefile_type; + register Property makefile; + + make_state->stat.time = file_no_time; + if ((exists(make_state) == file_doesnt_exist) || + (make_state->stat.time == previous)) { + return; + } + save_makefile_type = makefile_type; + makefile_type = rereading_statefile; + /* Make sure we clear the old cached contents of .make.state */ + makefile = maybe_append_prop(make_state, makefile_prop); + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = save_makefile_type; +} + +/* + * do_assign(line, target) + * + * Handles runtime assignments for command lines prefixed with "=". + * + * Parameters: + * line The command that contains an assignment + * target The Name of the target, used for error reports + * + * Global variables used: + * assign_done Set to indicate doname needs to reprocess + */ +static void +do_assign(register Name line, register Name target) +{ + Wstring wcb(line); + register wchar_t *string = wcb.get_string(); + register wchar_t *equal; + register Name name; + register Boolean append = false; + + /* + * If any runtime assignments are done, doname() must reprocess all + * targets in the future since the macro values used to build the + * command lines for the targets might have changed. + */ + assign_done = true; + /* Skip white space. */ + while (iswspace(*string)) { + string++; + } + equal = string; + /* Find "+=" or "=". */ + while (!iswspace(*equal) && + (*equal != (int) plus_char) && + (*equal != (int) equal_char)) { + equal++; + } + /* Internalize macro name. */ + name = GETNAME(string, equal - string); + /* Skip over "+=" "=". */ + while (!((*equal == (int) nul_char) || + (*equal == (int) equal_char) || + (*equal == (int) plus_char))) { + equal++; + } + switch (*equal) { + case nul_char: + fatal(gettext("= expected in rule `%s' for target `%s'"), + line->string_mb, + target->string_mb); + case plus_char: + append = true; + equal++; + break; + } + equal++; + /* Skip over whitespace in front of value. */ + while (iswspace(*equal)) { + equal++; + } + /* Enter new macro value. */ + enter_equal(name, + GETNAME(equal, wcb.get_string() + line->hash.length - equal), + append); +} + +/* + * build_command_strings(target, line) + * + * Builds the command string to used when + * building a target. If the string is different from the previous one + * is_out_of_date is set. + * + * Parameters: + * target Target to build commands for + * line Where to stuff result + * + * Global variables used: + * c_at The Name "@", used to set macro value + * command_changed Set if command is different from old + * debug_level Should we trace activities? + * do_not_exec_rule Always echo when running -n + * empty_name The Name "", used for empty rule + * funny Semantics of characters + * ignore_errors Used to init field for line + * is_conditional Set to false befor evaling macro, checked + * after expanding macros + * keep_state Indicates that .KEEP_STATE is on + * make_word_mentioned Set by macro eval, inits field for cmd + * query The Name "?", used to set macro value + * query_mentioned Set by macro eval, inits field for cmd + * recursion_level Used for tracing + * silent Used to init field for line + */ +static void +build_command_strings(Name target, register Property line) +{ + String_rec command_line; + register Cmd_line command_template = line->body.line.command_template; + register Cmd_line *insert = &line->body.line.command_used; + register Cmd_line used = *insert; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *start; + Name new_command_line; + register Boolean new_command_longer = false; + register Boolean ignore_all_command_dependency = true; + Property member; + static Name less_name; + static Name percent_name; + static Name star; + Name tmp_name; + + if (less_name == NULL) { + MBSTOWCS(wcs_buffer, "<"); + less_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "%"); + percent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "*"); + star = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* We have to check if a target depends on conditional macros */ + /* Targets that do must be reprocessed by doname() each time around */ + /* since the macro values used when building the target might have */ + /* changed */ + conditional_macro_used = false; + /* If we are building a lib.a(member) target $@ should be bound */ + /* to lib.a */ + if (target->is_member && + ((member = get_prop(target->prop, member_prop)) != NULL)) { + target = member->body.member.library; + } + /* If we are building a "::" help target $@ should be bound to */ + /* the real target name */ + /* A lib.a(member) target is never :: */ + if (target->has_target_prop) { + target = get_prop(target->prop, target_prop)-> + body.target.target; + } + /* Bind the magic macros that make supplies */ + tmp_name = target; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(c_at, tmp_name, false); + + tmp_name = line->body.line.star; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(star, tmp_name, false); + + tmp_name = line->body.line.less; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(less_name, tmp_name, false); + + tmp_name = line->body.line.percent; + if(tmp_name != NULL) { + if (tmp_name->has_vpath_alias_prop) { + tmp_name = get_prop(tmp_name->prop, vpath_alias_prop)-> + body.vpath_alias.alias; + } + } + (void) SETVAR(percent_name, tmp_name, false); + + /* $? is seldom used and it is expensive to build */ + /* so we store the list form and build the string on demand */ + Chain query_list = NULL; + Chain *query_list_tail = &query_list; + + for (Chain ch = line->body.line.query; ch != NULL; ch = ch->next) { + *query_list_tail = ALLOC(Chain); + (*query_list_tail)->name = ch->name; + if ((*query_list_tail)->name->has_vpath_alias_prop) { + (*query_list_tail)->name = + get_prop((*query_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + (*query_list_tail)->next = NULL; + query_list_tail = &(*query_list_tail)->next; + } + (void) setvar_daemon(query, + (Name) query_list, + false, + chain_daemon, + false, + debug_level); + + /* build $^ */ + Chain hat_list = NULL; + Chain *hat_list_tail = &hat_list; + + for (Dependency dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* skip automatic dependencies */ + if (!dependency->automatic) { + if ((dependency->name != force) && + (dependency->stale == false)) { + *hat_list_tail = ALLOC(Chain); + + if (dependency->name->is_member && + (get_prop(dependency->name->prop, member_prop) != NULL)) { + (*hat_list_tail)->name = + get_prop(dependency->name->prop, + member_prop)->body.member.member; + } else { + (*hat_list_tail)->name = dependency->name; + } + + if((*hat_list_tail)->name != NULL) { + if ((*hat_list_tail)->name->has_vpath_alias_prop) { + (*hat_list_tail)->name = + get_prop((*hat_list_tail)->name->prop, + vpath_alias_prop)->body.vpath_alias.alias; + } + } + + (*hat_list_tail)->next = NULL; + hat_list_tail = &(*hat_list_tail)->next; + } + } + } + (void) setvar_daemon(hat, + (Name) hat_list, + false, + chain_daemon, + false, + debug_level); + +/* We have two command sequences we need to handle */ +/* The old one that we probably read from .make.state */ +/* and the new one we are building that will replace the old one */ +/* Even when KEEP_STATE is not on we build a new command sequence and store */ +/* it in the line prop. This command sequence is then executed by */ +/* run_command(). If KEEP_STATE is on it is also later written to */ +/* .make.state. The routine replaces the old command line by line with the */ +/* new one trying to reuse Cmd_lines */ + + /* If there is no old command_used we have to start creating */ + /* Cmd_lines to keep the new cmd in */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = NULL; + insert = &used->next; + } + /* Run thru the template for the new command and build the expanded */ + /* new command lines */ + for (; + command_template != NULL; + command_template = command_template->next, insert = &used->next, used = *insert) { + /* If there is no old command_used Cmd_line we need to */ + /* create one and say that cmd consistency failed */ + if (used == NULL) { + new_command_longer = true; + *insert = used = ALLOC(Cmd_line); + used->next = NULL; + used->command_line = empty_name; + } + /* Prepare the Cmd_line for the processing */ + /* The command line prefixes "@-=?" are stripped and that */ + /* information is saved in the Cmd_line */ + used->assign = false; + used->ignore_error = ignore_errors; + used->silent = silent; + used->always_exec = false; + /* Expand the macros in the command line */ + INIT_STRING_FROM_STACK(command_line, buffer); + make_word_mentioned = + query_mentioned = + false; + expand_value(command_template->command_line, &command_line, true); + /* If the macro $(MAKE) is mentioned in the command */ + /* "make -n" runs actually execute the command */ + used->make_refd = make_word_mentioned; + used->ignore_command_dependency = query_mentioned; + /* Strip the prefixes */ + start = command_line.buffer.start; + for (; + iswspace(*start) || + (get_char_semantics_value(*start) & (int) command_prefix_sem); + start++) { + switch (*start) { + case question_char: + used->ignore_command_dependency = true; + break; + case exclam_char: + used->ignore_command_dependency = false; + break; + case equal_char: + used->assign = true; + break; + case hyphen_char: + used->ignore_error = true; + break; + case at_char: + if (!do_not_exec_rule) { + used->silent = true; + } + break; + case plus_char: + if(posix) { + used->always_exec = true; + } + break; + } + } + /* If all command lines of the template are prefixed with "?"*/ + /* the VIRTUAL_ROOT is not used for cmd consistency checks */ + if (!used->ignore_command_dependency) { + ignore_all_command_dependency = false; + } + /* Internalize the expanded and stripped command line */ + new_command_line = GETNAME(start, FIND_LENGTH); + if ((used->command_line == NULL) && + (line->body.line.sccs_command)) { + used->command_line = new_command_line; + new_command_longer = false; + } + /* Compare it with the old one for command consistency */ + if (used->command_line != new_command_line) { + Name vpath_translated = vpath_translation(new_command_line); + if (keep_state && + !used->ignore_command_dependency && (vpath_translated != used->command_line)) { + if (debug_level > 0) { + if (used->command_line != NULL + && *used->command_line->string_mb != + '\0') { + (void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from old\n\t%s\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + "", + used-> + command_line-> + string_mb); + } else { + (void) printf(gettext("%*sBuilding %s because new command \n\t%s\n%*sdifferent from empty old command\n"), + recursion_level, + "", + target->string_mb, + vpath_translated->string_mb, + recursion_level, + ""); + } + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + used->command_line = new_command_line; + } + if (command_line.free_after_use) { + retmem(command_line.buffer.start); + } + } + /* Check if the old command is longer than the new for */ + /* command consistency */ + if (used != NULL) { + *insert = NULL; + if (keep_state && + !ignore_all_command_dependency) { + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s because new command shorter than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + } + /* Check if the new command is longer than the old command for */ + /* command consistency */ + if (new_command_longer && + !ignore_all_command_dependency && + keep_state) { + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s because new command longer than old\n"), + recursion_level, + "", + target->string_mb); + } + command_changed = true; + line->body.line.is_out_of_date = true; + } + /* Unbind the magic macros */ + (void) SETVAR(c_at, (Name) NULL, false); + (void) SETVAR(star, (Name) NULL, false); + (void) SETVAR(less_name, (Name) NULL, false); + (void) SETVAR(percent_name, (Name) NULL, false); + (void) SETVAR(query, (Name) NULL, false); + if (query_list != NULL) { + delete_query_chain(query_list); + } + (void) SETVAR(hat, (Name) NULL, false); + if (hat_list != NULL) { + delete_query_chain(hat_list); + } + + if (conditional_macro_used) { + target->conditional_macro_list = cond_macro_list; + cond_macro_list = NULL; + target->depends_on_conditional = true; + } +} + +/* + * touch_command(line, target, result) + * + * If this is an "make -t" run we do this. + * We touch all targets in the target group ("foo + fie:") if any. + * + * Return value: + * Indicates if the command failed or not + * + * Parameters: + * line The command line to update + * target The target we are touching + * result Initial value for the result we return + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * silent Do not echo commands + */ +static Doname +touch_command(register Property line, register Name target, Doname result) +{ + Name name; + register Chain target_group; + String_rec touch_string; + wchar_t buffer[MAXPATHLEN]; + Name touch_cmd; + Cmd_line rule; + + for (name = target, target_group = NULL; name != NULL;) { + if (!name->is_member) { + /* + * Build a touch command that can be passed + * to dosys(). If KEEP_STATE is on, "make -t" + * will save the proper command, not the + * "touch" in .make.state. + */ + INIT_STRING_FROM_STACK(touch_string, buffer); + MBSTOWCS(wcs_buffer, "touch "); + append_string(wcs_buffer, &touch_string, FIND_LENGTH); + touch_cmd = name; + if (name->has_vpath_alias_prop) { + touch_cmd = get_prop(name->prop, + vpath_alias_prop)-> + body.vpath_alias.alias; + } + APPEND_NAME(touch_cmd, + &touch_string, + FIND_LENGTH); + touch_cmd = GETNAME(touch_string.buffer.start, + FIND_LENGTH); + if (touch_string.free_after_use) { + retmem(touch_string.buffer.start); + } + if (!silent || + do_not_exec_rule && + (target_group == NULL)) { + (void) printf("%s\n", touch_cmd->string_mb); + } + /* Run the touch command, or simulate it */ + if (!do_not_exec_rule) { + result = dosys(touch_cmd, + false, + false, + false, + false, + name); + } else { + result = build_ok; + } + } else { + result = build_ok; + } + if (target_group == NULL) { + target_group = line->body.line.target_group; + } else { + target_group = target_group->next; + } + if (target_group != NULL) { + name = target_group->name; + } else { + name = NULL; + } + } + return result; +} + +/* + * update_target(line, result) + * + * updates the status of a target after executing its commands. + * + * Parameters: + * line The command line block to update + * result Indicates that build is OK so can update + * + * Global variables used: + * do_not_exec_rule Indicates that -n is on + * touch Fake the new timestamp if we are just touching + */ +void +update_target(Property line, Doname result) +{ + Name target; + Chain target_group; + Property line2; + timestruc_t old_stat_time; + Property member; + + /* + * [tolik] Additional fix for bug 1063790. It was fixed + * for serial make long ago, but DMake dumps core when + * target is a symlink and sccs file is newer then target. + * In this case, finish_children() calls update_target() + * with line==NULL. + */ + if(line == NULL) { + /* XXX. Should we do anything here? */ + return; + } + + target = line->body.line.target; + + if ((result == build_ok) && (line->body.line.command_used != NULL)) { + if (do_not_exec_rule || + touch || + (target->is_member && + (line->body.line.command_template != NULL) && + (line->body.line.command_template->command_line->string_mb[0] == 0) && + (line->body.line.command_template->next == NULL))) { + /* If we are simulating execution we need to fake a */ + /* new timestamp for the target we didnt build */ + target->stat.time = file_max_time; + } else { + /* + * If we really built the target we read the new + * timestamp. + * Fix for bug #1110906: if .c file is newer than + * the corresponding .o file which is in an archive + * file, make will compile the .c file but it won't + * update the object in the .a file. + */ + old_stat_time = target->stat.time; + target->stat.time = file_no_time; + (void) exists(target); + if ((target->is_member) && + (target->stat.time == old_stat_time)) { + member = get_prop(target->prop, member_prop); + if (member != NULL) { + target->stat.time = member->body.member.library->stat.time; + target->stat.time.tv_sec++; + } + } + } + /* If the target is part of a group we need to propagate the */ + /* result of the run to all members */ + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + target_group->name->stat.time = target->stat.time; + line2 = maybe_append_prop(target_group->name, + line_prop); + line2->body.line.command_used = + line->body.line.command_used; + line2->body.line.target = target_group->name; + } + } + target->has_built = true; +} + +/* + * sccs_get(target, command) + * + * Figures out if it possible to sccs get a file + * and builds the command to do it if it is. + * + * Return value: + * Indicates if sccs get failed or not + * + * Parameters: + * target Target to get + * command Where to deposit command to use + * + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + * sccs_get_rule The rule to used for sccs getting + */ +static Doname +sccs_get(register Name target, register Property *command) +{ + register int result; + char link[MAXPATHLEN]; + String_rec string; + wchar_t name[MAXPATHLEN]; + register wchar_t *p; + timestruc_t sccs_time; + register Property line; + int sym_link_depth = 0; + + /* For sccs, we need to chase symlinks. */ + while (target->stat.is_sym_link) { + if (sym_link_depth++ > 90) { + fatal(gettext("Can't read symbolic link `%s': Number of symbolic links encountered during path name traversal exceeds 90."), + target->string_mb); + } + /* Read the value of the link. */ + result = readlink_vroot(target->string_mb, + link, + sizeof(link), + NULL, + VROOT_DEFAULT); + if (result == -1) { + fatal(gettext("Can't read symbolic link `%s': %s"), + target->string_mb, errmsg(errno)); + } + link[result] = 0; + /* Use the value to build the proper filename. */ + INIT_STRING_FROM_STACK(string, name); + + Wstring wcb(target); + if ((link[0] != slash_char) && + ((p = (wchar_t *) wcsrchr(wcb.get_string(), slash_char)) != NULL)) { + append_string(wcb.get_string(), &string, p - wcb.get_string() + 1); + } + append_string(link, &string, result); + /* Replace the old name with the translated name. */ + target = normalize_name(string.buffer.start, string.text.p - string.buffer.start); + (void) exists(target); + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + + /* + * read_dir() also reads the ?/SCCS dir and saves information + * about which files have SCSC/s. files. + */ + if (target->stat.has_sccs == DONT_KNOW_SCCS) { + read_directory_of_file(target); + } + switch (target->stat.has_sccs) { + case DONT_KNOW_SCCS: + /* We dont know by now there is no SCCS/s.* */ + target->stat.has_sccs = NO_SCCS; + case NO_SCCS: + /* + * If there is no SCCS/s.* but the plain file exists, + * we say things are OK. + */ + if (target->stat.time > file_doesnt_exist) { + return build_ok; + } + /* If we cant find the plain file, we give up. */ + return build_dont_know; + case HAS_SCCS: + /* + * Pay dirt. We now need to figure out if the plain file + * is out of date relative to the SCCS/s.* file. + */ + sccs_time = exists(get_prop(target->prop, + sccs_prop)->body.sccs.file); + break; + } + + if ((!target->has_complained && + (sccs_time != file_doesnt_exist) && + (sccs_get_rule != NULL))) { + /* only checking */ + if (command == NULL) { + return build_ok; + } + /* + * We provide a command line for the target. The line is a + * "sccs get" command from default.mk. + */ + line = maybe_append_prop(target, line_prop); + *command = line; + if (sccs_time > target->stat.time) { + /* + * And only if the plain file is out of date do we + * request execution of the command. + */ + line->body.line.is_out_of_date = true; + if (debug_level > 0) { + (void) printf(gettext("%*sSccs getting %s because s. file is younger than source file\n"), + recursion_level, + "", + target->string_mb); + } + } + line->body.line.sccs_command = true; + line->body.line.command_template = sccs_get_rule; + if(!svr4 && (!allrules_read || posix)) { + if((target->prop) && + (target->prop->body.sccs.file) && + (target->prop->body.sccs.file->string_mb)) { + if((strlen(target->prop->body.sccs.file->string_mb) == + strlen(target->string_mb) + 2) && + (target->prop->body.sccs.file->string_mb[0] == 's') && + (target->prop->body.sccs.file->string_mb[1] == '.')) { + + line->body.line.command_template = get_posix_rule; + } + } + } + line->body.line.target = target; + /* + * Also make sure the rule is build with $* and $< + * bound properly. + */ + line->body.line.star = NULL; + line->body.line.less = NULL; + line->body.line.percent = NULL; + return build_ok; + } + return build_dont_know; +} + +/* + * read_directory_of_file(file) + * + * Reads the directory the specified file lives in. + * + * Parameters: + * file The file we need to read dir for + * + * Global variables used: + * dot The Name ".", used as the default dir + */ +void +read_directory_of_file(register Name file) +{ + + Wstring file_string(file); + wchar_t * wcb = file_string.get_string(); + wchar_t usr_include_buf[MAXPATHLEN]; + wchar_t usr_include_sys_buf[MAXPATHLEN]; + + register Name directory = dot; + register wchar_t *p = (wchar_t *) wcsrchr(wcb, + (int) slash_char); + register int length = p - wcb; + static Name usr_include; + static Name usr_include_sys; + + if (usr_include == NULL) { + MBSTOWCS(usr_include_buf, "/usr/include"); + usr_include = GETNAME(usr_include_buf, FIND_LENGTH); + MBSTOWCS(usr_include_sys_buf, "/usr/include/sys"); + usr_include_sys = GETNAME(usr_include_sys_buf, FIND_LENGTH); + } + + /* + * If the filename contains a "/" we have to extract the path + * Else the path defaults to ".". + */ + if (p != NULL) { + /* + * Check some popular directories first to possibly + * save time. Compare string length first to gain speed. + */ + if ((usr_include->hash.length == length) && + IS_WEQUALN(usr_include_buf, + wcb, + length)) { + directory = usr_include; + } else if ((usr_include_sys->hash.length == length) && + IS_WEQUALN(usr_include_sys_buf, + wcb, + length)) { + directory = usr_include_sys; + } else { + directory = GETNAME(wcb, length); + } + } + (void) read_dir(directory, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); +} + +/* + * add_pattern_conditionals(target) + * + * Scan the list of conditionals defined for pattern targets and add any + * that match this target to its list of conditionals. + * + * Parameters: + * target The target we should add conditionals for + * + * Global variables used: + * conditionals The list of pattern conditionals + */ +static void +add_pattern_conditionals(register Name target) +{ + register Property conditional; + Property new_prop; + Property *previous; + Name_rec dummy; + wchar_t *pattern; + wchar_t *percent; + int length; + + Wstring wcb(target); + Wstring wcb1; + + for (conditional = get_prop(conditionals->prop, conditional_prop); + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop)) { + wcb1.init(conditional->body.conditional.target); + pattern = wcb1.get_string(); + if (pattern[1] != 0) { + percent = (wchar_t *) wcschr(pattern, (int) percent_char); + /* Check for possible buffer under-read */ + if ((length = wcb.length()-wcslen(percent+1)) <= 0) { + continue; + } + if (!wcb.equaln(pattern, percent-pattern) || + !IS_WEQUAL(wcb.get_string(length), percent+1)) { + continue; + } + } + for (previous = &target->prop; + *previous != NULL; + previous = &(*previous)->next) { + if (((*previous)->type == conditional_prop) && + ((*previous)->body.conditional.sequence > + conditional->body.conditional.sequence)) { + break; + } + } + if (*previous == NULL) { + new_prop = append_prop(target, conditional_prop); + } else { + dummy.prop = NULL; + new_prop = append_prop(&dummy, conditional_prop); + new_prop->next = *previous; + *previous = new_prop; + } + target->conditional_cnt++; + new_prop->body.conditional = conditional->body.conditional; + } +} + +/* + * set_locals(target, old_locals) + * + * Sets any conditional macros for the target. + * Each target carries a possibly empty set of conditional properties. + * + * Parameters: + * target The target to set conditional macros for + * old_locals Space to store old values in + * + * Global variables used: + * debug_level Should we trace activity? + * is_conditional We need to preserve this value + * recursion_level Used for tracing + */ +void +set_locals(register Name target, register Property old_locals) +{ + register Property conditional; + register int i; + register Boolean saved_conditional_macro_used; + Chain cond_name; + Chain cond_chain; + + if (target->dont_activate_cond_values) { + return; + } + + saved_conditional_macro_used = conditional_macro_used; + + /* Scan the list of conditional properties and apply each one */ + for (conditional = get_prop(target->prop, conditional_prop), i = 0; + conditional != NULL; + conditional = get_prop(conditional->next, conditional_prop), + i++) { + /* Save the old value */ + old_locals[i].body.macro = + maybe_append_prop(conditional->body.conditional.name, + macro_prop)->body.macro; + if (debug_level > 1) { + (void) printf(gettext("%*sActivating conditional value: "), + recursion_level, + ""); + } + /* Set the conditional value. Macros are expanded when the */ + /* macro is refd as usual */ + if ((conditional->body.conditional.name != virtual_root) || + (conditional->body.conditional.value != virtual_root)) { + (void) SETVAR(conditional->body.conditional.name, + conditional->body.conditional.value, + (Boolean) conditional->body.conditional.append); + } + cond_name = ALLOC(Chain); + cond_name->name = conditional->body.conditional.name; + } + /* Put this target on the front of the chain of conditional targets */ + cond_chain = ALLOC(Chain); + cond_chain->name = target; + cond_chain->next = conditional_targets; + conditional_targets = cond_chain; + conditional_macro_used = saved_conditional_macro_used; +} + +/* + * reset_locals(target, old_locals, conditional, index) + * + * Removes any conditional macros for the target. + * + * Parameters: + * target The target we are retoring values for + * old_locals The values to restore + * conditional The first conditional block for the target + * index into the old_locals vector + * Global variables used: + * debug_level Should we trace activities? + * recursion_level Used for tracing + */ +void +reset_locals(register Name target, register Property old_locals, register Property conditional, register int index) +{ + register Property this_conditional; + Chain cond_chain; + + if (target->dont_activate_cond_values) { + return; + } + + /* Scan the list of conditional properties and restore the old value */ + /* to each one Reverse the order relative to when we assigned macros */ + this_conditional = get_prop(conditional->next, conditional_prop); + if (this_conditional != NULL) { + reset_locals(target, old_locals, this_conditional, index+1); + } else { + /* Remove conditional target from chain */ + if (conditional_targets == NULL || + conditional_targets->name != target) { + warning(gettext("Internal error: reset target not at head of condtional_targets chain")); + } else { + cond_chain = conditional_targets->next; + retmem_mb((caddr_t) conditional_targets); + conditional_targets = cond_chain; + } + } + get_prop(conditional->body.conditional.name->prop, + macro_prop)->body.macro = old_locals[index].body.macro; + if (conditional->body.conditional.name == virtual_root) { + (void) SETVAR(virtual_root, getvar(virtual_root), false); + } + if (debug_level > 1) { + if (old_locals[index].body.macro.value != NULL) { + (void) printf(gettext("%*sdeactivating conditional value: %s= %s\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb, + old_locals[index].body.macro.value-> + string_mb); + } else { + (void) printf(gettext("%*sdeactivating conditional value: %s =\n"), + recursion_level, + "", + conditional->body.conditional.name-> + string_mb); + } + } +} + +/* + * check_auto_dependencies(target, auto_count, automatics) + * + * Returns true if the target now has a dependency + * it didn't previously have (saved on automatics). + * + * Return value: + * true if new dependency found + * + * Parameters: + * target Target we check + * auto_count Number of old automatic vars + * automatics Saved old automatics + * + * Global variables used: + * keep_state Indicates that .KEEP_STATE is on + */ +Boolean +check_auto_dependencies(Name target, int auto_count, Name *automatics) +{ + Name *p; + int n; + Property line; + Dependency dependency; + + if (keep_state) { + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return false; + } + /* Go thru new list of automatic depes */ + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + /* And make sure that each one existed before we */ + /* built the target */ + if (dependency->automatic && !dependency->stale) { + for (n = auto_count, p = automatics; + n > 0; + n--) { + if (*p++ == dependency->name) { + /* If we can find it on the */ + /* saved list of autos we */ + /* are OK */ + goto not_new; + } + } + /* But if we scan over the old list */ + /* of auto. without finding it it is */ + /* new and we must check it */ + return true; + } + not_new:; + } + return false; + } else { + return false; + } +} + + +// Recursively delete each of the Chain struct on the chain. + +static void +delete_query_chain(Chain ch) +{ + if (ch == NULL) { + return; + } else { + delete_query_chain(ch->next); + retmem_mb((char *) ch); + } +} + +Doname +target_can_be_built(register Name target) { + Doname result = build_dont_know; + Name true_target = target; + Property line; + + if (target == wait_name) { + return(build_ok); + } + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + + (void) exists(true_target); + + if (true_target->state == build_running) { + return(build_running); + } + if (true_target->stat.time != file_doesnt_exist) { + result = build_ok; + } + + /* get line property for the target */ + line = get_prop(true_target->prop, line_prop); + + /* first check for explicit rule */ + if (line != NULL && line->body.line.command_template != NULL) { + result = build_ok; + } + /* try to find pattern rule */ + if (result == build_dont_know) { + result = find_percent_rule(target, NULL, false); + } + + /* try to find double suffix rule */ + if (result == build_dont_know) { + if (target->is_member) { + Property member = get_prop(target->prop, member_prop); + if (member != NULL && member->body.member.member != NULL) { + result = find_ar_suffix_rule(target, member->body.member.member, NULL, false); + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } else { + result = find_double_suffix_rule(target, NULL, false); + } + } + + /* try to find suffix rule */ + if ((result == build_dont_know) && second_pass) { + result = find_suffix_rule(target, target, empty_name, NULL, false); + } + + /* check for sccs */ + if (result == build_dont_know) { + result = sccs_get(target, NULL); + } + + /* try to find dyn target */ + if (result == build_dont_know) { + Name dtarg = find_dyntarget(target); + if (dtarg != NULL) { + result = target_can_be_built(dtarg); + } + } + + /* check whether target was mentioned in makefile */ + if (result == build_dont_know) { + if (target->colons != no_colon) { + result = build_ok; + } + } + + /* result */ + return result; +} diff --git a/bin/dosys.cc b/bin/dosys.cc new file mode 100644 index 0000000..ae7f4ef --- /dev/null +++ b/bin/dosys.cc @@ -0,0 +1,163 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * dosys.cc + * + * Execute one commandline + */ + +/* + * Included files + */ +#include /* open() */ +#include +#include /* doshell(), doexec() */ +#include /* getmem() */ +#include /* open() */ +#include /* getpid() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static int filter_file; +static char *filter_file_name; + +/* + * File table of contents + */ +static void redirect_stderr(void); + +/* + * dosys(command, ignore_error, call_make, silent_error, target) + * + * Check if command string contains meta chars and dispatch to + * the proper routine for executing one command line. + * + * Return value: + * Indicates if the command execution failed + * + * Parameters: + * command The command to run + * ignore_error Should make abort when an error is seen? + * call_make Did command reference $(MAKE) ? + * silent_error Should error messages be suppressed for pmake? + * target Target we are building + * + * Global variables used: + * do_not_exec_rule Is -n on? + * working_on_targets We started processing real targets + */ +Doname +dosys(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target) +{ + timestruc_t before; + register int length = command->hash.length; + Wstring wcb(command); + register wchar_t *p = wcb.get_string(); + register wchar_t *q; + Doname result; + + /* Strip spaces from head of command string */ + while (iswspace(*p)) { + p++, length--; + } + if (*p == (int) nul_char) { + return build_failed; + } + /* If we are faking it we just return */ + if (do_not_exec_rule && + working_on_targets && + !call_make && + !always_exec) { + return build_ok; + } + /* no_action_was_taken is used to print special message */ + no_action_was_taken = false; + + /* Copy string to make it OK to write it. */ + q = ALLOC_WC(length + 1); + (void) wcscpy(q, p); + /* Write the state file iff this command uses make. */ + if (call_make && command_changed) { + write_state_file(0, false); + } + make_state->stat.time = file_no_time; + (void)exists(make_state); + before = make_state->stat.time; + /* + * Run command directly if it contains no shell meta chars, + * else run it using the shell. + */ + if (await(ignore_error, + silent_error, + target, + wcb.get_string(), + command->meta ? + doshell(q, ignore_error, + stdout_file, stderr_file, 0) : + doexec(q, ignore_error, + stdout_file, stderr_file, + vroot_path, 0), + NULL, + -1 + )) { + result = build_ok; + } else { + result = build_failed; + } + retmem(q); + + if ((report_dependencies_level == 0) && + call_make) { + make_state->stat.time = file_no_time; + (void)exists(make_state); + if (before == make_state->stat.time) { + return result; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } + return result; +} diff --git a/bin/files.cc b/bin/files.cc new file mode 100644 index 0000000..1aefc4e --- /dev/null +++ b/bin/files.cc @@ -0,0 +1,711 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * files.c + * + * Various file related routines: + * Figure out if file exists + * Wildcard resolution for directory reader + * Directory reader + */ + + +/* + * Included files + */ +#include /* opendir() */ +#include /* errno */ +#include +#include /* getvar() */ +#include /* get_prop(), append_prop() */ +#include /* lstat() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +extern timestruc_t& exists(register Name target); +extern void set_target_stat(register Name target, struct stat buf); +static timestruc_t& vpath_exists(register Name target); +static Name enter_file_name(wchar_t *name_string, wchar_t *library); +static Boolean star_match(register char *string, register char *pattern); +static Boolean amatch(register wchar_t *string, register wchar_t *pattern); + +/* + * exists(target) + * + * Figure out the timestamp for one target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * debug_level Should we trace the stat call? + * recursion_level Used for tracing + * vpath_defined Was the variable VPATH defined in environment? + */ +timestruc_t& +exists(register Name target) +{ + struct stat buf; + register int result; + + /* We cache stat information. */ + if (target->stat.time != file_no_time) { + return target->stat.time; + } + + /* + * If the target is a member, we have to extract the time + * from the archive. + */ + if (target->is_member && + (get_prop(target->prop, member_prop) != NULL)) { + return read_archive(target); + } + + if (debug_level > 1) { + (void) printf("%*sstat(%s)\n", + recursion_level, + "", + target->string_mb); + } + + result = lstat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + if ((result != -1) && ((buf.st_mode & S_IFMT) == S_IFLNK)) { + /* + * If the file is a symbolic link, we remember that + * and then we get the status for the refd file. + */ + target->stat.is_sym_link = true; + result = stat_vroot(target->string_mb, &buf, NULL, VROOT_DEFAULT); + } else { + target->stat.is_sym_link = false; + } + + if (result < 0) { + target->stat.time = file_doesnt_exist; + target->stat.stat_errno = errno; + if ((errno == ENOENT) && + vpath_defined && +/* azv, fixing bug 1262942, VPATH works with a leaf name + * but not a directory name. + */ + (target->string_mb[0] != (int) slash_char) ) { +/* BID_1214655 */ +/* azv */ + vpath_exists(target); + // return vpath_exists(target); + } + } else { + /* Save all the information we need about the file */ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ + target->stat.time = MAX(buf.st_mtim, file_min_time); + } + } + if ((target->colon_splits > 0) && + (get_prop(target->prop, time_prop) == NULL)) { + append_prop(target, time_prop)->body.time.time = + target->stat.time; + } + return target->stat.time; +} + +/* + * set_target_stat( target, buf) + * + * Called by exists() to set some stat fields in the Name structure + * to those read by the stat_vroot() call (from disk). + * + * Parameters: + * target The target whose stat field is set + * buf stat values (on disk) of the file + * represented by target. + */ +void +set_target_stat(register Name target, struct stat buf) +{ + target->stat.stat_errno = 0; + target->stat.is_file = true; + target->stat.mode = buf.st_mode & 0777; + target->stat.size = buf.st_size; + target->stat.is_dir = + BOOLEAN((buf.st_mode & S_IFMT) == S_IFDIR); + if (target->stat.is_dir) { + target->stat.time = file_is_dir; + } else { + /* target->stat.time = buf.st_mtime; */ +/* BID_1129806 */ +/* vis@nbsp.nsk.su */ + target->stat.time = MAX(buf.st_mtim, file_min_time); + } +} + + +/* + * vpath_exists(target) + * + * Called if exists() discovers that there is a VPATH defined. + * This function stats the VPATH translation of the target. + * + * Return value: + * The time the target was created + * + * Parameters: + * target The target to check + * + * Global variables used: + * vpath_name The Name "VPATH", used to get macro value + */ +static timestruc_t& +vpath_exists(register Name target) +{ + wchar_t *vpath; + wchar_t file_name[MAXPATHLEN]; + wchar_t *name_p; + Name alias; + + /* + * To avoid recursive search through VPATH when exists(alias) is called + */ + vpath_defined = false; + + Wstring wcb(getvar(vpath_name)); + Wstring wcb1(target); + + vpath = wcb.get_string(); + + while (*vpath != (int) nul_char) { + name_p = file_name; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + *name_p++ = *vpath++; + } + *name_p++ = (int) slash_char; + (void) wcscpy(name_p, wcb1.get_string()); + alias = GETNAME(file_name, FIND_LENGTH); + if (exists(alias) != file_doesnt_exist) { + target->stat.is_file = true; + target->stat.mode = alias->stat.mode; + target->stat.size = alias->stat.size; + target->stat.is_dir = alias->stat.is_dir; + target->stat.time = alias->stat.time; + maybe_append_prop(target, vpath_alias_prop)-> + body.vpath_alias.alias = alias; + target->has_vpath_alias_prop = true; + vpath_defined = true; + return alias->stat.time; + } + while ((*vpath != (int) nul_char) && + ((*vpath == (int) colon_char) || iswspace(*vpath))) { + vpath++; + } + } + /* + * Restore vpath_defined + */ + vpath_defined = true; + return target->stat.time; +} + +/* + * read_dir(dir, pattern, line, library) + * + * Used to enter the contents of directories into makes namespace. + * Presence of a file is important when scanning for implicit rules. + * read_dir() is also used to expand wildcards in dependency lists. + * + * Return value: + * Non-0 if we found files to match the pattern + * + * Parameters: + * dir Path to the directory to read + * pattern Pattern for that files should match or NULL + * line When we scan using a pattern we enter files + * we find as dependencies for this line + * library If we scan for "lib.a()" + * + * Global variables used: + * debug_level Should we trace the dir reading? + * dot The Name ".", compared against + * sccs_dir_path The path to the SCCS dir (from PROJECTDIR) + * vpath_defined Was the variable VPATH defined in environment? + * vpath_name The Name "VPATH", use to get macro value + */ +int +read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library) +{ + wchar_t file_name[MAXPATHLEN]; + wchar_t *file_name_p = file_name; + Name file; + wchar_t plain_file_name[MAXPATHLEN]; + wchar_t *plain_file_name_p; + Name plain_file; + wchar_t tmp_wcs_buffer[MAXPATHLEN]; + DIR *dir_fd; + int m_local_dependency=0; +#define d_fileno d_ino + register struct dirent *dp; + wchar_t *vpath = NULL; + wchar_t *p; + int result = 0; + + if(dir->hash.length >= MAXPATHLEN) { + return 0; + } + + Wstring wcb(dir); + Wstring vps; + + /* A directory is only read once unless we need to expand wildcards. */ + if (pattern == NULL) { + if (dir->has_read_dir) { + return 0; + } + dir->has_read_dir = true; + } + /* Check if VPATH is active and setup list if it is. */ + if (vpath_defined && (dir == dot)) { + vps.init(getvar(vpath_name)); + vpath = vps.get_string(); + } + + /* + * Prepare the string where we build the full name of the + * files in the directory. + */ + if ((dir->hash.length > 1) || (wcb.get_string()[0] != (int) period_char)) { + (void) wcscpy(file_name, wcb.get_string()); + MBSTOWCS(wcs_buffer, "/"); + (void) wcscat(file_name, wcs_buffer); + file_name_p = file_name + wcslen(file_name); + } + + /* Open the directory. */ +vpath_loop: + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return 0; + } + + /* Read all the directory entries. */ + while ((dp = readdir(dir_fd)) != NULL) { + /* We ignore "." and ".." */ + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* + * Build the full name of the file using whatever + * path supplied to the function. + */ + MBSTOWCS(tmp_wcs_buffer, dp->d_name); + (void) wcscpy(file_name_p, tmp_wcs_buffer); + file = enter_file_name(file_name, library); + if ((pattern != NULL) && amatch(tmp_wcs_buffer, pattern)) { + /* + * If we are expanding a wildcard pattern, we + * enter the file as a dependency for the target. + */ + if (debug_level > 0){ + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target->string_mb, + file->string_mb, + mbs_buffer); + } + enter_dependency(line, file, false); + result++; + } else { + /* + * If the file has an SCCS/s. file, + * we will detect that later on. + */ + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + plain_file_name_p = plain_file_name; + (void) wcscpy(plain_file_name_p, tmp_wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + } + (void) closedir(dir_fd); + if ((vpath != NULL) && (*vpath != (int) nul_char)) { + while ((*vpath != (int) nul_char) && + (iswspace(*vpath) || (*vpath == (int) colon_char))) { + vpath++; + } + p = vpath; + while ((*vpath != (int) colon_char) && + (*vpath != (int) nul_char)) { + vpath++; + } + if (vpath > p) { + dir = GETNAME(p, vpath - p); + goto vpath_loop; + } + } +/* + * look into SCCS directory only if it's not svr4. For svr4 dont do that. + */ + +/* + * Now read the SCCS directory. + * Files in the SCSC directory are considered to be part of the set of + * files in the plain directory. They are also entered in their own right. + * Prepare the string where we build the true name of the SCCS files. + */ + (void) wcsncpy(plain_file_name, + file_name, + file_name_p - file_name); + plain_file_name[file_name_p - file_name] = 0; + plain_file_name_p = plain_file_name + wcslen(plain_file_name); + + if(!svr4) { + + if (sccs_dir_path != NULL) { + wchar_t tmp_wchar; + wchar_t path[MAXPATHLEN]; + char mb_path[MAXPATHLEN]; + + if (file_name_p - file_name > 0) { + tmp_wchar = *file_name_p; + *file_name_p = 0; + WCSTOMBS(mbs_buffer, file_name); + (void) sprintf(mb_path, "%s/%s/SCCS", + sccs_dir_path, + mbs_buffer); + *file_name_p = tmp_wchar; + } else { + (void) sprintf(mb_path, "%s/SCCS", sccs_dir_path); + } + MBSTOWCS(path, mb_path); + (void) wcscpy(file_name, path); + } else { + MBSTOWCS(wcs_buffer, "SCCS"); + (void) wcscpy(file_name_p, wcs_buffer); + } + } else { + MBSTOWCS(wcs_buffer, "."); + (void) wcscpy(file_name_p, wcs_buffer); + } + /* Internalize the constructed SCCS dir name. */ + (void) exists(dir = GETNAME(file_name, FIND_LENGTH)); + /* Just give up if the directory file doesnt exist. */ + if (!dir->stat.is_file) { + return result; + } + /* Open the directory. */ + dir_fd = opendir(dir->string_mb); + if (dir_fd == NULL) { + return result; + } + MBSTOWCS(wcs_buffer, "/"); + (void) wcscat(file_name, wcs_buffer); + file_name_p = file_name + wcslen(file_name); + + while ((dp = readdir(dir_fd)) != NULL) { + if ((dp->d_fileno == 0) || + ((dp->d_name[0] == (int) period_char) && + ((dp->d_name[1] == 0) || + ((dp->d_name[1] == (int) period_char) && + (dp->d_name[2] == 0))))) { + continue; + } + /* Construct and internalize the true name of the SCCS file. */ + MBSTOWCS(wcs_buffer, dp->d_name); + (void) wcscpy(file_name_p, wcs_buffer); + file = GETNAME(file_name, FIND_LENGTH); + file->stat.is_file = true; + file->stat.has_sccs = NO_SCCS; + /* + * If this is an s. file, we also enter it as if it + * existed in the plain directory. + */ + if ((dp->d_name[0] == 's') && + (dp->d_name[1] == (int) period_char)) { + + MBSTOWCS(wcs_buffer, dp->d_name + 2); + (void) wcscpy(plain_file_name_p, wcs_buffer); + plain_file = GETNAME(plain_file_name, FIND_LENGTH); + plain_file->stat.is_file = true; + plain_file->stat.has_sccs = HAS_SCCS; + /* if sccs dependency is already set,skip */ + if(plain_file->prop) { + Property sprop = get_prop(plain_file->prop,sccs_prop); + if(sprop != NULL) { + if (sprop->body.sccs.file) { + goto try_pattern; + } + } + } + + /* + * Enter the s. file as a dependency for the + * plain file. + */ + maybe_append_prop(plain_file, sccs_prop)-> + body.sccs.file = file; +try_pattern: + MBSTOWCS(tmp_wcs_buffer, dp->d_name + 2); + if ((pattern != NULL) && + amatch(tmp_wcs_buffer, pattern)) { + if (debug_level > 0) { + WCSTOMBS(mbs_buffer, pattern); + (void) printf(gettext("'%s: %s' due to %s expansion\n"), + line->body.line.target-> + string_mb, + plain_file->string_mb, + mbs_buffer); + } + enter_dependency(line, plain_file, false); + result++; + } + } + } + (void) closedir(dir_fd); + + return result; +} + +/* + * enter_file_name(name_string, library) + * + * Helper function for read_dir(). + * + * Return value: + * The Name that was entered + * + * Parameters: + * name_string Name of the file we want to enter + * library The library it is a member of, if any + * + * Global variables used: + */ +static Name +enter_file_name(wchar_t *name_string, wchar_t *library) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + String_rec lib_name; + Name name; + Property prop; + + if (library == NULL) { + name = GETNAME(name_string, FIND_LENGTH); + name->stat.is_file = true; + return name; + } + + INIT_STRING_FROM_STACK(lib_name, buffer); + append_string(library, &lib_name, FIND_LENGTH); + append_char((int) parenleft_char, &lib_name); + append_string(name_string, &lib_name, FIND_LENGTH); + append_char((int) parenright_char, &lib_name); + + name = GETNAME(lib_name.buffer.start, FIND_LENGTH); + name->stat.is_file = true; + name->is_member = true; + prop = maybe_append_prop(name, member_prop); + prop->body.member.library = GETNAME(library, FIND_LENGTH); + prop->body.member.library->stat.is_file = true; + prop->body.member.entry = NULL; + prop->body.member.member = GETNAME(name_string, FIND_LENGTH); + prop->body.member.member->stat.is_file = true; + return name; +} + +/* + * star_match(string, pattern) + * + * This is a regular shell type wildcard pattern matcher + * It is used when xpanding wildcards in dependency lists + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +star_match(register wchar_t *string, register wchar_t *pattern) +{ + register int pattern_ch; + + switch (*pattern) { + case 0: + return succeeded; + case bracketleft_char: + case question_char: + case asterisk_char: + while (*string) { + if (amatch(string++, pattern)) { + return succeeded; + } + } + break; + default: + pattern_ch = (int) *pattern++; + while (*string) { + if ((*string++ == pattern_ch) && + amatch(string, pattern)) { + return succeeded; + } + } + break; + } + return failed; +} + +/* + * amatch(string, pattern) + * + * Helper function for shell pattern matching + * + * Return value: + * Indication if the string matched the pattern + * + * Parameters: + * string String to match + * pattern Pattern to match it against + * + * Global variables used: + */ +static Boolean +amatch(register wchar_t *string, register wchar_t *pattern) +{ + register long lower_bound; + register long string_ch; + register long pattern_ch; + register int k; + +top: + for (; 1; pattern++, string++) { + lower_bound = 017777777777; + string_ch = *string; + switch (pattern_ch = *pattern) { + case bracketleft_char: + k = 0; + while ((pattern_ch = *++pattern) != 0) { + switch (pattern_ch) { + case bracketright_char: + if (!k) { + return failed; + } + string++; + pattern++; + goto top; + case hyphen_char: + k |= (lower_bound <= string_ch) && + (string_ch <= + (pattern_ch = pattern[1])); + default: + if (string_ch == + (lower_bound = pattern_ch)) { + k++; + } + } + } + return failed; + case asterisk_char: + return star_match(string, ++pattern); + case 0: + return BOOLEAN(!string_ch); + case question_char: + if (string_ch == 0) { + return failed; + } + break; + default: + if (pattern_ch != string_ch) { + return failed; + } + break; + } + } + /* NOTREACHED */ +} + diff --git a/bin/globals.cc b/bin/globals.cc new file mode 100644 index 0000000..b0c28c6 --- /dev/null +++ b/bin/globals.cc @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * globals.cc + * + * This declares all global variables + */ + +/* + * Included files + */ +#include +#include +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Global variables used by make only + */ + FILE *dependency_report_file; + +/* + * Global variables used by make + */ + Boolean allrules_read=false; + Name posix_name; + Name svr4_name; + Boolean sdot_target; /* used to identify s.m(/M)akefile */ + Boolean all_parallel; + Boolean assign_done; + int foo; + Boolean build_failed_seen; + Name built_last_make_run; + Name c_at; + Boolean cleanup; + Boolean close_report; + Boolean command_changed; + Boolean commands_done; + Chain conditional_targets; + Name conditionals; + Boolean continue_after_error; /* `-k' */ + Property current_line; + Name current_make_version; + Name current_target; + short debug_level; + Cmd_line default_rule; + Name default_rule_name; + Name default_target_to_build; + Name dmake_group; + Name dmake_max_jobs; + Name dmake_mode; + DMake_mode dmake_mode_type; + Name dmake_output_mode; + DMake_output_mode output_mode = txt1_mode; + Name dmake_odir; + Name dmake_rcfile; + Name done; + Name dot; + Name dot_keep_state; + Name dot_keep_state_file; + Name empty_name; + Boolean fatal_in_progress; + int file_number; +#if 0 + Boolean filter_stderr; /* `-X' */ +#endif + Name force; + Name ignore_name; + Boolean ignore_errors; /* `-i' */ + Boolean ignore_errors_all; /* `-i' */ + Name init; + int job_msg_id; + Boolean keep_state; + Name make_state; + timestruc_t make_state_before; + Dependency makefiles_used; + Name makeflags; +// Boolean make_state_locked; // Moved to lib/mksh + Name make_version; + char mbs_buffer2[(MAXPATHLEN * MB_LEN_MAX)]; + char *mbs_ptr; + char *mbs_ptr2; + Boolean depinfo_already_read = false; + Boolean no_action_was_taken = true; /* true if we've not ** + ** run any command */ + + Boolean no_parallel = false; + Name no_parallel_name; + Name not_auto; + Boolean only_parallel; + Boolean parallel; + Name parallel_name; + Name localhost_name; + int parallel_process_cnt; + Percent percent_list; + Dyntarget dyntarget_list; + Name plus; + Name pmake_machinesfile; + Name precious; + Name primary_makefile; + Boolean quest; /* `-q' */ + short read_trace_level; + Boolean reading_dependencies = false; + Name recursive_name; + int recursion_level; + short report_dependencies_level = 0; /* -P */ + Boolean report_pwd; + Boolean rewrite_statefile; + Running running_list; + char *sccs_dir_path; + Name sccs_get_name; + Name sccs_get_posix_name; + Cmd_line sccs_get_rule; + Cmd_line sccs_get_org_rule; + Cmd_line sccs_get_posix_rule; + Name get_name; + Cmd_line get_rule; + Name get_posix_name; + Cmd_line get_posix_rule; + Boolean all_precious; + Boolean silent_all; /* `-s' */ + Boolean report_cwd; /* `-w' */ + Boolean silent; /* `-s' */ + Name silent_name; + char *stderr_file = NULL; + char *stdout_file = NULL; + Boolean stdout_stderr_same; + Dependency suffixes; + Name suffixes_name; + Name sunpro_dependencies; + Boolean target_variants; + const char *tmpdir = "/tmp"; + const char *temp_file_directory = "."; + Name temp_file_name; + short temp_file_number; + time_t timing_start; + wchar_t *top_level_target; + Boolean touch; /* `-t' */ + Boolean trace_reader; /* `-D' */ + Boolean build_unconditional; /* `-u' */ + pathpt vroot_path = VROOT_DEFAULT; + Name wait_name; + wchar_t wcs_buffer2[MAXPATHLEN]; + wchar_t *wcs_ptr; + wchar_t *wcs_ptr2; + long int hostid; + +/* + * File table of contents + */ + diff --git a/bin/implicit.cc b/bin/implicit.cc new file mode 100644 index 0000000..184e5b6 --- /dev/null +++ b/bin/implicit.cc @@ -0,0 +1,1462 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * implicit.c + * + * Handle suffix and percent rules + */ + +/* + * Included files + */ +#include +#include /* expand_value() */ +#include /* retmem() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static wchar_t WIDE_NULL[1] = {(wchar_t) nul_char}; + +/* + * File table of contents + */ +extern Doname find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking); +extern Doname find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking); +extern Doname find_double_suffix_rule(register Name target, Property *command, Boolean rechecking); +extern void build_suffix_list(register Name target_suffix); +extern Doname find_percent_rule(register Name target, Property *command, Boolean rechecking); +static void create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent); +static Boolean match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf); +static void construct_string_from_pattern(Percent pat_rule, String percent, String result); +static Boolean dependency_exists(Name target, Property line); +extern Property maybe_append_prop(Name, Property_id); +extern void add_target_to_chain(Name target, Chain * query); + +/* + * find_suffix_rule(target, target_body, target_suffix, command, rechecking) + * + * Does the lookup for single and double suffix rules. + * It calls build_suffix_list() to build the list of possible suffixes + * for the given target. + * It then scans the list to find the first possible source file that + * exists. This is done by concatenating the body of the target name + * (target name less target suffix) and the source suffix and checking + * if the resulting file exists. + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * target_body The target name without the suffix + * target_suffix The suffix of the target + * command Pointer to slot to deposit cmd in if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + */ + +static Boolean actual_doname = false; + +/* /tolik/ + * fix bug 1247448: Suffix Rules failed when combine with Pattern Matching Rules. + * When make attemps to apply % rule it didn't look for a single suffix rule because + * if "doname" is called from "find_percent_rule" argument "implicit" is set to true + * and find_suffix_rule was not called. I've commented the checking of "implicit" + * in "doname" and make got infinite recursion for SVR4 tilde rules. + * Usage of "we_are_in_tilde" is intended to avoid this recursion. + */ + +static Boolean we_are_in_tilde = false; + +Doname +find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking) +{ + static wchar_t static_string_buf_3M [ 3 * MAXPATHLEN ]; + Name true_target = target; + wchar_t *sourcename = (wchar_t*)static_string_buf_3M; + register wchar_t *put_suffix; + register Property source_suffix; + register Name source; + Doname result; + register Property line; + extern Boolean tilde_rule; + Boolean name_found = true; + Boolean posix_tilde_attempt = true; + int src_len = MAXPATHLEN + strlen(target_body->string_mb); + + /* + * To avoid infinite recursion + */ + if(we_are_in_tilde) { + we_are_in_tilde = false; + return(build_dont_know); + } + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (debug_level > 1) { + (void) printf("%*sfind_suffix_rule(%s,%s,%s)\n", + recursion_level, + "", + true_target->string_mb, + target_body->string_mb, + target_suffix->string_mb); + } + if (command != NULL) { + if ((true_target->suffix_scan_done == true) && (*command == NULL)) { + return build_ok; + } + } + true_target->suffix_scan_done = true; + /* + * Enter all names from the directory where the target lives as + * files that makes sense. + * This will make finding the synthesized source possible. + */ + read_directory_of_file(target_body); + /* Cache the suffixes for this target suffix if not done. */ + if (!target_suffix->has_read_suffixes) { + build_suffix_list(target_suffix); + } + /* Preload the sourcename vector with the head of the target name. */ + if (src_len >= sizeof(static_string_buf_3M)) { + sourcename = ALLOC_WC(src_len); + } + (void) mbstowcs(sourcename, + target_body->string_mb, + (int) target_body->hash.length); + put_suffix = sourcename + target_body->hash.length; + /* Scan the suffix list for the target if one exists. */ + if (target_suffix->has_suffixes) { +posix_attempts: + for (source_suffix = get_prop(target_suffix->prop, + suffix_prop); + source_suffix != NULL; + source_suffix = get_prop(source_suffix->next, + suffix_prop)) { + /* Build the synthesized source name. */ + (void) mbstowcs(put_suffix, + source_suffix->body. + suffix.suffix->string_mb, + (int) source_suffix->body. + suffix.suffix->hash.length); + put_suffix[source_suffix->body. + suffix.suffix->hash.length] = + (int) nul_char; + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(gettext("%*sTrying %s\n"), + recursion_level, + "", + mbs_buffer); + } + source = getname_fn(sourcename, FIND_LENGTH, false, &name_found); + /* + * If the source file is not registered as + * a file, this source suffix did not match. + */ + if(vpath_defined && !posix && !svr4) { + (void) exists(source); + } + if (!source->stat.is_file) { + if(!(posix|svr4)) + { + if(!name_found) { + free_name(source); + } + continue; + } + + /* following code will ensure that the corresponding + ** tilde rules are executed when corresponding s. file + ** exists in the current directory. Though the current + ** target ends with a ~ character, there wont be any + ** any file in the current directory with that suffix + ** as it's fictitious. Even if it exists, it'll + ** execute all the rules for the ~ target. + */ + + if(source->string_mb[source->hash.length - 1] == '~' && + ( svr4 || posix_tilde_attempt ) ) + { + char *p, *np; + char *tmpbuf; + + tmpbuf = getmem(source->hash.length + 8); + /* + 8 to add "s." or "SCCS/s." */ + memset(tmpbuf,0,source->hash.length + 8); + source->string_mb[source->hash.length - 1] = '\0'; + if(p = (char *) memchr((char *)source->string_mb,'/',source->hash.length)) + { + while(1) { + if(np = (char *) memchr((char *)p+1,'/',source->hash.length - (p - source->string_mb))) { + p = np; + } else {break;} + } + /* copy everything including '/' */ + strncpy(tmpbuf, source->string_mb, p - source->string_mb + 1); + strcat(tmpbuf, "s."); + strcat(tmpbuf, p+1); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } else { + strcpy(tmpbuf, "s."); + strcat(tmpbuf, source->string_mb); + retmem((wchar_t *) source->string_mb); + source->string_mb = tmpbuf; + + } + source->hash.length = strlen(source->string_mb); + if(exists(source) == file_doesnt_exist) + continue; + tilde_rule = true; + we_are_in_tilde = true; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } else { + if(posix && posix_tilde_attempt) { + if(exists(source) == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + } + } + + if (command != NULL) { + if(!name_found) { + store_name(source); + } + /* + * The source file is a file. + * Make sure it is up to date. + */ + if (dependency_exists(source, + get_prop(target->prop, + line_prop))) { + result = (Doname) source->state; + } else { +#if 0 /* with_squiggle sends false, which is buggy. : djay */ + result = doname(source, + (Boolean) source_suffix->body. + suffix.suffix->with_squiggle, + true); +#else + result = doname(source, + true, + true); +#endif + } + } else { + result = target_can_be_built(source); + + if (result == build_ok) { + return result; + } else { + if(!name_found) { + free_name(source); + } + continue; + } + } + + switch (result) { + case build_dont_know: + /* + * If we still can't build the source, + * this rule is not a match, + * try the next one. + */ + if (source->stat.time == file_doesnt_exist) { + if(!name_found) { + free_name(source); + } + continue; + } + case build_running: + if(!name_found) { + store_name(source); + } + true_target->suffix_scan_done = false; + line = maybe_append_prop(target, line_prop); + enter_dependency(line, source, false); + line->body.line.target = true_target; + return build_running; + case build_ok: + if(!name_found) { + store_name(source); + } + break; + case build_failed: + if(!name_found) { + store_name(source); + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_failed; + } + + if (debug_level > 1) { + WCSTOMBS(mbs_buffer, sourcename); + (void) printf(gettext("%*sFound %s\n"), + recursion_level, + "", + mbs_buffer); + } + + if (source->depends_on_conditional) { + target->depends_on_conditional = true; + } +/* + * Since it is possible that the same target is built several times during + * the make run, we have to patch the target with all information we found + * here. Thus, the target will have an explicit rule the next time around. + */ + line = maybe_append_prop(target, line_prop); + if (*command == NULL) { + *command = line; + } + if ((source->stat.time > (*command)->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(gettext("%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + source->string_mb, + time_to_string(source-> + stat.time), + true_target->string_mb, + time_to_string((*command)-> + body.line. + dependency_time)); + } + /* + * Determine if this new dependency made the + * target out of date. + */ + (*command)->body.line.dependency_time = + MAX((*command)->body.line.dependency_time, + source->stat.time); + Boolean out_of_date; + if (target->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, + (*command)->body.line.dependency_time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, + (*command)->body.line.dependency_time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using suffix rule for %s%s because it is out of date relative to %s\n"), + recursion_level, + "", + true_target->string_mb, + source_suffix->body.suffix.suffix->string_mb, + target_suffix->string_mb, + source->string_mb); + } + } + /* + * Add the implicit rule as the target's explicit + * rule if none actually given, and register + * dependency. + * The time checking above really should be + * conditional on actual use of implicit rule + * as well. + */ + line->body.line.sccs_command = false; + if (line->body.line.command_template == NULL) { + line->body.line.command_template = + source_suffix->body.suffix.command_template; + } + enter_dependency(line, source, false); + line->body.line.target = true_target; + /* + * Also make sure the rule is built with + * $* and $< bound properly. + */ + line->body.line.star = target_body; + if(svr4|posix) { + char * p; + char tstr[256]; + extern Boolean dollarless_flag; + extern Name dollarless_value; + + if(tilde_rule) { + MBSTOWCS(wcs_buffer, source->string_mb); + dollarless_value = GETNAME(wcs_buffer,FIND_LENGTH); + } + else { + dollarless_flag = false; + } + } + line->body.line.less = source; + line->body.line.percent = NULL; + add_target_to_chain(source, &(line->body.line.query)); + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + return build_ok; + } + if(posix && posix_tilde_attempt) { + posix_tilde_attempt = false; + goto posix_attempts; + } + if ((command != NULL) && + ((*command) != NULL) && + ((*command)->body.line.star == NULL)) { + (*command)->body.line.star = target_body; + } + } + if (sourcename != static_string_buf_3M) { + retmem(sourcename); + } + /* Return here in case no rule matched the target */ + return build_dont_know; +} + +/* + * find_ar_suffix_rule(target, true_target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target member name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target using the suffix ".a". + * + * Return value: + * Indicates if search failed or not + * + * Parameters: + * target The target we need a rule for + * true_target The proper name + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * dot_a The Name ".a", compared against + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking) +{ + wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Property line; + Name body; + static Name dot_a; + + Wstring targ_string(true_target); + Wstring suf_string; + + if (dot_a == NULL) { + MBSTOWCS(wcs_buffer, ".a"); + dot_a = GETNAME(wcs_buffer, FIND_LENGTH); + } + target_end = targ_string.get_string() + true_target->hash.length; + + /* + * We compare the tail of the target name with the suffixes + * from .SUFFIXES. + */ + if (debug_level > 1) { + (void) printf("%*sfind_ar_suffix_rule(%s)\n", + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches any of + * those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + if (!IS_WEQUALN(suf_string.get_string(), + target_end - suffix_length, + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + target->suffix_scan_done = false; + body = GETNAME(targ_string.get_string(), + (int)(true_target->hash.length - + suffix_length)); + we_are_in_tilde = false; + switch (find_suffix_rule(target, + body, + dot_a, + command, + rechecking)) { + case build_ok: + line = get_prop(target->prop, line_prop); + line->body.line.star = body; + return build_ok; + case build_running: + return build_running; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail, and so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + return build_dont_know; +} + +/* + * find_double_suffix_rule(target, command, rechecking) + * + * Scans the .SUFFIXES list and tries + * to find a suffix on it that matches the tail of the target name. + * If it finds a matching suffix it calls find_suffix_rule() to find + * a rule for the target. + * + * Return value: + * Indicates if scan failed or not + * + * Parameters: + * target Target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + */ +Doname +find_double_suffix_rule(register Name target, Property *command, Boolean rechecking) +{ + Name true_target = target; + Name target_body; + register wchar_t *target_end; + register Dependency suffix; + register int suffix_length; + Boolean scanned_once = false; + Boolean name_found = true; + + Wstring targ_string; + Wstring suf_string; + + /* + * If the target is a constructed one for a "::" target, + * we need to consider that. + */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + targ_string.init(true_target); + + /* + * We compare the tail of the target name with the + * suffixes from .SUFFIXES. + */ + target_end = targ_string.get_string() + true_target->hash.length; + if (debug_level > 1) { + (void) printf("%*sfind_double_suffix_rule(%s)\n", + recursion_level, + "", + true_target->string_mb); + } + /* + * Scan the .SUFFIXES list to see if the target matches + * any of those suffixes. + */ + for (suffix = suffixes; suffix != NULL; suffix = suffix->next) { + target->suffix_scan_done = false; + true_target->suffix_scan_done = false; + /* Compare one suffix. */ + suffix_length = suffix->name->hash.length; + suf_string.init(suffix->name); + /* Check the lengths, or else RTC will report rua. */ + if (true_target->hash.length < suffix_length) { + goto not_this_one; + } else if (!IS_WEQUALN(suf_string.get_string(), + (target_end - suffix_length), + suffix_length)) { + goto not_this_one; + } + /* + * The target tail matched a suffix from the .SUFFIXES list. + * Now check for a rule to match. + */ + we_are_in_tilde = false; + target_body = GETNAME( + targ_string.get_string(), + (int)(true_target->hash.length - suffix_length) + ); + switch (find_suffix_rule(target, + target_body, + suffix->name, + command, + rechecking)) { + case build_ok: + return build_ok; + case build_running: + return build_running; + } + if (true_target->suffix_scan_done == true) { + scanned_once = true; + } + /* + * If no rule was found, we try the next suffix to see + * if it matches the target tail. And so on. + * Go here if the suffix did not match the target tail. + */ + not_this_one:; + } + if (scanned_once) + true_target->suffix_scan_done = true; + return build_dont_know; +} + +/* + * build_suffix_list(target_suffix) + * + * Scans the .SUFFIXES list and figures out + * which suffixes this target can be derived from. + * The target itself is not know here, we just know the suffix of the + * target. For each suffix on the list the target can be derived iff + * a rule exists for the name "". + * A list of all possible building suffixes is built, with the rule for + * each, and tacked to the target suffix nameblock. + * + * Parameters: + * target_suffix The suffix we build a match list for + * + * Global variables used: + * debug_level Indicates how much tracing to do + * recursion_level Used for tracing + * suffixes List of suffixes used for scan (from .SUFFIXES) + * working_on_targets Indicates that this is a real target + */ +void +build_suffix_list(register Name target_suffix) +{ + register Dependency source_suffix; + wchar_t rule_name[MAXPATHLEN]; + register Property line; + register Property suffix; + Name rule; + + /* If this is before default.mk has been read we just return to try */ + /* again later */ + if ((suffixes == NULL) || !working_on_targets) { + return; + } + if (debug_level > 1) { + (void) printf("%*sbuild_suffix_list(%s) ", + recursion_level, + "", + target_suffix->string_mb); + } + /* Mark the target suffix saying we cashed its list */ + target_suffix->has_read_suffixes = true; + /* Scan the .SUFFIXES list */ + for (source_suffix = suffixes; + source_suffix != NULL; + source_suffix = source_suffix->next) { + /* + * Build the name "". + * (a popular one would be ".c.o"). + */ + (void) mbstowcs(rule_name, + source_suffix->name->string_mb, + (int) source_suffix->name->hash.length); + (void) mbstowcs(rule_name + source_suffix->name->hash.length, + target_suffix->string_mb, + (int) target_suffix->hash.length); + /* + * Check if that name has a rule. If not, it cannot match + * any implicit rule scan and is ignored. + * The GETNAME() call only checks for presence, it will not + * enter the name if it is not defined. + */ + if (((rule = getname_fn(rule_name, + (int) (source_suffix->name-> + hash.length + + target_suffix->hash.length), + true)) != NULL) && + ((line = get_prop(rule->prop, line_prop)) != NULL)) { + if (debug_level > 1) { + (void) printf("%s ", rule->string_mb); + } + /* + * This makes it possible to quickly determine if + * it will pay to look for a suffix property. + */ + target_suffix->has_suffixes = true; + /* + * Add the suffix property to the target suffix + * and save the rule with it. + * All information the implicit rule scanner need + * is saved in the suffix property. + */ + suffix = append_prop(target_suffix, suffix_prop); + suffix->body.suffix.suffix = source_suffix->name; + suffix->body.suffix.command_template = + line->body.line.command_template; + } + } + if (debug_level > 1) { + (void) printf("\n"); + } +} + +/* + * find_percent_rule(target, command, rechecking) + * + * Tries to find a rule from the list of wildcard matched rules. + * It scans the list attempting to match the target. + * For each target match it checks if the corresponding source exists. + * If it does the match is returned. + * The percent_list is built at makefile read time. + * Each percent rule get one entry on the list. + * + * Return value: + * Indicates if the scan failed or not + * + * Parameters: + * target The target we need a rule for + * command Pointer to slot where we stuff cmd, if found + * rechecking true if we are rechecking target which depends + * on conditional macro and keep_state is set + * + * Global variables used: + * debug_level Indicates how much tracing to do + * percent_list List of all percent rules + * recursion_level Used for tracing + * empty_name + */ +Doname +find_percent_rule(register Name target, Property *command, Boolean rechecking) +{ + register Percent pat_rule, pat_depe; + register Name depe_to_check; + register Dependency depe; + register Property line; + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + String_rec percent; + wchar_t percent_buf[STRING_BUFFER_LENGTH]; + Name true_target = target; + Name less; + Boolean nonpattern_less; + Boolean dep_name_found = false; + Doname result = build_dont_know; + Percent rule_candidate = NULL; + Boolean rule_maybe_ok; + Boolean is_pattern; + + /* If the target is constructed for a "::" target we consider that */ + if (target->has_target_prop) { + true_target = get_prop(target->prop, + target_prop)->body.target.target; + } + if (target->has_long_member_name) { + true_target = get_prop(target->prop, + long_member_name_prop)->body.long_member_name.member_name; + } + if (debug_level > 1) { + (void) printf(gettext("%*sLooking for %% rule for %s\n"), + recursion_level, + "", + true_target->string_mb); + } + for (pat_rule = percent_list; + pat_rule != NULL; + pat_rule = pat_rule->next) { + /* Avoid infinite recursion when expanding patterns */ + if (pat_rule->being_expanded == true) { + continue; + } + + /* Mark this pat_rule as "maybe ok". If no % rule is found + make will use this rule. The following algorithm is used: + 1) make scans all pattern rules in order to find the rule + where ALL dependencies, including nonpattern ones, exist or + can be built (GNU behaviour). If such rule is found make + will apply it. + 2) During this check make also remembers the first pattern rule + where all PATTERN dependencies can be build (no matter what + happens with nonpattern dependencies). + 3) If no rule satisfying 1) is found, make will apply the rule + remembered in 2) if there is one. + */ + rule_maybe_ok = true; + + /* used to track first percent dependency */ + less = NULL; + nonpattern_less = true; + + /* check whether pattern matches. + if it matches, percent string will contain matched percent part of pattern */ + if (!match_found_with_pattern(true_target, pat_rule, &percent, percent_buf)) { + continue; + } + if (pat_rule->dependencies != NULL) { + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + /* checking result for dependency */ + result = build_dont_know; + + dep_name_found = true; + if (pat_depe->name->percent) { + is_pattern = true; + /* build dependency name */ + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + + if ((less == NULL) || nonpattern_less) { + less = depe_to_check; + nonpattern_less = false; + } + } else { + /* nonpattern dependency */ + is_pattern = false; + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = getname_fn(string.buffer.start, + FIND_LENGTH, + false, + &dep_name_found + ); + } + if (less == NULL) { + less = depe_to_check; + } + } + + if (depe_to_check == empty_name) { + result = build_ok; + } else { + if (debug_level > 1) { + (void) printf(gettext("%*sTrying %s\n"), + recursion_level, + "", + depe_to_check->string_mb); + } + + pat_rule->being_expanded = true; + + /* suppress message output */ + int save_debug_level = debug_level; + debug_level = 0; + + /* check whether dependency can be built */ + if (dependency_exists(depe_to_check, + get_prop(target->prop, + line_prop))) + { + result = (Doname) depe_to_check->state; + } else { + if(actual_doname) { + result = doname(depe_to_check, true, true); + } else { + result = target_can_be_built(depe_to_check); + } + if(!dep_name_found) { + if(result != build_ok && result != build_running) { + free_name(depe_to_check); + } else { + store_name(depe_to_check); + } + } + } + if(result != build_ok && is_pattern) { + rule_maybe_ok = false; + } + + /* restore debug_level */ + debug_level = save_debug_level; + } + + if (pat_depe->name->percent) { + if (string.free_after_use) { + retmem(string.buffer.start); + } + } + /* make can't figure out how to make this dependency */ + if (result != build_ok && result != build_running) { + pat_rule->being_expanded = false; + break; + } + } + } else { + result = build_ok; + } + + /* this pattern rule is the needed one since all dependencies could be built */ + if (result == build_ok || result == build_running) { + break; + } + + /* Make does not know how to build some of dependencies from this rule. + But if all "pattern" dependencies can be built, we remember this rule + as a candidate for the case if no other pattern rule found. + */ + if(rule_maybe_ok && rule_candidate == NULL) { + rule_candidate = pat_rule; + } + } + + /* if no pattern matching rule was found, use the remembered candidate + or return build_dont_know if there is no candidate. + */ + if (result != build_ok && result != build_running) { + if(rule_candidate) { + pat_rule = rule_candidate; + } else { + return build_dont_know; + } + } + + /* if we are performing only check whether dependency could be built with existing rules, + return success */ + if (command == NULL) { + if(pat_rule != NULL) { + pat_rule->being_expanded = false; + } + return result; + } + + if (debug_level > 1) { + (void) printf(gettext("%*sMatched %s:"), + recursion_level, + "", + target->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, &percent, &string); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } else { + depe_to_check = pat_depe->name; + if(depe_to_check->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe_to_check, &string, false); + depe_to_check = GETNAME(string.buffer.start, FIND_LENGTH); + } + } + + if (depe_to_check != empty_name) { + (void) printf(" %s", depe_to_check->string_mb); + } + } + + (void) printf(gettext(" from: %s:"), + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf("\n"); + } + + if (true_target->colons == no_colon) { + true_target->colons = one_colon; + } + + /* create deppendency list and target group from matched pattern rule */ + create_target_group_and_dependencies_list(target, pat_rule, &percent); + + /* save command */ + line = get_prop(target->prop, line_prop); + *command = line; + + /* free query chain if one exist */ + while(line->body.line.query != NULL) { + Chain to_free = line->body.line.query; + line->body.line.query = line->body.line.query->next; + retmem_mb((char *) to_free); + } + + if (line->body.line.dependencies != NULL) { + /* build all collected dependencies */ + for (depe = line->body.line.dependencies; + depe != NULL; + depe = depe->next) { + actual_doname = true; + result = doname_check(depe->name, true, true, depe->automatic); + + actual_doname = false; + if (result == build_failed) { + pat_rule->being_expanded = false; + return build_failed; + } + if (result == build_running) { + pat_rule->being_expanded = false; + return build_running; + } + + if ((depe->name->stat.time > line->body.line.dependency_time) && + (debug_level > 1)) { + (void) printf(gettext("%*sDate(%s)=%s Date-dependencies(%s)=%s\n"), + recursion_level, + "", + depe->name->string_mb, + time_to_string(depe->name->stat.time), + true_target->string_mb, + time_to_string(line->body.line.dependency_time)); + } + + line->body.line.dependency_time = + MAX(line->body.line.dependency_time, depe->name->stat.time); + + /* determine whether this dependency made target out of date */ + Boolean out_of_date; + if (target->is_member || depe->name->is_member) { + out_of_date = (Boolean) OUT_OF_DATE_SEC(target->stat.time, depe->name->stat.time); + } else { + out_of_date = (Boolean) OUT_OF_DATE(target->stat.time, depe->name->stat.time); + } + if (build_unconditional || out_of_date) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + add_target_to_chain(depe->name, &(line->body.line.query)); + + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using pattern rule %s:"), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb); + + for (pat_depe = pat_rule->dependencies; + pat_depe != NULL; + pat_depe = pat_depe->next) { + (void) printf(" %s", pat_depe->name->string_mb); + } + + (void) printf(gettext(" because it is out of date relative to %s\n"), + depe->name->string_mb); + } + } + } + } else { + if ((true_target->stat.time <= file_doesnt_exist) || + (true_target->stat.time < line->body.line.dependency_time)) { + if(!rechecking) { + line->body.line.is_out_of_date = true; + } + if (debug_level > 0) { + (void) printf(gettext("%*sBuilding %s using pattern rule %s: "), + recursion_level, + "", + true_target->string_mb, + pat_rule->name->string_mb, + (target->stat.time > file_doesnt_exist) ? + gettext("because it is out of date") : + gettext("because it does not exist")); + } + } + } + + /* enter explicit rule from percent rule */ + Name lmn_target = true_target; + if (true_target->has_long_member_name) { + lmn_target = get_prop(true_target->prop, long_member_name_prop)->body.long_member_name.member_name; + } + line->body.line.sccs_command = false; + line->body.line.target = true_target; + line->body.line.command_template = pat_rule->command_template; + line->body.line.star = GETNAME(percent.buffer.start, FIND_LENGTH); + line->body.line.less = less; + + if (lmn_target->parenleft) { + Wstring lmn_string(lmn_target); + + wchar_t *left = (wchar_t *) wcschr(lmn_string.get_string(), (int) parenleft_char); + wchar_t *right = (wchar_t *) wcschr(lmn_string.get_string(), (int) parenright_char); + + if ((left == NULL) || (right == NULL)) { + line->body.line.percent = NULL; + } else { + line->body.line.percent = GETNAME(left + 1, right - left - 1); + } + } else { + line->body.line.percent = NULL; + } + pat_rule->being_expanded = false; + + return result; +} + +/* + * match_found_with_pattern + * ( target, pat_rule, percent, percent_buf) + * + * matches "target->string" with a % pattern. + * If pattern contains a MACRO definition, it's expanded first. + * + * Return value: + * true if a match was found + * + * Parameters: + * target The target we're trying to match + * pattern + * percent record that contains "percent_buf" below + * percent_buf This is where the patched % part of pattern is stored + * + */ + +static Boolean +match_found_with_pattern(Name target, Percent pat_rule, String percent, wchar_t *percent_buf) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + + /* construct prefix string and check whether prefix matches */ + Name prefix = pat_rule->patterns[0]; + int prefix_length; + + Wstring targ_string(target); + Wstring pref_string(prefix); + Wstring suf_string; + + if (prefix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(prefix, &string, false); + prefix_length = string.text.p - string.buffer.start; + if ((string.buffer.start[0] == (int) period_char) && + (string.buffer.start[1] == (int) slash_char)) { + string.buffer.start += 2; + prefix_length -= 2; + } + if (!targ_string.equaln(string.buffer.start, prefix_length)) { + return false; + } + } else { + prefix_length = prefix->hash.length; + if (!targ_string.equaln(&pref_string, prefix_length)) { + return false; + } + } + + /* do the same with pattern suffix */ + Name suffix = pat_rule->patterns[pat_rule->patterns_total - 1]; + suf_string.init(suffix); + + int suffix_length; + if (suffix->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(suffix, &string, false); + suffix_length = string.text.p - string.buffer.start; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(string.buffer.start, target->hash.length - suffix_length)) { + return false; + } + } else { + suffix_length = (int) suffix->hash.length; + if(suffix_length > target->hash.length) { + return false; + } + if (!targ_string.equal(&suf_string, target->hash.length - suffix_length)) { + return false; + } + } + + Boolean match_found = false; + int percent_length = target->hash.length - prefix_length - suffix_length; + + while (!match_found && (percent_length >= 0)) { + /* init result string */ + INIT_STRING_FROM_STACK(string, string_buf); + + /* init percent string */ + percent->buffer.start = percent_buf; + percent->text.p = percent_buf; + percent->text.end = NULL; + percent->free_after_use = false; + percent->buffer.end = percent_buf + STRING_BUFFER_LENGTH; + + /* construct percent and result strings */ + targ_string.append_to_str(percent, prefix_length, percent_length); + construct_string_from_pattern(pat_rule, percent, &string); + + /* check for match */ + if (targ_string.equal(string.buffer.start, 0)) { + match_found = true; + } else { + percent_length--; + } + } + + /* result */ + return match_found; +} + + +/* + * create_target_group_and_dependencies_list + * (target, pat_rule, percent) + * + * constructs dependency list and a target group from pattern. + * + * If we have the lines + * %/%.a + %/%.b + C%/CC%.c: yyy %.d bb%/BB%.e + * commands + * + * and we have matched the pattern xx/xx.a with %/%.a, then we + * construct a target group that looks like this: + * xx/xx.a + xx/xx.b + Cxx/CCxx.c: dependencies + * + * and construct dependency list that looks like this: + * yyy xx.d bbxx/BBxx.e + already existed dependencies + * + * Return value: + * none + * + * Parameters: + * target The target we are building, in the previous + * example, this is xx/xx.a + * pat_rule the % pattern that matched "target", here %/%.a + * percent string containing matched % part. In the example=xx. + * + * Global variables used: + * empty_name + */ + +static void +create_target_group_and_dependencies_list(Name target, Percent pat_rule, String percent) { + String_rec string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + Percent pat_depe; + Name depe; + Property line = maybe_append_prop(target, line_prop); + Chain new_target_group = NULL; + Chain *new_target_group_tail = &new_target_group; + Chain group_member; + + /* create and append dependencies from rule */ + for (pat_depe = pat_rule->dependencies; pat_depe != NULL; pat_depe = pat_depe->next) { + if (pat_depe->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(pat_depe, percent, &string); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + if (depe != empty_name) { + enter_dependency(line, depe, false); + } + } else { + depe = pat_depe->name; + if(depe->dollar) { + INIT_STRING_FROM_STACK(string, string_buf); + expand_value(depe, &string, false); + depe = GETNAME(string.buffer.start, FIND_LENGTH); + } + enter_dependency(line, depe, false); + } + } + + /* if matched pattern is a group member, create new target group */ + for (group_member = pat_rule->target_group; group_member != NULL; group_member = group_member->next) { + Name new_target = group_member->name; + if (group_member->name->percent) { + INIT_STRING_FROM_STACK(string, string_buf); + construct_string_from_pattern(group_member->percent_member, percent, &string); + new_target = GETNAME(string.buffer.start, FIND_LENGTH); + if (new_target == empty_name) { + continue; + } + } + + /* check for duplicates */ + Chain tgm; + for (tgm = new_target_group; tgm != NULL; tgm = tgm->next) { + if (new_target == tgm->name) { + break; + } + } + if (tgm != NULL) { + continue; + } + + /* insert it into the targets list */ + (*new_target_group_tail) = ALLOC(Chain); + (*new_target_group_tail)->name = new_target; + (*new_target_group_tail)->next = NULL; + new_target_group_tail = &(*new_target_group_tail)->next; + } + + /* now we gathered all dependencies and created target group */ + line->body.line.target_group = new_target_group; + + /* update properties for group members */ + for (group_member = new_target_group; group_member != NULL; group_member = group_member->next) { + if (group_member->name != target) { + group_member->name->prop = target->prop; + group_member->name->conditional_cnt = target->conditional_cnt; + } + } +} + +/* + * construct_string_from_pattern + * (pat_rule, percent, result) + * + * after pattern matched a target this routine is called to construct targets and dependencies + * strings from this matched pattern rule and a string (percent) with substitutes % sign in pattern. + * + * Return value: + * none + * + * Parameters: + * pat_rule matched pattern rule + * percent string containing matched % sign part. + * result holds the result of string construction. + * + */ +static void +construct_string_from_pattern(Percent pat_rule, String percent, String result) { + for (int i = 0; i < pat_rule->patterns_total; i++) { + if (pat_rule->patterns[i]->dollar) { + expand_value(pat_rule->patterns[i], + result, + false); + + } else { + append_string(pat_rule->patterns[i]->string_mb, + result, + pat_rule->patterns[i]->hash.length); + } + + if (i < pat_rule->patterns_total - 1) { + append_string(percent->buffer.start, + result, + percent->text.p - percent->buffer.start); + } + } + + if ((result->buffer.start[0] == (int) period_char) && + (result->buffer.start[1] == (int) slash_char)) { + result->buffer.start += 2; + } +} + +/* + * dependency_exists(target, line) + * + * Returns true if the target exists in the + * dependency list of the line. + * + * Return value: + * True if target is on dependency list + * + * Parameters: + * target Target we scan for + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +dependency_exists(Name target, Property line) +{ + Dependency dp; + + if (line == NULL) { + return false; + } + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + if (dp->name == target) { + return true; + } + } + return false; +} + +void +add_target_to_chain(Name target, Chain * query) +{ + if (target->is_member && (get_prop(target->prop, member_prop) != NULL)) { + target = get_prop(target->prop, member_prop)->body.member.member; + } + Chain *query_tail; + for (query_tail = query; *query_tail != NULL; query_tail = &(*query_tail)->next) { + if ((*query_tail)->name == target) { + return; + } + } + *query_tail = ALLOC(Chain); + (*query_tail)->name = target; + (*query_tail)->next = NULL; +} + diff --git a/bin/macro.cc b/bin/macro.cc new file mode 100644 index 0000000..a33484a --- /dev/null +++ b/bin/macro.cc @@ -0,0 +1,167 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * macro.cc + * + * Handle expansion of make macros + */ + +/* + * Included files + */ +#include +#include /* getvar(), expand_value() */ +#include /* getmem() */ + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ + +void +setvar_append(register Name name, register Name value) +{ + register Property macro_apx = get_prop(name->prop, macro_append_prop); + register Property macro = get_prop(name->prop, macro_prop); + int length; + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Chain chain; + Name val = NULL; + + if(macro_apx == NULL) { + macro_apx = append_prop(name, macro_append_prop); + if(macro != NULL) { + macro_apx->body.macro_appendix.value = macro->body.macro.value; + } + } + + val = macro_apx->body.macro_appendix.value_to_append; + + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if (val != NULL) { + APPEND_NAME(val, + &destination, + (int) val->hash.length); + if (value != NULL) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + macro_apx->body.macro_appendix.value_to_append = value; + + SETVAR(name, empty_name, true); +} + +/* + * setvar_envvar() + * + * This function scans the list of environment variables that have + * dynamic values and sets them. + * + * Parameters: + * + * Global variables used: + * envvar A list of environment vars with $ in value + */ +void +setvar_envvar(void) +{ + wchar_t buffer[STRING_BUFFER_LENGTH]; + int length; + register char *mbs, *tmp_mbs_buffer = NULL; + register char *env, *tmp_mbs_buffer2 = NULL; + Envvar p; + String_rec value; + + for (p = envvar; p != NULL; p = p->next) { + if (p->already_put + ) { + continue; + } + INIT_STRING_FROM_STACK(value, buffer); + expand_value(p->value, &value, false); + if ((length = wcslen(value.buffer.start)) >= MAXPATHLEN) { + mbs = tmp_mbs_buffer = getmem((length + 1) * MB_LEN_MAX); + (void) wcstombs(mbs, + value.buffer.start, + (length + 1) * MB_LEN_MAX); + } else { + mbs = mbs_buffer; + WCSTOMBS(mbs, value.buffer.start); + } + length = 2 + strlen(p->name->string_mb) + strlen(mbs); + if (!p->already_put || length > (MAXPATHLEN * MB_LEN_MAX)) { + env = tmp_mbs_buffer2 = getmem(length); + } else { + env = mbs_buffer2; + } + (void) sprintf(env, + "%s=%s", + p->name->string_mb, + mbs); + if (!p->already_put) { + (void) putenv(env); + p->already_put = true; + if (p->env_string) { + retmem_mb(p->env_string); + } + p->env_string = env; + tmp_mbs_buffer2 = NULL; // We should not return this memory now + } + if (tmp_mbs_buffer2) { + retmem_mb(tmp_mbs_buffer2); + tmp_mbs_buffer2 = NULL; + } + if (tmp_mbs_buffer) { + retmem_mb(tmp_mbs_buffer); + tmp_mbs_buffer = NULL; + } + } +} + + diff --git a/bin/main.cc b/bin/main.cc new file mode 100644 index 0000000..ed0afc0 --- /dev/null +++ b/bin/main.cc @@ -0,0 +1,3215 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * main.cc + * + * make program main routine plus some helper routines + */ + +/* + * Included files + */ +#include /* bsd_signal() */ + + +#include /* setlocale() */ +#include +#include +#include /* getvar() */ +#include /* getmem(), setup_char_semantics() */ + +#include /* getpwnam() */ +#include +#include +#include +#include /* ENOENT */ +#include /* fstat() */ +#include /* open() */ + +# include /* sysinfo() */ + +#include /* stat() */ +#include /* wait() */ +#include /* execv(), unlink(), access() */ +#include /* report_dependency(), get_report_file() */ + +// From read2.cc +extern Name normalize_name(register wchar_t *name_string, register int length); + +extern void job_adjust_fini(); + + +/* + * Defined macros + */ +#define LD_SUPPORT_ENV_VAR "SGS_SUPPORT_32" +#define LD_SUPPORT_ENV_VAR_32 "SGS_SUPPORT_32" +#define LD_SUPPORT_ENV_VAR_64 "SGS_SUPPORT_64" +#define LD_SUPPORT_MAKE_LIB "libmakestate.so.1" +#ifdef __i386 +#define LD_SUPPORT_MAKE_ARCH "i386" +#elif __sparc +#define LD_SUPPORT_MAKE_ARCH "sparc" +#else +#error "Unsupported architecture" +#endif + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static char *argv_zero_string; +static Boolean build_failed_ever_seen; +static Boolean continue_after_error_ever_seen; /* `-k' */ +static Boolean dmake_group_specified; /* `-g' */ +static Boolean dmake_max_jobs_specified; /* `-j' */ +static Boolean dmake_mode_specified; /* `-m' */ +static Boolean dmake_add_mode_specified; /* `-x' */ +static Boolean dmake_output_mode_specified; /* `-x DMAKE_OUTPUT_MODE=' */ +static Boolean dmake_compat_mode_specified; /* `-x SUN_MAKE_COMPAT_MODE=' */ +static Boolean dmake_odir_specified; /* `-o' */ +static Boolean dmake_rcfile_specified; /* `-c' */ +static Boolean env_wins; /* `-e' */ +static Boolean ignore_default_mk; /* `-r' */ +static Boolean list_all_targets; /* `-T' */ +static int mf_argc; +static char **mf_argv; +static Dependency_rec not_auto_depen_struct; +static Dependency not_auto_depen = ¬_auto_depen_struct; +static Boolean pmake_cap_r_specified; /* `-R' */ +static Boolean pmake_machinesfile_specified; /* `-M' */ +static Boolean stop_after_error_ever_seen; /* `-S' */ +static Boolean trace_status; /* `-p' */ + +#ifdef DMAKE_STATISTICS +static Boolean getname_stat = false; +#endif + + static time_t start_time; + static int g_argc; + static char **g_argv; + +/* + * File table of contents + */ + extern "C" void cleanup_after_exit(void); + +extern "C" { + extern void dmake_exit_callback(void); + extern void dmake_message_callback(char *); +} + +extern Name normalize_name(register wchar_t *name_string, register int length); + +extern int main(int, char * []); + +static void append_makeflags_string(Name, String); +static void doalarm(int); +static void enter_argv_values(int , char **, ASCII_Dyn_Array *); +static void make_targets(int, char **, Boolean); +static int parse_command_option(char); +static void read_command_options(int, char **); +static void read_environment(Boolean); +static void read_files_and_state(int, char **); +static Boolean read_makefile(Name, Boolean, Boolean, Boolean); +static void report_recursion(Name); +static void set_sgs_support(void); +static void setup_for_projectdir(void); +static void setup_makeflags_argv(void); +static void report_dir_enter_leave(Boolean entering); + +extern void expand_value(Name, register String , Boolean); + +static const char verstring[] = "illumos make"; + +jmp_buf jmpbuffer; + +/* + * main(argc, argv) + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * list_all_targets make -T seen + * trace_status make -p seen + * + * Global variables used: + * debug_level Should we trace make actions? + * keep_state Set if .KEEP_STATE seen + * makeflags The Name "MAKEFLAGS", used to get macro + * remote_command_name Name of remote invocation cmd ("on") + * running_list List of parallel running processes + * stdout_stderr_same true if stdout and stderr are the same + * auto_dependencies The Name "SUNPRO_DEPENDENCIES" + * temp_file_directory Set to the dir where we create tmp file + * trace_reader Set to reflect tracing status + * working_on_targets Set when building user targets + */ +int +main(int argc, char *argv[]) +{ + /* + * cp is a -> to the value of the MAKEFLAGS env var, + * which has to be regular chars. + */ + register char *cp; + char make_state_dir[MAXPATHLEN]; + Boolean parallel_flag = false; + char *prognameptr; + char *slash_ptr; + mode_t um; + int i; + struct itimerval value; + char def_dmakerc_path[MAXPATHLEN]; + Name dmake_name, dmake_name2; + Name dmake_value, dmake_value2; + Property prop, prop2; + struct stat statbuf; + int statval; + + struct stat out_stat, err_stat; + hostid = gethostid(); + bsd_signals(); + + (void) setlocale(LC_ALL, ""); + + +#ifdef DMAKE_STATISTICS + if (getenv("DMAKE_STATISTICS")) { + getname_stat = true; + } +#endif + +#ifndef TEXT_DOMAIN +#define TEXT_DOMAIN "SYS_TEST" +#endif + textdomain(TEXT_DOMAIN); + + g_argc = argc; + g_argv = (char **) malloc((g_argc + 1) * sizeof(char *)); + for (i = 0; i < argc; i++) { + g_argv[i] = argv[i]; + } + g_argv[i] = NULL; + + /* + * Set argv_zero_string to some form of argv[0] for + * recursive MAKE builds. + */ + + if (*argv[0] == (int) slash_char) { + /* argv[0] starts with a slash */ + argv_zero_string = strdup(argv[0]); + } else if (strchr(argv[0], (int) slash_char) == NULL) { + /* argv[0] contains no slashes */ + argv_zero_string = strdup(argv[0]); + } else { + /* + * argv[0] contains at least one slash, + * but doesn't start with a slash + */ + char *tmp_current_path; + char *tmp_string; + + tmp_current_path = get_current_path(); + tmp_string = getmem(strlen(tmp_current_path) + 1 + + strlen(argv[0]) + 1); + (void) sprintf(tmp_string, + "%s/%s", + tmp_current_path, + argv[0]); + argv_zero_string = strdup(tmp_string); + retmem_mb(tmp_string); + } + + /* + * The following flags are reset if we don't have the + * (.nse_depinfo or .make.state) files locked and only set + * AFTER the file has been locked. This ensures that if the user + * interrupts the program while file_lock() is waiting to lock + * the file, the interrupt handler doesn't remove a lock + * that doesn't belong to us. + */ + make_state_lockfile = NULL; + make_state_locked = false; + + + /* + * look for last slash char in the path to look at the binary + * name. This is to resolve the hard link and invoke make + * in svr4 mode. + */ + + /* Sun OS make standart */ + svr4 = false; + posix = false; + if(!strcmp(argv_zero_string, "/usr/xpg4/bin/make")) { + svr4 = false; + posix = true; + } else { + prognameptr = strrchr(argv[0], '/'); + if(prognameptr) { + prognameptr++; + } else { + prognameptr = argv[0]; + } + if(!strcmp(prognameptr, "svr4.make")) { + svr4 = true; + posix = false; + } + } + if (getenv(USE_SVR4_MAKE) || getenv("USE_SVID")){ + svr4 = true; + posix = false; + } + + /* + * Find the dmake_compat_mode: posix, sun, svr4, or gnu_style, . + */ + char * dmake_compat_mode_var = getenv("SUN_MAKE_COMPAT_MODE"); + if (dmake_compat_mode_var != NULL) { + if (0 == strcasecmp(dmake_compat_mode_var, "GNU")) { + gnu_style = true; + } + //svr4 = false; + //posix = false; + } + + /* + * Temporary directory set up. + */ + char * tmpdir_var = getenv("TMPDIR"); + if (tmpdir_var != NULL && *tmpdir_var == '/' && strlen(tmpdir_var) < MAXPATHLEN) { + strcpy(mbs_buffer, tmpdir_var); + for (tmpdir_var = mbs_buffer+strlen(mbs_buffer); + *(--tmpdir_var) == '/' && tmpdir_var > mbs_buffer; + *tmpdir_var = '\0'); + if (strlen(mbs_buffer) + 32 < MAXPATHLEN) { /* 32 = strlen("/dmake.stdout.%d.%d.XXXXXX") */ + sprintf(mbs_buffer2, "%s/dmake.tst.%d.XXXXXX", + mbs_buffer, getpid()); + int fd = mkstemp(mbs_buffer2); + if (fd >= 0) { + close(fd); + unlink(mbs_buffer2); + tmpdir = strdup(mbs_buffer); + } + } + } + + /* find out if stdout and stderr point to the same place */ + if (fstat(1, &out_stat) < 0) { + fatal(gettext("fstat of standard out failed: %s"), errmsg(errno)); + } + if (fstat(2, &err_stat) < 0) { + fatal(gettext("fstat of standard error failed: %s"), errmsg(errno)); + } + if ((out_stat.st_dev == err_stat.st_dev) && + (out_stat.st_ino == err_stat.st_ino)) { + stdout_stderr_same = true; + } else { + stdout_stderr_same = false; + } + /* Make the vroot package scan the path using shell semantics */ + set_path_style(0); + + setup_char_semantics(); + + setup_for_projectdir(); + + /* + * If running with .KEEP_STATE, curdir will be set with + * the connected directory. + */ + (void) atexit(cleanup_after_exit); + + load_cached_names(); + +/* + * Set command line flags + */ + setup_makeflags_argv(); + read_command_options(mf_argc, mf_argv); + read_command_options(argc, argv); + if (debug_level > 0) { + cp = getenv(makeflags->string_mb); + (void) printf(gettext("MAKEFLAGS value: %s\n"), cp == NULL ? "" : cp); + } + + setup_interrupt(handle_interrupt); + + read_files_and_state(argc, argv); + + /* + * Find the dmake_output_mode: TXT1, TXT2 or HTML1. + */ + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + if (prop2 == NULL) { + /* DMAKE_OUTPUT_MODE not defined, default to TXT1 mode */ + output_mode = txt1_mode; + } else { + dmake_value2 = prop2->body.macro.value; + if ((dmake_value2 == NULL) || + (IS_EQUAL(dmake_value2->string_mb, "TXT1"))) { + output_mode = txt1_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, "TXT2")) { + output_mode = txt2_mode; + } else if (IS_EQUAL(dmake_value2->string_mb, "HTML1")) { + output_mode = html1_mode; + } else { + warning(gettext("Unsupported value `%s' for DMAKE_OUTPUT_MODE after -x flag (ignored)"), + dmake_value2->string_mb); + } + } + /* + * Find the dmake_mode: parallel, or serial. + */ + if ((!pmake_cap_r_specified) && + (!pmake_machinesfile_specified)) { + char *s, *b; + + if ((s = strdup(argv[0])) == NULL) + fatal(gettext("Out of memory")); + + b = basename(s); + + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + dmake_name2 = GETNAME(wcs_buffer, FIND_LENGTH); + prop2 = get_prop(dmake_name2->prop, macro_prop); + // If we're invoked as 'make' run serially, regardless of DMAKE_MODE + // If we're invoked as 'make' but passed -j, run parallel + // If we're invoked as 'dmake', without DMAKE_MODE, default parallel + // If we're invoked as 'dmake' and DMAKE_MODE is set, honour it. + if ((strcmp(b, "make") == 0) && + !dmake_max_jobs_specified) { + dmake_mode_type = serial_mode; + no_parallel = true; + } else if (prop2 == NULL) { + /* DMAKE_MODE not defined, default based on our name */ + if (strcmp(b, "dmake") == 0) { + dmake_mode_type = parallel_mode; + no_parallel = false; + } + } else { + dmake_value2 = prop2->body.macro.value; + if (IS_EQUAL(dmake_value2->string_mb, "parallel")) { + dmake_mode_type = parallel_mode; + no_parallel = false; + } else if (IS_EQUAL(dmake_value2->string_mb, "serial")) { + dmake_mode_type = serial_mode; + no_parallel = true; + } else { + fatal(gettext("Unknown dmake mode argument `%s' after -m flag"), dmake_value2->string_mb); + } + } + free(s); + } + + parallel_flag = true; + putenv(strdup("DMAKE_CHILD=TRUE")); + +// +// If dmake is running with -t option, set dmake_mode_type to serial. +// This is done because doname() calls touch_command() that runs serially. +// If we do not do that, maketool will have problems. +// + if(touch) { + dmake_mode_type = serial_mode; + no_parallel = true; + } + + /* + * Check whether stdout and stderr are physically same. + * This is in order to decide whether we need to redirect + * stderr separately from stdout. + * This check is performed only if __DMAKE_SEPARATE_STDERR + * is not set. This variable may be used in order to preserve + * the 'old' behaviour. + */ + out_err_same = true; + char * dmake_sep_var = getenv("__DMAKE_SEPARATE_STDERR"); + if (dmake_sep_var == NULL || (0 != strcasecmp(dmake_sep_var, "NO"))) { + struct stat stdout_stat; + struct stat stderr_stat; + if( (fstat(1, &stdout_stat) == 0) + && (fstat(2, &stderr_stat) == 0) ) + { + if( (stdout_stat.st_dev != stderr_stat.st_dev) + || (stdout_stat.st_ino != stderr_stat.st_ino) ) + { + out_err_same = false; + } + } + } + + +/* + * Enable interrupt handler for alarms + */ + (void) bsd_signal(SIGALRM, (SIG_PF)doalarm); + +/* + * Check if make should report + */ + if (getenv(sunpro_dependencies->string_mb) != NULL) { + FILE *report_file; + + report_dependency(""); + report_file = get_report_file(); + if ((report_file != NULL) && (report_file != (FILE*)-1)) { + (void) fprintf(report_file, "\n"); + } + } + +/* + * Make sure SUNPRO_DEPENDENCIES is exported (or not) properly. + */ + if (keep_state) { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = true; + } else { + maybe_append_prop(sunpro_dependencies, macro_prop)-> + body.macro.exported = false; + } + + working_on_targets = true; + if (trace_status) { + dump_make_state(); + fclose(stdout); + fclose(stderr); + exit_status = 0; + exit(0); + } + if (list_all_targets) { + dump_target_list(); + fclose(stdout); + fclose(stderr); + exit_status = 0; + exit(0); + } + trace_reader = false; + + /* + * Set temp_file_directory to the directory the .make.state + * file is written to. + */ + if ((slash_ptr = strrchr(make_state->string_mb, (int) slash_char)) == NULL) { + temp_file_directory = strdup(get_current_path()); + } else { + *slash_ptr = (int) nul_char; + (void) strcpy(make_state_dir, make_state->string_mb); + *slash_ptr = (int) slash_char; + /* when there is only one slash and it's the first + ** character, make_state_dir should point to '/'. + */ + if(make_state_dir[0] == '\0') { + make_state_dir[0] = '/'; + make_state_dir[1] = '\0'; + } + if (make_state_dir[0] == (int) slash_char) { + temp_file_directory = strdup(make_state_dir); + } else { + char tmp_current_path2[MAXPATHLEN]; + + (void) sprintf(tmp_current_path2, + "%s/%s", + get_current_path(), + make_state_dir); + temp_file_directory = strdup(tmp_current_path2); + } + } + + + report_dir_enter_leave(true); + + make_targets(argc, argv, parallel_flag); + + report_dir_enter_leave(false); + + if (build_failed_ever_seen) { + if (posix) { + exit_status = 1; + } + exit(1); + } + exit_status = 0; + exit(0); + /* NOTREACHED */ +} + +/* + * cleanup_after_exit() + * + * Called from exit(), performs cleanup actions. + * + * Parameters: + * status The argument exit() was called with + * arg Address of an argument vector to + * cleanup_after_exit() + * + * Global variables used: + * command_changed Set if we think .make.state should be rewritten + * current_line Is set we set commands_changed + * do_not_exec_rule + * True if -n flag on + * done The Name ".DONE", rule we run + * keep_state Set if .KEEP_STATE seen + * parallel True if building in parallel + * quest If -q is on we do not run .DONE + * report_dependencies + * True if -P flag on + * running_list List of parallel running processes + * temp_file_name The temp file is removed, if any + */ +extern "C" void +cleanup_after_exit(void) +{ + Running rp; + +extern long getname_bytes_count; +extern long getname_names_count; +extern long getname_struct_count; +extern long freename_bytes_count; +extern long freename_names_count; +extern long freename_struct_count; +extern long other_alloc; + +extern long env_alloc_num; +extern long env_alloc_bytes; + + +#ifdef DMAKE_STATISTICS +if(getname_stat) { + printf(">>> Getname statistics:\n"); + printf(" Allocated:\n"); + printf(" Names: %ld\n", getname_names_count); + printf(" Strings: %ld Kb (%ld bytes)\n", getname_bytes_count/1000, getname_bytes_count); + printf(" Structs: %ld Kb (%ld bytes)\n", getname_struct_count/1000, getname_struct_count); + printf(" Total bytes: %ld Kb (%ld bytes)\n", getname_struct_count/1000 + getname_bytes_count/1000, getname_struct_count + getname_bytes_count); + + printf("\n Unallocated: %ld\n", freename_names_count); + printf(" Names: %ld\n", freename_names_count); + printf(" Strings: %ld Kb (%ld bytes)\n", freename_bytes_count/1000, freename_bytes_count); + printf(" Structs: %ld Kb (%ld bytes)\n", freename_struct_count/1000, freename_struct_count); + printf(" Total bytes: %ld Kb (%ld bytes)\n", freename_struct_count/1000 + freename_bytes_count/1000, freename_struct_count + freename_bytes_count); + + printf("\n Total used: %ld Kb (%ld bytes)\n", (getname_struct_count/1000 + getname_bytes_count/1000) - (freename_struct_count/1000 + freename_bytes_count/1000), (getname_struct_count + getname_bytes_count) - (freename_struct_count + freename_bytes_count)); + + printf("\n>>> Other:\n"); + printf( + " Env (%ld): %ld Kb (%ld bytes)\n", + env_alloc_num, + env_alloc_bytes/1000, + env_alloc_bytes + ); + +} +#endif + + parallel = false; + /* If we used the SVR4_MAKE, don't build .DONE or .FAILED */ + if (!getenv(USE_SVR4_MAKE)){ + /* Build the target .DONE or .FAILED if we caught an error */ + if (!quest && !list_all_targets) { + Name failed_name; + + MBSTOWCS(wcs_buffer, ".FAILED"); + failed_name = GETNAME(wcs_buffer, FIND_LENGTH); + if ((exit_status != 0) && (failed_name->prop != NULL)) { + /* + * [tolik] switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; + (void) doname(failed_name, false, true); + } else { + if (!trace_status) { + /* + * Switch DMake to serial mode + */ + dmake_mode_type = serial_mode; + no_parallel = true; + (void) doname(done, false, true); + } + } + } + } + /* + * Remove the temp file utilities report dependencies thru if it + * is still around + */ + if (temp_file_name != NULL) { + (void) unlink(temp_file_name->string_mb); + } + /* + * Do not save the current command in .make.state if make + * was interrupted. + */ + if (current_line != NULL) { + command_changed = true; + current_line->body.line.command_used = NULL; + } + /* + * For each parallel build process running, remove the temp files + * and zap the command line so it won't be put in .make.state + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->temp_file != NULL) { + (void) unlink(rp->temp_file->string_mb); + } + if (rp->stdout_file != NULL) { + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } + if (rp->stderr_file != NULL) { + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } + command_changed = true; +/* + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + */ + } + /* Remove the statefile lock file if the file has been locked */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + (void) unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + /* Write .make.state */ + write_state_file(1, (Boolean) 1); + + job_adjust_fini(); +} + +/* + * handle_interrupt() + * + * This is where C-C traps are caught. + * + * Parameters: + * + * Global variables used (except DMake 1.0): + * current_target Sometimes the current target is removed + * do_not_exec_rule But not if -n is on + * quest or -q + * running_list List of parallel running processes + * touch Current target is not removed if -t on + */ +void +handle_interrupt(int) +{ + Property member; + Running rp; + + (void) fflush(stdout); + if (childPid > 0) { + kill(childPid, SIGTERM); + childPid = -1; + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->pid > 0) { + kill(rp->pid, SIGTERM); + rp->pid = -1; + } + } + if (getpid() == getpgrp()) { + bsd_signal(SIGTERM, SIG_IGN); + kill (-getpid(), SIGTERM); + } + /* Clean up all parallel children already finished */ + finish_children(false); + + /* Make sure the processes running under us terminate first */ + + while (wait((int *) NULL) != -1); + /* Delete the current targets unless they are precious */ + if ((current_target != NULL) && + current_target->is_member && + ((member = get_prop(current_target->prop, member_prop)) != NULL)) { + current_target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + (current_target != NULL) && + !(current_target->stat.is_precious || all_precious)) { + +/* BID_1030811 */ +/* azv 16 Oct 95 */ + current_target->stat.time = file_no_time; + + if (exists(current_target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + current_target->string_mb); + if (current_target->stat.is_dir) { + (void) fprintf(stderr, + gettext("not removed.\n"), + current_target->string_mb); + } else if (unlink(current_target->string_mb) == 0) { + (void) fprintf(stderr, + gettext("removed.\n"), + current_target->string_mb); + } else { + (void) fprintf(stderr, + gettext("could not be removed: %s.\n"), + current_target->string_mb, + errmsg(errno)); + } + } + } + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state != build_running) { + continue; + } + if (rp->target->is_member && + ((member = get_prop(rp->target->prop, member_prop)) != + NULL)) { + rp->target = member->body.member.library; + } + if (!do_not_exec_rule && + !touch && + !quest && + !(rp->target->stat.is_precious || all_precious)) { + + rp->target->stat.time = file_no_time; + if (exists(rp->target) != file_doesnt_exist) { + (void) fprintf(stderr, + "\n*** %s ", + rp->target->string_mb); + if (rp->target->stat.is_dir) { + (void) fprintf(stderr, + gettext("not removed.\n"), + rp->target->string_mb); + } else if (unlink(rp->target->string_mb) == 0) { + (void) fprintf(stderr, + gettext("removed.\n"), + rp->target->string_mb); + } else { + (void) fprintf(stderr, + gettext("could not be removed: %s.\n"), + rp->target->string_mb, + errmsg(errno)); + } + } + } + } + + + /* Have we locked .make.state or .nse_depinfo? */ + if ((make_state_lockfile != NULL) && (make_state_locked)) { + unlink(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + /* + * Re-read .make.state file (it might be changed by recursive make) + */ + check_state(NULL); + + report_dir_enter_leave(false); + + exit_status = 2; + exit(2); +} + +/* + * doalarm(sig, ...) + * + * Handle the alarm interrupt but do nothing. Side effect is to + * cause return from wait3. + * + * Parameters: + * sig + * + * Global variables used: + */ +/*ARGSUSED*/ +static void +doalarm(int) +{ + return; +} + + +/* + * read_command_options(argc, argv) + * + * Scan the cmd line options and process the ones that start with "-" + * + * Return value: + * -M argument, if any + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Global variables used: + */ +static void +read_command_options(register int argc, register char **argv) +{ + register int ch; + int current_optind = 1; + int last_optind_with_double_hyphen = 0; + int last_optind; + int last_current_optind; + register int i; + register int j; + register int k; + register int makefile_next = 0; /* + * flag to note options: + * -c, f, g, j, m, o + */ + const char *tptr; + const char *CMD_OPTS; + + extern char *optarg; + extern int optind, opterr, optopt; + +#define SUNPRO_CMD_OPTS "-~Bbc:Ddef:g:ij:K:kM:m:NnO:o:PpqRrSsTtuVvwx:" + +# define SVR4_CMD_OPTS "-c:ef:g:ij:km:nO:o:pqrsTtVv" + + /* + * Added V in SVR4_CMD_OPTS also, which is going to be a hidden + * option, just to make sure that the getopt doesn't fail when some + * users leave their USE_SVR4_MAKE set and try to use the makefiles + * that are designed to issue commands like $(MAKE) -V. Anyway it + * sets the same flag but ensures that getopt doesn't fail. + */ + + opterr = 0; + optind = 1; + while (1) { + last_optind=optind; /* Save optind and current_optind values */ + last_current_optind=current_optind; /* in case we have to repeat this round. */ + if (svr4) { + CMD_OPTS=SVR4_CMD_OPTS; + ch = getopt(argc, argv, SVR4_CMD_OPTS); + } else { + CMD_OPTS=SUNPRO_CMD_OPTS; + ch = getopt(argc, argv, SUNPRO_CMD_OPTS); + } + if (ch == EOF) { + if(optind < argc) { + /* + * Fixing bug 4102537: + * Strange behaviour of command make using -- option. + * Not all argv have been processed + * Skip non-flag argv and continue processing. + */ + optind++; + current_optind++; + continue; + } else { + break; + } + + } + if (ch == '?') { + if (optopt == '-') { + /* Bug 5060758: getopt() changed behavior (s10_60), + * and now we have to deal with cases when options + * with double hyphen appear here, from -$(MAKEFLAGS) + */ + i = current_optind; + if (argv[i][0] == '-') { + if (argv[i][1] == '-') { + if (argv[i][2] != '\0') { + /* Check if this option is allowed */ + tptr = strchr(CMD_OPTS, argv[i][2]); + if (tptr) { + if (last_optind_with_double_hyphen != current_optind) { + /* This is first time we are trying to fix "--" + * problem with this option. If we come here second + * time, we will go to fatal error. + */ + last_optind_with_double_hyphen = current_optind; + + /* Eliminate first hyphen character */ + for (j=0; argv[i][j] != '\0'; j++) { + argv[i][j] = argv[i][j+1]; + } + + /* Repeat the processing of this argument */ + optind=last_optind; + current_optind=last_current_optind; + continue; + } + } + } + } + } + } + } + + if (ch == '?') { + if (svr4) { + fprintf(stderr, + gettext("Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + gettext(" [ -j dmake_max_jobs ][ -m dmake_mode ][ -o dmake_odir ]...\n")); + fprintf(stderr, + gettext(" [ -e ][ -i ][ -k ][ -n ][ -p ][ -q ][ -r ][ -s ][ -t ][ -v ]\n")); + tptr = strchr(SVR4_CMD_OPTS, optopt); + } else { + fprintf(stderr, + gettext("Usage : dmake [ -f makefile ][ -c dmake_rcfile ][ -g dmake_group ]\n")); + fprintf(stderr, + gettext(" [ -j dmake_max_jobs ][ -K statefile ][ -m dmake_mode ][ -x MODE_NAME=VALUE ][ -o dmake_odir ]...\n")); + fprintf(stderr, + gettext(" [ -d ][ -dd ][ -D ][ -DD ][ -e ][ -i ][ -k ][ -n ][ -p ][ -P ][ -u ][ -w ]\n")); + fprintf(stderr, + gettext(" [ -q ][ -r ][ -s ][ -S ][ -t ][ -v ][ -V ][ target... ][ macro=value... ][ \"macro +=value\"... ]\n")); + tptr = strchr(SUNPRO_CMD_OPTS, optopt); + } + if (!tptr) { + fatal(gettext("Unknown option `-%c'"), optopt); + } else { + fatal(gettext("Missing argument after `-%c'"), optopt); + } + } + + + + makefile_next |= parse_command_option(ch); + /* + * If we're done processing all of the options of + * ONE argument string... + */ + if (current_optind < optind) { + i = current_optind; + k = 0; + /* If there's an argument for an option... */ + if ((optind - current_optind) > 1) { + k = i + 1; + } + switch (makefile_next) { + case 0: + argv[i] = NULL; + /* This shouldn't happen */ + if (k) { + argv[k] = NULL; + } + break; + case 1: /* -f seen */ + argv[i] = (char *)"-f"; + break; + case 2: /* -c seen */ + argv[i] = (char *)"-c"; + break; + case 4: /* -g seen */ + argv[i] = (char *)"-g"; + break; + case 8: /* -j seen */ + argv[i] = (char *)"-j"; + break; + case 16: /* -M seen */ + argv[i] = (char *)"-M"; + break; + case 32: /* -m seen */ + argv[i] = (char *)"-m"; + break; + case 128: /* -O seen */ + argv[i] = (char *)"-O"; + break; + case 256: /* -K seen */ + argv[i] = (char *)"-K"; + break; + case 512: /* -o seen */ + argv[i] = (char *)"-o"; + break; + case 1024: /* -x seen */ + argv[i] = (char *)"-x"; + break; + default: /* > 1 of -c, f, g, j, K, M, m, O, o, x seen */ + fatal(gettext("Illegal command line. More than one option requiring\nan argument given in the same argument group")); + } + + makefile_next = 0; + current_optind = optind; + } + } +} + +static void +quote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + switch (*from) { + case ';': /* End of command */ + case '(': /* Start group */ + case ')': /* End group */ + case '{': /* Start group */ + case '}': /* End group */ + case '[': /* Reg expr - any of a set of chars */ + case ']': /* End of set of chars */ + case '|': /* Pipe or logical-or */ + case '^': /* Old-fashioned pipe */ + case '&': /* Background or logical-and */ + case '<': /* Redirect stdin */ + case '>': /* Redirect stdout */ + case '*': /* Reg expr - any sequence of chars */ + case '?': /* Reg expr - any single char */ + case '$': /* Variable substitution */ + case '\'': /* Singe quote - turn off all magic */ + case '"': /* Double quote - span whitespace */ + case '`': /* Backquote - run a command */ + case '#': /* Comment */ + case ' ': /* Space (for MACRO=value1 value2 */ + case '\\': /* Escape char - turn off magic of next char */ + *to++ = '\\'; + break; + + default: + break; + } + *to++ = *from; + } + *to = '\0'; +} + +static void +unquote_str(char *str, char *qstr) +{ + char *to; + char *from; + + to = qstr; + for (from = str; *from; from++) { + if (*from == '\\') { + from++; + } + *to++ = *from; + } + *to = '\0'; +} + +/* + * Convert the MAKEFLAGS string value into a vector of char *, similar + * to argv. + */ +static void +setup_makeflags_argv() +{ + char *cp; + char *cp1; + char *cp2; + char *cp3; + char *cp_orig; + Boolean add_hyphen; + int i; + char tmp_char; + + mf_argc = 1; + cp = getenv(makeflags->string_mb); + cp_orig = cp; + + if (cp) { + /* + * If new MAKEFLAGS format, no need to add hyphen. + * If old MAKEFLAGS format, add hyphen before flags. + */ + + if ((strchr(cp, (int) hyphen_char) != NULL) || + (strchr(cp, (int) equal_char) != NULL)) { + + /* New MAKEFLAGS format */ + + add_hyphen = false; + + /* Check if MAKEFLAGS value begins with multiple + * hyphen characters, and remove all duplicates. + * Usually it happens when the next command is + * used: $(MAKE) -$(MAKEFLAGS) + * + * This was a workaround for BugID 5060758, but + * appears to have survived as a fix in make. + */ + while (*cp) { + if (*cp != (int) hyphen_char) { + break; + } + cp++; + if (*cp == (int) hyphen_char) { + /* There are two hyphens. Skip one */ + cp_orig = cp; + cp++; + } + if (!(*cp)) { + /* There are hyphens only. Skip all */ + cp_orig = cp; + break; + } + } + } else { + + /* Old MAKEFLAGS format */ + + add_hyphen = true; + } + } + + /* Find the number of arguments in MAKEFLAGS */ + while (cp && *cp) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + /* Increment arg count */ + mf_argc++; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + } + } + /* Allocate memory for the new MAKEFLAGS argv */ + mf_argv = (char **) malloc((mf_argc + 1) * sizeof(char *)); + mf_argv[0] = (char *)"MAKEFLAGS"; + /* + * Convert the MAKEFLAGS string value into a vector of char *, + * similar to argv. + */ + cp = cp_orig; + for (i = 1; i < mf_argc; i++) { + /* Skip white spaces */ + while (cp && *cp && isspace(*cp)) { + cp++; + } + if (cp && *cp) { + cp_orig = cp; + /* Go to next white space */ + while (cp && *cp && !isspace(*cp)) { + if(*cp == (int) backslash_char) { + cp++; + } + cp++; + } + tmp_char = *cp; + *cp = (int) nul_char; + if (add_hyphen) { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + mf_argv[i][0] = '\0'; + (void) strcat(mf_argv[i], "-"); + // (void) strcat(mf_argv[i], cp_orig); + unquote_str(cp_orig, mf_argv[i]+1); + } else { + mf_argv[i] = getmem(2 + strlen(cp_orig)); + //mf_argv[i] = strdup(cp_orig); + unquote_str(cp_orig, mf_argv[i]); + } + *cp = tmp_char; + } + } + mf_argv[i] = NULL; +} + +/* + * parse_command_option(ch) + * + * Parse make command line options. + * + * Return value: + * Indicates if any -f -c or -M were seen + * + * Parameters: + * ch The character to parse + * + * Static variables used: + * dmake_group_specified Set for make -g + * dmake_max_jobs_specified Set for make -j + * dmake_mode_specified Set for make -m + * dmake_add_mode_specified Set for make -x + * dmake_compat_mode_specified Set for make -x SUN_MAKE_COMPAT_MODE= + * dmake_output_mode_specified Set for make -x DMAKE_OUTPUT_MODE= + * dmake_odir_specified Set for make -o + * dmake_rcfile_specified Set for make -c + * env_wins Set for make -e + * ignore_default_mk Set for make -r + * trace_status Set for make -p + * + * Global variables used: + * .make.state path & name set for make -K + * continue_after_error Set for make -k + * debug_level Set for make -d + * do_not_exec_rule Set for make -n + * filter_stderr Set for make -X + * ignore_errors_all Set for make -i + * no_parallel Set for make -R + * quest Set for make -q + * read_trace_level Set for make -D + * report_dependencies Set for make -P + * silent_all Set for make -s + * touch Set for make -t + */ +static int +parse_command_option(register char ch) +{ + static int invert_next = 0; + int invert_this = invert_next; + + invert_next = 0; + switch (ch) { + case '-': /* Ignore "--" */ + return 0; + case '~': /* Invert next option */ + invert_next = 1; + return 0; + case 'B': /* Obsolete */ + return 0; + case 'b': /* Obsolete */ + return 0; + case 'c': /* Read alternative dmakerc file */ + if (invert_this) { + dmake_rcfile_specified = false; + } else { + dmake_rcfile_specified = true; + } + return 2; + case 'D': /* Show lines read */ + if (invert_this) { + read_trace_level--; + } else { + read_trace_level++; + } + return 0; + case 'd': /* Debug flag */ + if (invert_this) { + debug_level--; + } else { + debug_level++; + } + return 0; + case 'e': /* Environment override flag */ + if (invert_this) { + env_wins = false; + } else { + env_wins = true; + } + return 0; + case 'f': /* Read alternative makefile(s) */ + return 1; + case 'g': /* Use alternative DMake group */ + if (invert_this) { + dmake_group_specified = false; + } else { + dmake_group_specified = true; + } + return 4; + case 'i': /* Ignore errors */ + if (invert_this) { + ignore_errors_all = false; + } else { + ignore_errors_all = true; + } + return 0; + case 'j': /* Use alternative DMake max jobs */ + if (invert_this) { + dmake_max_jobs_specified = false; + } else { + dmake_mode_type = parallel_mode; + no_parallel = false; + dmake_max_jobs_specified = true; + } + return 8; + case 'K': /* Read alternative .make.state */ + return 256; + case 'k': /* Keep making even after errors */ + if (invert_this) { + continue_after_error = false; + } else { + continue_after_error = true; + continue_after_error_ever_seen = true; + } + return 0; + case 'M': /* Read alternative make.machines file */ + if (invert_this) { + pmake_machinesfile_specified = false; + } else { + pmake_machinesfile_specified = true; + dmake_mode_type = parallel_mode; + no_parallel = false; + } + return 16; + case 'm': /* Use alternative DMake build mode */ + if (invert_this) { + dmake_mode_specified = false; + } else { + dmake_mode_specified = true; + } + return 32; + case 'x': /* Use alternative DMake mode */ + if (invert_this) { + dmake_add_mode_specified = false; + } else { + dmake_add_mode_specified = true; + } + return 1024; + case 'N': /* Reverse -n */ + if (invert_this) { + do_not_exec_rule = true; + } else { + do_not_exec_rule = false; + } + return 0; + case 'n': /* Print, not exec commands */ + if (invert_this) { + do_not_exec_rule = false; + } else { + do_not_exec_rule = true; + } + return 0; + case 'O': /* Integrate with maketool, obsolete */ + return 0; + case 'o': /* Use alternative dmake output dir */ + if (invert_this) { + dmake_odir_specified = false; + } else { + dmake_odir_specified = true; + } + return 512; + case 'P': /* Print for selected targets */ + if (invert_this) { + report_dependencies_level--; + } else { + report_dependencies_level++; + } + return 0; + case 'p': /* Print description */ + if (invert_this) { + trace_status = false; + do_not_exec_rule = false; + } else { + trace_status = true; + do_not_exec_rule = true; + } + return 0; + case 'q': /* Question flag */ + if (invert_this) { + quest = false; + } else { + quest = true; + } + return 0; + case 'R': /* Don't run in parallel */ + if (invert_this) { + pmake_cap_r_specified = false; + no_parallel = false; + } else { + pmake_cap_r_specified = true; + dmake_mode_type = serial_mode; + no_parallel = true; + } + return 0; + case 'r': /* Turn off internal rules */ + if (invert_this) { + ignore_default_mk = false; + } else { + ignore_default_mk = true; + } + return 0; + case 'S': /* Reverse -k */ + if (invert_this) { + continue_after_error = true; + } else { + continue_after_error = false; + stop_after_error_ever_seen = true; + } + return 0; + case 's': /* Silent flag */ + if (invert_this) { + silent_all = false; + } else { + silent_all = true; + } + return 0; + case 'T': /* Print target list */ + if (invert_this) { + list_all_targets = false; + do_not_exec_rule = false; + } else { + list_all_targets = true; + do_not_exec_rule = true; + } + return 0; + case 't': /* Touch flag */ + if (invert_this) { + touch = false; + } else { + touch = true; + } + return 0; + case 'u': /* Unconditional flag */ + if (invert_this) { + build_unconditional = false; + } else { + build_unconditional = true; + } + return 0; + case 'V': /* SVR4 mode */ + svr4 = true; + return 0; + case 'v': /* Version flag */ + if (invert_this) { + } else { + fprintf(stdout, "%s: %s\n", getprogname(), verstring); + exit_status = 0; + exit(0); + } + return 0; + case 'w': /* Unconditional flag */ + if (invert_this) { + report_cwd = false; + } else { + report_cwd = true; + } + return 0; +#if 0 + case 'X': /* Filter stdout */ + if (invert_this) { + filter_stderr = false; + } else { + filter_stderr = true; + } + return 0; +#endif + default: + break; + } + return 0; +} + +/* + * setup_for_projectdir() + * + * Read the PROJECTDIR variable, if defined, and set the sccs path + * + * Parameters: + * + * Global variables used: + * sccs_dir_path Set to point to SCCS dir to use + */ +static void +setup_for_projectdir(void) +{ +static char path[MAXPATHLEN]; +char cwdpath[MAXPATHLEN]; +uid_t uid; +int done=0; + + /* Check if we should use PROJECTDIR when reading the SCCS dir. */ + sccs_dir_path = getenv("PROJECTDIR"); + if ((sccs_dir_path != NULL) && + (sccs_dir_path[0] != (int) slash_char)) { + struct passwd *pwent; + + { + uid = getuid(); + pwent = getpwuid(uid); + if (pwent == NULL) { + fatal(gettext("Bogus USERID ")); + } + if ((pwent = getpwnam(sccs_dir_path)) == NULL) { + /*empty block : it'll go & check cwd */ + } + else { + (void) sprintf(path, "%s/src", pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + (void) sprintf(path, "%s/source", pwent->pw_dir); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } + } + } + if (!done) { + if (getcwd(cwdpath, MAXPATHLEN - 1 )) { + + (void) sprintf(path, "%s/%s", cwdpath,sccs_dir_path); + if (access(path, F_OK) == 0) { + sccs_dir_path = path; + done = 1; + } else { + fatal(gettext("Bogus PROJECTDIR '%s'"), sccs_dir_path); + } + } + } + } + } +} + +char * +make_install_prefix(void) +{ + int ret; + char origin[PATH_MAX]; + char *dir; + + if ((ret = readlink("/proc/self/path/a.out", origin, + PATH_MAX - 1)) < 0) + fatal("failed to read origin from /proc\n"); + + + origin[ret] = '\0'; + return strdup(dirname(origin)); +} + +static char * +add_to_env(const char *var, const char *value, const char *fallback) +{ + const char *oldpath; + char *newpath; + + oldpath = getenv(var); + if (oldpath == NULL) { + if (value != NULL) { + asprintf(&newpath, "%s=%s", + var, value); + } else { + asprintf(&newpath, "%s=%s", + var, fallback); + } + } else { + if (value != NULL) { + asprintf(&newpath, "%s=%s:%s", + var, oldpath, value); + } else { + asprintf(&newpath, "%s=%s:%s", + var, oldpath, fallback); + } + } + + return (newpath); +} + +/* + * set_sgs_support() + * + * Add the libmakestate.so.1 lib to the env var SGS_SUPPORT + * if it's not already in there. + * The SGS_SUPPORT env var and libmakestate.so.1 is used by + * the linker ld to report .make.state info back to make. + * + * In the new world we always will set the 32-bit and 64-bit versions of this + * variable explicitly so that we can take into account the correct isa and our + * prefix. So say that the prefix was /opt/local. Then we would want to search + * /opt/local/lib/libmakestate.so.1:libmakestate.so.1. We still want to search + * the original location just as a safety measure. + */ +static void +set_sgs_support() +{ + int len; + char *newpath, *newpath64; + char *lib32, *lib64; + static char *prev_path, *prev_path64; + char *origin = make_install_prefix(); + struct stat st; + + asprintf(&lib32, "%s/%s/%s", origin, "../lib", + LD_SUPPORT_MAKE_LIB); + + if (stat(lib32, &st) != 0) { + free(lib32); + // Try the tools path + asprintf(&lib32, "%s/%s/%s/%s", origin, "../../lib/", + LD_SUPPORT_MAKE_ARCH, LD_SUPPORT_MAKE_LIB); + + if (stat(lib32, &st) != 0) { + free(lib32); + lib32 = NULL; + } + } + + asprintf(&lib64, "%s/%s/64/%s", origin, "../lib", + LD_SUPPORT_MAKE_LIB); + + if (stat(lib64, &st) != 0) { + free(lib64); + // Try the tools path + asprintf(&lib64, "%s/%s/%s/64/%s", origin, "../../lib/", + LD_SUPPORT_MAKE_ARCH, LD_SUPPORT_MAKE_LIB); + + if (stat(lib64, &st) != 0) { + free(lib64); + lib64 = NULL; + } + } + + newpath = add_to_env(LD_SUPPORT_ENV_VAR_32, lib32, LD_SUPPORT_MAKE_LIB); + newpath64 = add_to_env(LD_SUPPORT_ENV_VAR_64, lib64, LD_SUPPORT_MAKE_LIB); + + putenv(newpath); + if (prev_path) { + free(prev_path); + } + prev_path = newpath; + + putenv(newpath64); + if (prev_path64) { + free(prev_path64); + } + prev_path64 = newpath64; + free(lib32); + free(lib64); + free(origin); +} + +/* + * read_files_and_state(argc, argv) + * + * Read the makefiles we care about and the environment + * Also read the = style command line options + * + * Parameters: + * argc You know what this is + * argv You know what this is + * + * Static variables used: + * env_wins make -e, determines if env vars are RO + * ignore_default_mk make -r, determines if make.rules is read + * not_auto_depen dwight + * + * Global variables used: + * default_target_to_build Set to first proper target from file + * do_not_exec_rule Set to false when makfile is made + * dot The Name ".", used to read current dir + * empty_name The Name "", use as macro value + * keep_state Set if KEEP_STATE is in environment + * make_state The Name ".make.state", used to read file + * makefile_type Set to type of file being read + * makeflags The Name "MAKEFLAGS", used to set macro value + * not_auto dwight + * read_trace_level Checked to se if the reader should trace + * report_dependencies If -P is on we do not read .make.state + * trace_reader Set if reader should trace + * virtual_root The Name "VIRTUAL_ROOT", used to check value + */ +static void +read_files_and_state(int argc, char **argv) +{ + wchar_t buffer[1000]; + wchar_t buffer_posix[1000]; + register char ch; + register char *cp; + Property def_make_macro = NULL; + Name def_make_name; + Name default_makefile; + String_rec dest; + wchar_t destbuffer[STRING_BUFFER_LENGTH]; + register int i; + register int j; + Name keep_state_name; + int length; + Name Makefile; + register Property macro; + struct stat make_state_stat; + Name makefile_name; + register int makefile_next = 0; + register Boolean makefile_read = false; + String_rec makeflags_string; + String_rec makeflags_string_posix; + String_rec * makeflags_string_current; + Name makeflags_value_saved; + register Name name; + Name new_make_value; + Boolean save_do_not_exec_rule; + Name sdotMakefile; + Name sdotmakefile_name; + static wchar_t state_file_str; + static char state_file_str_mb[MAXPATHLEN]; + static struct _Name state_filename; + Boolean temp; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + ASCII_Dyn_Array makeflags_and_macro; + Boolean is_xpg4; + +/* + * Remember current mode. It may be changed after reading makefile + * and we will have to correct MAKEFLAGS variable. + */ + is_xpg4 = posix; + + MBSTOWCS(wcs_buffer, "KEEP_STATE"); + keep_state_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "Makefile"); + Makefile = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "makefile"); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "s.makefile"); + sdotmakefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "s.Makefile"); + sdotMakefile = GETNAME(wcs_buffer, FIND_LENGTH); + +/* + * initialize global dependency entry for .NOT_AUTO + */ + not_auto_depen->next = NULL; + not_auto_depen->name = not_auto; + not_auto_depen->automatic = not_auto_depen->stale = false; + +/* + * Read internal definitions and rules. + */ + if (read_trace_level > 1) { + trace_reader = true; + } + if (!ignore_default_mk) { + if (svr4) { + MBSTOWCS(wcs_buffer, "svr4.make.rules"); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } else { + MBSTOWCS(wcs_buffer, "make.rules"); + default_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + } + default_makefile->stat.is_file = true; + + (void) read_makefile(default_makefile, + true, + false, + true); + } + + /* + * If the user did not redefine the MAKE macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of MAKE to be some form + * of argv[0] for recursive MAKE builds. + */ + MBSTOWCS(wcs_buffer, "MAKE"); + def_make_name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + def_make_macro = get_prop(def_make_name->prop, macro_prop); + if ((def_make_macro != NULL) && + (IS_EQUAL(def_make_macro->body.macro.value->string_mb, + "make"))) { + MBSTOWCS(wcs_buffer, argv_zero_string); + new_make_value = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + (void) SETVAR(def_make_name, + new_make_value, + false); + } + + default_target_to_build = NULL; + trace_reader = false; + +/* + * Read environment args. Let file args which follow override unless + * -e option seen. If -e option is not mentioned. + */ + read_environment(env_wins); + if (getvar(virtual_root)->hash.length == 0) { + maybe_append_prop(virtual_root, macro_prop) + ->body.macro.exported = true; + MBSTOWCS(wcs_buffer, "/"); + (void) SETVAR(virtual_root, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + +/* + * We now scan mf_argv and argv to see if we need to set + * any of the DMake-added options/variables in MAKEFLAGS. + */ + + makeflags_and_macro.start = 0; + makeflags_and_macro.size = 0; + enter_argv_values(mf_argc, mf_argv, &makeflags_and_macro); + enter_argv_values(argc, argv, &makeflags_and_macro); + +/* + * Set MFLAGS and MAKEFLAGS + * + * Before reading makefile we do not know exactly which mode + * (posix or not) is used. So prepare two MAKEFLAGS strings + * for both posix and solaris modes because they are different. + */ + INIT_STRING_FROM_STACK(makeflags_string, buffer); + INIT_STRING_FROM_STACK(makeflags_string_posix, buffer_posix); + append_char((int) hyphen_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string_posix); + + switch (read_trace_level) { + case 2: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + case 1: + append_char('D', &makeflags_string); + append_char('D', &makeflags_string_posix); + } + switch (debug_level) { + case 2: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + case 1: + append_char('d', &makeflags_string); + append_char('d', &makeflags_string_posix); + } + if (env_wins) { + append_char('e', &makeflags_string); + append_char('e', &makeflags_string_posix); + } + if (ignore_errors_all) { + append_char('i', &makeflags_string); + append_char('i', &makeflags_string_posix); + } + if (continue_after_error) { + if (stop_after_error_ever_seen) { + append_char('S', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + } + append_char('k', &makeflags_string); + append_char('k', &makeflags_string_posix); + } else { + if (stop_after_error_ever_seen + && continue_after_error_ever_seen) { + append_char('k', &makeflags_string_posix); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('S', &makeflags_string_posix); + } + } + if (do_not_exec_rule) { + append_char('n', &makeflags_string); + append_char('n', &makeflags_string_posix); + } + switch (report_dependencies_level) { + case 4: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 3: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 2: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + case 1: + append_char('P', &makeflags_string); + append_char('P', &makeflags_string_posix); + } + if (trace_status) { + append_char('p', &makeflags_string); + append_char('p', &makeflags_string_posix); + } + if (quest) { + append_char('q', &makeflags_string); + append_char('q', &makeflags_string_posix); + } + if (silent_all) { + append_char('s', &makeflags_string); + append_char('s', &makeflags_string_posix); + } + if (touch) { + append_char('t', &makeflags_string); + append_char('t', &makeflags_string_posix); + } + if (build_unconditional) { + append_char('u', &makeflags_string); + append_char('u', &makeflags_string_posix); + } + if (report_cwd) { + append_char('w', &makeflags_string); + append_char('w', &makeflags_string_posix); + } + /* -c dmake_rcfile */ + if (dmake_rcfile_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_RCFILE"); + dmake_rcfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_rcfile, &makeflags_string); + append_makeflags_string(dmake_rcfile, &makeflags_string_posix); + } + /* -g dmake_group */ + if (dmake_group_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_GROUP"); + dmake_group = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_group, &makeflags_string); + append_makeflags_string(dmake_group, &makeflags_string_posix); + } + /* -j dmake_max_jobs */ + if (dmake_max_jobs_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + dmake_max_jobs = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_max_jobs, &makeflags_string); + append_makeflags_string(dmake_max_jobs, &makeflags_string_posix); + } + /* -m dmake_mode */ + if (dmake_mode_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + dmake_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_mode, &makeflags_string); + append_makeflags_string(dmake_mode, &makeflags_string_posix); + } + /* -x dmake_compat_mode */ +// if (dmake_compat_mode_specified) { +// MBSTOWCS(wcs_buffer, "SUN_MAKE_COMPAT_MODE"); +// dmake_compat_mode = GETNAME(wcs_buffer, FIND_LENGTH); +// append_makeflags_string(dmake_compat_mode, &makeflags_string); +// append_makeflags_string(dmake_compat_mode, &makeflags_string_posix); +// } + /* -x dmake_output_mode */ + if (dmake_output_mode_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + dmake_output_mode = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_output_mode, &makeflags_string); + append_makeflags_string(dmake_output_mode, &makeflags_string_posix); + } + /* -o dmake_odir */ + if (dmake_odir_specified) { + MBSTOWCS(wcs_buffer, "DMAKE_ODIR"); + dmake_odir = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(dmake_odir, &makeflags_string); + append_makeflags_string(dmake_odir, &makeflags_string_posix); + } + /* -M pmake_machinesfile */ + if (pmake_machinesfile_specified) { + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + pmake_machinesfile = GETNAME(wcs_buffer, FIND_LENGTH); + append_makeflags_string(pmake_machinesfile, &makeflags_string); + append_makeflags_string(pmake_machinesfile, &makeflags_string_posix); + } + /* -R */ + if (pmake_cap_r_specified) { + append_char((int) space_char, &makeflags_string); + append_char((int) hyphen_char, &makeflags_string); + append_char('R', &makeflags_string); + append_char((int) space_char, &makeflags_string_posix); + append_char((int) hyphen_char, &makeflags_string_posix); + append_char('R', &makeflags_string_posix); + } + +/* + * Make sure MAKEFLAGS is exported + */ + maybe_append_prop(makeflags, macro_prop)-> + body.macro.exported = true; + + if (makeflags_string.buffer.start[1] != (int) nul_char) { + if (makeflags_string.buffer.start[1] != (int) space_char) { + MBSTOWCS(wcs_buffer, "MFLAGS"); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start, + FIND_LENGTH), + false); + } else { + MBSTOWCS(wcs_buffer, "MFLAGS"); + (void) SETVAR(GETNAME(wcs_buffer, FIND_LENGTH), + GETNAME(makeflags_string.buffer.start + 2, + FIND_LENGTH), + false); + } + } + +/* + * Add command line macro to POSIX makeflags_string + */ + if (makeflags_and_macro.start) { + tmp_char = (char) space_char; + cp = makeflags_and_macro.start; + do { + append_char(tmp_char, &makeflags_string_posix); + } while ( tmp_char = *cp++ ); + retmem_mb(makeflags_and_macro.start); + } + +/* + * Now set the value of MAKEFLAGS macro in accordance + * with current mode. + */ + macro = maybe_append_prop(makeflags, macro_prop); + temp = (Boolean) macro->body.macro.read_only; + macro->body.macro.read_only = false; + if(posix || gnu_style) { + makeflags_string_current = &makeflags_string_posix; + } else { + makeflags_string_current = &makeflags_string; + } + if (makeflags_string_current->buffer.start[1] == (int) nul_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 1 + , FIND_LENGTH + ); + } else { + if (makeflags_string_current->buffer.start[1] != (int) space_char) { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + , FIND_LENGTH + ); + } else { + makeflags_value_saved = + GETNAME( makeflags_string_current->buffer.start + 2 + , FIND_LENGTH + ); + } + } + (void) SETVAR( makeflags + , makeflags_value_saved + , false + ); + macro->body.macro.read_only = temp; + +/* + * Read command line "-f" arguments and ignore -c, g, j, K, M, m, O and o args. + */ + save_do_not_exec_rule = do_not_exec_rule; + do_not_exec_rule = false; + if (read_trace_level > 0) { + trace_reader = true; + } + + for (i = 1; i < argc; i++) { + if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'f') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; /* Remove -f */ + if (i >= argc - 1) { + fatal(gettext("No filename argument after -f flag")); + } + MBSTOWCS(wcs_buffer, argv[++i]); + primary_makefile = GETNAME(wcs_buffer, FIND_LENGTH); + (void) read_makefile(primary_makefile, true, true, true); + argv[i] = NULL; /* Remove filename */ + makefile_read = true; + } else if (argv[i] && + (argv[i][0] == (int) hyphen_char) && + (argv[i][1] == 'c' || + argv[i][1] == 'g' || + argv[i][1] == 'j' || + argv[i][1] == 'K' || + argv[i][1] == 'M' || + argv[i][1] == 'm' || + argv[i][1] == 'O' || + argv[i][1] == 'o') && + (argv[i][2] == (int) nul_char)) { + argv[i] = NULL; + argv[++i] = NULL; + } + } + +/* + * If no command line "-f" args then look for "makefile", and then for + * "Makefile" if "makefile" isn't found. + */ + if (!makefile_read) { + (void) read_dir(dot, + (wchar_t *) NULL, + (Property) NULL, + (wchar_t *) NULL); + if (!posix) { + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(gettext("Both `makefile' and `Makefile' exist")); + } + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } else { + + enum sccs_stat save_m_has_sccs = NO_SCCS; + enum sccs_stat save_M_has_sccs = NO_SCCS; + + if (makefile_name->stat.is_file) { + if (Makefile->stat.is_file) { + warning(gettext("Both `makefile' and `Makefile' exist")); + } + } + if (makefile_name->stat.is_file) { + if (makefile_name->stat.has_sccs == NO_SCCS) { + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } else { + save_m_has_sccs = makefile_name->stat.has_sccs; + makefile_name->stat.has_sccs = NO_SCCS; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + } + if (!makefile_read && + Makefile->stat.is_file) { + if (Makefile->stat.has_sccs == NO_SCCS) { + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } else { + save_M_has_sccs = Makefile->stat.has_sccs; + Makefile->stat.has_sccs = NO_SCCS; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + if (!makefile_read && + makefile_name->stat.is_file) { + makefile_name->stat.has_sccs = save_m_has_sccs; + primary_makefile = makefile_name; + makefile_read = read_makefile(makefile_name, + false, + false, + true); + } + if (!makefile_read && + Makefile->stat.is_file) { + Makefile->stat.has_sccs = save_M_has_sccs; + primary_makefile = Makefile; + makefile_read = read_makefile(Makefile, + false, + false, + true); + } + } + } + do_not_exec_rule = save_do_not_exec_rule; + allrules_read = makefile_read; + trace_reader = false; + +/* + * Now get current value of MAKEFLAGS and compare it with + * the saved value we set before reading makefile. + * If they are different then MAKEFLAGS is subsequently set by + * makefile, just leave it there. Otherwise, if make mode + * is changed by using .POSIX target in makefile we need + * to correct MAKEFLAGS value. + */ + Name mf_val = getvar(makeflags); + if( (posix != is_xpg4) + && (!strcmp(mf_val->string_mb, makeflags_value_saved->string_mb))) + { + if (makeflags_string_posix.buffer.start[1] == (int) nul_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 1, + FIND_LENGTH), + false); + } else { + if (makeflags_string_posix.buffer.start[1] != (int) space_char) { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start, + FIND_LENGTH), + false); + } else { + (void) SETVAR(makeflags, + GETNAME(makeflags_string_posix.buffer.start + 2, + FIND_LENGTH), + false); + } + } + } + + if (makeflags_string.free_after_use) { + retmem(makeflags_string.buffer.start); + } + if (makeflags_string_posix.free_after_use) { + retmem(makeflags_string_posix.buffer.start); + } + makeflags_string.buffer.start = NULL; + makeflags_string_posix.buffer.start = NULL; + + if (posix) { + /* + * If the user did not redefine the ARFLAGS macro in the + * default makefile (make.rules), then we'd like to + * change the macro value of ARFLAGS to be in accordance + * with "POSIX" requirements. + */ + MBSTOWCS(wcs_buffer, "ARFLAGS"); + name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + macro = get_prop(name->prop, macro_prop); + if ((macro != NULL) && /* Maybe (macro == NULL) || ? */ + (IS_EQUAL(macro->body.macro.value->string_mb, + "rv"))) { + MBSTOWCS(wcs_buffer, "-rv"); + value = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + (void) SETVAR(name, + value, + false); + } + } + + if (!posix && !svr4) { + set_sgs_support(); + } + + +/* + * Make sure KEEP_STATE is in the environment if KEEP_STATE is on. + */ + macro = get_prop(keep_state_name->prop, macro_prop); + if ((macro != NULL) && + macro->body.macro.exported) { + keep_state = true; + } + if (keep_state) { + if (macro == NULL) { + macro = maybe_append_prop(keep_state_name, + macro_prop); + } + macro->body.macro.exported = true; + (void) SETVAR(keep_state_name, + empty_name, + false); + + /* + * Read state file + */ + + /* Before we read state, let's make sure we have + ** right state file. + */ + /* just in case macro references are used in make_state file + ** name, we better expand them at this stage using expand_value. + */ + INIT_STRING_FROM_STACK(dest, destbuffer); + expand_value(make_state, &dest, false); + + make_state = GETNAME(dest.buffer.start, FIND_LENGTH); + + if(!stat(make_state->string_mb, &make_state_stat)) { + if(!(make_state_stat.st_mode & S_IFREG) ) { + /* copy the make_state structure to the other + ** and then let make_state point to the new + ** one. + */ + memcpy(&state_filename, make_state,sizeof(state_filename)); + state_filename.string_mb = state_file_str_mb; + /* Just a kludge to avoid two slashes back to back */ + if((make_state->hash.length == 1)&& + (make_state->string_mb[0] == '/')) { + make_state->hash.length = 0; + make_state->string_mb[0] = '\0'; + } + sprintf(state_file_str_mb,"%s%s", + make_state->string_mb,"/.make.state"); + make_state = &state_filename; + /* adjust the length to reflect the appended string */ + make_state->hash.length += 12; + } + } else { /* the file doesn't exist or no permission */ + char tmp_path[MAXPATHLEN]; + char *slashp; + + if (slashp = strrchr(make_state->string_mb, '/')) { + strncpy(tmp_path, make_state->string_mb, + (slashp - make_state->string_mb)); + tmp_path[slashp - make_state->string_mb]=0; + if(strlen(tmp_path)) { + if(stat(tmp_path, &make_state_stat)) { + warning(gettext("directory %s for .KEEP_STATE_FILE does not exist"),tmp_path); + } + if (access(tmp_path, F_OK) != 0) { + warning(gettext("can't access dir %s"),tmp_path); + } + } + } + } + if (report_dependencies_level != 1) { + Makefile_type makefile_type_temp = makefile_type; + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + makefile_type = makefile_type_temp; + } + } +} + +/* + * Scan the argv for options and "=" type args and make them readonly. + */ +static void +enter_argv_values(int argc, char *argv[], ASCII_Dyn_Array *makeflags_and_macro) +{ + register char *cp; + register int i; + int length; + register Name name; + int opt_separator = argc; + char tmp_char; + wchar_t *tmp_wcs_buffer; + register Name value; + Boolean append = false; + Property macro; + struct stat statbuf; + + + /* Read argv options and "=" type args and make them readonly. */ + makefile_type = reading_nothing; + for (i = 1; i < argc; ++i) { + append = false; + if (argv[i] == NULL) { + continue; + } else if (((argv[i][0] == '-') && (argv[i][1] == '-')) || + ((argv[i][0] == (int) ' ') && + (argv[i][1] == (int) '-') && + (argv[i][2] == (int) ' ') && + (argv[i][3] == (int) '-'))) { + argv[i] = NULL; + opt_separator = i; + continue; + } else if ((i < opt_separator) && (argv[i][0] == (int) hyphen_char)) { + switch (parse_command_option(argv[i][1])) { + case 1: /* -f seen */ + ++i; + continue; + case 2: /* -c seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake rcfile argument after -c flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_RCFILE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 4: /* -g seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake group argument after -g flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_GROUP"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 8: /* -j seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake max jobs argument after -j flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 16: /* -M seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No pmake machinesfile argument after -M flag")); + } + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 32: /* -m seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake mode argument after -m flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 256: /* -K seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No makestate filename argument after -K flag")); + } + MBSTOWCS(wcs_buffer, argv[i+1]); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + keep_state = true; + argv[i] = NULL; + argv[i+1] = NULL; + continue; + case 512: /* -o seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No dmake output dir argument after -o flag")); + } + MBSTOWCS(wcs_buffer, "DMAKE_ODIR"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + break; + case 1024: /* -x seen */ + if (argv[i+1] == NULL) { + fatal(gettext("No argument after -x flag")); + } + length = strlen( "SUN_MAKE_COMPAT_MODE="); + if (strncmp(argv[i+1], "SUN_MAKE_COMPAT_MODE=", length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, "SUN_MAKE_COMPAT_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_compat_mode_specified = dmake_add_mode_specified; + break; + } + length = strlen( "DMAKE_OUTPUT_MODE="); + if (strncmp(argv[i+1], "DMAKE_OUTPUT_MODE=", length) == 0) { + argv[i+1] = &argv[i+1][length]; + MBSTOWCS(wcs_buffer, "DMAKE_OUTPUT_MODE"); + name = GETNAME(wcs_buffer, FIND_LENGTH); + dmake_output_mode_specified = dmake_add_mode_specified; + } else { + warning(gettext("Unknown argument `%s' after -x flag (ignored)"), + argv[i+1]); + argv[i] = argv[i + 1] = NULL; + continue; + } + break; + default: /* Shouldn't reach here */ + argv[i] = NULL; + continue; + } + argv[i] = NULL; + if (i == (argc - 1)) { + break; + } + if ((length = strlen(argv[i+1])) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, argv[i+1], length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, argv[i+1]); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i+1] = NULL; + } else if ((cp = strchr(argv[i], (int) equal_char)) != NULL) { +/* + * Combine all macro in dynamic array + */ + if(*(cp-1) == (int) plus_char) + { + if(isspace(*(cp-2))) { + append = true; + cp--; + } + } + if(!append) + append_or_replace_macro_in_dyn_array(makeflags_and_macro, argv[i]); + + while (isspace(*(cp-1))) { + cp--; + } + tmp_char = *cp; + *cp = (int) nul_char; + MBSTOWCS(wcs_buffer, argv[i]); + *cp = tmp_char; + name = GETNAME(wcs_buffer, wcslen(wcs_buffer)); + while (*cp != (int) equal_char) { + cp++; + } + cp++; + while (isspace(*cp) && (*cp != (int) nul_char)) { + cp++; + } + if ((length = strlen(cp)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, cp, length + 1); + value = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, cp); + value = GETNAME(wcs_buffer, FIND_LENGTH); + } + argv[i] = NULL; + } else { + /* Illegal MAKEFLAGS argument */ + continue; + } + if(append) { + setvar_append(name, value); + append = false; + } else { + macro = maybe_append_prop(name, macro_prop); + macro->body.macro.exported = true; + SETVAR(name, value, false)->body.macro.read_only = true; + } + } +} + +/* + * Append the DMake option and value to the MAKEFLAGS string. + */ +static void +append_makeflags_string(Name name, register String makeflags_string) +{ + const char *option; + + if (strcmp(name->string_mb, "DMAKE_GROUP") == 0) { + option = " -g "; + } else if (strcmp(name->string_mb, "DMAKE_MAX_JOBS") == 0) { + option = " -j "; + } else if (strcmp(name->string_mb, "DMAKE_MODE") == 0) { + option = " -m "; + } else if (strcmp(name->string_mb, "DMAKE_ODIR") == 0) { + option = " -o "; + } else if (strcmp(name->string_mb, "DMAKE_RCFILE") == 0) { + option = " -c "; + } else if (strcmp(name->string_mb, "PMAKE_MACHINESFILE") == 0) { + option = " -M "; + } else if (strcmp(name->string_mb, "DMAKE_OUTPUT_MODE") == 0) { + option = " -x DMAKE_OUTPUT_MODE="; + } else if (strcmp(name->string_mb, "SUN_MAKE_COMPAT_MODE") == 0) { + option = " -x SUN_MAKE_COMPAT_MODE="; + } else { + fatal(gettext("Internal error: name not recognized in append_makeflags_string()")); + } + Property prop = maybe_append_prop(name, macro_prop); + if( prop == 0 || prop->body.macro.value == 0 || + prop->body.macro.value->string_mb == 0 ) { + return; + } + char mbs_value[MAXPATHLEN + 100]; + strcpy(mbs_value, option); + strcat(mbs_value, prop->body.macro.value->string_mb); + MBSTOWCS(wcs_buffer, mbs_value); + append_string(wcs_buffer, makeflags_string, FIND_LENGTH); +} + +/* + * read_environment(read_only) + * + * This routine reads the process environment when make starts and enters + * it as make macros. The environment variable SHELL is ignored. + * + * Parameters: + * read_only Should we make env vars read only? + * + * Global variables used: + * report_pwd Set if this make was started by other make + */ +static void +read_environment(Boolean read_only) +{ + register char **environment; + int length; + wchar_t *tmp_wcs_buffer; + Boolean alloced_tmp_wcs_buffer = false; + register wchar_t *name; + register wchar_t *value; + register Name macro; + Property val; + Boolean read_only_saved; + + reading_environment = true; + environment = environ; + for (; *environment; environment++) { + read_only_saved = read_only; + if ((length = strlen(*environment)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + alloced_tmp_wcs_buffer = true; + (void) mbstowcs(tmp_wcs_buffer, *environment, length + 1); + name = tmp_wcs_buffer; + } else { + MBSTOWCS(wcs_buffer, *environment); + name = wcs_buffer; + } + value = (wchar_t *) wcschr(name, (int) equal_char); + + /* + * Looks like there's a bug in the system, but sometimes + * you can get blank lines in *environment. + */ + if (!value) { + continue; + } + MBSTOWCS(wcs_buffer2, "SHELL="); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + continue; + } + MBSTOWCS(wcs_buffer2, "MAKEFLAGS="); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + report_pwd = true; + /* + * In POSIX mode we do not want MAKEFLAGS to be readonly. + * If the MAKEFLAGS macro is subsequently set by the makefile, + * it replaces the MAKEFLAGS variable currently found in the + * environment. + * See Assertion 50 in section 6.2.5.3 of standard P1003.3.2/D8. + */ + if(posix) { + read_only_saved = false; + } + } + + /* + * We ignore SUNPRO_DEPENDENCIES. This environment variable is + * set by make and read by cpp which then writes info to + * .make.dependency.xxx. When make is invoked by another make + * (recursive make), we don't want to read this because then + * the child make will end up writing to the parent + * directory's .make.state and clobbering them. + */ + MBSTOWCS(wcs_buffer2, "SUNPRO_DEPENDENCIES"); + if (IS_WEQUALN(name, wcs_buffer2, wcslen(wcs_buffer2))) { + continue; + } + + macro = GETNAME(name, value - name); + maybe_append_prop(macro, macro_prop)->body.macro.exported = + true; + if ((value == NULL) || ((value + 1)[0] == (int) nul_char)) { + val = setvar_daemon(macro, + (Name) NULL, + false, no_daemon, false, debug_level); + } else { + val = setvar_daemon(macro, + GETNAME(value + 1, FIND_LENGTH), + false, no_daemon, false, debug_level); + } + val->body.macro.read_only = read_only_saved; + if (alloced_tmp_wcs_buffer) { + retmem(tmp_wcs_buffer); + alloced_tmp_wcs_buffer = false; + } + } + reading_environment = false; +} + +/* + * read_makefile(makefile, complain, must_exist, report_file) + * + * Read one makefile and check the result + * + * Return value: + * false is the read failed + * + * Parameters: + * makefile The file to read + * complain Passed thru to read_simple_file() + * must_exist Passed thru to read_simple_file() + * report_file Passed thru to read_simple_file() + * + * Global variables used: + * makefile_type Set to indicate we are reading main file + * recursion_level Initialized + */ +static Boolean +read_makefile(register Name makefile, Boolean complain, Boolean must_exist, Boolean report_file) +{ + Boolean b; + + makefile_type = reading_makefile; + recursion_level = 0; + reading_dependencies = true; + b = read_simple_file(makefile, true, true, complain, + must_exist, report_file, false); + reading_dependencies = false; + return b; +} + +/* + * make_targets(argc, argv, parallel_flag) + * + * Call doname on the specified targets + * + * Parameters: + * argc You know what this is + * argv You know what this is + * parallel_flag True if building in parallel + * + * Global variables used: + * build_failed_seen Used to generated message after failed -k + * commands_done Used to generate message "Up to date" + * default_target_to_build First proper target in makefile + * init The Name ".INIT", use to run command + * parallel Global parallel building flag + * quest make -q, suppresses messages + * recursion_level Initialized, used for tracing + * report_dependencies make -P, regroves whole process + */ +static void +make_targets(int argc, char **argv, Boolean parallel_flag) +{ + int i; + char *cp; + Doname result; + register Boolean target_to_make_found = false; + + (void) doname(init, true, true); + recursion_level = 1; + parallel = parallel_flag; +/* + * make remaining args + */ +/* + if ((report_dependencies_level == 0) && parallel) { + */ + if (parallel) { + /* + * If building targets in parallel, start all of the + * remaining args to build in parallel. + */ + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + commands_done = false; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + //default_target_to_build = GETNAME(wcs_buffer, + // FIND_LENGTH); + default_target_to_build = normalize_name(wcs_buffer, + wcslen(wcs_buffer)); + if (default_target_to_build == wait_name) { + if (parallel_process_cnt > 0) { + finish_running(); + } + continue; + } + top_level_target = get_wstring(default_target_to_build->string_mb); + /* + * If we can't execute the current target in + * parallel, hold off the target processing + * to preserve the order of the targets as they appeared + * in command line. + */ + if (!parallel_ok(default_target_to_build, false) + && parallel_process_cnt > 0) { + finish_running(); + } + result = doname_check(default_target_to_build, + true, + false, + false); + gather_recursive_deps(); + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + default_target_to_build->stat.time = file_no_time; + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + /* Now wait for all of the targets to finish running */ + finish_running(); + // setjmp(jmpbuffer); + + } + for (i = 1; i < argc; i++) { + if ((cp = argv[i]) != NULL) { + target_to_make_found = true; + if ((cp[0] == (int) period_char) && + (cp[1] == (int) slash_char)) { + cp += 2; + } + if((cp[0] == (int) ' ') && + (cp[1] == (int) '-') && + (cp[2] == (int) ' ') && + (cp[3] == (int) '-')) { + argv[i] = NULL; + continue; + } + MBSTOWCS(wcs_buffer, cp); + default_target_to_build = normalize_name(wcs_buffer, wcslen(wcs_buffer)); + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + commands_done = false; + if (parallel) { + result = (Doname) default_target_to_build->state; + } else { + result = doname_check(default_target_to_build, + true, + false, + false); + } + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(gettext("Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build->prop, + line_prop)); + } + default_target_to_build->stat.time = + file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = + build_dont_know; + } + if (!parallel && + /* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } + } + +/* + * If no file arguments have been encountered, + * make the first name encountered that doesnt start with a dot + */ + if (!target_to_make_found) { + if (default_target_to_build == NULL) { + fatal(gettext("No arguments to build")); + } + commands_done = false; + top_level_target = get_wstring(default_target_to_build->string_mb); + report_recursion(default_target_to_build); + + + if (getenv("SPRO_EXPAND_ERRORS")){ + (void) printf("::(%s)\n", + default_target_to_build->string_mb); + } + + + result = doname_parallel(default_target_to_build, true, false); + gather_recursive_deps(); + if (build_failed_seen) { + build_failed_ever_seen = true; + warning(gettext("Target `%s' not remade because of errors"), + default_target_to_build->string_mb); + } + build_failed_seen = false; + if (report_dependencies_level > 0) { + print_dependencies(default_target_to_build, + get_prop(default_target_to_build-> + prop, + line_prop)); + } + default_target_to_build->stat.time = file_no_time; + if (default_target_to_build->colon_splits > 0) { + default_target_to_build->state = build_dont_know; + } + if (/* !commands_done && */ + (result == build_ok) && + !quest && + (report_dependencies_level == 0) /* && + (exists(default_target_to_build) > file_doesnt_exist) */) { + if (posix) { + if (!commands_done) { + (void) printf(gettext("`%s' is updated.\n"), + default_target_to_build->string_mb); + } else { + if (no_action_was_taken) { + (void) printf(gettext("`%s': no action was taken.\n"), + default_target_to_build->string_mb); + } + } + } else { + if (!commands_done && + (exists(default_target_to_build) > file_doesnt_exist)) { + (void) printf(gettext("`%s' is up to date.\n"), + default_target_to_build->string_mb); + } + } + } + } +} + +/* + * report_recursion(target) + * + * If this is a recursive make and the parent make has KEEP_STATE on + * this routine reports the dependency to the parent make + * + * Parameters: + * target Target to report + * + * Global variables used: + * makefiles_used List of makefiles read + * recursive_name The Name ".RECURSIVE", printed + * report_dependency dwight + */ +static void +report_recursion(register Name target) +{ + register FILE *report_file = get_report_file(); + + if ((report_file == NULL) || (report_file == (FILE*)-1)) { + return; + } + if (primary_makefile == NULL) { + /* + * This can happen when there is no makefile and + * only implicit rules are being used. + */ + return; + } + (void) fprintf(report_file, + "%s: %s ", + get_target_being_reported_for(), + recursive_name->string_mb); + report_dependency(get_current_path()); + report_dependency(target->string_mb); + report_dependency(primary_makefile->string_mb); + (void) fprintf(report_file, "\n"); +} + +/* Next function "append_or_replace_macro_in_dyn_array" must be in "misc.cc". */ +/* NIKMOL */ +extern void +append_or_replace_macro_in_dyn_array(ASCII_Dyn_Array *Ar, char *macro) +{ + register char *cp0; /* work pointer in macro */ + register char *cp1; /* work pointer in array */ + register char *cp2; /* work pointer in array */ + register char *cp3; /* work pointer in array */ + register char *name; /* macro name */ + register char *value; /* macro value */ + register int len_array; + register int len_macro; + + char * esc_value = NULL; + int esc_len; + + if (!(len_macro = strlen(macro))) return; + name = macro; + while (isspace(*(name))) { + name++; + } + if (!(value = strchr(name, (int) equal_char))) { + /* no '=' in macro */ + goto ERROR_MACRO; + } + cp0 = value; + value++; + while (isspace(*(value))) { + value++; + } + while (isspace(*(cp0-1))) { + cp0--; + } + if (cp0 <= name) goto ERROR_MACRO; /* no name */ + if (!(Ar->size)) goto ALLOC_ARRAY; + cp1 = Ar->start; + +LOOK_FOR_NAME: + if (!(cp1 = strchr(cp1, name[0]))) goto APPEND_MACRO; + if (!(cp2 = strchr(cp1, (int) equal_char))) goto APPEND_MACRO; + if (strncmp(cp1, name, (size_t)(cp0-name))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + if (cp1 != Ar->start) { + if (!isspace(*(cp1-1))) { + /* another name */ + cp1++; + goto LOOK_FOR_NAME; + } + } + for (cp3 = cp1 + (cp0-name); cp3 < cp2; cp3++) { + if (isspace(*cp3)) continue; + /* else: another name */ + cp1++; + goto LOOK_FOR_NAME; + } + /* Look for the next macro name in array */ + cp3 = cp2+1; + if (*cp3 != (int) doublequote_char) { + /* internal error */ + goto ERROR_MACRO; + } + if (!(cp3 = strchr(cp3+1, (int) doublequote_char))) { + /* internal error */ + goto ERROR_MACRO; + } + cp3++; + while (isspace(*cp3)) { + cp3++; + } + + cp2 = cp1; /* remove old macro */ + if ((*cp3) && (cp3 < Ar->start + Ar->size)) { + for (; cp3 < Ar->start + Ar->size; cp3++) { + *cp2++ = *cp3; + } + } + for (; cp2 < Ar->start + Ar->size; cp2++) { + *cp2 = 0; + } + if (*cp1) { + /* check next name */ + goto LOOK_FOR_NAME; + } + goto APPEND_MACRO; + +ALLOC_ARRAY: + if (Ar->size) { + cp1 = Ar->start; + } else { + cp1 = 0; + } + Ar->size += 128; + Ar->start = getmem(Ar->size); + for (len_array=0; len_array < Ar->size; len_array++) { + Ar->start[len_array] = 0; + } + if (cp1) { + strcpy(Ar->start, cp1); + retmem((wchar_t *) cp1); + } + +APPEND_MACRO: + len_array = strlen(Ar->start); + esc_value = (char*)malloc(strlen(value)*2 + 1); + quote_str(value, esc_value); + esc_len = strlen(esc_value) - strlen(value); + if (len_array + len_macro + esc_len + 5 >= Ar->size) goto ALLOC_ARRAY; + strcat(Ar->start, " "); + strncat(Ar->start, name, cp0-name); + strcat(Ar->start, "="); + strncat(Ar->start, esc_value, strlen(esc_value)); + free(esc_value); + return; +ERROR_MACRO: + /* Macro without '=' or with invalid left/right part */ + return; +} + +static void +report_dir_enter_leave(Boolean entering) +{ + char rcwd[MAXPATHLEN]; +static char * mlev = NULL; + char * make_level_str = NULL; + int make_level_val = 0; + + make_level_str = getenv("MAKELEVEL"); + if(make_level_str) { + make_level_val = atoi(make_level_str); + } + if(mlev == NULL) { + mlev = (char*) malloc(MAXPATHLEN); + } + if(entering) { + sprintf(mlev, "MAKELEVEL=%d", make_level_val + 1); + } else { + make_level_val--; + sprintf(mlev, "MAKELEVEL=%d", make_level_val); + } + putenv(mlev); + + if(report_cwd) { + if(make_level_val <= 0) { + if(entering) { + sprintf(rcwd, + gettext("%s: Entering directory `%s'\n"), + getprogname(), + get_current_path()); + } else { + sprintf(rcwd, + gettext("%s: Leaving directory `%s'\n"), + getprogname(), + get_current_path()); + } + } else { + if(entering) { + sprintf(rcwd, + gettext("%s[%d]: Entering directory `%s'\n"), + getprogname(), + make_level_val, get_current_path()); + } else { + sprintf(rcwd, + gettext("%s[%d]: Leaving directory `%s'\n"), + getprogname(), + make_level_val, get_current_path()); + } + } + printf("%s", rcwd); + } +} diff --git a/bin/make.rules.file b/bin/make.rules.file new file mode 100644 index 0000000..098a448 --- /dev/null +++ b/bin/make.rules.file @@ -0,0 +1,498 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +SUFFIXES = .o .c .c~ .cc .cc~ .y .y~ .l .l~ .s .s~ .sh .sh~ .S .S~ .i .ln \ + .h .h~ .f .f~ .for .for~ .F .F~ .f90 .f90~ .ftn .ftn~ .mod .mod~ \ + .sym .def .def~ .p .p~ .r .r~ .cps .cps~ .C .C~ .Y .Y~ .L .L~ \ + .java .java~ .class + +.SUFFIXES: $(SUFFIXES) + +# OUTPUT_OPTION should be defined to "-o $@" when +# the default rules are used for non-local files. +OUTPUT_OPTION= + +# C language section. +CC=cc +CFLAGS= +CPPFLAGS= +LINT=lint +LINTFLAGS= +COMPILE.c=$(CC) $(CFLAGS) $(CPPFLAGS) -c +LINK.c=$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) +LINT.c=$(LINT) $(LINTFLAGS) $(CPPFLAGS) +.c: + $(LINK.c) -o $@ $< $(LDLIBS) +.c~: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $*.c +.c.o: + $(COMPILE.c) $(OUTPUT_OPTION) $< +.c~.o: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) -c $*.c +.c.i: + $(CC) $(CFLAGS) $(CPPFLAGS) -P $< +.c~.i: + $(GET) $(GFLAGS) -p $< > $*.c + $(CC) $(CFLAGS) $(CPPFLAGS) -P $*.c +.c.ln: + $(LINT.c) $(OUTPUT_OPTION) -c $< +.c~.ln: + $(GET) $(GFLAGS) -p $< > $*.c + $(LINT.c) $(OUTPUT_OPTION) -c $*.c +.c.a: + $(COMPILE.c) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.c~.a: + $(GET) $(GFLAGS) -p $< > $*.c + $(COMPILE.c) -o $% $*.c + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# C language section. yacc. +YACC=yacc +YFLAGS= +YACC.y=$(YACC) $(YFLAGS) +.y: + $(YACC.y) $< + $(LINK.c) -o $@ y.tab.c $(LDLIBS) + $(RM) y.tab.c +.y~: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c + +.y.c: + $(YACC.y) $< + mv y.tab.c $@ +.y~.c: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $@ +.y.ln: + $(YACC.y) $< + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y~.ln: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC.y) $*.y + $(LINT.c) -o $@ -i y.tab.c + $(RM) y.tab.c +.y.o: + $(YACC.y) $< + $(COMPILE.c) -o $@ y.tab.c + $(RM) y.tab.c +.y~.o: + $(GET) $(GFLAGS) -p $< > $*.y + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + rm -f y.tab.c + mv y.tab.o $@ + +# C language section. lex. +LEX=lex +LFLAGS= +LEX.l=$(LEX) $(LFLAGS) -t +.l: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINK.c) -o $@ $*.c -ll $(LDLIBS) + $(RM) $*.c +.l~: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +.l.c : + $(RM) $@ + $(LEX.l) $< > $@ +.l~.c: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ +.l.ln: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l~.ln: + $(GET) $(GFLAGS) -p $< > $*.l + $(RM) $*.c + $(LEX.l) $*.l > $*.c + $(LINT.c) -o $@ -i $*.c + $(RM) $*.c +.l.o: + $(RM) $*.c + $(LEX.l) $< > $*.c + $(COMPILE.c) -o $@ $*.c + $(RM) $*.c +.l~.o: + $(GET) $(GFLAGS) -p $< > $*.l + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + rm -f lex.yy.c + mv lex.yy.c $@ + +# C++ language section. +CCC=CC +CCFLAGS= +COMPILE.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.cc=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +COMPILE.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) -c +LINK.C=$(CCC) $(CCFLAGS) $(CPPFLAGS) $(LDFLAGS) +.cc: + $(LINK.cc) -o $@ $< $(LDLIBS) +.cc~: + $(GET) $(GFLAGS) -p $< > $*.cc + $(LINK.cc) -o $@ $*.cc $(LDLIBS) +.cc.o: + $(COMPILE.cc) $(OUTPUT_OPTION) $< +.cc~.o: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) $(OUTPUT_OPTION) $*.cc +.cc.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.cc~.i: + $(GET) $(GFLAGS) -p $< > $*.cc + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.cc +.cc.a: + $(COMPILE.cc) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.cc~.a: + $(GET) $(GFLAGS) -p $< > $*.cc + $(COMPILE.cc) -o $% $*.cc + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +.C: + $(LINK.C) -o $@ $< $(LDLIBS) +.C~: + $(GET) $(GFLAGS) -p $< > $*.C + $(LINK.C) -o $@ $*.C $(LDLIBS) +.C.o: + $(COMPILE.C) $(OUTPUT_OPTION) $< +.C~.o: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) $(OUTPUT_OPTION) $*.C +.C.i: + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $< +.C~.i: + $(GET) $(GFLAGS) -p $< > $*.C + $(CCC) $(CCFLAGS) $(CPPFLAGS) -P $*.C +.C.a: + $(COMPILE.C) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.C~.a: + $(GET) $(GFLAGS) -p $< > $*.C + $(COMPILE.C) -o $% $*.C + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. +FC=f77 +FFLAGS= +COMPILE.f=$(FC) $(FFLAGS) -c +LINK.f=$(FC) $(FFLAGS) $(LDFLAGS) +COMPILE.F=$(FC) $(FFLAGS) $(CPPFLAGS) -c +LINK.F=$(FC) $(FFLAGS) $(CPPFLAGS) $(LDFLAGS) +.f: + $(LINK.f) -o $@ $< $(LDLIBS) +.f~: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.f +.f.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.f~.o: + $(GET) $(GFLAGS) -p $< > $*.f + $(FC) $(FFLAGS) -c $*.f +.f.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f~.a: + $(GET) $(GFLAGS) -p $< > $*.f + $(COMPILE.f) -o $% $*.f + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for: + $(LINK.f) -o $@ $< $(LDLIBS) +.for~: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.for +.for.o: + $(COMPILE.f) $(OUTPUT_OPTION) $< +.for~.o: + $(GET) $(GFLAGS) -p $< > $*.for + $(FC) $(FFLAGS) -c $*.for +.for.a: + $(COMPILE.f) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.for~.a: + $(GET) $(GFLAGS) -p $< > $*.for + $(COMPILE.f) -o $% $*.for + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F: + $(LINK.F) -o $@ $< $(LDLIBS) +.F~: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $*.F +.F.o: + $(COMPILE.F) $(OUTPUT_OPTION) $< +.F~.o: + $(GET) $(GFLAGS) -p $< > $*.F + $(FC) $(FFLAGS) -c $*.F +.F.a: + $(COMPILE.F) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.F~.a: + $(GET) $(GFLAGS) -p $< > $*.F + $(COMPILE.F) -o $% $*.F + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN section. ratfor. +RFLAGS= +COMPILE.r=$(FC) $(FFLAGS) $(RFLAGS) -c +LINK.r=$(FC) $(FFLAGS) $(RFLAGS) $(LDFLAGS) +.r: + $(LINK.r) -o $@ $< $(LDLIBS) +.r~: + $(GET) $(GFLAGS) -p $< > $*.r + $(LINK.r) -o $@ $*.r $(LDLIBS) +.r.o: + $(COMPILE.r) $(OUTPUT_OPTION) $< +.r~.o: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) $(OUTPUT_OPTION) $*.r +.r.a: + $(COMPILE.r) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.r~.a: + $(GET) $(GFLAGS) -p $< > $*.r + $(COMPILE.r) -o $% $*.r + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# FORTRAN 90 section. +F90C=f90 +F90FLAGS= +COMPILE.f90=$(F90C) $(F90FLAGS) -c +LINK.f90=$(F90C) $(F90FLAGS) $(LDFLAGS) +COMPILE.ftn=$(F90C) $(F90FLAGS) -c +LINK.ftn=$(F90C) $(F90FLAGS) $(LDFLAGS) +.f90: + $(LINK.f90) -o $@ $< $(LDLIBS) +.f90~: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(LINK.f90) -o $@ $*.f90 $(LDLIBS) +.f90.o: + $(COMPILE.f90) $(OUTPUT_OPTION) $< +.f90~.o: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) $(OUTPUT_OPTION) $*.f90 +.f90.a: + $(COMPILE.f90) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.f90~.a: + $(GET) $(GFLAGS) -p $< > $*.f90 + $(COMPILE.f90) -o $% $*.f90 + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn: + $(LINK.ftn) -o $@ $< $(LDLIBS) +.ftn~: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(LINK.ftn) -o $@ $*.ftn $(LDLIBS) +.ftn.o: + $(COMPILE.ftn) $(OUTPUT_OPTION) $< +.ftn~.o: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) $(OUTPUT_OPTION) $*.ftn +.ftn.a: + $(COMPILE.ftn) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.ftn~.a: + $(GET) $(GFLAGS) -p $< > $*.ftn + $(COMPILE.ftn) -o $% $*.ftn + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Modula-2 section. +M2C=m2c +M2FLAGS= +MODFLAGS= +DEFFLAGS= +COMPILE.def=$(M2C) $(M2FLAGS) $(DEFFLAGS) +COMPILE.mod=$(M2C) $(M2FLAGS) $(MODFLAGS) +.def.sym: + $(COMPILE.def) -o $@ $< +.def~.sym: + $(GET) $(GFLAGS) -p $< > $*.def + $(COMPILE.def) -o $@ $*.def +.mod: + $(COMPILE.mod) -o $@ -e $@ $< +.mod~: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ -e $@ $*.mod +.mod.o: + $(COMPILE.mod) -o $@ $< +.mod~.o: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $@ $*.mod +.mod.a: + $(COMPILE.mod) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.mod~.a: + $(GET) $(GFLAGS) -p $< > $*.mod + $(COMPILE.mod) -o $% $*.mod + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Pascal section. +PC=pc +PFLAGS= +COMPILE.p=$(PC) $(PFLAGS) $(CPPFLAGS) -c +LINK.p=$(PC) $(PFLAGS) $(CPPFLAGS) $(LDFLAGS) +.p: + $(LINK.p) -o $@ $< $(LDLIBS) +.p~: + $(GET) $(GFLAGS) -p $< > $*.p + $(LINK.p) -o $@ $*.p $(LDLIBS) +.p.o: + $(COMPILE.p) $(OUTPUT_OPTION) $< +.p~.o: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) $(OUTPUT_OPTION) $*.p +.p.a: + $(COMPILE.p) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.p~.a: + $(GET) $(GFLAGS) -p $< > $*.p + $(COMPILE.p) -o $% $*.p + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Assembly section. +AS=as +ASFLAGS= +COMPILE.s=$(AS) $(ASFLAGS) +COMPILE.S=$(CC) $(ASFLAGS) $(CPPFLAGS) -c +.s.o: + $(COMPILE.s) -o $@ $< +.s~.o: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $@ $*.s +.s.a: + $(COMPILE.s) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.s~.a: + $(GET) $(GFLAGS) -p $< > $*.s + $(COMPILE.s) -o $% $*.s + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S.o: + $(COMPILE.S) -o $@ $< +.S~.o: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $@ $*.S +.S.a: + $(COMPILE.S) -o $% $< + $(AR) $(ARFLAGS) $@ $% + $(RM) $% +.S~.a: + $(GET) $(GFLAGS) -p $< > $*.S + $(COMPILE.S) -o $% $*.S + $(AR) $(ARFLAGS) $@ $% + $(RM) $% + +# Shell section. +.sh: + $(RM) $@ + cat $< > $@ + chmod +x $@ +.sh~: + $(GET) $(GFLAGS) -p $< > $*.sh + cp $*.sh $@ + chmod a+x $@ + +# NeWS section +CPS=cps +CPSFLAGS= +.cps.h: + $(CPS) $(CPSFLAGS) $*.cps +.cps~.h: + $(GET) $(GFLAGS) -p $< > $*.cps + $(CPS) $(CPSFLAGS) $*.cps + +# JAVA section +JAVAC=javac +JAVACFLAGS= +.java.class: + $(JAVAC) $(JAVACFLAGS) $< +.java~.class: + $(GET) $(GFLAGS) -p $< > $*.java + $(JAVAC) $(JAVACFLAGS) $< + +# Miscellaneous section. +LD=ld +LDFLAGS= +LDLIBS= +MAKE=make +RM=rm -f +AR=ar +ARFLAGS=rv +GET=get +GFLAGS= + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + cc -c markfile.c + $(RM) markfile.c + +SCCSFLAGS= +SCCSGETFLAGS=-s +.SCCS_GET: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ -G$@ + +.SCCS_GET_POSIX: + sccs $(SCCSFLAGS) get $(SCCSGETFLAGS) $@ + +.GET_POSIX: + $(GET) $(GFLAGS) s.$@ diff --git a/bin/misc.cc b/bin/misc.cc new file mode 100644 index 0000000..25bad0a --- /dev/null +++ b/bin/misc.cc @@ -0,0 +1,736 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * misc.cc + * + * This file contains various unclassified routines. Some main groups: + * getname + * Memory allocation + * String handling + * Property handling + * Error message handling + * Make internal state dumping + * main routine support + */ + +/* + * Included files + */ +#include +#include +#include /* SETVAR() */ +#include /* enable_interrupt() */ +#include /* va_list, va_start(), va_end() */ +#include /* SUNPRO_DEPENDENCIES */ +#include + +extern void job_adjust_fini(); + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static void print_rule(register Name target); +static void print_target_n_deps(register Name target); + +/***************************************** + * + * getname + */ + +/***************************************** + * + * Memory allocation + */ + +/* + * free_chain() + * + * frees a chain of Name_vector's + * + * Parameters: + * ptr Pointer to the first element in the chain + * to be freed. + * + * Global variables used: + */ +void +free_chain(Name_vector ptr) +{ + if (ptr != NULL) { + if (ptr->next != NULL) { + free_chain(ptr->next); + } + free((char *) ptr); + } +} + +/***************************************** + * + * String manipulation + */ + +/***************************************** + * + * Nameblock property handling + */ + +/***************************************** + * + * Error message handling + */ + +/* + * fatal(format, args...) + * + * Print a message and die + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * fatal_in_progress Indicates if this is a recursive call + * parallel_process_cnt Do we need to wait for anything? + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +fatal(const char *message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Fatal error: "), getprogname()); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); + if (fatal_in_progress) { + exit_status = 1; + exit(1); + } + fatal_in_progress = true; + /* Let all parallel children finish */ + if ((dmake_mode_type == parallel_mode) && + (parallel_process_cnt > 0)) { + (void) fprintf(stderr, + gettext("Waiting for %d %s to finish\n"), + parallel_process_cnt, + parallel_process_cnt == 1 ? + gettext("job") : gettext("jobs")); + (void) fflush(stderr); + } + + while (parallel_process_cnt > 0) { + await_parallel(true); + finish_children(false); + } + + job_adjust_fini(); + + exit_status = 1; + exit(1); +} + +/* + * warning(format, args...) + * + * Print a message and continue. + * + * Parameters: + * format printf type format string + * args Arguments to match the format + * + * Global variables used: + * report_pwd Should we report the current path? + */ +/*VARARGS*/ +void +warning(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Warning: "), getprogname()); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); +} + +/* + * time_to_string(time) + * + * Take a numeric time value and produce + * a proper string representation. + * + * Return value: + * The string representation of the time + * + * Parameters: + * time The time we need to translate + * + * Global variables used: + */ +char * +time_to_string(const timestruc_t &time) +{ + struct tm *tm; + char buf[128]; + + if (time == file_doesnt_exist) { + return gettext("File does not exist"); + } + if (time == file_max_time) { + return gettext("Younger than any file"); + } + tm = localtime(&time.tv_sec); + strftime(buf, sizeof (buf), "%c %Z", tm); + buf[127] = (int) nul_char; + return strdup(buf); +} + +/* + * get_current_path() + * + * Stuff current_path with the current path if it isnt there already. + * + * Parameters: + * + * Global variables used: + */ +char * +get_current_path(void) +{ + char pwd[(MAXPATHLEN * MB_LEN_MAX)]; + static char *current_path; + + if (current_path == NULL) { + getcwd(pwd, sizeof(pwd)); + if (pwd[0] == (int) nul_char) { + pwd[0] = (int) slash_char; + pwd[1] = (int) nul_char; + } + current_path = strdup(pwd); + } + return current_path; +} + +/***************************************** + * + * Make internal state dumping + * + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ + +/* + * dump_make_state() + * + * Dump make's internal state to stdout + * + * Parameters: + * + * Global variables used: + * svr4 Was ".SVR4" seen in makefile? + * svr4_name The Name ".SVR4", printed + * posix Was ".POSIX" seen in makefile? + * posix_name The Name ".POSIX", printed + * default_rule Points to the .DEFAULT rule + * default_rule_name The Name ".DEFAULT", printed + * default_target_to_build The first target to print + * dot_keep_state The Name ".KEEP_STATE", printed + * dot_keep_state_file The Name ".KEEP_STATE_FILE", printed + * hashtab The make hash table for Name blocks + * ignore_errors Was ".IGNORE" seen in makefile? + * ignore_name The Name ".IGNORE", printed + * keep_state Was ".KEEP_STATE" seen in makefile? + * percent_list The list of % rules + * precious The Name ".PRECIOUS", printed + * sccs_get_name The Name ".SCCS_GET", printed + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", printed + * get_name The Name ".GET", printed + * get_posix_name The Name ".GET_POSIX", printed + * sccs_get_rule Points to the ".SCCS_GET" rule + * silent Was ".SILENT" seen in makefile? + * silent_name The Name ".SILENT", printed + * suffixes The suffix list from ".SUFFIXES" + * suffixes_name The Name ".SUFFIX", printed + */ +void +dump_make_state(void) +{ + Name_set::iterator p, e; + register Property prop; + register Dependency dep; + register Cmd_line rule; + Percent percent, percent_depe; + + /* Default target */ + if (default_target_to_build != NULL) { + print_rule(default_target_to_build); + } + (void) printf("\n"); + + /* .POSIX */ + if (posix) { + (void) printf("%s:\n", posix_name->string_mb); + } + + /* .DEFAULT */ + if (default_rule != NULL) { + (void) printf("%s:\n", default_rule_name->string_mb); + for (rule = default_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .IGNORE */ + if (ignore_errors) { + (void) printf("%s:\n", ignore_name->string_mb); + } + + /* .KEEP_STATE: */ + if (keep_state) { + (void) printf("%s:\n\n", dot_keep_state->string_mb); + } + + /* .PRECIOUS */ + (void) printf("%s:", precious->string_mb); + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if ((p->stat.is_precious) || (all_precious)) { + (void) printf(" %s", p->string_mb); + } + } + (void) printf("\n"); + + /* .SCCS_GET */ + if (sccs_get_rule != NULL) { + (void) printf("%s:\n", sccs_get_name->string_mb); + for (rule = sccs_get_rule; rule != NULL; rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* .SILENT */ + if (silent) { + (void) printf("%s:\n", silent_name->string_mb); + } + + /* .SUFFIXES: */ + (void) printf("%s:", suffixes_name->string_mb); + for (dep = suffixes; dep != NULL; dep = dep->next) { + (void) printf(" %s", dep->name->string_mb); + build_suffix_list(dep->name); + } + (void) printf("\n\n"); + + /* % rules */ + for (percent = percent_list; + percent != NULL; + percent = percent->next) { + (void) printf("%s:", + percent->name->string_mb); + + for (percent_depe = percent->dependencies; + percent_depe != NULL; + percent_depe = percent_depe->next) { + (void) printf(" %s", percent_depe->name->string_mb); + } + + (void) printf("\n"); + + for (rule = percent->command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } + } + + /* Suffix rules */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + Wstring wcb(p); + if (wcb.get_string()[0] == (int) period_char) { + print_rule(p); + } + } + + /* Macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (((prop = get_prop(p->prop, macro_prop)) != NULL) && + (prop->body.macro.value != NULL)) { + (void) printf("%s", p->string_mb); + print_value(prop->body.macro.value, + (Daemon) prop->body.macro.daemon); + } + } + (void) printf("\n"); + + /* Conditional macro assignments */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + for (prop = get_prop(p->prop, conditional_prop); + prop != NULL; + prop = get_prop(prop->next, conditional_prop)) { + (void) printf("%s := %s", + p->string_mb, + prop->body.conditional.name-> + string_mb); + if (prop->body.conditional.append) { + printf(" +"); + } + else { + printf(" "); + } + print_value(prop->body.conditional.value, + no_daemon); + } + } + (void) printf("\n"); + + /* All other dependencies */ + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + if (p->colons != no_colon) { + print_rule(p); + } + } + (void) printf("\n"); +} + +/* + * print_rule(target) + * + * Print the rule for one target + * + * Parameters: + * target Target we print rule for + * + * Global variables used: + */ +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed || + ((line = get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) { + return; + } + target->dependency_printed = true; + + (void) printf("%s:", target->string_mb); + + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + (void) printf(" %s", dependency->name->string_mb); + } + + (void) printf("\n"); + + for (rule = line->body.line.command_template; + rule != NULL; + rule = rule->next) { + (void) printf("\t%s\n", rule->command_line->string_mb); + } +} + +void +dump_target_list(void) +{ + Name_set::iterator p, e; + Wstring str; + + for (p = hashtab.begin(), e = hashtab.end(); p != e; p++) { + str.init(p); + wchar_t * wcb = str.get_string(); + if ((p->colons != no_colon) && + ((wcb[0] != (int) period_char) || + ((wcb[0] == (int) period_char) && + (wcschr(wcb, (int) slash_char))))) { + print_target_n_deps(p); + } + } +} + +static void +print_target_n_deps(register Name target) +{ + register Cmd_line rule; + register Property line; + register Dependency dependency; + + if (target->dependency_printed) { + return; + } + target->dependency_printed = true; + + (void) printf("%s\n", target->string_mb); + + if ((line = get_prop(target->prop, line_prop)) == NULL) { + return; + } + for (dependency = line->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (!dependency->automatic) { + print_target_n_deps(dependency->name); + } + } +} + +/***************************************** + * + * main() support + */ + +/* + * load_cached_names() + * + * Load the vector of cached names + * + * Parameters: + * + * Global variables used: + * Many many pointers to Name blocks. + */ +void +load_cached_names(void) +{ + char *cp; + Name dollar; + + /* Load the cached_names struct */ + MBSTOWCS(wcs_buffer, ".BUILT_LAST_MAKE_RUN"); + built_last_make_run = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "@"); + c_at = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, " *conditionals* "); + conditionals = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * A version of make was released with NSE 1.0 that used + * VERSION-1.1 but this version is identical to VERSION-1.0. + * The version mismatch code makes a special case for this + * situation. If the version number is changed from 1.0 + * it should go to 1.2. + */ + MBSTOWCS(wcs_buffer, "VERSION-1.0"); + current_make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SVR4"); + svr4_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".POSIX"); + posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".DEFAULT"); + default_rule_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "$"); + dollar = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".DONE"); + done = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "."); + dot = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".KEEP_STATE"); + dot_keep_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".KEEP_STATE_FILE"); + dot_keep_state_file = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ""); + empty_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, " FORCE"); + force = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "HOST_ARCH"); + host_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "HOST_MACH"); + host_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".IGNORE"); + ignore_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".INIT"); + init = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".LOCAL"); + localhost_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".make.state"); + make_state = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "MAKEFLAGS"); + makeflags = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".MAKE_VERSION"); + make_version = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".NO_PARALLEL"); + no_parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".NOT_AUTO"); + not_auto = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".PARALLEL"); + parallel_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "PATH"); + path_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "+"); + plus = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".PRECIOUS"); + precious = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "?"); + query = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "^"); + hat = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".RECURSIVE"); + recursive_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SCCS_GET"); + sccs_get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SCCS_GET_POSIX"); + sccs_get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".GET"); + get_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".GET_POSIX"); + get_posix_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "SHELL"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SILENT"); + silent_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".SUFFIXES"); + suffixes_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, SUNPRO_DEPENDENCIES); + sunpro_dependencies = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "TARGET_ARCH"); + target_arch = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "TARGET_MACH"); + target_mach = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "VIRTUAL_ROOT"); + virtual_root = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "VPATH"); + vpath_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, ".WAIT"); + wait_name = GETNAME(wcs_buffer, FIND_LENGTH); + + wait_name->state = build_ok; + + /* Mark special targets so that the reader treats them properly */ + svr4_name->special_reader = svr4_special; + posix_name->special_reader = posix_special; + built_last_make_run->special_reader = built_last_make_run_special; + default_rule_name->special_reader = default_special; + dot_keep_state->special_reader = keep_state_special; + dot_keep_state_file->special_reader = keep_state_file_special; + ignore_name->special_reader = ignore_special; + make_version->special_reader = make_version_special; + no_parallel_name->special_reader = no_parallel_special; + parallel_name->special_reader = parallel_special; + localhost_name->special_reader = localhost_special; + precious->special_reader = precious_special; + sccs_get_name->special_reader = sccs_get_special; + sccs_get_posix_name->special_reader = sccs_get_posix_special; + get_name->special_reader = get_special; + get_posix_name->special_reader = get_posix_special; + silent_name->special_reader = silent_special; + suffixes_name->special_reader = suffixes_special; + + /* The value of $$ is $ */ + (void) SETVAR(dollar, dollar, false); + dollar->dollar = false; + + /* Set the value of $(SHELL) */ + if (posix) { + MBSTOWCS(wcs_buffer, "/usr/xpg4/bin/sh"); + } else { + MBSTOWCS(wcs_buffer, "/bin/sh"); + } + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + + /* + * Use " FORCE" to simulate a FRC dependency for :: type + * targets with no dependencies. + */ + (void) append_prop(force, line_prop); + force->stat.time = file_max_time; + + /* Make sure VPATH is defined before current dir is read */ + if ((cp = getenv(vpath_name->string_mb)) != NULL) { + MBSTOWCS(wcs_buffer, cp); + (void) SETVAR(vpath_name, + GETNAME(wcs_buffer, FIND_LENGTH), + false); + } + + /* Check if there is NO PATH variable. If not we construct one. */ + if (getenv(path_name->string_mb) == NULL) { + vroot_path = NULL; + add_dir_to_path(".", &vroot_path, -1); + add_dir_to_path("/bin", &vroot_path, -1); + add_dir_to_path("/usr/bin", &vroot_path, -1); + } +} + +/* + * iterate on list of conditional macros in np, and place them in + * a String_rec starting with, and separated by the '$' character. + */ +void +cond_macros_into_string(Name np, String_rec *buffer) +{ + Macro_list macro_list; + + /* + * Put the version number at the start of the string + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + append_string(wcs_buffer, buffer, FIND_LENGTH); + /* + * Add the rest of the conditional macros to the buffer + */ + if (np->depends_on_conditional){ + for (macro_list = np->conditional_macro_list; + macro_list != NULL; macro_list = macro_list->next){ + append_string(macro_list->macro_name, buffer, + FIND_LENGTH); + append_char((int) equal_char, buffer); + append_string(macro_list->value, buffer, FIND_LENGTH); + append_char((int) dollar_char, buffer); + } + } +} + diff --git a/bin/nse_printdep.cc b/bin/nse_printdep.cc new file mode 100644 index 0000000..df6ec1b --- /dev/null +++ b/bin/nse_printdep.cc @@ -0,0 +1,365 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Included files + */ +#include +#include /* get_prop() */ + +/* + * File table of contents + */ +void print_dependencies(register Name target, register Property line); +static void print_deps(register Name target, register Property line); +static void print_more_deps(Name target, Name name); +static void print_filename(Name name); +static Boolean should_print_dep(Property line); +static void print_forest(Name target); +static void print_deplist(Dependency head); +void print_value(register Name value, Daemon daemon); +static void print_rule(register Name target); +static void print_rec_info(Name target); +static Boolean is_out_of_date(Property line); +extern void depvar_print_results (void); + +/* + * print_dependencies(target, line) + * + * Print all the dependencies of a target. First print all the Makefiles. + * Then print all the dependencies. Finally, print all the .INIT + * dependencies. + * + * Parameters: + * target The target we print dependencies for + * line We get the dependency list from here + * + * Global variables used: + * done The Name ".DONE" + * init The Name ".INIT" + * makefiles_used List of all makefiles read + */ +void +print_dependencies(register Name target, register Property line) +{ + Dependency dp; + static Boolean makefiles_printed = false; + + if (target_variants) { + depvar_print_results(); + } + + if (!makefiles_printed) { + /* + * Search the makefile list for the primary makefile, + * then print it and its inclusions. After that go back + * and print the default.mk file and its inclusions. + */ + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + } + if (dp) { + print_deplist(dp); + for (dp = makefiles_used; dp != NULL; dp = dp->next) { + if (dp->name == primary_makefile) { + break; + } + (void)printf(" %s", dp->name->string_mb); + } + } + (void) printf("\n"); + makefiles_printed = true; + } + print_deps(target, line); +/* + print_more_deps(target, init); + print_more_deps(target, done); + */ + if (target_variants) { + print_forest(target); + } +} + +/* + * print_more_deps(target, name) + * + * Print some special dependencies. + * These are the dependencies for the .INIT and .DONE targets. + * + * Parameters: + * target Target built during make run + * name Special target to print dependencies for + * + * Global variables used: + */ +static void +print_more_deps(Name target, Name name) +{ + Property line; + register Dependency dependencies; + + line = get_prop(name->prop, line_prop); + if (line != NULL && line->body.line.dependencies != NULL) { + (void) printf("%s:\t", target->string_mb); + print_deplist(line->body.line.dependencies); + (void) printf("\n"); + for (dependencies= line->body.line.dependencies; + dependencies != NULL; + dependencies= dependencies->next) { + print_deps(dependencies->name, + get_prop(dependencies->name->prop, line_prop)); + } + } +} + +/* + * print_deps(target, line, go_recursive) + * + * Print a regular dependency list. Append to this information which + * indicates whether or not the target is recursive. + * + * Parameters: + * target target to print dependencies for + * line We get the dependency list from here + * go_recursive Should we show all dependencies recursively? + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", printed + */ +static void +print_deps(register Name target, register Property line) +{ + register Dependency dep; + + if ((target->dependency_printed) || + (target == force)) { + return; + } + target->dependency_printed = true; + + /* only print entries that are actually derived and are not leaf + * files and are not the result of sccs get. + */ + if (should_print_dep(line)) { + if ((report_dependencies_level == 2) || + (report_dependencies_level == 4)) { + if (is_out_of_date(line)) { + (void) printf("1 "); + } else { + (void) printf("0 "); + } + } + print_filename(target); + (void) printf(":\t"); + print_deplist(line->body.line.dependencies); + print_rec_info(target); + (void) printf("\n"); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + print_deps(dep->name, + get_prop(dep->name->prop, line_prop)); + } + } +} + +static Boolean +is_out_of_date(Property line) +{ + Dependency dep; + Property line2; + + if (line == NULL) { + return false; + } + if (line->body.line.is_out_of_date) { + return true; + } + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + line2 = get_prop(dep->name->prop, line_prop); + if (is_out_of_date(line2)) { + line->body.line.is_out_of_date = true; + return true; + } + } + return false; +} + +/* + * Given a dependency print it and all its siblings. + */ +static void +print_deplist(Dependency head) +{ + Dependency dp; + + for (dp = head; dp != NULL; dp = dp->next) { + if ((report_dependencies_level != 2) || + ((!dp->automatic) || + (dp->name->is_double_colon))) { + if (dp->name != force) { + putwchar(' '); + print_filename(dp->name); + } + } + } +} + +/* + * Print the name of a file for the -P option. + * If the file is a directory put on a trailing slash. + */ +static void +print_filename(Name name) +{ + (void) printf("%s", name->string_mb); +/* + if (name->stat.is_dir) { + putwchar('/'); + } + */ +} + +/* + * should_print_dep(line) + * + * Test if we should print the dependencies of this target. + * The line must exist and either have children dependencies + * or have a command that is not an SCCS command. + * + * Return value: + * true if the dependencies should be printed + * + * Parameters: + * line We get the dependency list from here + * + * Global variables used: + */ +static Boolean +should_print_dep(Property line) +{ + if (line == NULL) { + return false; + } + if (line->body.line.dependencies != NULL) { + return true; + } + if (line->body.line.sccs_command) { + return false; + } + return true; +} + +/* + * Print out the root nodes of all the dependency trees + * in this makefile. + */ +static void +print_forest(Name target) +{ + Name_set::iterator np, e; + Property line; + + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + if (np->is_target && !np->has_parent && np != target) { + (void) doname_check(np, true, false, false); + line = get_prop(np->prop, line_prop); + printf("-\n"); + print_deps(np, line); + } + } +} + + +/* + * This is a set of routines for dumping the internal make state + * Used for the -p option + */ +void +print_value(register Name value, Daemon daemon) + + +{ + Chain cp; + + if (value == NULL) + (void)printf("=\n"); + else + switch (daemon) { + case no_daemon: + (void)printf("= %s\n", value->string_mb); + break; + case chain_daemon: + for (cp= (Chain) value; cp != NULL; cp= cp->next) + (void)printf(cp->next == NULL ? "%s" : "%s ", + cp->name->string_mb); + (void)printf("\n"); + break; + }; +} + +static void +print_rule(register Name target) +{ + register Cmd_line rule; + register Property line; + + if (((line= get_prop(target->prop, line_prop)) == NULL) || + ((line->body.line.command_template == NULL) && + (line->body.line.dependencies == NULL))) + return; + print_dependencies(target, line); + for (rule= line->body.line.command_template; rule != NULL; rule= rule->next) + (void)printf("\t%s\n", rule->command_line->string_mb); +} + + +/* + * If target is recursive, print the following to standard out: + * .RECURSIVE subdir targ Makefile + */ +static void +print_rec_info(Name target) +{ + Recursive_make rp; + wchar_t *colon; + + report_recursive_init(); + + rp = find_recursive_target(target); + + if (rp) { + /* + * if found, print starting with the space after the ':' + */ + colon = (wchar_t *) wcschr(rp->oldline, (int) colon_char); + (void) printf("%s", colon + 1); + } +} + diff --git a/bin/parallel.cc b/bin/parallel.cc new file mode 100644 index 0000000..c7712df --- /dev/null +++ b/bin/parallel.cc @@ -0,0 +1,1892 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * parallel.cc + * + * Deal with the parallel processing + */ + +/* + * Included files + */ +#include /* errno */ +#include +#include +#include /* redirect_io() */ +#include /* expand_value() */ +#include /* getmem() */ +#include +#include +#include +#include +#include +#include +#include +#include + + + +/* + * Defined macros + */ +#define MAXRULES 100 + +/* + * This const should be in avo_dms/include/AvoDmakeCommand.h + */ +const int local_host_mask = 0x20; + + +/* + * typedefs & structs + */ + + +/* + * Static variables + */ +static Boolean just_did_subtree = false; +static char local_host[MAXNAMELEN] = ""; +static char user_name[MAXNAMELEN] = ""; +static int pmake_max_jobs = 0; +static pid_t process_running = -1; +static Running *running_tail = &running_list; +static Name subtree_conflict; +static Name subtree_conflict2; + + +/* + * File table of contents + */ +static void delete_running_struct(Running rp); +static Boolean dependency_conflict(Name target); +static Doname distribute_process(char **commands, Property line); +static void doname_subtree(Name target, Boolean do_get, Boolean implicit); +static void dump_out_file(char *filename, Boolean err); +static void finish_doname(Running rp); +static void maybe_reread_make_state(void); +static void process_next(void); +static void reset_conditionals(int cnt, Name *targets, Property *locals); +static pid_t run_rule_commands(char *host, char **commands); +static Property *set_conditionals(int cnt, Name *targets); +static void store_conditionals(Running rp); + + +/* + * execute_parallel(line, waitflg) + * + * DMake 2.x: + * parallel mode: spawns a parallel process to execute the command group. + * + * Return value: + * The result of the execution + * + * Parameters: + * line The command group to execute + */ +Doname +execute_parallel(Property line, Boolean waitflg, Boolean local) +{ + int argcnt; + int cmd_options = 0; + char *commands[MAXRULES + 5]; + char *cp; + Name dmake_name; + Name dmake_value; + int ignore; + Name make_machines_name; + char **p; + Property prop; + Doname result = build_ok; + Cmd_line rule; + Boolean silent_flag; + Name target = line->body.line.target; + Boolean wrote_state_file = false; + + if ((pmake_max_jobs == 0) && + (dmake_mode_type == parallel_mode)) { + if (local_host[0] == '\0') { + (void) gethostname(local_host, MAXNAMELEN); + } + MBSTOWCS(wcs_buffer, "DMAKE_MAX_JOBS"); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + pmake_max_jobs = atoi(dmake_value->string_mb); + if (pmake_max_jobs <= 0) { + warning(gettext("DMAKE_MAX_JOBS cannot be less than or equal to zero.")); + warning(gettext("setting DMAKE_MAX_JOBS to %d."), PMAKE_DEF_MAX_JOBS); + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } else { + /* + * For backwards compatibility w/ PMake 1.x, when + * DMake 2.x is being run in parallel mode, DMake + * should parse the PMake startup file + * $(HOME)/.make.machines to get the pmake_max_jobs. + */ + MBSTOWCS(wcs_buffer, "PMAKE_MACHINESFILE"); + dmake_name = GETNAME(wcs_buffer, FIND_LENGTH); + if (((prop = get_prop(dmake_name->prop, macro_prop)) != NULL) && + ((dmake_value = prop->body.macro.value) != NULL)) { + make_machines_name = dmake_value; + } else { + make_machines_name = NULL; + } + if ((pmake_max_jobs = read_make_machines(make_machines_name)) <= 0) { + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } + } + } + + if ((dmake_mode_type == serial_mode) || + ((dmake_mode_type == parallel_mode) && (waitflg))) { + return (execute_serial(line)); + } + + { + p = commands; + } + + argcnt = 0; + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (posix && (touch || quest) && !rule->always_exec) { + continue; + } + if (vpath_defined) { + rule->command_line = + vpath_translation(rule->command_line); + } + + silent_flag = false; + ignore = 0; + + if (rule->command_line->hash.length > 0) { + if (++argcnt == MAXRULES) { + return build_serial; + } + { + if (rule->silent && !silent) { + silent_flag = true; + } + if (rule->ignore_error) { + ignore++; + } + /* XXX - need to add support for + prefix */ + if (silent_flag || ignore) { + *p = getmem((silent_flag ? 1 : 0) + + ignore + + (strlen(rule-> + command_line-> + string_mb)) + + 1); + cp = *p++; + if (silent_flag) { + *cp++ = (int) at_char; + } + if (ignore) { + *cp++ = (int) hyphen_char; + } + (void) strcpy(cp, rule->command_line->string_mb); + } else { + *p++ = rule->command_line->string_mb; + } + } + } + } + if ((argcnt == 0) || + (report_dependencies_level > 0)) { + return build_ok; + } + { + *p = NULL; + + Doname res = distribute_process(commands, line); + if (res == build_running) { + parallel_process_cnt++; + } + + /* + * Return only those memory that were specially allocated + * for part of commands. + */ + for (int i = 0; commands[i] != NULL; i++) { + if ((commands[i][0] == (int) at_char) || + (commands[i][0] == (int) hyphen_char)) { + retmem_mb(commands[i]); + } + } + return res; + } +} + + + +#include /* sysconf(_SC_NPROCESSORS_ONLN) */ +#include /* ftok() */ +#include /* shmget(), shmat(), shmdt(), shmctl() */ +#include /* sem_init(), sem_trywait(), sem_post(), sem_destroy() */ +#include /* getloadavg() */ + +/* + * adjust_pmake_max_jobs (int pmake_max_jobs) + * + * Parameters: + * pmake_max_jobs - max jobs limit set by user + * + * External functions used: + * sysconf() + * getloadavg() + */ +static int +adjust_pmake_max_jobs (int pmake_max_jobs) +{ + static int ncpu = 0; + double loadavg[3]; + int adjustment; + int adjusted_max_jobs; + + if (ncpu <= 0) { + if ((ncpu = sysconf(_SC_NPROCESSORS_ONLN)) <= 0) { + ncpu = 1; + } + } + if (getloadavg(loadavg, 3) != 3) return(pmake_max_jobs); + adjustment = ((int)loadavg[LOADAVG_1MIN]); + if (adjustment < 2) return(pmake_max_jobs); + if (ncpu > 1) { + adjustment = adjustment / ncpu; + } + adjusted_max_jobs = pmake_max_jobs - adjustment; + if (adjusted_max_jobs < 1) adjusted_max_jobs = 1; + return(adjusted_max_jobs); +} + +/* + * M2 adjust mode data and functions + * + * m2_init() - initializes M2 shared semaphore + * m2_acquire_job() - decrements M2 semaphore counter + * m2_release_job() - increments M2 semaphore counter + * m2_fini() - destroys M2 semaphore and shared memory* + * + * Environment variables: + * __DMAKE_M2_FILE__ + * + * External functions: + * ftok(), shmget(), shmat(), shmdt(), shmctl() + * sem_init(), sem_trywait(), sem_post(), sem_destroy() + * creat(), close(), unlink() + * getenv(), putenv() + * + * Static variables: + * m2_file - tmp file name to create ipc key for shared memory + * m2_shm_id - shared memory id + * m2_shm_sem - shared memory semaphore + */ + +static char m2_file[MAXPATHLEN]; +static int m2_shm_id = -1; +static sem_t* m2_shm_sem = 0; + +static int +m2_init() { + char *var; + key_t key; + + if ((var = getenv("__DMAKE_M2_FILE__")) == 0) { + /* compose tmp file name */ + sprintf(m2_file, "%s/dmake.m2.%d.XXXXXX", tmpdir, getpid()); + + /* create tmp file */ + int fd = mkstemp(m2_file); + if (fd < 0) { + return -1; + } else { + close(fd); + } + } else { + /* using existing semaphore */ + strcpy(m2_file, var); + } + + /* combine IPC key */ + if ((key = ftok(m2_file, 38)) == (key_t) -1) { + return -1; + } + + /* create shared memory */ + if ((m2_shm_id = shmget(key, sizeof(*m2_shm_sem), 0666 | (var ? 0 : IPC_CREAT|IPC_EXCL))) == -1) { + return -1; + } + + /* attach shared memory */ + if ((m2_shm_sem = (sem_t*) shmat(m2_shm_id, 0, 0666)) == (sem_t*)-1) { + return -1; + } + + /* root process */ + if (var == 0) { + /* initialize semaphore */ + if (sem_init(m2_shm_sem, 1, pmake_max_jobs)) { + return -1; + } + + /* alloc memory for env variable */ + if ((var = (char*) malloc(MAXPATHLEN)) == 0) { + return -1; + } + + /* put key to env */ + sprintf(var, "__DMAKE_M2_FILE__=%s", m2_file); + if (putenv(var)) { + return -1; + } + } + return 0; +} + +static void +m2_fini() { + if (m2_shm_id >= 0) { + struct shmid_ds stat; + + /* determine the number of attached processes */ + if (shmctl(m2_shm_id, IPC_STAT, &stat) == 0) { + if (stat.shm_nattch <= 1) { + /* destroy semaphore */ + if (m2_shm_sem != 0) { + (void) sem_destroy(m2_shm_sem); + } + + /* destroy shared memory */ + (void) shmctl(m2_shm_id, IPC_RMID, &stat); + + /* remove tmp file created for the key */ + (void) unlink(m2_file); + } else { + /* detach shared memory */ + if (m2_shm_sem != 0) { + (void) shmdt((char*) m2_shm_sem); + } + } + } + + m2_shm_id = -1; + m2_shm_sem = 0; + } +} + +static int +m2_acquire_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_trywait(m2_shm_sem) == 0) { + return 1; + } + if (errno == EAGAIN) { + return 0; + } + } + return -1; +} + +static int +m2_release_job() { + if ((m2_shm_id >= 0) && (m2_shm_sem != 0)) { + if (sem_post(m2_shm_sem) == 0) { + return 0; + } + } + return -1; +} + +/* + * job adjust mode + * + * Possible values: + * ADJUST_M1 - adjustment by system load (default) + * ADJUST_M2 - fixed limit of jobs for the group of nested dmakes + * ADJUST_NONE - no adjustment - fixed limit of jobs for the current dmake + */ +static enum { + ADJUST_UNKNOWN, + ADJUST_M1, + ADJUST_M2, + ADJUST_NONE +} job_adjust_mode = ADJUST_UNKNOWN; + +/* + * void job_adjust_fini() + * + * Description: + * Cleans up job adjust data. + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +void +job_adjust_fini() { + if (job_adjust_mode == ADJUST_M2) { + m2_fini(); + } +} + +/* + * void job_adjust_error() + * + * Description: + * Prints warning message, cleans up job adjust data, and disables job adjustment + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * + * External functions: + * putenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_error() { + if (job_adjust_mode != ADJUST_NONE) { + /* cleanup internals */ + job_adjust_fini(); + + /* warning message for the user */ + warning(gettext("Encountered max jobs auto adjustment error - disabling auto adjustment.")); + + /* switch off job adjustment for the children */ + putenv(strdup("DMAKE_ADJUST_MAX_JOBS=NO")); + + /* and for this dmake */ + job_adjust_mode = ADJUST_NONE; + } +} + +/* + * void job_adjust_init() + * + * Description: + * Parses DMAKE_ADJUST_MAX_JOBS env variable + * and performs appropriate initializations. + * + * Environment: + * DMAKE_ADJUST_MAX_JOBS + * DMAKE_ADJUST_MAX_JOBS == "NO" - no adjustment + * DMAKE_ADJUST_MAX_JOBS == "M2" - M2 adjust mode + * other - M1 adjust mode + * + * External functions: + * getenv() + * + * Static variables: + * job_adjust_mode Current job adjust mode + */ +static void +job_adjust_init() { + if (job_adjust_mode == ADJUST_UNKNOWN) { + /* default mode */ + job_adjust_mode = ADJUST_M1; + + /* determine adjust mode */ + if (char *var = getenv("DMAKE_ADJUST_MAX_JOBS")) { + if (strcasecmp(var, "NO") == 0) { + job_adjust_mode = ADJUST_NONE; + } else if (strcasecmp(var, "M2") == 0) { + job_adjust_mode = ADJUST_M2; + } + } + + /* M2 specific initialization */ + if (job_adjust_mode == ADJUST_M2) { + if (m2_init()) { + job_adjust_error(); + } + } + } +} + + +/* + * distribute_process(char **commands, Property line) + * + * Parameters: + * commands argv vector of commands to execute + * + * Return value: + * The result of the execution + * + * Static variables used: + * process_running Set to the pid of the process set running + * job_adjust_mode Current job adjust mode + */ +static Doname +distribute_process(char **commands, Property line) +{ + static unsigned file_number = 0; + wchar_t string[MAXPATHLEN]; + char mbstring[MAXPATHLEN]; + int filed; + int res; + int tmp_index; + char *tmp_index_str_ptr; + + /* initialize adjust mode, if not initialized */ + if (job_adjust_mode == ADJUST_UNKNOWN) { + job_adjust_init(); + } + + /* actions depend on adjust mode */ + switch (job_adjust_mode) { + case ADJUST_M1: + while (parallel_process_cnt >= adjust_pmake_max_jobs (pmake_max_jobs)) { + await_parallel(false); + finish_children(true); + } + break; + case ADJUST_M2: + if ((res = m2_acquire_job()) == 0) { + if (parallel_process_cnt > 0) { + await_parallel(false); + finish_children(true); + + if ((res = m2_acquire_job()) == 0) { + return build_serial; + } + } else { + return build_serial; + } + } + if (res < 0) { + /* job adjustment error */ + job_adjust_error(); + + /* no adjustment */ + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + break; + default: + while (parallel_process_cnt >= pmake_max_jobs) { + await_parallel(false); + finish_children(true); + } + } + + setvar_envvar(); + /* + * Tell the user what DMake is doing. + */ + if (!silent && output_mode != txt2_mode) { + /* + * Print local_host --> x job(s). + */ + (void) fprintf(stdout, + gettext("%s --> %d %s\n"), + local_host, + parallel_process_cnt + 1, + (parallel_process_cnt == 0) ? gettext("job") : gettext("jobs")); + + /* Print command line(s). */ + tmp_index = 0; + while (commands[tmp_index] != NULL) { + /* No @ char. */ + /* XXX - need to add [2] when + prefix is added */ + if ((commands[tmp_index][0] != (int) at_char) && + (commands[tmp_index][1] != (int) at_char)) { + tmp_index_str_ptr = commands[tmp_index]; + if (*tmp_index_str_ptr == (int) hyphen_char) { + tmp_index_str_ptr++; + } + (void) fprintf(stdout, "%s\n", tmp_index_str_ptr); + } + tmp_index++; + } + (void) fflush(stdout); + } + + (void) sprintf(mbstring, + "%s/dmake.stdout.%d.%d.XXXXXX", + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stdout_file = strdup(mbstring); + stderr_file = NULL; + + if (!out_err_same) { + (void) sprintf(mbstring, + "%s/dmake.stderr.%d.%d.XXXXXX", + tmpdir, + getpid(), + file_number++); + + mktemp(mbstring); + + stderr_file = strdup(mbstring); + } + + process_running = run_rule_commands(local_host, commands); + + return build_running; +} + +/* + * doname_parallel(target, do_get, implicit) + * + * Processes the given target and finishes up any parallel + * processes left running. + * + * Return value: + * Result of target build + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + */ +Doname +doname_parallel(Name target, Boolean do_get, Boolean implicit) +{ + Doname result; + + result = doname_check(target, do_get, implicit, false); + if (result == build_ok || result == build_failed) { + return result; + } + finish_running(); + return (Doname) target->state; +} + +/* + * doname_subtree(target, do_get, implicit) + * + * Completely computes an object and its dependents for a + * serial subtree build. + * + * Parameters: + * target Target to build + * do_get True if sccs get to be done + * implicit True if this is an implicit target + * + * Static variables used: + * running_tail Tail of the list of running processes + * + * Global variables used: + * running_list The list of running processes + */ +static void +doname_subtree(Name target, Boolean do_get, Boolean implicit) +{ + Running save_running_list; + Running *save_running_tail; + + save_running_list = running_list; + save_running_tail = running_tail; + running_list = NULL; + running_tail = &running_list; + target->state = build_subtree; + target->checking_subtree = true; + while(doname_check(target, do_get, implicit, false) == build_running) { + target->checking_subtree = false; + finish_running(); + target->state = build_subtree; + } + target->checking_subtree = false; + running_list = save_running_list; + running_tail = save_running_tail; +} + +/* + * finish_running() + * + * Keeps processing until the running_list is emptied out. + * + * Parameters: + * + * Global variables used: + * running_list The list of running processes + */ +void +finish_running(void) +{ + while (running_list != NULL) { + { + await_parallel(false); + finish_children(true); + } + if (running_list != NULL) { + process_next(); + } + } +} + +/* + * process_next() + * + * Searches the running list for any targets which can start processing. + * This can be a pending target, a serial target, or a subtree target. + * + * Parameters: + * + * Static variables used: + * running_tail The end of the list of running procs + * subtree_conflict A target which conflicts with a subtree + * subtree_conflict2 The other target which conflicts + * + * Global variables used: + * commands_done True if commands executed + * debug_level Controls debug output + * parallel_process_cnt Number of parallel process running + * recursion_level Indentation for debug output + * running_list List of running processes + */ +static void +process_next(void) +{ + Running rp; + Running *rp_prev; + Property line; + Chain target_group; + Dependency dep; + Boolean quiescent = true; + Running *subtree_target; + Boolean saved_commands_done; + Property *conditionals; + + subtree_target = NULL; + subtree_conflict = NULL; + subtree_conflict2 = NULL; + /* + * If nothing currently running, build a serial target, if any. + */ +start_loop_1: + for (rp_prev = &running_list, rp = running_list; + rp != NULL && parallel_process_cnt == 0; + rp = rp->next) { + if (rp->state == build_serial) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = build_pending; + (void) doname_check(rp->target, + rp->do_get, + rp->implicit, + false); + quiescent = false; + delete_running_struct(rp); + goto start_loop_1; + } else { + rp_prev = &rp->next; + } + } + /* + * Find a target to build. The target must be pending, have all + * its dependencies built, and not be in a target group with a target + * currently building. + */ +start_loop_2: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (!(rp->state == build_pending || + rp->state == build_subtree)) { + quiescent = false; + rp_prev = &rp->next; + } else if (rp->state == build_pending) { + line = get_prop(rp->target->prop, line_prop); + for (dep = line->body.line.dependencies; + dep != NULL; + dep = dep->next) { + if (dep->name->state == build_running || + dep->name->state == build_pending || + dep->name->state == build_serial) { + break; + } + } + if (dep == NULL) { + for (target_group = line->body.line.target_group; + target_group != NULL; + target_group = target_group->next) { + if (is_running(target_group->name)) { + break; + } + } + if (target_group == NULL) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + rp->target->state = rp->redo ? + build_dont_know : build_pending; + saved_commands_done = commands_done; + conditionals = + set_conditionals + (rp->conditional_cnt, + rp->conditional_targets); + rp->target->dont_activate_cond_values = true; + if ((doname_check(rp->target, + rp->do_get, + rp->implicit, + rp->target->has_target_prop ? true : false) != + build_running) && + !commands_done) { + commands_done = + saved_commands_done; + } + rp->target->dont_activate_cond_values = false; + reset_conditionals + (rp->conditional_cnt, + rp->conditional_targets, + conditionals); + quiescent = false; + delete_running_struct(rp); + goto start_loop_2; + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + /* + * If nothing has been found to build and there exists a subtree + * target with no dependency conflicts, build it. + */ + if (quiescent) { +start_loop_3: + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { + if (rp->state == build_subtree) { + if (!dependency_conflict(rp->target)) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, + rp->do_get, + rp->implicit); + quiescent = false; + delete_running_struct(rp); + goto start_loop_3; + } else { + subtree_target = rp_prev; + rp_prev = &rp->next; + } + } else { + rp_prev = &rp->next; + } + } + } + /* + * If still nothing found to build, we either have a deadlock + * or a subtree with a dependency conflict with something waiting + * to build. + */ + if (quiescent) { + if (subtree_target == NULL) { + fatal(gettext("Internal error: deadlock detected in process_next")); + } else { + rp = *subtree_target; + if (debug_level > 0) { + warning(gettext("Conditional macro conflict encountered for %s between %s and %s"), + subtree_conflict2->string_mb, + rp->target->string_mb, + subtree_conflict->string_mb); + } + *subtree_target = (*subtree_target)->next; + if (rp->next == NULL) { + running_tail = subtree_target; + } + recursion_level = rp->recursion_level; + doname_subtree(rp->target, rp->do_get, rp->implicit); + delete_running_struct(rp); + } + } +} + +/* + * set_conditionals(cnt, targets) + * + * Sets the conditional macros for the targets given in the array of + * targets. The old macro values are returned in an array of + * Properties for later resetting. + * + * Return value: + * Array of conditional macro settings + * + * Parameters: + * cnt Number of targets + * targets Array of targets + */ +static Property * +set_conditionals(int cnt, Name *targets) +{ + Property *locals, *lp; + Name *tp; + + locals = (Property *) getmem(cnt * sizeof(Property)); + for (lp = locals, tp = targets; + cnt > 0; + cnt--, lp++, tp++) { + *lp = (Property) getmem((*tp)->conditional_cnt * + sizeof(struct _Property)); + set_locals(*tp, *lp); + } + return locals; +} + +/* + * reset_conditionals(cnt, targets, locals) + * + * Resets the conditional macros as saved in the given array of + * Properties. The resets are done in reverse order. Afterwards the + * data structures are freed. + * + * Parameters: + * cnt Number of targets + * targets Array of targets + * locals Array of dependency macro settings + */ +static void +reset_conditionals(int cnt, Name *targets, Property *locals) +{ + Name *tp; + Property *lp; + + for (tp = targets + (cnt - 1), lp = locals + (cnt - 1); + cnt > 0; + cnt--, tp--, lp--) { + reset_locals(*tp, + *lp, + get_prop((*tp)->prop, conditional_prop), + 0); + retmem_mb((caddr_t) *lp); + } + retmem_mb((caddr_t) locals); +} + +/* + * dependency_conflict(target) + * + * Returns true if there is an intersection between + * the subtree of the target and any dependents of the pending targets. + * + * Return value: + * True if conflict found + * + * Parameters: + * target Subtree target to check + * + * Static variables used: + * subtree_conflict Target conflict found + * subtree_conflict2 Second conflict found + * + * Global variables used: + * running_list List of running processes + * wait_name .WAIT, not a real dependency + */ +static Boolean +dependency_conflict(Name target) +{ + Property line; + Property pending_line; + Dependency dp; + Dependency pending_dp; + Running rp; + + /* Return if we are already checking this target */ + if (target->checking_subtree) { + return false; + } + target->checking_subtree = true; + line = get_prop(target->prop, line_prop); + if (line == NULL) { + target->checking_subtree = false; + return false; + } + /* Check each dependency of the target for conflicts */ + for (dp = line->body.line.dependencies; dp != NULL; dp = dp->next) { + /* Ignore .WAIT dependency */ + if (dp->name == wait_name) { + continue; + } + /* + * For each pending target, look for a dependency which + * is the same as a dependency of the subtree target. Since + * we can't build the subtree until all pending targets have + * finished which depend on the same dependency, this is + * a conflict. + */ + for (rp = running_list; rp != NULL; rp = rp->next) { + if (rp->state == build_pending) { + pending_line = get_prop(rp->target->prop, + line_prop); + if (pending_line == NULL) { + continue; + } + for(pending_dp = pending_line-> + body.line.dependencies; + pending_dp != NULL; + pending_dp = pending_dp->next) { + if (dp->name == pending_dp->name) { + target->checking_subtree + = false; + subtree_conflict = rp->target; + subtree_conflict2 = dp->name; + return true; + } + } + } + } + if (dependency_conflict(dp->name)) { + target->checking_subtree = false; + return true; + } + } + target->checking_subtree = false; + return false; +} + +/* + * await_parallel(waitflg) + * + * Waits for parallel children to exit and finishes their processing. + * If waitflg is false, the function returns after update_delay. + * + * Parameters: + * waitflg dwight + */ +void +await_parallel(Boolean waitflg) +{ + Boolean nohang; + pid_t pid; + int status; + Running rp; + int waiterr; + + nohang = false; + for ( ; ; ) { + if (!nohang) { + (void) alarm((int) update_delay); + } + pid = waitpid((pid_t)-1, + &status, + nohang ? WNOHANG : 0); + waiterr = errno; + if (!nohang) { + (void) alarm(0); + } + if (pid <= 0) { + if (waiterr == EINTR) { + if (waitflg) { + continue; + } else { + return; + } + } else { + return; + } + } + for (rp = running_list; + (rp != NULL) && (rp->pid != pid); + rp = rp->next) { + ; + } + if (rp == NULL) { + fatal(gettext("Internal error: returned child pid not in running_list")); + } else { + rp->state = (WIFEXITED(status) && WEXITSTATUS(status) == 0) ? build_ok : build_failed; + } + nohang = true; + parallel_process_cnt--; + + if (job_adjust_mode == ADJUST_M2) { + if (m2_release_job()) { + job_adjust_error(); + } + } + } +} + +/* + * finish_children(docheck) + * + * Finishes the processing for all targets which were running + * and have now completed. + * + * Parameters: + * docheck Completely check the finished target + * + * Static variables used: + * running_tail The tail of the running list + * + * Global variables used: + * continue_after_error -k flag + * fatal_in_progress True if we are finishing up after fatal err + * running_list List of running processes + */ +void +finish_children(Boolean docheck) +{ + int cmds_length; + Property line; + Property line2; + struct stat out_buf; + Running rp; + Running *rp_prev; + Cmd_line rule; + Boolean silent_flag; + + for (rp_prev = &running_list, rp = running_list; + rp != NULL; + rp = rp->next) { +bypass_for_loop_inc_4: + /* + * If the state is ok or failed, then this target has + * finished building. + * In parallel_mode, output the accumulated stdout/stderr. + * Read the auto dependency stuff, handle a failed build, + * update the target, then finish the doname process for + * that target. + */ + if (rp->state == build_ok || rp->state == build_failed) { + *rp_prev = rp->next; + if (rp->next == NULL) { + running_tail = rp_prev; + } + if ((line2 = rp->command) == NULL) { + line2 = get_prop(rp->target->prop, line_prop); + } + + + /* + * Check if there were any job output + * from the parallel build. + */ + if (rp->stdout_file != NULL) { + if (stat(rp->stdout_file, &out_buf) < 0) { + fatal(gettext("stat of %s failed: %s"), + rp->stdout_file, + errmsg(errno)); + } + + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + cmds_length = 0; + for (rule = line2->body.line.command_used, + silent_flag = silent; + rule != NULL; + rule = rule->next) { + cmds_length += rule->command_line->hash.length + 1; + silent_flag = BOOLEAN(silent_flag || rule->silent); + } + if (out_buf.st_size != cmds_length || silent_flag || + output_mode == txt2_mode) { + dump_out_file(rp->stdout_file, false); + } + } + (void) unlink(rp->stdout_file); + retmem_mb(rp->stdout_file); + rp->stdout_file = NULL; + } + + if (!out_err_same && (rp->stderr_file != NULL)) { + if (stat(rp->stderr_file, &out_buf) < 0) { + fatal(gettext("stat of %s failed: %s"), + rp->stderr_file, + errmsg(errno)); + } + if ((line2 != NULL) && + (out_buf.st_size > 0)) { + dump_out_file(rp->stderr_file, true); + } + (void) unlink(rp->stderr_file); + retmem_mb(rp->stderr_file); + rp->stderr_file = NULL; + } + + check_state(rp->temp_file); + if (rp->temp_file != NULL) { + free_name(rp->temp_file); + } + rp->temp_file = NULL; + if (rp->state == build_failed) { + line = get_prop(rp->target->prop, line_prop); + if (line != NULL) { + line->body.line.command_used = NULL; + } + if (continue_after_error || + fatal_in_progress || + !docheck) { + warning(gettext("Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + build_failed_seen = true; + } else { + /* + * XXX??? - DMake needs to exit(), + * but shouldn't call fatal(). + */ +#ifdef PRINT_EXIT_STATUS + warning("I'm in finish_children. rp->state == build_failed."); +#endif + + fatal(gettext("Command failed for target `%s'"), + rp->command ? line2->body.line.target->string_mb : rp->target->string_mb); + } + } + if (!docheck) { + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } + update_target(get_prop(rp->target->prop, line_prop), + rp->state); + finish_doname(rp); + delete_running_struct(rp); + rp = *rp_prev; + if (rp == NULL) { + break; + } else { + goto bypass_for_loop_inc_4; + } + } else { + rp_prev = &rp->next; + } + } +} + +/* + * dump_out_file(filename, err) + * + * Write the contents of the file to stdout, then unlink the file. + * + * Parameters: + * filename Name of temp file containing output + * + * Global variables used: + */ +static void +dump_out_file(char *filename, Boolean err) +{ + int chars_read; + char copybuf[BUFSIZ]; + int fd; + int out_fd = (err ? 2 : 1); + + if ((fd = open(filename, O_RDONLY)) < 0) { + fatal(gettext("open failed for output file %s: %s"), + filename, + errmsg(errno)); + } + if (!silent && output_mode != txt2_mode) { + (void) fprintf(err ? stderr : stdout, + err ? + gettext("%s --> Job errors\n") : + gettext("%s --> Job output\n"), + local_host); + (void) fflush(err ? stderr : stdout); + } + for (chars_read = read(fd, copybuf, BUFSIZ); + chars_read > 0; + chars_read = read(fd, copybuf, BUFSIZ)) { + /* + * Read buffers from the source file until end or error. + */ + if (write(out_fd, copybuf, chars_read) < 0) { + fatal(gettext("write failed for output file %s: %s"), + filename, + errmsg(errno)); + } + } + (void) close(fd); + (void) unlink(filename); +} + +/* + * finish_doname(rp) + * + * Completes the processing for a target which was left running. + * + * Parameters: + * rp Running list entry for target + * + * Global variables used: + * debug_level Debug flag + * recursion_level Indentation for debug output + */ +static void +finish_doname(Running rp) +{ + int auto_count = rp->auto_count; + Name *automatics = rp->automatics; + Doname result = rp->state; + Name target = rp->target; + Name true_target = rp->true_target; + Property *conditionals; + + recursion_level = rp->recursion_level; + if (result == build_ok) { + if (true_target == NULL) { + (void) printf("Target = %s\n", target->string_mb); + (void) printf(" State = %d\n", result); + fatal("Internal error: NULL true_target in finish_doname"); + } + /* If all went OK, set a nice timestamp */ + if (true_target->stat.time == file_doesnt_exist) { + true_target->stat.time = file_max_time; + } + } + target->state = result; + if (target->is_member) { + Property member; + + /* Propagate the timestamp from the member file to the member */ + if ((target->stat.time != file_max_time) && + ((member = get_prop(target->prop, member_prop)) != NULL) && + (exists(member->body.member.member) > file_doesnt_exist)) { + target->stat.time = +/* + exists(member->body.member.member); + */ + member->body.member.member->stat.time; + } + } + /* + * Check if we found any new auto dependencies when we + * built the target. + */ + if ((result == build_ok) && check_auto_dependencies(target, + auto_count, + automatics)) { + if (debug_level > 0) { + (void) printf(gettext("%*sTarget `%s' acquired new dependencies from build, checking all dependencies\n"), + recursion_level, + "", + true_target->string_mb); + } + target->rechecking_target = true; + target->state = build_running; + + /* [tolik, Tue Mar 25 1997] + * Fix for bug 4038824: + * command line options set by conditional macros get dropped + * rp->conditional_cnt and rp->conditional_targets must be copied + * to new 'rp' during add_pending(). Set_conditionals() stores + * rp->conditional_targets to the global variable 'conditional_targets' + * Add_pending() will use this variable to set up 'rp'. + */ + conditionals = set_conditionals(rp->conditional_cnt, rp->conditional_targets); + add_pending(target, + recursion_level, + rp->do_get, + rp->implicit, + true); + reset_conditionals(rp->conditional_cnt, rp->conditional_targets, conditionals); + } +} + +/* + * new_running_struct() + * + * Constructor for Running struct. Creates a structure and initializes + * its fields. + * + */ +static Running new_running_struct() +{ + Running rp; + + rp = ALLOC(Running); + rp->target = NULL; + rp->true_target = NULL; + rp->command = NULL; + rp->sprodep_value = NULL; + rp->sprodep_env = NULL; + rp->auto_count = 0; + rp->automatics = NULL; + rp->pid = -1; + rp->job_msg_id = -1; + rp->stdout_file = NULL; + rp->stderr_file = NULL; + rp->temp_file = NULL; + rp->next = NULL; + return rp; +} + +/* + * add_running(target, true_target, command, recursion_level, auto_count, + * automatics, do_get, implicit) + * + * Adds a record on the running list for this target, which + * was just spawned and is running. + * + * Parameters: + * target Target being built + * true_target True target for target + * command Running command. + * recursion_level Debug indentation level + * auto_count Count of automatic dependencies + * automatics List of automatic dependencies + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + * process_running PID of process + * + * Global variables used: + * current_line Current line for target + * current_target Current target being built + * stderr_file Temporary file for stdout + * stdout_file Temporary file for stdout + * temp_file_name Temporary file for auto dependencies + */ +void +add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit) +{ + Running rp; + Name *p; + + rp = new_running_struct(); + rp->state = build_running; + rp->target = target; + rp->true_target = true_target; + rp->command = command; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->auto_count = auto_count; + if (auto_count > 0) { + rp->automatics = (Name *) getmem(auto_count * sizeof (Name)); + for (p = rp->automatics; auto_count > 0; auto_count--) { + *p++ = *automatics++; + } + } else { + rp->automatics = NULL; + } + { + rp->pid = process_running; + process_running = -1; + childPid = -1; + } + rp->job_msg_id = job_msg_id; + rp->stdout_file = stdout_file; + rp->stderr_file = stderr_file; + rp->temp_file = temp_file_name; + rp->redo = false; + rp->next = NULL; + store_conditionals(rp); + stdout_file = NULL; + stderr_file = NULL; + temp_file_name = NULL; + current_target = NULL; + current_line = NULL; + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_pending(target, recursion_level, do_get, implicit, redo) + * + * Adds a record on the running list for a pending target + * (waiting for its dependents to finish running). + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * redo True if this target is being redone + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo) +{ + Running rp; + rp = new_running_struct(); + rp->state = build_pending; + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->redo = redo; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_serial(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in serial after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_serial; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * add_subtree(target, recursion_level, do_get, implicit) + * + * Adds a record on the running list for a target which must be + * executed in isolation after others have finished. + * + * Parameters: + * target Target being built + * recursion_level Debug indentation level + * do_get Sccs get flag + * implicit Implicit flag + * + * Static variables used: + * running_tail Tail of running list + */ +void +add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit) +{ + Running rp; + + rp = new_running_struct(); + rp->target = target; + rp->recursion_level = recursion_level; + rp->do_get = do_get; + rp->implicit = implicit; + rp->state = build_subtree; + rp->redo = false; + store_conditionals(rp); + *running_tail = rp; + running_tail = &rp->next; +} + +/* + * store_conditionals(rp) + * + * Creates an array of the currently active targets with conditional + * macros (found in the chain conditional_targets) and puts that + * array in the Running struct. + * + * Parameters: + * rp Running struct for storing chain + * + * Global variables used: + * conditional_targets Chain of current dynamic conditionals + */ +static void +store_conditionals(Running rp) +{ + int cnt; + Chain cond_name; + + if (conditional_targets == NULL) { + rp->conditional_cnt = 0; + rp->conditional_targets = NULL; + return; + } + cnt = 0; + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + cnt++; + } + rp->conditional_cnt = cnt; + rp->conditional_targets = (Name *) getmem(cnt * sizeof(Name)); + for (cond_name = conditional_targets; + cond_name != NULL; + cond_name = cond_name->next) { + rp->conditional_targets[--cnt] = cond_name->name; + } +} + +/* + * parallel_ok(target, line_prop_must_exists) + * + * Returns true if the target can be run in parallel + * + * Return value: + * True if can run in parallel + * + * Parameters: + * target Target being tested + * + * Global variables used: + * all_parallel True if all targets default to parallel + * only_parallel True if no targets default to parallel + */ +Boolean +parallel_ok(Name target, Boolean line_prop_must_exists) +{ + Boolean assign; + Boolean make_refd; + Property line; + Cmd_line rule; + + assign = make_refd = false; + if (((line = get_prop(target->prop, line_prop)) == NULL) && + line_prop_must_exists) { + return false; + } + if (line != NULL) { + for (rule = line->body.line.command_used; + rule != NULL; + rule = rule->next) { + if (rule->assign) { + assign = true; + } else if (rule->make_refd) { + make_refd = true; + } + } + } + if (assign) { + return false; + } else if (target->parallel) { + return true; + } else if (target->no_parallel) { + return false; + } else if (all_parallel) { + return true; + } else if (only_parallel) { + return false; + } else if (make_refd) { + return false; + } else { + return true; + } +} + +/* + * is_running(target) + * + * Returns true if the target is running. + * + * Return value: + * True if target is running + * + * Parameters: + * target Target to check + * + * Global variables used: + * running_list List of running processes + */ +Boolean +is_running(Name target) +{ + Running rp; + + if (target->state != build_running) { + return false; + } + for (rp = running_list; + rp != NULL && target != rp->target; + rp = rp->next); + if (rp == NULL) { + return false; + } else { + return (rp->state == build_running) ? true : false; + } +} + +/* + * This function replaces the makesh binary. + */ + + +static pid_t +run_rule_commands(char *host, char **commands) +{ + Boolean always_exec; + Name command; + Boolean ignore; + int length; + Doname result; + Boolean silent_flag; + wchar_t *tmp_wcs_buffer; + + childPid = fork(); + switch (childPid) { + case -1: /* Error */ + fatal(gettext("Could not fork child process for dmake job: %s"), + errmsg(errno)); + break; + case 0: /* Child */ + /* To control the processed targets list is not the child's business */ + running_list = NULL; + if(out_err_same) { + redirect_io(stdout_file, (char*)NULL); + } else { + redirect_io(stdout_file, stderr_file); + } + for (commands = commands; + (*commands != (char *)NULL); + commands++) { + silent_flag = silent; + ignore = false; + always_exec = false; + while ((**commands == (int) at_char) || + (**commands == (int) hyphen_char) || + (**commands == (int) plus_char)) { + if (**commands == (int) at_char) { + silent_flag = true; + } + if (**commands == (int) hyphen_char) { + ignore = true; + } + if (**commands == (int) plus_char) { + always_exec = true; + } + (*commands)++; + } + if ((length = strlen(*commands)) >= MAXPATHLEN) { + tmp_wcs_buffer = ALLOC_WC(length + 1); + (void) mbstowcs(tmp_wcs_buffer, *commands, length + 1); + command = GETNAME(tmp_wcs_buffer, FIND_LENGTH); + retmem(tmp_wcs_buffer); + } else { + MBSTOWCS(wcs_buffer, *commands); + command = GETNAME(wcs_buffer, FIND_LENGTH); + } + if ((command->hash.length > 0) && + !silent_flag) { + (void) printf("%s\n", command->string_mb); + } + result = dosys(command, + ignore, + false, + false, /* bugs #4085164 & #4990057 */ + /* BOOLEAN(silent_flag && ignore), */ + always_exec, + (Name) NULL); + if (result == build_failed) { + if (silent_flag) { + (void) printf(gettext("The following command caused the error:\n%s\n"), command->string_mb); + } + if (!ignore) { + _exit(1); + } + } + } + _exit(0); + break; + default: + break; + } + return childPid; +} + +static void +maybe_reread_make_state(void) +{ + /* Copying dosys()... */ + if (report_dependencies_level == 0) { + make_state->stat.time = file_no_time; + (void) exists(make_state); + if (make_state_before == make_state->stat.time) { + return; + } + makefile_type = reading_statefile; + if (read_trace_level > 1) { + trace_reader = true; + } + temp_file_number++; + (void) read_simple_file(make_state, + false, + false, + false, + false, + false, + true); + trace_reader = false; + } +} + + +static void +delete_running_struct(Running rp) +{ + if ((rp->conditional_cnt > 0) && + (rp->conditional_targets != NULL)) { + retmem_mb((char *) rp->conditional_targets); + } +/**/ + if ((rp->auto_count > 0) && + (rp->automatics != NULL)) { + retmem_mb((char *) rp->automatics); + } +/**/ + if(rp->sprodep_value) { + free_name(rp->sprodep_value); + } + if(rp->sprodep_env) { + retmem_mb(rp->sprodep_env); + } + retmem_mb((char *) rp); + +} + + diff --git a/bin/pmake.cc b/bin/pmake.cc new file mode 100644 index 0000000..660d8c0 --- /dev/null +++ b/bin/pmake.cc @@ -0,0 +1,420 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Included files + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* host2netname(), netname2host() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static int get_max(wchar_t **ms_address, wchar_t *hostname); +static Boolean pskip_comment(wchar_t **cp_address); +static void pskip_till_next_word(wchar_t **cp); +static Boolean pskip_white_space(wchar_t **cp_address); + + +/* + * read_make_machines(Name make_machines_name) + * + * For backwards compatibility w/ PMake 1.x, when DMake 2.x is + * being run in parallel mode, DMake should parse the PMake startup + * file $(HOME)/.make.machines to get the PMake max jobs. + * + * Return value: + * int of PMake max jobs + * + * Parameters: + * make_machines_name Name of .make.machines file + * + */ +int +read_make_machines(Name make_machines_name) +{ + wchar_t c; + Boolean default_make_machines; + struct hostent *hp; + wchar_t local_host[MAX_HOSTNAMELEN + 1]; + char local_host_mb[MAX_HOSTNAMELEN + 1] = ""; + int local_host_wslen; + wchar_t full_host[MAXNETNAMELEN + 1]; + int full_host_wslen = 0; + char *homedir; + Name MAKE_MACHINES; + struct stat make_machines_buf; + FILE *make_machines_file; + wchar_t *make_machines_list = NULL; + char *make_machines_list_mb = NULL; + wchar_t make_machines_path[MAXPATHLEN]; + char mb_make_machines_path[MAXPATHLEN]; + wchar_t *mp; + wchar_t *ms; + int pmake_max_jobs = 0; + struct utsname uts_info; + + + MBSTOWCS(wcs_buffer, "MAKE_MACHINES"); + MAKE_MACHINES = GETNAME(wcs_buffer, FIND_LENGTH); + /* Did the user specify a .make.machines file on the command line? */ + default_make_machines = false; + if (make_machines_name == NULL) { + /* Try reading the default .make.machines file, in $(HOME). */ + homedir = getenv("HOME"); + if ((homedir != NULL) && (strlen(homedir) < (sizeof(mb_make_machines_path) - 16))) { + sprintf(mb_make_machines_path, + "%s/.make.machines", homedir); + MBSTOWCS(make_machines_path, mb_make_machines_path); + make_machines_name = GETNAME(make_machines_path, FIND_LENGTH); + default_make_machines = true; + } + if (make_machines_name == NULL) { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + } +/* + make_machines_list_mb = getenv(MAKE_MACHINES->string_mb); + */ + /* Open the .make.machines file. */ + if ((make_machines_file = fopen(make_machines_name->string_mb, "r")) == NULL) { + if (!default_make_machines) { + /* Error opening .make.machines file. */ + fatal(gettext("Open of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* + * No $(HOME)/.make.machines file. + * Return 0 for PMake max jobs. + */ + return(0); + } + /* Stat the .make.machines file to get the size of the file. */ + } else if (fstat(fileno(make_machines_file), &make_machines_buf) < 0) { + /* Error stat'ing .make.machines file. */ + fatal(gettext("Stat of %s failed: %s"), + make_machines_name->string_mb, + errmsg(errno)); + } else { + /* Allocate memory for "MAKE_MACHINES=" */ + make_machines_list_mb = + (char *) getmem((int) (strlen(MAKE_MACHINES->string_mb) + + 2 + + make_machines_buf.st_size)); + sprintf(make_machines_list_mb, + "%s=", + MAKE_MACHINES->string_mb); + /* Read in the .make.machines file. */ + if (fread(make_machines_list_mb + strlen(MAKE_MACHINES->string_mb) + 1, + sizeof(char), + (int) make_machines_buf.st_size, + make_machines_file) != make_machines_buf.st_size) { + /* + * Error reading .make.machines file. + * Return 0 for PMake max jobs. + */ + warning(gettext("Unable to read %s"), + make_machines_name->string_mb); + (void) fclose(make_machines_file); + retmem_mb((caddr_t) make_machines_list_mb); + return(0); + } else { + (void) fclose(make_machines_file); + /* putenv "MAKE_MACHINES=" */ + *(make_machines_list_mb + + strlen(MAKE_MACHINES->string_mb) + + 1 + + make_machines_buf.st_size) = (int) nul_char; + if (putenv(make_machines_list_mb) != 0) { + warning(gettext("Couldn't put contents of %s in environment"), + make_machines_name->string_mb); + } else { + make_machines_list_mb += strlen(MAKE_MACHINES->string_mb) + 1; + make_machines_list = ALLOC_WC(strlen(make_machines_list_mb) + 1); + (void) mbstowcs(make_machines_list, + make_machines_list_mb, + (strlen(make_machines_list_mb) + 1)); + } + } + } + + uname(&uts_info); + strcpy(local_host_mb, &uts_info.nodename[0]); + MBSTOWCS(local_host, local_host_mb); + local_host_wslen = wcslen(local_host); + + // There is no getdomainname() function on Solaris. + // And netname2host() function does not work on Linux. + // So we have to use different APIs. + if (host2netname(mbs_buffer, NULL, NULL) && + netname2host(mbs_buffer, mbs_buffer2, MAXNETNAMELEN+1)) { + MBSTOWCS(full_host, mbs_buffer2); + full_host_wslen = wcslen(full_host); + } + + for (ms = make_machines_list; + (ms) && (*ms ); + ) { + /* + * Skip white space and comments till you reach + * a machine name. + */ + pskip_till_next_word(&ms); + + /* + * If we haven't reached the end of file, process the + * machine name. + */ + if (*ms) { + /* + * If invalid machine name decrement counter + * and skip line. + */ + mp = ms; + SKIPWORD(ms); + c = *ms; + *ms++ = '\0'; /* Append null to machine name. */ + /* + * If this was the beginning of a comment + * (we overwrote a # sign) and it's not + * end of line yet, shift the # sign. + */ + if ((c == '#') && (*ms != '\n') && (*ms)) { + *ms = '#'; + } + WCSTOMBS(mbs_buffer, mp); + /* + * Print "Ignoring unknown host" if: + * 1) hostname is longer than MAX_HOSTNAMELEN, or + * 2) hostname is unknown + */ + if ((wcslen(mp) > MAX_HOSTNAMELEN) || + ((hp = gethostbyname(mbs_buffer)) == NULL)) { + warning(gettext("Ignoring unknown host %s"), + mbs_buffer); + SKIPTOEND(ms); + /* Increment ptr if not end of file. */ + if (*ms) { + ms++; + } + } else { + /* Compare current hostname with local_host. */ + if (wcslen(mp) == local_host_wslen && + IS_WEQUALN(mp, local_host, local_host_wslen)) { + /* + * Bingo, local_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + /* Compare current hostname with full_host. */ + } else if (wcslen(mp) == full_host_wslen && + IS_WEQUALN(mp, full_host, full_host_wslen)) { + /* + * Bingo, full_host is in .make.machines. + * Continue reading. + */ + pmake_max_jobs = PMAKE_DEF_MAX_JOBS; + } else { + if (c != '\n') { + SKIPTOEND(ms); + if (*ms) { + ms++; + } + } + continue; + } + /* If we get here, local_host is in .make.machines. */ + if (c != '\n') { + /* Now look for keyword 'max'. */ + MBSTOWCS(wcs_buffer, "max"); + SKIPSPACE(ms); + while ((*ms != '\n') && (*ms)) { + if (*ms == '#') { + pskip_comment(&ms); + } else if (IS_WEQUALN(ms, wcs_buffer, 3)) { + /* Skip "max". */ + ms += 3; + pmake_max_jobs = get_max(&ms, mp); + SKIPSPACE(ms); + } else { + warning(gettext("unknown option for host %s"), mbs_buffer); + SKIPTOEND(ms); + break; + } + } + } + break; /* out of outermost for() loop. */ + } + } + } + retmem(make_machines_list); + return(pmake_max_jobs); +} + +/* + * pskip_till_next_word(cp) + * + * Parameters: + * cp the address of the string pointer. + * + * On return: + * cp points to beginning of machine name. + * + */ +static void +pskip_till_next_word(wchar_t **cp) +{ + /* + * Keep recursing until all combinations of white spaces + * and comments have been skipped. + */ + if (pskip_white_space(cp) || pskip_comment(cp)) { + pskip_till_next_word(cp); + } +} + +/* + * pskip_white_space(cp_address) + * + * Advances the string pointer so that it points to the first + * non white character (space/tab/linefeed). + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_white_space(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + while (*cp && iswspace(*cp)) { + cp++; + } + /* Have we skipped any characters? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +/* + * pskip_comment(cp_address) + * + * If cp_address is pointing to '#' (the beginning of a comment), + * increment the pointer till you reach end of line. + * + * Parameters: + * cp_address the address of the string pointer. + * + * Return Value: + * True if the pointer was changed. + * + */ +static Boolean +pskip_comment(wchar_t **cp_address) +{ + wchar_t *cp = *cp_address; + + /* Is this the beginning of a comment? Skip till end of line. */ + if (*cp == '#') { + SKIPTOEND(cp); + } + /* Have we skipped a comment line? */ + if (cp != *cp_address) { + *cp_address = cp; + return(true); + } else { + return(false); + } +} + +static int +get_max(wchar_t **ms_address, wchar_t *hostname) +{ + wchar_t *ms = *ms_address; + int limit = PMAKE_DEF_MAX_JOBS; /* Default setting. */ + + WCSTOMBS(mbs_buffer, hostname); + /* Look for `='. */ + SKIPSPACE(ms); + if ((!*ms) || (*ms == '\n') || (*ms != '=')) { + SKIPTOEND(ms); + warning(gettext("expected `=' after max, ignoring rest of line for host %s"), + mbs_buffer); + *ms_address = ms; + return((int) limit); + } else { + ms++; + SKIPSPACE(ms); + if ((*ms != '\n') && (*ms != '\0')) { + /* We've found, hopefully, a valid "max" value. */ + limit = (int) wcstol(ms, &ms, 10); + if (limit < 1) { + limit = PMAKE_DEF_MAX_JOBS; + warning(gettext("max value cannot be less than or equal to zero for host %s"), mbs_buffer); + } + } else { + /* No "max" value after "max=". */ + warning(gettext("no max value specified for host %s"), mbs_buffer); + } + *ms_address = ms; + return(limit); + } +} + + diff --git a/bin/read.cc b/bin/read.cc new file mode 100644 index 0000000..0d47f5b --- /dev/null +++ b/bin/read.cc @@ -0,0 +1,2148 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include /* alloca() */ +#include /* errno */ +#include /* fcntl() */ +#include +#include /* expand_value(), expand_macro() */ +#include /* getmem() */ +#include /* get_next_block_fn() */ +#include /* read() */ +#include /* read(), unlink() */ +#include + + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +static int line_started_with_space=0; // Used to diagnose spaces instead of tabs + +/* + * File table of contents + */ +static void parse_makefile(register Name true_makefile_name, register Source source); +static Source push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source); +extern void enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen); +extern Name normalize_name(register wchar_t *name_string, register int length); + +/* + * read_simple_file(makefile_name, chase_path, doname_it, + * complain, must_exist, report_file, lock_makefile) + * + * Make the makefile and setup to read it. Actually read it if it is stdio + * + * Return value: + * false if the read failed + * + * Parameters: + * makefile_name Name of the file to read + * chase_path Use the makefile path when opening file + * doname_it Call doname() to build the file first + * complain Print message if doname/open fails + * must_exist Generate fatal if file is missing + * report_file Report file when running -P + * lock_makefile Lock the makefile when reading + * + * Static variables used: + * + * Global variables used: + * do_not_exec_rule Is -n on? + * file_being_read Set to the name of the new file + * line_number The number of the current makefile line + * makefiles_used A list of all makefiles used, appended to + */ + + +Boolean +read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile) +{ + static short max_include_depth; + register Property makefile = maybe_append_prop(makefile_name, + makefile_prop); + Boolean forget_after_parse = false; + static pathpt makefile_path; + register int n; + char *path; + register Source source = ALLOC(Source); + Property orig_makefile = makefile; + Dependency *dpp; + Dependency dp; + register int length; + wchar_t *previous_file_being_read = file_being_read; + int previous_line_number = line_number; + wchar_t previous_current_makefile[MAXPATHLEN]; + Makefile_type save_makefile_type; + Name normalized_makefile_name; + register wchar_t *string_start; + register wchar_t *string_end; + + + + wchar_t * wcb = get_wstring(makefile_name->string_mb); + + if (max_include_depth++ >= 40) { + fatal(gettext("Too many nested include statements")); + } + if (makefile->body.makefile.contents != NULL) { + retmem(makefile->body.makefile.contents); + } + source->inp_buf = + source->inp_buf_ptr = + source->inp_buf_end = NULL; + source->error_converting = false; + makefile->body.makefile.contents = NULL; + makefile->body.makefile.size = 0; + if ((makefile_name->hash.length != 1) || + (wcb[0] != (int) hyphen_char)) { + if ((makefile->body.makefile.contents == NULL) && + (doname_it)) { + if (makefile_path == NULL) { + char *pfx = make_install_prefix(); + char *path; + + add_dir_to_path(".", + &makefile_path, + -1); + + // As regularly installed + asprintf(&path, "%s/../share/lib/make", pfx); + add_dir_to_path(path, &makefile_path, -1); + free(path); + + // Tools build + asprintf(&path, "%s/../../share/", pfx); + add_dir_to_path(path, &makefile_path, -1); + free(path); + + add_dir_to_path("/usr/share/lib/make", + &makefile_path, + -1); + add_dir_to_path("/etc/default", + &makefile_path, + -1); + + free(pfx); + } + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if (doname(makefile_name, true, false) == build_dont_know) { + /* Try normalized filename */ + string_start=get_wstring(makefile_name->string_mb); + for (string_end=string_start+1; *string_end != L'\0'; string_end++); + normalized_makefile_name=normalize_name(string_start, string_end - string_start); + if ((strcmp(makefile_name->string_mb, normalized_makefile_name->string_mb) == 0) || + (doname(normalized_makefile_name, true, false) == build_dont_know)) { + n = access_vroot(makefile_name->string_mb, + 4, + chase_path ? + makefile_path : NULL, + VROOT_DEFAULT); + if (n == 0) { + get_vroot_path((char **) NULL, + &path, + (char **) NULL); + if ((path[0] == (int) period_char) && + (path[1] == (int) slash_char)) { + path += 2; + } + MBSTOWCS(wcs_buffer, path); + makefile_name = GETNAME(wcs_buffer, + FIND_LENGTH); + } + } + retmem(string_start); + /* + * Commented out: retmem_mb(normalized_makefile_name->string_mb); + * We have to return this memory, but it seems to trigger a bug + * in dmake or in Sun C++ 5.7 compiler (it works ok if this code + * is compiled using Sun C++ 5.6). + */ + // retmem_mb(normalized_makefile_name->string_mb); + } + makefile_type = save_makefile_type; + } + source->string.free_after_use = false; + source->previous = NULL; + source->already_expanded = false; + /* Lock the file for read, but not when -n. */ + if (lock_makefile && + !do_not_exec_rule) { + + make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1); + (void) sprintf(make_state_lockfile, + "%s.lock", + make_state->string_mb); + (void) file_lock(make_state->string_mb, + make_state_lockfile, + (int *) &make_state_locked, + 0); + if(!make_state_locked) { + printf("-- NO LOCKING for read\n"); + retmem_mb(make_state_lockfile); + make_state_lockfile = 0; + return failed; + } + } + if (makefile->body.makefile.contents == NULL) { + save_makefile_type = makefile_type; + makefile_type = reading_nothing; + if ((doname_it) && + (doname(makefile_name, true, false) == build_failed)) { + if (complain) { + (void) fprintf(stderr, + gettext("%s: Couldn't make `%s'\n"), + getprogname(), + makefile_name->string_mb); + } + max_include_depth--; + makefile_type = save_makefile_type; + return failed; + } + makefile_type = save_makefile_type; + // + // Before calling exists() make sure that we have the right timestamp + // + makefile_name->stat.time = file_no_time; + + if (exists(makefile_name) == file_doesnt_exist) { + if (complain || + (makefile_name->stat.stat_errno != ENOENT)) { + if (must_exist) { + fatal(gettext("Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } else { + warning(gettext("Can't find `%s': %s"), + makefile_name->string_mb, + errmsg(makefile_name-> + stat.stat_errno)); + } + } + max_include_depth--; + if(make_state_locked && (make_state_lockfile != NULL)) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + retmem(wcb); + retmem_mb((char *)source); + return failed; + } + /* + * These values are the size and bytes of + * the MULTI-BYTE makefile. + */ + orig_makefile->body.makefile.size = + makefile->body.makefile.size = + source->bytes_left_in_file = + makefile_name->stat.size; + if (report_file) { + for (dpp = &makefiles_used; + *dpp != NULL; + dpp = &(*dpp)->next); + dp = ALLOC(Dependency); + dp->next = NULL; + dp->name = makefile_name; + dp->automatic = false; + dp->stale = false; + dp->built = false; + *dpp = dp; + } + source->fd = open_vroot(makefile_name->string_mb, + O_RDONLY, + 0, + NULL, + VROOT_DEFAULT); + if (source->fd < 0) { + if (complain || (errno != ENOENT)) { + if (must_exist) { + fatal(gettext("Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } else { + warning(gettext("Can't open `%s': %s"), + makefile_name->string_mb, + errmsg(errno)); + } + } + max_include_depth--; + return failed; + } + (void) fcntl(source->fd, F_SETFD, 1); + orig_makefile->body.makefile.contents = + makefile->body.makefile.contents = + source->string.text.p = + source->string.buffer.start = + ALLOC_WC((int) (makefile_name->stat.size + 2)); + if (makefile_type == reading_cpp_file) { + forget_after_parse = true; + } + source->string.text.end = source->string.text.p; + source->string.buffer.end = + source->string.text.p + makefile_name->stat.size; + } else { + /* Do we ever reach here? */ + source->fd = -1; + source->string.text.p = + source->string.buffer.start = + makefile->body.makefile.contents; + source->string.text.end = + source->string.buffer.end = + source->string.text.p + makefile->body.makefile.size; + source->bytes_left_in_file = + makefile->body.makefile.size; + } + file_being_read = wcb; + } else { + char *stdin_text_p; + char *stdin_text_end; + char *stdin_buffer_start; + char *stdin_buffer_end; + char *p_mb; + int num_mb_chars; + size_t num_wc_chars; + + MBSTOWCS(wcs_buffer, "Standard in"); + makefile_name = GETNAME(wcs_buffer, FIND_LENGTH); + /* + * Memory to read standard in, then convert it + * to wide char strings. + */ + stdin_buffer_start = + stdin_text_p = getmem(length = 1024); + stdin_buffer_end = stdin_text_p + length; + MBSTOWCS(wcs_buffer, "standard input"); + file_being_read = (wchar_t *) wcsdup(wcs_buffer); + line_number = 0; + while ((n = read(fileno(stdin), + stdin_text_p, + length)) > 0) { + length -= n; + stdin_text_p += n; + if (length == 0) { + p_mb = getmem(length = 1024 + + (stdin_buffer_end - + stdin_buffer_start)); + (void) strncpy(p_mb, + stdin_buffer_start, + (stdin_buffer_end - + stdin_buffer_start)); + retmem_mb(stdin_buffer_start); + stdin_text_p = p_mb + + (stdin_buffer_end - stdin_buffer_start); + stdin_buffer_start = p_mb; + stdin_buffer_end = + stdin_buffer_start + length; + length = 1024; + } + } + if (n < 0) { + fatal(gettext("Error reading standard input: %s"), + errmsg(errno)); + } + stdin_text_p = stdin_buffer_start; + stdin_text_end = stdin_buffer_end - length; + num_mb_chars = stdin_text_end - stdin_text_p; + + /* + * Now, convert the sequence of multibyte chars into + * a sequence of corresponding wide character codes. + */ + source->string.free_after_use = false; + source->previous = NULL; + source->bytes_left_in_file = 0; + source->fd = -1; + source->already_expanded = false; + source->string.buffer.start = + source->string.text.p = ALLOC_WC(num_mb_chars + 1); + source->string.buffer.end = + source->string.text.p + num_mb_chars; + num_wc_chars = mbstowcs(source->string.text.p, + stdin_text_p, + num_mb_chars); + if ((int) num_wc_chars >= 0) { + source->string.text.end = + source->string.text.p + num_wc_chars; + } + (void) retmem_mb(stdin_text_p); + } + line_number = 1; + if (trace_reader) { + (void) printf(gettext(">>>>>>>>>>>>>>>> Reading makefile %s\n"), + makefile_name->string_mb); + } + parse_makefile(makefile_name, source); + if (trace_reader) { + (void) printf(gettext(">>>>>>>>>>>>>>>> End of makefile %s\n"), + makefile_name->string_mb); + } + if(file_being_read) { + retmem(file_being_read); + } + file_being_read = previous_file_being_read; + line_number = previous_line_number; + makefile_type = reading_nothing; + max_include_depth--; + if (make_state_locked) { + /* Unlock .make.state. */ + unlink(make_state_lockfile); + make_state_locked = false; + retmem_mb(make_state_lockfile); + } + if (forget_after_parse) { + retmem(makefile->body.makefile.contents); + makefile->body.makefile.contents = NULL; + } + retmem_mb((char *)source); + return succeeded; +} + +/* + * parse_makefile(true_makefile_name, source) + * + * Strings are read from Sources. + * When macros are found, their values are represented by a + * Source that is pushed on a stack. At end of string + * (that is returned from GET_CHAR() as 0), the block is popped. + * + * Parameters: + * true_makefile_name The name of makefile we are parsing + * source The source block to read from + * + * Global variables used: + * do_not_exec_rule Is -n on? + * line_number The number of the current makefile line + * makefile_type What kind of makefile are we reading? + * empty_name The Name "" + */ +static void +parse_makefile(register Name true_makefile_name, register Source source) +{ +/* + char mb_buffer[MB_LEN_MAX]; + */ + register wchar_t *source_p; + register wchar_t *source_end; + register wchar_t *string_start; + wchar_t *string_end; + register Boolean macro_seen_in_string; + Boolean append; + String_rec name_string; + wchar_t name_buffer[STRING_BUFFER_LENGTH]; + register int distance; + register int paren_count; + int brace_count; + int char_number; + Cmd_line command; + Cmd_line command_tail; + Name macro_value; + + Name_vector_rec target; + Name_vector_rec depes; + Name_vector_rec extra_name_vector; + Name_vector current_names; + Name_vector extra_names = &extra_name_vector; + Name_vector nvp; + Boolean target_group_seen; + + register Reader_state state; + register Reader_state on_eoln_state; + register Separator separator; + + wchar_t buffer[4 * STRING_BUFFER_LENGTH]; + Source extrap; + + Boolean save_do_not_exec_rule = do_not_exec_rule; + Name makefile_name; + + static Name sh_name; + static Name shell_name; + int i; + + static wchar_t include_space[10]; + static wchar_t include_tab[10]; + int tmp_bytes_left_in_string; + Boolean tmp_maybe_include = false; + int emptycount = 0; + Boolean first_target; + + String_rec include_name; + wchar_t include_buffer[STRING_BUFFER_LENGTH]; + + target.next = depes.next = NULL; + /* Move some values from their struct to register declared locals */ + CACHE_SOURCE(0); + + start_new_line: + /* + * Read whitespace on old line. Leave pointer on first char on + * next line. + */ + first_target = true; + on_eoln_state = exit_state; +/* + for (WCTOMB(mb_buffer, GET_CHAR()); + 1; + source_p++, WCTOMB(mb_buffer, GET_CHAR())) + switch (mb_buffer[0]) { + */ + for (char_number=0; 1; source_p++,char_number++) switch (GET_CHAR()) { + case nul_char: + /* End of this string. Pop it and return to the previous one */ + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + end_of_line: + source_p++; + if (source->fd >= 0) { + line_number++; + } + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + /* Go back to the top of this loop */ + goto start_new_line; + case newline_char: + case numbersign_char: + case dollar_char: + case space_char: + case tab_char: + /* + * Go back to the top of this loop since the + * new line does not start with a regular char. + */ + goto start_new_line; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + case space_char: + if (char_number == 0) + line_started_with_space=line_number; + case tab_char: + /* Whitespace. Just keep going in this loop */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + /* Comments can be continued */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + /* + * After we skip the comment we go to + * the end of line handler since end of + * line terminates comments. + */ + goto end_of_line; + } + } + case dollar_char: + /* Macro reference */ + if (source->already_expanded) { + /* + * If we are reading from the expansion of a + * macro we already expanded everything enough. + */ + goto start_new_line_no_skip; + } + /* + * Expand the value and push the Source on the stack of + * things being read. + */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* We found the first proper char on the new line */ + goto start_new_line_no_skip; + } + + /* + * We found the first normal char (one that starts an identifier) + * on the newline. + */ +start_new_line_no_skip: + /* Inspect that first char to see if it maybe is special anyway */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + goto start_new_line_no_skip; + case newline_char: + /* Just in case */ + goto start_new_line; + case exclam_char: + /* Evaluate the line before it is read */ + string_start = source_p + 1; + macro_seen_in_string = false; + /* Stuff the line in a string so we can eval it. */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_1; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK( + name_string, name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_1: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + if (*source_p == (int) nul_char) { + source_p++; + } + /* Eval the macro */ + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + /* And push the value */ + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto line_evald; + } + } + default: + goto line_evald; + } + + /* We now have a line we can start reading */ + line_evald: + if (source == NULL) { + GOTO_STATE(exit_state); + } + /* Check if this is an include command */ + if ((makefile_type == reading_makefile) && + !source->already_expanded) { + if (include_space[0] == (int) nul_char) { + MBSTOWCS(include_space, "include "); + MBSTOWCS(include_tab, "include\t"); + } + if ((IS_WEQUALN(source_p, include_space, 8)) || + (IS_WEQUALN(source_p, include_tab, 8))) { + source_p += 7; + if (iswspace(*source_p)) { + Makefile_type save_makefile_type; + wchar_t *name_start; + int name_length; + + /* + * Yes, this is an include. + * Skip spaces to get to the filename. + */ + while (iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + string_start = source_p; + /* Find the end of the filename */ + macro_seen_in_string = false; + while (!iswspace(*source_p) || + (*source_p == (int) nul_char)) { + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + + default: + source_p++; + break; + } + } + + source->string.text.p = source_p; + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + name_start = name_string.buffer.start; + name_length = name_string.text.p - name_start; + } else { + name_start = string_start; + name_length = source_p - string_start; + } + + /* Strip "./" from the head of the name */ + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + /* if include file name is surrounded by double quotes */ + if ((name_start[0] == (int) doublequote_char) && + (name_start[name_length - 1] == (int) doublequote_char)) { + name_start += 1; + name_length -= 2; + + /* if name does not begin with a slash char */ + if (name_start[0] != (int) slash_char) { + if ((name_start[0] == (int) period_char) && + (name_start[1] == (int) slash_char)) { + name_start += 2; + name_length -= 2; + } + + INIT_STRING_FROM_STACK(include_name, include_buffer); + APPEND_NAME(true_makefile_name, + &include_name, + true_makefile_name->hash.length); + + wchar_t *slash = wcsrchr(include_name.buffer.start, (int) slash_char); + if (slash != NULL) { + include_name.text.p = slash + 1; + append_string(name_start, + &include_name, + name_length); + + name_start = include_name.buffer.start; + name_length = include_name.text.p - name_start; + } + } + } + + /* Even when we run -n we want to create makefiles */ + do_not_exec_rule = false; + makefile_name = GETNAME(name_start, name_length); + if (makefile_name->dollar) { + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + wchar_t *p; + wchar_t *q; + + INIT_STRING_FROM_STACK(destination, buffer); + expand_value(makefile_name, + &destination, + false); + for (p = destination.buffer.start; + (*p != (int) nul_char) && iswspace(*p); + p++); + for (q = p; + (*q != (int) nul_char) && !iswspace(*q); + q++); + makefile_name = GETNAME(p, q-p); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } + source_p++; + UNCACHE_SOURCE(); + /* Read the file */ + save_makefile_type = makefile_type; + if (read_simple_file(makefile_name, + true, + true, + true, + false, + true, + false) == failed) { + fatal_reader(gettext("Read of include file `%s' failed"), + makefile_name->string_mb); + } + makefile_type = save_makefile_type; + do_not_exec_rule = save_do_not_exec_rule; + CACHE_SOURCE(0); + goto start_new_line; + } else { + source_p -= 7; + } + } else { + /* Check if the word include was split across 8K boundary. */ + + tmp_bytes_left_in_string = source->string.text.end - source_p; + if (tmp_bytes_left_in_string < 8) { + tmp_maybe_include = false; + if (IS_WEQUALN(source_p, + include_space, + tmp_bytes_left_in_string)) { + tmp_maybe_include = true; + } + if (tmp_maybe_include) { + GET_NEXT_BLOCK(source); + tmp_maybe_include = false; + goto line_evald; + } + } + } + } + + /* Reset the status in preparation for the new line */ + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + for (nvp = &depes; nvp != NULL; nvp = nvp->next) { + nvp->used = 0; + } + target_group_seen = false; + command = command_tail = NULL; + macro_value = NULL; + append = false; + current_names = ⌖ + SET_STATE(scan_name_state); + on_eoln_state = illegal_eoln_state; + separator = none_seen; + + /* The state machine starts here */ + enter_state: + while (1) switch (state) { + +/**************************************************************** + * Scan name state + */ +case scan_name_state: + /* Scan an identifier. We skip over chars until we find a break char */ + /* First skip white space. */ + for (; 1; source_p++) switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + /* We found the end of the line. */ + /* Do postprocessing or return error */ + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Continuation */ + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } else { + source_p--; + } + break; + case tab_char: + case space_char: + /* Whitespace is skipped */ + break; + case numbersign_char: + /* Comment. Skip over it */ + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + } + } + case dollar_char: + /* Macro reference. Expand and push value */ + if (source->already_expanded) { + goto scan_name; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + break; + default: + /* End of white space */ + goto scan_name; + } + + /* First proper identifier character */ + scan_name: + + string_start = source_p; + paren_count = brace_count = 0; + macro_seen_in_string = false; + resume_name_scan: + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + /* Save what we have seen so far of the identifier */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Get more text to read */ + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case newline_char: + if (paren_count > 0) { + fatal_reader(gettext("Unmatched `(' on line")); + } + if (brace_count > 0) { + fatal_reader(gettext("Unmatched `{' on line")); + } + source_p++; + /* Enter name */ + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p - 1, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + /* Do postprocessing or return error */ + if (source->fd >= 0) { + line_number++; + } + GOTO_STATE(on_eoln_state); + case backslash_char: + /* Check if this is a quoting backslash */ + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + macro_seen_in_string = true; + } + append_string(string_start, + &name_string, + source_p - string_start); + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + *source_p = (int) space_char; + string_start = source_p; + goto resume_name_scan; + } else { + string_start = source_p; + break; + } + break; + case numbersign_char: + if (paren_count + brace_count > 0) { + break; + } + fatal_reader(gettext("Unexpected comment seen")); + case dollar_char: + if (source->already_expanded) { + break; + } + /* Save the identifier so far */ + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + /* Eval and push the macro */ + source_p++; + UNCACHE_SOURCE(); + { + Source t = + (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(1); + string_start = source_p + 1; + break; + case parenleft_char: + paren_count++; + break; + case parenright_char: + if (--paren_count < 0) { + fatal_reader(gettext("Unmatched `)' on line")); + } + break; + case braceleft_char: + brace_count++; + break; + case braceright_char: + if (--brace_count < 0) { + fatal_reader(gettext("Unmatched `}' on line")); + } + break; + case ampersand_char: + case greater_char: + case bar_char: + if (paren_count + brace_count == 0) { + source_p++; + } + /* Fall into */ + case tab_char: + case space_char: + if (paren_count + brace_count > 0) { + break; + } + current_names = enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) sizeof (Name_vector_rec)); + } + goto enter_state; + case colon_char: + if (paren_count + brace_count > 0) { + break; + } + if (separator == conditional_seen) { + break; + } +/** POSIX **/ +#if 0 + if(posix) { + emptycount = 0; + } +#endif +/** END POSIX **/ + /* End of the target list. We now start reading */ + /* dependencies or a conditional assignment */ + if (separator != none_seen) { + fatal_reader(gettext("Extra `:', `::', or `:=' on dependency line")); + } + /* Enter the last target */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + /* Check if it is ":" "::" or ":=" */ + scan_colon_label: + switch (*++source_p) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + goto scan_colon_label; + case equal_char: + if(svr4) { + fatal_reader(gettext("syntax error")); + } + separator = conditional_seen; + source_p++; + current_names = &depes; + GOTO_STATE(scan_name_state); + case colon_char: + separator = two_colon; + source_p++; + break; + default: + separator = one_colon; + } + current_names = &depes; + on_eoln_state = enter_dependencies_state; + GOTO_STATE(scan_name_state); + case semicolon_char: + if (paren_count + brace_count > 0) { + break; + } + /* End of reading names. Start reading the rule */ + if ((separator != one_colon) && + (separator != two_colon)) { + fatal_reader(gettext("Unexpected command seen")); + } + /* Enter the last dependency */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + source_p++; + /* Make sure to enter a rule even if the is */ + /* no text here */ + command = command_tail = ALLOC(Cmd_line); + command->next = NULL; + command->command_line = empty_name; + command->make_refd = false; + command->ignore_command_dependency = false; + command->assign = false; + command->ignore_error = false; + command->silent = false; + + GOTO_STATE(scan_command_state); + case plus_char: + /* + ** following code drops the target separator plus char if it starts + ** a line. + */ + if(first_target && !macro_seen_in_string && + source_p == string_start) { + for (; 1; source_p++) + switch (GET_CHAR()) { + case nul_char: + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case plus_char: + source_p++; + while (*source_p == (int) nul_char) { + if (source_p != string_start) { + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + } + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p == (int) tab_char || + *source_p == (int) space_char) { + macro_seen_in_string = false; + string_start = source_p + 1; + } else { + goto resume_name_scan; + } + break; + case tab_char: + case space_char: + string_start = source_p + 1; + break; + default: + goto resume_name_scan; + } + } + if (paren_count + brace_count > 0) { + break; + } + /* We found "+=" construct */ + if (source_p != string_start) { + /* "+" is not a break char. */ + /* Ignore it if it is part of an identifier */ + source_p++; + goto resume_name_scan; + } + /* Make sure the "+" is followed by a "=" */ + scan_append: + switch (*++source_p) { + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + source_p--; + string_start = source_p; + if (source == NULL) { + GOTO_STATE(illegal_eoln_state); + } + goto scan_append; + case equal_char: + if(!svr4) { + append = true; + } else { + fatal_reader(gettext("Must be a separator on rules")); + } + break; + default: + /* The "+" just starts a regular name. */ + /* Start reading that name */ + goto resume_name_scan; + } + /* Fall into */ + case equal_char: + if (paren_count + brace_count > 0) { + break; + } + /* We found macro assignment. */ + /* Check if it is legal and if it is appending */ + switch (separator) { + case none_seen: + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + case conditional_seen: + on_eoln_state = enter_conditional_state; + break; + default: + /* Reader must special check for "MACRO:sh=" */ + /* notation */ + if (sh_name == NULL) { + MBSTOWCS(wcs_buffer, "sh"); + sh_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, "shell"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + } + + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start + ); + + if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == sh_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == sh_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(sh_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } else if ( (((target.used == 1) && + (depes.used == 1) && + (depes.names[0] == shell_name)) || + ((target.used == 1) && + (depes.used == 0) && + (separator == one_colon) && + (GETNAME(name_string.buffer.start,FIND_LENGTH) == shell_name))) && + (!svr4)) { + String_rec macro_name; + wchar_t buffer[100]; + + INIT_STRING_FROM_STACK(macro_name, + buffer); + APPEND_NAME(target.names[0], + ¯o_name, + FIND_LENGTH); + append_char((int) colon_char, + ¯o_name); + APPEND_NAME(shell_name, + ¯o_name, + FIND_LENGTH); + target.names[0] = + GETNAME(macro_name.buffer.start, + FIND_LENGTH); + separator = equal_seen; + on_eoln_state = enter_equal_state; + break; + } + if(svr4) { + fatal_reader(gettext("syntax error")); + } + else { + fatal_reader(gettext("Macro assignment on dependency line")); + } + } + if (append) { + source_p--; + } + /* Enter the macro name */ + if ((string_start != source_p) || + macro_seen_in_string) { + current_names = + enter_name(&name_string, + macro_seen_in_string, + string_start, + source_p, + current_names, + &extra_names, + &target_group_seen); + first_target = false; + if (extra_names == NULL) { + extra_names = (Name_vector) + alloca((int) + sizeof (Name_vector_rec)); + } + } + if (append) { + source_p++; + } + macro_value = NULL; + source_p++; + distance = 0; + /* Skip whitespace to the start of the value */ + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + if (*source_p != (int) newline_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_char((int) + backslash_char, + &name_string); + append_char(*source_p, + &name_string); + string_start = source_p+1; + goto macro_value_start; + } else { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + case numbersign_char: + string_start = source_p; + goto macro_value_end; + case tab_char: + case space_char: + break; + default: + string_start = source_p; + goto macro_value_start; + } + } + macro_value_start: + /* Find the end of the value */ + for (; 1; source_p++) { + if (distance != 0) { + *source_p = *(source_p + distance); + } + switch (GET_CHAR()) { + case nul_char: + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + break; + case backslash_char: + source_p++; + if (distance != 0) { + *source_p = + *(source_p + distance); + } + if (*source_p == (int) nul_char) { + if (!macro_seen_in_string) { + macro_seen_in_string = + true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + +/* BID_1225561 */ + *(source_p - 1) = (int) space_char; + append_string(string_start, + &name_string, + source_p - + string_start - 1); + GET_NEXT_BLOCK(source); + string_start = source_p; + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (distance != 0) { + *source_p = + *(source_p + + distance); + } + if (*source_p == (int) newline_char) { + append_char((int) space_char, &name_string); + } else { + append_char((int) backslash_char, &name_string); + } +/****************/ + } + if (*source_p == (int) newline_char) { + source_p--; + line_number++; + distance++; + *source_p = (int) space_char; + while ((*(source_p + + distance + 1) == + (int) tab_char) || + (*(source_p + + distance + 1) == + (int) space_char)) { + distance++; + } + } + break; + case newline_char: + case numbersign_char: + goto macro_value_end; + } + } + macro_value_end: + /* Complete the value in the string */ + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + if (name_string.buffer.start != name_string.text.p) { + macro_value = + GETNAME(name_string.buffer.start, + FIND_LENGTH); + } + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + for (; distance > 0; distance--) { + *source_p++ = (int) space_char; + } + GOTO_STATE(on_eoln_state); + } + } + +/**************************************************************** + * enter dependencies state + */ + case enter_dependencies_state: + enter_dependencies_label: +/* Expects pointer on first non whitespace char after last dependency. (On */ +/* next line.) We end up here after having read a "targets : dependencies" */ +/* line. The state checks if there is a rule to read and if so dispatches */ +/* to scan_command_state scan_command_state reads one rule line and the */ +/* returns here */ + + /* First check if the first char on the next line is special */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK(source); + if (source == NULL) { + break; + } + goto enter_dependencies_label; + case exclam_char: + /* The line should be evaluate before it is read */ + macro_seen_in_string = false; + string_start = source_p + 1; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case newline_char: + goto eoln_2; + case nul_char: + if (source->fd > 0) { + if (!macro_seen_in_string) { + macro_seen_in_string = true; + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + break; + } + eoln_2: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + extrap = (Source) + alloca((int) sizeof (Source_rec)); + extrap->string.buffer.start = NULL; + extrap->inp_buf = + extrap->inp_buf_ptr = + extrap->inp_buf_end = NULL; + extrap->error_converting = false; + expand_value(GETNAME(name_string.buffer.start, + FIND_LENGTH), + &extrap->string, + false); + if (name_string.free_after_use) { + retmem(name_string.buffer.start); + } + UNCACHE_SOURCE(); + extrap->string.text.p = + extrap->string.buffer.start; + extrap->fd = -1; + extrap->previous = source; + source = extrap; + CACHE_SOURCE(0); + goto enter_dependencies_label; + } + } + case dollar_char: + if (source->already_expanded) { + break; + } + source_p++; + UNCACHE_SOURCE(); + { + Source t = (Source) alloca((int) sizeof (Source_rec)); + source = push_macro_value(t, + buffer, + sizeof buffer, + source); + } + CACHE_SOURCE(0); + goto enter_dependencies_label; + case numbersign_char: + if (makefile_type != reading_makefile) { + source_p++; + GOTO_STATE(scan_command_state); + } + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + } + source_p--; + break; + case backslash_char: + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + if (source->error_converting) { + // Illegal byte sequence - skip its first byte + source->inp_buf_ptr++; + source_p--; + break; + } + } + if(*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + } + break; + case newline_char: + source_p++; + if (source->fd >= 0) { + line_number++; + } + goto enter_dependencies_label; + } + } + + case tab_char: + GOTO_STATE(scan_command_state); + } + + /* We read all the command lines for the target/dependency line. */ + /* Enter the stuff */ + enter_target_groups_and_dependencies( &target, &depes, command, + separator, target_group_seen); + + goto start_new_line; + +/**************************************************************** + * scan command state + */ +case scan_command_state: + /* We need to read one rule line. Do that and return to */ + /* the enter dependencies state */ + string_start = source_p; + macro_seen_in_string = false; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case backslash_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + append_char((int) backslash_char, &name_string); + append_char(*source_p, &name_string); + if (*source_p == (int) newline_char) { + if (source->fd >= 0) { + line_number++; + } + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + if (*source_p == (int) tab_char) { + source_p++; + } + } else { + if (*++source_p == (int) nul_char) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + string_start = source_p; + goto command_newline; + } + } + } + string_start = source_p; + if ((*source_p == (int) newline_char) || + (*source_p == (int) backslash_char) || + (*source_p == (int) nul_char)) { + source_p--; + } + break; + case newline_char: + command_newline: + if ((string_start != source_p) || + macro_seen_in_string) { + if (macro_seen_in_string) { + append_string(string_start, + &name_string, + source_p - string_start); + string_start = + name_string.buffer.start; + string_end = name_string.text.p; + } else { + string_end = source_p; + } + while ((*string_start != (int) newline_char) && + iswspace(*string_start)){ + string_start++; + } + if ((string_end > string_start) || + (makefile_type == reading_statefile)) { + if (command_tail == NULL) { + command = + command_tail = + ALLOC(Cmd_line); + } else { + command_tail->next = + ALLOC(Cmd_line); + command_tail = + command_tail->next; + } + command_tail->next = NULL; + command_tail->make_refd = false; + command_tail->ignore_command_dependency = false; + command_tail->assign = false; + command_tail->ignore_error = false; + command_tail->silent = false; + command_tail->command_line = + GETNAME(string_start, + string_end - string_start); + if (macro_seen_in_string && + name_string.free_after_use) { + retmem(name_string. + buffer.start); + } + } + } + do { + if ((source != NULL) && (source->fd >= 0)) { + line_number++; + } + if ((source != NULL) && + (*++source_p == (int) nul_char)) { + GET_NEXT_BLOCK(source); + if (source == NULL) { + GOTO_STATE(on_eoln_state); + } + } + } while (*source_p == (int) newline_char); + + GOTO_STATE(enter_dependencies_state); + case nul_char: + if (!macro_seen_in_string) { + INIT_STRING_FROM_STACK(name_string, + name_buffer); + } + append_string(string_start, + &name_string, + source_p - string_start); + macro_seen_in_string = true; + GET_NEXT_BLOCK(source); + string_start = source_p; + source_p--; + if (source == NULL) { + GOTO_STATE(enter_dependencies_state); + } + break; + } + } + +/**************************************************************** + * enter equal state + */ +case enter_equal_state: + if (target.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + enter_equal(target.names[0], macro_value, append); + goto start_new_line; + +/**************************************************************** + * enter conditional state + */ +case enter_conditional_state: + if (depes.used != 1) { + GOTO_STATE(poorly_formed_macro_state); + } + for (nvp = ⌖ nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + enter_conditional(nvp->names[i], + depes.names[0], + macro_value, + append); + } + } + goto start_new_line; + +/**************************************************************** + * Error states + */ +case illegal_bytes_state: + fatal_reader(gettext("Invalid byte sequence")); +case illegal_eoln_state: + if (line_number > 1) { + if (line_started_with_space == (line_number - 1)) { + line_number--; + fatal_reader(gettext("Unexpected end of line seen\n\t*** missing separator (did you mean TAB instead of 8 spaces?)")); + } + } + fatal_reader(gettext("Unexpected end of line seen")); +case poorly_formed_macro_state: + fatal_reader(gettext("Badly formed macro assignment")); +case exit_state: + return; +default: + fatal_reader(gettext("Internal error. Unknown reader state")); +} +} + +/* + * push_macro_value(bp, buffer, size, source) + * + * Macro and function that evaluates one macro + * and makes the reader read from the value of it + * + * Return value: + * The source block to read the macro from + * + * Parameters: + * bp The new source block to fill in + * buffer Buffer to read from + * size size of the buffer + * source The old source block + * + * Global variables used: + */ +static Source +push_macro_value(register Source bp, register wchar_t *buffer, int size, register Source source) +{ + bp->string.buffer.start = bp->string.text.p = buffer; + bp->string.text.end = NULL; + bp->string.buffer.end = buffer + (size/SIZEOFWCHAR_T); + bp->string.free_after_use = false; + bp->inp_buf = + bp->inp_buf_ptr = + bp->inp_buf_end = NULL; + bp->error_converting = false; + expand_macro(source, &bp->string, (wchar_t *) NULL, false); + bp->string.text.p = bp->string.buffer.start; + + /* 4209588: 'make' doesn't understand a macro with whitespaces in the head as target. + * strip whitespace from the begining of the macro value + */ + while (iswspace(*bp->string.text.p)) { + bp->string.text.p++; + } + + bp->fd = -1; + bp->already_expanded = true; + bp->previous = source; + return bp; +} + +/* + * enter_target_groups_and_dependencies(target, depes, command, separator, + * target_group_seen) + * + * Parameters: + * target Structure that shows the target(s) on the line + * we are currently parsing. This can looks like + * target1 .. targetN : dependencies + * commands + * or + * target1 + .. + targetN : dependencies + * commands + * depes Dependencies + * command Points to the command(s) to be executed for + * this target. + * separator : or :: or := + * target_group_seen Set if we have target1 + .. + targetN + * + * + * After reading the command lines for a target, this routine + * is called to setup the dependencies and the commands for it. + * If the target is a % pattern or part of a target group, then + * the appropriate routines are called. + */ + +void +enter_target_groups_and_dependencies(Name_vector target, Name_vector depes, Cmd_line command, Separator separator, Boolean target_group_seen) +{ + int i; + Boolean reset= true; + Chain target_group_member; + Percent percent_ptr; + + for (; target != NULL; target = target->next) { + for (i = 0; i < target->used; i++) { + if (target->names[i] != NULL) { + if (target_group_seen) { + target_group_member = + find_target_groups(target, i, reset); + if(target_group_member == NULL) { + fatal_reader(gettext("Unexpected '+' on dependency line")); + } + } + reset = false; + + /* If we saw it in the makefile it must be + * a file */ + target->names[i]->stat.is_file = true; + /* Make sure that we use dependencies + * entered for makefiles */ + target->names[i]->state = build_dont_know; + + /* If the target is special we delegate + * the processing */ + if (target->names[i]->special_reader + != no_special) { + special_reader(target->names[i], + depes, + command); + } + /* Check if this is a "a%b : x%y" type rule */ + else if (target->names[i]->percent) { + percent_ptr = + enter_percent(target->names[i], + target->target_group[i], + depes, command); + if (target_group_seen) { + target_group_member->percent_member = + percent_ptr; + } + } else if (target->names[i]->dollar) { + enter_dyntarget(target->names[i]); + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } else { + if (target_group_seen) { + target_group_member->percent_member = + NULL; + } + + enter_dependencies + (target->names[i], + target->target_group[i], + depes, + command, + separator); + } + } + } + } +} + + diff --git a/bin/read2.cc b/bin/read2.cc new file mode 100644 index 0000000..50324de --- /dev/null +++ b/bin/read2.cc @@ -0,0 +1,1898 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include +#include /* sh_command2string() */ +#include /* expand_value() */ +#include /* retmem() */ +#include /* va_list, va_start(), va_end() */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +static Boolean built_last_make_run_seen; + +/* + * File table of contents + */ +static Name_vector enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names); +extern Name normalize_name(register wchar_t *name_string, register int length); +static void read_suffixes_list(register Name_vector depes); +static void make_relative(wchar_t *to, wchar_t *result); +static void print_rule(register Cmd_line command); +static void sh_transform(Name *name, Name *value); + + +/* + * enter_name(string, tail_present, string_start, string_end, + * current_names, extra_names, target_group_seen) + * + * Take one string and enter it as a name. The string is passed in + * two parts. A make string and possibly a C string to append to it. + * The result is stuffed in the vector current_names. + * extra_names points to a vector that is used if current_names overflows. + * This is allocad in the calling routine. + * Here we handle the "lib.a[members]" notation. + * + * Return value: + * The name vector that was used + * + * Parameters: + * tail_present Indicates if both C and make string was passed + * string_start C string + * string_end Pointer to char after last in C string + * string make style string with head of name + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * target_group_seen Pointer to boolean that is set if "+" is seen + * + * Global variables used: + * makefile_type When we read a report file we normalize paths + * plus Points to the Name "+" + */ + +Name_vector +enter_name(String string, Boolean tail_present, register wchar_t *string_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen) +{ + Name name; + register wchar_t *cp; + wchar_t ch; + + /* If we were passed a separate tail of the name we append it to the */ + /* make string with the rest of it */ + if (tail_present) { + append_string(string_start, string, string_end - string_start); + string_start = string->buffer.start; + string_end = string->text.p; + } + ch = *string_end; + *string_end = (int) nul_char; + /* + * Check if there are any ( or [ that are not prefixed with $. + * If there are, we have to deal with the lib.a(members) format. + */ + for (cp = (wchar_t *) wcschr(string_start, (int) parenleft_char); + cp != NULL; + cp = (wchar_t *) wcschr(cp + 1, (int) parenleft_char)) { + if (*(cp - 1) != (int) dollar_char) { + *string_end = ch; + return enter_member_name(string_start, + cp, + string_end, + current_names, + extra_names); + } + } + *string_end = ch; + + if (makefile_type == reading_cpp_file) { + /* Remove extra ../ constructs if we are reading from a report file */ + name = normalize_name(string_start, string_end - string_start); + } else { + /* + * /tolik, fix bug 1197477/ + * Normalize every target name before entering. + * ..//obj/a.o and ../obj//a.o are not two different targets. + * There is only one target ../obj/a.o + */ + /*name = GETNAME(string_start, string_end - string_start);*/ + name = normalize_name(string_start, string_end - string_start); + } + + /* Internalize the name. Detect the name "+" (target group here) */ +if(current_names->used != 0 && current_names->names[current_names->used-1] == plus) { + if(name == plus) { + return current_names; + } +} + /* If the current_names vector is full we patch in the one from */ + /* extra_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + current_names->next = *extra_names; + *extra_names = NULL; + current_names = current_names->next; + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + if (name == plus) { + *target_group_seen = true; + } + if (tail_present && string->free_after_use) { + retmem(string->buffer.start); + } + return current_names; +} + +/* + * enter_member_name(lib_start, member_start, string_end, + * current_names, extra_names) + * + * A string has been found to contain member names. + * (The "lib.a[members]" and "lib.a(members)" notation) + * Handle it pretty much as enter_name() does for simple names. + * + * Return value: + * The name vector that was used + * + * Parameters: + * lib_start Points to the of start of "lib.a(member.o)" + * member_start Points to "member.o" from above string. + * string_end Points to char after last of above string. + * current_names Vector to deposit the name in + * extra_names Where to get next name vector if we run out + * + * Global variables used: + */ +static Name_vector +enter_member_name(register wchar_t *lib_start, register wchar_t *member_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names) +{ + register Boolean entry = false; + wchar_t buffer[STRING_BUFFER_LENGTH]; + Name lib; + Name member; + Name name; + Property prop; + wchar_t *memberp; + wchar_t *q; + register int paren_count; + register Boolean has_dollar; + register wchar_t *cq; + Name long_member_name = NULL; + + /* Internalize the name of the library */ + lib = GETNAME(lib_start, member_start - lib_start); + lib->is_member = true; + member_start++; + if (*member_start == (int) parenleft_char) { + /* This is really the "lib.a((entries))" format */ + entry = true; + member_start++; + } + /* Move the library name to the buffer where we intend to build the */ + /* "lib.a(member)" for each member */ + (void) wcsncpy(buffer, lib_start, member_start - lib_start); + memberp = buffer + (member_start-lib_start); + while (1) { + long_member_name = NULL; + /* Skip leading spaces */ + for (; + (member_start < string_end) && iswspace(*member_start); + member_start++); + /* Find the end of the member name. Allow nested (). Detect $*/ + for (cq = memberp, has_dollar = false, paren_count = 0; + (member_start < string_end) && + ((*member_start != (int) parenright_char) || + (paren_count > 0)) && + !iswspace(*member_start); + *cq++ = *member_start++) { + switch (*member_start) { + case parenleft_char: + paren_count++; + break; + case parenright_char: + paren_count--; + break; + case dollar_char: + has_dollar = true; + } + } + /* Internalize the member name */ + member = GETNAME(memberp, cq - memberp); + *cq = 0; + if ((q = (wchar_t *) wcsrchr(memberp, (int) slash_char)) == NULL) { + q = memberp; + } + if ((cq - q > (int) ar_member_name_len) && + !has_dollar) { + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + long_member_name = GETNAME(buffer, cq - buffer); + cq = q + (int) ar_member_name_len; + } + *cq++ = (int) parenright_char; + if (entry) { + *cq++ = (int) parenright_char; + } + /* Internalize the "lib.a(member)" notation for this member */ + name = GETNAME(buffer, cq - buffer); + name->is_member = lib->is_member; + if (long_member_name != NULL) { + prop = append_prop(name, long_member_name_prop); + name->has_long_member_name = true; + prop->body.long_member_name.member_name = + long_member_name; + } + /* And add the member prop */ + prop = append_prop(name, member_prop); + prop->body.member.library = lib; + if (entry) { + /* "lib.a((entry))" notation */ + prop->body.member.entry = member; + prop->body.member.member = NULL; + } else { + /* "lib.a(member)" Notation */ + prop->body.member.entry = NULL; + prop->body.member.member = member; + } + /* Handle overflow of current_names */ + if (current_names->used == VSIZEOF(current_names->names)) { + if (current_names->next != NULL) { + current_names = current_names->next; + } else { + if (*extra_names == NULL) { + current_names = + current_names->next = + ALLOC(Name_vector); + } else { + current_names = + current_names->next = + *extra_names; + *extra_names = NULL; + } + current_names->used = 0; + current_names->next = NULL; + } + } + current_names->target_group[current_names->used] = NULL; + current_names->names[current_names->used++] = name; + while (iswspace(*member_start)) { + member_start++; + } + /* Check if there are more members */ + if ((*member_start == (int) parenright_char) || + (member_start >= string_end)) { + return current_names; + } + } + /* NOTREACHED */ +} + +/* + * normalize_name(name_string, length) + * + * Take a namestring and remove redundant ../, // and ./ constructs + * + * Return value: + * The normalized name + * + * Parameters: + * name_string Path string to normalize + * length Length of that string + * + * Global variables used: + * dot The Name ".", compared against + * dotdot The Name "..", compared against + */ +Name +normalize_name(register wchar_t *name_string, register int length) +{ + static Name dotdot; + register wchar_t *string = ALLOC_WC(length + 1); + register wchar_t *string2; + register wchar_t *cdp; + wchar_t *current_component; + Name name; + register int count; + + if (dotdot == NULL) { + MBSTOWCS(wcs_buffer, ".."); + dotdot = GETNAME(wcs_buffer, FIND_LENGTH); + } + + /* + * Copy string removing ./ and //. + * First strip leading ./ + */ + while ((length > 1) && + (name_string[0] == (int) period_char) && + (name_string[1] == (int) slash_char)) { + name_string += 2; + length -= 2; + while ((length > 0) && (name_string[0] == (int) slash_char)) { + name_string++; + length--; + } + } + /* Then copy the rest of the string removing /./ & // */ + cdp = string; + while (length > 0) { + if (((length > 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) slash_char)) || + ((length == 2) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char))) { + name_string += 2; + length -= 2; + continue; + } + if ((length > 1) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) slash_char)) { + name_string++; + length--; + continue; + } + *cdp++ = *name_string++; + length--; + } + *cdp = (int) nul_char; + /* + * Now scan for /../ and remove such combinations iff + * is not another .. + * Each time something is removed, the whole process is restarted. + */ +removed_one: + name_string = string; + string2 = name_string; /*save for free*/ + current_component = + cdp = + string = + ALLOC_WC((length = wcslen(name_string)) + 1); + while (length > 0) { + if (((length > 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char) && + (name_string[3] == (int) slash_char)) || + ((length == 3) && + (name_string[0] == (int) slash_char) && + (name_string[1] == (int) period_char) && + (name_string[2] == (int) period_char))) { + /* Positioned on the / that starts a /.. sequence */ + if (((count = cdp - current_component) != 0) && + (exists(name = GETNAME(string, cdp - string)) > file_doesnt_exist) && + (!name->stat.is_sym_link)) { + name = GETNAME(current_component, count); + if(name != dotdot) { + cdp = current_component; + name_string += 3; + length -= 3; + if (length > 0) { + name_string++; /* skip slash */ + length--; + while (length > 0) { + *cdp++ = *name_string++; + length--; + } + } + *cdp = (int) nul_char; + retmem(string2); + goto removed_one; + } + } + } + if ((*cdp++ = *name_string++) == (int) slash_char) { + current_component = cdp; + } + length--; + } + *cdp = (int) nul_char; + if (string[0] == (int) nul_char) { + name = dot; + } else { + name = GETNAME(string, FIND_LENGTH); + } + retmem(string); + retmem(string2); + return name; +} + +/* + * find_target_groups(target_list) + * + * If a "+" was seen when the target list was scanned we need to extract + * the groups. Each target in the name vector that is a member of a + * group gets a pointer to a chain of all the members stuffed in its + * target_group vector slot + * + * Parameters: + * target_list The list of targets that contains "+" + * + * Global variables used: + * plus The Name "+", compared against + */ +Chain +find_target_groups(register Name_vector target_list, register int i, Boolean reset) +{ + static Chain target_group = NULL; + static Chain tail_target_group = NULL; + static Name *next; + static Boolean clear_target_group = false; + + if (reset) { + target_group = NULL; + tail_target_group = NULL; + clear_target_group = false; + } + + /* Scan the list of targets */ + /* If the previous target terminated a group */ + /* we flush the pointer to that member chain */ + if (clear_target_group) { + clear_target_group = false; + target_group = NULL; + } + /* Pick up a pointer to the cell with */ + /* the next target */ + if (i + 1 != target_list->used) { + next = &target_list->names[i + 1]; + } else { + next = (target_list->next != NULL) ? + &target_list->next->names[0] : NULL; + } + /* We have four states here : + * 0: No target group started and next element is not "+" + * This is not interesting. + * 1: A target group is being built and the next element + * is not "+". This terminates the group. + * 2: No target group started and the next member is "+" + * This is the first target in a group. + * 3: A target group started and the next member is a "+" + * The group continues. + */ + switch ((target_group ? 1 : 0) + + (next && (*next == plus) ? + 2 : 0)) { + case 0: /* Not target_group */ + break; + case 1: /* Last group member */ + /* We need to keep this pointer so */ + /* we can stuff it for last member */ + clear_target_group = true; + /* fall into */ + case 3: /* Middle group member */ + /* Add this target to the */ + /* current chain */ + tail_target_group->next = ALLOC(Chain); + tail_target_group = tail_target_group->next; + tail_target_group->next = NULL; + tail_target_group->name = target_list->names[i]; + break; + case 2: /* First group member */ + /* Start a new chain */ + target_group = tail_target_group = ALLOC(Chain); + target_group->next = NULL; + target_group->name = target_list->names[i]; + break; + } + /* Stuff the current chain, if any, in the */ + /* targets group slot */ + target_list->target_group[i] = target_group; + if ((next != NULL) && + (*next == plus)) { + *next = NULL; + } + return (tail_target_group); +} + +/* + * enter_dependencies(target, target_group, depes, command, separator) + * + * Take one target and a list of dependencies and process the whole thing. + * The target might be special in some sense in which case that is handled + * + * Parameters: + * target The target we want to enter + * target_group Non-NULL if target is part of a group this time + * depes A list of dependencies for the target + * command The command the target should be entered with + * separator Indicates if this is a ":" or a "::" rule + * + * Static variables used: + * built_last_make_run_seen If the previous target was + * .BUILT_LAST_MAKE_RUN we say to rewrite + * the state file later on + * + * Global variables used: + * command_changed Set to indicate if .make.state needs rewriting + * default_target_to_build Set to the target if reading makefile + * and this is the first regular target + * force The Name " FORCE", used with "::" targets + * makefile_type We do different things for makefile vs. report + * not_auto The Name ".NOT_AUTO", compared against + * recursive_name The Name ".RECURSIVE", compared against + * temp_file_number Used to figure out when to clear stale + * automatic dependencies + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_dependencies(register Name target, Chain target_group, register Name_vector depes, register Cmd_line command, register Separator separator) +{ + register int i; + register Property line; + Name name; + Name directory; + wchar_t *namep; + char *mb_namep; + Dependency dp; + Dependency *dpp; + Property line2; + wchar_t relative[MAXPATHLEN]; + register int recursive_state; + Boolean register_as_auto; + Boolean not_auto_found; + char *slash; + Wstring depstr; + + /* Check if this is a .RECURSIVE line */ + if ((depes->used >= 3) && + (depes->names[0] == recursive_name)) { + target->has_recursive_dependency = true; + depes->names[0] = NULL; + recursive_state = 0; + dp = NULL; + dpp = &dp; + /* Read the dependencies. They are " */ + /* *" */ + for (; depes != NULL; depes = depes->next) { + for (i = 0; i < depes->used; i++) { + if (depes->names[i] != NULL) { + switch (recursive_state++) { + case 0: /* Directory */ + { + depstr.init(depes->names[i]); + make_relative(depstr.get_string(), + relative); + directory = + GETNAME(relative, + FIND_LENGTH); + } + break; + case 1: /* Target */ + name = depes->names[i]; + break; + default: /* Makefiles */ + *dpp = ALLOC(Dependency); + (*dpp)->next = NULL; + (*dpp)->name = depes->names[i]; + (*dpp)->automatic = false; + (*dpp)->stale = false; + (*dpp)->built = false; + dpp = &((*dpp)->next); + break; + } + } + } + } + /* Check if this recursion already has been reported else */ + /* enter the recursive prop for the target */ + /* The has_built flag is used to tell if this .RECURSIVE */ + /* was discovered from this run (read from a tmp file) */ + /* or was from discovered from the original .make.state */ + /* file */ + for (line = get_prop(target->prop, recursive_prop); + line != NULL; + line = get_prop(line->next, recursive_prop)) { + if ((line->body.recursive.directory == directory) && + (line->body.recursive.target == name)) { + line->body.recursive.makefiles = dp; + line->body.recursive.has_built = + (Boolean) + (makefile_type == reading_cpp_file); + return; + } + } + line2 = append_prop(target, recursive_prop); + line2->body.recursive.directory = directory; + line2->body.recursive.target = name; + line2->body.recursive.makefiles = dp; + line2->body.recursive.has_built = + (Boolean) (makefile_type == reading_cpp_file); + line2->body.recursive.in_depinfo = false; + return; + } + /* If this is the first target that doesnt start with a "." in the */ + /* makefile we remember that */ + Wstring tstr(target); + wchar_t * wcb = tstr.get_string(); + if ((makefile_type == reading_makefile) && + (default_target_to_build == NULL) && + ((wcb[0] != (int) period_char) || + wcschr(wcb, (int) slash_char))) { + +/* BID 1181577: $(EMPTY_MACRO) + $(EMPTY_MACRO): +** The target with empty name cannot be default_target_to_build +*/ + if (target->hash.length != 0) + default_target_to_build = target; + } + /* Check if the line is ":" or "::" */ + if (makefile_type == reading_makefile) { + if (target->colons == no_colon) { + target->colons = separator; + } else { + if (target->colons != separator) { + fatal_reader(gettext(":/:: conflict for target `%s'"), + target->string_mb); + } + } + if (target->colons == two_colon) { + if (depes->used == 0) { + /* If this is a "::" type line with no */ + /* dependencies we add one "FRC" type */ + /* dependency for free */ + depes->used = 1; /* Force :: targets with no + * depes to always run */ + depes->names[0] = force; + } + /* Do not delete "::" type targets when interrupted */ + target->stat.is_precious = true; + /* + * Build a synthetic target "%target" + * for "target". + */ + mb_namep = getmem((int) (strlen(target->string_mb) + 10)); + namep = ALLOC_WC((int) (target->hash.length + 10)); + slash = strrchr(target->string_mb, (int) slash_char); + if (slash == NULL) { + (void) sprintf(mb_namep, + "%d@%s", + target->colon_splits++, + target->string_mb); + } else { + *slash = 0; + (void) sprintf(mb_namep, + "%s/%d@%s", + target->string_mb, + target->colon_splits++, + slash + 1); + *slash = (int) slash_char; + } + MBSTOWCS(namep, mb_namep); + retmem_mb(mb_namep); + name = GETNAME(namep, FIND_LENGTH); + retmem(namep); + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Make "target" depend on "%target */ + line2 = maybe_append_prop(target, line_prop); + enter_dependency(line2, name, true); + line2->body.line.target = target; + /* Put a prop on "%target that makes */ + /* appear as "target" */ + /* when it is processed */ + maybe_append_prop(name, target_prop)-> + body.target.target = target; + target->is_double_colon_parent = true; + name->is_double_colon = true; + name->has_target_prop = true; + if (trace_reader) { + (void) printf("\n"); + } + (target = name)->stat.is_file = true; + } + } + /* This really is a regular dependency line. Just enter it */ + line = maybe_append_prop(target, line_prop); + line->body.line.target = target; + /* Depending on what kind of makefile we are reading we have to */ + /* treat things differently */ + switch (makefile_type) { + case reading_makefile: + /* Reading regular makefile. Just notice whether this */ + /* redefines the rule for the target */ + if (command != NULL) { + if (line->body.line.command_template != NULL) { + line->body.line.command_template_redefined = + true; + if ((wcb[0] == (int) period_char) && + !wcschr(wcb, (int) slash_char)) { + line->body.line.command_template = + command; + } + } else { + line->body.line.command_template = command; + } + } else { + if ((wcb[0] == (int) period_char) && + !wcschr(wcb, (int) slash_char)) { + line->body.line.command_template = command; + } + } + break; + case rereading_statefile: + /* Rereading the statefile. We only enter thing that changed */ + /* since the previous time we read it */ + if (!built_last_make_run_seen) { + for (Cmd_line next, cmd = command; cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + return; + } + built_last_make_run_seen = false; + command_changed = true; + target->ran_command = true; + case reading_statefile: + /* Reading the statefile for the first time. Enter the rules */ + /* as "Commands used" not "templates to use" */ + if (command != NULL) { + for (Cmd_line next, cmd = line->body.line.command_used; + cmd != NULL; cmd = next) { + next = cmd->next; + free(cmd); + } + line->body.line.command_used = command; + } + case reading_cpp_file: + /* Reading report file from programs that reports */ + /* dependencies. If this is the first time the target is */ + /* read from this reportfile we clear all old */ + /* automatic depes */ + if (target->temp_file_number == temp_file_number) { + break; + } + target->temp_file_number = temp_file_number; + command_changed = true; + if (line != NULL) { + for (dp = line->body.line.dependencies; + dp != NULL; + dp = dp->next) { + if (dp->automatic) { + dp->stale = true; + } + } + } + break; + default: + fatal_reader(gettext("Internal error. Unknown makefile type %d"), + makefile_type); + } + /* A target may only be involved in one target group */ + if (line->body.line.target_group != NULL) { + if (target_group != NULL) { + fatal_reader(gettext("Too many target groups for target `%s'"), + target->string_mb); + } + } else { + line->body.line.target_group = target_group; + } + + if (trace_reader) { + (void) printf("%s:\t", target->string_mb); + } + /* Enter the dependencies */ + register_as_auto = BOOLEAN(makefile_type != reading_makefile); + not_auto_found = false; + for (; + (depes != NULL) && !not_auto_found; + depes = depes->next) { + for (i = 0; i < depes->used; i++) { + /* the dependency .NOT_AUTO signals beginning of + * explicit dependancies which were put at end of + * list in .make.state file - we stop entering + * dependencies at this point + */ + if (depes->names[i] == not_auto) { + not_auto_found = true; + break; + } + enter_dependency(line, + depes->names[i], + register_as_auto); + } + } + if (trace_reader) { + (void) printf("\n"); + print_rule(command); + } +} + +/* + * enter_dependency(line, depe, automatic) + * + * Enter one dependency. Do not enter duplicates. + * + * Parameters: + * line The line block that the dependeny is + * entered for + * depe The dependency to enter + * automatic Used to set the field "automatic" + * + * Global variables used: + * makefile_type We do different things for makefile vs. report + * trace_reader Indicates that we should echo stuff we read + * wait_name The Name ".WAIT", compared against + */ +void +enter_dependency(Property line, register Name depe, Boolean automatic) +{ + register Dependency dp; + register Dependency *insert; + + if (trace_reader) { + (void) printf("%s ", depe->string_mb); + } + /* Find the end of the list and check for duplicates */ + for (insert = &line->body.line.dependencies, dp = *insert; + dp != NULL; + insert = &dp->next, dp = *insert) { + if ((dp->name == depe) && (depe != wait_name)) { + if (dp->automatic) { + dp->automatic = automatic; + if (automatic) { + dp->built = false; + depe->stat.is_file = true; + } + } + dp->stale = false; + return; + } + } + /* Insert the new dependency since we couldnt find it */ + dp = *insert = ALLOC(Dependency); + dp->name = depe; + dp->next = NULL; + dp->automatic = automatic; + dp->stale = false; + dp->built = false; + depe->stat.is_file = true; + + if ((makefile_type == reading_makefile) && + (line != NULL) && + (line->body.line.target != NULL)) { + line->body.line.target->has_regular_dependency = true; + } +} + +/* + * enter_percent(target, depes, command) + * + * Enter "x%y : a%b" type lines + * % patterns are stored in four parts head and tail for target and source + * + * Parameters: + * target Left hand side of pattern + * depes The dependency list with the rh pattern + * command The command for the pattern + * + * Global variables used: + * empty_name The Name "", compared against + * percent_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Percent +enter_percent(register Name target, Chain target_group, register Name_vector depes, Cmd_line command) +{ + register Percent result = ALLOC(Percent); + register Percent depe; + register Percent *depe_tail = &result->dependencies; + register Percent *insert; + register wchar_t *cp, *cp1; + Name_vector nvp; + int i; + int pattern; + + result->next = NULL; + result->patterns = NULL; + result->patterns_total = 0; + result->command_template = command; + result->being_expanded = false; + result->name = target; + result->dependencies = NULL; + result->target_group = target_group; + + /* get patterns count */ + Wstring wcb(target); + cp = wcb.get_string(); + while (true) { + cp = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp != NULL) { + result->patterns_total++; + cp++; + } else { + break; + } + } + result->patterns_total++; + + /* allocate storage for patterns */ + result->patterns = (Name *) getmem(sizeof(Name) * result->patterns_total); + + /* then create patterns */ + cp = wcb.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp1 != NULL) { + result->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + result->patterns[pattern] = GETNAME(cp, (int) target->hash.length - (cp - wcb.get_string())); + break; + } + } + + Wstring wcb1; + + /* build dependencies list */ + for (nvp = depes; nvp != NULL; nvp = nvp->next) { + for (i = 0; i < nvp->used; i++) { + depe = ALLOC(Percent); + depe->next = NULL; + depe->patterns = NULL; + depe->patterns_total = 0; + depe->name = nvp->names[i]; + depe->dependencies = NULL; + depe->command_template = NULL; + depe->being_expanded = false; + depe->target_group = NULL; + + *depe_tail = depe; + depe_tail = &depe->next; + + if (depe->name->percent) { + /* get patterns count */ + wcb1.init(depe->name); + cp = wcb1.get_string(); + while (true) { + cp = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp != NULL) { + depe->patterns_total++; + cp++; + } else { + break; + } + } + depe->patterns_total++; + + /* allocate storage for patterns */ + depe->patterns = (Name *) getmem(sizeof(Name) * depe->patterns_total); + + /* then create patterns */ + cp = wcb1.get_string(); + pattern = 0; + while (true) { + cp1 = (wchar_t *) wcschr(cp, (int) percent_char); + if (cp1 != NULL) { + depe->patterns[pattern] = GETNAME(cp, cp1 - cp); + cp = cp1 + 1; + pattern++; + } else { + depe->patterns[pattern] = GETNAME(cp, (int) depe->name->hash.length - (cp - wcb1.get_string())); + break; + } + } + } + } + } + + /* Find the end of the percent list and append the new pattern */ + for (insert = &percent_list; (*insert) != NULL; insert = &(*insert)->next); + *insert = result; + + if (trace_reader) { + (void) printf("%s:", result->name->string_mb); + + for (depe = result->dependencies; depe != NULL; depe = depe->next) { + (void) printf(" %s", depe->name->string_mb); + } + + (void) printf("\n"); + + print_rule(command); + } + + return result; +} + +/* + * enter_dyntarget(target) + * + * Enter "$$(MACRO) : b" type lines + * + * Parameters: + * target Left hand side of pattern + * + * Global variables used: + * dyntarget_list The list of all percent rules, added to + * trace_reader Indicates that we should echo stuff we read + */ +Dyntarget +enter_dyntarget(register Name target) +{ + register Dyntarget result = ALLOC(Dyntarget); + Dyntarget p; + Dyntarget *insert; + int i; + + result->next = NULL; + result->name = target; + + + /* Find the end of the dyntarget list and append the new pattern */ + for (insert = &dyntarget_list, p = *insert; + p != NULL; + insert = &p->next, p = *insert); + *insert = result; + + if (trace_reader) { + (void) printf("Dynamic target %s:\n", result->name->string_mb); + } + return( result); +} + + +/* + * special_reader(target, depes, command) + * + * Read the pseudo targets make knows about + * This handles the special targets that should not be entered as regular + * target/dependency sets. + * + * Parameters: + * target The special target + * depes The list of dependencies it was entered with + * command The command it was entered with + * + * Static variables used: + * built_last_make_run_seen Set to indicate .BUILT_LAST... seen + * + * Global variables used: + * all_parallel Set to indicate that everything runs parallel + * svr4 Set when ".SVR4" target is read + * svr4_name The Name ".SVR4" + * posix Set when ".POSIX" target is read + * posix_name The Name ".POSIX" + * current_make_version The Name "" + * default_rule Set when ".DEFAULT" target is read + * default_rule_name The Name ".DEFAULT", used for tracing + * dot_keep_state The Name ".KEEP_STATE", used for tracing + * ignore_errors Set if ".IGNORE" target is read + * ignore_name The Name ".IGNORE", used for tracing + * keep_state Set if ".KEEP_STATE" target is read + * no_parallel_name The Name ".NO_PARALLEL", used for tracing + * only_parallel Set to indicate only some targets runs parallel + * parallel_name The Name ".PARALLEL", used for tracing + * precious The Name ".PRECIOUS", used for tracing + * sccs_get_name The Name ".SCCS_GET", used for tracing + * sccs_get_posix_name The Name ".SCCS_GET_POSIX", used for tracing + * get_name The Name ".GET", used for tracing + * sccs_get_rule Set when ".SCCS_GET" target is read + * silent Set when ".SILENT" target is read + * silent_name The Name ".SILENT", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +void +special_reader(Name target, register Name_vector depes, Cmd_line command) +{ + register int n; + + switch (target->special_reader) { + + case svr4_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + svr4 = true; + posix = false; + keep_state = false; + all_parallel = false; + only_parallel = false; + if (trace_reader) { + (void) printf("%s:\n", svr4_name->string_mb); + } + break; + + case posix_special: + if(svr4) + break; + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + posix = true; + /* with posix on, use the posix get rule */ + sccs_get_rule = sccs_get_posix_rule; + /* turn keep state off being SunPro make specific */ + keep_state = false; + /* Use /usr/xpg4/bin/sh on Solaris */ + MBSTOWCS(wcs_buffer, "/usr/xpg4/bin/sh"); + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + if (trace_reader) { + (void) printf("%s:\n", posix_name->string_mb); + } + break; + + case built_last_make_run_special: + built_last_make_run_seen = true; + break; + + case default_special: + if (depes->used != 0) { + warning(gettext("Illegal dependency list for target `%s'"), + target->string_mb); + } + default_rule = command; + if (trace_reader) { + (void) printf("%s:\n", + default_rule_name->string_mb); + print_rule(command); + } + break; + + + case ignore_special: + if ((depes->used != 0) &&(!posix)){ + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + ignore_errors_all = true; + } + if(svr4) { + ignore_errors_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->ignore_error_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", ignore_name->string_mb); + } + break; + + case keep_state_special: + if(svr4) + break; + /* ignore keep state, being SunPro make specific */ + if(posix) + break; + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + keep_state = true; + if (trace_reader) { + (void) printf("%s:\n", + dot_keep_state->string_mb); + } + break; + + case keep_state_file_special: + if(svr4) + break; + if(posix) + break; + /* it's not necessary to specify KEEP_STATE, if this + ** is given, so set the keep_state. + */ + keep_state = true; + if (depes->used != 0) { + if((!make_state) ||(!strcmp(make_state->string_mb,".make.state"))) { + make_state = depes->names[0]; + } + } + break; + case make_version_special: + if(svr4) + break; + if (depes->used != 1) { + fatal_reader(gettext("Illegal dependency list for target `%s'"), + target->string_mb); + } + if (depes->names[0] != current_make_version) { + /* + * Special case the fact that version 1.0 and 1.1 + * are identical. + */ + if (!IS_EQUAL(depes->names[0]->string_mb, + "VERSION-1.1") || + !IS_EQUAL(current_make_version->string_mb, + "VERSION-1.0")) { + /* + * Version mismatches should cause the + * .make.state file to be skipped. + * This is currently not true - it is read + * anyway. + */ + warning(gettext("Version mismatch between current version `%s' and `%s'"), + current_make_version->string_mb, + depes->names[0]->string_mb); + } + } + break; + + case no_parallel_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + no_parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + } + } + break; + + case parallel_special: + if(svr4) + break; + if (depes->used == 0) { + /* everything runs in parallel */ + all_parallel = true; + only_parallel = false; + } + /* Set the parallel bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + parallel_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->parallel = true; + depes->names[n]->no_parallel = false; + } + } + break; + + case localhost_special: + if(svr4) + break; + /* Set the no_parallel bit for all the targets on */ + /* the dependency list */ + if (depes->used == 0) { + /* only those explicitly made parallel */ + only_parallel = true; + all_parallel = false; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + localhost_name->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->no_parallel = true; + depes->names[n]->parallel = false; + depes->names[n]->localhost = true; + } + } + break; + + case precious_special: + if (depes->used == 0) { + /* everything is precious */ + all_precious = true; + } else { + all_precious = false; + } + if(svr4) { + all_precious = true; + break; + } + /* Set the precious bit for all the targets on */ + /* the dependency list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + if (trace_reader) { + (void) printf("%s:\t%s\n", + precious->string_mb, + depes->names[n]->string_mb); + } + depes->names[n]->stat.is_precious = true; + } + } + break; + + case sccs_get_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_rule = command; + sccs_get_org_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_name->string_mb); + print_rule(command); + } + break; + + case sccs_get_posix_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + sccs_get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", sccs_get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_posix_special: + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + get_posix_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_posix_name->string_mb); + print_rule(command); + } + break; + + case get_special: + if(!svr4) { + break; + } + if (depes->used != 0) { + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + get_rule = command; + sccs_get_rule = command; + if (trace_reader) { + (void) printf("%s:\n", get_name->string_mb); + print_rule(command); + } + break; + + case silent_special: + if ((depes->used != 0) && (!posix)){ + fatal_reader(gettext("Illegal dependencies for target `%s'"), + target->string_mb); + } + if (depes->used == 0) + { + silent_all = true; + } + if(svr4) { + silent_all = true; + break; + } + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + depes->names[n]->silent_mode = true; + } + } + if (trace_reader) { + (void) printf("%s:\n", silent_name->string_mb); + } + break; + + case suffixes_special: + read_suffixes_list(depes); + break; + + default: + + fatal_reader(gettext("Internal error: Unknown special reader")); + } +} + +/* + * read_suffixes_list(depes) + * + * Read the special list .SUFFIXES. If it is empty the old list is + * cleared. Else the new one is appended. Suffixes with ~ are extracted + * and marked. + * + * Parameters: + * depes The list of suffixes + * + * Global variables used: + * hashtab The central hashtable for Names. + * suffixes The list of suffixes, set or appended to + * suffixes_name The Name ".SUFFIXES", used for tracing + * trace_reader Indicates that we should echo stuff we read + */ +static void +read_suffixes_list(register Name_vector depes) +{ + register int n; + register Dependency dp; + register Dependency *insert_dep; + register Name np; + Name np2; + register Boolean first = true; + + if (depes->used == 0) { + /* .SUFFIXES with no dependency list clears the */ + /* suffixes list */ + for (Name_set::iterator np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + np->with_squiggle = + np->without_squiggle = + false; + } + suffixes = NULL; + if (trace_reader) { + (void) printf("%s:\n", suffixes_name->string_mb); + } + return; + } + Wstring str; + /* Otherwise we append to the list */ + for (; depes != NULL; depes = depes->next) { + for (n = 0; n < depes->used; n++) { + np = depes->names[n]; + /* Find the end of the list and check if the */ + /* suffix already has been entered */ + for (insert_dep = &suffixes, dp = *insert_dep; + dp != NULL; + insert_dep = &dp->next, dp = *insert_dep) { + if (dp->name == np) { + goto duplicate_suffix; + } + } + if (trace_reader) { + if (first) { + (void) printf("%s:\t", + suffixes_name->string_mb); + first = false; + } + (void) printf("%s ", depes->names[n]->string_mb); + } + if(!(posix|svr4)) { + /* If the suffix is suffixed with "~" we */ + /* strip that and mark the suffix nameblock */ + str.init(np); + wchar_t * wcb = str.get_string(); + if (wcb[np->hash.length - 1] == + (int) tilde_char) { + np2 = GETNAME(wcb, + (int)(np->hash.length - 1)); + np2->with_squiggle = true; + if (np2->without_squiggle) { + continue; + } + np = np2; + } + } + np->without_squiggle = true; + /* Add the suffix to the list */ + dp = *insert_dep = ALLOC(Dependency); + insert_dep = &dp->next; + dp->next = NULL; + dp->name = np; + dp->built = false; + duplicate_suffix:; + } + } + if (trace_reader) { + (void) printf("\n"); + } +} + +/* + * make_relative(to, result) + * + * Given a file name compose a relative path name from it to the + * current directory. + * + * Parameters: + * to The path we want to make relative + * result Where to put the resulting relative path + * + * Global variables used: + */ +static void +make_relative(wchar_t *to, wchar_t *result) +{ + wchar_t *from; + wchar_t *allocated; + wchar_t *cp; + wchar_t *tocomp; + int ncomps; + int i; + int len; + + /* Check if the path is already relative. */ + if (to[0] != (int) slash_char) { + (void) wcscpy(result, to); + return; + } + + MBSTOWCS(wcs_buffer, get_current_path()); + from = allocated = (wchar_t *) wcsdup(wcs_buffer); + + /* + * Find the number of components in the from name. + * ncomp = number of slashes + 1. + */ + ncomps = 1; + for (cp = from; *cp != (int) nul_char; cp++) { + if (*cp == (int) slash_char) { + ncomps++; + } + } + + /* + * See how many components match to determine how many "..", + * if any, will be needed. + */ + result[0] = (int) nul_char; + tocomp = to; + while ((*from != (int) nul_char) && (*from == *to)) { + if (*from == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + from++; + to++; + } + + /* + * Now for some special cases. Check for exact matches and + * for either name terminating exactly. + */ + if (*from == (int) nul_char) { + if (*to == (int) nul_char) { + MBSTOWCS(wcs_buffer, "."); + (void) wcscpy(result, wcs_buffer); + retmem(allocated); + return; + } + if (*to == (int) slash_char) { + ncomps--; + tocomp = &to[1]; + } + } else if ((*from == (int) slash_char) && (*to == (int) nul_char)) { + ncomps--; + tocomp = to; + } + /* Add on the ".."s. */ + for (i = 0; i < ncomps; i++) { + MBSTOWCS(wcs_buffer, "../"); + (void) wcscat(result, wcs_buffer); + } + + /* Add on the remainder of the to name, if any. */ + if (*tocomp == (int) nul_char) { + len = wcslen(result); + result[len - 1] = (int) nul_char; + } else { + (void) wcscat(result, tocomp); + } + retmem(allocated); + return; +} + +/* + * print_rule(command) + * + * Used when tracing the reading of rules + * + * Parameters: + * command Command to print + * + * Global variables used: + */ +static void +print_rule(register Cmd_line command) +{ + for (; command != NULL; command = command->next) { + (void) printf("\t%s\n", command->command_line->string_mb); + } +} + +/* + * enter_conditional(target, name, value, append) + * + * Enter "target := MACRO= value" constructs + * + * Parameters: + * target The target the macro is for + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * conditionals A special Name that stores all conditionals + * where the target is a % pattern + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_conditional(register Name target, Name name, Name value, register Boolean append) +{ + register Property conditional; + static int sequence; + Name orig_target = target; + + if (name == target_arch) { + enter_conditional(target, virtual_root, virtual_root, false); + } + + if (target->percent) { + target = conditionals; + } + + if (name->colon) { + sh_transform(&name, &value); + } + + /* Count how many conditionals we must activate before building the */ + /* target */ + if (target->percent) { + target = conditionals; + } + + target->conditional_cnt++; + maybe_append_prop(name, macro_prop)->body.macro.is_conditional = true; + /* Add the property for the target */ + conditional = append_prop(target, conditional_prop); + conditional->body.conditional.target = orig_target; + conditional->body.conditional.name = name; + conditional->body.conditional.value = value; + conditional->body.conditional.sequence = sequence++; + conditional->body.conditional.append = append; + if (trace_reader) { + if (value == NULL) { + (void) printf("%s := %s %c=\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s := %s %c= %s\n", + target->string_mb, + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * enter_equal(name, value, append) + * + * Enter "MACRO= value" constructs + * + * Parameters: + * name The name of the macro + * value The value for the macro + * append Indicates if the assignment is appending or not + * + * Global variables used: + * trace_reader Indicates that we should echo stuff we read + */ +void +enter_equal(Name name, Name value, register Boolean append) +{ + wchar_t *string; + Name temp; + + if (name->colon) { + sh_transform(&name, &value); + } + (void) SETVAR(name, value, append); + + /* if we're setting FC, we want to set F77 to the same value. */ + Wstring nms(name); + wchar_t * wcb = nms.get_string(); + string = wcb; + if (string[0]=='F' && + string[1]=='C' && + string[2]=='\0') { + MBSTOWCS(wcs_buffer, "F77"); + temp = GETNAME(wcs_buffer, FIND_LENGTH); + (void) SETVAR(temp, value, append); +/* + fprintf(stderr, gettext("warning: FC is obsolete, use F77 instead\n")); + */ + } + + if (trace_reader) { + if (value == NULL) { + (void) printf("%s %c=\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char); + } else { + (void) printf("%s %c= %s\n", + name->string_mb, + append ? + (int) plus_char : (int) space_char, + value->string_mb); + } + } +} + +/* + * sh_transform(name, value) + * + * Parameters: + * name The name of the macro we might transform + * value The value to transform + * + */ +static void +sh_transform(Name *name, Name *value) +{ + /* Check if we need :sh transform */ + wchar_t *colon; + String_rec command; + String_rec destination; + wchar_t buffer[1000]; + wchar_t buffer1[1000]; + + static wchar_t colon_sh[4]; + static wchar_t colon_shell[7]; + + if (colon_sh[0] == (int) nul_char) { + MBSTOWCS(colon_sh, ":sh"); + MBSTOWCS(colon_shell, ":shell"); + } + Wstring nms((*name)); + wchar_t * wcb = nms.get_string(); + + colon = (wchar_t *) wcsrchr(wcb, (int) colon_char); + if ((colon != NULL) && (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell))) { + INIT_STRING_FROM_STACK(destination, buffer); + + if(*value == NULL) { + buffer[0] = 0; + } else { + Wstring wcb1((*value)); + if (IS_WEQUAL(colon, colon_shell)) { + INIT_STRING_FROM_STACK(command, buffer1); + expand_value(*value, &command, false); + } else { + command.text.p = wcb1.get_string() + (*value)->hash.length; + command.text.end = command.text.p; + command.buffer.start = wcb1.get_string(); + command.buffer.end = command.text.p; + } + sh_command2string(&command, &destination); + } + + (*value) = GETNAME(destination.buffer.start, FIND_LENGTH); + *colon = (int) nul_char; + (*name) = GETNAME(wcb, FIND_LENGTH); + *colon = (int) colon_char; + } +} + +/* + * fatal_reader(format, args...) + * + * Parameters: + * format printf style format string + * args arguments to match the format + * + * Global variables used: + * file_being_read Name of the makefile being read + * line_number Line that is being read + * report_pwd Indicates whether current path should be shown + * temp_file_name When reading tempfile we report that name + */ +/*VARARGS*/ +void +fatal_reader(char * pattern, ...) +{ + va_list args; + char message[1000]; + + va_start(args, pattern); + if (file_being_read != NULL) { + WCSTOMBS(mbs_buffer, file_being_read); + if (line_number != 0) { + (void) sprintf(message, + gettext("%s, line %d: %s"), + mbs_buffer, + line_number, + pattern); + } else { + (void) sprintf(message, + "%s: %s", + mbs_buffer, + pattern); + } + pattern = message; + } + + (void) fflush(stdout); + (void) fprintf(stderr, gettext("%s: Fatal error in reader: "), + getprogname()); + (void) vfprintf(stderr, pattern, args); + (void) fprintf(stderr, "\n"); + va_end(args); + + if (temp_file_name != NULL) { + (void) fprintf(stderr, + gettext("%s: Temp-file %s not removed\n"), + getprogname(), + temp_file_name->string_mb); + temp_file_name = NULL; + } + + if (report_pwd) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path()); + } + (void) fflush(stderr); + exit_status = 1; + exit(1); +} + diff --git a/bin/rep.cc b/bin/rep.cc new file mode 100644 index 0000000..f0cb9e1 --- /dev/null +++ b/bin/rep.cc @@ -0,0 +1,394 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * rep.c + * + * This file handles the .nse_depinfo file + */ + +/* + * Included files + */ +#include +#include /* retmem() */ +#include /* NSE_DEPINFO */ + +/* + * Static variables + */ +static Recursive_make recursive_list; +static Recursive_make *bpatch = &recursive_list; +static Boolean changed; + +/* + * File table of contents + */ + + +/* + * report_recursive_init() + * + * Read the .nse_depinfo file and make a list of all the + * .RECURSIVE entries. + * + * Parameters: + * + * Static variables used: + * bpatch Points to slot where next cell should be added + * + * Global variables used: + * recursive_name The Name ".RECURSIVE", compared against + */ + +void +report_recursive_init(void) +{ + char *search_dir; + char nse_depinfo[MAXPATHLEN]; + FILE *fp; + int line_size, line_index; + wchar_t *line; + wchar_t *bigger_line; + wchar_t *colon; + wchar_t *dollar; + Recursive_make rp; + + /* + * This routine can be called more than once, don't do + * anything after the first time. + */ + if (depinfo_already_read) { + return; + } else { + depinfo_already_read = true; + } + + search_dir = getenv("NSE_DEP"); + if (search_dir == NULL) { + return; + } + (void) sprintf(nse_depinfo, "%s/%s", search_dir, NSE_DEPINFO); + fp = fopen(nse_depinfo, "r"); + if (fp == NULL) { + return; + } + line_size = MAXPATHLEN; + line_index = line_size - 1; + line = ALLOC_WC(line_size); + Wstring rns(recursive_name); + wchar_t * wcb = rns.get_string(); + while (fgetws(line, line_size, fp) != NULL) { + while (wcslen(line) == line_index) { + if (line[wcslen(line) - 1] == '\n') { + continue; + } + bigger_line = ALLOC_WC(2 * line_size); + wcscpy(bigger_line, line); + retmem(line); + line = bigger_line; + if (fgetws(&line[line_index], line_size, fp) == NULL) + continue; + line_index = 2 * line_index; + line_size = 2 * line_size; + } + + colon = (wchar_t *) wcschr(line, (int) colon_char); + if (colon == NULL) { + continue; + } + dollar = (wchar_t *) wcschr(line, (int) dollar_char); + line[wcslen(line) - 1] = (int) nul_char; + if (IS_WEQUALN(&colon[2], wcb, + (int) recursive_name->hash.length)) { + /* + * If this entry is an old entry, ignore it + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + if (dollar == NULL || + !IS_WEQUALN(wcs_buffer, (dollar+1) - VER_LEN, VER_LEN)){ + continue; + } + rp = ALLOC(Recursive_make); + (void) memset((char *) rp, 0, sizeof (Recursive_make_rec)); + /* + * set conditional_macro_string if string is present + */ + rp->oldline = (wchar_t *) wcsdup(line); + if ( dollar != NULL ){ + rp->cond_macrostring = + (wchar_t *) wcsdup(dollar - VER_LEN + 1); + } + /* + * get target name into recursive struct + */ + *colon = (int) nul_char; + rp->target = (wchar_t *) wcsdup(line); + *bpatch = rp; + bpatch = &rp->next; + } + } + (void) fclose(fp); +} + +/* + * report_recursive_dep(target, line) + * + * Report a target as recursive. + * + * Parameters: + * line Dependency line reported + * + * Static variables used: + * bpatch Points to slot where next cell should be added + * changed Written if report set changed + */ +void +report_recursive_dep(Name target, wchar_t *line) +{ + Recursive_make rp; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + String_rec string; + + INIT_STRING_FROM_STACK(string, rec_buf); + cond_macros_into_string(target, &string); + /* + * find an applicable recursive entry, if there isn't one, create it + */ + rp = find_recursive_target(target); + if (rp == NULL) { + rp = ALLOC(Recursive_make); + (void) memset((char *) rp, 0, sizeof (Recursive_make_rec)); + wchar_t * wcb = get_wstring(target->string_mb); // XXX Tolik: needs retmem + rp->target = wcb; + rp->newline = (wchar_t *) wcsdup(line); + rp->cond_macrostring = (wchar_t *) wcsdup(rec_buf); + *bpatch = rp; + bpatch = &rp->next; + changed = true; + } else { + if ((rp->oldline != NULL) && !IS_WEQUAL(rp->oldline, line)) { + rp->newline = (wchar_t *) wcsdup(line); + changed = true; + } + rp->removed = false; + } +} + +/* + * find_recursive_target(target) + * + * Search the list for a given target. + * + * Return value: + * The target cell + * + * Parameters: + * target The target we need + * top_level_target more info used to determinde the + * target we need + * + * Static variables used: + * recursive_list The list of targets + */ +Recursive_make +find_recursive_target(Name target) +{ + Recursive_make rp; + String_rec string; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + + INIT_STRING_FROM_STACK(string, rec_buf); + cond_macros_into_string(target, &string); + + Wstring tstr(target); + wchar_t * wcb = tstr.get_string(); + for (rp = recursive_list; rp != NULL; rp = rp->next) { + /* + * If this entry has already been removed, ignore it. + */ + if (rp->removed) + continue; + /* + * If this target, and the target on the list are the same + * and if one of them contains conditional macro info, while + * the other doesn't, remove this entry from the list of + * recursive entries. This can only happen if the Makefile + * has changed to no longer contain conditional macros. + */ + if (IS_WEQUAL(rp->target, wcb)) { + if (rp->cond_macrostring[VER_LEN] == '\0' && + string.buffer.start[VER_LEN] != '\0'){ + rp->removed = true; + continue; + } else if (rp->cond_macrostring[VER_LEN] != '\0' && + string.buffer.start[VER_LEN] == '\0'){ + rp->removed = true; + continue; + } + } + /* + * If this is not a VERS2 entry, only need to match + * the target name. toptarg information from VERS1 entries + * are ignored. + */ + MBSTOWCS(wcs_buffer, DEPINFO_FMT_VERSION); + if (IS_WEQUALN(wcs_buffer, string.buffer.start, VER_LEN)) { + if (IS_WEQUAL(rp->cond_macrostring, + string.buffer.start) && + IS_WEQUAL(rp->target, wcb)) { + return rp; + } + } else { + if (IS_WEQUAL(rp->target, wcb)) { + return rp; + } + } + } + return NULL; +} + +/* + * remove_recursive_dep(target, top_level_target) + * + * Mark a target as no longer recursive. + * + * Parameters: + * target The target we want to remove + * top_level_target target we want to remove must be built from + * the same top level target + * + * Static variables used: + * changed Written if report set changed + */ +void +remove_recursive_dep(Name target) +{ + Recursive_make rp; + + rp = find_recursive_target(target); + + if ( rp != NULL ) { + rp->removed = true; + changed = true; + if(rp->target) { + retmem(rp->target); + rp->target = NULL; + } + if(rp->newline) { + retmem(rp->newline); + rp->newline = NULL; + } + if(rp->oldline) { + retmem(rp->oldline); + rp->oldline = NULL; + } + if(rp->cond_macrostring) { + retmem(rp->cond_macrostring); + rp->cond_macrostring = NULL; + } + } +} + + +/* gather_recursive_deps() + * + * Create or update list of recursive targets. + */ +void +gather_recursive_deps(void) +{ + Name_set::iterator np, e; + String_rec rec; + wchar_t rec_buf[STRING_BUFFER_LENGTH]; + register Property lines; + Boolean has_recursive; + Dependency dp; + + report_recursive_init(); + + /* Go thru all targets and dump recursive dependencies */ + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + if (np->has_recursive_dependency){ + has_recursive = false; + /* + * start .RECURSIVE line with target: + */ + INIT_STRING_FROM_STACK(rec, rec_buf); + APPEND_NAME(np, &rec, FIND_LENGTH); + append_char((int) colon_char, &rec); + append_char((int) space_char, &rec); + + for (lines = get_prop(np->prop,recursive_prop); + lines != NULL; + lines = get_prop(lines->next, recursive_prop)) { + /* + * if entry is already in depinfo + * file or entry was not built, ignore it + */ + if (lines->body.recursive.in_depinfo) + continue; + if (!lines->body.recursive.has_built) + continue; + has_recursive = true; + lines->body.recursive.in_depinfo=true; + + /* + * Write the remainder of the + * .RECURSIVE line + */ + APPEND_NAME(recursive_name, &rec, + FIND_LENGTH); + append_char((int) space_char, &rec); + APPEND_NAME(lines->body.recursive.directory, + &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + APPEND_NAME(lines->body.recursive.target, + &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + + /* Complete list of makefiles used */ + for (dp = lines->body.recursive.makefiles; + dp != NULL; + dp = dp->next) { + APPEND_NAME(dp->name, &rec, FIND_LENGTH); + append_char((int) space_char, &rec); + } + } + /* + * dump list of conditional targets, + * and report recursive entry, if needed + */ + cond_macros_into_string(np, &rec); + if (has_recursive){ + report_recursive_dep(np, rec.buffer.start); + } + + } else if ( np->has_built ) { + remove_recursive_dep(np); + } + } +} + diff --git a/bin/state.cc b/bin/state.cc new file mode 100644 index 0000000..57a0150 --- /dev/null +++ b/bin/state.cc @@ -0,0 +1,444 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * state.c + * + * This file contains the routines that write the .make.state file + */ + +/* + * Included files + */ +#include +#include /* errmsg() */ +#include /* setjmp() */ +#include /* getpid() */ +#include /* errno */ +#include /* MB_CUR_MAX */ + +/* + * Defined macros + */ +#define LONGJUMP_VALUE 17 +#define XFWRITE(string, length, fd) {if (fwrite(string, 1, length, fd) == 0) \ + longjmp(long_jump, LONGJUMP_VALUE);} +#define XPUTC(ch, fd) { \ + if (putc((int) ch, fd) == EOF) \ + longjmp(long_jump, LONGJUMP_VALUE); \ + } +#define XFPUTS(string, fd) fputs(string, fd) + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static char * escape_target_name(Name np) +{ + if(np->dollar) { + int len = strlen(np->string_mb); + char * buff = (char*)malloc(2 * len); + int pos = 0; + wchar_t wc; + int pp = 0; + while(pos < len) { + int n = mbtowc(&wc, np->string_mb + pos, MB_CUR_MAX); + if(n < 0) { // error - this shouldn't happen + (void)free(buff); + return strdup(np->string_mb); + } + if(wc == dollar_char) { + buff[pp] = '\\'; pp++; + buff[pp] = '$'; pp++; + } else { + for(int j=0;jstring_mb[pos+j]; pp++; + } + } + pos += n; + } + buff[pp] = '\0'; + return buff; + } else { + return strdup(np->string_mb); + } +} + +static void print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump); + +/* + * write_state_file(report_recursive, exiting) + * + * Write a new version of .make.state + * + * Parameters: + * report_recursive Should only be done at end of run + * exiting true if called from the exit handler + * + * Global variables used: + * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written + * command_changed If no command changed we do not need to write + * current_make_version The Name "", written + * do_not_exec_rule If -n is on we do not write statefile + * hashtab The hashtable that contains all names + * keep_state If .KEEP_STATE is no on we do not write file + * make_state The Name ".make.state", used for opening file + * make_version The Name ".MAKE_VERSION", written + * recursive_name The Name ".RECURSIVE", written + * rewrite_statefile Indicates that something changed + */ + +void +write_state_file(int, Boolean exiting) +{ + register FILE *fd; + int lock_err; + char buffer[MAXPATHLEN]; + char make_state_tempfile[MAXPATHLEN]; + jmp_buf long_jump; + register int attempts = 0; + Name_set::iterator np, e; + register Property lines; + register int m; + Dependency dependency; + register Boolean name_printed; + Boolean built_this_run = false; + char *target_name; + int line_length; + register Cmd_line cp; + + + if (!rewrite_statefile || + !command_changed || + !keep_state || + do_not_exec_rule || + (report_dependencies_level > 0)) { + return; + } + /* Lock the file for writing. */ + make_state_lockfile = getmem(strlen(make_state->string_mb) + strlen(".lock") + 1); + (void) sprintf(make_state_lockfile, + "%s.lock", + make_state->string_mb); + if (lock_err = file_lock(make_state->string_mb, + make_state_lockfile, + (int *) &make_state_locked, 0)) { + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + + /* + * We need to make sure that we are not being + * called by the exit handler so we don't call + * it again. + */ + + if (exiting) { + (void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid()); + report_pwd = true; + warning(gettext("Writing to %s"), buffer); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fprintf(stderr, + gettext("Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + return; + } + } else { + report_pwd = true; + fatal(gettext("Can't lock .make.state")); + } + } + + (void) sprintf(make_state_tempfile, + "%s.tmp", + make_state->string_mb); + /* Delete old temporary statefile (in case it exists) */ + (void) unlink(make_state_tempfile); + if ((fd = fopen(make_state_tempfile, "w")) == NULL) { + lock_err = errno; /* Save it! unlink() can change errno */ + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not open temporary statefile `%s': %s"), + make_state_tempfile, + errmsg(lock_err)); + } + /* + * Set a trap for failed writes. If a write fails, the routine + * will try saving the .make.state file under another name in /tmp. + */ + if (setjmp(long_jump)) { + (void) fclose(fd); + if (attempts++ > 5) { + if ((make_state_lockfile != NULL) && + make_state_locked) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + fatal(gettext("Giving up on writing statefile")); + } + sleep(10); + (void) sprintf(buffer, "%s/.make.state.%d.XXXXXX", tmpdir, getpid()); + int fdes = mkstemp(buffer); + if ((fdes < 0) || (fd = fdopen(fdes, "w")) == NULL) { + fatal(gettext("Could not open statefile `%s': %s"), + buffer, + errmsg(errno)); + } + warning(gettext("Initial write of statefile failed. Trying again on %s"), + buffer); + } + + /* Write the version stamp. */ + XFWRITE(make_version->string_mb, + strlen(make_version->string_mb), + fd); + XPUTC(colon_char, fd); + XPUTC(tab_char, fd); + XFWRITE(current_make_version->string_mb, + strlen(current_make_version->string_mb), + fd); + XPUTC(newline_char, fd); + + /* + * Go through all the targets, dump their dependencies and + * command used. + */ + for (np = hashtab.begin(), e = hashtab.end(); np != e; np++) { + /* + * If the target has no command used nor dependencies, + * we can go to the next one. + */ + if ((lines = get_prop(np->prop, line_prop)) == NULL) { + continue; + } + /* If this target is a special target, don't print. */ + if (np->special_reader != no_special) { + continue; + } + /* + * Find out if any of the targets dependencies should + * be written to .make.state. + */ + for (m = 0, dependency = lines->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + if (m = !dependency->stale + && (dependency->name != force) +#ifndef PRINT_EXPLICIT_DEPEN + && dependency->automatic +#endif + ) { + break; + } + } + /* Only print if dependencies listed. */ + if (m || (lines->body.line.command_used != NULL)) { + name_printed = false; + /* + * If this target was built during this make run, + * we mark it. + */ + built_this_run = false; + if (np->has_built) { + built_this_run = true; + XFWRITE(built_last_make_run->string_mb, + strlen(built_last_make_run->string_mb), + fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + /* If the target has dependencies, we dump them. */ + target_name = escape_target_name(np); + if (np->has_long_member_name) { + target_name = + get_prop(np->prop, long_member_name_prop) + ->body.long_member_name.member_name-> + string_mb; + } + if (m) { + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XFPUTS("\t", fd); + name_printed = true; + line_length = 0; + for (dependency = + lines->body.line.dependencies; + dependency != NULL; + dependency = dependency->next) { + print_auto_depes(dependency, + fd, + built_this_run, + &line_length, + target_name, + long_jump); + } + XFPUTS("\n", fd); + } + /* If there is a command used, we dump it. */ + if (lines->body.line.command_used != NULL) { + /* + * Only write the target name if it + * wasn't done for the dependencies. + */ + if (!name_printed) { + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + /* + * Write the command lines. + * Prefix each textual line with a tab. + */ + for (cp = lines->body.line.command_used; + cp != NULL; + cp = cp->next) { + char *csp; + int n; + + XPUTC(tab_char, fd); + if (cp->command_line != NULL) { + for (csp = cp-> + command_line-> + string_mb, + n = strlen(cp-> + command_line-> + string_mb); + n > 0; + n--, csp++) { + XPUTC(*csp, fd); + if (*csp == + (int) newline_char) { + XPUTC(tab_char, + fd); + } + } + } + XPUTC(newline_char, fd); + } + } + (void)free(target_name); + } + } + if (fclose(fd) == EOF) { + longjmp(long_jump, LONGJUMP_VALUE); + } + if (attempts == 0) { + if (unlink(make_state->string_mb) != 0 && errno != ENOENT) { + lock_err = errno; /* Save it! unlink() can change errno */ + /* Delete temporary statefile */ + (void) unlink(make_state_tempfile); + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not delete old statefile `%s': %s"), + make_state->string_mb, + errmsg(lock_err)); + } + if (rename(make_state_tempfile, make_state->string_mb) != 0) { + lock_err = errno; /* Save it! unlink() can change errno */ + /* Delete temporary statefile */ + (void) unlink(make_state_tempfile); + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + fatal(gettext("Could not rename `%s' to `%s': %s"), + make_state_tempfile, + make_state->string_mb, + errmsg(lock_err)); + } + } + if ((make_state_lockfile != NULL) && make_state_locked) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } +} + +/* + * print_auto_depes(dependency, fd, built_this_run, + * line_length, target_name, long_jump) + * + * Will print a dependency list for automatic entries. + * + * Parameters: + * dependency The dependency to print + * fd The file to print it to + * built_this_run If on we prefix each line with .BUILT_THIS... + * line_length Pointer to line length var that we update + * target_name We need this when we restart line + * long_jump setjmp/longjmp buffer used for IO error action + * + * Global variables used: + * built_last_make_run The Name ".BUILT_LAST_MAKE_RUN", written + * force The Name " FORCE", compared against + */ +static void +print_auto_depes(register Dependency dependency, register FILE *fd, register Boolean built_this_run, register int *line_length, register char *target_name, jmp_buf long_jump) +{ + if (!dependency->automatic || + dependency->stale || + (dependency->name == force)) { + return; + } + XFWRITE(dependency->name->string_mb, + strlen(dependency->name->string_mb), + fd); + /* + * Check if the dependency line is too long. + * If so, break it and start a new one. + */ + if ((*line_length += (int) strlen(dependency->name->string_mb) + 1) > 450) { + *line_length = 0; + XPUTC(newline_char, fd); + if (built_this_run) { + XFPUTS(built_last_make_run->string_mb, fd); + XPUTC(colon_char, fd); + XPUTC(newline_char, fd); + } + XFPUTS(target_name, fd); + XPUTC(colon_char, fd); + XPUTC(tab_char, fd); + } else { + XFPUTS(" ", fd); + } + return; +} + + diff --git a/bin/svr4.make.rules.file b/bin/svr4.make.rules.file new file mode 100644 index 0000000..cda4edb --- /dev/null +++ b/bin/svr4.make.rules.file @@ -0,0 +1,241 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# +# Copyright 1994 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# +.SUFFIXES: .o .c .c~ .y .y~ .l .l~ .s .s~ .sh .sh~ .h .h~ .f .f~ \ +.C .C~ .Y .Y~ .L .L~ + +MAKE=make +BUILD=build +AR=ar +ARFLAGS=rv +AS=as +ASFLAGS= +CC=cc +CFLAGS=-O +F77=f77 +FFLAGS=-O +GET=get +GFLAGS= +LD=ld +LDFLAGS= +LEX=lex +LFLAGS= +YACC=yacc +YFLAGS= +C++C=CC +C++FLAGS=-O + + +.c: + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) +.c~: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) $*.c -o $@ $(LDFLAGS) + -rm -f $*.c +.f: + $(F77) $(FFLAGS) $< -o $@ $(LDFLAGS) +.f~: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) $*.f -o $@ $(LDFLAGS) + -rm -f $*.f +.s: + $(AS) $(ASFLAGS) $< -o $@ $(LDFLAGS) +.s~: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) $*.s -o $* $(LDFLAGS) + -rm -f $*.s +.sh: + cp $< $@; chmod 0777 $@ +.sh~: + $(GET) $(GFLAGS) $< + cp $*.sh $*; chmod 0777 $@ + -rm -f $*.sh +.C: + $(C++C) $(C++FLAGS) $< -o $@ $(LDFLAGS) +.C~: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) $*.C -o $@ $(LDFLAGS) + -rm -f $*.C + +.c.a: + $(CC) $(CFLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.c.o: + $(CC) $(CFLAGS) -c $< +.c~.a: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[co] +.c~.c: + $(GET) $(GFLAGS) $< +.c~.o: + $(GET) $(GFLAGS) $< + $(CC) $(CFLAGS) -c $*.c + -rm -f $*.c +.f.a: + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.f.o: + $(F77) $(FFLAGS) -c $*.f +.f~.a: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[fo] +.f~.f: + $(GET) $(GFLAGS) $< +.f~.o: + $(GET) $(GFLAGS) $< + $(F77) $(FFLAGS) -c $*.f + -rm -f $*.f +.h~.h: + $(GET) $(GFLAGS) $< +.l.c: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.l.o: + $(LEX) $(LFLAGS) $< + $(CC) $(CFLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.l~.c: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + mv lex.yy.c $@ + -rm -f $*.l +.l~.l: + $(GET) $(GFLAGS) $< +.l~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.l + $(CC) $(CFLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.l + mv lex.yy.o $@ +.s.a: + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o +.s.o: + $(AS) $(ASFLAGS) -o $@ $< +.s~.a: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[so] +.s~.o: + $(GET) $(GFLAGS) $< + $(AS) $(ASFLAGS) -o $*.o $*.s + -rm -f $*.s +.s~.s: + $(GET) $(GFLAGS) $< +.sh~.sh: + $(GET) $(GFLAGS) $< +.y.c: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.y.o: + $(YACC) $(YFLAGS) $< + $(CC) $(CFLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.y~.c: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + mv y.tab.c $*.c + -rm -f $*.y +.y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.y + $(CC) $(CFLAGS) -c y.tab.c + -rm -f y.tab.c $*.y + mv y.tab.o $*.o +.y~.y : + $(GET) $(GFLAGS) $< +.C.a: + $(C++C) $(C++FLAGS) -c $< + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.o +.C.o: + $(C++C) $(C++FLAGS) -c $< +.C~.a: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + $(AR) $(ARFLAGS) $@ $*.o + -rm -f $*.[Co] +.C~.C: + $(GET) $(GFLAGS) $< +.C~.o: + $(GET) $(GFLAGS) $< + $(C++C) $(C++FLAGS) -c $*.C + -rm -f $*.C +.L.C: + $(LEX) $(LFLAGS) $< + mv lex.yy.c $@ +.L.o: + $(LEX) $(LFLAGS) $< + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm lex.yy.c; mv lex.yy.o $@ +.L~.C: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + mv lex.yy.c $@ + -rm -f $*.L +.L~.L: + $(GET) $(GFLAGS) $< +.L~.o: + $(GET) $(GFLAGS) $< + $(LEX) $(LFLAGS) $*.L + $(C++C) $(C++FLAGS) -c lex.yy.c + -rm -f lex.yy.c $*.L + mv lex.yy.c $@ +.Y.C: + $(YACC) $(YFLAGS) $< + mv y.tab.c $@ +.Y.o: + $(YACC) $(YFLAGS) $< + $(C++C) $(C++FLAGS) -c y.tab.c + -rm y.tab.c + mv y.tab.o $@ +.Y~.C: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + mv y.tab.c $*.C + -rm -f $*.Y +.Y~.o: + $(GET) $(GFLAGS) $< + $(YACC) $(YFLAGS) $*.Y + $(C++C) $(C++FLAGS) -c y.tab.c + -rm -f y.tab.c $*.Y + mv y.tab.o $*.o +.Y~.Y : + $(GET) $(GFLAGS) $< + +markfile.o: markfile + echo "static char _sccsid[] = \"`grep @'(#)' markfile`\";" > markfile.c + $(CC) -c markfile.c + -rm -f markfile.c + +.SCCS_GET: + $(GET) $(GFLAGS) s.$@ diff --git a/include/bsd/bsd.h b/include/bsd/bsd.h new file mode 100644 index 0000000..2893d62 --- /dev/null +++ b/include/bsd/bsd.h @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * bsd/bsd.h: Interface definitions to BSD compatibility functions for SVR4. + */ + +#ifndef _BSD_BSD_H +#define _BSD_BSD_H + +#include + + +#ifndef __cplusplus +typedef void (*SIG_PF) (int); +#endif + +#ifdef __cplusplus +extern "C" SIG_PF bsd_signal(int a, SIG_PF b); +#else +extern void (*bsd_signal(int, void (*) (int))) (int); +#endif +extern void bsd_signals(void); + +#endif diff --git a/include/mk/defs.h b/include/mk/defs.h new file mode 100644 index 0000000..5f5beab --- /dev/null +++ b/include/mk/defs.h @@ -0,0 +1,396 @@ +#ifndef _MK_DEFS_H +#define _MK_DEFS_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Included files + */ + +#include + + + +/* + * Defined macros + */ + +#define SKIPSPACE(x) while (*x && \ + ((*x == (int) space_char) || \ + (*x == (int) tab_char) || \ + (*x == (int) comma_char))) { \ + x++; \ + } + +#define SKIPWORD(x) while (*x && \ + (*x != (int) space_char) && \ + (*x != (int) tab_char) && \ + (*x != (int) newline_char) && \ + (*x != (int) comma_char) && \ + (*x != (int) equal_char)) { \ + x++; \ + } + +#define SKIPTOEND(x) while (*x && \ + (*x != (int) newline_char)) { \ + x++; \ + } + +#define PMAKE_DEF_MAX_JOBS 2 /* Default number of parallel jobs. */ + +#define OUT_OF_DATE(a,b) \ + (((a) < (b)) || (((a) == file_doesnt_exist) && ((b) == file_doesnt_exist))) + +#define OUT_OF_DATE_SEC(a,b) \ + (((a).tv_sec < (b).tv_sec) || (((a).tv_sec == file_doesnt_exist.tv_sec) && ((b).tv_sec == file_doesnt_exist.tv_sec))) + +#define SETVAR(name, value, append) \ + setvar_daemon(name, value, append, no_daemon, \ + true, debug_level) +#define MAX(a,b) (((a)>(b))?(a):(b)) +/* + * New feature added to SUN5_0 make, invoke the vanilla svr4 make when + * the USE_SVR4_MAKE environment variable is set. + */ +#define SVR4_MAKE "/usr/ccs/lib/svr4.make" +#define USE_SVR4_MAKE "USE_SVR4_MAKE" +/* + * The standard MAXHOSTNAMELEN is 64. We want 32. + */ +#define MAX_HOSTNAMELEN 32 + + +/* + * typedefs & structs + */ +typedef enum { + no_state, + scan_name_state, + scan_command_state, + enter_dependencies_state, + enter_conditional_state, + enter_equal_state, + illegal_bytes_state, + illegal_eoln_state, + poorly_formed_macro_state, + exit_state +} Reader_state; + +struct _Name_vector { + struct _Name *names[64]; + struct _Chain *target_group[64]; + short used; + struct _Name_vector *next; +}; + +struct _Running { + struct _Running *next; + Doname state; + struct _Name *target; + struct _Name *true_target; + struct _Property *command; + struct _Name *sprodep_value; + char *sprodep_env; + int recursion_level; + Boolean do_get; + Boolean implicit; + Boolean redo; + int auto_count; + struct _Name **automatics; + pid_t pid; + int job_msg_id; + char *stdout_file; + char *stderr_file; + struct _Name *temp_file; + int conditional_cnt; + struct _Name **conditional_targets; + Boolean make_refd; +}; + +typedef enum { + serial_mode, + parallel_mode +} DMake_mode; + +typedef enum { + txt1_mode, + txt2_mode, + html1_mode +} DMake_output_mode; + +struct _Recursive_make { + struct _Recursive_make *next; /* Linked list */ + wchar_t *target;/* Name of target */ + wchar_t *oldline;/* Original line in .nse_depinfo */ + wchar_t *newline;/* New line in .nse_depinfo */ + wchar_t *cond_macrostring; + /* string built from value of + * conditional macros used by + * this target + */ + Boolean removed;/* This target is no longer recursive*/ +}; + +struct _Dyntarget { + struct _Dyntarget *next; + struct _Name *name; +}; + + +/* + * Typedefs for all structs + */ +typedef struct _Cmd_line *Cmd_line, Cmd_line_rec; +typedef struct _Dependency *Dependency, Dependency_rec; +typedef struct _Macro *Macro, Macro_rec; +typedef struct _Name_vector *Name_vector, Name_vector_rec; +typedef struct _Percent *Percent, Percent_rec; +typedef struct _Dyntarget *Dyntarget; +typedef struct _Recursive_make *Recursive_make, Recursive_make_rec; +typedef struct _Running *Running, Running_rec; + + +/* + * extern declarations for all global variables. + * The actual declarations are in globals.cc + */ +extern Boolean allrules_read; +extern Name posix_name; +extern Name svr4_name; +extern Boolean sdot_target; +extern Boolean all_parallel; +extern Boolean assign_done; +extern Boolean build_failed_seen; +extern Name built_last_make_run; +extern Name c_at; +extern Boolean command_changed; +extern Boolean commands_done; +extern Chain conditional_targets; +extern Name conditionals; +extern Boolean continue_after_error; +extern Property current_line; +extern Name current_make_version; +extern Name current_target; +extern short debug_level; +extern Cmd_line default_rule; +extern Name default_rule_name; +extern Name default_target_to_build; +extern Boolean depinfo_already_read; +extern Name dmake_group; +extern Name dmake_max_jobs; +extern Name dmake_mode; +extern DMake_mode dmake_mode_type; +extern Name dmake_output_mode; +extern DMake_output_mode output_mode; +extern Name dmake_odir; +extern Name dmake_rcfile; +extern Name done; +extern Name dot; +extern Name dot_keep_state; +extern Name dot_keep_state_file; +extern Name empty_name; +extern Boolean fatal_in_progress; +extern int file_number; +extern Name force; +extern Name ignore_name; +extern Boolean ignore_errors; +extern Boolean ignore_errors_all; +extern Name init; +extern int job_msg_id; +extern Boolean keep_state; +extern Name make_state; +extern timestruc_t make_state_before; +extern Boolean make_state_locked; +extern Dependency makefiles_used; +extern Name makeflags; +extern Name make_version; +extern char mbs_buffer2[]; +extern char *mbs_ptr; +extern char *mbs_ptr2; +extern Boolean no_action_was_taken; +extern Boolean no_parallel; +extern Name no_parallel_name; +extern Name not_auto; +extern Boolean only_parallel; +extern Boolean parallel; +extern Name parallel_name; +extern Name localhost_name; +extern int parallel_process_cnt; +extern Percent percent_list; +extern Dyntarget dyntarget_list; +extern Name plus; +extern Name pmake_machinesfile; +extern Name precious; +extern Name primary_makefile; +extern Boolean quest; +extern short read_trace_level; +extern Boolean reading_dependencies; +extern int recursion_level; +extern Name recursive_name; +extern short report_dependencies_level; +extern Boolean report_pwd; +extern Boolean rewrite_statefile; +extern Running running_list; +extern char *sccs_dir_path; +extern Name sccs_get_name; +extern Name sccs_get_posix_name; +extern Cmd_line sccs_get_rule; +extern Cmd_line sccs_get_org_rule; +extern Cmd_line sccs_get_posix_rule; +extern Name get_name; +extern Name get_posix_name; +extern Cmd_line get_rule; +extern Cmd_line get_posix_rule; +extern Boolean all_precious; +extern Boolean report_cwd; +extern Boolean silent_all; +extern Boolean silent; +extern Name silent_name; +extern char *stderr_file; +extern char *stdout_file; +extern Boolean stdout_stderr_same; +extern Dependency suffixes; +extern Name suffixes_name; +extern Name sunpro_dependencies; +extern Boolean target_variants; +extern const char *tmpdir; +extern const char *temp_file_directory; +extern Name temp_file_name; +extern short temp_file_number; +extern wchar_t *top_level_target; +extern Boolean touch; +extern Boolean trace_reader; +extern Boolean build_unconditional; +extern pathpt vroot_path; +extern Name wait_name; +extern wchar_t wcs_buffer2[]; +extern wchar_t *wcs_ptr; +extern wchar_t *wcs_ptr2; +extern long int hostid; + +/* + * Declarations of system defined variables + */ +/* On linux this variable is defined in 'signal.h' */ +extern char *sys_siglist[]; + +/* + * Declarations of system supplied functions + */ +extern int file_lock(char *, char *, int *, int); + +/* + * Declarations of functions declared and used by make + */ +extern void add_pending(Name target, int recursion_level, Boolean do_get, Boolean implicit, Boolean redo); +extern void add_running(Name target, Name true_target, Property command, int recursion_level, int auto_count, Name *automatics, Boolean do_get, Boolean implicit); +extern void add_serial(Name target, int recursion_level, Boolean do_get, Boolean implicit); +extern void add_subtree(Name target, int recursion_level, Boolean do_get, Boolean implicit); +extern void append_or_replace_macro_in_dyn_array(ASCII_Dyn_Array *Ar, char *macro); +extern void await_parallel(Boolean waitflg); +extern void build_suffix_list(Name target_suffix); +extern Boolean check_auto_dependencies(Name target, int auto_count, Name *automatics); +extern void check_state(Name temp_file_name); +extern void cond_macros_into_string(Name np, String_rec *buffer); +extern void construct_target_string(); +extern void create_xdrs_ptr(void); +extern void depvar_add_to_list (Name name, Boolean cmdline); +extern Doname doname(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic = false); +extern Doname doname_check(register Name target, register Boolean do_get, register Boolean implicit, register Boolean automatic); +extern Doname doname_parallel(Name target, Boolean do_get, Boolean implicit); +extern Doname dosys(register Name command, register Boolean ignore_error, register Boolean call_make, Boolean silent_error, Boolean always_exec, Name target); +extern void dump_make_state(void); +extern void dump_target_list(void); +extern void enter_conditional(register Name target, Name name, Name value, register Boolean append); +extern void enter_dependencies(register Name target, Chain target_group, register Name_vector depes, register Cmd_line command, register Separator separator); +extern void enter_dependency(Property line, register Name depe, Boolean automatic); +extern void enter_equal(Name name, Name value, register Boolean append); +extern Percent enter_percent(register Name target, Chain target_group, register Name_vector depes, Cmd_line command); +extern Dyntarget enter_dyntarget(register Name target); +extern Name_vector enter_name(String string, Boolean tail_present, register wchar_t *string_start, register wchar_t *string_end, Name_vector current_names, Name_vector *extra_names, Boolean *target_group_seen); +extern Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error); +extern Doname execute_parallel(Property line, Boolean waitflg, Boolean local = false); +extern Doname execute_serial(Property line); +extern timestruc_t& exists(register Name target); +extern void fatal(const char *, ...); +extern void fatal_reader(char *, ...); +extern Doname find_ar_suffix_rule(register Name target, Name true_target, Property *command, Boolean rechecking); +extern Doname find_double_suffix_rule(register Name target, Property *command, Boolean rechecking); +extern Doname find_percent_rule(register Name target, Property *command, Boolean rechecking); +extern int find_run_directory (char *cmd, char *cwd, char *dir, char **pgm, char **run, char *path); +extern Doname find_suffix_rule(Name target, Name target_body, Name target_suffix, Property *command, Boolean rechecking); +extern Chain find_target_groups(register Name_vector target_list, register int i, Boolean reset); +extern void finish_children(Boolean docheck); +extern void finish_running(void); +extern void free_chain(Name_vector ptr); +extern void gather_recursive_deps(void); +extern char *get_current_path(void); +extern int get_job_msg_id(void); +extern wchar_t *getmem_wc(register int size); +/* On linux getwd(char *) is defined in 'unistd.h' */ +#ifdef __cplusplus +extern "C" { +#endif +extern char *getwd(char *); +#ifdef __cplusplus +} +#endif +extern void handle_interrupt(int); +extern Boolean is_running(Name target); +extern void load_cached_names(void); +extern Boolean parallel_ok(Name target, Boolean line_prop_must_exists); +extern void print_dependencies(register Name target, register Property line); +extern void send_job_start_msg(Property line); +extern void send_rsrc_info_msg(int max_jobs, char *hostname, char *username); +extern void print_value(register Name value, Daemon daemon); +extern timestruc_t& read_archive(register Name target); +extern int read_dir(Name dir, wchar_t *pattern, Property line, wchar_t *library); +extern void read_directory_of_file(register Name file); +extern int read_make_machines(Name make_machines_name); +extern Boolean read_simple_file(register Name makefile_name, register Boolean chase_path, register Boolean doname_it, Boolean complain, Boolean must_exist, Boolean report_file, Boolean lock_makefile); +extern void remove_recursive_dep(Name target); +extern void report_recursive_dep(Name target, char *line); +extern void report_recursive_done(void); +extern void report_recursive_init(void); +extern Recursive_make find_recursive_target(Name target); +extern void reset_locals(register Name target, register Property old_locals, register Property conditional, register int index); +extern void set_locals(register Name target, register Property old_locals); +extern void setvar_append(register Name name, register Name value); +extern void setvar_envvar(void); +extern void special_reader(Name target, register Name_vector depes, Cmd_line command); +extern void startup_rxm(); +extern Doname target_can_be_built(register Name target); +extern char *time_to_string(const timestruc_t &time); +extern void update_target(Property line, Doname result); +extern void warning(char *, ...); +extern void write_state_file(int report_recursive, Boolean exiting); +extern Name vpath_translation(register Name cmd); +extern char *make_install_prefix(void); + +#define DEPINFO_FMT_VERSION "VERS2$" +#define VER_LEN strlen(DEPINFO_FMT_VERSION) + + +#endif diff --git a/include/mksh/defs.h b/include/mksh/defs.h new file mode 100644 index 0000000..9ad58c4 --- /dev/null +++ b/include/mksh/defs.h @@ -0,0 +1,945 @@ +#ifndef _MKSH_DEFS_H +#define _MKSH_DEFS_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include /* MB_LEN_MAX */ +#include +#include /* wchar_t */ +#include /* strcmp() */ +#include /* MAXPATHLEN */ +#include /* time_t, caddr_t */ +#include /* pathpt */ +#include /* timestruc_t */ +#include /* errno */ + +#include + +/* + * A type and some utilities for boolean values + */ + +#define false BOOLEAN_false +#define true BOOLEAN_true + +typedef enum { + false = 0, + true = 1, + failed = 0, + succeeded = 1 +} Boolean; +#define BOOLEAN(expr) ((expr) ? true : false) + +/* + * Some random constants (in an enum so dbx knows their values) + */ +enum { + update_delay = 30, /* time between rstat checks */ + ar_member_name_len = 1024, + hashsize = 2048 /* size of hash table */ +}; + + +/* + * Symbols that defines all the different char constants make uses + */ +enum { + ampersand_char = '&', + asterisk_char = '*', + at_char = '@', + backquote_char = '`', + backslash_char = '\\', + bar_char = '|', + braceleft_char = '{', + braceright_char = '}', + bracketleft_char = '[', + bracketright_char = ']', + colon_char = ':', + comma_char = ',', + dollar_char = '$', + doublequote_char = '"', + equal_char = '=', + exclam_char = '!', + greater_char = '>', + hat_char = '^', + hyphen_char = '-', + less_char = '<', + newline_char = '\n', + nul_char = '\0', + numbersign_char = '#', + parenleft_char = '(', + parenright_char = ')', + percent_char = '%', + period_char = '.', + plus_char = '+', + question_char = '?', + quote_char = '\'', + semicolon_char = ';', + slash_char = '/', + space_char = ' ', + tab_char = '\t', + tilde_char = '~' +}; + +/* + * For make i18n. Codeset independent. + * Setup character semantics by identifying all the special characters + * of make, and assigning each an entry in the char_semantics[] vector. + */ +enum { + ampersand_char_entry = 0, /* 0 */ + asterisk_char_entry, /* 1 */ + at_char_entry, /* 2 */ + backquote_char_entry, /* 3 */ + backslash_char_entry, /* 4 */ + bar_char_entry, /* 5 */ + bracketleft_char_entry, /* 6 */ + bracketright_char_entry, /* 7 */ + colon_char_entry, /* 8 */ + dollar_char_entry, /* 9 */ + doublequote_char_entry, /* 10 */ + equal_char_entry, /* 11 */ + exclam_char_entry, /* 12 */ + greater_char_entry, /* 13 */ + hat_char_entry, /* 14 */ + hyphen_char_entry, /* 15 */ + less_char_entry, /* 16 */ + newline_char_entry, /* 17 */ + numbersign_char_entry, /* 18 */ + parenleft_char_entry, /* 19 */ + parenright_char_entry, /* 20 */ + percent_char_entry, /* 21 */ + plus_char_entry, /* 22 */ + question_char_entry, /* 23 */ + quote_char_entry, /* 24 */ + semicolon_char_entry, /* 25 */ + no_semantics_entry /* 26 */ +}; + +/* + * CHAR_SEMANTICS_ENTRIES should be the number of entries above. + * The last entry in char_semantics[] should be blank. + */ +#define CHAR_SEMANTICS_ENTRIES 27 +/* +#define CHAR_SEMANTICS_STRING "&*@`\\|[]:$=!>-\n#()%+?;^<'\"" + */ + +/* + * Some utility macros + */ +#define ALLOC(x) ((struct _##x *)getmem(sizeof (struct _##x))) +#define ALLOC_WC(x) ((wchar_t *)getmem((x) * SIZEOFWCHAR_T)) +#define FIND_LENGTH -1 +#define GETNAME(a,b) getname_fn((a), (b), false) +#define IS_EQUAL(a,b) (!strcmp((a), (b))) +#define IS_EQUALN(a,b,n) (!strncmp((a), (b), (n))) +#define IS_WEQUAL(a,b) (!wcscmp((a), (b))) +#define IS_WEQUALN(a,b,n) (!wcsncmp((a), (b), (n))) +#define MBLEN(a) mblen((a), MB_LEN_MAX) +#define MBSTOWCS(a,b) (void) mbstowcs_with_check((a), (b), MAXPATHLEN) +#define MBTOWC(a,b) mbtowc((a), (b), MB_LEN_MAX) +#define SIZEOFWCHAR_T (sizeof (wchar_t)) +#define VSIZEOF(v) (sizeof (v) / sizeof ((v)[0])) +#define WCSTOMBS(a,b) (void) wcstombs((a), (b), (MAXPATHLEN * MB_LEN_MAX)) +#define WCTOMB(a,b) (void) wctomb((a), (b)) +#define HASH(v, c) (v = (v)*31 + (unsigned int)(c)) + +extern void mbstowcs_with_check(wchar_t *pwcs, const char *s, size_t n); + +/* + * Bits stored in funny vector to classify chars + */ +enum { + dollar_sem = 0001, + meta_sem = 0002, + percent_sem = 0004, + wildcard_sem = 0010, + command_prefix_sem = 0020, + special_macro_sem = 0040, + colon_sem = 0100, + parenleft_sem = 0200 +}; + +/* + * Type returned from doname class functions + */ +typedef enum { + build_dont_know = 0, + build_failed, + build_ok, + build_in_progress, + build_running, /* PARALLEL & DISTRIBUTED */ + build_pending, /* PARALLEL & DISTRIBUTED */ + build_serial, /* PARALLEL & DISTRIBUTED */ + build_subtree /* PARALLEL & DISTRIBUTED */ +} Doname; + +/* + * The String struct defines a string with the following layout + * "xxxxxxxxxxxxxxxCxxxxxxxxxxxxxxx________" + * ^ ^ ^ ^ + * | | | | + * buffer.start text.p text.end buffer.end + * text.p points to the next char to read/write. + */ +struct _String { + struct Text { + wchar_t *p; /* Read/Write pointer */ + wchar_t *end; /* Read limit pointer */ + } text; + struct Physical_buffer { + wchar_t *start; /* Points to start of buffer */ + wchar_t *end; /* End of physical buffer */ + } buffer; + Boolean free_after_use:1; +}; + +#define STRING_BUFFER_LENGTH 1024 +#define INIT_STRING_FROM_STACK(str, buf) { \ + str.buffer.start = (buf); \ + str.text.p = (buf); \ + str.text.end = NULL; \ + str.buffer.end = (buf) \ + + (sizeof (buf)/SIZEOFWCHAR_T); \ + str.free_after_use = false; \ + } + +#define APPEND_NAME(np, dest, len) append_string((np)->string_mb, (dest), (len)); + +class Wstring { + public: + struct _String string; + wchar_t string_buf[STRING_BUFFER_LENGTH]; + + public: + Wstring(); + Wstring(struct _Name * name); + ~Wstring(); + + void init(struct _Name * name); + void init(wchar_t * name, unsigned length); + unsigned length() { + return wcslen(string.buffer.start); + }; + void append_to_str(struct _String * str, unsigned off, unsigned length); + + wchar_t * get_string() { + return string.buffer.start; + }; + + wchar_t * get_string(unsigned off) { + return string.buffer.start + off; + }; + + Boolean equaln(wchar_t * str, unsigned length); + Boolean equal(wchar_t * str); + Boolean equal(wchar_t * str, unsigned off); + Boolean equal(wchar_t * str, unsigned off, unsigned length); + + Boolean equaln(Wstring * str, unsigned length); + Boolean equal(Wstring * str); + Boolean equal(Wstring * str, unsigned off); + Boolean equal(Wstring * str, unsigned off, unsigned length); +}; + + +/* + * Used for storing the $? list and also for the "target + target:" + * construct. + */ +struct _Chain { + struct _Chain *next; + struct _Name *name; + struct _Percent *percent_member; +}; + +/* + * Stores one command line for a rule + */ +struct _Cmd_line { + struct _Cmd_line *next; + struct _Name *command_line; + Boolean make_refd:1; /* $(MAKE) referenced? */ + /* + * Remember any command line prefixes given + */ + Boolean ignore_command_dependency:1; /* `?' */ + Boolean assign:1; /* `=' */ + Boolean ignore_error:1; /* `-' */ + Boolean silent:1; /* `@' */ + Boolean always_exec:1; /* `+' */ +}; + +/* + * Linked list of targets/files + */ +struct _Dependency { + struct _Dependency *next; + struct _Name *name; + Boolean automatic:1; + Boolean stale:1; + Boolean built:1; +}; + +/* + * The specials are markers for targets that the reader should special case + */ +typedef enum { + no_special, + built_last_make_run_special, + default_special, + get_posix_special, + get_special, + ignore_special, + keep_state_file_special, + keep_state_special, + make_version_special, + no_parallel_special, + parallel_special, + posix_special, + precious_special, + sccs_get_posix_special, + sccs_get_special, + silent_special, + suffixes_special, + svr4_special, + localhost_special +} Special; + +typedef enum { + no_colon, + one_colon, + two_colon, + equal_seen, + conditional_seen, + none_seen +} Separator; + +/* + * Magic values for the timestamp stored with each name object + */ + + +extern const timestruc_t file_no_time; +extern const timestruc_t file_doesnt_exist; +extern const timestruc_t file_is_dir; +extern const timestruc_t file_min_time; +extern const timestruc_t file_max_time; + +/* + * Each Name has a list of properties + * The properties are used to store information that only + * a subset of the Names need + */ +typedef enum { + no_prop, + conditional_prop, + line_prop, + macro_prop, + makefile_prop, + member_prop, + recursive_prop, + sccs_prop, + suffix_prop, + target_prop, + time_prop, + vpath_alias_prop, + long_member_name_prop, + macro_append_prop, + env_mem_prop +} Property_id; + +typedef enum { + no_daemon = 0, + chain_daemon +} Daemon; + +struct _Env_mem { + char *value; +}; + +struct _Macro_appendix { + struct _Name *value; + struct _Name *value_to_append; +}; + +struct _Macro { + /* + * For "ABC = xyz" constructs + * Name "ABC" get one macro prop + */ + struct _Name *value; + Boolean exported:1; + Boolean read_only:1; + /* + * This macro is defined conditionally + */ + Boolean is_conditional:1; + /* + * The list for $? is stored as a structured list that + * is translated into a string iff it is referenced. + * This is why some macro values need a daemon. + */ + Daemon daemon:2; +}; + +struct _Macro_list { + struct _Macro_list *next; + char *macro_name; + char *value; +}; + +enum sccs_stat { + DONT_KNOW_SCCS = 0, + NO_SCCS, + HAS_SCCS +}; + +struct _Name { + struct _Property *prop; /* List of properties */ + char *string_mb; /* Multi-byte name string */ + struct { + unsigned int length; + } hash; + struct { + timestruc_t time; /* Modification */ + int stat_errno; /* error from "stat" */ + off_t size; /* Of file */ + mode_t mode; /* Of file */ + Boolean is_file:1; + Boolean is_dir:1; + Boolean is_sym_link:1; + Boolean is_precious:1; + enum sccs_stat has_sccs:2; + } stat; + /* + * Count instances of :: definitions for this target + */ + short colon_splits; + /* + * We only clear the automatic depes once per target per report + */ + short temp_file_number; + /* + * Count how many conditional macros this target has defined + */ + short conditional_cnt; + /* + * A conditional macro was used when building this target + */ + Boolean depends_on_conditional:1; + /* + * Pointer to list of conditional macros which were used to build + * this target + */ + struct _Macro_list *conditional_macro_list; + Boolean has_member_depe:1; + Boolean is_member:1; + /* + * This target is a directory that has been read + */ + Boolean has_read_dir:1; + /* + * This name is a macro that is now being expanded + */ + Boolean being_expanded:1; + /* + * This name is a magic name that the reader must know about + */ + Special special_reader:5; + Doname state:3; + Separator colons:3; + Boolean has_depe_list_expanded:1; + Boolean suffix_scan_done:1; + Boolean has_complained:1; /* For sccs */ + /* + * This target has been built during this make run + */ + Boolean ran_command:1; + Boolean with_squiggle:1; /* for .SUFFIXES */ + Boolean without_squiggle:1; /* for .SUFFIXES */ + Boolean has_read_suffixes:1; /* Suffix list cached*/ + Boolean has_suffixes:1; + Boolean has_target_prop:1; + Boolean has_vpath_alias_prop:1; + Boolean dependency_printed:1; /* For dump_make_state() */ + Boolean dollar:1; /* In namestring */ + Boolean meta:1; /* In namestring */ + Boolean percent:1; /* In namestring */ + Boolean wildcard:1; /* In namestring */ + Boolean has_parent:1; + Boolean is_target:1; + Boolean has_built:1; + Boolean colon:1; /* In namestring */ + Boolean parenleft:1; /* In namestring */ + Boolean has_recursive_dependency:1; + Boolean has_regular_dependency:1; + Boolean is_double_colon:1; + Boolean is_double_colon_parent:1; + Boolean has_long_member_name:1; + /* + * allowed to run in parallel + */ + Boolean parallel:1; + /* + * not allowed to run in parallel + */ + Boolean no_parallel:1; + /* + * used in dependency_conflict + */ + Boolean checking_subtree:1; + Boolean added_pattern_conditionals:1; + /* + * rechecking target for possible rebuild + */ + Boolean rechecking_target:1; + /* + * build this target in silent mode + */ + Boolean silent_mode:1; + /* + * build this target in ignore error mode + */ + Boolean ignore_error_mode:1; + Boolean dont_activate_cond_values:1; + /* + * allowed to run serially on local host + */ + Boolean localhost:1; +}; + +/* + * Stores the % matched default rules + */ +struct _Percent { + struct _Percent *next; + struct _Name **patterns; + struct _Name *name; + struct _Percent *dependencies; + struct _Cmd_line *command_template; + struct _Chain *target_group; + int patterns_total; + Boolean being_expanded; +}; + +struct Conditional { + /* + * For "foo := ABC [+]= xyz" constructs + * Name "foo" gets one conditional prop + */ + struct _Name *target; + struct _Name *name; + struct _Name *value; + int sequence; + Boolean append:1; +}; + +struct Line { + /* + * For "target : dependencies" constructs + * Name "target" gets one line prop + */ + struct _Cmd_line *command_template; + struct _Cmd_line *command_used; + struct _Dependency *dependencies; + timestruc_t dependency_time; + struct _Chain *target_group; + Boolean is_out_of_date:1; + Boolean sccs_command:1; + Boolean command_template_redefined:1; + Boolean dont_rebuild_command_used:1; + /* + * Values for the dynamic macros + */ + struct _Name *target; + struct _Name *star; + struct _Name *less; + struct _Name *percent; + struct _Chain *query; +}; + +struct Makefile { + /* + * Names that reference makefiles gets one prop + */ + wchar_t *contents; + off_t size; +}; + +struct Member { + /* + * For "lib(member)" and "lib((entry))" constructs + * Name "lib(member)" gets one member prop + * Name "lib((entry))" gets one member prop + * The member field is filled in when the prop is refd + */ + struct _Name *library; + struct _Name *entry; + struct _Name *member; +}; + +struct Recursive { + /* + * For "target: .RECURSIVE dir makefiles" constructs + * Used to keep track of recursive calls to make + * Name "target" gets one recursive prop + */ + struct _Name *directory; + struct _Name *target; + struct _Dependency *makefiles; + Boolean has_built; + Boolean in_depinfo; +}; + +struct Sccs { + /* + * Each file that has a SCCS s. file gets one prop + */ + struct _Name *file; +}; + +struct Suffix { + /* + * Cached list of suffixes that can build this target + * suffix is built from .SUFFIXES + */ + struct _Name *suffix; + struct _Cmd_line *command_template; +}; + +struct Target { + /* + * For "target:: dependencies" constructs + * The "::" construct is handled by converting it to + * "foo: 1@foo" + "1@foo: dependecies" + * "1@foo" gets one target prop + * This target prop cause $@ to be bound to "foo" + * not "1@foo" when the rule is evaluated + */ + struct _Name *target; +}; + +struct STime { + /* + * Save the original time for :: targets + */ + timestruc_t time; +}; + +struct Vpath_alias { + /* + * If a file was found using the VPATH it gets + * a vpath_alias prop + */ + struct _Name *alias; +}; + +struct Long_member_name { + /* + * Targets with a truncated member name carries + * the full lib(member) name for the state file + */ + struct _Name *member_name; +}; + +union Body { + struct _Macro macro; + struct Conditional conditional; + struct Line line; + struct Makefile makefile; + struct Member member; + struct Recursive recursive; + struct Sccs sccs; + struct Suffix suffix; + struct Target target; + struct STime time; + struct Vpath_alias vpath_alias; + struct Long_member_name long_member_name; + struct _Macro_appendix macro_appendix; + struct _Env_mem env_mem; +}; + +#define PROPERTY_HEAD_SIZE (sizeof (struct _Property)-sizeof (union Body)) +struct _Property { + struct _Property *next; + Property_id type:4; + union Body body; +}; + +/* Structure for dynamic "ascii" arrays */ +struct ASCII_Dyn_Array { + char *start; + size_t size; +}; + +struct _Envvar { + struct _Name *name; + struct _Name *value; + struct _Envvar *next; + char *env_string; + Boolean already_put:1; +}; + +/* + * Macros for the reader + */ +#define GOTO_STATE(new_state) { \ + SET_STATE(new_state); \ + goto enter_state; \ + } +#define SET_STATE(new_state) state = (new_state) + +#define UNCACHE_SOURCE() if (source != NULL) { \ + source->string.text.p = source_p; \ + } +#define CACHE_SOURCE(comp) if (source != NULL) { \ + source_p = source->string.text.p - \ + (comp); \ + source_end = source->string.text.end; \ + } +#define GET_NEXT_BLOCK_NOCHK(source) { UNCACHE_SOURCE(); \ + source = get_next_block_fn(source); \ + CACHE_SOURCE(0) \ + } +#define GET_NEXT_BLOCK(source) { GET_NEXT_BLOCK_NOCHK(source); \ + if (source != NULL && source->error_converting) { \ + GOTO_STATE(illegal_bytes_state); \ + } \ + } +#define GET_CHAR() ((source == NULL) || \ + (source_p >= source_end) ? 0 : *source_p) + +struct _Source { + struct _String string; + struct _Source *previous; + off_t bytes_left_in_file; + short fd; + Boolean already_expanded:1; + Boolean error_converting:1; + char *inp_buf; + char *inp_buf_end; + char *inp_buf_ptr; +}; + +typedef enum { + reading_nothing, + reading_makefile, + reading_statefile, + rereading_statefile, + reading_cpp_file +} Makefile_type; + +/* + * Typedefs for all structs + */ +typedef struct _Chain *Chain, Chain_rec; +typedef struct _Envvar *Envvar, Envvar_rec; +typedef struct _Macro_list *Macro_list, Macro_list_rec; +typedef struct _Name *Name, Name_rec; +typedef struct _Property *Property, Property_rec; +typedef struct _Source *Source, Source_rec; +typedef struct _String *String, String_rec; + +/* + * name records hash table. + */ +struct Name_set { +private: + // single node in a tree + struct entry { + entry(Name name_, entry *parent_) : + name(name_), + parent(parent_), + left(0), + right(0), + depth(1) + {} + + Name name; + + entry *parent; + entry *left; + entry *right; + unsigned depth; + + void setup_depth() { + unsigned rdepth = (right != 0) ? right->depth : 0; + unsigned ldepth = (left != 0) ? left->depth : 0; + depth = 1 + ((ldepth > rdepth) ? ldepth : rdepth); + } + }; + +public: + // make iterator a friend of Name_set to have access to struct entry + struct iterator; + friend struct Name_set::iterator; + + // iterator over tree nodes + struct iterator { + public: + // constructors + iterator() : node(0) {} + iterator(entry *node_) : node(node_) {} + + // dereference operator + Name operator->() const { return node->name; } + + // conversion operator + operator Name() { return node->name; } + + // assignment operator + iterator& operator=(const iterator &o) { node = o.node; return *this; } + + // equality/inequality operators + int operator==(const iterator &o) const { return (node == o.node); } + int operator!=(const iterator &o) const { return (node != o.node); } + + // pre/post increment operators + iterator& operator++(); + iterator operator++(int) { iterator it = *this; ++*this; return it; } + + private: + // the node iterator points to + entry *node; + }; + +public: + // constructor + Name_set() : root(0) {} + + // lookup, insert and remove operations + Name lookup(const char *key); + Name insert(const char *key, Boolean &found); + void insert(Name name); + + // begin/end iterators + iterator begin() const; + iterator end() const { return iterator(); } + +private: + // rebalance given node + void rebalance(entry *node); + +private: + // tree root + entry *root; +}; + +/* + * extern declarations for all global variables. + * The actual declarations are in globals.cc + */ +extern char char_semantics[]; +extern wchar_t char_semantics_char[]; +extern Macro_list cond_macro_list; +extern Boolean conditional_macro_used; +extern Boolean do_not_exec_rule; /* `-n' */ +extern Boolean dollarget_seen; +extern Boolean dollarless_flag; +extern Name dollarless_value; +extern char **environ; +extern Envvar envvar; +extern int exit_status; +extern wchar_t *file_being_read; +/* Variable gnu_style=true if env. var. SUN_MAKE_COMPAT_MODE=GNU (RFE 4866328) */ +extern Boolean gnu_style; +extern Name_set hashtab; +extern Name host_arch; +extern Name host_mach; +extern int line_number; +extern char *make_state_lockfile; +extern Boolean make_word_mentioned; +extern Makefile_type makefile_type; +extern char mbs_buffer[]; +extern Name path_name; +extern Boolean posix; +extern Name query; +extern Boolean query_mentioned; +extern Name hat; +extern Boolean reading_environment; +extern Name shell_name; +extern Boolean svr4; +extern Name target_arch; +extern Name target_mach; +extern Boolean tilde_rule; +extern wchar_t wcs_buffer[]; +extern Boolean working_on_targets; +extern Name virtual_root; +extern Boolean vpath_defined; +extern Name vpath_name; +extern Boolean make_state_locked; +extern Boolean out_err_same; +extern pid_t childPid; + +/* + * RFE 1257407: make does not use fine granularity time info available from stat. + * High resolution time comparison. + */ + +inline int +operator==(const timestruc_t &t1, const timestruc_t &t2) { + return ((t1.tv_sec == t2.tv_sec) && (t1.tv_nsec == t2.tv_nsec)); +} + +inline int +operator!=(const timestruc_t &t1, const timestruc_t &t2) { + return ((t1.tv_sec != t2.tv_sec) || (t1.tv_nsec != t2.tv_nsec)); +} + +inline int +operator>(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec > t2.tv_nsec); + } + return (t1.tv_sec > t2.tv_sec); +} + +inline int +operator>=(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec >= t2.tv_nsec); + } + return (t1.tv_sec > t2.tv_sec); +} + +inline int +operator<(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec < t2.tv_nsec); + } + return (t1.tv_sec < t2.tv_sec); +} + +inline int +operator<=(const timestruc_t &t1, const timestruc_t &t2) { + if (t1.tv_sec == t2.tv_sec) { + return (t1.tv_nsec <= t2.tv_nsec); + } + return (t1.tv_sec < t2.tv_sec); +} + +#endif diff --git a/include/mksh/dosys.h b/include/mksh/dosys.h new file mode 100644 index 0000000..18e3392 --- /dev/null +++ b/include/mksh/dosys.h @@ -0,0 +1,37 @@ +#ifndef _MKSH_DOSYS_H +#define _MKSH_DOSYS_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include + +extern Boolean await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs, int job_msg_id); +extern int doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio); +extern int doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio); +extern void redirect_io(char *stdout_file, char *stderr_file); +extern void sh_command2string(register String command, register String destination); + +#endif diff --git a/include/mksh/globals.h b/include/mksh/globals.h new file mode 100644 index 0000000..98a8766 --- /dev/null +++ b/include/mksh/globals.h @@ -0,0 +1,30 @@ +#ifndef _MKSH_GLOBALS_H +#define _MKSH_GLOBALS_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +#endif diff --git a/include/mksh/i18n.h b/include/mksh/i18n.h new file mode 100644 index 0000000..08f7e61 --- /dev/null +++ b/include/mksh/i18n.h @@ -0,0 +1,33 @@ +#ifndef _MKSH_I18N_H +#define _MKSH_I18N_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern int get_char_semantics_entry(wchar_t ch); +extern char get_char_semantics_value(wchar_t ch); + +#endif diff --git a/include/mksh/libmksh_init.h b/include/mksh/libmksh_init.h new file mode 100644 index 0000000..b10b5e0 --- /dev/null +++ b/include/mksh/libmksh_init.h @@ -0,0 +1,31 @@ +#ifndef _MKSH_INIT_H +#define _MKSH_INIT_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +int libmksh_init() +void libmksh_fini(); + +#endif diff --git a/include/mksh/macro.h b/include/mksh/macro.h new file mode 100644 index 0000000..2068d48 --- /dev/null +++ b/include/mksh/macro.h @@ -0,0 +1,36 @@ +#ifndef _MKSH_MACRO_H +#define _MKSH_MACRO_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2002 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern void expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd); +extern void expand_value(Name value, register String destination, Boolean cmd); +extern Name getvar(register Name name); + +extern Property setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level); + +#endif diff --git a/include/mksh/misc.h b/include/mksh/misc.h new file mode 100644 index 0000000..f3504bb --- /dev/null +++ b/include/mksh/misc.h @@ -0,0 +1,55 @@ +#ifndef _MKSH_MISC_H +#define _MKSH_MISC_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include + +extern void append_char(wchar_t from, register String to); +extern Property append_prop(register Name target, register Property_id type); +extern void append_string(register wchar_t *from, register String to, register int length); +extern void enable_interrupt(register void (*handler) (int)); +extern char *errmsg(int errnum); +extern void fatal_mksh(const char *message, ...); +extern void fatal_reader_mksh(const char *pattern, ...); +extern char *get_current_path_mksh(void); +extern Property get_prop(register Property start, register Property_id type); +extern char *getmem(register int size); +extern Name getname_fn(wchar_t *name, register int len, register Boolean dont_enter, register Boolean * foundp = NULL); +extern void store_name(Name name); +extern void free_name(Name name); +extern void handle_interrupt_mksh(int); +extern Property maybe_append_prop(register Name target, register Property_id type); +extern void retmem(wchar_t *p); +extern void retmem_mb(caddr_t p); +extern void setup_char_semantics(void); +extern void setup_interrupt(register void (*handler) (int)); +extern void warning_mksh(char * message, ...); + +extern void append_string(register char *from, register String to, register int length); +extern wchar_t *get_wstring(char * from); + + +#endif diff --git a/include/mksh/mksh.h b/include/mksh/mksh.h new file mode 100644 index 0000000..5c0804e --- /dev/null +++ b/include/mksh/mksh.h @@ -0,0 +1,37 @@ +#ifndef _MKSH_MKSH_H +#define _MKSH_MKSH_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * Included files + */ + +#include +#include + + +#endif diff --git a/include/mksh/read.h b/include/mksh/read.h new file mode 100644 index 0000000..7866d3d --- /dev/null +++ b/include/mksh/read.h @@ -0,0 +1,33 @@ +#ifndef _MKSH_READ_H +#define _MKSH_READ_H +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern Source get_next_block_fn(register Source source); + +#endif diff --git a/include/vroot/args.h b/include/vroot/args.h new file mode 100644 index 0000000..09bb4b4 --- /dev/null +++ b/include/vroot/args.h @@ -0,0 +1,63 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1999 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _ARGS_H_ +#define _ARGS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef enum { rw_read, rw_write} rwt, *rwpt; + +extern void translate_with_thunk(register char *filename, int (*thunk) (char *), pathpt path_vector, pathpt vroot_vector, rwt rw); + +union Args { + struct { int mode;} access; + struct { int mode;} chmod; + struct { int user; int group;} chown; + struct { int mode;} creat; + struct { char **argv; char **environ;} execve; + struct { struct stat *buffer;} lstat; + struct { int mode;} mkdir; + struct { char *name; int mode;} mount; + struct { int flags; int mode;} open; + struct { char *buffer; int buffer_size;} readlink; + struct { struct stat *buffer;} stat; + struct { int length;} truncate; + struct { struct timeval *time;} utimes; +}; + +extern union Args vroot_args; +extern int vroot_result; + +#endif diff --git a/include/vroot/report.h b/include/vroot/report.h new file mode 100644 index 0000000..6edd863 --- /dev/null +++ b/include/vroot/report.h @@ -0,0 +1,48 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#ifndef _REPORT_H_ +#define _REPORT_H_ + +#include + +extern FILE *get_report_file(void); +extern char *get_target_being_reported_for(void); +extern void report_dependency(const char *name); +extern int file_lock(char *name, char *lockname, int *file_locked, int timeout); + +#define SUNPRO_DEPENDENCIES "SUNPRO_DEPENDENCIES" +#define LD "LD" +#define COMP "COMP" + +/* + * These relate to Sun's ancient source control system that predated TeamWare, + * named NSE. They appear to be used regardless of its presence, however, and + * so linger. + */ +#define NSE_DEPINFO ".nse_depinfo" +#define NSE_DEPINFO_LOCK ".nse_depinfo.lock" + +#endif diff --git a/include/vroot/vroot.h b/include/vroot/vroot.h new file mode 100644 index 0000000..cf84681 --- /dev/null +++ b/include/vroot/vroot.h @@ -0,0 +1,61 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#ifndef _VROOT_H_ +#define _VROOT_H_ + +#include +#include + +#define VROOT_DEFAULT ((pathpt)-1) + +typedef struct { + char *path; + short length; +} pathcellt, *pathcellpt, patht; +typedef patht *pathpt; + +extern void add_dir_to_path(const char *path, register pathpt *pointer, register int position); +extern void flush_path_cache(void); +extern void flush_vroot_cache(void); +extern const char *get_path_name(void); +extern char *get_vroot_path(register char **vroot, register char **path, register char **filename); +extern const char *get_vroot_name(void); +extern int open_vroot(char *path, int flags, int mode, pathpt vroot_path, pathpt vroot_vroot); +extern pathpt parse_path_string(register char *string, register int remove_slash); +extern void scan_path_first(void); +extern void scan_vroot_first(void); +extern void set_path_style(int style); + +extern int access_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot); + +extern int execve_vroot(char *path, char **argv, char **environ, pathpt vroot_path, pathpt vroot_vroot); + +extern int lstat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot); +extern int stat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot); +extern int readlink_vroot(char *path, char *buffer, int buffer_size, pathpt vroot_path, pathpt vroot_vroot); + +#endif diff --git a/lib/bsd/bsd.cc b/lib/bsd/bsd.cc new file mode 100644 index 0000000..931a528 --- /dev/null +++ b/lib/bsd/bsd.cc @@ -0,0 +1,73 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +#include + +/* External references. + */ + +/* Forward references. + */ + +/* Static data. + */ + +extern SIG_PF +bsd_signal (int Signal, SIG_PF Handler) +{ + auto SIG_PF previous_handler; +#ifdef sun + previous_handler = sigset (Signal, Handler); +#else + auto struct sigaction new_action; + auto struct sigaction old_action; + + new_action.sa_flags = SA_SIGINFO; + new_action.sa_handler = (void (*) ()) Handler; + (void) sigemptyset (&new_action.sa_mask); + (void) sigaddset (&new_action.sa_mask, Signal); + + (void) sigaction (Signal, &new_action, &old_action); + + previous_handler = (SIG_PF) old_action.sa_handler; +#endif + return previous_handler; +} + +extern void +bsd_signals (void) +{ + static int initialized = 0; + + if (initialized == 0) + { + initialized = 1; + } + + return; +} diff --git a/lib/makestate/ld_file.c b/lib/makestate/ld_file.c new file mode 100644 index 0000000..b491b2a --- /dev/null +++ b/lib/makestate/ld_file.c @@ -0,0 +1,188 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma init(ld_support_init) + +#include +#include +#include +#include +#include +#include +#include + +#define SUNPRO_DEPENDENCIES "SUNPRO_DEPENDENCIES" + +/* + * Linked list of strings - used to keep lists of names + * of directories or files. + */ + +struct Stritem { + char * str; + void * next; +}; + +typedef struct Stritem Stritem; + +static char * depend_file = NULL; +static Stritem * list = NULL; + + +void mk_state_init() +{ + depend_file = getenv(SUNPRO_DEPENDENCIES); +} /* mk_state_init() */ + + + +static void +prepend_str(Stritem **list, const char * str) +{ + Stritem * new; + char * newstr; + + if (!(new = calloc(1, sizeof (Stritem)))) { + perror("libmakestate.so"); + return; + } /* if */ + + if (!(newstr = malloc(strlen(str) + 1))) { + perror("libmakestate.so"); + return; + } /* if */ + + new->str = strcpy(newstr, str); + new->next = *list; + *list = new; + +} /* prepend_str() */ + + +void +mk_state_collect_dep(const char * file) +{ + /* + * SUNPRO_DEPENDENCIES wasn't set, we don't collect .make.state + * information. + */ + if (!depend_file) + return; + + prepend_str(&list, file); + +} /* mk_state_collect_dep() */ + + +void +mk_state_update_exit() +{ + Stritem * cur; + char lockfile[MAXPATHLEN], * err, * space, * target; + FILE * ofp; + extern char * file_lock(char *, char *, int); + + if (!depend_file) + return; + + if ((space = strchr(depend_file, ' ')) == NULL) + return; + *space = '\0'; + target = &space[1]; + + (void) sprintf(lockfile, "%s.lock", depend_file); + if ((err = file_lock(depend_file, lockfile, 0))) { + (void) fprintf(stderr, "%s\n", err); + return; + } /* if */ + + if (!(ofp = fopen(depend_file, "a"))) + return; + + if (list) + (void) fprintf(ofp, "%s: ", target); + + for (cur = list; cur; cur = cur->next) + (void) fprintf(ofp, " %s", cur->str); + + (void) fputc('\n', ofp); + + (void) fclose(ofp); + (void) unlink(lockfile); + *space = ' '; + +} /* mk_state_update_exit() */ + +static void +/* LINTED static unused */ +ld_support_init() +{ + mk_state_init(); + +} /* ld_support_init() */ + +/* ARGSUSED */ +void +ld_file(const char * file, const Elf_Kind ekind, int flags, Elf *elf) +{ + if(! ((flags & LD_SUP_DERIVED) && !(flags & LD_SUP_EXTRACTED))) + return; + + mk_state_collect_dep(file); + +} /* ld_file */ + +void +ld_atexit(int exit_code) +{ + if (exit_code) + return; + + mk_state_update_exit(); + +} /* ld_atexit() */ + +/* + * Supporting 64-bit objects + */ +void +ld_file64(const char * file, const Elf_Kind ekind, int flags, Elf *elf) +{ + if(! ((flags & LD_SUP_DERIVED) && !(flags & LD_SUP_EXTRACTED))) + return; + + mk_state_collect_dep(file); + +} /* ld_file64 */ + +void +ld_atexit64(int exit_code) +{ + if (exit_code) + return; + + mk_state_update_exit(); + +} /* ld_atexit64() */ diff --git a/lib/makestate/lock.c b/lib/makestate/lock.c new file mode 100644 index 0000000..38c30e3 --- /dev/null +++ b/lib/makestate/lock.c @@ -0,0 +1,172 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* errno */ + +#if defined(_LP64) +/* + * The symbols _sys_errlist and _sys_nerr are not visible in the + * LP64 libc. Use strerror(3C) instead. + */ +#else /* #_LP64 */ +extern char * sys_errlist[]; +extern int sys_nerr; +#endif /* #_LP64 */ + +static void file_lock_error(); + +/* + * This code stolen from the NSE library and changed to not depend + * upon any NSE routines or header files. + * + * Simple file locking. + * Create a symlink to a file. The "test and set" will be + * atomic as creating the symlink provides both functions. + * + * The timeout value specifies how long to wait for stale locks + * to disappear. If the lock is more than 'timeout' seconds old + * then it is ok to blow it away. This part has a small window + * of vunerability as the operations of testing the time, + * removing the lock and creating a new one are not atomic. + * It would be possible for two processes to both decide to blow + * away the lock and then have process A remove the lock and establish + * its own, and then then have process B remove the lock which accidentily + * removes A's lock rather than the stale one. + * + * A further complication is with the NFS. If the file in question is + * being served by an NFS server, then its time is set by that server. + * We can not use the time on the client machine to check for a stale + * lock. Therefore, a temp file on the server is created to get + * the servers current time. + * + * Returns an error message. NULL return means the lock was obtained. + * + */ +char * +file_lock(char * name, char * lockname, int timeout) +{ + int r; + int fd; + struct stat statb; + struct stat fs_statb; + char tmpname[MAXPATHLEN]; + static char msg[MAXPATHLEN]; + + if (timeout <= 0) { + timeout = 15; + } + for (;;) { + r = symlink(name, lockname); + if (r == 0) { + return (NULL); + } + if (errno != EEXIST) { + file_lock_error(msg, name, + (const char *)"symlink(%s, %s)", name, lockname); + return (msg); + } + for (;;) { + (void) sleep(1); + r = lstat(lockname, &statb); + if (r == -1) { + /* + * The lock must have just gone away - try + * again. + */ + break; + } + + /* + * With the NFS the time given a file is the time on + * the file server. This time may vary from the + * client's time. Therefore, we create a tmpfile in + * the same directory to establish the time on the + * server and use this time to see if the lock has + * expired. + */ + (void) sprintf(tmpname, "%s.XXXXXX", lockname); + (void) mktemp(tmpname); + fd = creat(tmpname, 0666); + if (fd != -1) { + (void) close(fd); + } else { + file_lock_error(msg, name, + (const char *)"creat(%s)", tmpname); + return (msg); + } + if (stat(tmpname, &fs_statb) == -1) { + file_lock_error(msg, name, + (const char *)"stat(%s)", tmpname); + return (msg); + } + (void) unlink(tmpname); + if (statb.st_mtime + timeout < fs_statb.st_mtime) { + /* + * The lock has expired - blow it away. + */ + (void) unlink(lockname); + break; + } + } + } + /* NOTREACHED */ +} + +/* + * Format a message telling why the lock could not be created. + */ +/* VARARGS4 */ +static void +file_lock_error(char * msg, char * file, const char * str, char * arg1, + char * arg2) +{ + int len; + + (void) sprintf(msg, "Could not lock file `%s'; ", file); + len = strlen(msg); + (void) sprintf(&msg[len], str, arg1, arg2); + (void) strcat(msg, " failed - "); +#if defined(_LP64) + /* Needs to be changed to use strerror(3C) instead. */ + len = strlen(msg); + (void) sprintf(&msg[len], "errno %d", errno); +#else /* #_LP64 */ + if (errno < sys_nerr) { + (void) strcat(msg, sys_errlist[errno]); + } else { + len = strlen(msg); + (void) sprintf(&msg[len], "errno %d", errno); + } +#endif /* #_LP64 */ +} diff --git a/lib/mksh/dosys.cc b/lib/mksh/dosys.cc new file mode 100644 index 0000000..5ff0ab7 --- /dev/null +++ b/lib/mksh/dosys.cc @@ -0,0 +1,577 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2005 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* + * Copyright 2015, Joyent, Inc. + */ + + +/* + * dosys.cc + * + * Execute one commandline + */ + +/* + * Included files + */ +#include /* WIFEXITED(status) */ +#include /* alloca() */ + +#include /* errno */ +#include /* errno */ +#include /* open() */ +#include +#include /* getvar() */ +#include /* getmem(), fatal_mksh(), errmsg() */ +#include /* SIG_DFL */ +#include /* open() */ +#include /* wait() */ +#include /* ulimit() */ +#include /* close(), dup2() */ +#include /* closefrom() */ +#include + +/* + * typedefs & structs + */ + +/* + * Static variables + */ + +/* + * File table of contents + */ +static Boolean exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path); + +/* + * Workaround for NFS bug. Sometimes, when running 'open' on a remote + * dmake server, it fails with "Stale NFS file handle" error. + * The second attempt seems to work. + */ +int +my_open(const char *path, int oflag, mode_t mode) { + int res = open(path, oflag, mode); + if (res < 0 && (errno == ESTALE || errno == EAGAIN)) { + /* Stale NFS file handle. Try again */ + res = open(path, oflag, mode); + } + return res; +} + +/* + * void + * redirect_io(char *stdout_file, char *stderr_file) + * + * Redirects stdout and stderr for a child mksh process. + */ +void +redirect_io(char *stdout_file, char *stderr_file) +{ + int i; + + (void) closefrom(3); + if ((i = my_open(stdout_file, + O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, + S_IREAD | S_IWRITE)) < 0) { + fatal_mksh(gettext("Couldn't open standard out temp file `%s': %s"), + stdout_file, + errmsg(errno)); + } else { + if (dup2(i, 1) == -1) { + fatal_mksh("*** Error: dup2(3, 1) failed: %s", + errmsg(errno)); + } + close(i); + } + if (stderr_file == NULL) { + if (dup2(1, 2) == -1) { + fatal_mksh("*** Error: dup2(1, 2) failed: %s", + errmsg(errno)); + } + } else if ((i = my_open(stderr_file, + O_WRONLY | O_CREAT | O_TRUNC | O_DSYNC, + S_IREAD | S_IWRITE)) < 0) { + fatal_mksh(gettext("Couldn't open standard error temp file `%s': %s"), + stderr_file, + errmsg(errno)); + } else { + if (dup2(i, 2) == -1) { + fatal_mksh("*** Error: dup2(3, 2) failed: %s", + errmsg(errno)); + } + close(i); + } +} + +/* + * doshell(command, ignore_error) + * + * Used to run command lines that include shell meta-characters. + * The make macro SHELL is supposed to contain a path to the shell. + * + * Return value: + * The pid of the process we started + * + * Parameters: + * command The command to run + * ignore_error Should we abort on error? + * + * Global variables used: + * filter_stderr If -X is on we redirect stderr + * shell_name The Name "SHELL", used to get the path to shell + */ +int +doshell(wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, int nice_prio) +{ + char *argv[6]; + int argv_index = 0; + int cmd_argv_index; + int length; + char nice_prio_buf[MAXPATHLEN]; + register Name shell = getvar(shell_name); + register char *shellname; + char *tmp_mbs_buffer; + + + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + if ((shellname = strrchr(shell->string_mb, (int) slash_char)) == NULL) { + shellname = shell->string_mb; + } else { + shellname++; + } + + /* + * Only prepend the /usr/bin/nice command to the original command + * if the nice priority, nice_prio, is NOT zero (0). + * Nice priorities can be a positive or a negative number. + */ + if (nice_prio != 0) { + argv[argv_index++] = (char *)"nice"; + (void) sprintf(nice_prio_buf, "-%d", nice_prio); + argv[argv_index++] = strdup(nice_prio_buf); + } + argv[argv_index++] = shellname; + argv[argv_index++] = (char*)(ignore_error ? "-c" : "-ce"); + if ((length = wcslen(command)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); + cmd_argv_index = argv_index; + argv[argv_index++] = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, command); + cmd_argv_index = argv_index; + argv[argv_index++] = strdup(mbs_buffer); + } + argv[argv_index] = NULL; + (void) fflush(stdout); + if ((childPid = fork()) == 0) { + enable_interrupt((void (*) (int)) SIG_DFL); +#if 0 + if (filter_stderr) { + redirect_stderr(); + } +#endif + if (nice_prio != 0) { + (void) execve("/usr/bin/nice", argv, environ); + fatal_mksh(gettext("Could not load `/usr/bin/nice': %s"), + errmsg(errno)); + } else { + (void) execve(shell->string_mb, argv, environ); + fatal_mksh(gettext("Could not load Shell from `%s': %s"), + shell->string_mb, + errmsg(errno)); + } + } + if (childPid == -1) { + fatal_mksh(gettext("fork failed: %s"), + errmsg(errno)); + } + retmem_mb(argv[cmd_argv_index]); + return childPid; +} + +/* + * exec_vp(name, argv, envp, ignore_error) + * + * Like execve, but does path search. + * This starts command when make invokes it directly (without a shell). + * + * Return value: + * Returns false if the exec failed + * + * Parameters: + * name The name of the command to run + * argv Arguments for the command + * envp The environment for it + * ignore_error Should we abort on error? + * + * Global variables used: + * shell_name The Name "SHELL", used to get the path to shell + * vroot_path The path used by the vroot package + */ +static Boolean +exec_vp(register char *name, register char **argv, char **envp, register Boolean ignore_error, pathpt vroot_path) +{ + register Name shell = getvar(shell_name); + register char *shellname; + char *shargv[4]; + Name tmp_shell; + + if (IS_EQUAL(shell->string_mb, "")) { + shell = shell_name; + } + + for (int i = 0; i < 5; i++) { + (void) execve_vroot(name, + argv + 1, + envp, + vroot_path, + VROOT_DEFAULT); + switch (errno) { + case ENOEXEC: + case ENOENT: + /* That failed. Let the shell handle it */ + shellname = strrchr(shell->string_mb, (int) slash_char); + if (shellname == NULL) { + shellname = shell->string_mb; + } else { + shellname++; + } + shargv[0] = shellname; + shargv[1] = (char*)(ignore_error ? "-c" : "-ce"); + shargv[2] = argv[0]; + shargv[3] = NULL; + tmp_shell = getvar(shell_name); + if (IS_EQUAL(tmp_shell->string_mb, "")) { + tmp_shell = shell_name; + } + (void) execve_vroot(tmp_shell->string_mb, + shargv, + envp, + vroot_path, + VROOT_DEFAULT); + return failed; + case ETXTBSY: + /* + * The program is busy (debugged?). + * Wait and then try again. + */ + (void) sleep((unsigned) i); + case EAGAIN: + break; + default: + return failed; + } + } + return failed; +} + +/* + * doexec(command, ignore_error) + * + * Will scan an argument string and split it into words + * thus building an argument list that can be passed to exec_ve() + * + * Return value: + * The pid of the process started here + * + * Parameters: + * command The command to run + * ignore_error Should we abort on error? + * + * Global variables used: + * filter_stderr If -X is on we redirect stderr + */ +int +doexec(register wchar_t *command, register Boolean ignore_error, char *stdout_file, char *stderr_file, pathpt vroot_path, int nice_prio) +{ + int arg_count = 5; + char **argv; + int length; + char nice_prio_buf[MAXPATHLEN]; + register char **p; + wchar_t *q; + register wchar_t *t; + char *tmp_mbs_buffer; + + /* + * Only prepend the /usr/bin/nice command to the original command + * if the nice priority, nice_prio, is NOT zero (0). + * Nice priorities can be a positive or a negative number. + */ + if (nice_prio != 0) { + arg_count += 2; + } + for (t = command; *t != (int) nul_char; t++) { + if (iswspace(*t)) { + arg_count++; + } + } + argv = (char **)alloca(arg_count * (sizeof(char *))); + /* + * Reserve argv[0] for sh in case of exec_vp failure. + * Don't worry about prepending /usr/bin/nice command to argv[0]. + * In fact, doing it may cause the sh command to fail! + */ + p = &argv[1]; + if ((length = wcslen(command)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, command, (length * MB_LEN_MAX) + 1); + argv[0] = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, command); + argv[0] = strdup(mbs_buffer); + } + + if (nice_prio != 0) { + *p++ = strdup("/usr/bin/nice"); + (void) sprintf(nice_prio_buf, "-%d", nice_prio); + *p++ = strdup(nice_prio_buf); + } + /* Build list of argument words. */ + for (t = command; *t;) { + if (p >= &argv[arg_count]) { + /* This should never happen, right? */ + WCSTOMBS(mbs_buffer, command); + fatal_mksh(gettext("Command `%s' has more than %d arguments"), + mbs_buffer, + arg_count); + } + q = t; + while (!iswspace(*t) && (*t != (int) nul_char)) { + t++; + } + if (*t) { + for (*t++ = (int) nul_char; iswspace(*t); t++); + } + if ((length = wcslen(q)) >= MAXPATHLEN) { + tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + (void) wcstombs(tmp_mbs_buffer, q, (length * MB_LEN_MAX) + 1); + *p++ = strdup(tmp_mbs_buffer); + retmem_mb(tmp_mbs_buffer); + } else { + WCSTOMBS(mbs_buffer, q); + *p++ = strdup(mbs_buffer); + } + } + *p = NULL; + + /* Then exec the command with that argument list. */ + (void) fflush(stdout); + if ((childPid = fork()) == 0) { + enable_interrupt((void (*) (int)) SIG_DFL); +#if 0 + if (filter_stderr) { + redirect_stderr(); + } +#endif + (void) exec_vp(argv[1], argv, environ, ignore_error, vroot_path); + fatal_mksh(gettext("Cannot load command `%s': %s"), argv[1], errmsg(errno)); + } + if (childPid == -1) { + fatal_mksh(gettext("fork failed: %s"), + errmsg(errno)); + } + for (int i = 0; argv[i] != NULL; i++) { + retmem_mb(argv[i]); + } + return childPid; +} + +/* + * await(ignore_error, silent_error, target, command, running_pid) + * + * Wait for one child process and analyzes + * the returned status when the child process terminates. + * + * Return value: + * Returns true if commands ran OK + * + * Parameters: + * ignore_error Should we abort on error? + * silent_error Should error messages be suppressed for dmake? + * target The target we are building, for error msgs + * command The command we ran, for error msgs + * running_pid The pid of the process we are waiting for + * + * Static variables used: + * filter_file The fd for the filter file + * filter_file_name The name of the filter file + * + * Global variables used: + * filter_stderr Set if -X is on + */ +Boolean +await(register Boolean ignore_error, register Boolean silent_error, Name target, wchar_t *command, pid_t running_pid, void *xdrs_p, int job_msg_id) +{ + int status; + char *buffer; + int core_dumped; + int exit_status; + FILE *outfp; + register pid_t pid; + struct stat stat_buff; + int termination_signal; + char tmp_buf[MAXPATHLEN]; + + while ((pid = wait(&status)) != running_pid) { + if (pid == -1) { + fatal_mksh(gettext("wait() failed: %s"), errmsg(errno)); + } + } + (void) fflush(stdout); + (void) fflush(stderr); + + if (status == 0) { + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and status is 0."); +#endif + + return succeeded; + } + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and status is *NOT* 0."); +#endif + + + exit_status = WEXITSTATUS(status); + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), and exit_status is %d.", exit_status); +#endif + + termination_signal = WTERMSIG(status); + core_dumped = WCOREDUMP(status); + + /* + * If the child returned an error, we now try to print a + * nice message about it. + */ + + tmp_buf[0] = (int) nul_char; + if (!silent_error) { + if (exit_status != 0) { + (void) fprintf(stdout, + gettext("*** Error code %d"), + exit_status); + } else { + (void) fprintf(stdout, + gettext("*** Signal %d"), + termination_signal); + if (core_dumped) { + (void) fprintf(stdout, + gettext(" - core dumped")); + } + } + if (ignore_error) { + (void) fprintf(stdout, + gettext(" (ignored)")); + } + (void) fprintf(stdout, "\n"); + (void) fflush(stdout); + } + +#ifdef PRINT_EXIT_STATUS + warning_mksh("I'm in await(), returning failed."); +#endif + + return failed; +} + +/* + * sh_command2string(command, destination) + * + * Run one sh command and capture the output from it. + * + * Return value: + * + * Parameters: + * command The command to run + * destination Where to deposit the output from the command + * + * Static variables used: + * + * Global variables used: + */ +void +sh_command2string(register String command, register String destination) +{ + register FILE *fd; + register int chr; + int status; + Boolean command_generated_output = false; + + command->text.p = (int) nul_char; + WCSTOMBS(mbs_buffer, command->buffer.start); + if ((fd = popen(mbs_buffer, "r")) == NULL) { + WCSTOMBS(mbs_buffer, command->buffer.start); + fatal_mksh(gettext("Could not run command `%s' for :sh transformation"), + mbs_buffer); + } + while ((chr = getc(fd)) != EOF) { + if (chr == (int) newline_char) { + chr = (int) space_char; + } + command_generated_output = true; + append_char(chr, destination); + } + + /* + * We don't want to keep the last LINE_FEED since usually + * the output of the 'sh:' command is used to evaluate + * some MACRO. ( /bin/sh and other shell add a line feed + * to the output so that the prompt appear in the right place. + * We don't need that + */ + if (command_generated_output){ + if ( *(destination->text.p-1) == (int) space_char) { + * (-- destination->text.p) = '\0'; + } + } else { + /* + * If the command didn't generate any output, + * set the buffer to a null string. + */ + *(destination->text.p) = '\0'; + } + + status = pclose(fd); + if (status != 0) { + WCSTOMBS(mbs_buffer, command->buffer.start); + fatal_mksh(gettext("The command `%s' returned status `%d'"), + mbs_buffer, + WEXITSTATUS(status)); + } +} + + diff --git a/lib/mksh/globals.cc b/lib/mksh/globals.cc new file mode 100644 index 0000000..255985e --- /dev/null +++ b/lib/mksh/globals.cc @@ -0,0 +1,128 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * globals.cc + * + * This declares all global variables + */ + +/* + * Included files + */ +#include + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Global variables + */ +char char_semantics[CHAR_SEMANTICS_ENTRIES]; +wchar_t char_semantics_char[] = { + ampersand_char, + asterisk_char, + at_char, + backquote_char, + backslash_char, + bar_char, + bracketleft_char, + bracketright_char, + colon_char, + dollar_char, + doublequote_char, + equal_char, + exclam_char, + greater_char, + hat_char, + hyphen_char, + less_char, + newline_char, + numbersign_char, + parenleft_char, + parenright_char, + percent_char, + plus_char, + question_char, + quote_char, + semicolon_char, + nul_char +}; +Macro_list cond_macro_list; +Boolean conditional_macro_used; +Boolean do_not_exec_rule; /* `-n' */ +Boolean dollarget_seen; +Boolean dollarless_flag; +Name dollarless_value; +Envvar envvar; +#ifdef lint +char **environ; +#endif +int exit_status; +wchar_t *file_being_read; +/* Variable gnu_style=true if env. var. SUN_MAKE_COMPAT_MODE=GNU (RFE 4866328) */ +Boolean gnu_style = false; +Name_set hashtab; +Name host_arch; +Name host_mach; +int line_number; +char *make_state_lockfile; +Boolean make_word_mentioned; +Makefile_type makefile_type = reading_nothing; +char mbs_buffer[(MAXPATHLEN * MB_LEN_MAX)]; +Name path_name; +Boolean posix = true; +Name hat; +Name query; +Boolean query_mentioned; +Boolean reading_environment; +Name shell_name; +Boolean svr4 = false; +Name target_arch; +Name target_mach; +Boolean tilde_rule; +Name virtual_root; +Boolean vpath_defined; +Name vpath_name; +wchar_t wcs_buffer[MAXPATHLEN]; +Boolean working_on_targets; +Boolean out_err_same; +pid_t childPid = -1; // This variable is used for killing child's process + // Such as qrsh, running command, etc. + +/* + * timestamps defined in defs.h + */ +const timestruc_t file_no_time = { -1, 0 }; +const timestruc_t file_doesnt_exist = { 0, 0 }; +const timestruc_t file_is_dir = { 1, 0 }; +const timestruc_t file_min_time = { 2, 0 }; +const timestruc_t file_max_time = { INT_MAX, 0 }; diff --git a/lib/mksh/i18n.cc b/lib/mksh/i18n.cc new file mode 100644 index 0000000..acc0e0c --- /dev/null +++ b/lib/mksh/i18n.cc @@ -0,0 +1,91 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2003 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * i18n.cc + * + * Deal with internationalization conversions + */ + +/* + * Included files + */ +#include +#include /* setup_char_semantics() */ + +/* + * get_char_semantics_value(ch) + * + * Return value: + * The character semantics of ch. + * + * Parameters: + * ch character we want semantics for. + * + */ +char +get_char_semantics_value(wchar_t ch) +{ + static Boolean char_semantics_setup; + + if (!char_semantics_setup) { + setup_char_semantics(); + char_semantics_setup = true; + } + return char_semantics[get_char_semantics_entry(ch)]; +} + +/* + * get_char_semantics_entry(ch) + * + * Return value: + * The slot number in the array for special make chars, + * else the slot number of the last array entry. + * + * Parameters: + * ch The wide character + * + * Global variables used: + * char_semantics_char[] array of special wchar_t chars + * "&*@`\\|[]:$=!>-\n#()%?;^<'\"" + */ +int +get_char_semantics_entry(wchar_t ch) +{ + wchar_t *char_sem_char; + + char_sem_char = (wchar_t *) wcschr(char_semantics_char, ch); + if (char_sem_char == NULL) { + /* + * Return the integer entry for the last slot, + * whose content is empty. + */ + return (CHAR_SEMANTICS_ENTRIES - 1); + } else { + return (char_sem_char - char_semantics_char); + } +} + diff --git a/lib/mksh/macro.cc b/lib/mksh/macro.cc new file mode 100644 index 0000000..b815105 --- /dev/null +++ b/lib/mksh/macro.cc @@ -0,0 +1,1335 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * macro.cc + * + * Handle expansion of make macros + */ + +/* + * Included files + */ +#include /* sh_command2string() */ +#include /* get_char_semantics_value() */ +#include +#include /* retmem() */ +#include /* get_next_block_fn() */ + +#include + +/* + * File table of contents + */ +static void add_macro_to_global_list(Name macro_to_add); +static void expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd); + +static void init_arch_macros(void); +static void init_mach_macros(void); +static Boolean init_arch_done = false; +static Boolean init_mach_done = false; + + +long env_alloc_num = 0; +long env_alloc_bytes = 0; + +/* + * getvar(name) + * + * Return expanded value of macro. + * + * Return value: + * The expanded value of the macro + * + * Parameters: + * name The name of the macro we want the value for + * + * Global variables used: + */ +Name +getvar(register Name name) +{ + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Name result; + + if ((name == host_arch) || (name == target_arch)) { + if (!init_arch_done) { + init_arch_done = true; + init_arch_macros(); + } + } + if ((name == host_mach) || (name == target_mach)) { + if (!init_mach_done) { + init_mach_done = true; + init_mach_macros(); + } + } + + INIT_STRING_FROM_STACK(destination, buffer); + expand_value(maybe_append_prop(name, macro_prop)->body.macro.value, + &destination, + false); + result = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + return result; +} + +/* + * expand_value(value, destination, cmd) + * + * Recursively expands all macros in the string value. + * destination is where the expanded value should be appended. + * + * Parameters: + * value The value we are expanding + * destination Where to deposit the expansion + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + */ +void +expand_value(Name value, register String destination, Boolean cmd) +{ + Source_rec sourceb; + register Source source = &sourceb; + register wchar_t *source_p = NULL; + register wchar_t *source_end = NULL; + wchar_t *block_start = NULL; + int quote_seen = 0; + + if (value == NULL) { + /* + * Make sure to get a string allocated even if it + * will be empty. + */ + MBSTOWCS(wcs_buffer, ""); + append_string(wcs_buffer, destination, FIND_LENGTH); + destination->text.end = destination->text.p; + return; + } + if (!value->dollar) { + /* + * If the value we are expanding does not contain + * any $, we don't have to parse it. + */ + APPEND_NAME(value, + destination, + (int) value->hash.length + ); + destination->text.end = destination->text.p; + return; + } + + if (value->being_expanded) { + fatal_reader_mksh(gettext("Loop detected when expanding macro value `%s'"), + value->string_mb); + } + value->being_expanded = true; + /* Setup the structure we read from */ + Wstring vals(value); + sourceb.string.text.p = sourceb.string.buffer.start = wcsdup(vals.get_string()); + sourceb.string.free_after_use = true; + sourceb.string.text.end = + sourceb.string.buffer.end = + sourceb.string.text.p + value->hash.length; + sourceb.previous = NULL; + sourceb.fd = -1; + sourceb.inp_buf = + sourceb.inp_buf_ptr = + sourceb.inp_buf_end = NULL; + sourceb.error_converting = false; + /* Lift some pointers from the struct to local register variables */ + CACHE_SOURCE(0); +/* We parse the string in segments */ +/* We read chars until we find a $, then we append what we have read so far */ +/* (since last $ processing) to the destination. When we find a $ we call */ +/* expand_macro() and let it expand that particular $ reference into dest */ + block_start = source_p; + quote_seen = 0; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case backslash_char: + /* Quote $ in macro value */ + if (!cmd) { + quote_seen = ~quote_seen; + } + continue; + case dollar_char: + /* Save the plain string we found since */ + /* start of string or previous $ */ + if (quote_seen) { + append_string(block_start, + destination, + source_p - block_start - 1); + block_start = source_p; + break; + } + append_string(block_start, + destination, + source_p - block_start); + source->string.text.p = ++source_p; + UNCACHE_SOURCE(); + /* Go expand the macro reference */ + expand_macro(source, destination, sourceb.string.buffer.start, cmd); + CACHE_SOURCE(1); + block_start = source_p + 1; + break; + case nul_char: + /* The string ran out. Get some more */ + append_string(block_start, + destination, + source_p - block_start); + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + destination->text.end = destination->text.p; + value->being_expanded = false; + return; + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_value()"); + } + block_start = source_p; + source_p--; + continue; + } + quote_seen = 0; + } + retmem(sourceb.string.buffer.start); +} + +/* + * expand_macro(source, destination, current_string, cmd) + * + * Should be called with source->string.text.p pointing to + * the first char after the $ that starts a macro reference. + * source->string.text.p is returned pointing to the first char after + * the macro name. + * It will read the macro name, expanding any macros in it, + * and get the value. The value is then expanded. + * destination is a String that is filled in with the expanded macro. + * It may be passed in referencing a buffer to expand the macro into. + * Note that most expansions are done on demand, e.g. right + * before the command is executed and not while the file is + * being parsed. + * + * Parameters: + * source The source block that references the string + * to expand + * destination Where to put the result + * current_string The string we are expanding, for error msg + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + * funny Vector of semantic tags for characters + * is_conditional Set if a conditional macro is refd + * make_word_mentioned Set if the word "MAKE" is mentioned + * makefile_type We deliver extra msg when reading makefiles + * query The Name "?", compared against + * query_mentioned Set if the word "?" is mentioned + */ +void +expand_macro(register Source source, register String destination, wchar_t *current_string, Boolean cmd) +{ + static Name make = (Name)NULL; + static wchar_t colon_sh[4]; + static wchar_t colon_shell[7]; + String_rec string; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register wchar_t *source_p = source->string.text.p; + register wchar_t *source_end = source->string.text.end; + register int closer = 0; + wchar_t *block_start = (wchar_t *)NULL; + int quote_seen = 0; + register int closer_level = 1; + Name name = (Name)NULL; + wchar_t *colon = (wchar_t *)NULL; + wchar_t *percent = (wchar_t *)NULL; + wchar_t *eq = (wchar_t *) NULL; + Property macro = NULL; + wchar_t *p = (wchar_t*)NULL; + String_rec extracted; + wchar_t extracted_string[MAXPATHLEN]; + wchar_t *left_head = NULL; + wchar_t *left_tail = NULL; + wchar_t *right_tail = NULL; + int left_head_len = 0; + int left_tail_len = 0; + int tmp_len = 0; + wchar_t *right_hand[128]; + int i = 0; + enum { + no_extract, + dir_extract, + file_extract + } extraction = no_extract; + enum { + no_replace, + suffix_replace, + pattern_replace, + sh_replace + } replacement = no_replace; + + if (make == NULL) { + MBSTOWCS(wcs_buffer, "MAKE"); + make = GETNAME(wcs_buffer, FIND_LENGTH); + + MBSTOWCS(colon_sh, ":sh"); + MBSTOWCS(colon_shell, ":shell"); + } + + right_hand[0] = NULL; + + /* First copy the (macro-expanded) macro name into string. */ + INIT_STRING_FROM_STACK(string, buffer); +recheck_first_char: + /* Check the first char of the macro name to figure out what to do. */ + switch (GET_CHAR()) { + case nul_char: + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + WCSTOMBS(mbs_buffer, current_string); + fatal_reader_mksh(gettext("'$' at end of string `%s'"), + mbs_buffer); + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); + } + goto recheck_first_char; + case parenleft_char: + /* Multi char name. */ + closer = (int) parenright_char; + break; + case braceleft_char: + /* Multi char name. */ + closer = (int) braceright_char; + break; + case newline_char: + fatal_reader_mksh(gettext("'$' at end of line")); + default: + /* Single char macro name. Just suck it up */ + append_char(*source_p, &string); + source->string.text.p = source_p + 1; + goto get_macro_value; + } + + /* Handle multi-char macro names */ + block_start = ++source_p; + quote_seen = 0; + for (; 1; source_p++) { + switch (GET_CHAR()) { + case nul_char: + append_string(block_start, + &string, + source_p - block_start); + GET_NEXT_BLOCK_NOCHK(source); + if (source == NULL) { + if (current_string != NULL) { + WCSTOMBS(mbs_buffer, current_string); + fatal_reader_mksh(gettext("Unmatched `%c' in string `%s'"), + closer == + (int) braceright_char ? + (int) braceleft_char : + (int) parenleft_char, + mbs_buffer); + } else { + fatal_reader_mksh(gettext("Premature EOF")); + } + } + if (source->error_converting) { + fatal_reader_mksh("Internal error: Invalid byte sequence in expand_macro()"); + } + block_start = source_p; + source_p--; + continue; + case newline_char: + fatal_reader_mksh(gettext("Unmatched `%c' on line"), + closer == (int) braceright_char ? + (int) braceleft_char : + (int) parenleft_char); + case backslash_char: + /* Quote dollar in macro value. */ + if (!cmd) { + quote_seen = ~quote_seen; + } + continue; + case dollar_char: + /* + * Macro names may reference macros. + * This expands the value of such macros into the + * macro name string. + */ + if (quote_seen) { + append_string(block_start, + &string, + source_p - block_start - 1); + block_start = source_p; + break; + } + append_string(block_start, + &string, + source_p - block_start); + source->string.text.p = ++source_p; + UNCACHE_SOURCE(); + expand_macro(source, &string, current_string, cmd); + CACHE_SOURCE(0); + block_start = source_p; + source_p--; + break; + case parenleft_char: + /* Allow nested pairs of () in the macro name. */ + if (closer == (int) parenright_char) { + closer_level++; + } + break; + case braceleft_char: + /* Allow nested pairs of {} in the macro name. */ + if (closer == (int) braceright_char) { + closer_level++; + } + break; + case parenright_char: + case braceright_char: + /* + * End of the name. Save the string in the macro + * name string. + */ + if ((*source_p == closer) && (--closer_level <= 0)) { + source->string.text.p = source_p + 1; + append_string(block_start, + &string, + source_p - block_start); + goto get_macro_value; + } + break; + } + quote_seen = 0; + } + /* + * We got the macro name. We now inspect it to see if it + * specifies any translations of the value. + */ +get_macro_value: + name = NULL; + /* First check if we have a $(@D) type translation. */ + if ((get_char_semantics_value(string.buffer.start[0]) & + (int) special_macro_sem) && + (string.text.p - string.buffer.start >= 2) && + ((string.buffer.start[1] == 'D') || + (string.buffer.start[1] == 'F'))) { + switch (string.buffer.start[1]) { + case 'D': + extraction = dir_extract; + break; + case 'F': + extraction = file_extract; + break; + default: + WCSTOMBS(mbs_buffer, string.buffer.start); + fatal_reader_mksh(gettext("Illegal macro reference `%s'"), + mbs_buffer); + } + /* Internalize the macro name using the first char only. */ + name = GETNAME(string.buffer.start, 1); + (void) wcscpy(string.buffer.start, string.buffer.start + 2); + } + /* Check for other kinds of translations. */ + if ((colon = (wchar_t *) wcschr(string.buffer.start, + (int) colon_char)) != NULL) { + /* + * We have a $(FOO:.c=.o) type translation. + * Get the name of the macro proper. + */ + if (name == NULL) { + name = GETNAME(string.buffer.start, + colon - string.buffer.start); + } + /* Pickup all the translations. */ + if (IS_WEQUAL(colon, colon_sh) || IS_WEQUAL(colon, colon_shell)) { + replacement = sh_replace; + } else if ((svr4) || + ((percent = (wchar_t *) wcschr(colon + 1, + (int) percent_char)) == NULL)) { + while (colon != NULL) { + if ((eq = (wchar_t *) wcschr(colon + 1, + (int) equal_char)) == NULL) { + fatal_reader_mksh(gettext("= missing from replacement macro reference")); + } + left_tail_len = eq - colon - 1; + if(left_tail) { + retmem(left_tail); + } + left_tail = ALLOC_WC(left_tail_len + 1); + (void) wcsncpy(left_tail, + colon + 1, + eq - colon - 1); + left_tail[eq - colon - 1] = (int) nul_char; + replacement = suffix_replace; + if ((colon = (wchar_t *) wcschr(eq + 1, + (int) colon_char)) != NULL) { + tmp_len = colon - eq; + if(right_tail) { + retmem(right_tail); + } + right_tail = ALLOC_WC(tmp_len); + (void) wcsncpy(right_tail, + eq + 1, + colon - eq - 1); + right_tail[colon - eq - 1] = + (int) nul_char; + } else { + if(right_tail) { + retmem(right_tail); + } + right_tail = ALLOC_WC(wcslen(eq) + 1); + (void) wcscpy(right_tail, eq + 1); + } + } + } else { + if ((eq = (wchar_t *) wcschr(colon + 1, + (int) equal_char)) == NULL) { + fatal_reader_mksh(gettext("= missing from replacement macro reference")); + } + if ((percent = (wchar_t *) wcschr(colon + 1, + (int) percent_char)) == NULL) { + fatal_reader_mksh(gettext("%% missing from replacement macro reference")); + } + if (eq < percent) { + fatal_reader_mksh(gettext("%% missing from replacement macro reference")); + } + + if (percent > (colon + 1)) { + tmp_len = percent - colon; + if(left_head) { + retmem(left_head); + } + left_head = ALLOC_WC(tmp_len); + (void) wcsncpy(left_head, + colon + 1, + percent - colon - 1); + left_head[percent-colon-1] = (int) nul_char; + left_head_len = percent-colon-1; + } else { + left_head = NULL; + left_head_len = 0; + } + + if (eq > percent+1) { + tmp_len = eq - percent; + if(left_tail) { + retmem(left_tail); + } + left_tail = ALLOC_WC(tmp_len); + (void) wcsncpy(left_tail, + percent + 1, + eq - percent - 1); + left_tail[eq-percent-1] = (int) nul_char; + left_tail_len = eq-percent-1; + } else { + left_tail = NULL; + left_tail_len = 0; + } + + if ((percent = (wchar_t *) wcschr(++eq, + (int) percent_char)) == NULL) { + + right_hand[0] = ALLOC_WC(wcslen(eq) + 1); + right_hand[1] = NULL; + (void) wcscpy(right_hand[0], eq); + } else { + i = 0; + do { + right_hand[i] = ALLOC_WC(percent-eq+1); + (void) wcsncpy(right_hand[i], + eq, + percent - eq); + right_hand[i][percent-eq] = + (int) nul_char; + if (i++ >= VSIZEOF(right_hand)) { + fatal_mksh(gettext("Too many %% in pattern")); + } + eq = percent + 1; + if (eq[0] == (int) nul_char) { + MBSTOWCS(wcs_buffer, ""); + right_hand[i] = (wchar_t *) wcsdup(wcs_buffer); + i++; + break; + } + } while ((percent = (wchar_t *) wcschr(eq, (int) percent_char)) != NULL); + if (eq[0] != (int) nul_char) { + right_hand[i] = ALLOC_WC(wcslen(eq) + 1); + (void) wcscpy(right_hand[i], eq); + i++; + } + right_hand[i] = NULL; + } + replacement = pattern_replace; + } + } + if (name == NULL) { + /* + * No translations found. + * Use the whole string as the macro name. + */ + name = GETNAME(string.buffer.start, + string.text.p - string.buffer.start); + } + if (string.free_after_use) { + retmem(string.buffer.start); + } + if (name == make) { + make_word_mentioned = true; + } + if (name == query) { + query_mentioned = true; + } + if ((name == host_arch) || (name == target_arch)) { + if (!init_arch_done) { + init_arch_done = true; + init_arch_macros(); + } + } + if ((name == host_mach) || (name == target_mach)) { + if (!init_mach_done) { + init_mach_done = true; + init_mach_macros(); + } + } + /* Get the macro value. */ + macro = get_prop(name->prop, macro_prop); + if ((macro != NULL) && macro->body.macro.is_conditional) { + conditional_macro_used = true; + /* + * Add this conditional macro to the beginning of the + * global list. + */ + add_macro_to_global_list(name); + if (makefile_type == reading_makefile) { + warning_mksh(gettext("Conditional macro `%s' referenced in file `%ws', line %d"), + name->string_mb, file_being_read, line_number); + } + } + /* Macro name read and parsed. Expand the value. */ + if ((macro == NULL) || (macro->body.macro.value == NULL)) { + /* If the value is empty, we just get out of here. */ + goto exit; + } + if (replacement == sh_replace) { + /* If we should do a :sh transform, we expand the command + * and process it. + */ + INIT_STRING_FROM_STACK(string, buffer); + /* Expand the value into a local string buffer and run cmd. */ + expand_value_with_daemon(name, macro, &string, cmd); + sh_command2string(&string, destination); + } else if ((replacement != no_replace) || (extraction != no_extract)) { + /* + * If there were any transforms specified in the macro + * name, we deal with them here. + */ + INIT_STRING_FROM_STACK(string, buffer); + /* Expand the value into a local string buffer. */ + expand_value_with_daemon(name, macro, &string, cmd); + /* Scan the expanded string. */ + p = string.buffer.start; + while (*p != (int) nul_char) { + wchar_t chr; + + /* + * First skip over any white space and append + * that to the destination string. + */ + block_start = p; + while ((*p != (int) nul_char) && iswspace(*p)) { + p++; + } + append_string(block_start, + destination, + p - block_start); + /* Then find the end of the next word. */ + block_start = p; + while ((*p != (int) nul_char) && !iswspace(*p)) { + p++; + } + /* If we cant find another word we are done */ + if (block_start == p) { + break; + } + /* Then apply the transforms to the word */ + INIT_STRING_FROM_STACK(extracted, extracted_string); + switch (extraction) { + case dir_extract: + /* + * $(@D) type transform. Extract the + * path from the word. Deliver "." if + * none is found. + */ + if (p != NULL) { + chr = *p; + *p = (int) nul_char; + } + eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); + if (p != NULL) { + *p = chr; + } + if ((eq == NULL) || (eq > p)) { + MBSTOWCS(wcs_buffer, "."); + append_string(wcs_buffer, &extracted, 1); + } else { + append_string(block_start, + &extracted, + eq - block_start); + } + break; + case file_extract: + /* + * $(@F) type transform. Remove the path + * from the word if any. + */ + if (p != NULL) { + chr = *p; + *p = (int) nul_char; + } + eq = (wchar_t *) wcsrchr(block_start, (int) slash_char); + if (p != NULL) { + *p = chr; + } + if ((eq == NULL) || (eq > p)) { + append_string(block_start, + &extracted, + p - block_start); + } else { + append_string(eq + 1, + &extracted, + p - eq - 1); + } + break; + case no_extract: + append_string(block_start, + &extracted, + p - block_start); + break; + } + switch (replacement) { + case suffix_replace: + /* + * $(FOO:.o=.c) type transform. + * Maybe replace the tail of the word. + */ + if (((extracted.text.p - + extracted.buffer.start) >= + left_tail_len) && + IS_WEQUALN(extracted.text.p - left_tail_len, + left_tail, + left_tail_len)) { + append_string(extracted.buffer.start, + destination, + (extracted.text.p - + extracted.buffer.start) + - left_tail_len); + append_string(right_tail, + destination, + FIND_LENGTH); + } else { + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + } + break; + case pattern_replace: + /* $(X:a%b=c%d) type transform. */ + if (((extracted.text.p - + extracted.buffer.start) >= + left_head_len+left_tail_len) && + IS_WEQUALN(left_head, + extracted.buffer.start, + left_head_len) && + IS_WEQUALN(left_tail, + extracted.text.p - left_tail_len, + left_tail_len)) { + i = 0; + while (right_hand[i] != NULL) { + append_string(right_hand[i], + destination, + FIND_LENGTH); + i++; + if (right_hand[i] != NULL) { + append_string(extracted.buffer. + start + + left_head_len, + destination, + (extracted.text.p - extracted.buffer.start)-left_head_len-left_tail_len); + } + } + } else { + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + } + break; + case no_replace: + append_string(extracted.buffer.start, + destination, + FIND_LENGTH); + break; + case sh_replace: + break; + } + } + if (string.free_after_use) { + retmem(string.buffer.start); + } + } else { + /* + * This is for the case when the macro name did not + * specify transforms. + */ + if (!strncmp(name->string_mb, "GET", 3)) { + dollarget_seen = true; + } + dollarless_flag = false; + if (!strncmp(name->string_mb, "<", 1) && + dollarget_seen) { + dollarless_flag = true; + dollarget_seen = false; + } + expand_value_with_daemon(name, macro, destination, cmd); + } +exit: + if(left_tail) { + retmem(left_tail); + } + if(right_tail) { + retmem(right_tail); + } + if(left_head) { + retmem(left_head); + } + i = 0; + while (right_hand[i] != NULL) { + retmem(right_hand[i]); + i++; + } + *destination->text.p = (int) nul_char; + destination->text.end = destination->text.p; +} + +static void +add_macro_to_global_list(Name macro_to_add) +{ + Macro_list new_macro; + Macro_list macro_on_list; + char *name_on_list = (char*)NULL; + char *name_to_add = macro_to_add->string_mb; + char *value_on_list = (char*)NULL; + const char *value_to_add = (char*)NULL; + + if (macro_to_add->prop->body.macro.value != NULL) { + value_to_add = macro_to_add->prop->body.macro.value->string_mb; + } else { + value_to_add = ""; + } + + /* + * Check if this macro is already on list, if so, do nothing + */ + for (macro_on_list = cond_macro_list; + macro_on_list != NULL; + macro_on_list = macro_on_list->next) { + + name_on_list = macro_on_list->macro_name; + value_on_list = macro_on_list->value; + + if (IS_EQUAL(name_on_list, name_to_add)) { + if (IS_EQUAL(value_on_list, value_to_add)) { + return; + } + } + } + new_macro = (Macro_list) malloc(sizeof(Macro_list_rec)); + new_macro->macro_name = strdup(name_to_add); + new_macro->value = strdup(value_to_add); + new_macro->next = cond_macro_list; + cond_macro_list = new_macro; +} + +/* + * init_arch_macros(void) + * + * Set the magic macros TARGET_ARCH, HOST_ARCH, + * + * Parameters: + * + * Global variables used: + * host_arch Property for magic macro HOST_ARCH + * target_arch Property for magic macro TARGET_ARCH + * + * Return value: + * The function does not return a value, but can + * call fatal() in case of error. + */ +static void +init_arch_macros(void) +{ + String_rec result_string; + wchar_t wc_buf[STRING_BUFFER_LENGTH]; + char mb_buf[STRING_BUFFER_LENGTH]; + FILE *pipe; + Name value; + int set_host, set_target; + const char *mach_command = "/bin/mach"; + + set_host = (get_prop(host_arch->prop, macro_prop) == NULL); + set_target = (get_prop(target_arch->prop, macro_prop) == NULL); + + if (set_host || set_target) { + INIT_STRING_FROM_STACK(result_string, wc_buf); + append_char((int) hyphen_char, &result_string); + + if ((pipe = popen(mach_command, "r")) == NULL) { + fatal_mksh(gettext("Execute of %s failed"), mach_command); + } + while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { + MBSTOWCS(wcs_buffer, mb_buf); + append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); + } + if (pclose(pipe) != 0) { + fatal_mksh(gettext("Execute of %s failed"), mach_command); + } + + value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); + + if (set_host) { + (void) setvar_daemon(host_arch, value, false, no_daemon, true, 0); + } + if (set_target) { + (void) setvar_daemon(target_arch, value, false, no_daemon, true, 0); + } + } +} + +/* + * init_mach_macros(void) + * + * Set the magic macros TARGET_MACH, HOST_MACH, + * + * Parameters: + * + * Global variables used: + * host_mach Property for magic macro HOST_MACH + * target_mach Property for magic macro TARGET_MACH + * + * Return value: + * The function does not return a value, but can + * call fatal() in case of error. + */ +static void +init_mach_macros(void) +{ + String_rec result_string; + wchar_t wc_buf[STRING_BUFFER_LENGTH]; + char mb_buf[STRING_BUFFER_LENGTH]; + FILE *pipe; + Name value; + int set_host, set_target; + const char *arch_command = "/bin/arch"; + + set_host = (get_prop(host_mach->prop, macro_prop) == NULL); + set_target = (get_prop(target_mach->prop, macro_prop) == NULL); + + if (set_host || set_target) { + INIT_STRING_FROM_STACK(result_string, wc_buf); + append_char((int) hyphen_char, &result_string); + + if ((pipe = popen(arch_command, "r")) == NULL) { + fatal_mksh(gettext("Execute of %s failed"), arch_command); + } + while (fgets(mb_buf, sizeof(mb_buf), pipe) != NULL) { + MBSTOWCS(wcs_buffer, mb_buf); + append_string(wcs_buffer, &result_string, wcslen(wcs_buffer)); + } + if (pclose(pipe) != 0) { + fatal_mksh(gettext("Execute of %s failed"), arch_command); + } + + value = GETNAME(result_string.buffer.start, wcslen(result_string.buffer.start)); + + if (set_host) { + (void) setvar_daemon(host_mach, value, false, no_daemon, true, 0); + } + if (set_target) { + (void) setvar_daemon(target_mach, value, false, no_daemon, true, 0); + } + } +} + +/* + * expand_value_with_daemon(name, macro, destination, cmd) + * + * Checks for daemons and then maybe calls expand_value(). + * + * Parameters: + * name Name of the macro (Added by the NSE) + * macro The property block with the value to expand + * destination Where the result should be deposited + * cmd If we are evaluating a command line we + * turn \ quoting off + * + * Global variables used: + */ +static void +expand_value_with_daemon(Name, register Property macro, register String destination, Boolean cmd) +{ + register Chain chain; + + + switch (macro->body.macro.daemon) { + case no_daemon: + if (!svr4 && !posix) { + expand_value(macro->body.macro.value, destination, cmd); + } else { + if (dollarless_flag && tilde_rule) { + expand_value(dollarless_value, destination, cmd); + dollarless_flag = false; + tilde_rule = false; + } else { + expand_value(macro->body.macro.value, destination, cmd); + } + } + return; + case chain_daemon: + /* If this is a $? value we call the daemon to translate the */ + /* list of names to a string */ + for (chain = (Chain) macro->body.macro.value; + chain != NULL; + chain = chain->next) { + APPEND_NAME(chain->name, + destination, + (int) chain->name->hash.length); + if (chain->next != NULL) { + append_char((int) space_char, destination); + } + } + return; + } +} + +/* + * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. + */ +char *sunpro_dependencies_buf = NULL; +char *sunpro_dependencies_oldbuf = NULL; +int sunpro_dependencies_buf_size = 0; + +/* + * setvar_daemon(name, value, append, daemon, strip_trailing_spaces) + * + * Set a macro value, possibly supplying a daemon to be used + * when referencing the value. + * + * Return value: + * The property block with the new value + * + * Parameters: + * name Name of the macro to set + * value The value to set + * append Should we reset or append to the current value? + * daemon Special treatment when reading the value + * strip_trailing_spaces from the end of value->string + * debug_level Indicates how much tracing we should do + * + * Global variables used: + * makefile_type Used to check if we should enforce read only + * path_name The Name "PATH", compared against + * virtual_root The Name "VIRTUAL_ROOT", compared against + * vpath_defined Set if the macro VPATH is set + * vpath_name The Name "VPATH", compared against + * envvar A list of environment vars with $ in value + */ +Property +setvar_daemon(register Name name, register Name value, Boolean append, Daemon daemon, Boolean strip_trailing_spaces, short debug_level) +{ + register Property macro = maybe_append_prop(name, macro_prop); + register Property macro_apx = get_prop(name->prop, macro_append_prop); + int length = 0; + String_rec destination; + wchar_t buffer[STRING_BUFFER_LENGTH]; + register Chain chain; + Name val; + wchar_t *val_string = (wchar_t*)NULL; + Wstring wcb; + + + if ((makefile_type != reading_nothing) && + macro->body.macro.read_only) { + return macro; + } + /* Strip spaces from the end of the value */ + if (daemon == no_daemon) { + if(value != NULL) { + wcb.init(value); + length = wcb.length(); + val_string = wcb.get_string(); + } + if ((length > 0) && iswspace(val_string[length-1])) { + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + append_string(val_string, &destination, length); + if (strip_trailing_spaces) { + while ((length > 0) && + iswspace(destination.buffer.start[length-1])) { + destination.buffer.start[--length] = 0; + } + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + } + } + + if(macro_apx != NULL) { + val = macro_apx->body.macro_appendix.value; + } else { + val = macro->body.macro.value; + } + + if (append) { + /* + * If we are appending, we just tack the new value after + * the old one with a space in between. + */ + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if ((macro != NULL) && (val != NULL)) { + APPEND_NAME(val, + &destination, + (int) val->hash.length); + if (value != NULL) { + wcb.init(value); + if(wcb.length() > 0) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + } + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + wcb.init(value); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } + + /* Debugging trace */ + if (debug_level > 1) { + if (value != NULL) { + switch (daemon) { + case chain_daemon: + (void) printf("%s =", name->string_mb); + for (chain = (Chain) value; + chain != NULL; + chain = chain->next) { + (void) printf(" %s", chain->name->string_mb); + } + (void) printf("\n"); + break; + case no_daemon: + (void) printf("%s= %s\n", + name->string_mb, + value->string_mb); + break; + } + } else { + (void) printf("%s =\n", name->string_mb); + } + } + /* Set the new values in the macro property block */ +/**/ + if(macro_apx != NULL) { + macro_apx->body.macro_appendix.value = value; + INIT_STRING_FROM_STACK(destination, buffer); + buffer[0] = 0; + if (value != NULL) { + APPEND_NAME(value, + &destination, + (int) value->hash.length); + if (macro_apx->body.macro_appendix.value_to_append != NULL) { + MBTOWC(wcs_buffer, " "); + append_char(wcs_buffer[0], &destination); + } + } + if (macro_apx->body.macro_appendix.value_to_append != NULL) { + APPEND_NAME(macro_apx->body.macro_appendix.value_to_append, + &destination, + (int) macro_apx->body.macro_appendix.value_to_append->hash.length); + } + value = GETNAME(destination.buffer.start, FIND_LENGTH); + if (destination.free_after_use) { + retmem(destination.buffer.start); + } + } +/**/ + macro->body.macro.value = value; + macro->body.macro.daemon = daemon; + /* + * If the user changes the VIRTUAL_ROOT, we need to flush + * the vroot package cache. + */ + if (name == path_name) { + flush_path_cache(); + } + if (name == virtual_root) { + flush_vroot_cache(); + } + /* If this sets the VPATH we remember that */ + if ((name == vpath_name) && + (value != NULL) && + (value->hash.length > 0)) { + vpath_defined = true; + } + /* + * For environment variables we also set the + * environment value each time. + */ + if (macro->body.macro.exported) { + static char *env; + + if (!reading_environment && (value != NULL)) { + Envvar p; + + for (p = envvar; p != NULL; p = p->next) { + if (p->name == name) { + p->value = value; + p->already_put = false; + goto found_it; + } + } + p = ALLOC(Envvar); + p->name = name; + p->value = value; + p->next = envvar; + p->env_string = NULL; + p->already_put = false; + envvar = p; +found_it:; + } if (reading_environment || (value == NULL) || !value->dollar) { + length = 2 + strlen(name->string_mb); + if (value != NULL) { + length += strlen(value->string_mb); + } + Property env_prop = maybe_append_prop(name, env_mem_prop); + /* + * We use a permanent buffer to reset SUNPRO_DEPENDENCIES value. + */ + if (!strncmp(name->string_mb, "SUNPRO_DEPENDENCIES", 19)) { + if (length >= sunpro_dependencies_buf_size) { + sunpro_dependencies_buf_size=length*2; + if (sunpro_dependencies_buf_size < 4096) + sunpro_dependencies_buf_size = 4096; // Default minimum size + if (sunpro_dependencies_buf) + sunpro_dependencies_oldbuf = sunpro_dependencies_buf; + sunpro_dependencies_buf=getmem(sunpro_dependencies_buf_size); + } + env = sunpro_dependencies_buf; + } else { + env = getmem(length); + } + env_alloc_num++; + env_alloc_bytes += length; + (void) sprintf(env, + "%s=%s", + name->string_mb, + value == NULL ? + "" : value->string_mb); + (void) putenv(env); + env_prop->body.env_mem.value = env; + if (sunpro_dependencies_oldbuf) { + /* Return old buffer */ + retmem_mb(sunpro_dependencies_oldbuf); + sunpro_dependencies_oldbuf = NULL; + } + } + } + if (name == target_arch) { + Name ha = getvar(host_arch); + Name ta = getvar(target_arch); + Name vr = getvar(virtual_root); + int length; + wchar_t *new_value; + wchar_t *old_vr; + Boolean new_value_allocated = false; + + Wstring ha_str(ha); + Wstring ta_str(ta); + Wstring vr_str(vr); + + wchar_t * wcb_ha = ha_str.get_string(); + wchar_t * wcb_ta = ta_str.get_string(); + wchar_t * wcb_vr = vr_str.get_string(); + + length = 32 + + wcslen(wcb_ha) + + wcslen(wcb_ta) + + wcslen(wcb_vr); + old_vr = wcb_vr; + MBSTOWCS(wcs_buffer, "/usr/arch/"); + if (IS_WEQUALN(old_vr, + wcs_buffer, + wcslen(wcs_buffer))) { + old_vr = (wchar_t *) wcschr(old_vr, (int) colon_char) + 1; + } + if ( (ha == ta) || (wcslen(wcb_ta) == 0) ) { + new_value = old_vr; + } else { + new_value = ALLOC_WC(length); + new_value_allocated = true; + WCSTOMBS(mbs_buffer, old_vr); + (void) swprintf(new_value, length * SIZEOFWCHAR_T, + L"/usr/arch/%s/%s:%s", + ha->string_mb + 1, + ta->string_mb + 1, + mbs_buffer); + } + if (new_value[0] != 0) { + (void) setvar_daemon(virtual_root, + GETNAME(new_value, FIND_LENGTH), + false, + no_daemon, + true, + debug_level); + } + if (new_value_allocated) { + retmem(new_value); + } + } + return macro; +} diff --git a/lib/mksh/misc.cc b/lib/mksh/misc.cc new file mode 100644 index 0000000..4765102 --- /dev/null +++ b/lib/mksh/misc.cc @@ -0,0 +1,1113 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * misc.cc + * + * This file contains various unclassified routines. Some main groups: + * getname + * Memory allocation + * String handling + * Property handling + * Error message handling + * Make internal state dumping + * main routine support + */ + +/* + * Included files + */ +#include /* bsd_signal() */ +#include /* get_char_semantics_value() */ +#include +#include /* va_list, va_start(), va_end() */ +#include /* mbstowcs() */ +#include /* SIG_DFL */ +#include /* wait() */ + +#include /* strerror() */ +#include + + +/* + * Defined macros + */ + +/* + * typedefs & structs + */ + +/* + * Static variables + */ +extern "C" { + void (*sigivalue)(int) = SIG_DFL; + void (*sigqvalue)(int) = SIG_DFL; + void (*sigtvalue)(int) = SIG_DFL; + void (*sighvalue)(int) = SIG_DFL; +} + +long getname_bytes_count = 0; +long getname_names_count = 0; +long getname_struct_count = 0; + +long freename_bytes_count = 0; +long freename_names_count = 0; +long freename_struct_count = 0; + +long expandstring_count = 0; +long getwstring_count = 0; + +/* + * File table of contents + */ +static void expand_string(register String string, register int length); + +#define FATAL_ERROR_MSG_SIZE 200 + +/* + * getmem(size) + * + * malloc() version that checks the returned value. + * + * Return value: + * The memory chunk we allocated + * + * Parameters: + * size The size of the chunk we need + * + * Global variables used: + */ +char * +getmem(register int size) +{ + register char *result = (char *) malloc((unsigned) size); + if (result == NULL) { + char buf[FATAL_ERROR_MSG_SIZE]; + sprintf(buf, "*** Error: malloc(%d) failed: %s\n", size, strerror(errno)); + strcat(buf, gettext("mksh: Fatal error: Out of memory\n")); + fputs(buf, stderr); + exit_status = 1; + exit(1); + } + return result; +} + +/* + * retmem(p) + * + * Cover funtion for free() to make it possible to insert advises. + * + * Parameters: + * p The memory block to free + * + * Global variables used: + */ +void +retmem(wchar_t *p) +{ + (void) free((char *) p); +} + +void +retmem_mb(caddr_t p) +{ + (void) free(p); +} + +/* + * getname_fn(name, len, dont_enter) + * + * Hash a name string to the corresponding nameblock. + * + * Return value: + * The Name block for the string + * + * Parameters: + * name The string we want to internalize + * len The length of that string + * dont_enter Don't enter the name if it does not exist + * + * Global variables used: + * funny The vector of semantic tags for characters + * hashtab The hashtable used for the nametable + */ +Name +getname_fn(wchar_t *name, register int len, register Boolean dont_enter, register Boolean * foundp) +{ + register int length; + register wchar_t *cap = name; + register Name np; + static Name_rec empty_Name; + char *tmp_mbs_buffer = NULL; + char *mbs_name = mbs_buffer; + + /* + * First figure out how long the string is. + * If the len argument is -1 we count the chars here. + */ + if (len == FIND_LENGTH) { + length = wcslen(name); + } else { + length = len; + } + + Wstring ws; + ws.init(name, length); + if (length >= MAXPATHLEN) { + mbs_name = tmp_mbs_buffer = getmem((length * MB_LEN_MAX) + 1); + } + (void) wcstombs(mbs_name, ws.get_string(), (length * MB_LEN_MAX) + 1); + + /* Look for the string */ + if (dont_enter || (foundp != 0)) { + np = hashtab.lookup(mbs_name); + if (foundp != 0) { + *foundp = (np != 0) ? true : false; + } + if ((np != 0) || dont_enter) { + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + } + return np; + } else { + np = ALLOC(Name); + } + } else { + Boolean found; + np = hashtab.insert(mbs_name, found); + if (found) { + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + } + return np; + } + } + getname_struct_count += sizeof(struct _Name); + *np = empty_Name; + + np->string_mb = strdup(mbs_name); + if(tmp_mbs_buffer != NULL) { + retmem_mb(tmp_mbs_buffer); + mbs_name = tmp_mbs_buffer = NULL; + } + getname_bytes_count += strlen(np->string_mb) + 1; + /* Fill in the new Name */ + np->stat.time = file_no_time; + np->hash.length = length; + /* Scan the namestring to classify it */ + for (cap = name, len = 0; --length >= 0;) { + len |= get_char_semantics_value(*cap++); + } + np->dollar = BOOLEAN((len & (int) dollar_sem) != 0); + np->meta = BOOLEAN((len & (int) meta_sem) != 0); + np->percent = BOOLEAN((len & (int) percent_sem) != 0); + np->wildcard = BOOLEAN((len & (int) wildcard_sem) != 0); + np->colon = BOOLEAN((len & (int) colon_sem) != 0); + np->parenleft = BOOLEAN((len & (int) parenleft_sem) != 0); + getname_names_count++; + return np; +} + +void +store_name(Name name) +{ + hashtab.insert(name); +} + +void +free_name(Name name) +{ + freename_names_count++; + freename_struct_count += sizeof(struct _Name); + freename_bytes_count += strlen(name->string_mb) + 1; + retmem_mb(name->string_mb); + for (Property next, p = name->prop; p != NULL; p = next) { + next = p->next; + free(p); + } + free(name); +} + +/* + * enable_interrupt(handler) + * + * This routine sets a new interrupt handler for the signals make + * wants to deal with. + * + * Parameters: + * handler The function installed as interrupt handler + * + * Static variables used: + * sigivalue The original signal handler + * sigqvalue The original signal handler + * sigtvalue The original signal handler + * sighvalue The original signal handler + */ +void +enable_interrupt(register void (*handler) (int)) +{ + if (sigivalue != SIG_IGN) { + (void) bsd_signal(SIGINT, (SIG_PF) handler); + } + if (sigqvalue != SIG_IGN) { + (void) bsd_signal(SIGQUIT, (SIG_PF) handler); + } + if (sigtvalue != SIG_IGN) { + (void) bsd_signal(SIGTERM, (SIG_PF) handler); + } + if (sighvalue != SIG_IGN) { + (void) bsd_signal(SIGHUP, (SIG_PF) handler); + } +} + +/* + * setup_char_semantics() + * + * Load the vector char_semantics[] with lexical markers + * + * Parameters: + * + * Global variables used: + * char_semantics The vector of character semantics that we set + */ +void +setup_char_semantics(void) +{ + const char *s; + wchar_t wc_buffer[1]; + int entry; + + if (svr4) { + s = "@-"; + } else { + s = "=@-?!+"; + } + for (s; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) command_prefix_sem; + } + char_semantics[dollar_char_entry] |= (int) dollar_sem; + for (s = "#|=^();&<>*?[]:$`'\"\\\n"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) meta_sem; + } + char_semantics[percent_char_entry] |= (int) percent_sem; + for (s = "@*<%?^"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) special_macro_sem; + } + for (s = "?[*"; MBTOWC(wc_buffer, s); s++) { + entry = get_char_semantics_entry(*wc_buffer); + char_semantics[entry] |= (int) wildcard_sem; + } + char_semantics[colon_char_entry] |= (int) colon_sem; + char_semantics[parenleft_char_entry] |= (int) parenleft_sem; +} + +/* + * errmsg(errnum) + * + * Return the error message for a system call error + * + * Return value: + * An error message string + * + * Parameters: + * errnum The number of the error we want to describe + * + * Global variables used: + * sys_errlist A vector of error messages + * sys_nerr The size of sys_errlist + */ +char * +errmsg(int errnum) +{ + + extern int sys_nerr; + char *errbuf; + + if ((errnum < 0) || (errnum > sys_nerr)) { + errbuf = getmem(6+1+11+1); + (void) sprintf(errbuf, gettext("Error %d"), errnum); + return errbuf; + } else { + return strerror(errnum); + + } +} + +static char static_buf[MAXPATHLEN*3]; + +/* + * fatal_mksh(format, args...) + * + * Print a message and die + * + * Parameters: + * format printf type format string + * args Arguments to match the format + */ +/*VARARGS*/ +void +fatal_mksh(const char *message, ...) +{ + va_list args; + char *buf = static_buf; + char *mksh_fat_err = gettext("mksh: Fatal error: "); + char *cur_wrk_dir = gettext("Current working directory: "); + int mksh_fat_err_len = strlen(mksh_fat_err); + + va_start(args, message); + (void) fflush(stdout); + (void) strcpy(buf, mksh_fat_err); + size_t buf_len = vsnprintf(static_buf + mksh_fat_err_len, + sizeof(static_buf) - mksh_fat_err_len, + message, args) + + mksh_fat_err_len + + strlen(cur_wrk_dir) + + strlen(get_current_path_mksh()) + + 3; // "\n\n" + va_end(args); + if (buf_len >= sizeof(static_buf)) { + buf = getmem(buf_len); + (void) strcpy(buf, mksh_fat_err); + va_start(args, message); + (void) vsprintf(buf + mksh_fat_err_len, message, args); + va_end(args); + } + (void) strcat(buf, "\n"); +/* + if (report_pwd) { + */ + if (1) { + (void) strcat(buf, cur_wrk_dir); + (void) strcat(buf, get_current_path_mksh()); + (void) strcat(buf, "\n"); + } + (void) fputs(buf, stderr); + (void) fflush(stderr); + if (buf != static_buf) { + retmem_mb(buf); + } + exit_status = 1; + exit(1); +} + +/* + * fatal_reader_mksh(format, args...) + * + * Parameters: + * format printf style format string + * args arguments to match the format + */ +/*VARARGS*/ +void +fatal_reader_mksh(const char * pattern, ...) +{ + va_list args; + char message[1000]; + + va_start(args, pattern); +/* + if (file_being_read != NULL) { + WCSTOMBS(mbs_buffer, file_being_read); + if (line_number != 0) { + (void) sprintf(message, + gettext("%s, line %d: %s"), + mbs_buffer, + line_number, + pattern); + } else { + (void) sprintf(message, + "%s: %s", + mbs_buffer, + pattern); + } + pattern = message; + } + */ + + (void) fflush(stdout); + (void) fprintf(stderr, gettext("mksh: Fatal error in reader: ")); + (void) vfprintf(stderr, pattern, args); + (void) fprintf(stderr, "\n"); + va_end(args); + +/* + if (temp_file_name != NULL) { + (void) fprintf(stderr, + gettext("mksh: Temp-file %s not removed\n"), + temp_file_name->string_mb); + temp_file_name = NULL; + } + */ + +/* + if (report_pwd) { + */ + if (1) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path_mksh()); + } + (void) fflush(stderr); + exit_status = 1; + exit(1); +} + +/* + * warning_mksh(format, args...) + * + * Print a message and continue. + * + * Parameters: + * format printf type format string + * args Arguments to match the format + */ +/*VARARGS*/ +void +warning_mksh(char * message, ...) +{ + va_list args; + + va_start(args, message); + (void) fflush(stdout); + (void) fprintf(stderr, gettext("mksh: Warning: ")); + (void) vfprintf(stderr, message, args); + (void) fprintf(stderr, "\n"); + va_end(args); +/* + if (report_pwd) { + */ + if (1) { + (void) fprintf(stderr, + gettext("Current working directory %s\n"), + get_current_path_mksh()); + } + (void) fflush(stderr); +} + +/* + * get_current_path_mksh() + * + * Stuff current_path with the current path if it isnt there already. + * + * Parameters: + * + * Global variables used: + */ +char * +get_current_path_mksh(void) +{ + char pwd[(MAXPATHLEN * MB_LEN_MAX)]; + static char *current_path; + + if (current_path == NULL) { + getcwd(pwd, sizeof(pwd)); + if (pwd[0] == (int) nul_char) { + pwd[0] = (int) slash_char; + pwd[1] = (int) nul_char; + } + current_path = strdup(pwd); + } + return current_path; +} + +/* + * append_prop(target, type) + * + * Create a new property and append it to the property list of a Name. + * + * Return value: + * A new property block for the target + * + * Parameters: + * target The target that wants a new property + * type The type of property being requested + * + * Global variables used: + */ +Property +append_prop(register Name target, register Property_id type) +{ + register Property *insert = &target->prop; + register Property prop = *insert; + register int size; + + switch (type) { + case conditional_prop: + size = sizeof (struct Conditional); + break; + case line_prop: + size = sizeof (struct Line); + break; + case macro_prop: + size = sizeof (struct _Macro); + break; + case makefile_prop: + size = sizeof (struct Makefile); + break; + case member_prop: + size = sizeof (struct Member); + break; + case recursive_prop: + size = sizeof (struct Recursive); + break; + case sccs_prop: + size = sizeof (struct Sccs); + break; + case suffix_prop: + size = sizeof (struct Suffix); + break; + case target_prop: + size = sizeof (struct Target); + break; + case time_prop: + size = sizeof (struct STime); + break; + case vpath_alias_prop: + size = sizeof (struct Vpath_alias); + break; + case long_member_name_prop: + size = sizeof (struct Long_member_name); + break; + case macro_append_prop: + size = sizeof (struct _Macro_appendix); + break; + case env_mem_prop: + size = sizeof (struct _Env_mem); + break; + default: + fatal_mksh(gettext("Internal error. Unknown prop type %d"), type); + } + for (; prop != NULL; insert = &prop->next, prop = *insert); + size += PROPERTY_HEAD_SIZE; + *insert = prop = (Property) getmem(size); + memset((char *) prop, 0, size); + prop->type = type; + prop->next = NULL; + return prop; +} + +/* + * maybe_append_prop(target, type) + * + * Append a property to the Name if none of this type exists + * else return the one already there + * + * Return value: + * A property of the requested type for the target + * + * Parameters: + * target The target that wants a new property + * type The type of property being requested + * + * Global variables used: + */ +Property +maybe_append_prop(register Name target, register Property_id type) +{ + register Property prop; + + if ((prop = get_prop(target->prop, type)) != NULL) { + return prop; + } + return append_prop(target, type); +} + +/* + * get_prop(start, type) + * + * Scan the property list of a Name to find the next property + * of a given type. + * + * Return value: + * The first property of the type, if any left + * + * Parameters: + * start The first property block to check for type + * type The type of property block we need + * + * Global variables used: + */ +Property +get_prop(register Property start, register Property_id type) +{ + for (; start != NULL; start = start->next) { + if (start->type == type) { + return start; + } + } + return NULL; +} + +/* + * append_string(from, to, length) + * + * Append a C string to a make string expanding it if nessecary + * + * Parameters: + * from The source (C style) string + * to The destination (make style) string + * length The length of the from string + * + * Global variables used: + */ +void +append_string(register wchar_t *from, register String to, register int length) +{ + if (length == FIND_LENGTH) { + length = wcslen(from); + } + if (to->buffer.start == NULL) { + expand_string(to, 32 + length); + } + if (to->buffer.end - to->text.p <= length) { + expand_string(to, + (to->buffer.end - to->buffer.start) * 2 + + length); + } + if (length > 0) { + (void) wcsncpy(to->text.p, from, length); + to->text.p += length; + } + *(to->text.p) = (int) nul_char; +} + +wchar_t * get_wstring(char *from) { + if(from == NULL) { + return NULL; + } + getwstring_count++; + wchar_t * wcbuf = ALLOC_WC(strlen(from) + 1); + mbstowcs(wcbuf, from, strlen(from)+1); + return wcbuf; +} + +void +append_string(register char *from, register String to, register int length) +{ + if (length == FIND_LENGTH) { + length = strlen(from); + } + if (to->buffer.start == NULL) { + expand_string(to, 32 + length); + } + if (to->buffer.end - to->text.p <= length) { + expand_string(to, + (to->buffer.end - to->buffer.start) * 2 + + length); + } + if (length > 0) { + (void) mbstowcs(to->text.p, from, length); + to->text.p += length; + } + *(to->text.p) = (int) nul_char; +} + +/* + * expand_string(string, length) + * + * Allocate more memory for strings that run out of space. + * + * Parameters: + * string The make style string we want to expand + * length The new length we need + * + * Global variables used: + */ +static void +expand_string(register String string, register int length) +{ + register wchar_t *p; + + if (string->buffer.start == NULL) { + /* For strings that have no memory allocated */ + string->buffer.start = + string->text.p = + string->text.end = + ALLOC_WC(length); + string->buffer.end = string->buffer.start + length; + string->text.p[0] = (int) nul_char; + string->free_after_use = true; + expandstring_count++; + return; + } + if (string->buffer.end - string->buffer.start >= length) { + /* If we really don't need more memory. */ + return; + } + /* + * Get more memory, copy the string and free the old buffer if + * it is was malloc()'ed. + */ + expandstring_count++; + p = ALLOC_WC(length); + (void) wcscpy(p, string->buffer.start); + string->text.p = p + (string->text.p - string->buffer.start); + string->text.end = p + (string->text.end - string->buffer.start); + string->buffer.end = p + length; + if (string->free_after_use) { + retmem(string->buffer.start); + } + string->buffer.start = p; + string->free_after_use = true; +} + +/* + * append_char(from, to) + * + * Append one char to a make string expanding it if nessecary + * + * Parameters: + * from Single character to append to string + * to The destination (make style) string + * + * Global variables used: + */ +void +append_char(wchar_t from, register String to) +{ + if (to->buffer.start == NULL) { + expand_string(to, 32); + } + if (to->buffer.end - to->text.p <= 2) { + expand_string(to, to->buffer.end - to->buffer.start + 32); + } + *(to->text.p)++ = from; + *(to->text.p) = (int) nul_char; +} + +/* + * handle_interrupt_mksh() + * + * This is where C-C traps are caught. + */ +void +handle_interrupt_mksh(int) +{ + (void) fflush(stdout); + /* Make sure the processes running under us terminate first. */ + if (childPid > 0) { + kill(childPid, SIGTERM); + childPid = -1; + } + while (wait((int *) NULL) != -1); + exit_status = 2; + exit(2); +} + +/* + * setup_interrupt() + * + * This routine saves the original interrupt handler pointers + * + * Parameters: + * + * Static variables used: + * sigivalue The original signal handler + * sigqvalue The original signal handler + * sigtvalue The original signal handler + * sighvalue The original signal handler + */ +void +setup_interrupt(register void (*handler) (int)) +{ + sigivalue = bsd_signal(SIGINT, SIG_IGN); + sigqvalue = bsd_signal(SIGQUIT, SIG_IGN); + sigtvalue = bsd_signal(SIGTERM, SIG_IGN); + sighvalue = bsd_signal(SIGHUP, SIG_IGN); + enable_interrupt(handler); +} + + +void +mbstowcs_with_check(wchar_t *pwcs, const char *s, size_t n) +{ + if(mbstowcs(pwcs, s, n) == -1) { + fatal_mksh(gettext("The string `%s' is not valid in current locale"), s); + } +} + + + +Wstring::Wstring() +{ + INIT_STRING_FROM_STACK(string, string_buf); +} + +Wstring::Wstring(struct _Name * name) +{ + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name->string_mb, &string, name->hash.length); +} + +Wstring::~Wstring() +{ + if(string.free_after_use) { + retmem(string.buffer.start); + } +} + +void +Wstring::init(struct _Name * name) +{ + if(string.free_after_use) { + retmem(string.buffer.start); + } + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name->string_mb, &string, name->hash.length); +} + +void +Wstring::init(wchar_t * name, unsigned length) +{ + INIT_STRING_FROM_STACK(string, string_buf); + append_string(name, &string, length); + string.buffer.start[length] = 0; +} + +Boolean +Wstring::equaln(wchar_t * str, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start, str, length); +} + +Boolean +Wstring::equaln(Wstring * str, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start, str->string.buffer.start, length); +} + +Boolean +Wstring::equal(wchar_t * str, unsigned off, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start + off, str, length); +} + +Boolean +Wstring::equal(wchar_t * str, unsigned off) +{ + return (Boolean)IS_WEQUAL(string.buffer.start + off, str); +} + +Boolean +Wstring::equal(wchar_t * str) +{ + return equal(str, 0); +} + +Boolean +Wstring::equal(Wstring * str, unsigned off, unsigned length) +{ + return (Boolean)IS_WEQUALN(string.buffer.start + off, str->string.buffer.start, length); +} + +Boolean +Wstring::equal(Wstring * str) +{ + return equal(str, 0); +} + +Boolean +Wstring::equal(Wstring * str, unsigned off) +{ + return (Boolean)IS_WEQUAL(string.buffer.start + off, str->string.buffer.start); +} + +void +Wstring::append_to_str(struct _String * str, unsigned off, unsigned length) +{ + append_string(string.buffer.start + off, str, length); +} + +Name +Name_set::lookup(const char *key) +{ + for (entry *node = root; node != 0;) { + int res = strcmp(key, node->name->string_mb); + if (res < 0) { + node = node->left; + } else if (res > 0) { + node = node->right; + } else { + return node->name; + } + } + return 0; +} + +Name +Name_set::insert(const char *key, Boolean &found) +{ + Name name = 0; + + if (root != 0) { + for (entry *node = root; name == 0;) { + int res = strcmp(key, node->name->string_mb); + if (res < 0) { + if (node->left != 0) { + node = node->left; + } else { + found = false; + name = ALLOC(Name); + + node->left = new entry(name, node); + rebalance(node); + } + } else if (res > 0) { + if (node->right != 0) { + node = node->right; + } else { + found = false; + name = ALLOC(Name); + + node->right = new entry(name, node); + rebalance(node); + } + } else { + found = true; + name = node->name; + } + } + } else { + found = false; + name = ALLOC(Name); + + root = new entry(name, 0); + } + return name; +} + +void +Name_set::insert(Name name) { + if (root != 0) { + for (entry *node = root;;) { + int res = strcmp(name->string_mb, node->name->string_mb); + if (res < 0) { + if (node->left != 0) { + node = node->left; + } else { + node->left = new entry(name, node); + rebalance(node); + break; + } + } else if (res > 0) { + if (node->right != 0) { + node = node->right; + } else { + node->right = new entry(name, node); + rebalance(node); + break; + } + } else { + // should be an error: inserting already existing name + break; + } + } + } else { + root = new entry(name, 0); + } +} + +void +Name_set::rebalance(Name_set::entry *node) { + for (; node != 0; node = node->parent) { + entry *right = node->right; + entry *left = node->left; + + unsigned rdepth = (right != 0) ? right->depth : 0; + unsigned ldepth = (left != 0) ? left->depth : 0; + + if (ldepth > rdepth + 1) { + if ((node->left = left->right) != 0) { + left->right->parent = node; + } + if ((left->parent = node->parent) != 0) { + if (node == node->parent->right) { + node->parent->right = left; + } else { + node->parent->left = left; + } + } else { + root = left; + } + left->right = node; + node->parent = left; + + node->setup_depth(); + node = left; + } else if (rdepth > ldepth + 1) { + if ((node->right = right->left) != 0) { + right->left->parent = node; + } + if ((right->parent = node->parent) != 0) { + if (node == node->parent->right) { + node->parent->right = right; + } else { + node->parent->left = right; + } + } else { + root = right; + } + right->left = node; + node->parent = right; + + node->setup_depth(); + node = right; + } + node->setup_depth(); + } +} + +Name_set::iterator +Name_set::begin() const { + for (entry *node = root; node != 0; node = node->left) { + if (node->left == 0) { + return iterator(node); + } + } + return iterator(); +} + +Name_set::iterator& +Name_set::iterator::operator++() { + if (node != 0) { + if (node->right != 0) { + node = node->right; + while (node->left != 0) { + node = node->left; + } + } else { + while ((node->parent != 0) && (node->parent->right == node)) { + node = node->parent; + } + node = node->parent; + } + } + return *this; +} diff --git a/lib/mksh/mksh.cc b/lib/mksh/mksh.cc new file mode 100644 index 0000000..9c0ec13 --- /dev/null +++ b/lib/mksh/mksh.cc @@ -0,0 +1,131 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * mksh.cc + * + * Execute the command(s) of one Make or DMake rule + */ + +/* + * Included files + */ +#include /* redirect_io() */ +#include /* retmem() */ +#include +#include +#include + + +/* + * Workaround for NFS bug. Sometimes, when running 'chdir' on a remote + * dmake server, it fails with "Stale NFS file handle" error. + * The second attempt seems to work. + */ +int +my_chdir(char * dir) { + int res = chdir(dir); + if (res != 0 && (errno == ESTALE || errno == EAGAIN)) { + /* Stale NFS file handle. Try again */ + res = chdir(dir); + } + return res; +} + + +/* + * File table of contents + */ +static void change_sunpro_dependencies_value(char *oldpath, char *newpath); +static void init_mksh_globals(char *shell); +static void set_env_vars(char *env_list[]); + + +static void +set_env_vars(char *env_list[]) +{ + char **env_list_p; + + for (env_list_p = env_list; + *env_list_p != (char *) NULL; + env_list_p++) { + putenv(*env_list_p); + } +} + +static void +init_mksh_globals(char *shell) +{ +/* + MBSTOWCS(wcs_buffer, "SHELL"); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); + MBSTOWCS(wcs_buffer, shell); + (void) SETVAR(shell_name, GETNAME(wcs_buffer, FIND_LENGTH), false); + */ + char * dmake_shell; + if ((dmake_shell = getenv("DMAKE_SHELL")) == NULL) { + dmake_shell = shell; + } + MBSTOWCS(wcs_buffer, dmake_shell); + shell_name = GETNAME(wcs_buffer, FIND_LENGTH); +} + +/* + * Change the pathname in the value of the SUNPRO_DEPENDENCIES env variable + * from oldpath to newpath. + */ +static void +change_sunpro_dependencies_value(char *oldpath, char *newpath) +{ + char buf[MAXPATHLEN]; + static char *env; + int length; + int oldpathlen; + char *sp_dep_value; + + /* check if SUNPRO_DEPENDENCIES is set in the environment */ + if ((sp_dep_value = getenv("SUNPRO_DEPENDENCIES")) != NULL) { + oldpathlen = strlen(oldpath); + /* check if oldpath is indeed in the value of SUNPRO_DEPENDENCIES */ + if (strncmp(oldpath, sp_dep_value, oldpathlen) == 0) { + (void) sprintf(buf, + "%s%s", + newpath, + sp_dep_value + oldpathlen); + length = 2 + + strlen("SUNPRO_DEPENDENCIES") + + strlen(buf); + env = getmem(length); + (void) sprintf(env, + "%s=%s", + "SUNPRO_DEPENDENCIES", + buf); + (void) putenv(env); + } + } +} + + diff --git a/lib/mksh/read.cc b/lib/mksh/read.cc new file mode 100644 index 0000000..6a6a261 --- /dev/null +++ b/lib/mksh/read.cc @@ -0,0 +1,170 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +/* + * read.c + * + * This file contains the makefile reader. + */ + +/* + * Included files + */ +#include /* retmem() */ +#include +#include /* read() */ +#include /* close(), unlink(), read() */ +#include + +#define STRING_LEN_TO_CONVERT (8*1024) + +/* + * get_next_block_fn(source) + * + * Will get the next block of text to read either + * by popping one source bVSIZEOFlock of the stack of Sources + * or by reading some more from the makefile. + * + * Return value: + * The new source block to read from + * + * Parameters: + * source The old source block + * + * Global variables used: + * file_being_read The name of the current file, error msg + */ +Boolean make_state_locked; +Source +get_next_block_fn(register Source source) +{ + register off_t to_read; + register int length; + register size_t num_wc_chars; + char ch_save; + char *ptr; + + if (source == NULL) { + return NULL; + } + if ((source->fd < 0) || + ((source->bytes_left_in_file <= 0) && + (source->inp_buf_ptr >= source->inp_buf_end))) { + /* We can't read from the makefile, so pop the source block */ + if (source->fd > 2) { + (void) close(source->fd); + if (make_state_lockfile != NULL) { + (void) unlink(make_state_lockfile); + retmem_mb(make_state_lockfile); + make_state_lockfile = NULL; + make_state_locked = false; + } + } + if (source->string.free_after_use && + (source->string.buffer.start != NULL)) { + retmem(source->string.buffer.start); + source->string.buffer.start = NULL; + } + if (source->inp_buf != NULL) { + retmem_mb(source->inp_buf); + source->inp_buf = NULL; + } + source = source->previous; + if (source != NULL) { + source->error_converting = false; + } + return source; + } + if (source->bytes_left_in_file > 0) { + /* + * Read the whole makefile. + * Hopefully the kernel managed to prefetch the stuff. + */ + to_read = source->bytes_left_in_file; + source->inp_buf_ptr = source->inp_buf = getmem(to_read + 1); + source->inp_buf_end = source->inp_buf + to_read; + length = read(source->fd, source->inp_buf, (unsigned int) to_read); + if (length != to_read) { + WCSTOMBS(mbs_buffer, file_being_read); + if (length == 0) { + fatal_mksh(gettext("Error reading `%s': Premature EOF"), + mbs_buffer); + } else { + fatal_mksh(gettext("Error reading `%s': %s"), + mbs_buffer, + errmsg(errno)); + } + } + *source->inp_buf_end = nul_char; + source->bytes_left_in_file = 0; + } + /* + * Try to convert the next piece. + */ + ptr = source->inp_buf_ptr + STRING_LEN_TO_CONVERT; + if (ptr > source->inp_buf_end) { + ptr = source->inp_buf_end; + } + for (num_wc_chars = 0; ptr > source->inp_buf_ptr; ptr--) { + ch_save = *ptr; + *ptr = nul_char; + num_wc_chars = mbstowcs(source->string.text.end, + source->inp_buf_ptr, + STRING_LEN_TO_CONVERT); + *ptr = ch_save; + if (num_wc_chars != (size_t)-1) { + break; + } + } + + if ((int) num_wc_chars == (size_t)-1) { + source->error_converting = true; + return source; + } + + source->error_converting = false; + source->inp_buf_ptr = ptr; + source->string.text.end += num_wc_chars; + *source->string.text.end = 0; + + if (source->inp_buf_ptr >= source->inp_buf_end) { + if (*(source->string.text.end - 1) != (int) newline_char) { + WCSTOMBS(mbs_buffer, file_being_read); + warning_mksh(gettext("newline is not last character in file %s"), + mbs_buffer); + *source->string.text.end++ = (int) newline_char; + *source->string.text.end = (int) nul_char; + *source->string.buffer.end++; + } + if (source->inp_buf != NULL) { + retmem_mb(source->inp_buf); + source->inp_buf = NULL; + } + } + return source; +} + + diff --git a/lib/vroot/access.cc b/lib/vroot/access.cc new file mode 100644 index 0000000..7161c10 --- /dev/null +++ b/lib/vroot/access.cc @@ -0,0 +1,42 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +static int access_thunk(char *path) +{ + vroot_result= access(path, vroot_args.access.mode); + return((vroot_result == 0) || (errno != ENOENT)); +} + +int access_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.access.mode= mode; + translate_with_thunk(path, access_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/args.cc b/lib/vroot/args.cc new file mode 100644 index 0000000..0f8155a --- /dev/null +++ b/lib/vroot/args.cc @@ -0,0 +1,31 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +union Args vroot_args; +int vroot_result; diff --git a/lib/vroot/chdir.cc b/lib/vroot/chdir.cc new file mode 100644 index 0000000..fb4aaa3 --- /dev/null +++ b/lib/vroot/chdir.cc @@ -0,0 +1,41 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +static int chdir_thunk(char *path) +{ + vroot_result= chdir(path); + return(vroot_result == 0); +} + +int chdir_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, chdir_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chmod.cc b/lib/vroot/chmod.cc new file mode 100644 index 0000000..7217462 --- /dev/null +++ b/lib/vroot/chmod.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int chmod(const char *path, mode_t mode); + +#include +#include + +static int chmod_thunk(char *path) +{ + vroot_result= chmod(path, vroot_args.chmod.mode); + return(vroot_result == 0); +} + +int chmod_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.chmod.mode= mode; + translate_with_thunk(path, chmod_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chown.cc b/lib/vroot/chown.cc new file mode 100644 index 0000000..00018ba --- /dev/null +++ b/lib/vroot/chown.cc @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int chown(const char *path, uid_t owner, gid_t group); + +#include +#include + +static int chown_thunk(char *path) +{ + vroot_result= chown(path, vroot_args.chown.user, vroot_args.chown.group); + return(vroot_result == 0); +} + +int chown_vroot(char *path, int user, int group, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.chown.user= user; + vroot_args.chown.group= group; + translate_with_thunk(path, chown_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/chroot.cc b/lib/vroot/chroot.cc new file mode 100644 index 0000000..0561e1e --- /dev/null +++ b/lib/vroot/chroot.cc @@ -0,0 +1,44 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int chroot(const char *path); + +#include +#include + +static int chroot_thunk(char *path) +{ + vroot_result= chroot(path); + return(vroot_result == 0); +} + +int chroot_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, chroot_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/creat.cc b/lib/vroot/creat.cc new file mode 100644 index 0000000..9bf7fad --- /dev/null +++ b/lib/vroot/creat.cc @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern int creat(const char *path, mode_t mode); + +#include +#include + +static int creat_thunk(char *path) +{ + vroot_result= creat(path, vroot_args.creat.mode); + return(vroot_result >= 0); +} + +int creat_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.creat.mode= mode; + translate_with_thunk(path, creat_thunk, vroot_path, vroot_vroot, rw_write); + return(vroot_result); +} diff --git a/lib/vroot/execve.cc b/lib/vroot/execve.cc new file mode 100644 index 0000000..cf23e66 --- /dev/null +++ b/lib/vroot/execve.cc @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int execve (const char *path, char *const argv[], char *const envp[]); + +#include +#include + +static int execve_thunk(char *path) +{ + execve(path, vroot_args.execve.argv, vroot_args.execve.environ); + switch (errno) { + case ETXTBSY: + case ENOEXEC: return 1; + default: return 0; + } +} + +int execve_vroot(char *path, char **argv, char **environ, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.execve.argv= argv; + vroot_args.execve.environ= environ; + translate_with_thunk(path, execve_thunk, vroot_path, vroot_vroot, rw_read); + return(-1); +} diff --git a/lib/vroot/lock.cc b/lib/vroot/lock.cc new file mode 100644 index 0000000..d3389ce --- /dev/null +++ b/lib/vroot/lock.cc @@ -0,0 +1,175 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* errno */ +#include + +extern char *sys_errlist[]; +extern int sys_nerr; + +static void file_lock_error(char *msg, char *file, char *str, int arg1, int arg2); + +#define BLOCK_INTERUPTS sigfillset(&newset) ; \ + sigprocmask(SIG_SETMASK, &newset, &oldset) + +#define UNBLOCK_INTERUPTS \ + sigprocmask(SIG_SETMASK, &oldset, &newset) + +/* + * This code stolen from the NSE library and changed to not depend + * upon any NSE routines or header files. + * + * Simple file locking. + * Create a symlink to a file. The "test and set" will be + * atomic as creating the symlink provides both functions. + * + * The timeout value specifies how long to wait for stale locks + * to disappear. If the lock is more than 'timeout' seconds old + * then it is ok to blow it away. This part has a small window + * of vunerability as the operations of testing the time, + * removing the lock and creating a new one are not atomic. + * It would be possible for two processes to both decide to blow + * away the lock and then have process A remove the lock and establish + * its own, and then then have process B remove the lock which accidentily + * removes A's lock rather than the stale one. + * + * A further complication is with the NFS. If the file in question is + * being served by an NFS server, then its time is set by that server. + * We can not use the time on the client machine to check for a stale + * lock. Therefore, a temp file on the server is created to get + * the servers current time. + * + * Returns an error message. NULL return means the lock was obtained. + * + * 12/6/91 Added the parameter "file_locked". Before this parameter + * was added, the calling procedure would have to wait for file_lock() + * to return before it sets the flag. If the user interrupted "make" + * between the time the lock was acquired and the time file_lock() + * returns, make wouldn't know that the file has been locked, and therefore + * it wouldn' remove the lock. Setting the flag right after locking the file + * makes this window much smaller. + */ + +int +file_lock(char *name, char *lockname, int *file_locked, int timeout) +{ + int counter = 0; + static char msg[MAXPATHLEN+1]; + int printed_warning = 0; + int r; + struct stat statb; + sigset_t newset; + sigset_t oldset; + + *file_locked = 0; + if (timeout <= 0) { + timeout = 120; + } + for (;;) { + BLOCK_INTERUPTS; + r = symlink(name, lockname); + if (r == 0) { + *file_locked = 1; + UNBLOCK_INTERUPTS; + return 0; /* success */ + } + UNBLOCK_INTERUPTS; + + if (errno != EEXIST) { + file_lock_error(msg, name, (char *)"symlink(%s, %s)", + (int) name, (int) lockname); + fprintf(stderr, "%s", msg); + return errno; + } + + counter = 0; + for (;;) { + sleep(1); + r = lstat(lockname, &statb); + if (r == -1) { + /* + * The lock must have just gone away - try + * again. + */ + break; + } + + if ((counter > 5) && (!printed_warning)) { + /* Print waiting message after 5 secs */ + (void) getcwd(msg, MAXPATHLEN); + fprintf(stderr, + gettext("file_lock: file %s is already locked.\n"), + name); + fprintf(stderr, + gettext("file_lock: will periodically check the lockfile %s for two minutes.\n"), + lockname); + fprintf(stderr, + gettext("Current working directory %s\n"), + msg); + + printed_warning = 1; + } + + if (++counter > timeout ) { + /* + * Waited enough - return an error.. + */ + return EEXIST; + } + } + } + /* NOTREACHED */ +} + +/* + * Format a message telling why the lock could not be created. + */ +static void +file_lock_error(char *msg, char *file, char *str, int arg1, int arg2) +{ + int len; + + sprintf(msg, gettext("Could not lock file `%s'; "), file); + len = strlen(msg); + sprintf(&msg[len], str, arg1, arg2); + strcat(msg, gettext(" failed - ")); + if (errno < sys_nerr) { + strcat(msg, strerror(errno)); + } else { + len = strlen(msg); + sprintf(&msg[len], "errno %d", errno); + } +} + diff --git a/lib/vroot/lstat.cc b/lib/vroot/lstat.cc new file mode 100644 index 0000000..e5a8b2d --- /dev/null +++ b/lib/vroot/lstat.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int lstat(const char *path, struct stat *buf); + +#include +#include + +static int lstat_thunk(char *path) +{ + vroot_result= lstat(path, vroot_args.lstat.buffer); + return(vroot_result == 0); +} + +int lstat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.lstat.buffer= buffer; + translate_with_thunk(path, lstat_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/mkdir.cc b/lib/vroot/mkdir.cc new file mode 100644 index 0000000..d64c6ef --- /dev/null +++ b/lib/vroot/mkdir.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int mkdir(const char *path, mode_t mode); + +#include +#include + +static int mkdir_thunk(char *path) +{ + vroot_result= mkdir(path, vroot_args.mkdir.mode); + return(vroot_result == 0); +} + +int mkdir_vroot(char *path, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.mkdir.mode= mode; + translate_with_thunk(path, mkdir_thunk, vroot_path, vroot_vroot, rw_write); + return(vroot_result); +} diff --git a/lib/vroot/mount.cc b/lib/vroot/mount.cc new file mode 100644 index 0000000..6ffc67f --- /dev/null +++ b/lib/vroot/mount.cc @@ -0,0 +1,47 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1995 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int mount(const char *spec, const char *dir, int mflag, ...); + +#include +#include + +static int mount_thunk(char *path) +{ + vroot_result= mount(path, vroot_args.mount.name, vroot_args.mount.mode); + return(vroot_result == 0); +} + +int mount_vroot(char *target, char *name, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.mount.name= name; + vroot_args.mount.mode= mode; + translate_with_thunk(target, mount_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/open.cc b/lib/vroot/open.cc new file mode 100644 index 0000000..8780baf --- /dev/null +++ b/lib/vroot/open.cc @@ -0,0 +1,49 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern int open(const char *path, int oflag, ...); + +#include +#include + +static int open_thunk(char *path) +{ + vroot_result= open(path, vroot_args.open.flags, vroot_args.open.mode); + return(vroot_result >= 0); +} + +int open_vroot(char *path, int flags, int mode, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.open.flags= flags; + vroot_args.open.mode= mode; + translate_with_thunk(path, open_thunk, vroot_path, vroot_vroot, + ((flags & (O_CREAT|O_APPEND)) != 0) ? rw_write : rw_read); + return(vroot_result); +} diff --git a/lib/vroot/readlink.cc b/lib/vroot/readlink.cc new file mode 100644 index 0000000..06ca253 --- /dev/null +++ b/lib/vroot/readlink.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int readlink(const char *path, void *buf, size_t bufsiz); + +#include +#include + +static int readlink_thunk(char *path) +{ + vroot_result= readlink(path, vroot_args.readlink.buffer, vroot_args.readlink.buffer_size); + return(vroot_result >= 0); +} + +int readlink_vroot(char *path, char *buffer, int buffer_size, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.readlink.buffer= buffer; + vroot_args.readlink.buffer_size= buffer_size; + translate_with_thunk(path, readlink_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/report.cc b/lib/vroot/report.cc new file mode 100644 index 0000000..9def773 --- /dev/null +++ b/lib/vroot/report.cc @@ -0,0 +1,333 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include /* for tmpdir */ + +static FILE *report_file; +static FILE *command_output_fp; +static char *target_being_reported_for; +static char *search_dir; +static char command_output_tmpfile[30]; +static int is_path = 0; +static char sfile[MAXPATHLEN]; +extern "C" { +static void (*warning_ptr) (char *, ...) = (void (*) (char *, ...)) NULL; +} + +FILE * +get_report_file(void) +{ + return(report_file); +} + +char * +get_target_being_reported_for(void) +{ + return(target_being_reported_for); +} + +extern "C" { +static void +close_report_file(void) +{ + (void)fputs("\n", report_file); + (void)fclose(report_file); +} +} // extern "C" + +static void +clean_up(FILE *nse_depinfo_fp, FILE *merge_fp, char *nse_depinfo_file, char *merge_file, int unlinkf) +{ + fclose(nse_depinfo_fp); + fclose(merge_fp); + fclose(command_output_fp); + unlink(command_output_tmpfile); + if (unlinkf) + unlink(merge_file); + else + rename(merge_file, nse_depinfo_file); +} + + +/* + * Update the file, if necessary. We don't want to rewrite + * the file if we don't have to because we don't want the time of the file + * to change in that case. + */ + +extern "C" { +static void +close_file(void) +{ + char line[MAXPATHLEN+2]; + char buf[MAXPATHLEN+2]; + FILE *nse_depinfo_fp; + FILE *merge_fp; + char nse_depinfo_file[MAXPATHLEN]; + char merge_file[MAXPATHLEN]; + char lock_file[MAXPATHLEN]; + int err; + int len; + int changed = 0; + int file_locked; + + fprintf(command_output_fp, "\n"); + fclose(command_output_fp); + if ((command_output_fp = fopen(command_output_tmpfile, "r")) == NULL) { + return; + } + sprintf(nse_depinfo_file, "%s/%s", search_dir, NSE_DEPINFO); + sprintf(merge_file, "%s/.tmp%s.%d", search_dir, NSE_DEPINFO, getpid()); + sprintf(lock_file, "%s/%s", search_dir, NSE_DEPINFO_LOCK); + err = file_lock(nse_depinfo_file, lock_file, &file_locked, 0); + if (err) { + if (warning_ptr != (void (*) (char *, ...)) NULL) { + (*warning_ptr)(gettext("Couldn't write to %s"), nse_depinfo_file); + } + unlink(command_output_tmpfile); + return; + } + /* If .nse_depinfo file doesn't exist */ + if ((nse_depinfo_fp = fopen(nse_depinfo_file, "r+")) == NULL) { + if (is_path) { + if ((nse_depinfo_fp = + fopen(nse_depinfo_file, "w")) == NULL) { + fprintf(stderr, gettext("Cannot open `%s' for writing\n"), + nse_depinfo_file); + unlink(command_output_tmpfile); + + unlink(lock_file); + return; + } + while (fgets(line, MAXPATHLEN+2, command_output_fp) + != NULL) { + fprintf(nse_depinfo_fp, "%s", line); + } + fclose(command_output_fp); + } + fclose(nse_depinfo_fp); + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } + if ((merge_fp = fopen(merge_file, "w")) == NULL) { + fprintf(stderr, gettext("Cannot open %s for writing\n"), merge_file); + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } + len = strlen(sfile); + while (fgets(line, MAXPATHLEN+2, nse_depinfo_fp) != NULL) { + if (strncmp(line, sfile, len) == 0 && line[len] == ':') { + while (fgets(buf, MAXPATHLEN+2, command_output_fp) + != NULL) { + if (is_path) { + fprintf(merge_fp, "%s", buf); + if (strcmp(line, buf)) { + /* changed */ + changed = 1; + } + } + if (buf[strlen(buf)-1] == '\n') { + break; + } + } + if (changed || !is_path) { + while (fgets(line, MAXPATHLEN, nse_depinfo_fp) + != NULL) { + fputs(line, merge_fp); + } + clean_up(nse_depinfo_fp, merge_fp, + nse_depinfo_file, merge_file, 0); + } else { + clean_up(nse_depinfo_fp, merge_fp, + nse_depinfo_file, merge_file, 1); + } + if (file_locked) { + unlink(lock_file); + } + unlink(command_output_tmpfile); + return; + } /* entry found */ + fputs(line, merge_fp); + } + /* Entry never found. Add it if there is a search path */ + if (is_path) { + while (fgets(line, MAXPATHLEN+2, command_output_fp) != NULL) { + fprintf(nse_depinfo_fp, "%s", line); + } + } + clean_up(nse_depinfo_fp, merge_fp, nse_depinfo_file, merge_file, 1); + if (file_locked) { + unlink(lock_file); + } +} + +} // extern "C" + +static void +report_dep(char *iflag, char *filename) +{ + + if (command_output_fp == NULL) { + sprintf(command_output_tmpfile, + "%s/%s.%d.XXXXXX", tmpdir, NSE_DEPINFO, getpid()); + int fd = mkstemp(command_output_tmpfile); + if ((fd < 0) || (command_output_fp = fdopen(fd, "w")) == NULL) { + return; + } + if ((search_dir = getenv("NSE_DEP")) == NULL) { + return; + } + atexit(close_file); + strcpy(sfile, filename); + if (iflag == NULL || *iflag == '\0') { + return; + } + fprintf(command_output_fp, "%s:", sfile); + } + fprintf(command_output_fp, " "); + fprintf(command_output_fp, iflag); + if (iflag != NULL) { + is_path = 1; + } +} + +void +report_libdep(char *lib, char *flag) +{ + char *ptr; + char filename[MAXPATHLEN]; + char *p; + + if ((p= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + return; + } + ptr = strchr(p, ' '); + if(ptr) { + sprintf(filename, "%s-%s", ptr+1, flag); + is_path = 1; + report_dep(lib, filename); + } +} + +void +report_search_path(char *iflag) +{ + char curdir[MAXPATHLEN]; + char *sdir; + char *newiflag; + char filename[MAXPATHLEN]; + char *p, *ptr; + + if ((sdir = getenv("NSE_DEP")) == NULL) { + return; + } + if ((p= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + return; + } + ptr = strchr(p, ' '); + if( ! ptr ) { + return; + } + sprintf(filename, "%s-CPP", ptr+1); + getcwd(curdir, sizeof(curdir)); + if (strcmp(curdir, sdir) != 0 && strlen(iflag) > 2 && + iflag[2] != '/') { + /* Makefile must have had an "cd xx; cc ..." */ + /* Modify the -I path to be relative to the cd */ + newiflag = (char *)malloc(strlen(iflag) + strlen(curdir) + 2); + sprintf(newiflag, "-%c%s/%s", iflag[1], curdir, &iflag[2]); + report_dep(newiflag, filename); + } else { + report_dep(iflag, filename); + } +} + +void +report_dependency(const char *name) +{ + register char *filename; + char buffer[MAXPATHLEN+1]; + register char *p; + register char *p2; + char nse_depinfo_file[MAXPATHLEN]; + + if (report_file == NULL) { + if ((filename= getenv(SUNPRO_DEPENDENCIES)) == NULL) { + report_file = (FILE *)-1; + return; + } + if (strlen(filename) == 0) { + report_file = (FILE *)-1; + return; + } + (void)strcpy(buffer, name); + name = buffer; + p = strchr(filename, ' '); + if(p) { + *p= 0; + } else { + report_file = (FILE *)-1; + return; + } + if ((report_file= fopen(filename, "a")) == NULL) { + if ((report_file= fopen(filename, "w")) == NULL) { + report_file= (FILE *)-1; + return; + } + } + atexit(close_report_file); + if ((p2= strchr(p+1, ' ')) != NULL) + *p2= 0; + target_being_reported_for= (char *)malloc((unsigned)(strlen(p+1)+1)); + (void)strcpy(target_being_reported_for, p+1); + (void)fputs(p+1, report_file); + (void)fputs(":", report_file); + *p= ' '; + if (p2 != NULL) + *p2= ' '; + } + if (report_file == (FILE *)-1) + return; + (void)fputs(name, report_file); + (void)fputs(" ", report_file); +} + + diff --git a/lib/vroot/rmdir.cc b/lib/vroot/rmdir.cc new file mode 100644 index 0000000..f297aac --- /dev/null +++ b/lib/vroot/rmdir.cc @@ -0,0 +1,44 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int rmdir(const char *path); + +#include +#include + +static int rmdir_thunk(char *path) +{ + vroot_result= rmdir(path); + return(vroot_result == 0); +} + +int rmdir_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, rmdir_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/setenv.cc b/lib/vroot/setenv.cc new file mode 100644 index 0000000..881be5a --- /dev/null +++ b/lib/vroot/setenv.cc @@ -0,0 +1,61 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1994 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include +#include + +extern char **environ; + +static short setenv_made_new_vector= 0; + +char *setenv(char *name, char *value) +{ char *p= NULL, **q; + int length= 0, vl; + + if ((p= getenv(name)) == NULL) { /* Allocate new vector */ + for (q= environ; *q != NULL; q++, length++); + q= (char **)malloc((unsigned)(sizeof(char *)*(length+2))); + memcpy(((char *)q)+sizeof(char *), (char *)environ, sizeof(char *)*(length+1)); + if (setenv_made_new_vector++) + free((char *)environ); + length= strlen(name); + environ= q;} + else { /* Find old slot */ + length= strlen(name); + for (q= environ; *q != NULL; q++) + if (!strncmp(*q, name, length)) + break;}; + vl= strlen(value); + if (!p || (length+vl+1 > strlen(p))) + *q= p= (char *) malloc((unsigned)(length+vl+2)); + else + p= *q; + (void)strcpy(p, name); p+= length; + *p++= '='; + (void)strcpy(p, value); + return(value); +} diff --git a/lib/vroot/stat.cc b/lib/vroot/stat.cc new file mode 100644 index 0000000..4cd6a08 --- /dev/null +++ b/lib/vroot/stat.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1998 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int stat(const char *path, struct stat *buf); + +#include +#include + +static int stat_thunk(char *path) +{ + vroot_result= stat(path, vroot_args.stat.buffer); + return(vroot_result == 0); +} + +int stat_vroot(char *path, struct stat *buffer, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.stat.buffer= buffer; + translate_with_thunk(path, stat_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/truncate.cc b/lib/vroot/truncate.cc new file mode 100644 index 0000000..d9d118d --- /dev/null +++ b/lib/vroot/truncate.cc @@ -0,0 +1,45 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int truncate(const char *path, off_t length); + +#include +#include + +static int truncate_thunk(char *path) +{ + vroot_result= truncate(path, vroot_args.truncate.length); + return(vroot_result == 0); +} + +int truncate_vroot(char *path, int length, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.truncate.length= length; + translate_with_thunk(path, truncate_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/unlink.cc b/lib/vroot/unlink.cc new file mode 100644 index 0000000..51134d1 --- /dev/null +++ b/lib/vroot/unlink.cc @@ -0,0 +1,44 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include + +extern int unlink(const char *path); + +#include +#include + +static int unlink_thunk(char *path) +{ + vroot_result= unlink(path); + return(vroot_result == 0); +} + +int unlink_vroot(char *path, pathpt vroot_path, pathpt vroot_vroot) +{ + translate_with_thunk(path, unlink_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/utimes.cc b/lib/vroot/utimes.cc new file mode 100644 index 0000000..9cddab3 --- /dev/null +++ b/lib/vroot/utimes.cc @@ -0,0 +1,46 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 1993 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +extern int utimes(char *file, struct timeval *tvp); + +#include +#include + +static int utimes_thunk(char *path) +{ + vroot_result= utimes(path, vroot_args.utimes.time); + return(vroot_result == 0); +} + +int utimes_vroot(char *path, struct timeval *time, pathpt vroot_path, pathpt vroot_vroot) +{ + vroot_args.utimes.time= time; + translate_with_thunk(path, utimes_thunk, vroot_path, vroot_vroot, rw_read); + return(vroot_result); +} diff --git a/lib/vroot/vroot.cc b/lib/vroot/vroot.cc new file mode 100644 index 0000000..785a0bc --- /dev/null +++ b/lib/vroot/vroot.cc @@ -0,0 +1,338 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + + +#include +#include + +#include +#include + +#include +#include +#include + +typedef struct { + short init; + pathpt vector; + const char *env_var; +} vroot_patht; + +typedef struct { + vroot_patht vroot; + vroot_patht path; + char full_path[MAXPATHLEN+1]; + char *vroot_start; + char *path_start; + char *filename_start; + int scan_vroot_first; + int cpp_style_path; +} vroot_datat, *vroot_datapt; + +static vroot_datat vroot_data= { + { 0, NULL, "VIRTUAL_ROOT"}, + { 0, NULL, "PATH"}, + "", NULL, NULL, NULL, 0, 1}; + +void +add_dir_to_path(const char *path, register pathpt *pointer, register int position) +{ + register int size= 0; + register int length; + register char *name; + register pathcellpt p; + pathpt new_path; + + if (*pointer != NULL) { + for (p= &((*pointer)[0]); p->path != NULL; p++, size++); + if (position < 0) + position= size;} + else + if (position < 0) + position= 0; + if (position >= size) { + new_path= (pathpt)calloc((unsigned)(position+2), sizeof(pathcellt)); + if (*pointer != NULL) { + memcpy((char *)new_path,(char *)(*pointer), size*sizeof(pathcellt)); + free((char *)(*pointer));}; + *pointer= new_path;}; + length= strlen(path); + name= (char *)malloc((unsigned)(length+1)); + (void)strcpy(name, path); + if ((*pointer)[position].path != NULL) + free((*pointer)[position].path); + (*pointer)[position].path= name; + (*pointer)[position].length= length; +} + +pathpt +parse_path_string(register char *string, register int remove_slash) +{ + register char *p; + pathpt result= NULL; + + if (string != NULL) + for (; 1; string= p+1) { + if (p= strchr(string, ':')) *p= 0; + if ((remove_slash == 1) && !strcmp(string, "/")) + add_dir_to_path("", &result, -1); + else + add_dir_to_path(string, &result, -1); + if (p) *p= ':'; + else return(result);}; + return((pathpt)NULL); +} + +const char * +get_vroot_name(void) +{ + return(vroot_data.vroot.env_var); +} + +const char * +get_path_name(void) +{ + return(vroot_data.path.env_var); +} + +void +flush_path_cache(void) +{ + vroot_data.path.init= 0; +} + +void +flush_vroot_cache(void) +{ + vroot_data.vroot.init= 0; +} + +void +scan_path_first(void) +{ + vroot_data.scan_vroot_first= 0; +} + +void +scan_vroot_first(void) +{ + vroot_data.scan_vroot_first= 1; +} + +void +set_path_style(int style) +{ + vroot_data.cpp_style_path= style; +} + +char * +get_vroot_path(register char **vroot, register char **path, register char **filename) +{ + if (vroot != NULL) { + if ((*vroot= vroot_data.vroot_start) == NULL) + if ((*vroot= vroot_data.path_start) == NULL) + *vroot= vroot_data.filename_start;}; + if (path != NULL) { + if ((*path= vroot_data.path_start) == NULL) + *path= vroot_data.filename_start;}; + if (filename != NULL) + *filename= vroot_data.filename_start; + return(vroot_data.full_path); +} + +void +translate_with_thunk(register char *filename, int (*thunk) (char *), pathpt path_vector, pathpt vroot_vector, rwt rw) +{ + register pathcellt *vp; + pathcellt *pp; + register pathcellt *pp1; + register char *p; + int flags[256]; + +/* Setup path to use */ + if (rw == rw_write) + pp1= NULL; /* Do not use path when writing */ + else { + if (path_vector == VROOT_DEFAULT) { + if (!vroot_data.path.init) { + vroot_data.path.init= 1; + vroot_data.path.vector= parse_path_string(getenv(vroot_data.path.env_var), 0);}; + path_vector= vroot_data.path.vector;}; + pp1= path_vector == NULL ? NULL : &(path_vector)[0];}; + +/* Setup vroot to use */ + if (vroot_vector == VROOT_DEFAULT) { + if (!vroot_data.vroot.init) { + vroot_data.vroot.init= 1; + vroot_data.vroot.vector= parse_path_string(getenv(vroot_data.vroot.env_var), 1);}; + vroot_vector= vroot_data.vroot.vector;}; + vp= vroot_vector == NULL ? NULL : &(vroot_vector)[0]; + +/* Setup to remember pieces */ + vroot_data.vroot_start= NULL; + vroot_data.path_start= NULL; + vroot_data.filename_start= NULL; + + int flen = strlen(filename); + if(flen >= MAXPATHLEN) { + errno = ENAMETOOLONG; + return; + } + + switch ((vp ?1:0) + (pp1 ? 2:0)) { + case 0: /* No path. No vroot. */ + use_name: + (void)strcpy(vroot_data.full_path, filename); + vroot_data.filename_start= vroot_data.full_path; + (void)(*thunk)(vroot_data.full_path); + return; + case 1: /* No path. Vroot */ + if (filename[0] != '/') goto use_name; + for (; vp->path != NULL; vp++) { + if((1 + flen + vp->length) >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + p= vroot_data.full_path; + (void)strcpy(vroot_data.vroot_start= p, vp->path); + p+= vp->length; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;}; + (void)strcpy(vroot_data.full_path, filename); + return; + case 2: /* Path. No vroot. */ + if (vroot_data.cpp_style_path) { + if (filename[0] == '/') goto use_name; + } else { + if (strchr(filename, '/') != NULL) goto use_name; + }; + for (; pp1->path != NULL; pp1++) { + p= vroot_data.full_path; + if((1 + flen + pp1->length) >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + if (vroot_data.cpp_style_path) { + (void)strcpy(vroot_data.path_start= p, pp1->path); + p+= pp1->length; + *p++= '/'; + } else { + if (pp1->length != 0) { + (void)strcpy(vroot_data.path_start= p, + pp1->path); + p+= pp1->length; + *p++= '/'; + }; + }; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;}; + (void)strcpy(vroot_data.full_path, filename); + return; + case 3: { /* Path. Vroot. */ + int *rel_path, path_len= 1; + if (vroot_data.scan_vroot_first == 0) { + for (pp= pp1; pp->path != NULL; pp++) path_len++; + rel_path= flags; + for (path_len-= 2; path_len >= 0; path_len--) rel_path[path_len]= 0; + for (; vp->path != NULL; vp++) + for (pp= pp1, path_len= 0; pp->path != NULL; pp++, path_len++) { + int len = 0; + if (rel_path[path_len] == 1) continue; + if (pp->path[0] != '/') rel_path[path_len]= 1; + p= vroot_data.full_path; + if ((filename[0] == '/') || (pp->path[0] == '/')) { + if(vp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.vroot_start= p, vp->path); p+= vp->length; + len += vp->length; + }; + if (vroot_data.cpp_style_path) { + if (filename[0] != '/') { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + }; + } else { + if (strchr(filename, '/') == NULL) { + if (pp->length != 0) { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, + pp->path); + p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } + }; + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;};} + else { pathcellt *vp1= vp; + for (pp= pp1, path_len= 0; pp->path != NULL; pp++, path_len++) + for (vp= vp1; vp->path != NULL; vp++) { + int len = 0; + p= vroot_data.full_path; + if ((filename[0] == '/') || (pp->path[0] == '/')) { + if(vp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.vroot_start= p, vp->path); p+= vp->length; + len += vp->length; + } + if (vroot_data.cpp_style_path) { + if (filename[0] != '/') { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } else { + if (strchr(filename, '/') == NULL) { + if(1 + len + pp->length >= MAXPATHLEN) { + errno = ENAMETOOLONG; + continue; + } + (void)strcpy(vroot_data.path_start= p, pp->path); p+= pp->length; + *p++= '/'; + len += 1 + pp->length; + } + } + (void)strcpy(vroot_data.filename_start= p, filename); + if ((*thunk)(vroot_data.full_path)) return;};}; + (void)strcpy(vroot_data.full_path, filename); + return;};}; +}