pmake/ 42700 1750 1750 0 5431143543 10235 5ustar karlkarlpmake/lst.lib/ 40700 1750 1750 0 5431143544 11603 5ustar karlkarlpmake/lst.lib/Makefile.dist100600 1750 1750 4076 5431141712 14306 0ustar karlkarl# A Not so Simple Makefile # Define the ar command AR= ar ARFLAGS= rv # Define the Ranlib command RANLIB= ranlib # What OBJS are needed for pmake OBJS= lstAppend.o lstAtEnd.o lstAtFront.o lstClose.o lstConcat.o \ lstDatum.o lstDeQueue.o lstDestroy.o lstDupl.o lstEnQueue.o \ lstFind.o lstFindFrom.o lstFirst.o lstForEach.o \ lstForEachFrom.o lstInit.o lstInsert.o lstIsAtEnd.o \ lstIsEmpty.o lstLast.o lstMember.o lstNext.o lstOpen.o \ lstRemove.o lstReplace.o lstSucc.o all: lst.lib.a lst.lib.a: ${OBJS} ${AR} ${ARFLAGS} $@ ${OBJS} ${RANLIB} $@ clean: rm -f lst.lib.a ${OBJS} # From here down are the new dependencies lstAppend.o : lstAppend.c lstInt.h ../lst.h ../sprite.h lstAtEnd.o : lstAtEnd.c lstInt.h ../lst.h ../sprite.h lstAtFront.o : lstAtFront.c lstInt.h ../lst.h ../sprite.h lstClose.o : lstClose.c lstInt.h ../lst.h ../sprite.h lstConcat.o : lstConcat.c lstInt.h ../lst.h ../sprite.h lstDatum.o : lstDatum.c lstInt.h ../lst.h ../sprite.h lstDeQueue.o : lstDeQueue.c lstInt.h ../lst.h ../sprite.h lstDestroy.o : lstDestroy.c lstInt.h ../lst.h ../sprite.h lstDupl.o : lstDupl.c lstInt.h ../lst.h ../sprite.h lstEnQueue.o : lstEnQueue.c lstInt.h ../lst.h ../sprite.h lstFind.o : lstFind.c lstInt.h ../lst.h ../sprite.h lstFindFrom.o : lstFindFrom.c lstInt.h ../lst.h ../sprite.h lstFirst.o : lstFirst.c lstInt.h ../lst.h ../sprite.h lstForEach.o : lstForEach.c lstInt.h ../lst.h ../sprite.h lstForEachFrom.o : lstForEachFrom.c lstInt.h ../lst.h ../sprite.h lstInit.o : lstInit.c lstInt.h ../lst.h ../sprite.h lstInsert.o : lstInsert.c lstInt.h ../lst.h ../sprite.h lstIsAtEnd.o : lstIsAtEnd.c lstInt.h ../lst.h ../sprite.h lstIsEmpty.o : lstIsEmpty.c lstInt.h ../lst.h ../sprite.h lstLast.o : lstLast.c lstInt.h ../lst.h ../sprite.h lstMember.o : lstMember.c lstInt.h ../lst.h ../sprite.h lstNext.o : lstNext.c lstInt.h ../lst.h ../sprite.h lstOpen.o : lstOpen.c lstInt.h ../lst.h ../sprite.h lstRemove.o : lstRemove.c lstInt.h ../lst.h ../sprite.h lstReplace.o : lstReplace.c lstInt.h ../lst.h ../sprite.h lstSucc.o : lstSucc.c lstInt.h ../lst.h ../sprite.h pmake/lst.lib/lstAppend.c100600 1750 1750 7247 5431141712 14005 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstAppend.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstAppend.c -- * Add a new node with a new datum after an existing node */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Append -- * Create a new node and add it to the given list after the given node. * * Results: * SUCCESS if all went well. * * Side Effects: * A new ListNode is created and linked in to the List. The lastPtr * field of the List will be altered if ln is the last node in the * list. lastPtr and firstPtr will alter if the list was empty and * ln was NILLNODE. * *----------------------------------------------------------------------- */ ReturnStatus Lst_Append (l, ln, d) Lst l; /* affected list */ LstNode ln; /* node after which to append the datum */ ClientData d; /* said datum */ { register List list; register ListNode lNode; register ListNode nLNode; if (LstValid (l) && (ln == NILLNODE && LstIsEmpty (l))) { goto ok; } if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { return (FAILURE); } ok: list = (List)l; lNode = (ListNode)ln; PAlloc (nLNode, ListNode); nLNode->datum = d; nLNode->useCount = nLNode->flags = 0; if (lNode == NilListNode) { if (list->isCirc) { nLNode->nextPtr = nLNode->prevPtr = nLNode; } else { nLNode->nextPtr = nLNode->prevPtr = NilListNode; } list->firstPtr = list->lastPtr = nLNode; } else { nLNode->prevPtr = lNode; nLNode->nextPtr = lNode->nextPtr; lNode->nextPtr = nLNode; if (nLNode->nextPtr != NilListNode) { nLNode->nextPtr->prevPtr = nLNode; } if (lNode == list->lastPtr) { list->lastPtr = nLNode; } } return (SUCCESS); } pmake/lst.lib/lstAtEnd.c100600 1750 1750 5133 5431141712 13561 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstAtEnd.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstAtEnd.c -- * Add a node at the end of the list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_AtEnd -- * Add a node to the end of the given list * * Results: * SUCCESS if life is good. * * Side Effects: * A new ListNode is created and added to the list. * *----------------------------------------------------------------------- */ ReturnStatus Lst_AtEnd (l, d) Lst l; /* List to which to add the datum */ ClientData d; /* Datum to add */ { register LstNode end; end = Lst_Last (l); return (Lst_Append (l, end, d)); } pmake/lst.lib/lstAtFront.c100600 1750 1750 5202 5431141712 14140 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstAtFront.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstAtFront.c -- * Add a node at the front of the list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_AtFront -- * Place a piece of data at the front of a list * * Results: * SUCCESS or FAILURE * * Side Effects: * A new ListNode is created and stuck at the front of the list. * hence, firstPtr (and possible lastPtr) in the list are altered. * *----------------------------------------------------------------------- */ ReturnStatus Lst_AtFront (l, d) Lst l; ClientData d; { register LstNode front; front = Lst_First (l); return (Lst_Insert (l, front, d)); } pmake/lst.lib/lstClose.c100600 1750 1750 5544 5431141712 13641 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstClose.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstClose.c -- * Close a list for sequential access. * The sequential functions access the list in a slightly different way. * CurPtr points to their idea of the current node in the list and they * access the list based on it. Because the list is circular, Lst_Next * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be * used to determine when to stop. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Close -- * Close a list which was opened for sequential access. * * Results: * None. * * Side Effects: * The list is closed. * *----------------------------------------------------------------------- */ void Lst_Close (l) Lst l; /* The list to close */ { register List list = (List) l; if (LstValid(l) == TRUE) { list->isOpen = FALSE; list->atEnd = Unknown; } } pmake/lst.lib/lstConcat.c100600 1750 1750 15024 5431141713 14016 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstConcat.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * listConcat.c -- * Function to concatentate two lists. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Concat -- * Concatenate two lists. New elements are created to hold the data * elements, if specified, but the elements themselves are not copied. * If the elements should be duplicated to avoid confusion with another * list, the Lst_Duplicate function should be called first. * If LST_CONCLINK is specified, the second list is destroyed since * its pointers have been corrupted and the list is no longer useable. * * Results: * SUCCESS if all went well. FAILURE otherwise. * * Side Effects: * New elements are created and appended the the first list. *----------------------------------------------------------------------- */ ReturnStatus Lst_Concat (l1, l2, flags) Lst l1; /* The list to which l2 is to be appended */ Lst l2; /* The list to append to l1 */ int flags; /* LST_CONCNEW if LstNode's should be duplicated * LST_CONCLINK if should just be relinked */ { register ListNode ln; /* original LstNode */ register ListNode nln; /* new LstNode */ register ListNode last; /* the last element in the list. Keeps * bookkeeping until the end */ register List list1 = (List)l1; register List list2 = (List)l2; if (!LstValid (l1) || !LstValid (l2)) { return (FAILURE); } if (flags == LST_CONCLINK) { if (list2->firstPtr != NilListNode) { /* * We set the nextPtr of the * last element of list two to be NIL to make the loop easier and * so we don't need an extra case should the first list turn * out to be non-circular -- the final element will already point * to NIL space and the first element will be untouched if it * existed before and will also point to NIL space if it didn't. */ list2->lastPtr->nextPtr = NilListNode; /* * So long as the second list isn't empty, we just link the * first element of the second list to the last element of the * first list. If the first list isn't empty, we then link the * last element of the list to the first element of the second list * The last element of the second list, if it exists, then becomes * the last element of the first list. */ list2->firstPtr->prevPtr = list1->lastPtr; if (list1->lastPtr != NilListNode) { list1->lastPtr->nextPtr = list2->firstPtr; } list1->lastPtr = list2->lastPtr; } if (list1->isCirc && list1->firstPtr != NilListNode) { /* * If the first list is supposed to be circular and it is (now) * non-empty, we must make sure it's circular by linking the * first element to the last and vice versa */ list1->firstPtr->prevPtr = list1->lastPtr; list1->lastPtr->nextPtr = list1->firstPtr; } free ((Address)l2); } else if (list2->firstPtr != NilListNode) { /* * We set the nextPtr of the last element of list 2 to be nil to make * the loop less difficult. The loop simply goes through the entire * second list creating new LstNodes and filling in the nextPtr, and * prevPtr to fit into l1 and its datum field from the * datum field of the corresponding element in l2. The 'last' node * follows the last of the new nodes along until the entire l2 has * been appended. Only then does the bookkeeping catch up with the * changes. During the first iteration of the loop, if 'last' is nil, * the first list must have been empty so the newly-created node is * made the first node of the list. */ list2->lastPtr->nextPtr = NilListNode; for (last = list1->lastPtr, ln = list2->firstPtr; ln != NilListNode; ln = ln->nextPtr) { PAlloc (nln, ListNode); nln->datum = ln->datum; if (last != NilListNode) { last->nextPtr = nln; } else { list1->firstPtr = nln; } nln->prevPtr = last; nln->flags = nln->useCount = 0; last = nln; } /* * Finish bookkeeping. The last new element becomes the last element * of list one. */ list1->lastPtr = last; /* * The circularity of both list one and list two must be corrected * for -- list one because of the new nodes added to it; list two * because of the alteration of list2->lastPtr's nextPtr to ease the * above for loop. */ if (list1->isCirc) { list1->lastPtr->nextPtr = list1->firstPtr; list1->firstPtr->prevPtr = list1->lastPtr; } else { last->nextPtr = NilListNode; } if (list2->isCirc) { list2->lastPtr->nextPtr = list2->firstPtr; } } return (SUCCESS); } pmake/lst.lib/lstDatum.c100600 1750 1750 5032 5431141713 13637 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstDatum.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstDatum.c -- * Return the datum associated with a list node. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Datum -- * Return the datum stored in the given node. * * Results: * The datum or (ick!) NIL if the node is invalid. * * Side Effects: * None. * *----------------------------------------------------------------------- */ ClientData Lst_Datum (ln) LstNode ln; { if (ln != NILLNODE) { return (((ListNode)ln)->datum); } else { return ((ClientData) NIL); } } pmake/lst.lib/lstDeQueue.c100600 1750 1750 5467 5431141713 14136 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstDeQueue.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstDeQueue.c -- * Remove the node and return its datum from the head of the list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_DeQueue -- * Remove and return the datum at the head of the given list. * * Results: * The datum in the node at the head or (ick) NIL if the list * is empty. * * Side Effects: * The head node is removed from the list. * *----------------------------------------------------------------------- */ ClientData Lst_DeQueue (l) Lst l; { ClientData rd; register ListNode tln; tln = (ListNode) Lst_First (l); if (tln == NilListNode) { return ((ClientData) NIL); } rd = tln->datum; if (Lst_Remove (l, (LstNode)tln) == FAILURE) { return ((ClientData) NIL); } else { return (rd); } } pmake/lst.lib/lstDestroy.c100600 1750 1750 6272 5431141713 14225 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstDestroy.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstDestroy.c -- * Nuke a list and all its resources */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Destroy -- * Destroy a list and free all its resources. If the freeProc is * given, it is called with the datum from each node in turn before * the node is freed. * * Results: * None. * * Side Effects: * The given list is freed in its entirety. * *----------------------------------------------------------------------- */ void Lst_Destroy (l, freeProc) Lst l; register void (*freeProc)(); { register ListNode ln; register ListNode tln = NilListNode; register List list = (List)l; if (l == NILLST || ! l) { /* * Note the check for l == (Lst)0 to catch uninitialized static Lst's. * Gross, but useful. */ return; } if (freeProc) { for (ln = list->firstPtr; ln != NilListNode && tln != list->firstPtr; ln = tln) { tln = ln->nextPtr; (*freeProc) (ln->datum); free ((Address)ln); } } else { for (ln = list->firstPtr; ln != NilListNode && tln != list->firstPtr; ln = tln) { tln = ln->nextPtr; free ((Address)ln); } } free ((Address)l); } pmake/lst.lib/lstDupl.c100600 1750 1750 6405 5431141713 13476 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstDupl.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * listDupl.c -- * Duplicate a list. This includes duplicating the individual * elements. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Duplicate -- * Duplicate an entire list. If a function to copy a ClientData is * given, the individual client elements will be duplicated as well. * * Results: * The new Lst structure or NILLST if failure. * * Side Effects: * A new list is created. *----------------------------------------------------------------------- */ Lst Lst_Duplicate (l, copyProc) Lst l; /* the list to duplicate */ ClientData (*copyProc)(); /* A function to duplicate each ClientData */ { register Lst nl; register ListNode ln; register List list = (List)l; if (!LstValid (l)) { return (NILLST); } nl = Lst_Init (list->isCirc); if (nl == NILLST) { return (NILLST); } ln = list->firstPtr; while (ln != NilListNode) { if (copyProc != NOCOPY) { if (Lst_AtEnd (nl, (*copyProc) (ln->datum)) == FAILURE) { return (NILLST); } } else if (Lst_AtEnd (nl, ln->datum) == FAILURE) { return (NILLST); } if (list->isCirc && ln == list->lastPtr) { ln = NilListNode; } else { ln = ln->nextPtr; } } return (nl); } pmake/lst.lib/lstEnQueue.c100600 1750 1750 5262 5431141713 14141 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstEnQueue.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstEnQueue.c-- * Treat the list as a queue and place a datum at its end */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_EnQueue -- * Add the datum to the tail of the given list. * * Results: * SUCCESS or FAILURE as returned by Lst_Append. * * Side Effects: * the lastPtr field is altered all the time and the firstPtr field * will be altered if the list used to be empty. * *----------------------------------------------------------------------- */ ReturnStatus Lst_EnQueue (l, d) Lst l; ClientData d; { if (LstValid (l) == FALSE) { return (FAILURE); } return (Lst_Append (l, Lst_Last(l), d)); } pmake/lst.lib/lstFind.c100600 1750 1750 5040 5431141714 13445 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstFind.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstFind.c -- * Find a node on a list. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Find -- * Find a node on the given list using the given comparison function * and the given datum. * * Results: * The found node or NILLNODE if none matches. * * Side Effects: * None. * *----------------------------------------------------------------------- */ LstNode Lst_Find (l, d, cProc) Lst l; ClientData d; int (*cProc)(); { return (Lst_FindFrom (l, Lst_First(l), d, cProc)); } pmake/lst.lib/lstFindFrom.c100600 1750 1750 6127 5431141714 14300 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstFindFrom.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstFindFrom.c -- * Find a node on a list from a given starting point. Used by Lst_Find. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_FindFrom -- * Search for a node starting and ending with the given one on the * given list using the passed datum and comparison function to * determine when it has been found. * * Results: * The found node or NILLNODE * * Side Effects: * None. * *----------------------------------------------------------------------- */ LstNode Lst_FindFrom (l, ln, d, cProc) Lst l; register LstNode ln; register ClientData d; register int (*cProc)(); { register ListNode tln; Boolean found = FALSE; if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { return (NILLNODE); } tln = (ListNode)ln; do { if ((*cProc) (tln->datum, d) == 0) { found = TRUE; break; } else { tln = tln->nextPtr; } } while (tln != (ListNode)ln && tln != NilListNode); if (found) { return ((LstNode)tln); } else { return (NILLNODE); } } pmake/lst.lib/lstFirst.c100600 1750 1750 5022 5431141714 13654 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstFirst.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstFirst.c -- * Return the first node of a list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_First -- * Return the first node on the given list. * * Results: * The first node or NILLNODE if the list is empty. * * Side Effects: * None. * *----------------------------------------------------------------------- */ LstNode Lst_First (l) Lst l; { if (!LstValid (l) || LstIsEmpty (l)) { return (NILLNODE); } else { return ((LstNode)((List)l)->firstPtr); } } pmake/lst.lib/lstForEach.c100600 1750 1750 5251 5431141714 14100 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstForEach.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstForeach.c -- * Perform a given function on all elements of a list. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_ForEach -- * Apply the given function to each element of the given list. The * function should return 0 if Lst_ForEach should continue and non- * zero if it should abort. * * Results: * None. * * Side Effects: * Only those created by the passed-in function. * *----------------------------------------------------------------------- */ /*VARARGS2*/ void Lst_ForEach (l, proc, d) Lst l; register int (*proc)(); register ClientData d; { Lst_ForEachFrom(l, Lst_First(l), proc, d); } pmake/lst.lib/lstForEachFrom.c100600 1750 1750 7021 5431141714 14721 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstForEachFrom.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * lstForEachFrom.c -- * Perform a given function on all elements of a list starting from * a given point. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_ForEachFrom -- * Apply the given function to each element of the given list. The * function should return 0 if traversal should continue and non- * zero if it should abort. * * Results: * None. * * Side Effects: * Only those created by the passed-in function. * *----------------------------------------------------------------------- */ /*VARARGS2*/ void Lst_ForEachFrom (l, ln, proc, d) Lst l; LstNode ln; register int (*proc)(); register ClientData d; { register ListNode tln = (ListNode)ln; register List list = (List)l; register ListNode next; Boolean done; int result; if (!LstValid (list) || LstIsEmpty (list)) { return; } do { /* * Take care of having the current element deleted out from under * us. */ next = tln->nextPtr; tln->useCount++; result = (*proc) (tln->datum, d); tln->useCount--; /* * We're done with the traversal if * - nothing's been added after the current node and * - the next node to examine is the first in the queue or * doesn't exist. */ done = (next == tln->nextPtr && (next == NilListNode || next == list->firstPtr)); next = tln->nextPtr; if (tln->flags & LN_DELETED) { free((char *)tln); } tln = next; } while (!result && !LstIsEmpty(list) && !done); } pmake/lst.lib/lstInit.c100600 1750 1750 5250 5431141714 13473 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstInit.c 5.4 (Berkeley) 12/28/90"; #endif /* not lint */ /*- * init.c -- * Initialize a new linked list. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Init -- * Create and initialize a new list. * * Results: * The created list. * * Side Effects: * A list is created, what else? * *----------------------------------------------------------------------- */ Lst Lst_Init(circ) Boolean circ; /* TRUE if the list should be made circular */ { register List nList; PAlloc (nList, List); nList->firstPtr = NilListNode; nList->lastPtr = NilListNode; nList->isOpen = FALSE; nList->isCirc = circ; nList->atEnd = Unknown; return ((Lst)nList); } pmake/lst.lib/lstInsert.c100600 1750 1750 7117 5431141715 14041 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstInsert.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstInsert.c -- * Insert a new datum before an old one */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Insert -- * Insert a new node with the given piece of data before the given * node in the given list. * * Results: * SUCCESS or FAILURE. * * Side Effects: * the firstPtr field will be changed if ln is the first node in the * list. * *----------------------------------------------------------------------- */ ReturnStatus Lst_Insert (l, ln, d) Lst l; /* list to manipulate */ LstNode ln; /* node before which to insert d */ ClientData d; /* datum to be inserted */ { register ListNode nLNode; /* new lnode for d */ register ListNode lNode = (ListNode)ln; register List list = (List)l; /* * check validity of arguments */ if (LstValid (l) && (LstIsEmpty (l) && ln == NILLNODE)) goto ok; if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { return (FAILURE); } ok: PAlloc (nLNode, ListNode); nLNode->datum = d; nLNode->useCount = nLNode->flags = 0; if (ln == NILLNODE) { if (list->isCirc) { nLNode->prevPtr = nLNode->nextPtr = nLNode; } else { nLNode->prevPtr = nLNode->nextPtr = NilListNode; } list->firstPtr = list->lastPtr = nLNode; } else { nLNode->prevPtr = lNode->prevPtr; nLNode->nextPtr = lNode; if (nLNode->prevPtr != NilListNode) { nLNode->prevPtr->nextPtr = nLNode; } lNode->prevPtr = nLNode; if (lNode == list->firstPtr) { list->firstPtr = nLNode; } } return (SUCCESS); } pmake/lst.lib/lstInt.h100600 1750 1750 7415 5431141715 13335 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)lstInt.h 5.4 (Berkeley) 12/28/90 */ /*- * lstInt.h -- * Internals for the list library */ #ifndef _LSTINT_H_ #define _LSTINT_H_ #include "lst.h" typedef struct ListNode { struct ListNode *prevPtr; /* previous element in list */ struct ListNode *nextPtr; /* next in list */ short useCount:8, /* Count of functions using the node. * node may not be deleted until count * goes to 0 */ flags:8; /* Node status flags */ ClientData datum; /* datum associated with this element */ } *ListNode; /* * Flags required for synchronization */ #define LN_DELETED 0x0001 /* List node should be removed when done */ #define NilListNode ((ListNode)-1) typedef enum { Head, Middle, Tail, Unknown } Where; typedef struct { ListNode firstPtr; /* first node in list */ ListNode lastPtr; /* last node in list */ Boolean isCirc; /* true if the list should be considered * circular */ /* * fields for sequential access */ Where atEnd; /* Where in the list the last access was */ Boolean isOpen; /* true if list has been Lst_Open'ed */ ListNode curPtr; /* current node, if open. NilListNode if * *just* opened */ ListNode prevPtr; /* Previous node, if open. Used by * Lst_Remove */ } *List; #define NilList ((List)-1) /* * PAlloc (var, ptype) -- * Allocate a pointer-typedef structure 'ptype' into the variable 'var' */ #define PAlloc(var,ptype) var = (ptype) malloc (sizeof (*var)) /* * LstValid (l) -- * Return TRUE if the list l is valid */ #define LstValid(l) (((Lst)l == NILLST) ? FALSE : TRUE) /* * LstNodeValid (ln, l) -- * Return TRUE if the LstNode ln is valid with respect to l */ #define LstNodeValid(ln, l) ((((LstNode)ln) == NILLNODE) ? FALSE : TRUE) /* * LstIsEmpty (l) -- * TRUE if the list l is empty. */ #define LstIsEmpty(l) (((List)l)->firstPtr == NilListNode) #endif _LSTINT_H_ pmake/lst.lib/lstIsAtEnd.c100600 1750 1750 6234 5431141715 14063 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstIsAtEnd.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstIsAtEnd.c -- * Tell if the current node is at the end of the list. * The sequential functions access the list in a slightly different way. * CurPtr points to their idea of the current node in the list and they * access the list based on it. Because the list is circular, Lst_Next * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be * used to determine when to stop. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_IsAtEnd -- * Return true if have reached the end of the given list. * * Results: * TRUE if at the end of the list (this includes the list not being * open or being invalid) or FALSE if not. We return TRUE if the list * is invalid or unopend so as to cause the caller to exit its loop * asap, the assumption being that the loop is of the form * while (!Lst_IsAtEnd (l)) { * ... * } * * Side Effects: * None. * *----------------------------------------------------------------------- */ Boolean Lst_IsAtEnd (l) Lst l; { register List list = (List) l; return (!LstValid (l) || !list->isOpen || (list->atEnd == Head) || (list->atEnd == Tail)); } pmake/lst.lib/lstIsEmpty.c100600 1750 1750 5074 5431141715 14167 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstIsEmpty.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstIsEmpty.c -- * A single function to decide if a list is empty */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_IsEmpty -- * Return TRUE if the given list is empty. * * Results: * TRUE if the list is empty, FALSE otherwise. * * Side Effects: * None. * * A list is considered empty if its firstPtr == NilListNode (or if * the list itself is NILLIST). *----------------------------------------------------------------------- */ Boolean Lst_IsEmpty (l) Lst l; { return ( ! LstValid (l) || LstIsEmpty(l)); } pmake/lst.lib/lstLast.c100600 1750 1750 5021 5431141715 13470 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstLast.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstLast.c -- * Return the last element of a list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Last -- * Return the last node on the list l. * * Results: * The requested node or NILLNODE if the list is empty. * * Side Effects: * None. * *----------------------------------------------------------------------- */ LstNode Lst_Last (l) Lst l; { if (!LstValid(l) || LstIsEmpty (l)) { return (NILLNODE); } else { return ((LstNode)((List)l)->lastPtr); } } pmake/lst.lib/lstMember.c100600 1750 1750 4723 5431141715 14004 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstMember.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * lstMember.c -- * See if a given datum is on a given list. */ #include "lstInt.h" LstNode Lst_Member (l, d) Lst l; ClientData d; { List list = (List) l; register ListNode lNode; lNode = list->firstPtr; if (lNode == NilListNode) { return NILLNODE; } do { if (lNode->datum == d) { return (LstNode)lNode; } lNode = lNode->nextPtr; } while (lNode != NilListNode && lNode != list->firstPtr); return NILLNODE; } pmake/lst.lib/lstNext.c100600 1750 1750 7421 5431141715 13511 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstNext.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstNext.c -- * Return the next node for a list. * The sequential functions access the list in a slightly different way. * CurPtr points to their idea of the current node in the list and they * access the list based on it. Because the list is circular, Lst_Next * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be * used to determine when to stop. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Next -- * Return the next node for the given list. * * Results: * The next node or NILLNODE if the list has yet to be opened. Also * if the list is non-circular and the end has been reached, NILLNODE * is returned. * * Side Effects: * the curPtr field is updated. * *----------------------------------------------------------------------- */ LstNode Lst_Next (l) Lst l; { register ListNode tln; register List list = (List)l; if ((LstValid (l) == FALSE) || (list->isOpen == FALSE)) { return (NILLNODE); } list->prevPtr = list->curPtr; if (list->curPtr == NilListNode) { if (list->atEnd == Unknown) { /* * If we're just starting out, atEnd will be Unknown. * Then we want to start this thing off in the right * direction -- at the start with atEnd being Middle. */ list->curPtr = tln = list->firstPtr; list->atEnd = Middle; } else { tln = NilListNode; list->atEnd = Tail; } } else { tln = list->curPtr->nextPtr; list->curPtr = tln; if (tln == list->firstPtr || tln == NilListNode) { /* * If back at the front, then we've hit the end... */ list->atEnd = Tail; } else { /* * Reset to Middle if gone past first. */ list->atEnd = Middle; } } return ((LstNode)tln); } pmake/lst.lib/lstOpen.c100600 1750 1750 6144 5431141716 13476 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstOpen.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstOpen.c -- * Open a list for sequential access. The sequential functions access the * list in a slightly different way. CurPtr points to their idea of the * current node in the list and they access the list based on it. * If the list is circular, Lst_Next and Lst_Prev will go around * the list forever. Lst_IsAtEnd must be used to determine when to stop. */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Open -- * Open a list for sequential access. A list can still be searched, * etc., without confusing these functions. * * Results: * SUCCESS or FAILURE. * * Side Effects: * isOpen is set TRUE and curPtr is set to NilListNode so the * other sequential functions no it was just opened and can choose * the first element accessed based on this. * *----------------------------------------------------------------------- */ ReturnStatus Lst_Open (l) register Lst l; { if (LstValid (l) == FALSE) { return (FAILURE); } ((List) l)->isOpen = TRUE; ((List) l)->atEnd = LstIsEmpty (l) ? Head : Unknown; ((List) l)->curPtr = NilListNode; return (SUCCESS); } pmake/lst.lib/lstRemove.c100600 1750 1750 10327 5431141716 14050 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstRemove.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstRemove.c -- * Remove an element from a list */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Remove -- * Remove the given node from the given list. * * Results: * SUCCESS or FAILURE. * * Side Effects: * The list's firstPtr will be set to NilListNode if ln is the last * node on the list. firsPtr and lastPtr will be altered if ln is * either the first or last node, respectively, on the list. * *----------------------------------------------------------------------- */ ReturnStatus Lst_Remove (l, ln) Lst l; LstNode ln; { register List list = (List) l; register ListNode lNode = (ListNode) ln; if (!LstValid (l) || !LstNodeValid (ln, l)) { return (FAILURE); } /* * unlink it from the list */ if (lNode->nextPtr != NilListNode) { lNode->nextPtr->prevPtr = lNode->prevPtr; } if (lNode->prevPtr != NilListNode) { lNode->prevPtr->nextPtr = lNode->nextPtr; } /* * if either the firstPtr or lastPtr of the list point to this node, * adjust them accordingly */ if (list->firstPtr == lNode) { list->firstPtr = lNode->nextPtr; } if (list->lastPtr == lNode) { list->lastPtr = lNode->prevPtr; } /* * Sequential access stuff. If the node we're removing is the current * node in the list, reset the current node to the previous one. If the * previous one was non-existent (prevPtr == NilListNode), we set the * end to be Unknown, since it is. */ if (list->isOpen && (list->curPtr == lNode)) { list->curPtr = list->prevPtr; if (list->curPtr == NilListNode) { list->atEnd = Unknown; } } /* * the only way firstPtr can still point to ln is if ln is the last * node on the list (the list is circular, so lNode->nextptr == lNode in * this case). The list is, therefore, empty and is marked as such */ if (list->firstPtr == lNode) { list->firstPtr = NilListNode; } /* * note that the datum is unmolested. The caller must free it as * necessary and as expected. */ if (lNode->useCount == 0) { free ((Address)ln); } else { lNode->flags |= LN_DELETED; } return (SUCCESS); } pmake/lst.lib/lstReplace.c100600 1750 1750 5134 5431141716 14146 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstReplace.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstReplace.c -- * Replace the datum in a node with a new datum */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Replace -- * Replace the datum in the given node with the new datum * * Results: * SUCCESS or FAILURE. * * Side Effects: * The datum field fo the node is altered. * *----------------------------------------------------------------------- */ ReturnStatus Lst_Replace (ln, d) register LstNode ln; ClientData d; { if (ln == NILLNODE) { return (FAILURE); } else { ((ListNode) ln)->datum = d; return (SUCCESS); } } pmake/lst.lib/lstSucc.c100600 1750 1750 5167 5431141716 13476 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)lstSucc.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * LstSucc.c -- * return the successor to a given node */ #include "lstInt.h" /*- *----------------------------------------------------------------------- * Lst_Succ -- * Return the sucessor to the given node on its list. * * Results: * The successor of the node, if it exists (note that on a circular * list, if the node is the only one in the list, it is its own * successor). * * Side Effects: * None. * *----------------------------------------------------------------------- */ LstNode Lst_Succ (ln) LstNode ln; { if (ln == NILLNODE) { return (NILLNODE); } else { return ((LstNode) ((ListNode) ln)->nextPtr); } } pmake/Makefile100600 1750 1750 1153 5431142054 11766 0ustar karlkarl# @(#)Makefile 5.2 (Berkeley) 12/28/90 PROG= make CFLAGS+=-I${.CURDIR} CC= cc SRCS= arch.c buf.c compat.c cond.c dir.c hash.c job.c main.c \ make.c parse.c str.c suff.c targ.c var.c SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \ lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \ lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \ lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \ lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c SRCS+= setenv.c getenv.c strerror.c .PATH: ${.CURDIR}/lst.lib ${.CURDIR}/support .include pmake/mk/ 40700 1750 1750 0 5431141421 10633 5ustar karlkarlpmake/mk/Makefile100600 1750 1750 431 5431141411 12347 0ustar karlkarl# @(#)Makefile 5.3 (Berkeley) 6/22/90 FILES= bsd.doc.mk bsd.lib.mk bsd.man.mk bsd.prog.mk bsd.subdir.mk sys.mk NOOBJ= noobj all clean cleandir depend lint tags: install: install -c -o ${BINOWN} -g ${BINOWN} -m 444 ${FILES} \ ${DESTDIR}${BINDIR}/mk .include pmake/mk/bsd.README100600 1750 1750 22276 5431141411 12411 0ustar karlkarl# @(#)bsd.README 5.1 (Berkeley) 5/11/90 This is the README file for the new make "include" files for the BSD source tree. The files are installed in /usr/share/mk, and are, by convention, named with the suffix ".mk". Each ".mk" file has a corresponding ".rd" file which is an explanation of the ".mk" file. Note, this file is not intended to replace reading through the .mk files for anything tricky. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= RANDOM THINGS WORTH KNOWING: The files are simply C-style #include files, and pretty much behave like you'd expect. The syntax is slightly different in that a single '.' is used instead of the hash mark, i.e. ".include ". One difference that will save you lots of debugging time is that inclusion of the file is normally done at the *end* of the Makefile. The reason for this is because .mk files often modify variables and behavior based on the values of variables set in the Makefile. To make this work, remember that the FIRST target found is the target that is used, i.e. if the Makefile has: a: echo a a: echo a number two the command "make a" will echo "a". To make things confusing, the SECOND variable assignment is the overriding one, i.e. if the Makefile has: a= foo a= bar b: echo ${a} the command "make b" will echo "bar". This is for compatibility with the way the V7 make behaved. It's fairly difficult to make the BSD .mk files work when you're building multiple programs in a single directory. It's a lot easier split up the programs than to deal with the problem. Most of the agony comes from making the "obj" directory stuff work right, not because we switch to a new version of make. So, don't get mad at us, figure out a better way to handle multiple architectures so we can quit using the symbolic link stuff. (Imake doesn't count.) The file .depend in the source directory is expected to contain dependencies for the source files. This file is read automatically by make after reading the Makefile. The variable DESTDIR works as before. It's not set anywhere but will change the tree where the file gets installed. The profiled libraries are no longer built in a different directory than the regular libraries. A new suffix, ".po", is used to denote a profiled object. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file has the default rules for all makes, in the BSD environment or otherwise. You probably don't want to touch this file. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file handles installing manual pages and their links. It has a single target: maninstall: Install the manual pages and their links. It sets/uses the following variables: MANDIR Base path for manual installation. MANGRP Manual group. MANOWN Manual owner. MANMODE Manual mode. MANSUBDIR Subdirectory under the manual page section, i.e. "/vax" or "/tahoe" for machine specific manual pages. MAN1 ... MAN8 The manual pages to be installed (use a .0 suffix). MLINKS List of manual page links (using a .1 - .8 suffix). The linked-to file must come first, the linked file second, and there may be multiple pairs. The files are soft-linked. The include file includes a file named "../Makefile.inc" if it exists. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file contains the owners, groups, etc. for both manual pages and binaries. It has no targets. It sets/uses the following variables: BINGRP Binary group. BINOWN Binary owner. BINMODE Binary mode. STRIP The flag passed to the install program to cause the binary to be stripped. This is to be used when building your own install script so that the entire system can be made stripped/not-stripped using a single nob. MANDIR Base path for manual installation. MANGRP Manual group. MANOWN Manual owner. MANMODE Manual mode. This file is generally useful when building your own Makefiles so that they use the same default owners etc. as the rest of the tree. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file handles building programs from one or more source files, along with their manual pages. It has a limited number of suffixes, consistent with the current needs of the BSD tree. It has seven targets: all: build the program and its manual page clean: remove the program, any object files and the files a.out, Errs, errs, mklog, and core. cleandir: remove all of the files removed by the target clean, as well as .depend, tags, and any manual pages. depend: make the dependencies for the source files, and store them in the file .depend. install: install the program and its manual pages; if the Makefile does not itself define the target install, the targets beforeinstall and afterinstall may also be used to cause actions immediately before and after the install target is executed. lint: run lint on the source files tags: create a tags file for the source files. It sets/uses the following variables: BINGRP Binary group. BINOWN Binary owner. BINMODE Binary mode. CLEANFILES Additional files to remove for the clean and cleandir targets. COPTS Additional flags to the compiler when creating C objects. HIDEGAME If HIDEGAME is defined, the binary is installed in /usr/games/hide, and a symbolic link is created to /usr/games/dm. LDADD Additional loader objects. Usually used for libraries. For example, to load with the compatibility and utility libraries, use: LDFILES=-lutil -lcompat LDFLAGS Additional loader flags. LINKS The list of binary links; should be full pathnames, the linked-to file coming first, followed by the linked file. The files are hard-linked. For example, to link /bin/test and /bin/[, use: LINKS= ${DESTDIR}/bin/test ${DESTDIR}/bin/[ MAN1...MAN8 Manual pages (should end in .0). If no MAN variable is defined, "MAN1=${PROG}.0" is assumed. PROG The name of the program to build. If not supplied, nothing is built. SRCS List of source files to build the program. If PROG is not defined, it's assumed to be ${PROG}.c. DPADD Additional dependencies for the program. Usually used for libraries. For example, to depend on the compatibility and utility libraries use: SRCLIB=${LIBCOMPAT} ${LIBUTIL} The following libraries are predefined for DPADD: LIBC /lib/libc.a LIBCOMPAT /usr/lib/libcompat.a LIBCURSES /usr/lib/libcurses.a LIBDBM /usr/lib/libdbm.a LIBDES /usr/lib/libdes.a LIBL /usr/lib/libl.a LIBKDB /usr/lib/libkdb.a LIBKRB /usr/lib/libkrb.a LIBM /usr/lib/libm.a LIBMP /usr/lib/libmp.a LIBPC /usr/lib/libpc.a LIBPLOT /usr/lib/libplot.a LIBRPC /usr/lib/sunrpc.a LIBTERM /usr/lib/libterm.a LIBUTIL /usr/lib/libutil.a SHAREDSTRINGS If defined, a new .c.o rule is used that results in shared strings, using xstr(1). STRIP The flag passed to the install program to cause the binary to be stripped. SUBDIR A list of subdirectories that should be built as well. Each of the targets will execute the same target in the subdirectories. The include file includes the file named "../Makefile.inc" if it exists, as well as the include file . Some simple examples: To build foo from foo.c with a manual page foo.1, use: PROG= foo .include To build foo from foo.c with a manual page foo.2, add the line: MAN2= foo.0 If foo does not have a manual page at all, add the line: NOMAN= noman If foo has multiple source files, add the line: SRCS= a.c b.c c.c d.c =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file contains the default targets for building subdirectories. It has the same seven targets as : all, clean, cleandir, depend, install, lint, and tags. For all of the directories listed in the variable SUBDIRS, the specified directory will be visited and the target made. There is also a default target which allows the command "make subdir" where subdir is any directory listed in the variable SUBDIRS. =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= The include file has support for building libraries. It has the same seven targets as : all, clean, cleandir, depend, install, lint, and tags. It has a limited number of suffixes, consistent with the current needs of the BSD tree. It sets/uses the following variables: LIBDIR Target directory for libraries. LINTLIBDIR Target directory for lint libraries. LIBGRP Library group. LIBOWN Library owner. LIBMODE Library mode. LDADD Additional loader objects. MAN1 ... MAN8 The manual pages to be installed (use a .0 suffix). SRCS List of source files to build the library. Suffix types .s, .c, and .f are supported. Note, .s files are preferred to .c files of the same name. (This is not the default for versions of make.) The include file includes the file named "../Makefile.inc" if it exists, as well as the include file . It has rules for building profiled objects; profiled libraries are built by default. Libraries are ranlib'd before installation. pmake/mk/bsd.doc.mk100600 1750 1750 1320 5431141411 12572 0ustar karlkarl# @(#)bsd.doc.mk 5.3 (Berkeley) 1/2/91 PRINTER=psc BIB?= bib EQN?= deqn -P${PRINTER} GREMLIN?= grn -P${PRINTER} GRIND?= vgrind -f INDXBIB?= indxbib PIC?= pic -P${PRINTER} REFER?= refer ROFF?= ditroff -t ${MACROS} ${PAGES} -P${PRINTER} SOELIM?= soelim TBL?= dtbl -P${PRINTER} .PATH: ${.CURDIR} .if !target(print) print: paper.${PRINTER} lpr -P${PRINTER} paper.${PRINTER} .endif clean cleandir: rm -f paper.* [eE]rrs mklog ${CLEANFILES} FILES?= ${SRCS} install: install -c -o ${BINOWN} -g ${BINGRP} -m 444 \ Makefile ${FILES} ${EXTRA} ${DESTDIR}${BINDIR}/${DIR} spell: ${SRCS} spell ${SRCS} | sort | comm -23 - spell.ok > paper.spell BINDIR?= /usr/share/doc BINGRP?= bin BINOWN?= bin BINMODE?= 444 pmake/mk/bsd.lib.mk100600 1750 1750 6652 5431141411 12610 0ustar karlkarl# @(#)bsd.lib.mk 5.26 (Berkeley) 5/2/91 .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif LIBDIR?= /usr/lib LINTLIBDIR?= /usr/libdata/lint LIBGRP?= bin LIBOWN?= bin LIBMODE?= 444 STRIP?= -s BINGRP?= bin BINOWN?= bin BINMODE?= 555 .MAIN: all # prefer .s to a .c, add .po, remove stuff not used in the BSD libraries .SUFFIXES: .SUFFIXES: .out .o .po .s .c .f .y .l .8 .7 .6 .5 .4 .3 .2 .1 .0 .8.0 .7.0 .6.0 .5.0 .4.0 .3.0 .2.0 .1.0: nroff -mandoc ${.IMPSRC} > ${.TARGET} .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} @${LD} -x -r ${.TARGET} @mv a.out ${.TARGET} .c.po: ${CC} -p ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} @${LD} -X -r ${.TARGET} @mv a.out ${.TARGET} .s.o: ${CPP} -E ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} | \ ${AS} -o ${.TARGET} @${LD} -x -r ${.TARGET} @mv a.out ${.TARGET} .s.po: ${CPP} -E -DPROF ${CFLAGS:M-[ID]*} ${AINC} ${.IMPSRC} | \ ${AS} -o ${.TARGET} @${LD} -X -r ${.TARGET} @mv a.out ${.TARGET} MANALL= ${MAN1} ${MAN2} ${MAN3} ${MAN4} ${MAN5} ${MAN6} ${MAN7} ${MAN8} .if !defined(NOPROFILE) _LIBS=lib${LIB}.a lib${LIB}_p.a .else _LIBS=lib${LIB}.a .endif all: ${_LIBS} ${MANALL}# llib-l${LIB}.ln OBJS+= ${SRCS:R:S/$/.o/g} lib${LIB}.a:: ${OBJS} @echo building standard ${LIB} library @rm -f lib${LIB}.a @${AR} cTq lib${LIB}.a `lorder ${OBJS} | tsort` ${LDADD} ranlib lib${LIB}.a POBJS+= ${OBJS:.o=.po} lib${LIB}_p.a:: ${POBJS} @echo building profiled ${LIB} library @rm -f lib${LIB}_p.a @${AR} cTq lib${LIB}_p.a `lorder ${POBJS} | tsort` ${LDADD} ranlib lib${LIB}_p.a llib-l${LIB}.ln: ${SRCS} ${LINT} -C${LIB} ${CFLAGS} ${.ALLSRC:M*.c} .if !target(clean) clean: rm -f a.out Errs errs mklog core ${CLEANFILES} ${OBJS} ${POBJS} \ profiled/*.o lib${LIB}.a lib${LIB}_p.a llib-l${LIB}.ln .endif .if !target(cleandir) cleandir: rm -f a.out Errs errs mklog core ${CLEANFILES} ${OBJS} ${POBJS} \ profiled/*.o lib${LIB}.a lib${LIB}_p.a llib-l${LIB}.ln rm -f ${MANALL} ${.CURDIR}/tags .depend .endif .if !target(depend) depend: .depend .depend: ${SRCS} mkdep ${CFLAGS:M-[ID]*} ${AINC} ${.ALLSRC} @(TMP=/tmp/_depend$$$$; \ sed -e 's/^\([^\.]*\).o:/\1.o \1.po:/' < .depend > $$TMP; \ mv $$TMP .depend) .endif .if !target(install) .if !target(beforeinstall) beforeinstall: .endif realinstall: beforeinstall ranlib lib${LIB}.a install -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} lib${LIB}.a \ ${DESTDIR}${LIBDIR} ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}.a ranlib lib${LIB}_p.a install -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ lib${LIB}_p.a ${DESTDIR}${LIBDIR} ${RANLIB} -t ${DESTDIR}${LIBDIR}/lib${LIB}_p.a # install -c -o ${LIBOWN} -g ${LIBGRP} -m ${LIBMODE} \ # llib-l${LIB}.ln ${DESTDIR}${LINTLIBDIR} .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; \ while test $$# -ge 2; do \ l=${DESTDIR}$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ echo $$t -\> $$l; \ rm -f $$t; \ ln $$l $$t; \ done; true .endif install: afterinstall afterinstall: realinstall maninstall .endif .if !target(lint) lint: .endif .if !target(tags) tags: ${SRCS} -cd ${.CURDIR}; ctags -f /dev/stdout ${.ALLSRC:M*.c} | \ sed "s;\${.CURDIR}/;;" > tags .endif .include .if !target(obj) .if defined(NOOBJ) obj: .else obj: @cd ${.CURDIR}; rm -rf obj; \ here=`pwd`; dest=/usr/obj/`echo $$here | sed 's,/usr/src/,,'`; \ echo "$$here -> $$dest"; ln -s $$dest obj; \ if test -d /usr/obj -a ! -d $$dest; then \ mkdir -p $$dest; \ else \ true; \ fi; .endif .endif pmake/mk/bsd.man.mk100600 1750 1750 3046 5431141411 12607 0ustar karlkarl# @(#)bsd.man.mk 5.2 (Berkeley) 5/11/90 .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif MANGRP?= bin MANOWN?= bin MANMODE?= 444 MANDIR?= /usr/share/man/cat MINSTALL= install -c -o ${MANOWN} -g ${MANGRP} -m ${MANMODE} maninstall: .if defined(MAN1) && !empty(MAN1) ${MINSTALL} ${MAN1} ${DESTDIR}${MANDIR}1${MANSUBDIR} .endif .if defined(MAN2) && !empty(MAN2) ${MINSTALL} ${MAN2} ${DESTDIR}${MANDIR}2${MANSUBDIR} .endif .if defined(MAN3) && !empty(MAN3) ${MINSTALL} ${MAN3} ${DESTDIR}${MANDIR}3${MANSUBDIR} .endif .if defined(MAN3F) && !empty(MAN3F) ${MINSTALL} ${MAN3F} ${DESTDIR}${MANDIR}3f${MANSUBDIR} .endif .if defined(MAN4) && !empty(MAN4) ${MINSTALL} ${MAN4} ${DESTDIR}${MANDIR}4${MANSUBDIR} .endif .if defined(MAN5) && !empty(MAN5) ${MINSTALL} ${MAN5} ${DESTDIR}${MANDIR}5${MANSUBDIR} .endif .if defined(MAN6) && !empty(MAN6) ${MINSTALL} ${MAN6} ${DESTDIR}${MANDIR}6${MANSUBDIR} .endif .if defined(MAN7) && !empty(MAN7) ${MINSTALL} ${MAN7} ${DESTDIR}${MANDIR}7${MANSUBDIR} .endif .if defined(MAN8) && !empty(MAN8) ${MINSTALL} ${MAN8} ${DESTDIR}${MANDIR}8${MANSUBDIR} .endif .if defined(MLINKS) && !empty(MLINKS) @set ${MLINKS}; \ while test $$# -ge 2; do \ name=$$1; \ shift; \ dir=${DESTDIR}${MANDIR}`expr $$name : '[^\.]*\.\(.*\)'`; \ l=$${dir}${MANSUBDIR}/`expr $$name : '\([^\.]*\)'`.0; \ name=$$1; \ shift; \ dir=${DESTDIR}${MANDIR}`expr $$name : '[^\.]*\.\(.*\)'`; \ t=$${dir}${MANSUBDIR}/`expr $$name : '\([^\.]*\)'`.0; \ echo $$t -\> $$l; \ rm -f $$t; \ ln $$l $$t; \ done; true .endif pmake/mk/bsd.prog.mk100600 1750 1750 7230 5431141411 13002 0ustar karlkarl# @(#)bsd.prog.mk 5.26 (Berkeley) 6/25/91 .if exists(${.CURDIR}/../Makefile.inc) .include "${.CURDIR}/../Makefile.inc" .endif .SUFFIXES: .out .o .c .y .l .s .8 .7 .6 .5 .4 .3 .2 .1 .0 .8.0 .7.0 .6.0 .5.0 .4.0 .3.0 .2.0 .1.0: nroff -mandoc ${.IMPSRC} > ${.TARGET} CFLAGS+=${COPTS} STRIP?= -s BINGRP?= bin BINOWN?= bin BINMODE?= 555 LIBC?= /usr/lib/libc.a LIBCOMPAT?= /usr/lib/libcompat.a LIBCURSES?= /usr/lib/libcurses.a LIBDBM?= /usr/lib/libdbm.a LIBDES?= /usr/lib/libdes.a LIBL?= /usr/lib/libl.a LIBKDB?= /usr/lib/libkdb.a LIBKRB?= /usr/lib/libkrb.a LIBM?= /usr/lib/libm.a LIBMP?= /usr/lib/libmp.a LIBPC?= /usr/lib/libpc.a LIBPLOT?= /usr/lib/libplot.a LIBRESOLV?= /usr/lib/libresolv.a LIBRPC?= /usr/lib/sunrpc.a LIBTERM?= /usr/lib/libterm.a LIBUTIL?= /usr/lib/libutil.a .if defined(SHAREDSTRINGS) CLEANFILES+=strings .c.o: ${CC} -E ${CFLAGS} ${.IMPSRC} | xstr -c - @${CC} ${CFLAGS} -c x.c -o ${.TARGET} @rm -f x.c .endif .if defined(PROG) .if defined(SRCS) OBJS+= ${SRCS:R:S/$/.o/g} ${PROG}: ${OBJS} ${LIBC} ${DPADD} ${CC} ${LDFLAGS} -o ${.TARGET} ${OBJS} ${LDADD} .else defined(PROG) SRCS= ${PROG}.c ${PROG}: ${SRCS} ${LIBC} ${DPADD} ${CC} ${CFLAGS} -o ${.TARGET} ${.CURDIR}/${SRCS} ${LDADD} MKDEP= -p .endif .if !defined(MAN1) && !defined(MAN2) && !defined(MAN3) && \ !defined(MAN4) && !defined(MAN5) && !defined(MAN6) && \ !defined(MAN7) && !defined(MAN8) && !defined(NOMAN) MAN1= ${PROG}.0 .endif .endif MANALL= ${MAN1} ${MAN2} ${MAN3} ${MAN4} ${MAN5} ${MAN6} ${MAN7} ${MAN8} _PROGSUBDIR: .USE .if defined(SUBDIR) && !empty(SUBDIR) @for entry in ${SUBDIR}; do \ (echo "===> $$entry"; \ if test -d ${.CURDIR}/$${entry}.${MACHINE}; then \ cd ${.CURDIR}/$${entry}.${MACHINE}; \ else \ cd ${.CURDIR}/$${entry}; \ fi; \ ${MAKE} ${.TARGET:S/realinstall/install/:S/.depend/depend/}); \ done .endif .MAIN: all all: ${PROG} ${MANALL} _PROGSUBDIR .if !target(clean) clean: _PROGSUBDIR rm -f a.out [Ee]rrs mklog core ${PROG} ${OBJS} ${CLEANFILES} .endif .if !target(cleandir) cleandir: _PROGSUBDIR rm -f a.out [Ee]rrs mklog core ${PROG} ${OBJS} ${CLEANFILES} rm -f .depend ${MANALL} .endif # some of the rules involve .h sources, so remove them from mkdep line .if !target(depend) depend: .depend _PROGSUBDIR .depend: ${SRCS} .if defined(PROG) mkdep ${MKDEP} ${CFLAGS:M-[ID]*} ${.ALLSRC:M*.c} .endif .endif .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif realinstall: _PROGSUBDIR .if defined(PROG) install ${STRIP} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \ ${PROG} ${DESTDIR}${BINDIR} .endif .if defined(HIDEGAME) (cd ${DESTDIR}/usr/games; rm -f ${PROG}; ln -s dm ${PROG}; \ chown games.bin ${PROG}) .endif .if defined(LINKS) && !empty(LINKS) @set ${LINKS}; \ while test $$# -ge 2; do \ l=${DESTDIR}$$1; \ shift; \ t=${DESTDIR}$$1; \ shift; \ echo $$t -\> $$l; \ rm -f $$t; \ ln $$l $$t; \ done; true .endif install: maninstall maninstall: afterinstall afterinstall: realinstall realinstall: beforeinstall .endif .if !target(lint) lint: ${SRCS} _PROGSUBDIR .if defined(PROG) @${LINT} ${LINTFLAGS} ${CFLAGS} ${.ALLSRC} | more 2>&1 .endif .endif .if !target(obj) .if defined(NOOBJ) obj: _PROGSUBDIR .else obj: _PROGSUBDIR @cd ${.CURDIR}; rm -rf obj; \ here=`pwd`; dest=/usr/obj/`echo $$here | sed 's,/usr/src/,,'`; \ echo "$$here -> $$dest"; ln -s $$dest obj; \ if test -d /usr/obj -a ! -d $$dest; then \ mkdir -p $$dest; \ else \ true; \ fi; .endif .endif .if !target(tags) tags: ${SRCS} _PROGSUBDIR .if defined(PROG) -cd ${.CURDIR}; ctags -f /dev/stdout ${.ALLSRC} | \ sed "s;\${.CURDIR}/;;" > tags .endif .endif .if !defined(NOMAN) .include .endif pmake/mk/bsd.subdir.mk100600 1750 1750 2103 5431141412 13316 0ustar karlkarl# @(#)bsd.subdir.mk 5.9 (Berkeley) 2/1/91 .MAIN: all STRIP?= -s BINGRP?= bin BINOWN?= bin BINMODE?= 555 _SUBDIRUSE: .USE @for entry in ${SUBDIR}; do \ (if test -d ${.CURDIR}/$${entry}.${MACHINE}; then \ echo "===> $${entry}.${MACHINE}"; \ cd ${.CURDIR}/$${entry}.${MACHINE}; \ else \ echo "===> $$entry"; \ cd ${.CURDIR}/$${entry}; \ fi; \ ${MAKE} ${.TARGET:realinstall=install}); \ done ${SUBDIR}:: @if test -d ${.TARGET}.${MACHINE}; then \ cd ${.CURDIR}/${.TARGET}.${MACHINE}; \ else \ cd ${.CURDIR}/${.TARGET}; \ fi; \ ${MAKE} all .if !target(all) all: _SUBDIRUSE .endif .if !target(clean) clean: _SUBDIRUSE .endif .if !target(cleandir) cleandir: _SUBDIRUSE .endif .if !target(depend) depend: _SUBDIRUSE .endif .if !target(install) .if !target(beforeinstall) beforeinstall: .endif .if !target(afterinstall) afterinstall: .endif install: afterinstall afterinstall: realinstall realinstall: beforeinstall _SUBDIRUSE .endif .if !target(lint) lint: _SUBDIRUSE .endif .if !target(obj) obj: _SUBDIRUSE .endif .if !target(tags) tags: _SUBDIRUSE .endif pmake/mk/sys.mk100600 1750 1750 2456 5431141412 12110 0ustar karlkarl# @(#)sys.mk 5.11 (Berkeley) 3/13/91 unix= We run UNIX. .SUFFIXES: .out .a .ln .o .c .F .f .e .r .y .l .s .cl .p .h .LIBS: .a AR= ar ARFLAGS= rl RANLIB= ranlib AS= as AFLAGS= CC= cc CFLAGS= -O CPP= cpp FC= f77 FFLAGS= -O EFLAGS= LEX= lex LFLAGS= LD= ld LDFLAGS= LINT= lint LINTFLAGS= -chapbx MAKE= make PC= pc PFLAGS= RC= f77 RFLAGS= SHELL= sh YACC= yacc YFLAGS=-d .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} .p.o: ${PC} ${PFLAGS} -c ${.IMPSRC} .e.o .r.o .F.o .f.o: ${FC} ${RFLAGS} ${EFLAGS} ${FFLAGS} -c ${.IMPSRC} .s.o: ${AS} ${AFLAGS} -o ${.TARGET} ${.IMPSRC} .y.o: ${YACC} ${YFLAGS} ${.IMPSRC} ${CC} ${CFLAGS} -c y.tab.c -o ${.TARGET} rm -f y.tab.c .l.o: ${LEX} ${LFLAGS} ${.IMPSRC} ${CC} ${CFLAGS} -c lex.yy.c -o ${.TARGET} rm -f lex.yy.c .y.c: ${YACC} ${YFLAGS} ${.IMPSRC} mv y.tab.c ${.TARGET} .l.c: ${LEX} ${LFLAGS} ${.IMPSRC} mv lex.yy.c ${.TARGET} .s.out .c.out .o.out: ${CC} ${CFLAGS} ${.IMPSRC} ${LDLIBS} -o ${.TARGET} .f.out .F.out .r.out .e.out: ${FC} ${EFLAGS} ${RFLAGS} ${FFLAGS} ${.IMPSRC} \ ${LDLIBS} -o ${.TARGET} rm -f ${.PREFIX}.o .y.out: ${YACC} ${YFLAGS} ${.IMPSRC} ${CC} ${CFLAGS} y.tab.c ${LDLIBS} -ly -o ${.TARGET} rm -f y.tab.c .l.out: ${LEX} ${LFLAGS} ${.IMPSRC} ${CC} ${CFLAGS} lex.yy.c ${LDLIBS} -ll -o ${.TARGET} rm -f lex.yy.c pmake/support/ 40700 1750 1750 0 5431143546 11752 5ustar karlkarlpmake/support/Makefile.dist100600 1750 1750 624 5431141762 14433 0ustar karlkarl# A Not so Simple Makefile # Define the ar command AR= ar ARFLAGS= rv # Define the Ranlib command RANLIB= ranlib # What OBJS are needed for pmake OBJS= setenv.o getenv.o strerror.o all: support.a support.a: ${OBJS} ${AR} ${ARFLAGS} $@ ${OBJS} ${RANLIB} $@ clean: rm -f support.a ${OBJS} # From here down are the new dependencies setenv.o : setenv.c getenv.o : getenv.c strerror.o : strerror.c pmake/support/getenv.c100600 1750 1750 5536 5431141762 13514 0ustar karlkarl/* * Copyright (c) 1987 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef linux #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)getenv.c 5.8 (Berkeley) 2/23/91"; #endif /* LIBC_SCCS and not lint */ #include #include #include /* * getenv -- * Returns ptr to value associated with name, if any, else NULL. */ char * getenv(name) const char *name; { int offset; char *_findenv(); return(_findenv(name, &offset)); } /* * _findenv -- * Returns pointer to value associated with name, if any, else NULL. * Sets offset to be the offset of the name/value combination in the * environmental array, for use by setenv(3) and unsetenv(3). * Explicitly removes '=' in argument name. * * This routine *should* be a static; don't use it. */ char * _findenv(name, offset) register char *name; int *offset; { extern char **environ; register int len; register char **P, *C; for (C = name, len = 0; *C && *C != '='; ++C, ++len); for (P = environ; *P; ++P) if (!strncmp(*P, name, len)) if (*(C = *P + len) == '=') { *offset = P - environ; return(++C); } return(NULL); } #endif pmake/support/setenv.c100600 1750 1750 7505 5431141763 13527 0ustar karlkarl/* * Copyright (c) 1987 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef linux #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)setenv.c 5.6 (Berkeley) 6/4/91"; #endif /* LIBC_SCCS and not lint */ #include #include #include /* * setenv -- * Set the value of the environmental variable "name" to be * "value". If rewrite is set, replace any current value. */ setenv(name, value, rewrite) register const char *name; register const char *value; int rewrite; { extern char **environ; static int alloced; /* if allocated space before */ register char *C; int l_value, offset; char *_findenv(); if (*value == '=') /* no `=' in value */ ++value; l_value = strlen(value); if ((C = _findenv(name, &offset))) { /* find if already exists */ if (!rewrite) return (0); if (strlen(C) >= l_value) { /* old larger; copy over */ while (*C++ = *value++); return (0); } } else { /* create new slot */ register int cnt; register char **P; for (P = environ, cnt = 0; *P; ++P, ++cnt); if (alloced) { /* just increase size */ environ = (char **)realloc((char *)environ, (size_t)(sizeof(char *) * (cnt + 2))); if (!environ) return (-1); } else { /* get new space */ alloced = 1; /* copy old entries into it */ P = (char **)malloc((size_t)(sizeof(char *) * (cnt + 2))); if (!P) return (-1); bcopy(environ, P, cnt * sizeof(char *)); environ = P; } environ[cnt + 1] = NULL; offset = cnt; } for (C = (char *)name; *C && *C != '='; ++C); /* no `=' in name */ if (!(environ[offset] = /* name + `=' + value */ malloc((size_t)((int)(C - name) + l_value + 2)))) return (-1); for (C = environ[offset]; (*C = *name++) && *C != '='; ++C) ; for (*C++ = '='; *C++ = *value++; ) ; return (0); } /* * unsetenv(name) -- * Delete environmental variable "name". */ void unsetenv(name) const char *name; { extern char **environ; register char **P; int offset; while (_findenv(name, &offset)) /* if set multiple times */ for (P = &environ[offset];; ++P) if (!(*P = *(P + 1))) break; } #endif pmake/support/strerror.c100600 1750 1750 2673 5431141763 14106 0ustar karlkarl/* * Copyright (c) 1988 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted provided * that: (1) source distributions retain this entire copyright notice and * comment, and (2) distributions including binaries display the following * acknowledgement: ``This product includes software developed by the * University of California, Berkeley and its contributors'' in the * documentation or other materials provided with the distribution and in * all advertising materials mentioning features or use of this software. * Neither the name of the University nor the names of its contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef linux #if defined(LIBC_SCCS) && !defined(lint) static char sccsid[] = "@(#)strerror.c 5.4 (Berkeley) 6/24/90"; #endif /* LIBC_SCCS and not lint */ #include char * strerror(errnum) int errnum; { extern int sys_nerr; extern char *sys_errlist[]; static char ebuf[40]; /* 64-bit number + slop */ if ((unsigned int)errnum < sys_nerr) return(sys_errlist[errnum]); (void)sprintf(ebuf, "Unknown error: %d", errnum); return(ebuf); } #endif pmake/README100600 1750 1750 1274 5431142054 11212 0ustar karlkarlHi All, This is a version of BSD 4.4 make (pmake), which should compile and run under SUNOS 4.1.1 and Linux 0.99pl10 (and above) If you have problems with 'arch.c' then check out the definition for RANLIBMAG, you should be able to safely define this to be "".. If you are porting to a new architecture, then may I suggest you add an extra entry in 'main.c' to define MACHINE for your machine if your headers don't define it anyway.. Some of the ideas for how to port this came from Paul Vixie's version. The main bits are what is in the support directory. Any copyrights etc on all files are fully respected. Karl All porting and New Makefiles (C) 1993 Borg Enterprises - Karl London pmake/Makefile.dist100600 1750 1750 4023 5431142054 12727 0ustar karlkarl# A Not so Simple Makefile - Written by K London 3/8/93 # (C) Borg Enterprises 1993 # Which C compiler CC= gcc # Debugging or Optimisation O= -O # Define the CFLAGS # Use the line below if you have setenv() in your system library #CFLAGS= -I. $O -DHAVESETENV CFLAGS= -I. $O # Define the Libraries you need. LIBS= # Define the name of the lst.lib LSTLIB= lst.lib/lst.lib.a # Define the name of the support lib SUPLIB= support/support.a # What OBJS are needed for pmake OBJS= arch.o buf.o compat.o cond.o dir.o hash.o job.o make.o main.o parse.o \ str.o suff.o targ.o var.o pmake: ${OBJS} ${LSTLIB} ${SUPLIB} ${CC} -o pmake ${OBJS} ${LSTLIB} ${LIBS} lst.lib/lst.lib.a: cd lst.lib; ${MAKE} -f Makefile.dist CFLAGS='$(CFLAGS) -I..' all support/support.a: cd support; ${MAKE} -f Makefile.dist CFLAGS='$(CFLAGS) -I..' all all: pmake install: all cp pmake /usr/local/bin/pmake chmod a+x,go-rw /usr/local/bin/pmake mkdir /usr/share mkdir /usr/share/mk chmod a+rx /usr/share chmod a+rx /usr/share/mk cp mk/* /usr/share/mk chmod a+rx /usr/share/mk/* clean: rm -f ${OBJS} core pmake cd lst.lib; ${MAKE} -f Makefile.dist clean cd support; ${MAKE} -f Makefile.dist clean # From here down are the new dependencies arch.o : arch.c make.h sprite.h lst.h config.h nonints.h hash.h buf.o : buf.c sprite.h buf.h compat.o : compat.c make.h sprite.h lst.h config.h nonints.h cond.o : cond.c make.h sprite.h lst.h config.h nonints.h dir.o : dir.c make.h sprite.h lst.h config.h nonints.h hash.h hash.o : hash.c sprite.h hash.h job.o : job.c make.h sprite.h lst.h config.h nonints.h job.h pathnames.h make.o : make.c make.h sprite.h lst.h config.h nonints.h main.o : main.c make.h sprite.h lst.h config.h nonints.h pathnames.h parse.o : parse.c make.h sprite.h lst.h config.h nonints.h buf.h pathnames.h str.o : str.c make.h sprite.h lst.h config.h nonints.h suff.o : suff.c make.h sprite.h lst.h config.h nonints.h bit.h targ.o : targ.c make.h sprite.h lst.h config.h nonints.h hash.h var.o : var.c make.h sprite.h lst.h config.h nonints.h buf.h pmake/arch.c100600 1750 1750 72022 5431142301 11425 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)arch.c 5.7 (Berkeley) 12/28/90"; #endif /* not lint */ /*- * arch.c -- * Functions to manipulate libraries, archives and their members. * * Once again, cacheing/hashing comes into play in the manipulation * of archives. The first time an archive is referenced, all of its members' * headers are read and hashed and the archive closed again. All hashed * archives are kept on a list which is searched each time an archive member * is referenced. * * The interface to this module is: * Arch_ParseArchive Given an archive specification, return a list * of GNode's, one for each member in the spec. * FAILURE is returned if the specification is * invalid for some reason. * * Arch_Touch Alter the modification time of the archive * member described by the given node to be * the current time. * * Arch_TouchLib Update the modification time of the library * described by the given node. This is special * because it also updates the modification time * of the library's table of contents. * * Arch_MTime Find the modification time of a member of * an archive *in the archive*. The time is also * placed in the member's GNode. Returns the * modification time. * * Arch_MemTime Find the modification time of a member of * an archive. Called when the member doesn't * already exist. Looks in the archive for the * modification time. Returns the modification * time. * * Arch_FindLib Search for a library along a path. The * library name in the GNode should be in * -l format. * * Arch_LibOODate Special function to decide if a library node * is out-of-date. * * Arch_Init Initialize this module. */ #include #include #include #include #include #ifndef linux #include #endif #include #include "make.h" #include "hash.h" #ifndef RANLIBMAG #define RANLIBMAG "__.SYMDEF" #endif static Lst archives; /* Lst of archives we've already examined */ typedef struct Arch { char *name; /* Name of archive */ Hash_Table members; /* All the members of the archive described * by key/value pairs */ } Arch; static FILE *ArchFindMember(); /*- *----------------------------------------------------------------------- * Arch_ParseArchive -- * Parse the archive specification in the given line and find/create * the nodes for the specified archive members, placing their nodes * on the given list. * * Results: * SUCCESS if it was a valid specification. The linePtr is updated * to point to the first non-space after the archive spec. The * nodes for the members are placed on the given list. * * Side Effects: * Some nodes may be created. The given list is extended. * *----------------------------------------------------------------------- */ ReturnStatus Arch_ParseArchive (linePtr, nodeLst, ctxt) char **linePtr; /* Pointer to start of specification */ Lst nodeLst; /* Lst on which to place the nodes */ GNode *ctxt; /* Context in which to expand variables */ { register char *cp; /* Pointer into line */ GNode *gn; /* New node */ char *libName; /* Library-part of specification */ char *memName; /* Member-part of specification */ char nameBuf[BSIZE]; /* temporary place for node name */ char saveChar; /* Ending delimiter of member-name */ Boolean subLibName; /* TRUE if libName should have/had * variable substitution performed on it */ libName = *linePtr; subLibName = FALSE; for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; Boolean freeIt; char *result; result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); if (result == var_Error) { return(FAILURE); } else { subLibName = TRUE; } if (freeIt) { free(result); } cp += length-1; } } *cp++ = '\0'; if (subLibName) { libName = Var_Subst(libName, ctxt, TRUE); } while (1) { /* * First skip to the start of the member's name, mark that * place and skip to the end of it (either white-space or * a close paren). */ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ while (*cp != '\0' && *cp != ')' && isspace (*cp)) { cp++; } memName = cp; while (*cp != '\0' && *cp != ')' && !isspace (*cp)) { if (*cp == '$') { /* * Variable spec, so call the Var module to parse the puppy * so we can safely advance beyond it... */ int length; Boolean freeIt; char *result; result=Var_Parse(cp, ctxt, TRUE, &length, &freeIt); if (result == var_Error) { return(FAILURE); } else { doSubst = TRUE; } if (freeIt) { free(result); } cp += length; } else { cp++; } } /* * If the specification ends without a closing parenthesis, * chances are there's something wrong (like a missing backslash), * so it's better to return failure than allow such things to happen */ if (*cp == '\0') { printf("No closing parenthesis in archive specification\n"); return (FAILURE); } /* * If we didn't move anywhere, we must be done */ if (cp == memName) { break; } saveChar = *cp; *cp = '\0'; /* * XXX: This should be taken care of intelligently by * SuffExpandChildren, both for the archive and the member portions. */ /* * If member contains variables, try and substitute for them. * This will slow down archive specs with dynamic sources, of course, * since we'll be (non-)substituting them three times, but them's * the breaks -- we need to do this since SuffExpandChildren calls * us, otherwise we could assume the thing would be taken care of * later. */ if (doSubst) { char *buf; char *sacrifice; char *oldMemName = memName; memName = Var_Subst(memName, ctxt, TRUE); /* * Now form an archive spec and recurse to deal with nested * variables and multi-word variable values.... The results * are just placed at the end of the nodeLst we're returning. */ buf = sacrifice = emalloc(strlen(memName)+strlen(libName)+3); sprintf(buf, "%s(%s)", libName, memName); if (index(memName, '$') && strcmp(memName, oldMemName) == 0) { /* * Must contain dynamic sources, so we can't deal with it now. * Just create an ARCHV node for the thing and let * SuffExpandChildren handle it... */ gn = Targ_FindNode(buf, TARG_CREATE); if (gn == NILGNODE) { free(buf); return(FAILURE); } else { gn->type |= OP_ARCHV; (void)Lst_AtEnd(nodeLst, (ClientData)gn); } } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { /* * Error in nested call -- free buffer and return FAILURE * ourselves. */ free(buf); return(FAILURE); } /* * Free buffer and continue with our work. */ free(buf); } else if (Dir_HasWildcards(memName)) { Lst members = Lst_Init(FALSE); char *member; Dir_Expand(memName, dirSearchPath, members); while (!Lst_IsEmpty(members)) { member = (char *)Lst_DeQueue(members); sprintf(nameBuf, "%s(%s)", libName, member); free(member); gn = Targ_FindNode (nameBuf, TARG_CREATE); if (gn == NILGNODE) { return (FAILURE); } else { /* * We've found the node, but have to make sure the rest of * the world knows it's an archive member, without having * to constantly check for parentheses, so we type the * thing with the OP_ARCHV bit before we place it on the * end of the provided list. */ gn->type |= OP_ARCHV; (void) Lst_AtEnd (nodeLst, (ClientData)gn); } } Lst_Destroy(members, NOFREE); } else { sprintf(nameBuf, "%s(%s)", libName, memName); gn = Targ_FindNode (nameBuf, TARG_CREATE); if (gn == NILGNODE) { return (FAILURE); } else { /* * We've found the node, but have to make sure the rest of the * world knows it's an archive member, without having to * constantly check for parentheses, so we type the thing with * the OP_ARCHV bit before we place it on the end of the * provided list. */ gn->type |= OP_ARCHV; (void) Lst_AtEnd (nodeLst, (ClientData)gn); } } if (doSubst) { free(memName); } *cp = saveChar; } /* * If substituted libName, free it now, since we need it no longer. */ if (subLibName) { free(libName); } /* * We promised the pointer would be set up at the next non-space, so * we must advance cp there before setting *linePtr... (note that on * entrance to the loop, cp is guaranteed to point at a ')') */ do { cp++; } while (*cp != '\0' && isspace (*cp)); *linePtr = cp; return (SUCCESS); } /*- *----------------------------------------------------------------------- * ArchFindArchive -- * See if the given archive is the one we are looking for. Called * From ArchStatMember and ArchFindMember via Lst_Find. * * Results: * 0 if it is, non-zero if it isn't. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static int ArchFindArchive (ar, archName) Arch *ar; /* Current list element */ char *archName; /* Name we want */ { return (strcmp (archName, ar->name)); } /*- *----------------------------------------------------------------------- * ArchStatMember -- * Locate a member of an archive, given the path of the archive and * the path of the desired member. * * Results: * A pointer to the current struct ar_hdr structure for the member. Note * That no position is returned, so this is not useful for touching * archive members. This is mostly because we have no assurances that * The archive will remain constant after we read all the headers, so * there's not much point in remembering the position... * * Side Effects: * *----------------------------------------------------------------------- */ static struct ar_hdr * ArchStatMember (archive, member, hash) char *archive; /* Path to the archive */ char *member; /* Name of member. If it is a path, only the * last component is used. */ Boolean hash; /* TRUE if archive should be hashed if not * already so. */ { #define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1) FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ char *cp; /* Useful character pointer */ char magic[SARMAG]; int len; LstNode ln; /* Lst member containing archive descriptor */ Arch *ar; /* Archive descriptor */ Hash_Entry *he; /* Entry containing member's description */ struct ar_hdr arh; /* archive-member header for reading archive */ char memName[AR_MAX_NAME_LEN+1]; /* Current member name while hashing. The name is * truncated to AR_MAX_NAME_LEN bytes, but we need * room for the null byte... */ char copy[AR_MAX_NAME_LEN+1]; /* Holds copy of last path element from member, if * it has to be truncated, so we don't have to * figure it out again once the table is hashed. */ /* * Because of space constraints and similar things, files are archived * using their final path components, not the entire thing, so we need * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ cp = rindex (member, '/'); if (cp != (char *) NULL) { member = cp + 1; } len = strlen (member); if (len > AR_MAX_NAME_LEN) { len = AR_MAX_NAME_LEN; strncpy(copy, member, AR_MAX_NAME_LEN); copy[AR_MAX_NAME_LEN] = '\0'; member = copy; } ln = Lst_Find (archives, (ClientData) archive, ArchFindArchive); if (ln != NILLNODE) { ar = (Arch *) Lst_Datum (ln); he = Hash_FindEntry (&ar->members, member); if (he != (Hash_Entry *) NULL) { return ((struct ar_hdr *) Hash_GetValue (he)); } else { return ((struct ar_hdr *) NULL); } } if (!hash) { /* * Caller doesn't want the thing hashed, just use ArchFindMember * to read the header for the member out and close down the stream * again. Since the archive is not to be hashed, we assume there's * no need to allocate extra room for the header we're returning, * so just declare it static. */ static struct ar_hdr sarh; arch = ArchFindMember(archive, member, &sarh, "r"); if (arch == (FILE *)NULL) { return ((struct ar_hdr *)NULL); } else { fclose(arch); return (&sarh); } } /* * We don't have this archive on the list yet, so we want to find out * everything that's in it and cache it so we can get at it quickly. */ arch = fopen (archive, "r"); if (arch == (FILE *) NULL) { return ((struct ar_hdr *) NULL); } /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if ((fread (magic, SARMAG, 1, arch) != 1) || (strncmp (magic, ARMAG, SARMAG) != 0)) { fclose (arch); return ((struct ar_hdr *) NULL); } ar = (Arch *)emalloc (sizeof (Arch)); ar->name = strdup (archive); Hash_InitTable (&ar->members, -1); memName[AR_MAX_NAME_LEN] = '\0'; while (fread ((char *)&arh, sizeof (struct ar_hdr), 1, arch) == 1) { if (strncmp ( arh.ar_fmag, ARFMAG, sizeof (arh.ar_fmag)) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ fclose (arch); Hash_DeleteTable (&ar->members); free ((Address)ar); return ((struct ar_hdr *) NULL); } else { (void) strncpy (memName, arh.ar_name, sizeof(arh.ar_name)); for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { continue; } cp[1] = '\0'; he = Hash_CreateEntry (&ar->members, strdup (memName), (Boolean *)NULL); Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr))); bcopy ((Address)&arh, (Address)Hash_GetValue (he), sizeof (struct ar_hdr)); } /* * We need to advance the stream's pointer to the start of the * next header. Files are padded with newlines to an even-byte * boundary, so we need to extract the size of the file from the * 'size' field of the header and round it up during the seek. */ arh.ar_size[sizeof(arh.ar_size)-1] = '\0'; (void) sscanf (arh.ar_size, "%10d", &size); fseek (arch, (size + 1) & ~1, 1); } fclose (arch); (void) Lst_AtEnd (archives, (ClientData) ar); /* * Now that the archive has been read and cached, we can look into * the hash table to find the desired member's header. */ he = Hash_FindEntry (&ar->members, member); if (he != (Hash_Entry *) NULL) { return ((struct ar_hdr *) Hash_GetValue (he)); } else { return ((struct ar_hdr *) NULL); } } /*- *----------------------------------------------------------------------- * ArchFindMember -- * Locate a member of an archive, given the path of the archive and * the path of the desired member. If the archive is to be modified, * the mode should be "r+", if not, it should be "r". * * Results: * An FILE *, opened for reading and writing, positioned at the * start of the member's struct ar_hdr, or NULL if the member was * nonexistent. The current struct ar_hdr for member. * * Side Effects: * The passed struct ar_hdr structure is filled in. * *----------------------------------------------------------------------- */ static FILE * ArchFindMember (archive, member, arhPtr, mode) char *archive; /* Path to the archive */ char *member; /* Name of member. If it is a path, only the * last component is used. */ struct ar_hdr *arhPtr; /* Pointer to header structure to be filled in */ char *mode; /* The mode for opening the stream */ { FILE * arch; /* Stream to archive */ int size; /* Size of archive member */ char *cp; /* Useful character pointer */ char magic[SARMAG]; int len; arch = fopen (archive, mode); if (arch == (FILE *) NULL) { return ((FILE *) NULL); } /* * We use the ARMAG string to make sure this is an archive we * can handle... */ if ((fread (magic, SARMAG, 1, arch) != 1) || (strncmp (magic, ARMAG, SARMAG) != 0)) { fclose (arch); return ((FILE *) NULL); } /* * Because of space constraints and similar things, files are archived * using their final path components, not the entire thing, so we need * to point 'member' to the final component, if there is one, to make * the comparisons easier... */ cp = rindex (member, '/'); if (cp != (char *) NULL) { member = cp + 1; } len = strlen (member); if (len > sizeof (arhPtr->ar_name)) { len = sizeof (arhPtr->ar_name); } while (fread ((char *)arhPtr, sizeof (struct ar_hdr), 1, arch) == 1) { if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof (arhPtr->ar_fmag) ) != 0) { /* * The header is bogus, so the archive is bad * and there's no way we can recover... */ fclose (arch); return ((FILE *) NULL); } else if (strncmp (member, arhPtr->ar_name, len) == 0) { /* * If the member's name doesn't take up the entire 'name' field, * we have to be careful of matching prefixes. Names are space- * padded to the right, so if the character in 'name' at the end * of the matched string is anything but a space, this isn't the * member we sought. */ if (len != sizeof(arhPtr->ar_name) && arhPtr->ar_name[len] != ' '){ continue; } else { /* * To make life easier, we reposition the file at the start * of the header we just read before we return the stream. * In a more general situation, it might be better to leave * the file at the actual member, rather than its header, but * not here... */ fseek (arch, -sizeof(struct ar_hdr), 1); return (arch); } } else { /* * This isn't the member we're after, so we need to advance the * stream's pointer to the start of the next header. Files are * padded with newlines to an even-byte boundary, so we need to * extract the size of the file from the 'size' field of the * header and round it up during the seek. */ arhPtr->ar_size[sizeof(arhPtr->ar_size)-1] = '\0'; (void)sscanf (arhPtr->ar_size, "%10d", &size); fseek (arch, (size + 1) & ~1, 1); } } /* * We've looked everywhere, but the member is not to be found. Close the * archive and return NULL -- an error. */ fclose (arch); return ((FILE *) NULL); } /*- *----------------------------------------------------------------------- * Arch_Touch -- * Touch a member of an archive. * * Results: * The 'time' field of the member's header is updated. * * Side Effects: * The modification time of the entire archive is also changed. * For a library, this could necessitate the re-ranlib'ing of the * whole thing. * *----------------------------------------------------------------------- */ void Arch_Touch (gn) GNode *gn; /* Node of member to touch */ { FILE * arch; /* Stream open to archive, positioned properly */ struct ar_hdr arh; /* Current header describing member */ arch = ArchFindMember(Var_Value (ARCHIVE, gn), Var_Value (TARGET, gn), &arh, "r+"); sprintf(arh.ar_date, "%-12d", now); if (arch != (FILE *) NULL) { (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); fclose (arch); } } /*- *----------------------------------------------------------------------- * Arch_TouchLib -- * Given a node which represents a library, touch the thing, making * sure that the table of contents also is touched. * * Results: * None. * * Side Effects: * Both the modification time of the library and of the RANLIBMAG * member are set to 'now'. * *----------------------------------------------------------------------- */ void Arch_TouchLib (gn) GNode *gn; /* The node of the library to touch */ { FILE * arch; /* Stream open to archive */ struct ar_hdr arh; /* Header describing table of contents */ struct timeval times[2]; /* Times for utimes() call */ arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+"); sprintf(arh.ar_date, "%-12d", now); if (arch != (FILE *) NULL) { (void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch); fclose (arch); times[0].tv_sec = times[1].tv_sec = now; times[0].tv_usec = times[1].tv_usec = 0; utimes(gn->path, times); } } /*- *----------------------------------------------------------------------- * Arch_MTime -- * Return the modification time of a member of an archive. * * Results: * The modification time (seconds). * * Side Effects: * The mtime field of the given node is filled in with the value * returned by the function. * *----------------------------------------------------------------------- */ int Arch_MTime (gn) GNode *gn; /* Node describing archive member */ { struct ar_hdr *arhPtr; /* Header of desired member */ int modTime; /* Modification time as an integer */ arhPtr = ArchStatMember (Var_Value (ARCHIVE, gn), Var_Value (TARGET, gn), TRUE); if (arhPtr != (struct ar_hdr *) NULL) { (void)sscanf (arhPtr->ar_date, "%12d", &modTime); } else { modTime = 0; } gn->mtime = modTime; return (modTime); } /*- *----------------------------------------------------------------------- * Arch_MemMTime -- * Given a non-existent archive member's node, get its modification * time from its archived form, if it exists. * * Results: * The modification time. * * Side Effects: * The mtime field is filled in. * *----------------------------------------------------------------------- */ int Arch_MemMTime (gn) GNode *gn; { LstNode ln; GNode *pgn; char *nameStart, *nameEnd; if (Lst_Open (gn->parents) != SUCCESS) { gn->mtime = 0; return (0); } while ((ln = Lst_Next (gn->parents)) != NILLNODE) { pgn = (GNode *) Lst_Datum (ln); if (pgn->type & OP_ARCHV) { /* * If the parent is an archive specification and is being made * and its member's name matches the name of the node we were * given, record the modification time of the parent in the * child. We keep searching its parents in case some other * parent requires this child to exist... */ nameStart = index (pgn->name, '(') + 1; nameEnd = index (nameStart, ')'); if (pgn->make && strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { gn->mtime = Arch_MTime(pgn); } } else if (pgn->make) { /* * Something which isn't a library depends on the existence of * this target, so it needs to exist. */ gn->mtime = 0; break; } } Lst_Close (gn->parents); return (gn->mtime); } /*- *----------------------------------------------------------------------- * Arch_FindLib -- * Search for a library along the given search path. * * Results: * None. * * Side Effects: * The node's 'path' field is set to the found path (including the * actual file name, not -l...). If the system can handle the -L * flag when linking (or we cannot find the library), we assume that * the user has placed the .LIBRARIES variable in the final linking * command (or the linker will know where to find it) and set the * TARGET variable for this node to be the node's name. Otherwise, * we set the TARGET variable to be the full path of the library, * as returned by Dir_FindFile. * *----------------------------------------------------------------------- */ void Arch_FindLib (gn, path) GNode *gn; /* Node of library to find */ Lst path; /* Search path */ { char *libName; /* file name for archive */ libName = (char *)emalloc (strlen (gn->name) + 6 - 2); sprintf(libName, "lib%s.a", &gn->name[2]); gn->path = Dir_FindFile (libName, path); free (libName); #ifdef LIBRARIES Var_Set (TARGET, gn->name, gn); #else Var_Set (TARGET, gn->path == (char *) NULL ? gn->name : gn->path, gn); #endif LIBRARIES } /*- *----------------------------------------------------------------------- * Arch_LibOODate -- * Decide if a node with the OP_LIB attribute is out-of-date. Called * from Make_OODate to make its life easier. * * There are several ways for a library to be out-of-date that are * not available to ordinary files. In addition, there are ways * that are open to regular files that are not available to * libraries. A library that is only used as a source is never * considered out-of-date by itself. This does not preclude the * library's modification time from making its parent be out-of-date. * A library will be considered out-of-date for any of these reasons, * given that it is a target on a dependency line somewhere: * Its modification time is less than that of one of its * sources (gn->mtime < gn->cmtime). * Its modification time is greater than the time at which the * make began (i.e. it's been modified in the course * of the make, probably by archiving). * Its modification time doesn't agree with the modification * time of its RANLIBMAG member (i.e. its table of contents * is out-of-date). * * * Results: * TRUE if the library is out-of-date. FALSE otherwise. * * Side Effects: * The library will be hashed if it hasn't been already. * *----------------------------------------------------------------------- */ Boolean Arch_LibOODate (gn) GNode *gn; /* The library's graph node */ { Boolean oodate; if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { oodate = FALSE; } else if ((gn->mtime > now) || (gn->mtime < gn->cmtime)) { oodate = TRUE; } else { struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ int modTimeTOC; /* The table-of-contents's mod time */ arhPtr = ArchStatMember (gn->path, RANLIBMAG, FALSE); if (arhPtr != (struct ar_hdr *)NULL) { (void)sscanf (arhPtr->ar_date, "%12d", &modTimeTOC); if (DEBUG(ARCH) || DEBUG(MAKE)) { printf("%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); } oodate = (gn->mtime > modTimeTOC); } else { /* * A library w/o a table of contents is out-of-date */ if (DEBUG(ARCH) || DEBUG(MAKE)) { printf("No t.o.c...."); } oodate = TRUE; } } return (oodate); } /*- *----------------------------------------------------------------------- * Arch_Init -- * Initialize things for this module. * * Results: * None. * * Side Effects: * The 'archives' list is initialized. * *----------------------------------------------------------------------- */ void Arch_Init () { archives = Lst_Init (FALSE); } pmake/bit.h100600 1750 1750 7114 5431142055 11261 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)bit.h 5.3 (Berkeley) 6/1/90 */ /* * bit.h -- * * Definition of macros for setting and clearing bits in an array * of integers. * * It is assumed that "int" is 32 bits wide. */ #ifndef _BIT #define _BIT #include "sprite.h" #define BIT_NUM_BITS_PER_INT 32 #define BIT_NUM_BITS_PER_BYTE 8 #define Bit_NumInts(numBits) \ (((numBits)+BIT_NUM_BITS_PER_INT -1)/BIT_NUM_BITS_PER_INT) #define Bit_NumBytes(numBits) \ (Bit_NumInts(numBits) * sizeof(int)) #define Bit_Alloc(numBits, bitArrayPtr) \ bitArrayPtr = (int *)malloc((unsigned)Bit_NumBytes(numBits)); \ Bit_Zero((numBits), (bitArrayPtr)) #define Bit_Free(bitArrayPtr) \ free((char *)bitArrayPtr) #define Bit_Set(numBits, bitArrayPtr) \ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] |= \ (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) #define Bit_IsSet(numBits, bitArrayPtr) \ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] & \ (1 << ((numBits) % BIT_NUM_BITS_PER_INT))) #define Bit_Clear(numBits, bitArrayPtr) \ ((bitArrayPtr)[(numBits)/BIT_NUM_BITS_PER_INT] &= \ ~(1 << ((numBits) % BIT_NUM_BITS_PER_INT))) #define Bit_IsClear(numBits, bitArrayPtr) \ (!(Bit_IsSet((numBits), (bitArrayPtr)))) #define Bit_Copy(numBits, srcArrayPtr, destArrayPtr) \ bcopy((char *)(srcArrayPtr), (char *)(destArrayPtr), \ Bit_NumBytes(numBits)) #define Bit_Zero(numBits, bitArrayPtr) \ bzero((char *)(bitArrayPtr), Bit_NumBytes(numBits)) extern int Bit_FindFirstSet(); extern int Bit_FindFirstClear(); extern Boolean Bit_Intersect(); extern Boolean Bit_Union(); extern Boolean Bit_AnySet(); extern int *Bit_Expand(); #endif _BIT pmake/buf.c100600 1750 1750 25450 5431142056 11276 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 12/28/90"; #endif /* not lint */ /*- * buf.c -- * Functions for automatically-expanded buffers. */ #include "sprite.h" #include "buf.h" #ifndef max #define max(a,b) ((a) > (b) ? (a) : (b)) #endif /* * BufExpand -- * Expand the given buffer to hold the given number of additional * bytes. * Makes sure there's room for an extra NULL byte at the end of the * buffer in case it holds a string. */ #define BufExpand(bp,nb) \ if (bp->left < (nb)+1) {\ int newSize = (bp)->size + max((nb)+1,BUF_ADD_INC); \ Byte *newBuf = (Byte *) realloc((bp)->buffer, newSize); \ \ (bp)->inPtr = newBuf + ((bp)->inPtr - (bp)->buffer); \ (bp)->outPtr = newBuf + ((bp)->outPtr - (bp)->buffer);\ (bp)->buffer = newBuf;\ (bp)->size = newSize;\ (bp)->left = newSize - ((bp)->inPtr - (bp)->buffer);\ } #define BUF_DEF_SIZE 256 /* Default buffer size */ #define BUF_ADD_INC 256 /* Expansion increment when Adding */ #define BUF_UNGET_INC 16 /* Expansion increment when Ungetting */ /*- *----------------------------------------------------------------------- * Buf_OvAddByte -- * Add a single byte to the buffer. left is zero or negative. * * Results: * None. * * Side Effects: * The buffer may be expanded. * *----------------------------------------------------------------------- */ void Buf_OvAddByte (bp, byte) register Buffer bp; Byte byte; { bp->left = 0; BufExpand (bp, 1); *bp->inPtr++ = byte; bp->left--; /* * Null-terminate */ *bp->inPtr = 0; } /*- *----------------------------------------------------------------------- * Buf_AddBytes -- * Add a number of bytes to the buffer. * * Results: * None. * * Side Effects: * Guess what? * *----------------------------------------------------------------------- */ void Buf_AddBytes (bp, numBytes, bytesPtr) register Buffer bp; int numBytes; Byte *bytesPtr; { BufExpand (bp, numBytes); bcopy (bytesPtr, bp->inPtr, numBytes); bp->inPtr += numBytes; bp->left -= numBytes; /* * Null-terminate */ *bp->inPtr = 0; } /*- *----------------------------------------------------------------------- * Buf_UngetByte -- * Place the byte back at the beginning of the buffer. * * Results: * SUCCESS if the byte was added ok. FAILURE if not. * * Side Effects: * The byte is stuffed in the buffer and outPtr is decremented. * *----------------------------------------------------------------------- */ void Buf_UngetByte (bp, byte) register Buffer bp; Byte byte; { if (bp->outPtr != bp->buffer) { bp->outPtr--; *bp->outPtr = byte; } else if (bp->outPtr == bp->inPtr) { *bp->inPtr = byte; bp->inPtr++; bp->left--; *bp->inPtr = 0; } else { /* * Yech. have to expand the buffer to stuff this thing in. * We use a different expansion constant because people don't * usually push back many bytes when they're doing it a byte at * a time... */ int numBytes = bp->inPtr - bp->outPtr; Byte *newBuf; newBuf = (Byte *)emalloc(bp->size + BUF_UNGET_INC); bcopy ((char *)bp->outPtr, (char *)(newBuf+BUF_UNGET_INC), numBytes+1); bp->outPtr = newBuf + BUF_UNGET_INC; bp->inPtr = bp->outPtr + numBytes; free ((char *)bp->buffer); bp->buffer = newBuf; bp->size += BUF_UNGET_INC; bp->left = bp->size - (bp->inPtr - bp->buffer); bp->outPtr -= 1; *bp->outPtr = byte; } } /*- *----------------------------------------------------------------------- * Buf_UngetBytes -- * Push back a series of bytes at the beginning of the buffer. * * Results: * None. * * Side Effects: * outPtr is decremented and the bytes copied into the buffer. * *----------------------------------------------------------------------- */ void Buf_UngetBytes (bp, numBytes, bytesPtr) register Buffer bp; int numBytes; Byte *bytesPtr; { if (bp->outPtr - bp->buffer >= numBytes) { bp->outPtr -= numBytes; bcopy (bytesPtr, bp->outPtr, numBytes); } else if (bp->outPtr == bp->inPtr) { Buf_AddBytes (bp, numBytes, bytesPtr); } else { int curNumBytes = bp->inPtr - bp->outPtr; Byte *newBuf; int newBytes = max(numBytes,BUF_UNGET_INC); newBuf = (Byte *)emalloc (bp->size + newBytes); bcopy((char *)bp->outPtr, (char *)(newBuf+newBytes), curNumBytes+1); bp->outPtr = newBuf + newBytes; bp->inPtr = bp->outPtr + curNumBytes; free ((char *)bp->buffer); bp->buffer = newBuf; bp->size += newBytes; bp->left = bp->size - (bp->inPtr - bp->buffer); bp->outPtr -= numBytes; bcopy ((char *)bytesPtr, (char *)bp->outPtr, numBytes); } } /*- *----------------------------------------------------------------------- * Buf_GetByte -- * Return the next byte from the buffer. Actually returns an integer. * * Results: * Returns BUF_ERROR if there's no byte in the buffer, or the byte * itself if there is one. * * Side Effects: * outPtr is incremented and both outPtr and inPtr will be reset if * the buffer is emptied. * *----------------------------------------------------------------------- */ int Buf_GetByte (bp) register Buffer bp; { int res; if (bp->inPtr == bp->outPtr) { return (BUF_ERROR); } else { res = (int) *bp->outPtr; bp->outPtr += 1; if (bp->outPtr == bp->inPtr) { bp->outPtr = bp->inPtr = bp->buffer; bp->left = bp->size; *bp->inPtr = 0; } return (res); } } /*- *----------------------------------------------------------------------- * Buf_GetBytes -- * Extract a number of bytes from the buffer. * * Results: * The number of bytes gotten. * * Side Effects: * The passed array is overwritten. * *----------------------------------------------------------------------- */ int Buf_GetBytes (bp, numBytes, bytesPtr) register Buffer bp; int numBytes; Byte *bytesPtr; { if (bp->inPtr - bp->outPtr < numBytes) { numBytes = bp->inPtr - bp->outPtr; } bcopy (bp->outPtr, bytesPtr, numBytes); bp->outPtr += numBytes; if (bp->outPtr == bp->inPtr) { bp->outPtr = bp->inPtr = bp->buffer; bp->left = bp->size; *bp->inPtr = 0; } return (numBytes); } /*- *----------------------------------------------------------------------- * Buf_GetAll -- * Get all the available data at once. * * Results: * A pointer to the data and the number of bytes available. * * Side Effects: * None. * *----------------------------------------------------------------------- */ Byte * Buf_GetAll (bp, numBytesPtr) register Buffer bp; int *numBytesPtr; { if (numBytesPtr != (int *)NULL) { *numBytesPtr = bp->inPtr - bp->outPtr; } return (bp->outPtr); } /*- *----------------------------------------------------------------------- * Buf_Discard -- * Throw away bytes in a buffer. * * Results: * None. * * Side Effects: * The bytes are discarded. * *----------------------------------------------------------------------- */ void Buf_Discard (bp, numBytes) register Buffer bp; int numBytes; { if (bp->inPtr - bp->outPtr <= numBytes) { bp->inPtr = bp->outPtr = bp->buffer; bp->left = bp->size; *bp->inPtr = 0; } else { bp->outPtr += numBytes; } } /*- *----------------------------------------------------------------------- * Buf_Size -- * Returns the number of bytes in the given buffer. Doesn't include * the null-terminating byte. * * Results: * The number of bytes. * * Side Effects: * None. * *----------------------------------------------------------------------- */ int Buf_Size (buf) Buffer buf; { return (buf->inPtr - buf->outPtr); } /*- *----------------------------------------------------------------------- * Buf_Init -- * Initialize a buffer. If no initial size is given, a reasonable * default is used. * * Results: * A buffer to be given to other functions in this library. * * Side Effects: * The buffer is created, the space allocated and pointers * initialized. * *----------------------------------------------------------------------- */ Buffer Buf_Init (size) int size; /* Initial size for the buffer */ { Buffer bp; /* New Buffer */ bp = (Buffer)emalloc(sizeof(*bp)); if (size <= 0) { size = BUF_DEF_SIZE; } bp->left = bp->size = size; bp->buffer = (Byte *)emalloc(size); bp->inPtr = bp->outPtr = bp->buffer; *bp->inPtr = 0; return (bp); } /*- *----------------------------------------------------------------------- * Buf_Destroy -- * Nuke a buffer and all its resources. * * Results: * None. * * Side Effects: * The buffer is freed. * *----------------------------------------------------------------------- */ void Buf_Destroy (buf, freeData) Buffer buf; /* Buffer to destroy */ Boolean freeData; /* TRUE if the data should be destroyed as well */ { if (freeData) { free ((char *)buf->buffer); } free ((char *)buf); } pmake/buf.h100600 1750 1750 6443 5431142056 11264 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)buf.h 5.4 (Berkeley) 12/28/90 */ /*- * buf.h -- * Header for users of the buf library. */ #ifndef _BUF_H #define _BUF_H #include "sprite.h" typedef unsigned char Byte; typedef struct Buffer { int size; /* Current size of the buffer */ int left; /* Space left (== size - (inPtr - buffer)) */ Byte *buffer; /* The buffer itself */ Byte *inPtr; /* Place to write to */ Byte *outPtr; /* Place to read from */ } *Buffer; Buffer Buf_Init(); /* Initialize a buffer */ void Buf_Destroy(); /* Destroy a buffer */ void Buf_AddBytes(); /* Add a range of bytes to a buffer */ int Buf_GetByte(); /* Get a byte from a buffer */ int Buf_GetBytes(); /* Get multiple bytes */ void Buf_UngetByte(); /* Push a byte back into the buffer */ void Buf_UngetBytes(); /* Push many bytes back into the buf */ Byte *Buf_GetAll(); /* Get them all */ void Buf_Discard(); /* Throw away some of the bytes */ int Buf_Size(); /* See how many are there */ /* Buf_AddByte adds a single byte to a buffer. */ #define Buf_AddByte(bp, byte) \ (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte) : \ (void)(*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0)) void Buf_OvAddByte(); /* adds a byte when buffer overflows */ #define BUF_ERROR 256 #endif _BUF_H pmake/compat.c100600 1750 1750 42114 5431142057 12002 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)compat.c 5.7 (Berkeley) 3/1/91"; #endif /* not lint */ /*- * compat.c -- * The routines in this file implement the full-compatibility * mode of PMake. Most of the special functionality of PMake * is available in this mode. Things not supported: * - different shells. * - friendly variable substitution. * * Interface: * Compat_Run Initialize things for this module and recreate * thems as need creatin' */ #include #include #include #include #include #include #include "make.h" extern int errno; /* * The following array is used to make a fast determination of which * characters are interpreted specially by the shell. If a command * contains any of these characters, it is executed by the shell, not * directly by us. */ static char meta[256]; static GNode *curTarg = NILGNODE; static GNode *ENDNode; static int CompatRunCommand(); /*- *----------------------------------------------------------------------- * CompatInterrupt -- * Interrupt the creation of the current target and remove it if * it ain't precious. * * Results: * None. * * Side Effects: * The target is removed and the process exits. If .INTERRUPT exists, * its commands are run first WITH INTERRUPTS IGNORED.. * *----------------------------------------------------------------------- */ static void CompatInterrupt (signo) int signo; { GNode *gn; if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) { char *file = Var_Value (TARGET, curTarg); if (unlink (file) == SUCCESS) { printf ("*** %s removed\n", file); } /* * Run .INTERRUPT only if hit with interrupt signal */ if (signo == SIGINT) { gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); if (gn != NILGNODE) { Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); } } } exit (0); } /*- *----------------------------------------------------------------------- * CompatRunCommand -- * Execute the next command for a target. If the command returns an * error, the node's made field is set to ERROR and creation stops. * * Results: * 0 if the command succeeded, 1 if an error occurred. * * Side Effects: * The node's 'made' field may be set to ERROR. * *----------------------------------------------------------------------- */ static int CompatRunCommand (cmd, gn) char *cmd; /* Command to execute */ GNode *gn; /* Node from which the command came */ { char *cmdStart; /* Start of expanded command */ register char *cp; Boolean silent, /* Don't print command */ errCheck; /* Check errors */ union wait reason; /* Reason for child's death */ int status; /* Description of child's death */ int cpid; /* Child actually found */ int numWritten; /* Number of bytes written for error message */ ReturnStatus stat; /* Status of fork */ LstNode cmdNode; /* Node where current command is located */ char **av; /* Argument vector for thing to exec */ int argc; /* Number of arguments in av or 0 if not * dynamically allocated */ Boolean local; /* TRUE if command should be executed * locally */ silent = gn->type & OP_SILENT; errCheck = !(gn->type & OP_IGNORE); cmdNode = Lst_Member (gn->commands, (ClientData)cmd); cmdStart = Var_Subst (cmd, gn, FALSE); /* * brk_string will return an argv with a NULL in av[1], thus causing * execvp to choke and die horribly. Besides, how can we execute a null * command? In any case, we warn the user that the command expanded to * nothing (is this the right thing to do?). */ if (*cmdStart == '\0') { Error("%s expands to empty string", cmd); return(0); } else { cmd = cmdStart; } Lst_Replace (cmdNode, (ClientData)cmdStart); if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart); return(0); } else if (strcmp(cmdStart, "...") == 0) { gn->type |= OP_SAVE_CMDS; return(0); } while ((*cmd == '@') || (*cmd == '-')) { if (*cmd == '@') { silent = TRUE; } else { errCheck = FALSE; } cmd++; } /* * Search for meta characters in the command. If there are no meta * characters, there's no need to execute a shell to execute the * command. */ for (cp = cmd; !meta[*cp]; cp++) { continue; } /* * Print the command before echoing if we're not supposed to be quiet for * this one. We also print the command if -n given. */ if (!silent || noExecute) { printf ("%s\n", cmd); fflush(stdout); } /* * If we're not supposed to execute any commands, this is as far as * we go... */ if (noExecute) { return (0); } if (*cp != '\0') { /* * If *cp isn't the null character, we hit a "meta" character and * need to pass the command off to the shell. We give the shell the * -e flag as well as -c if it's supposed to exit when it hits an * error. */ static char *shargv[4] = { "/bin/sh" }; shargv[1] = (errCheck ? "-ec" : "-c"); shargv[2] = cmd; shargv[3] = (char *)NULL; av = shargv; argc = 0; } else { /* * No meta-characters, so no need to exec a shell. Break the command * into words to form an argument vector we can execute. * brk_string sticks our name in av[0], so we have to * skip over it... */ av = brk_string(cmd, &argc); av += 1; } local = TRUE; /* * Fork and execute the single command. If the fork fails, we abort. */ cpid = vfork(); if (cpid < 0) { Fatal("Could not fork"); } if (cpid == 0) { if (local) { execvp(av[0], av); numWritten = write (2, av[0], strlen (av[0])); numWritten = write (2, ": not found\n", sizeof(": not found")); } else { (void)execv(av[0], av); } exit(1); } /* * The child is off and running. Now all we can do is wait... */ while (1) { int id; if (!local) { id = 0; } while ((stat = wait((int *)&reason)) != cpid) { if (stat == -1 && errno != EINTR) { break; } } if (stat > -1) { if (WIFSTOPPED(reason)) { status = reason.w_stopval; /* stopped */ } else if (WIFEXITED(reason)) { status = reason.w_retcode; /* exited */ if (status != 0) { printf ("*** Error code %d", status); } } else { status = reason.w_termsig; /* signaled */ printf ("*** Signal %d", status); } if (!WIFEXITED(reason) || (status != 0)) { if (errCheck) { gn->made = ERROR; if (keepgoing) { /* * Abort the current target, but let others * continue. */ printf (" (continuing)\n"); } } else { /* * Continue executing commands for this target. * If we return 0, this will happen... */ printf (" (ignored)\n"); status = 0; } } break; } else { Fatal ("error in wait: %d", stat); /*NOTREACHED*/ } } return (status); } /*- *----------------------------------------------------------------------- * CompatMake -- * Make a target. * * Results: * 0 * * Side Effects: * If an error is detected and not being ignored, the process exits. * *----------------------------------------------------------------------- */ static int CompatMake (gn, pgn) GNode *gn; /* The node to make */ GNode *pgn; /* Parent to abort if necessary */ { if (gn->type & OP_USE) { Make_HandleUse(gn, pgn); } else if (gn->made == UNMADE) { /* * First mark ourselves to be made, then apply whatever transformations * the suffix module thinks are necessary. Once that's done, we can * descend and make all our children. If any of them has an error * but the -k flag was given, our 'make' field will be set FALSE again. * This is our signal to not attempt to do anything but abort our * parent as well. */ gn->make = TRUE; gn->made = BEINGMADE; Suff_FindDeps (gn); Lst_ForEach (gn->children, CompatMake, (ClientData)gn); if (!gn->make) { gn->made = ABORTED; pgn->make = FALSE; return (0); } if (Lst_Member (gn->iParents, pgn) != NILLNODE) { Var_Set (IMPSRC, Var_Value(TARGET, gn), pgn); } /* * All the children were made ok. Now cmtime contains the modification * time of the newest child, we need to find out if we exist and when * we were modified last. The criteria for datedness are defined by the * Make_OODate function. */ if (DEBUG(MAKE)) { printf("Examining %s...", gn->name); } if (! Make_OODate(gn)) { gn->made = UPTODATE; if (DEBUG(MAKE)) { printf("up-to-date.\n"); } return (0); } else if (DEBUG(MAKE)) { printf("out-of-date.\n"); } /* * If the user is just seeing if something is out-of-date, exit now * to tell him/her "yes". */ if (queryFlag) { exit (-1); } /* * We need to be re-made. We also have to make sure we've got a $? * variable. To be nice, we also define the $> variable using * Make_DoAllVar(). */ Make_DoAllVar(gn); /* * Alter our type to tell if errors should be ignored or things * should not be printed so CompatRunCommand knows what to do. */ if (Targ_Ignore (gn)) { gn->type |= OP_IGNORE; } if (Targ_Silent (gn)) { gn->type |= OP_SILENT; } if (Job_CheckCommands (gn, Fatal)) { /* * Our commands are ok, but we still have to worry about the -t * flag... */ if (!touchFlag) { curTarg = gn; Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn); curTarg = NILGNODE; } else { Job_Touch (gn, gn->type & OP_SILENT); } } else { gn->made = ERROR; } if (gn->made != ERROR) { /* * If the node was made successfully, mark it so, update * its modification time and timestamp all its parents. Note * that for .ZEROTIME targets, the timestamping isn't done. * This is to keep its state from affecting that of its parent. */ gn->made = MADE; #ifndef RECHECK /* * We can't re-stat the thing, but we can at least take care of * rules where a target depends on a source that actually creates * the target, but only if it has changed, e.g. * * parse.h : parse.o * * parse.o : parse.y * yacc -d parse.y * cc -c y.tab.c * mv y.tab.o parse.o * cmp -s y.tab.h parse.h || mv y.tab.h parse.h * * In this case, if the definitions produced by yacc haven't * changed from before, parse.h won't have been updated and * gn->mtime will reflect the current modification time for * parse.h. This is something of a kludge, I admit, but it's a * useful one.. * * XXX: People like to use a rule like * * FRC: * * To force things that depend on FRC to be made, so we have to * check for gn->children being empty as well... */ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { gn->mtime = now; } #else /* * This is what Make does and it's actually a good thing, as it * allows rules like * * cmp -s y.tab.h parse.h || cp y.tab.h parse.h * * to function as intended. Unfortunately, thanks to the stateless * nature of NFS (and the speed of this program), there are times * when the modification time of a file created on a remote * machine will not be modified before the stat() implied by * the Dir_MTime occurs, thus leading us to believe that the file * is unchanged, wreaking havoc with files that depend on this one. * * I have decided it is better to make too much than to make too * little, so this stuff is commented out unless you're sure it's * ok. * -- ardeb 1/12/88 */ if (noExecute || Dir_MTime(gn) == 0) { gn->mtime = now; } if (DEBUG(MAKE)) { printf("update time: %s\n", Targ_FmtTime(gn->mtime)); } #endif if (!(gn->type & OP_EXEC)) { pgn->childMade = TRUE; Make_TimeStamp(pgn, gn); } } else if (keepgoing) { pgn->make = FALSE; } else { printf ("\n\nStop.\n"); exit (1); } } else if (gn->made == ERROR) { /* * Already had an error when making this beastie. Tell the parent * to abort. */ pgn->make = FALSE; } else { if (Lst_Member (gn->iParents, pgn) != NILLNODE) { Var_Set (IMPSRC, Var_Value(TARGET, gn), pgn); } switch(gn->made) { case BEINGMADE: Error("Graph cycles through %s\n", gn->name); gn->made = ERROR; pgn->make = FALSE; break; case MADE: if ((gn->type & OP_EXEC) == 0) { pgn->childMade = TRUE; Make_TimeStamp(pgn, gn); } break; case UPTODATE: if ((gn->type & OP_EXEC) == 0) { Make_TimeStamp(pgn, gn); } break; } } return (0); } /*- *----------------------------------------------------------------------- * Compat_Run -- * Initialize this mode and start making. * * Results: * None. * * Side Effects: * Guess what? * *----------------------------------------------------------------------- */ void Compat_Run(targs) Lst targs; /* List of target nodes to re-create */ { char *cp; /* Pointer to string of shell meta-characters */ GNode *gn; /* Current root target */ int errors; /* Number of targets not remade due to errors */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) { signal(SIGINT, CompatInterrupt); } if (signal(SIGTERM, SIG_IGN) != SIG_IGN) { signal(SIGTERM, CompatInterrupt); } if (signal(SIGHUP, SIG_IGN) != SIG_IGN) { signal(SIGHUP, CompatInterrupt); } if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) { signal(SIGQUIT, CompatInterrupt); } for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { meta[*cp] = 1; } /* * The null character serves as a sentinel in the string. */ meta[0] = 1; ENDNode = Targ_FindNode(".END", TARG_CREATE); /* * If the user has defined a .BEGIN target, execute the commands attached * to it. */ if (!queryFlag) { gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); if (gn != NILGNODE) { Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn); } } /* * For each entry in the list of targets to create, call CompatMake on * it to create the thing. CompatMake will leave the 'made' field of gn * in one of several states: * UPTODATE gn was already up-to-date * MADE gn was recreated successfully * ERROR An error occurred while gn was being created * ABORTED gn was not remade because one of its inferiors * could not be made due to errors. */ errors = 0; while (!Lst_IsEmpty (targs)) { gn = (GNode *) Lst_DeQueue (targs); CompatMake (gn, gn); if (gn->made == UPTODATE) { printf ("`%s' is up to date.\n", gn->name); } else if (gn->made == ABORTED) { printf ("`%s' not remade because of errors.\n", gn->name); errors += 1; } } /* * If the user has defined a .END target, run its commands. */ if (errors == 0) { Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn); } } pmake/cond.c100600 1750 1750 70455 5431142057 11453 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)cond.c 5.6 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * cond.c -- * Functions to handle conditionals in a makefile. * * Interface: * Cond_Eval Evaluate the conditional in the passed line. * */ #include "make.h" #include #include /* * The parsing of conditional expressions is based on this grammar: * E -> F || E * E -> F * F -> T && F * F -> T * T -> defined(variable) * T -> make(target) * T -> exists(file) * T -> empty(varspec) * T -> target(name) * T -> symbol * T -> $(varspec) op value * T -> $(varspec) == "string" * T -> $(varspec) != "string" * T -> ( E ) * T -> ! T * op -> == | != | > | < | >= | <= * * 'symbol' is some other symbol to which the default function (condDefProc) * is applied. * * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) * will return And for '&' and '&&', Or for '|' and '||', Not for '!', * LParen for '(', RParen for ')' and will evaluate the other terminal * symbols, using either the default function or the function given in the * terminal, and return the result as either True or False. * * All Non-Terminal functions (CondE, CondF and CondT) return Err on error. */ typedef enum { And, Or, Not, True, False, LParen, RParen, EndOfFile, None, Err } Token; /*- * Structures to handle elegantly the different forms of #if's. The * last two fields are stored in condInvert and condDefProc, respectively. */ static Boolean CondDoDefined(), CondDoMake(); static struct If { char *form; /* Form of if */ int formlen; /* Length of form */ Boolean doNot; /* TRUE if default function should be negated */ Boolean (*defProc)(); /* Default function to apply */ } ifs[] = { "ifdef", 5, FALSE, CondDoDefined, "ifndef", 6, TRUE, CondDoDefined, "ifmake", 6, FALSE, CondDoMake, "ifnmake", 7, TRUE, CondDoMake, "if", 2, FALSE, CondDoDefined, (char *)0, 0, FALSE, (Boolean (*)())0, }; static Boolean condInvert; /* Invert the default function */ static Boolean (*condDefProc)(); /* Default function to apply */ static char *condExpr; /* The expression to parse */ static Token condPushBack=None; /* Single push-back token used in * parsing */ #define MAXIF 30 /* greatest depth of #if'ing */ static Boolean condStack[MAXIF]; /* Stack of conditionals's values */ static int condTop = MAXIF; /* Top-most conditional */ static int skipIfLevel=0; /* Depth of skipped conditionals */ static Boolean skipLine = FALSE; /* Whether the parse module is skipping * lines */ static Token CondT(), CondF(), CondE(); /*- *----------------------------------------------------------------------- * CondPushBack -- * Push back the most recent token read. We only need one level of * this, so the thing is just stored in 'condPushback'. * * Results: * None. * * Side Effects: * condPushback is overwritten. * *----------------------------------------------------------------------- */ static void CondPushBack (t) Token t; /* Token to push back into the "stream" */ { condPushBack = t; } /*- *----------------------------------------------------------------------- * CondGetArg -- * Find the argument of a built-in function. * * Results: * The length of the argument and the address of the argument. * * Side Effects: * The pointer is set to point to the closing parenthesis of the * function call. * *----------------------------------------------------------------------- */ static int CondGetArg (linePtr, argPtr, func, parens) char **linePtr; char **argPtr; char *func; Boolean parens; /* TRUE if arg should be bounded by parens */ { register char *cp; int argLen; register Buffer buf; cp = *linePtr; if (parens) { while (*cp != '(' && *cp != '\0') { cp++; } if (*cp == '(') { cp++; } } if (*cp == '\0') { /* * No arguments whatsoever. Because 'make' and 'defined' aren't really * "reserved words", we don't print a message. I think this is better * than hitting the user with a warning message every time s/he uses * the word 'make' or 'defined' at the beginning of a symbol... */ *argPtr = cp; return (0); } while (*cp == ' ' || *cp == '\t') { cp++; } /* * Create a buffer for the argument and start it out at 16 characters * long. Why 16? Why not? */ buf = Buf_Init(16); while ((index(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) { if (*cp == '$') { /* * Parse the variable spec and install it as part of the argument * if it's valid. We tell Var_Parse to complain on an undefined * variable, so we don't do it too. Nor do we return an error, * though perhaps we should... */ char *cp2; int len; Boolean doFree; cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &doFree); Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); if (doFree) { free(cp2); } cp += len; } else { Buf_AddByte(buf, (Byte)*cp); cp++; } } Buf_AddByte(buf, (Byte)'\0'); *argPtr = (char *)Buf_GetAll(buf, &argLen); Buf_Destroy(buf, FALSE); while (*cp == ' ' || *cp == '\t') { cp++; } if (parens && *cp != ')') { Parse_Error (PARSE_WARNING, "Missing closing parenthesis for %s()", func); return (0); } else if (parens) { /* * Advance pointer past close parenthesis. */ cp++; } *linePtr = cp; return (argLen); } /*- *----------------------------------------------------------------------- * CondDoDefined -- * Handle the 'defined' function for conditionals. * * Results: * TRUE if the given variable is defined. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean CondDoDefined (argLen, arg) int argLen; char *arg; { char savec = arg[argLen]; Boolean result; arg[argLen] = '\0'; if (Var_Value (arg, VAR_CMD) != (char *)NULL) { result = TRUE; } else { result = FALSE; } arg[argLen] = savec; return (result); } /*- *----------------------------------------------------------------------- * CondStrMatch -- * Front-end for Str_Match so it returns 0 on match and non-zero * on mismatch. Callback function for CondDoMake via Lst_Find * * Results: * 0 if string matches pattern * * Side Effects: * None * *----------------------------------------------------------------------- */ static int CondStrMatch(string, pattern) char *string; char *pattern; { return(!Str_Match(string,pattern)); } /*- *----------------------------------------------------------------------- * CondDoMake -- * Handle the 'make' function for conditionals. * * Results: * TRUE if the given target is being made. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean CondDoMake (argLen, arg) int argLen; char *arg; { char savec = arg[argLen]; Boolean result; arg[argLen] = '\0'; if (Lst_Find (create, (ClientData)arg, CondStrMatch) == NILLNODE) { result = FALSE; } else { result = TRUE; } arg[argLen] = savec; return (result); } /*- *----------------------------------------------------------------------- * CondDoExists -- * See if the given file exists. * * Results: * TRUE if the file exists and FALSE if it does not. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean CondDoExists (argLen, arg) int argLen; char *arg; { char savec = arg[argLen]; Boolean result; char *path; arg[argLen] = '\0'; path = Dir_FindFile(arg, dirSearchPath); if (path != (char *)NULL) { result = TRUE; free(path); } else { result = FALSE; } arg[argLen] = savec; return (result); } /*- *----------------------------------------------------------------------- * CondDoTarget -- * See if the given node exists and is an actual target. * * Results: * TRUE if the node exists as a target and FALSE if it does not. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean CondDoTarget (argLen, arg) int argLen; char *arg; { char savec = arg[argLen]; Boolean result; GNode *gn; arg[argLen] = '\0'; gn = Targ_FindNode(arg, TARG_NOCREATE); if ((gn != NILGNODE) && !OP_NOP(gn->type)) { result = TRUE; } else { result = FALSE; } arg[argLen] = savec; return (result); } /*- *----------------------------------------------------------------------- * CondCvtArg -- * Convert the given number into a double. If the number begins * with 0x, or just x, it is interpreted as a hexadecimal integer * and converted to a double from there. All other strings just have * atof called on them. * * Results: * The double value of string. * * Side Effects: * * *----------------------------------------------------------------------- */ static double CondCvtArg(str) register char *str; { int sign = 1; double atof(); if (*str == '-') { sign = -1; str++; } else if (*str == '+') { str++; } if (((*str == '0') && (str[1] == 'x')) || (*str == 'x')) { register int i; str += (*str == 'x') ? 1 : 2; i = 0; while (isxdigit(*str)) { i *= 16; if (*str <= '9') { i += *str - '0'; } else if (*str <= 'F') { i += *str - 'A' + 10; } else { i += *str - 'a' + 10; } str++; } if (sign < 0) { return((double)(-i)); } else { return((double)i); } } else if (sign < 0) { return(- atof(str)); } else { return(atof(str)); } } /*- *----------------------------------------------------------------------- * CondToken -- * Return the next token from the input. * * Results: * A Token for the next lexical token in the stream. * * Side Effects: * condPushback will be set back to None if it is used. * *----------------------------------------------------------------------- */ static Token CondToken(doEval) Boolean doEval; { Token t; if (condPushBack == None) { while (*condExpr == ' ' || *condExpr == '\t') { condExpr++; } switch (*condExpr) { case '(': t = LParen; condExpr++; break; case ')': t = RParen; condExpr++; break; case '|': if (condExpr[1] == '|') { condExpr++; } condExpr++; t = Or; break; case '&': if (condExpr[1] == '&') { condExpr++; } condExpr++; t = And; break; case '!': t = Not; condExpr++; break; case '\n': case '\0': t = EndOfFile; break; case '$': { char *lhs; char *rhs; char *op; int varSpecLen; Boolean doFree; /* * Parse the variable spec and skip over it, saving its * value in lhs. */ t = Err; lhs = Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree); if (lhs == var_Error) { /* * Even if !doEval, we still report syntax errors, which * is what getting var_Error back with !doEval means. */ return(Err); } condExpr += varSpecLen; /* * Skip whitespace to get to the operator */ while (isspace(*condExpr)) { condExpr++; } /* * Make sure the operator is a valid one. If it isn't a * known relational operator, pretend we got a * != 0 comparison. */ op = condExpr; switch (*condExpr) { case '!': case '=': case '<': case '>': if (condExpr[1] == '=') { condExpr += 2; } else { condExpr += 1; } break; default: op = "!="; rhs = "0"; goto do_compare; } while (isspace(*condExpr)) { condExpr++; } if (*condExpr == '\0') { Parse_Error(PARSE_WARNING, "Missing right-hand-side of operator"); goto error; } rhs = condExpr; do_compare: if (*rhs == '"') { /* * Doing a string comparison. Only allow == and != for * operators. */ char *string; char *cp, *cp2; Buffer buf; if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { Parse_Error(PARSE_WARNING, "String comparison operator should be either == or !="); goto error; } buf = Buf_Init(0); for (cp = rhs+1; (*cp != '"') && (*cp != '\0'); cp++) { if ((*cp == '\\') && (cp[1] != '\0')) { /* * Backslash escapes things -- skip over next * character, if it exists. */ cp++; Buf_AddByte(buf, (Byte)*cp); } else if (*cp == '$') { int len; Boolean freeIt; cp2 = Var_Parse(cp, VAR_CMD, doEval,&len, &freeIt); if (cp2 != var_Error) { Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); if (freeIt) { free(cp2); } cp += len - 1; } else { Buf_AddByte(buf, (Byte)*cp); } } else { Buf_AddByte(buf, (Byte)*cp); } } Buf_AddByte(buf, (Byte)0); string = (char *)Buf_GetAll(buf, (int *)0); Buf_Destroy(buf, FALSE); if (DEBUG(COND)) { printf("lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, string, op); } /* * Null-terminate rhs and perform the comparison. * t is set to the result. */ if (*op == '=') { t = strcmp(lhs, string) ? False : True; } else { t = strcmp(lhs, string) ? True : False; } free(string); if (rhs == condExpr) { condExpr = cp + 1; } } else { /* * rhs is either a float or an integer. Convert both the * lhs and the rhs to a double and compare the two. */ double left, right; char *string; left = CondCvtArg(lhs); if (*rhs == '$') { int len; Boolean freeIt; string = Var_Parse(rhs, VAR_CMD, doEval,&len,&freeIt); if (string == var_Error) { right = 0.0; } else { right = CondCvtArg(string); if (freeIt) { free(string); } if (rhs == condExpr) { condExpr += len; } } } else { right = CondCvtArg(rhs); if (rhs == condExpr) { /* * Skip over the right-hand side */ while(!isspace(*condExpr) && (*condExpr != '\0')) { condExpr++; } } } if (DEBUG(COND)) { printf("left = %f, right = %f, op = %.2s\n", left, right, op); } switch(op[0]) { case '!': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto error; } t = (left != right ? True : False); break; case '=': if (op[1] != '=') { Parse_Error(PARSE_WARNING, "Unknown operator"); goto error; } t = (left == right ? True : False); break; case '<': if (op[1] == '=') { t = (left <= right ? True : False); } else { t = (left < right ? True : False); } break; case '>': if (op[1] == '=') { t = (left >= right ? True : False); } else { t = (left > right ? True : False); } break; } } error: if (doFree) { free(lhs); } break; } default: { Boolean (*evalProc)(); Boolean invert = FALSE; char *arg; int arglen; if (strncmp (condExpr, "defined", 7) == 0) { /* * Use CondDoDefined to evaluate the argument and * CondGetArg to extract the argument from the 'function * call'. */ evalProc = CondDoDefined; condExpr += 7; arglen = CondGetArg (&condExpr, &arg, "defined", TRUE); if (arglen == 0) { condExpr -= 7; goto use_default; } } else if (strncmp (condExpr, "make", 4) == 0) { /* * Use CondDoMake to evaluate the argument and * CondGetArg to extract the argument from the 'function * call'. */ evalProc = CondDoMake; condExpr += 4; arglen = CondGetArg (&condExpr, &arg, "make", TRUE); if (arglen == 0) { condExpr -= 4; goto use_default; } } else if (strncmp (condExpr, "exists", 6) == 0) { /* * Use CondDoExists to evaluate the argument and * CondGetArg to extract the argument from the * 'function call'. */ evalProc = CondDoExists; condExpr += 6; arglen = CondGetArg(&condExpr, &arg, "exists", TRUE); if (arglen == 0) { condExpr -= 6; goto use_default; } } else if (strncmp(condExpr, "empty", 5) == 0) { /* * Use Var_Parse to parse the spec in parens and return * True if the resulting string is empty. */ int length; Boolean doFree; char *val; condExpr += 5; for (arglen = 0; condExpr[arglen] != '(' && condExpr[arglen] != '\0'; arglen += 1) { /* void */ ; } if (condExpr[arglen] != '\0') { val = Var_Parse(&condExpr[arglen - 1], VAR_CMD, doEval, &length, &doFree); if (val == var_Error) { t = Err; } else { t = (*val == '\0') ? True : False; } if (doFree) { free(val); } /* * Advance condExpr to beyond the closing ). Note that * we subtract one from arglen + length b/c length * is calculated from condExpr[arglen - 1]. */ condExpr += arglen + length - 1; } else { condExpr -= 5; goto use_default; } break; } else if (strncmp (condExpr, "target", 6) == 0) { /* * Use CondDoTarget to evaluate the argument and * CondGetArg to extract the argument from the * 'function call'. */ evalProc = CondDoTarget; condExpr += 6; arglen = CondGetArg(&condExpr, &arg, "target", TRUE); if (arglen == 0) { condExpr -= 6; goto use_default; } } else { /* * The symbol is itself the argument to the default * function. We advance condExpr to the end of the symbol * by hand (the next whitespace, closing paren or * binary operator) and set to invert the evaluation * function if condInvert is TRUE. */ use_default: invert = condInvert; evalProc = condDefProc; arglen = CondGetArg(&condExpr, &arg, "", FALSE); } /* * Evaluate the argument using the set function. If invert * is TRUE, we invert the sense of the function. */ t = (!doEval || (* evalProc) (arglen, arg) ? (invert ? False : True) : (invert ? True : False)); free(arg); break; } } } else { t = condPushBack; condPushBack = None; } return (t); } /*- *----------------------------------------------------------------------- * CondT -- * Parse a single term in the expression. This consists of a terminal * symbol or Not and a terminal symbol (not including the binary * operators): * T -> defined(variable) | make(target) | exists(file) | symbol * T -> ! T | ( E ) * * Results: * True, False or Err. * * Side Effects: * Tokens are consumed. * *----------------------------------------------------------------------- */ static Token CondT(doEval) Boolean doEval; { Token t; t = CondToken(doEval); if (t == EndOfFile) { /* * If we reached the end of the expression, the expression * is malformed... */ t = Err; } else if (t == LParen) { /* * T -> ( E ) */ t = CondE(doEval); if (t != Err) { if (CondToken(doEval) != RParen) { t = Err; } } } else if (t == Not) { t = CondT(doEval); if (t == True) { t = False; } else if (t == False) { t = True; } } return (t); } /*- *----------------------------------------------------------------------- * CondF -- * Parse a conjunctive factor (nice name, wot?) * F -> T && F | T * * Results: * True, False or Err * * Side Effects: * Tokens are consumed. * *----------------------------------------------------------------------- */ static Token CondF(doEval) Boolean doEval; { Token l, o; l = CondT(doEval); if (l != Err) { o = CondToken(doEval); if (o == And) { /* * F -> T && F * * If T is False, the whole thing will be False, but we have to * parse the r.h.s. anyway (to throw it away). * If T is True, the result is the r.h.s., be it an Err or no. */ if (l == True) { l = CondF(doEval); } else { (void) CondF(FALSE); } } else { /* * F -> T */ CondPushBack (o); } } return (l); } /*- *----------------------------------------------------------------------- * CondE -- * Main expression production. * E -> F || E | F * * Results: * True, False or Err. * * Side Effects: * Tokens are, of course, consumed. * *----------------------------------------------------------------------- */ static Token CondE(doEval) Boolean doEval; { Token l, o; l = CondF(doEval); if (l != Err) { o = CondToken(doEval); if (o == Or) { /* * E -> F || E * * A similar thing occurs for ||, except that here we make sure * the l.h.s. is False before we bother to evaluate the r.h.s. * Once again, if l is False, the result is the r.h.s. and once * again if l is True, we parse the r.h.s. to throw it away. */ if (l == False) { l = CondE(doEval); } else { (void) CondE(FALSE); } } else { /* * E -> F */ CondPushBack (o); } } return (l); } /*- *----------------------------------------------------------------------- * Cond_Eval -- * Evaluate the conditional in the passed line. The line * looks like this: * # * where is any of if, ifmake, ifnmake, ifdef, * ifndef, elif, elifmake, elifnmake, elifdef, elifndef * and consists of &&, ||, !, make(target), defined(variable) * and parenthetical groupings thereof. * * Results: * COND_PARSE if should parse lines after the conditional * COND_SKIP if should skip lines after the conditional * COND_INVALID if not a valid conditional. * * Side Effects: * None. * *----------------------------------------------------------------------- */ Cond_Eval (line) char *line; /* Line to parse */ { struct If *ifp; Boolean isElse; Boolean value; int level; /* Level at which to report errors. */ level = PARSE_FATAL; for (line++; *line == ' ' || *line == '\t'; line++) { continue; } /* * Find what type of if we're dealing with. The result is left * in ifp and isElse is set TRUE if it's an elif line. */ if (line[0] == 'e' && line[1] == 'l') { line += 2; isElse = TRUE; } else if (strncmp (line, "endif", 5) == 0) { /* * End of a conditional section. If skipIfLevel is non-zero, that * conditional was skipped, so lines following it should also be * skipped. Hence, we return COND_SKIP. Otherwise, the conditional * was read so succeeding lines should be parsed (think about it...) * so we return COND_PARSE, unless this endif isn't paired with * a decent if. */ if (skipIfLevel != 0) { skipIfLevel -= 1; return (COND_SKIP); } else { if (condTop == MAXIF) { Parse_Error (level, "if-less endif"); return (COND_INVALID); } else { skipLine = FALSE; condTop += 1; return (COND_PARSE); } } } else { isElse = FALSE; } /* * Figure out what sort of conditional it is -- what its default * function is, etc. -- by looking in the table of valid "ifs" */ for (ifp = ifs; ifp->form != (char *)0; ifp++) { if (strncmp (ifp->form, line, ifp->formlen) == 0) { break; } } if (ifp->form == (char *) 0) { /* * Nothing fit. If the first word on the line is actually * "else", it's a valid conditional whose value is the inverse * of the previous if we parsed. */ if (isElse && (line[0] == 's') && (line[1] == 'e')) { if (condTop == MAXIF) { Parse_Error (level, "if-less else"); return (COND_INVALID); } else if (skipIfLevel == 0) { value = !condStack[condTop]; } else { return (COND_SKIP); } } else { /* * Not a valid conditional type. No error... */ return (COND_INVALID); } } else { if (isElse) { if (condTop == MAXIF) { Parse_Error (level, "if-less elif"); return (COND_INVALID); } else if (skipIfLevel != 0) { /* * If skipping this conditional, just ignore the whole thing. * If we don't, the user might be employing a variable that's * undefined, for which there's an enclosing ifdef that * we're skipping... */ return(COND_SKIP); } } else if (skipLine) { /* * Don't even try to evaluate a conditional that's not an else if * we're skipping things... */ skipIfLevel += 1; return(COND_SKIP); } /* * Initialize file-global variables for parsing */ condDefProc = ifp->defProc; condInvert = ifp->doNot; line += ifp->formlen; while (*line == ' ' || *line == '\t') { line++; } condExpr = line; condPushBack = None; switch (CondE(TRUE)) { case True: if (CondToken(TRUE) == EndOfFile) { value = TRUE; break; } goto err; /*FALLTHRU*/ case False: if (CondToken(TRUE) == EndOfFile) { value = FALSE; break; } /*FALLTHRU*/ case Err: err: Parse_Error (level, "Malformed conditional (%s)", line); return (COND_INVALID); } } if (!isElse) { condTop -= 1; } else if ((skipIfLevel != 0) || condStack[condTop]) { /* * If this is an else-type conditional, it should only take effect * if its corresponding if was evaluated and FALSE. If its if was * TRUE or skipped, we return COND_SKIP (and start skipping in case * we weren't already), leaving the stack unmolested so later elif's * don't screw up... */ skipLine = TRUE; return (COND_SKIP); } if (condTop < 0) { /* * This is the one case where we can definitely proclaim a fatal * error. If we don't, we're hosed. */ Parse_Error (PARSE_FATAL, "Too many nested if's. %d max.", MAXIF); return (COND_INVALID); } else { condStack[condTop] = value; skipLine = !value; return (value ? COND_PARSE : COND_SKIP); } } /*- *----------------------------------------------------------------------- * Cond_End -- * Make sure everything's clean at the end of a makefile. * * Results: * None. * * Side Effects: * Parse_Error will be called if open conditionals are around. * *----------------------------------------------------------------------- */ void Cond_End() { if (condTop != MAXIF) { Parse_Error(PARSE_FATAL, "%d open conditional%s", MAXIF-condTop, MAXIF-condTop == 1 ? "" : "s"); } condTop = MAXIF; } pmake/config.h100600 1750 1750 7177 5431142060 11755 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)config.h 5.9 (Berkeley) 6/1/90 */ #define DEFSHELL 1 /* Bourne shell */ /* * DEFMAXJOBS * DEFMAXLOCAL * These control the default concurrency. On no occasion will more * than DEFMAXJOBS targets be created at once (locally or remotely) * DEFMAXLOCAL is the highest number of targets which will be * created on the local machine at once. Note that if you set this * to 0, nothing will ever happen... */ #define DEFMAXJOBS 4 #define DEFMAXLOCAL 1 /* * INCLUDES * LIBRARIES * These control the handling of the .INCLUDES and .LIBS variables. * If INCLUDES is defined, the .INCLUDES variable will be filled * from the search paths of those suffixes which are marked by * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS * See suff.c for more details. */ #define INCLUDES #define LIBRARIES /* * LIBSUFF * Is the suffix used to denote libraries and is used by the Suff module * to find the search path on which to seek any -l targets. * * RECHECK * If defined, Make_Update will check a target for its current * modification time after it has been re-made, setting it to the * starting time of the make only if the target still doesn't exist. * Unfortunately, under NFS the modification time often doesn't * get updated in time, so a target will appear to not have been * re-made, causing later targets to appear up-to-date. On systems * that don't have this problem, you should defined this. Under * NFS you probably should not, unless you aren't exporting jobs. * * POSIX * If the POSIX standard for Make is to be followed. There are * several areas that I dislike, hence this constant. */ #define LIBSUFF ".a" #define RECHECK /*#define POSIX*/ pmake/dir.c100600 1750 1750 112553 5431142061 11315 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)dir.c 5.6 (Berkeley) 12/28/90"; #endif /* not lint */ /*- * dir.c -- * Directory searching using wildcards and/or normal names... * Used both for source wildcarding in the Makefile and for finding * implicit sources. * * The interface for this module is: * Dir_Init Initialize the module. * * Dir_HasWildcards Returns TRUE if the name given it needs to * be wildcard-expanded. * * Dir_Expand Given a pattern and a path, return a Lst of names * which match the pattern on the search path. * * Dir_FindFile Searches for a file on a given search path. * If it exists, the entire path is returned. * Otherwise NULL is returned. * * Dir_MTime Return the modification time of a node. The file * is searched for along the default search path. * The path and mtime fields of the node are filled * in. * * Dir_AddDir Add a directory to a search path. * * Dir_MakeFlags Given a search path and a command flag, create * a string with each of the directories in the path * preceded by the command flag and all of them * separated by a space. * * Dir_Destroy Destroy an element of a search path. Frees up all * things that can be freed for the element as long * as the element is no longer referenced by any other * search path. * Dir_ClearPath Resets a search path to the empty list. * * For debugging: * Dir_PrintDirectories Print stats about the directory cache. */ #include #include #include #include #include "make.h" #include "hash.h" /* * A search path consists of a Lst of Path structures. A Path structure * has in it the name of the directory and a hash table of all the files * in the directory. This is used to cut down on the number of system * calls necessary to find implicit dependents and their like. Since * these searches are made before any actions are taken, we need not * worry about the directory changing due to creation commands. If this * hampers the style of some makefiles, they must be changed. * * A list of all previously-read directories is kept in the * openDirectories Lst. This list is checked first before a directory * is opened. * * The need for the caching of whole directories is brought about by * the multi-level transformation code in suff.c, which tends to search * for far more files than regular make does. In the initial * implementation, the amount of time spent performing "stat" calls was * truly astronomical. The problem with hashing at the start is, * of course, that pmake doesn't then detect changes to these directories * during the course of the make. Three possibilities suggest themselves: * * 1) just use stat to test for a file's existence. As mentioned * above, this is very inefficient due to the number of checks * engendered by the multi-level transformation code. * 2) use readdir() and company to search the directories, keeping * them open between checks. I have tried this and while it * didn't slow down the process too much, it could severely * affect the amount of parallelism available as each directory * open would take another file descriptor out of play for * handling I/O for another job. Given that it is only recently * that UNIX OS's have taken to allowing more than 20 or 32 * file descriptors for a process, this doesn't seem acceptable * to me. * 3) record the mtime of the directory in the Path structure and * verify the directory hasn't changed since the contents were * hashed. This will catch the creation or deletion of files, * but not the updating of files. However, since it is the * creation and deletion that is the problem, this could be * a good thing to do. Unfortunately, if the directory (say ".") * were fairly large and changed fairly frequently, the constant * rehashing could seriously degrade performance. It might be * good in such cases to keep track of the number of rehashes * and if the number goes over a (small) limit, resort to using * stat in its place. * * An additional thing to consider is that pmake is used primarily * to create C programs and until recently pcc-based compilers refused * to allow you to specify where the resulting object file should be * placed. This forced all objects to be created in the current * directory. This isn't meant as a full excuse, just an explanation of * some of the reasons for the caching used here. * * One more note: the location of a target's file is only performed * on the downward traversal of the graph and then only for terminal * nodes in the graph. This could be construed as wrong in some cases, * but prevents inadvertent modification of files when the "installed" * directory for a file is provided in the search path. * * Another data structure maintained by this module is an mtime * cache used when the searching of cached directories fails to find * a file. In the past, Dir_FindFile would simply perform an access() * call in such a case to determine if the file could be found using * just the name given. When this hit, however, all that was gained * was the knowledge that the file existed. Given that an access() is * essentially a stat() without the copyout() call, and that the same * filesystem overhead would have to be incurred in Dir_MTime, it made * sense to replace the access() with a stat() and record the mtime * in a cache for when Dir_MTime was actually called. */ Lst dirSearchPath; /* main search path */ static Lst openDirectories; /* the list of all open directories */ /* * Variables for gathering statistics on the efficiency of the hashing * mechanism. */ static int hits, /* Found in directory cache */ misses, /* Sad, but not evil misses */ nearmisses, /* Found under search path */ bigmisses; /* Sought by itself */ typedef struct Path { char *name; /* Name of directory */ int refCount; /* Number of paths with this directory */ int hits; /* the number of times a file in this * directory has been found */ Hash_Table files; /* Hash table of files in directory */ } Path; static Path *dot; /* contents of current directory */ static Hash_Table mtimes; /* Results of doing a last-resort stat in * Dir_FindFile -- if we have to go to the * system to find the file, we might as well * have its mtime on record. XXX: If this is done * way early, there's a chance other rules will * have already updated the file, in which case * we'll update it again. Generally, there won't * be two rules to update a single file, so this * should be ok, but... */ /*- *----------------------------------------------------------------------- * Dir_Init -- * initialize things for this module * * Results: * none * * Side Effects: * some directories may be opened. *----------------------------------------------------------------------- */ void Dir_Init () { dirSearchPath = Lst_Init (FALSE); openDirectories = Lst_Init (FALSE); Hash_InitTable(&mtimes, 0); /* * Since the Path structure is placed on both openDirectories and * the path we give Dir_AddDir (which in this case is openDirectories), * we need to remove "." from openDirectories and what better time to * do it than when we have to fetch the thing anyway? */ Dir_AddDir (openDirectories, "."); dot = (Path *) Lst_DeQueue (openDirectories); /* * We always need to have dot around, so we increment its reference count * to make sure it's not destroyed. */ dot->refCount += 1; } /*- *----------------------------------------------------------------------- * DirFindName -- * See if the Path structure describes the same directory as the * given one by comparing their names. Called from Dir_AddDir via * Lst_Find when searching the list of open directories. * * Results: * 0 if it is the same. Non-zero otherwise * * Side Effects: * None *----------------------------------------------------------------------- */ static int DirFindName (p, dname) Path *p; /* Current name */ char *dname; /* Desired name */ { return (strcmp (p->name, dname)); } /*- *----------------------------------------------------------------------- * Dir_HasWildcards -- * see if the given name has any wildcard characters in it * * Results: * returns TRUE if the word should be expanded, FALSE otherwise * * Side Effects: * none *----------------------------------------------------------------------- */ Boolean Dir_HasWildcards (name) char *name; /* name to check */ { register char *cp; for (cp = name; *cp; cp++) { switch(*cp) { case '{': case '[': case '?': case '*': return (TRUE); } } return (FALSE); } /*- *----------------------------------------------------------------------- * DirMatchFiles -- * Given a pattern and a Path structure, see if any files * match the pattern and add their names to the 'expansions' list if * any do. This is incomplete -- it doesn't take care of patterns like * src/*src/*.c properly (just *.c on any of the directories), but it * will do for now. * * Results: * Always returns 0 * * Side Effects: * File names are added to the expansions lst. The directory will be * fully hashed when this is done. *----------------------------------------------------------------------- */ static int DirMatchFiles (pattern, p, expansions) char *pattern; /* Pattern to look for */ Path *p; /* Directory to search */ Lst expansions; /* Place to store the results */ { Hash_Search search; /* Index into the directory's table */ Hash_Entry *entry; /* Current entry in the table */ char *f; /* Current entry in the directory */ Boolean isDot; /* TRUE if the directory being searched is . */ isDot = (*p->name == '.' && p->name[1] == '\0'); for (entry = Hash_EnumFirst(&p->files, &search); entry != (Hash_Entry *)NULL; entry = Hash_EnumNext(&search)) { /* * See if the file matches the given pattern. Note we follow the UNIX * convention that dot files will only be found if the pattern * begins with a dot (note also that as a side effect of the hashing * scheme, .* won't match . or .. since they aren't hashed). */ if (Str_Match(entry->name, pattern) && ((entry->name[0] != '.') || (pattern[0] == '.'))) { (void)Lst_AtEnd(expansions, (isDot ? strdup(entry->name) : str_concat(p->name, entry->name, STR_ADDSLASH))); } } return (0); } /*- *----------------------------------------------------------------------- * DirExpandCurly -- * Expand curly braces like the C shell. Does this recursively. * Note the special case: if after the piece of the curly brace is * done there are no wildcard characters in the result, the result is * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. * * Results: * None. * * Side Effects: * The given list is filled with the expansions... * *----------------------------------------------------------------------- */ static void DirExpandCurly(word, brace, path, expansions) char *word; /* Entire word to expand */ char *brace; /* First curly brace in it */ Lst path; /* Search path to use */ Lst expansions; /* Place to store the expansions */ { char *end; /* Character after the closing brace */ char *cp; /* Current position in brace clause */ char *start; /* Start of current piece of brace clause */ int bracelevel; /* Number of braces we've seen. If we see a * right brace when this is 0, we've hit the * end of the clause. */ char *file; /* Current expansion */ int otherLen; /* The length of the other pieces of the * expansion (chars before and after the * clause in 'word') */ char *cp2; /* Pointer for checking for wildcards in * expansion before calling Dir_Expand */ start = brace+1; /* * Find the end of the brace clause first, being wary of nested brace * clauses. */ for (end = start, bracelevel = 0; *end != '\0'; end++) { if (*end == '{') { bracelevel++; } else if ((*end == '}') && (bracelevel-- == 0)) { break; } } if (*end == '\0') { Error("Unterminated {} clause \"%s\"", start); return; } else { end++; } otherLen = brace - word + strlen(end); for (cp = start; cp < end; cp++) { /* * Find the end of this piece of the clause. */ bracelevel = 0; while (*cp != ',') { if (*cp == '{') { bracelevel++; } else if ((*cp == '}') && (bracelevel-- <= 0)) { break; } cp++; } /* * Allocate room for the combination and install the three pieces. */ file = emalloc(otherLen + cp - start + 1); if (brace != word) { strncpy(file, word, brace-word); } if (cp != start) { strncpy(&file[brace-word], start, cp-start); } strcpy(&file[(brace-word)+(cp-start)], end); /* * See if the result has any wildcards in it. If we find one, call * Dir_Expand right away, telling it to place the result on our list * of expansions. */ for (cp2 = file; *cp2 != '\0'; cp2++) { switch(*cp2) { case '*': case '?': case '{': case '[': Dir_Expand(file, path, expansions); goto next; } } if (*cp2 == '\0') { /* * Hit the end w/o finding any wildcards, so stick the expansion * on the end of the list. */ (void)Lst_AtEnd(expansions, file); } else { next: free(file); } start = cp+1; } } /*- *----------------------------------------------------------------------- * DirExpandInt -- * Internal expand routine. Passes through the directories in the * path one by one, calling DirMatchFiles for each. NOTE: This still * doesn't handle patterns in directories... * * Results: * None. * * Side Effects: * Things are added to the expansions list. * *----------------------------------------------------------------------- */ static void DirExpandInt(word, path, expansions) char *word; /* Word to expand */ Lst path; /* Path on which to look */ Lst expansions; /* Place to store the result */ { LstNode ln; /* Current node */ Path *p; /* Directory in the node */ if (Lst_Open(path) == SUCCESS) { while ((ln = Lst_Next(path)) != NILLNODE) { p = (Path *)Lst_Datum(ln); DirMatchFiles(word, p, expansions); } Lst_Close(path); } } /*- *----------------------------------------------------------------------- * DirPrintWord -- * Print a word in the list of expansions. Callback for Dir_Expand * when DEBUG(DIR), via Lst_ForEach. * * Results: * === 0 * * Side Effects: * The passed word is printed, followed by a space. * *----------------------------------------------------------------------- */ static int DirPrintWord(word) char *word; { printf("%s ", word); return(0); } /*- *----------------------------------------------------------------------- * Dir_Expand -- * Expand the given word into a list of words by globbing it looking * in the directories on the given search path. * * Results: * A list of words consisting of the files which exist along the search * path matching the given pattern. * * Side Effects: * Directories may be opened. Who knows? *----------------------------------------------------------------------- */ void Dir_Expand (word, path, expansions) char *word; /* the word to expand */ Lst path; /* the list of directories in which to find * the resulting files */ Lst expansions; /* the list on which to place the results */ { char *cp; if (DEBUG(DIR)) { printf("expanding \"%s\"...", word); } cp = index(word, '{'); if (cp) { DirExpandCurly(word, cp, path, expansions); } else { cp = index(word, '/'); if (cp) { /* * The thing has a directory component -- find the first wildcard * in the string. */ for (cp = word; *cp; cp++) { if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { break; } } if (*cp == '{') { /* * This one will be fun. */ DirExpandCurly(word, cp, path, expansions); return; } else if (*cp != '\0') { /* * Back up to the start of the component */ char *dirpath; while (cp > word && *cp != '/') { cp--; } if (cp != word) { /* * If the glob isn't in the first component, try and find * all the components up to the one with a wildcard. */ *cp = '\0'; dirpath = Dir_FindFile(word, path); *cp = '/'; /* * dirpath is null if can't find the leading component * XXX: Dir_FindFile won't find internal components. * i.e. if the path contains ../Etc/Object and we're * looking for Etc, it won't be found. Ah well. * Probably not important. */ if (dirpath != (char *)NULL) { path = Lst_Init(FALSE); Dir_AddDir(path, dirpath); DirExpandInt(cp+1, path, expansions); Lst_Destroy(path, NOFREE); } } else { /* * Start the search from the local directory */ DirExpandInt(word, path, expansions); } } else { /* * Return the file -- this should never happen. */ DirExpandInt(word, path, expansions); } } else { /* * First the files in dot */ DirMatchFiles(word, dot, expansions); /* * Then the files in every other directory on the path. */ DirExpandInt(word, path, expansions); } } if (DEBUG(DIR)) { Lst_ForEach(expansions, DirPrintWord, NULL); putchar('\n'); } } /*- *----------------------------------------------------------------------- * Dir_FindFile -- * Find the file with the given name along the given search path. * * Results: * The path to the file or NULL. This path is guaranteed to be in a * different part of memory than name and so may be safely free'd. * * Side Effects: * If the file is found in a directory which is not on the path * already (either 'name' is absolute or it is a relative path * [ dir1/.../dirn/file ] which exists below one of the directories * already on the search path), its directory is added to the end * of the path on the assumption that there will be more files in * that directory later on. Sometimes this is true. Sometimes not. *----------------------------------------------------------------------- */ char * Dir_FindFile (name, path) char *name; /* the file to find */ Lst path; /* the Lst of directories to search */ { register char *p1; /* pointer into p->name */ register char *p2; /* pointer into name */ LstNode ln; /* a list element */ register char *file; /* the current filename to check */ register Path *p; /* current path member */ register char *cp; /* index of first slash, if any */ Boolean hasSlash; /* true if 'name' contains a / */ struct stat stb; /* Buffer for stat, if necessary */ Hash_Entry *entry; /* Entry for mtimes table */ /* * Find the final component of the name and note whether it has a * slash in it (the name, I mean) */ cp = rindex (name, '/'); if (cp) { hasSlash = TRUE; cp += 1; } else { hasSlash = FALSE; cp = name; } if (DEBUG(DIR)) { printf("Searching for %s...", name); } /* * No matter what, we always look for the file in the current directory * before anywhere else and we *do not* add the ./ to it if it exists. * This is so there are no conflicts between what the user specifies * (fish.c) and what pmake finds (./fish.c). */ if ((!hasSlash || (cp - name == 2 && *name == '.')) && (Hash_FindEntry (&dot->files, cp) != (Hash_Entry *)NULL)) { if (DEBUG(DIR)) { printf("in '.'\n"); } hits += 1; dot->hits += 1; return (strdup (name)); } if (Lst_Open (path) == FAILURE) { if (DEBUG(DIR)) { printf("couldn't open path, file not found\n"); } misses += 1; return ((char *) NULL); } /* * We look through all the directories on the path seeking one which * contains the final component of the given name and whose final * component(s) match the name's initial component(s). If such a beast * is found, we concatenate the directory name and the final component * and return the resulting string. If we don't find any such thing, * we go on to phase two... */ while ((ln = Lst_Next (path)) != NILLNODE) { p = (Path *) Lst_Datum (ln); if (DEBUG(DIR)) { printf("%s...", p->name); } if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { if (DEBUG(DIR)) { printf("here..."); } if (hasSlash) { /* * If the name had a slash, its initial components and p's * final components must match. This is false if a mismatch * is encountered before all of the initial components * have been checked (p2 > name at the end of the loop), or * we matched only part of one of the components of p * along with all the rest of them (*p1 != '/'). */ p1 = p->name + strlen (p->name) - 1; p2 = cp - 2; while (p2 >= name && *p1 == *p2) { p1 -= 1; p2 -= 1; } if (p2 >= name || (p1 >= p->name && *p1 != '/')) { if (DEBUG(DIR)) { printf("component mismatch -- continuing..."); } continue; } } file = str_concat (p->name, cp, STR_ADDSLASH); if (DEBUG(DIR)) { printf("returning %s\n", file); } Lst_Close (path); p->hits += 1; hits += 1; return (file); } else if (hasSlash) { /* * If the file has a leading path component and that component * exactly matches the entire name of the current search * directory, we assume the file doesn't exist and return NULL. */ for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { continue; } if (*p1 == '\0' && p2 == cp - 1) { if (DEBUG(DIR)) { printf("must be here but isn't -- returing NULL\n"); } Lst_Close (path); return ((char *) NULL); } } } /* * We didn't find the file on any existing members of the directory. * If the name doesn't contain a slash, that means it doesn't exist. * If it *does* contain a slash, however, there is still hope: it * could be in a subdirectory of one of the members of the search * path. (eg. /usr/include and sys/types.h. The above search would * fail to turn up types.h in /usr/include, but it *is* in * /usr/include/sys/types.h) If we find such a beast, we assume there * will be more (what else can we assume?) and add all but the last * component of the resulting name onto the search path (at the * end). This phase is only performed if the file is *not* absolute. */ if (!hasSlash) { if (DEBUG(DIR)) { printf("failed.\n"); } misses += 1; return ((char *) NULL); } if (*name != '/') { Boolean checkedDot = FALSE; if (DEBUG(DIR)) { printf("failed. Trying subdirectories..."); } (void) Lst_Open (path); while ((ln = Lst_Next (path)) != NILLNODE) { p = (Path *) Lst_Datum (ln); if (p != dot) { file = str_concat (p->name, name, STR_ADDSLASH); } else { /* * Checking in dot -- DON'T put a leading ./ on the thing. */ file = strdup(name); checkedDot = TRUE; } if (DEBUG(DIR)) { printf("checking %s...", file); } if (stat (file, &stb) == 0) { if (DEBUG(DIR)) { printf("got it.\n"); } Lst_Close (path); /* * We've found another directory to search. We know there's * a slash in 'file' because we put one there. We nuke it after * finding it and call Dir_AddDir to add this new directory * onto the existing search path. Once that's done, we restore * the slash and triumphantly return the file name, knowing * that should a file in this directory every be referenced * again in such a manner, we will find it without having to do * numerous numbers of access calls. Hurrah! */ cp = rindex (file, '/'); *cp = '\0'; Dir_AddDir (path, file); *cp = '/'; /* * Save the modification time so if it's needed, we don't have * to fetch it again. */ if (DEBUG(DIR)) { printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), file); } entry = Hash_CreateEntry(&mtimes, (ClientData)file, (Boolean *)NULL); Hash_SetValue(entry, stb.st_mtime); nearmisses += 1; return (file); } else { free (file); } } if (DEBUG(DIR)) { printf("failed. "); } Lst_Close (path); if (checkedDot) { /* * Already checked by the given name, since . was in the path, * so no point in proceeding... */ if (DEBUG(DIR)) { printf("Checked . already, returning NULL\n"); } return(NULL); } } /* * Didn't find it that way, either. Sigh. Phase 3. Add its directory * onto the search path in any case, just in case, then look for the * thing in the hash table. If we find it, grand. We return a new * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. * Note that if the directory holding the file doesn't exist, this will * do an extra search of the final directory on the path. Unless something * weird happens, this search won't succeed and life will be groovy. * * Sigh. We cannot add the directory onto the search path because * of this amusing case: * $(INSTALLDIR)/$(FILE): $(FILE) * * $(FILE) exists in $(INSTALLDIR) but not in the current one. * When searching for $(FILE), we will find it in $(INSTALLDIR) * b/c we added it here. This is not good... */ #ifdef notdef cp[-1] = '\0'; Dir_AddDir (path, name); cp[-1] = '/'; bigmisses += 1; ln = Lst_Last (path); if (ln == NILLNODE) { return ((char *) NULL); } else { p = (Path *) Lst_Datum (ln); } if (Hash_FindEntry (&p->files, cp) != (Hash_Entry *)NULL) { return (strdup (name)); } else { return ((char *) NULL); } #else /* !notdef */ if (DEBUG(DIR)) { printf("Looking for \"%s\"...", name); } bigmisses += 1; entry = Hash_FindEntry(&mtimes, name); if (entry != (Hash_Entry *)NULL) { if (DEBUG(DIR)) { printf("got it (in mtime cache)\n"); } return(strdup(name)); } else if (stat (name, &stb) == 0) { entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL); if (DEBUG(DIR)) { printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), name); } Hash_SetValue(entry, stb.st_mtime); return (strdup (name)); } else { if (DEBUG(DIR)) { printf("failed. Returning NULL\n"); } return ((char *)NULL); } #endif /* notdef */ } /*- *----------------------------------------------------------------------- * Dir_MTime -- * Find the modification time of the file described by gn along the * search path dirSearchPath. * * Results: * The modification time or 0 if it doesn't exist * * Side Effects: * The modification time is placed in the node's mtime slot. * If the node didn't have a path entry before, and Dir_FindFile * found one for it, the full name is placed in the path slot. *----------------------------------------------------------------------- */ int Dir_MTime (gn) GNode *gn; /* the file whose modification time is * desired */ { char *fullName; /* the full pathname of name */ struct stat stb; /* buffer for finding the mod time */ Hash_Entry *entry; if (gn->type & OP_ARCHV) { return Arch_MTime (gn); } else if (gn->path == (char *)NULL) { fullName = Dir_FindFile (gn->name, dirSearchPath); } else { fullName = gn->path; } if (fullName == (char *)NULL) { fullName = gn->name; } entry = Hash_FindEntry(&mtimes, fullName); if (entry != (Hash_Entry *)NULL) { /* * Only do this once -- the second time folks are checking to * see if the file was actually updated, so we need to actually go * to the file system. */ if (DEBUG(DIR)) { printf("Using cached time %s for %s\n", Targ_FmtTime(Hash_GetValue(entry)), fullName); } stb.st_mtime = (time_t)Hash_GetValue(entry); Hash_DeleteEntry(&mtimes, entry); } else if (stat (fullName, &stb) < 0) { if (gn->type & OP_MEMBER) { return Arch_MemMTime (gn); } else { stb.st_mtime = 0; } } if (fullName && gn->path == (char *)NULL) { gn->path = fullName; } gn->mtime = stb.st_mtime; return (gn->mtime); } /*- *----------------------------------------------------------------------- * Dir_AddDir -- * Add the given name to the end of the given path. The order of * the arguments is backwards so ParseDoDependency can do a * Lst_ForEach of its list of paths... * * Results: * none * * Side Effects: * A structure is added to the list and the directory is * read and hashed. *----------------------------------------------------------------------- */ void Dir_AddDir (path, name) Lst path; /* the path to which the directory should be * added */ char *name; /* the name of the directory to add */ { LstNode ln; /* node in case Path structure is found */ register Path *p; /* pointer to new Path structure */ DIR *d; /* for reading directory */ register struct direct *dp; /* entry in directory */ Hash_Entry *he; char *fName; ln = Lst_Find (openDirectories, (ClientData)name, DirFindName); if (ln != NILLNODE) { p = (Path *)Lst_Datum (ln); if (Lst_Member(path, (ClientData)p) == NILLNODE) { p->refCount += 1; (void)Lst_AtEnd (path, (ClientData)p); } } else { if (DEBUG(DIR)) { printf("Caching %s...", name); fflush(stdout); } if ((d = opendir (name)) != (DIR *) NULL) { p = (Path *) emalloc (sizeof (Path)); p->name = strdup (name); p->hits = 0; p->refCount = 1; Hash_InitTable (&p->files, -1); /* * Skip the first two entries -- these will *always* be . and .. */ (void)readdir(d); (void)readdir(d); while ((dp = readdir (d)) != (struct direct *) NULL) { #ifdef sun /* * The sun directory library doesn't check for a 0 inode * (0-inode slots just take up space), so we have to do * it ourselves. */ if (dp->d_fileno == 0) { continue; } #endif sun (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL); } (void) closedir (d); (void)Lst_AtEnd (openDirectories, (ClientData)p); (void)Lst_AtEnd (path, (ClientData)p); } if (DEBUG(DIR)) { printf("done\n"); } } } /*- *----------------------------------------------------------------------- * Dir_CopyDir -- * Callback function for duplicating a search path via Lst_Duplicate. * Ups the reference count for the directory. * * Results: * Returns the Path it was given. * * Side Effects: * The refCount of the path is incremented. * *----------------------------------------------------------------------- */ ClientData Dir_CopyDir(p) Path *p; /* Directory descriptor to copy */ { p->refCount += 1; return ((ClientData)p); } /*- *----------------------------------------------------------------------- * Dir_MakeFlags -- * Make a string by taking all the directories in the given search * path and preceding them by the given flag. Used by the suffix * module to create variables for compilers based on suffix search * paths. * * Results: * The string mentioned above. Note that there is no space between * the given flag and each directory. The empty string is returned if * Things don't go well. * * Side Effects: * None *----------------------------------------------------------------------- */ char * Dir_MakeFlags (flag, path) char *flag; /* flag which should precede each directory */ Lst path; /* list of directories */ { char *str; /* the string which will be returned */ char *tstr; /* the current directory preceded by 'flag' */ LstNode ln; /* the node of the current directory */ Path *p; /* the structure describing the current directory */ str = strdup (""); if (Lst_Open (path) == SUCCESS) { while ((ln = Lst_Next (path)) != NILLNODE) { p = (Path *) Lst_Datum (ln); tstr = str_concat (flag, p->name, 0); str = str_concat (str, tstr, STR_ADDSPACE | STR_DOFREE); } Lst_Close (path); } return (str); } /*- *----------------------------------------------------------------------- * Dir_Destroy -- * Nuke a directory descriptor, if possible. Callback procedure * for the suffixes module when destroying a search path. * * Results: * None. * * Side Effects: * If no other path references this directory (refCount == 0), * the Path and all its data are freed. * *----------------------------------------------------------------------- */ void Dir_Destroy (p) Path *p; /* The directory descriptor to nuke */ { Hash_Search thing1; Hash_Entry *thing2; p->refCount -= 1; if (p->refCount == 0) { LstNode ln; ln = Lst_Member (openDirectories, (ClientData)p); (void) Lst_Remove (openDirectories, ln); Hash_DeleteTable (&p->files); free((Address)p->name); free((Address)p); } } /*- *----------------------------------------------------------------------- * Dir_ClearPath -- * Clear out all elements of the given search path. This is different * from destroying the list, notice. * * Results: * None. * * Side Effects: * The path is set to the empty list. * *----------------------------------------------------------------------- */ void Dir_ClearPath(path) Lst path; /* Path to clear */ { Path *p; while (!Lst_IsEmpty(path)) { p = (Path *)Lst_DeQueue(path); Dir_Destroy(p); } } /*- *----------------------------------------------------------------------- * Dir_Concat -- * Concatenate two paths, adding the second to the end of the first. * Makes sure to avoid duplicates. * * Results: * None * * Side Effects: * Reference counts for added dirs are upped. * *----------------------------------------------------------------------- */ void Dir_Concat(path1, path2) Lst path1; /* Dest */ Lst path2; /* Source */ { LstNode ln; Path *p; for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) { p = (Path *)Lst_Datum(ln); if (Lst_Member(path1, (ClientData)p) == NILLNODE) { p->refCount += 1; (void)Lst_AtEnd(path1, (ClientData)p); } } } /********** DEBUG INFO **********/ Dir_PrintDirectories() { LstNode ln; Path *p; printf ("#*** Directory Cache:\n"); printf ("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", hits, misses, nearmisses, bigmisses, (hits+bigmisses+nearmisses ? hits * 100 / (hits + bigmisses + nearmisses) : 0)); printf ("# %-20s referenced\thits\n", "directory"); if (Lst_Open (openDirectories) == SUCCESS) { while ((ln = Lst_Next (openDirectories)) != NILLNODE) { p = (Path *) Lst_Datum (ln); printf ("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); } Lst_Close (openDirectories); } } static int DirPrintDir (p) Path *p; { printf ("%s ", p->name); return (0); } Dir_PrintPath (path) Lst path; { Lst_ForEach (path, DirPrintDir, (ClientData)0); } pmake/hash.c100600 1750 1750 24703 5431142061 11441 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)hash.c 5.5 (Berkeley) 12/28/90"; #endif /* not lint */ /* hash.c -- * * This module contains routines to manipulate a hash table. * See hash.h for a definition of the structure of the hash * table. Hash tables grow automatically as the amount of * information increases. */ #include "sprite.h" #include "hash.h" /* * Forward references to local procedures that are used before they're * defined: */ static void RebuildTable(); /* * The following defines the ratio of # entries to # buckets * at which we rebuild the table to make it larger. */ #define rebuildLimit 8 /* *--------------------------------------------------------- * * Hash_InitTable -- * * This routine just sets up the hash table. * * Results: * None. * * Side Effects: * Memory is allocated for the initial bucket area. * *--------------------------------------------------------- */ void Hash_InitTable(t, numBuckets) register Hash_Table *t; /* Structure to use to hold table. */ int numBuckets; /* How many buckets to create for starters. * This number is rounded up to a power of * two. If <= 0, a reasonable default is * chosen. The table will grow in size later * as needed. */ { register int i; register struct Hash_Entry **hp; /* * Round up the size to a power of two. */ if (numBuckets <= 0) i = 16; else { for (i = 2; i < numBuckets; i <<= 1) /* void */ ; } t->numEntries = 0; t->size = i; t->mask = i - 1; t->bucketPtr = hp = (struct Hash_Entry **)emalloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; } /* *--------------------------------------------------------- * * Hash_DeleteTable -- * * This routine removes everything from a hash table * and frees up the memory space it occupied (except for * the space in the Hash_Table structure). * * Results: * None. * * Side Effects: * Lots of memory is freed up. * *--------------------------------------------------------- */ void Hash_DeleteTable(t) Hash_Table *t; { register struct Hash_Entry **hp, *h, *nexth; register int i; for (hp = t->bucketPtr, i = t->size; --i >= 0;) { for (h = *hp++; h != NULL; h = nexth) { nexth = h->next; free((char *)h); } } free((char *)t->bucketPtr); /* * Set up the hash table to cause memory faults on any future access * attempts until re-initialization. */ t->bucketPtr = NULL; } /* *--------------------------------------------------------- * * Hash_FindEntry -- * * Searches a hash table for an entry corresponding to key. * * Results: * The return value is a pointer to the entry for key, * if key was present in the table. If key was not * present, NULL is returned. * * Side Effects: * None. * *--------------------------------------------------------- */ Hash_Entry * Hash_FindEntry(t, key) Hash_Table *t; /* Hash table to search. */ char *key; /* A hash key. */ { register Hash_Entry *e; register unsigned h; register char *p; for (h = 0, p = key; *p;) h = (h << 5) - h + *p++; p = key; for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) if (e->namehash == h && strcmp(e->name, p) == 0) return (e); return (NULL); } /* *--------------------------------------------------------- * * Hash_CreateEntry -- * * Searches a hash table for an entry corresponding to * key. If no entry is found, then one is created. * * Results: * The return value is a pointer to the entry. If *newPtr * isn't NULL, then *newPtr is filled in with TRUE if a * new entry was created, and FALSE if an entry already existed * with the given key. * * Side Effects: * Memory may be allocated, and the hash buckets may be modified. *--------------------------------------------------------- */ Hash_Entry * Hash_CreateEntry(t, key, newPtr) register Hash_Table *t; /* Hash table to search. */ char *key; /* A hash key. */ Boolean *newPtr; /* Filled in with TRUE if new entry created, * FALSE otherwise. */ { register Hash_Entry *e; register unsigned h; register char *p; int keylen; struct Hash_Entry **hp; /* * Hash the key. As a side effect, save the length (strlen) of the * key in case we need to create the entry. */ for (h = 0, p = key; *p;) h = (h << 5) - h + *p++; keylen = p - key; p = key; for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { if (e->namehash == h && strcmp(e->name, p) == 0) { if (newPtr != NULL) *newPtr = FALSE; return (e); } } /* * The desired entry isn't there. Before allocating a new entry, * expand the table if necessary (and this changes the resulting * bucket chain). */ if (t->numEntries >= rebuildLimit * t->size) RebuildTable(t); e = (Hash_Entry *) emalloc(sizeof(*e) + keylen); hp = &t->bucketPtr[h & t->mask]; e->next = *hp; *hp = e; e->clientData = NULL; e->namehash = h; (void) strcpy(e->name, p); t->numEntries++; if (newPtr != NULL) *newPtr = TRUE; return (e); } /* *--------------------------------------------------------- * * Hash_DeleteEntry -- * * Delete the given hash table entry and free memory associated with * it. * * Results: * None. * * Side Effects: * Hash chain that entry lives in is modified and memory is freed. * *--------------------------------------------------------- */ void Hash_DeleteEntry(t, e) Hash_Table *t; Hash_Entry *e; { register Hash_Entry **hp, *p; if (e == NULL) return; for (hp = &t->bucketPtr[e->namehash & t->mask]; (p = *hp) != NULL; hp = &p->next) { if (p == e) { *hp = p->next; free((char *)p); t->numEntries--; return; } } (void) write(2, "bad call to Hash_DeleteEntry\n", 29); abort(); } /* *--------------------------------------------------------- * * Hash_EnumFirst -- * This procedure sets things up for a complete search * of all entries recorded in the hash table. * * Results: * The return value is the address of the first entry in * the hash table, or NULL if the table is empty. * * Side Effects: * The information in searchPtr is initialized so that successive * calls to Hash_Next will return successive HashEntry's * from the table. * *--------------------------------------------------------- */ Hash_Entry * Hash_EnumFirst(t, searchPtr) Hash_Table *t; /* Table to be searched. */ register Hash_Search *searchPtr;/* Area in which to keep state * about search.*/ { searchPtr->tablePtr = t; searchPtr->nextIndex = 0; searchPtr->hashEntryPtr = NULL; return Hash_EnumNext(searchPtr); } /* *--------------------------------------------------------- * * Hash_EnumNext -- * This procedure returns successive entries in the hash table. * * Results: * The return value is a pointer to the next HashEntry * in the table, or NULL when the end of the table is * reached. * * Side Effects: * The information in searchPtr is modified to advance to the * next entry. * *--------------------------------------------------------- */ Hash_Entry * Hash_EnumNext(searchPtr) register Hash_Search *searchPtr; /* Area used to keep state about search. */ { register Hash_Entry *e; Hash_Table *t = searchPtr->tablePtr; /* * The hashEntryPtr field points to the most recently returned * entry, or is nil if we are starting up. If not nil, we have * to start at the next one in the chain. */ e = searchPtr->hashEntryPtr; if (e != NULL) e = e->next; /* * If the chain ran out, or if we are starting up, we need to * find the next nonempty chain. */ while (e == NULL) { if (searchPtr->nextIndex >= t->size) return (NULL); e = t->bucketPtr[searchPtr->nextIndex++]; } searchPtr->hashEntryPtr = e; return (e); } /* *--------------------------------------------------------- * * RebuildTable -- * This local routine makes a new hash table that * is larger than the old one. * * Results: * None. * * Side Effects: * The entire hash table is moved, so any bucket numbers * from the old table are invalid. * *--------------------------------------------------------- */ static void RebuildTable(t) register Hash_Table *t; { register Hash_Entry *e, *next, **hp, **xp; register int i, mask; register Hash_Entry **oldhp; int oldsize; oldhp = t->bucketPtr; oldsize = i = t->size; i <<= 1; t->size = i; t->mask = mask = i - 1; t->bucketPtr = hp = (struct Hash_Entry **) emalloc(sizeof(*hp) * i); while (--i >= 0) *hp++ = NULL; for (hp = oldhp, i = oldsize; --i >= 0;) { for (e = *hp++; e != NULL; e = next) { next = e->next; xp = &t->bucketPtr[e->namehash & mask]; e->next = *xp; *xp = e; } } free((char *)oldhp); } pmake/hash.h100600 1750 1750 10105 5431142061 11435 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)hash.h 5.4 (Berkeley) 12/28/90 */ /* hash.h -- * * This file contains definitions used by the hash module, * which maintains hash tables. */ #ifndef _HASH #define _HASH /* * The following defines one entry in the hash table. */ typedef struct Hash_Entry { struct Hash_Entry *next; /* Used to link together all the * entries associated with the same * bucket. */ ClientData clientData; /* Arbitrary piece of data associated * with key. */ unsigned namehash; /* hash value of key */ char name[1]; /* key string */ } Hash_Entry; typedef struct Hash_Table { struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one * for each bucket in the table. */ int size; /* Actual size of array. */ int numEntries; /* Number of entries in the table. */ int mask; /* Used to select bits for hashing. */ } Hash_Table; /* * The following structure is used by the searching routines * to record where we are in the search. */ typedef struct Hash_Search { Hash_Table *tablePtr; /* Table being searched. */ int nextIndex; /* Next bucket to check (after current). */ Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */ } Hash_Search; /* * Macros. */ /* * ClientData Hash_GetValue(h) * Hash_Entry *h; */ #define Hash_GetValue(h) ((h)->clientData) /* * Hash_SetValue(h, val); * Hash_Entry *h; * char *val; */ #define Hash_SetValue(h, val) ((h)->clientData = (ClientData) (val)) /* * Hash_Size(n) returns the number of words in an object of n bytes */ #define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) /* * The following procedure declarations and macros * are the only things that should be needed outside * the implementation code. */ extern Hash_Entry * Hash_CreateEntry(); extern void Hash_DeleteTable(); extern void Hash_DeleteEntry(); extern void Hash_DeleteTable(); extern Hash_Entry * Hash_EnumFirst(); extern Hash_Entry * Hash_EnumNext(); extern Hash_Entry * Hash_FindEntry(); extern void Hash_InitTable(); extern void Hash_PrintStats(); #endif _HASH pmake/job.c100600 1750 1750 224373 5431142064 11320 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)job.c 5.15 (Berkeley) 3/1/91"; #endif /* not lint */ /*- * job.c -- * handle the creation etc. of our child processes. * * Interface: * Job_Make Start the creation of the given target. * * Job_CatchChildren Check for and handle the termination of any * children. This must be called reasonably * frequently to keep the whole make going at * a decent clip, since job table entries aren't * removed until their process is caught this way. * Its single argument is TRUE if the function * should block waiting for a child to terminate. * * Job_CatchOutput Print any output our children have produced. * Should also be called fairly frequently to * keep the user informed of what's going on. * If no output is waiting, it will block for * a time given by the SEL_* constants, below, * or until output is ready. * * Job_Init Called to intialize this module. in addition, * any commands attached to the .BEGIN target * are executed before this function returns. * Hence, the makefile must have been parsed * before this function is called. * * Job_Full Return TRUE if the job table is filled. * * Job_Empty Return TRUE if the job table is completely * empty. * * Job_ParseShell Given the line following a .SHELL target, parse * the line as a shell specification. Returns * FAILURE if the spec was incorrect. * * Job_End Perform any final processing which needs doing. * This includes the execution of any commands * which have been/were attached to the .END * target. It should only be called when the * job table is empty. * * Job_AbortAll Abort all currently running jobs. It doesn't * handle output or do anything for the jobs, * just kills them. It should only be called in * an emergency, as it were. * * Job_CheckCommands Verify that the commands for a target are * ok. Provide them if necessary and possible. * * Job_Touch Update a target without really updating it. * * Job_Wait Wait for all currently-running jobs to finish. */ #include "make.h" #include #include #include #include #include #include #include #include #include #include "job.h" #include "pathnames.h" extern int errno; /* * error handling variables */ int errors = 0; /* number of errors reported */ int aborting = 0; /* why is the make aborting? */ #define ABORT_ERROR 1 /* Because of an error */ #define ABORT_INTERRUPT 2 /* Because it was interrupted */ #define ABORT_WAIT 3 /* Waiting for jobs to finish */ /* * post-make command processing. The node postCommands is really just the * .END target but we keep it around to avoid having to search for it * all the time. */ static GNode *postCommands; /* node containing commands to execute when * everything else is done */ static int numCommands; /* The number of commands actually printed * for a target. Should this number be * 0, no shell will be executed. */ /* * Return values from JobStart. */ #define JOB_RUNNING 0 /* Job is running */ #define JOB_ERROR 1 /* Error in starting the job */ #define JOB_FINISHED 2 /* The job is already finished */ #define JOB_STOPPED 3 /* The job is stopped */ /* * tfile is the name of a file into which all shell commands are put. It is * used over by removing it before the child shell is executed. The XXXXX in * the string are replaced by the pid of the make process in a 5-character * field with leading zeroes. */ static char tfile[] = TMPPAT; /* * Descriptions for various shells. */ static Shell shells[] = { /* * CSH description. The csh can do echo control by playing * with the setting of the 'echo' shell variable. Sadly, * however, it is unable to do error control nicely. */ { "csh", TRUE, "unset verbose", "set verbose", "unset verbose", 10, FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"", "v", "e", }, /* * SH description. Echo control is also possible and, under * sun UNIX anyway, one can even control error checking. */ { "sh", TRUE, "set -", "set -v", "set -", 5, FALSE, "echo \"%s\"\n", "sh -c '%s || exit 0'\n", "v", "e", }, /* * UNKNOWN. */ { (char *)0, FALSE, (char *)0, (char *)0, (char *)0, 0, FALSE, (char *)0, (char *)0, (char *)0, (char *)0, } }; Shell *commandShell = &shells[DEFSHELL]; /* this is the shell to * which we pass all * commands in the Makefile. * It is set by the * Job_ParseShell function */ char *shellPath = (char *) NULL, /* full pathname of * executable image */ *shellName; /* last component of shell */ static int maxJobs; /* The most children we can run at once */ static int maxLocal; /* The most local ones we can have */ int nJobs; /* The number of children currently running */ int nLocal; /* The number of local children */ Lst jobs; /* The structures that describe them */ Boolean jobFull; /* Flag to tell when the job table is full. It * is set TRUE when (1) the total number of * running jobs equals the maximum allowed or * (2) a job can only be run locally, but * nLocal equals maxLocal */ #ifndef RMT_WILL_WATCH static fd_set outputs; /* Set of descriptors of pipes connected to * the output channels of children */ #endif GNode *lastNode; /* The node for which output was most recently * produced. */ char *targFmt; /* Format string to use to head output from a * job when it's not the most-recent job heard * from */ #define TARG_FMT "--- %s ---\n" /* Default format */ /* * When JobStart attempts to run a job remotely but can't, and isn't allowed * to run the job locally, or when Job_CatchChildren detects a job that has * been migrated home, the job is placed on the stoppedJobs queue to be run * when the next job finishes. */ Lst stoppedJobs; /* Lst of Job structures describing * jobs that were stopped due to concurrency * limits or migration home */ # if defined(USE_PGRP) #define KILL(pid,sig) killpg((pid),(sig)) # else #define KILL(pid,sig) kill((pid),(sig)) # endif static void JobRestart(); static int JobStart(); static void JobInterrupt(); /*- *----------------------------------------------------------------------- * JobCondPassSig -- * Pass a signal to a job if the job is remote or if USE_PGRP * is defined. * * Results: * === 0 * * Side Effects: * None, except the job may bite it. * *----------------------------------------------------------------------- */ static int JobCondPassSig(job, signo) Job *job; /* Job to biff */ int signo; /* Signal to send it */ { #ifdef RMT_WANTS_SIGNALS if (job->flags & JOB_REMOTE) { (void)Rmt_Signal(job, signo); } else { KILL(job->pid, signo); } #else /* * Assume that sending the signal to job->pid will signal any remote * job as well. */ KILL(job->pid, signo); #endif return(0); } /*- *----------------------------------------------------------------------- * JobPassSig -- * Pass a signal on to all remote jobs and to all local jobs if * USE_PGRP is defined, then die ourselves. * * Results: * None. * * Side Effects: * We die by the same signal. * *----------------------------------------------------------------------- */ static void JobPassSig(signo) int signo; /* The signal number we've received */ { int mask; Lst_ForEach(jobs, JobCondPassSig, (ClientData)signo); /* * Deal with proper cleanup based on the signal received. We only run * the .INTERRUPT target if the signal was in fact an interrupt. The other * three termination signals are more of a "get out *now*" command. */ if (signo == SIGINT) { JobInterrupt(TRUE); } else if ((signo == SIGHUP) || (signo == SIGTERM) || (signo == SIGQUIT)) { JobInterrupt(FALSE); } /* * Leave gracefully if SIGQUIT, rather than core dumping. */ if (signo == SIGQUIT) { Finish(); } /* * Send ourselves the signal now we've given the message to everyone else. * Note we block everything else possible while we're getting the signal. * This ensures that all our jobs get continued when we wake up before * we take any other signal. */ mask = sigblock(0); (void) sigsetmask(~0 & ~(1 << (signo-1))); signal(signo, SIG_DFL); kill(getpid(), signo); Lst_ForEach(jobs, JobCondPassSig, (ClientData)SIGCONT); sigsetmask(mask); signal(signo, JobPassSig); } /*- *----------------------------------------------------------------------- * JobCmpPid -- * Compare the pid of the job with the given pid and return 0 if they * are equal. This function is called from Job_CatchChildren via * Lst_Find to find the job descriptor of the finished job. * * Results: * 0 if the pid's match * * Side Effects: * None *----------------------------------------------------------------------- */ static int JobCmpPid (job, pid) int pid; /* process id desired */ Job *job; /* job to examine */ { return (pid - job->pid); } /*- *----------------------------------------------------------------------- * JobPrintCommand -- * Put out another command for the given job. If the command starts * with an @ or a - we process it specially. In the former case, * so long as the -s and -n flags weren't given to make, we stick * a shell-specific echoOff command in the script. In the latter, * we ignore errors for the entire job, unless the shell has error * control. * If the command is just "..." we take all future commands for this * job to be commands to be executed once the entire graph has been * made and return non-zero to signal that the end of the commands * was reached. These commands are later attached to the postCommands * node and executed by Job_End when all things are done. * This function is called from JobStart via Lst_ForEach. * * Results: * Always 0, unless the command was "..." * * Side Effects: * If the command begins with a '-' and the shell has no error control, * the JOB_IGNERR flag is set in the job descriptor. * If the command is "..." and we're not ignoring such things, * tailCmds is set to the successor node of the cmd. * numCommands is incremented if the command is actually printed. *----------------------------------------------------------------------- */ static int JobPrintCommand (cmd, job) char *cmd; /* command string to print */ Job *job; /* job for which to print it */ { Boolean noSpecials; /* true if we shouldn't worry about * inserting special commands into * the input stream. */ Boolean shutUp = FALSE; /* true if we put a no echo command * into the command file */ Boolean errOff = FALSE; /* true if we turned error checking * off before printing the command * and need to turn it back on */ char *cmdTemplate; /* Template to use when printing the * command */ char *cmdStart; /* Start of expanded command */ LstNode cmdNode; /* Node for replacing the command */ noSpecials = (noExecute && ! (job->node->type & OP_MAKE)); if (strcmp (cmd, "...") == 0) { if ((job->flags & JOB_IGNDOTS) == 0) { job->tailCmds = Lst_Succ (Lst_Member (job->node->commands, (ClientData)cmd)); return (1); } return (0); } #define DBPRINTF(fmt, arg) if (DEBUG(JOB)) printf (fmt, arg); fprintf (job->cmdFILE, fmt, arg) numCommands += 1; /* * For debugging, we replace each command with the result of expanding * the variables in the command. */ cmdNode = Lst_Member (job->node->commands, (ClientData)cmd); cmdStart = cmd = Var_Subst (cmd, job->node, FALSE); Lst_Replace (cmdNode, (ClientData)cmdStart); cmdTemplate = "%s\n"; /* * Check for leading @' and -'s to control echoing and error checking. */ while (*cmd == '@' || *cmd == '-') { if (*cmd == '@') { shutUp = TRUE; } else { errOff = TRUE; } cmd++; } if (shutUp) { if (! (job->flags & JOB_SILENT) && !noSpecials && commandShell->hasEchoCtl) { DBPRINTF ("%s\n", commandShell->echoOff); } else { shutUp = FALSE; } } if (errOff) { if ( ! (job->flags & JOB_IGNERR) && !noSpecials) { if (commandShell->hasErrCtl) { /* * we don't want the error-control commands showing * up either, so we turn off echoing while executing * them. We could put another field in the shell * structure to tell JobDoOutput to look for this * string too, but why make it any more complex than * it already is? */ if (! (job->flags & JOB_SILENT) && !shutUp && commandShell->hasEchoCtl) { DBPRINTF ("%s\n", commandShell->echoOff); DBPRINTF ("%s\n", commandShell->ignErr); DBPRINTF ("%s\n", commandShell->echoOn); } else { DBPRINTF ("%s\n", commandShell->ignErr); } } else if (commandShell->ignErr && (*commandShell->ignErr != '\0')) { /* * The shell has no error control, so we need to be * weird to get it to ignore any errors from the command. * If echoing is turned on, we turn it off and use the * errCheck template to echo the command. Leave echoing * off so the user doesn't see the weirdness we go through * to ignore errors. Set cmdTemplate to use the weirdness * instead of the simple "%s\n" template. */ if (! (job->flags & JOB_SILENT) && !shutUp && commandShell->hasEchoCtl) { DBPRINTF ("%s\n", commandShell->echoOff); DBPRINTF (commandShell->errCheck, cmd); shutUp = TRUE; } cmdTemplate = commandShell->ignErr; /* * The error ignoration (hee hee) is already taken care * of by the ignErr template, so pretend error checking * is still on. */ errOff = FALSE; } else { errOff = FALSE; } } else { errOff = FALSE; } } DBPRINTF (cmdTemplate, cmd); if (errOff) { /* * If echoing is already off, there's no point in issuing the * echoOff command. Otherwise we issue it and pretend it was on * for the whole command... */ if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ DBPRINTF ("%s\n", commandShell->echoOff); shutUp = TRUE; } DBPRINTF ("%s\n", commandShell->errCheck); } if (shutUp) { DBPRINTF ("%s\n", commandShell->echoOn); } return (0); } /*- *----------------------------------------------------------------------- * JobSaveCommand -- * Save a command to be executed when everything else is done. * Callback function for JobFinish... * * Results: * Always returns 0 * * Side Effects: * The command is tacked onto the end of postCommands's commands list. * *----------------------------------------------------------------------- */ static int JobSaveCommand (cmd, gn) char *cmd; GNode *gn; { cmd = Var_Subst (cmd, gn, FALSE); (void)Lst_AtEnd (postCommands->commands, (ClientData)cmd); return (0); } /*- *----------------------------------------------------------------------- * JobFinish -- * Do final processing for the given job including updating * parents and starting new jobs as available/necessary. Note * that we pay no attention to the JOB_IGNERR flag here. * This is because when we're called because of a noexecute flag * or something, jstat.w_status is 0 and when called from * Job_CatchChildren, the status is zeroed if it s/b ignored. * * Results: * None * * Side Effects: * Some nodes may be put on the toBeMade queue. * Final commands for the job are placed on postCommands. * * If we got an error and are aborting (aborting == ABORT_ERROR) and * the job list is now empty, we are done for the day. * If we recognized an error (errors !=0), we set the aborting flag * to ABORT_ERROR so no more jobs will be started. *----------------------------------------------------------------------- */ /*ARGSUSED*/ void JobFinish (job, status) Job *job; /* job to finish */ union wait status; /* sub-why job went away */ { Boolean done; if ((WIFEXITED(status) && (((status.w_retcode != 0) && !(job->flags & JOB_IGNERR)))) || (WIFSIGNALED(status) && (status.w_termsig != SIGCONT))) { /* * If it exited non-zero and either we're doing things our * way or we're not ignoring errors, the job is finished. * Similarly, if the shell died because of a signal * the job is also finished. In these * cases, finish out the job's output before printing the exit * status... */ if (usePipes) { #ifdef RMT_WILL_WATCH Rmt_Ignore(job->inPipe); #else FD_CLR(job->inPipe, &outputs); #endif /* RMT_WILL_WATCH */ if (job->outPipe != job->inPipe) { (void)close (job->outPipe); } JobDoOutput (job, TRUE); (void)close (job->inPipe); } else { (void)close (job->outFd); JobDoOutput (job, TRUE); } if (job->cmdFILE != NULL && job->cmdFILE != stdout) { fclose(job->cmdFILE); } done = TRUE; } else if (WIFEXITED(status) && status.w_retcode != 0) { /* * Deal with ignored errors in -B mode. We need to print a message * telling of the ignored error as well as setting status.w_status * to 0 so the next command gets run. To do this, we set done to be * TRUE if in -B mode and the job exited non-zero. Note we don't * want to close down any of the streams until we know we're at the * end. */ done = TRUE; } else { /* * No need to close things down or anything. */ done = FALSE; } if (done || WIFSTOPPED(status) || (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) || DEBUG(JOB)) { FILE *out; if (!usePipes && (job->flags & JOB_IGNERR)) { /* * If output is going to a file and this job is ignoring * errors, arrange to have the exit status sent to the * output file as well. */ out = fdopen (job->outFd, "w"); } else { out = stdout; } if (WIFEXITED(status)) { if (status.w_retcode != 0) { if (usePipes && job->node != lastNode) { fprintf (out, targFmt, job->node->name); lastNode = job->node; } fprintf (out, "*** Error code %d%s\n", status.w_retcode, (job->flags & JOB_IGNERR) ? " (ignored)" : ""); if (job->flags & JOB_IGNERR) { status.w_status = 0; } } else if (DEBUG(JOB)) { if (usePipes && job->node != lastNode) { fprintf (out, targFmt, job->node->name); lastNode = job->node; } fprintf (out, "*** Completed successfully\n"); } } else if (WIFSTOPPED(status)) { if (usePipes && job->node != lastNode) { fprintf (out, targFmt, job->node->name); lastNode = job->node; } if (! (job->flags & JOB_REMIGRATE)) { fprintf (out, "*** Stopped -- signal %d\n", status.w_stopsig); } job->flags |= JOB_RESUME; (void)Lst_AtEnd(stoppedJobs, (ClientData)job); fflush(out); return; } else if (status.w_termsig == SIGCONT) { /* * If the beastie has continued, shift the Job from the stopped * list to the running one (or re-stop it if concurrency is * exceeded) and go and get another child. */ if (job->flags & (JOB_RESUME|JOB_REMIGRATE|JOB_RESTART)) { if (usePipes && job->node != lastNode) { fprintf (out, targFmt, job->node->name); lastNode = job->node; } fprintf (out, "*** Continued\n"); } if (! (job->flags & JOB_CONTINUING)) { JobRestart(job); } else { Lst_AtEnd(jobs, (ClientData)job); nJobs += 1; if (! (job->flags & JOB_REMOTE)) { nLocal += 1; } if (nJobs == maxJobs) { jobFull = TRUE; if (DEBUG(JOB)) { printf("Job queue is full.\n"); } } } fflush(out); return; } else { if (usePipes && job->node != lastNode) { fprintf (out, targFmt, job->node->name); lastNode = job->node; } fprintf (out, "*** Signal %d\n", status.w_termsig); } fflush (out); } /* * Now handle the -B-mode stuff. If the beast still isn't finished, * try and restart the job on the next command. If JobStart says it's * ok, it's ok. If there's an error, this puppy is done. */ if ((status.w_status == 0) && !Lst_IsAtEnd (job->node->commands)) { switch (JobStart (job->node, job->flags & JOB_IGNDOTS, job)) { case JOB_RUNNING: done = FALSE; break; case JOB_ERROR: done = TRUE; status.w_retcode = 1; break; case JOB_FINISHED: /* * If we got back a JOB_FINISHED code, JobStart has already * called Make_Update and freed the job descriptor. We set * done to false here to avoid fake cycles and double frees. * JobStart needs to do the update so we can proceed up the * graph when given the -n flag.. */ done = FALSE; break; } } else { done = TRUE; } if (done && (aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (status.w_status == 0)) { /* * As long as we aren't aborting and the job didn't return a non-zero * status that we shouldn't ignore, we call Make_Update to update * the parents. In addition, any saved commands for the node are placed * on the .END target. */ if (job->tailCmds != NILLNODE) { Lst_ForEachFrom (job->node->commands, job->tailCmds, JobSaveCommand, (ClientData)job->node); } job->node->made = MADE; Make_Update (job->node); free((Address)job); } else if (status.w_status) { errors += 1; free((Address)job); } while (!errors && !jobFull && !Lst_IsEmpty(stoppedJobs)) { JobRestart((Job *)Lst_DeQueue(stoppedJobs)); } /* * Set aborting if any error. */ if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { /* * If we found any errors in this batch of children and the -k flag * wasn't given, we set the aborting flag so no more jobs get * started. */ aborting = ABORT_ERROR; } if ((aborting == ABORT_ERROR) && Job_Empty()) { /* * If we are aborting and the job table is now empty, we finish. */ (void) unlink (tfile); Finish (errors); } } /*- *----------------------------------------------------------------------- * Job_Touch -- * Touch the given target. Called by JobStart when the -t flag was * given * * Results: * None * * Side Effects: * The data modification of the file is changed. In addition, if the * file did not exist, it is created. *----------------------------------------------------------------------- */ void Job_Touch (gn, silent) GNode *gn; /* the node of the file to touch */ Boolean silent; /* TRUE if should not print messages */ { int streamID; /* ID of stream opened to do the touch */ struct timeval times[2]; /* Times for utimes() call */ struct stat attr; /* Attributes of the file */ if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) { /* * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets * and, as such, shouldn't really be created. */ return; } if (!silent) { printf ("touch %s\n", gn->name); } if (noExecute) { return; } if (gn->type & OP_ARCHV) { Arch_Touch (gn); } else if (gn->type & OP_LIB) { Arch_TouchLib (gn); } else { char *file = gn->path ? gn->path : gn->name; times[0].tv_sec = times[1].tv_sec = now; times[0].tv_usec = times[1].tv_usec = 0; if (utimes(file, times) < 0){ streamID = open (file, O_RDWR | O_CREAT, 0666); if (streamID >= 0) { char c; /* * Read and write a byte to the file to change the * modification time, then close the file. */ if (read(streamID, &c, 1) == 1) { lseek(streamID, 0L, L_SET); write(streamID, &c, 1); } (void)close (streamID); } else printf("*** couldn't touch %s: %s", file, strerror(errno)); } } } /*- *----------------------------------------------------------------------- * Job_CheckCommands -- * Make sure the given node has all the commands it needs. * * Results: * TRUE if the commands list is/was ok. * * Side Effects: * The node will have commands from the .DEFAULT rule added to it * if it needs them. *----------------------------------------------------------------------- */ Boolean Job_CheckCommands (gn, abortProc) GNode *gn; /* The target whose commands need * verifying */ void (*abortProc)(); /* Function to abort with message */ { if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) && (gn->type & OP_LIB) == 0) { /* * No commands. Look for .DEFAULT rule from which we might infer * commands */ if ((DEFAULT != NILGNODE) && !Lst_IsEmpty(DEFAULT->commands)) { /* * Make only looks for a .DEFAULT if the node was never the * target of an operator, so that's what we do too. If * a .DEFAULT was given, we substitute its commands for gn's * commands and set the IMPSRC variable to be the target's name * The DEFAULT node acts like a transformation rule, in that * gn also inherits any attributes or sources attached to * .DEFAULT itself. */ Make_HandleUse(DEFAULT, gn); Var_Set (IMPSRC, Var_Value (TARGET, gn), gn); } else if (Dir_MTime (gn) == 0) { /* * The node wasn't the target of an operator we have no .DEFAULT * rule to go on and the target doesn't already exist. There's * nothing more we can do for this branch. If the -k flag wasn't * given, we stop in our tracks, otherwise we just don't update * this node's parents so they never get examined. */ if (gn->type & OP_OPTIONAL) { printf ("make: don't know how to make %s (ignored)\n", gn->name); } else if (keepgoing) { printf ("make: don't know how to make %s (continuing)\n", gn->name); return (FALSE); } else { (*abortProc) ("make: don't know how to make %s. Stop", gn->name); return(FALSE); } } } return (TRUE); } #ifdef RMT_WILL_WATCH /*- *----------------------------------------------------------------------- * JobLocalInput -- * Handle a pipe becoming readable. Callback function for Rmt_Watch * * Results: * None * * Side Effects: * JobDoOutput is called. * *----------------------------------------------------------------------- */ /*ARGSUSED*/ static void JobLocalInput(stream, job) int stream; /* Stream that's ready (ignored) */ Job *job; /* Job to which the stream belongs */ { JobDoOutput(job, FALSE); } #endif /* RMT_WILL_WATCH */ /*- *----------------------------------------------------------------------- * JobExec -- * Execute the shell for the given job. Called from JobStart and * JobRestart. * * Results: * None. * * Side Effects: * A shell is executed, outputs is altered and the Job structure added * to the job table. * *----------------------------------------------------------------------- */ static void JobExec(job, argv) Job *job; /* Job to execute */ char **argv; { int cpid; /* ID of new child */ if (DEBUG(JOB)) { int i; printf("Running %s %sly\n", job->node->name, job->flags&JOB_REMOTE?"remote":"local"); printf("\tCommand: "); for (i = 0; argv[i] != (char *)NULL; i++) { printf("%s ", argv[i]); } printf("\n"); } /* * Some jobs produce no output and it's disconcerting to have * no feedback of their running (since they produce no output, the * banner with their name in it never appears). This is an attempt to * provide that feedback, even if nothing follows it. */ if ((lastNode != job->node) && (job->flags & JOB_FIRST) && !(job->flags & JOB_SILENT)) { printf(targFmt, job->node->name); lastNode = job->node; } #ifdef RMT_NO_EXEC if (job->flags & JOB_REMOTE) { goto jobExecFinish; } #endif /* RMT_NO_EXEC */ if ((cpid = vfork()) == -1) { Punt ("Cannot fork"); } else if (cpid == 0) { /* * Must duplicate the input stream down to the child's input and * reset it to the beginning (again). Since the stream was marked * close-on-exec, we must clear that bit in the new input. */ (void) dup2(fileno(job->cmdFILE), 0); fcntl(0, F_SETFD, 0); lseek(0, 0, L_SET); if (usePipes) { /* * Set up the child's output to be routed through the pipe * we've created for it. */ (void) dup2 (job->outPipe, 1); } else { /* * We're capturing output in a file, so we duplicate the * descriptor to the temporary file into the standard * output. */ (void) dup2 (job->outFd, 1); } /* * The output channels are marked close on exec. This bit was * duplicated by the dup2 (on some systems), so we have to clear * it before routing the shell's error output to the same place as * its standard output. */ fcntl(1, F_SETFD, 0); (void) dup2 (1, 2); #ifdef USE_PGRP /* * We want to switch the child into a different process family so * we can kill it and all its descendants in one fell swoop, * by killing its process family, but not commit suicide. */ (void) setpgrp(0, getpid()); #endif USE_PGRP (void) execv (shellPath, argv); (void) write (2, "Could not execute shell\n", sizeof ("Could not execute shell")); _exit (1); } else { job->pid = cpid; if (usePipes && (job->flags & JOB_FIRST) ) { /* * The first time a job is run for a node, we set the current * position in the buffer to the beginning and mark another * stream to watch in the outputs mask */ job->curPos = 0; #ifdef RMT_WILL_WATCH Rmt_Watch(job->inPipe, JobLocalInput, job); #else FD_SET(job->inPipe, &outputs); #endif /* RMT_WILL_WATCH */ } if (job->flags & JOB_REMOTE) { job->rmtID = (char *)0; } else { nLocal += 1; /* * XXX: Used to not happen if CUSTOMS. Why? */ if (job->cmdFILE != stdout) { fclose(job->cmdFILE); job->cmdFILE = NULL; } } } jobExecFinish: /* * Now the job is actually running, add it to the table. */ nJobs += 1; (void)Lst_AtEnd (jobs, (ClientData)job); if (nJobs == maxJobs) { jobFull = TRUE; } } /*- *----------------------------------------------------------------------- * JobMakeArgv -- * Create the argv needed to execute the shell for a given job. * * * Results: * * Side Effects: * *----------------------------------------------------------------------- */ static void JobMakeArgv(job, argv) Job *job; char **argv; { int argc; static char args[10]; /* For merged arguments */ argv[0] = shellName; argc = 1; if ((commandShell->exit && (*commandShell->exit != '-')) || (commandShell->echo && (*commandShell->echo != '-'))) { /* * At least one of the flags doesn't have a minus before it, so * merge them together. Have to do this because the *(&(@*#*&#$# * Bourne shell thinks its second argument is a file to source. * Grrrr. Note the ten-character limitation on the combined arguments. */ (void)sprintf(args, "-%s%s", ((job->flags & JOB_IGNERR) ? "" : (commandShell->exit ? commandShell->exit : "")), ((job->flags & JOB_SILENT) ? "" : (commandShell->echo ? commandShell->echo : ""))); if (args[1]) { argv[argc] = args; argc++; } } else { if (!(job->flags & JOB_IGNERR) && commandShell->exit) { argv[argc] = commandShell->exit; argc++; } if (!(job->flags & JOB_SILENT) && commandShell->echo) { argv[argc] = commandShell->echo; argc++; } } argv[argc] = (char *)NULL; } /*- *----------------------------------------------------------------------- * JobRestart -- * Restart a job that stopped for some reason. * * Results: * None. * * Side Effects: * jobFull will be set if the job couldn't be run. * *----------------------------------------------------------------------- */ static void JobRestart(job) Job *job; /* Job to restart */ { if (job->flags & JOB_REMIGRATE) { if (DEBUG(JOB)) { printf("Remigrating %x\n", job->pid); } if (nLocal != maxLocal) { /* * Job cannot be remigrated, but there's room on the local * machine, so resume the job and note that another * local job has started. */ if (DEBUG(JOB)) { printf("resuming on local machine\n"); } KILL(job->pid, SIGCONT); nLocal +=1; job->flags &= ~(JOB_REMIGRATE|JOB_RESUME); } else { /* * Job cannot be restarted. Mark the table as full and * place the job back on the list of stopped jobs. */ if (DEBUG(JOB)) { printf("holding\n"); } (void)Lst_AtFront(stoppedJobs, (ClientData)job); jobFull = TRUE; if (DEBUG(JOB)) { printf("Job queue is full.\n"); } return; } (void)Lst_AtEnd(jobs, (ClientData)job); nJobs += 1; if (nJobs == maxJobs) { jobFull = TRUE; if (DEBUG(JOB)) { printf("Job queue is full.\n"); } } } else if (job->flags & JOB_RESTART) { /* * Set up the control arguments to the shell. This is based on the * flags set earlier for this job. If the JOB_IGNERR flag is clear, * the 'exit' flag of the commandShell is used to cause it to exit * upon receiving an error. If the JOB_SILENT flag is clear, the * 'echo' flag of the commandShell is used to get it to start echoing * as soon as it starts processing commands. */ char *argv[4]; JobMakeArgv(job, argv); if (DEBUG(JOB)) { printf("Restarting %s...", job->node->name); } if (((nLocal >= maxLocal) && ! (job->flags & JOB_SPECIAL))) { /* * Can't be exported and not allowed to run locally -- put it * back on the hold queue and mark the table full */ if (DEBUG(JOB)) { printf("holding\n"); } (void)Lst_AtFront(stoppedJobs, (ClientData)job); jobFull = TRUE; if (DEBUG(JOB)) { printf("Job queue is full.\n"); } return; } else { /* * Job may be run locally. */ if (DEBUG(JOB)) { printf("running locally\n"); } job->flags &= ~JOB_REMOTE; } JobExec(job, argv); } else { /* * The job has stopped and needs to be restarted. Why it stopped, * we don't know... */ if (DEBUG(JOB)) { printf("Resuming %s...", job->node->name); } if (((job->flags & JOB_REMOTE) || (nLocal < maxLocal) || (((job->flags & JOB_SPECIAL)) && (maxLocal == 0))) && (nJobs != maxJobs)) { /* * If the job is remote, it's ok to resume it as long as the * maximum concurrency won't be exceeded. If it's local and * we haven't reached the local concurrency limit already (or the * job must be run locally and maxLocal is 0), it's also ok to * resume it. */ Boolean error; extern int errno; union wait status; #ifdef RMT_WANTS_SIGNALS if (job->flags & JOB_REMOTE) { error = !Rmt_Signal(job, SIGCONT); } else #endif /* RMT_WANTS_SIGNALS */ error = (KILL(job->pid, SIGCONT) != 0); if (!error) { /* * Make sure the user knows we've continued the beast and * actually put the thing in the job table. */ job->flags |= JOB_CONTINUING; status.w_termsig = SIGCONT; JobFinish(job, status); job->flags &= ~(JOB_RESUME|JOB_CONTINUING); if (DEBUG(JOB)) { printf("done\n"); } } else { Error("couldn't resume %s: %s", job->node->name, strerror(errno)); status.w_status = 0; status.w_retcode = 1; JobFinish(job, status); } } else { /* * Job cannot be restarted. Mark the table as full and * place the job back on the list of stopped jobs. */ if (DEBUG(JOB)) { printf("table full\n"); } (void)Lst_AtFront(stoppedJobs, (ClientData)job); jobFull = TRUE; if (DEBUG(JOB)) { printf("Job queue is full.\n"); } } } } /*- *----------------------------------------------------------------------- * JobStart -- * Start a target-creation process going for the target described * by the graph node gn. * * Results: * JOB_ERROR if there was an error in the commands, JOB_FINISHED * if there isn't actually anything left to do for the job and * JOB_RUNNING if the job has been started. * * Side Effects: * A new Job node is created and added to the list of running * jobs. PMake is forked and a child shell created. *----------------------------------------------------------------------- */ static int JobStart (gn, flags, previous) GNode *gn; /* target to create */ short flags; /* flags for the job to override normal ones. * e.g. JOB_SPECIAL or JOB_IGNDOTS */ Job *previous; /* The previous Job structure for this node, * if any. */ { register Job *job; /* new job descriptor */ char *argv[4]; /* Argument vector to shell */ char args[5]; /* arguments to shell */ static int jobno = 0; /* job number of catching output in a file */ Boolean cmdsOK; /* true if the nodes commands were all right */ Boolean local; /* Set true if the job was run locally */ Boolean noExec; /* Set true if we decide not to run the job */ if (previous != (Job *)NULL) { previous->flags &= ~ (JOB_FIRST|JOB_IGNERR|JOB_SILENT|JOB_REMOTE); job = previous; } else { job = (Job *) emalloc (sizeof (Job)); if (job == (Job *)NULL) { Punt("JobStart out of memory"); } flags |= JOB_FIRST; } job->node = gn; job->tailCmds = NILLNODE; /* * Set the initial value of the flags for this job based on the global * ones and the node's attributes... Any flags supplied by the caller * are also added to the field. */ job->flags = 0; if (Targ_Ignore (gn)) { job->flags |= JOB_IGNERR; } if (Targ_Silent (gn)) { job->flags |= JOB_SILENT; } job->flags |= flags; /* * Check the commands now so any attributes from .DEFAULT have a chance * to migrate to the node */ if (job->flags & JOB_FIRST) { cmdsOK = Job_CheckCommands(gn, Error); } else { cmdsOK = TRUE; } /* * If the -n flag wasn't given, we open up OUR (not the child's) * temporary file to stuff commands in it. The thing is rd/wr so we don't * need to reopen it to feed it to the shell. If the -n flag *was* given, * we just set the file to be stdout. Cute, huh? */ if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) { /* * We're serious here, but if the commands were bogus, we're * also dead... */ if (!cmdsOK) { DieHorribly(); } job->cmdFILE = fopen (tfile, "w+"); if (job->cmdFILE == (FILE *) NULL) { Punt ("Could not open %s", tfile); } fcntl(fileno(job->cmdFILE), F_SETFD, 1); /* * Send the commands to the command file, flush all its buffers then * rewind and remove the thing. */ noExec = FALSE; /* * used to be backwards; replace when start doing multiple commands * per shell. */ if (1) { /* * Be compatible: If this is the first time for this node, * verify its commands are ok and open the commands list for * sequential access by later invocations of JobStart. * Once that is done, we take the next command off the list * and print it to the command file. If the command was an * ellipsis, note that there's nothing more to execute. */ if ((job->flags&JOB_FIRST) && (Lst_Open(gn->commands) != SUCCESS)){ cmdsOK = FALSE; } else { LstNode ln = Lst_Next (gn->commands); if ((ln == NILLNODE) || JobPrintCommand ((char *)Lst_Datum (ln), job)) { noExec = TRUE; Lst_Close (gn->commands); } if (noExec && !(job->flags & JOB_FIRST)) { /* * If we're not going to execute anything, the job * is done and we need to close down the various * file descriptors we've opened for output, then * call JobDoOutput to catch the final characters or * send the file to the screen... Note that the i/o streams * are only open if this isn't the first job. * Note also that this could not be done in * Job_CatchChildren b/c it wasn't clear if there were * more commands to execute or not... */ if (usePipes) { #ifdef RMT_WILL_WATCH Rmt_Ignore(job->inPipe); #else FD_CLR(job->inPipe, &outputs); #endif if (job->outPipe != job->inPipe) { (void)close (job->outPipe); } JobDoOutput (job, TRUE); (void)close (job->inPipe); } else { (void)close (job->outFd); JobDoOutput (job, TRUE); } } } } else { /* * We can do all the commands at once. hooray for sanity */ numCommands = 0; Lst_ForEach (gn->commands, JobPrintCommand, (ClientData)job); /* * If we didn't print out any commands to the shell script, * there's not much point in executing the shell, is there? */ if (numCommands == 0) { noExec = TRUE; } } } else if (noExecute) { /* * Not executing anything -- just print all the commands to stdout * in one fell swoop. This will still set up job->tailCmds correctly. */ if (lastNode != gn) { printf (targFmt, gn->name); lastNode = gn; } job->cmdFILE = stdout; /* * Only print the commands if they're ok, but don't die if they're * not -- just let the user know they're bad and keep going. It * doesn't do any harm in this case and may do some good. */ if (cmdsOK) { Lst_ForEach(gn->commands, JobPrintCommand, (ClientData)job); } /* * Don't execute the shell, thank you. */ noExec = TRUE; } else { /* * Just touch the target and note that no shell should be executed. * Set cmdFILE to stdout to make life easier. Check the commands, too, * but don't die if they're no good -- it does no harm to keep working * up the graph. */ job->cmdFILE = stdout; Job_Touch (gn, job->flags&JOB_SILENT); noExec = TRUE; } /* * If we're not supposed to execute a shell, don't. */ if (noExec) { /* * Unlink and close the command file if we opened one */ if (job->cmdFILE != stdout) { (void) unlink (tfile); fclose(job->cmdFILE); } else { fflush (stdout); } /* * We only want to work our way up the graph if we aren't here because * the commands for the job were no good. */ if (cmdsOK) { if (aborting == 0) { if (job->tailCmds != NILLNODE) { Lst_ForEachFrom(job->node->commands, job->tailCmds, JobSaveCommand, (ClientData)job->node); } Make_Update(job->node); } free((Address)job); return(JOB_FINISHED); } else { free((Address)job); return(JOB_ERROR); } } else { fflush (job->cmdFILE); (void) unlink (tfile); } /* * Set up the control arguments to the shell. This is based on the flags * set earlier for this job. */ JobMakeArgv(job, argv); /* * If we're using pipes to catch output, create the pipe by which we'll * get the shell's output. If we're using files, print out that we're * starting a job and then set up its temporary-file name. This is just * tfile with two extra digits tacked on -- jobno. */ if (job->flags & JOB_FIRST) { if (usePipes) { int fd[2]; (void) pipe(fd); job->inPipe = fd[0]; job->outPipe = fd[1]; (void)fcntl (job->inPipe, F_SETFD, 1); (void)fcntl (job->outPipe, F_SETFD, 1); } else { printf ("Remaking `%s'\n", gn->name); fflush (stdout); sprintf (job->outFile, "%s%02d", tfile, jobno); jobno = (jobno + 1) % 100; job->outFd = open(job->outFile,O_WRONLY|O_CREAT|O_APPEND,0600); (void)fcntl (job->outFd, F_SETFD, 1); } } local = TRUE; if (local && (((nLocal >= maxLocal) && !(job->flags & JOB_SPECIAL) && (maxLocal != 0)))) { /* * The job can only be run locally, but we've hit the limit of * local concurrency, so put the job on hold until some other job * finishes. Note that the special jobs (.BEGIN, .INTERRUPT and .END) * may be run locally even when the local limit has been reached * (e.g. when maxLocal == 0), though they will be exported if at * all possible. */ jobFull = TRUE; if (DEBUG(JOB)) { printf("Can only run job locally.\n"); } job->flags |= JOB_RESTART; (void)Lst_AtEnd(stoppedJobs, (ClientData)job); } else { if ((nLocal >= maxLocal) && local) { /* * If we're running this job locally as a special case (see above), * at least say the table is full. */ jobFull = TRUE; if (DEBUG(JOB)) { printf("Local job queue is full.\n"); } } JobExec(job, argv); } return(JOB_RUNNING); } /*- *----------------------------------------------------------------------- * JobDoOutput -- * This function is called at different times depending on * whether the user has specified that output is to be collected * via pipes or temporary files. In the former case, we are called * whenever there is something to read on the pipe. We collect more * output from the given job and store it in the job's outBuf. If * this makes up a line, we print it tagged by the job's identifier, * as necessary. * If output has been collected in a temporary file, we open the * file and read it line by line, transfering it to our own * output channel until the file is empty. At which point we * remove the temporary file. * In both cases, however, we keep our figurative eye out for the * 'noPrint' line for the shell from which the output came. If * we recognize a line, we don't print it. If the command is not * alone on the line (the character after it is not \0 or \n), we * do print whatever follows it. * * Results: * None * * Side Effects: * curPos may be shifted as may the contents of outBuf. *----------------------------------------------------------------------- */ void JobDoOutput (job, finish) register Job *job; /* the job whose output needs printing */ Boolean finish; /* TRUE if this is the last time we'll be * called for this job */ { Boolean gotNL = FALSE; /* true if got a newline */ register int nr; /* number of bytes read */ register int i; /* auxiliary index into outBuf */ register int max; /* limit for i (end of current data) */ int nRead; /* (Temporary) number of bytes read */ char c; /* character after noPrint string */ FILE *oFILE; /* Stream pointer to shell's output file */ char inLine[132]; if (usePipes) { /* * Read as many bytes as will fit in the buffer. */ end_loop: nRead = read (job->inPipe, &job->outBuf[job->curPos], JOB_BUFSIZE - job->curPos); if (nRead < 0) { if (DEBUG(JOB)) { perror("JobDoOutput(piperead)"); } nr = 0; } else { nr = nRead; } /* * If we hit the end-of-file (the job is dead), we must flush its * remaining output, so pretend we read a newline if there's any * output remaining in the buffer. * Also clear the 'finish' flag so we stop looping. */ if ((nr == 0) && (job->curPos != 0)) { job->outBuf[job->curPos] = '\n'; nr = 1; finish = FALSE; } else if (nr == 0) { finish = FALSE; } /* * Look for the last newline in the bytes we just got. If there is * one, break out of the loop with 'i' as its index and gotNL set * TRUE. */ max = job->curPos + nr; for (i = job->curPos + nr - 1; i >= job->curPos; i--) { if (job->outBuf[i] == '\n') { gotNL = TRUE; break; } else if (job->outBuf[i] == '\0') { /* * Why? */ job->outBuf[i] = ' '; } } if (!gotNL) { job->curPos += nr; if (job->curPos == JOB_BUFSIZE) { /* * If we've run out of buffer space, we have no choice * but to print the stuff. sigh. */ gotNL = TRUE; i = job->curPos; } } if (gotNL) { /* * Need to send the output to the screen. Null terminate it * first, overwriting the newline character if there was one. * So long as the line isn't one we should filter (according * to the shell description), we print the line, preceeded * by a target banner if this target isn't the same as the * one for which we last printed something. * The rest of the data in the buffer are then shifted down * to the start of the buffer and curPos is set accordingly. */ job->outBuf[i] = '\0'; if (i >= job->curPos) { register char *cp, *ecp; cp = job->outBuf; if (commandShell->noPrint) { ecp = Str_FindSubstring(job->outBuf, commandShell->noPrint); while (ecp != (char *)NULL) { if (cp != ecp) { *ecp = '\0'; if (job->node != lastNode) { printf (targFmt, job->node->name); lastNode = job->node; } /* * The only way there wouldn't be a newline after * this line is if it were the last in the buffer. * however, since the non-printable comes after it, * there must be a newline, so we don't print one. */ printf ("%s", cp); } cp = ecp + commandShell->noPLen; if (cp != &job->outBuf[i]) { /* * Still more to print, look again after skipping * the whitespace following the non-printable * command.... */ cp++; while (*cp == ' ' || *cp == '\t' || *cp == '\n') { cp++; } ecp = Str_FindSubstring (cp, commandShell->noPrint); } else { break; } } } /* * There's still more in that thar buffer. This time, though, * we know there's no newline at the end, so we add one of * our own free will. */ if (*cp != '\0') { if (job->node != lastNode) { printf (targFmt, job->node->name); lastNode = job->node; } printf ("%s\n", cp); } fflush (stdout); } if (i < max - 1) { bcopy (&job->outBuf[i + 1], /* shift the remaining */ job->outBuf, /* characters down */ max - (i + 1)); job->curPos = max - (i + 1); } else { /* * We have written everything out, so we just start over * from the start of the buffer. No copying. No nothing. */ job->curPos = 0; } } if (finish) { /* * If the finish flag is true, we must loop until we hit * end-of-file on the pipe. This is guaranteed to happen eventually * since the other end of the pipe is now closed (we closed it * explicitly and the child has exited). When we do get an EOF, * finish will be set FALSE and we'll fall through and out. */ goto end_loop; } } else { /* * We've been called to retrieve the output of the job from the * temporary file where it's been squirreled away. This consists of * opening the file, reading the output line by line, being sure not * to print the noPrint line for the shell we used, then close and * remove the temporary file. Very simple. * * Change to read in blocks and do FindSubString type things as for * pipes? That would allow for "@echo -n..." */ oFILE = fopen (job->outFile, "r"); if (oFILE != (FILE *) NULL) { printf ("Results of making %s:\n", job->node->name); while (fgets (inLine, sizeof(inLine), oFILE) != NULL) { register char *cp, *ecp, *endp; cp = inLine; endp = inLine + strlen(inLine); if (endp[-1] == '\n') { *--endp = '\0'; } if (commandShell->noPrint) { ecp = Str_FindSubstring(cp, commandShell->noPrint); while (ecp != (char *)NULL) { if (cp != ecp) { *ecp = '\0'; /* * The only way there wouldn't be a newline after * this line is if it were the last in the buffer. * however, since the non-printable comes after it, * there must be a newline, so we don't print one. */ printf ("%s", cp); } cp = ecp + commandShell->noPLen; if (cp != endp) { /* * Still more to print, look again after skipping * the whitespace following the non-printable * command.... */ cp++; while (*cp == ' ' || *cp == '\t' || *cp == '\n') { cp++; } ecp = Str_FindSubstring(cp, commandShell->noPrint); } else { break; } } } /* * There's still more in that thar buffer. This time, though, * we know there's no newline at the end, so we add one of * our own free will. */ if (*cp != '\0') { printf ("%s\n", cp); } } fclose (oFILE); (void) unlink (job->outFile); } } fflush(stdout); } /*- *----------------------------------------------------------------------- * Job_CatchChildren -- * Handle the exit of a child. Called from Make_Make. * * Results: * none. * * Side Effects: * The job descriptor is removed from the list of children. * * Notes: * We do waits, blocking or not, according to the wisdom of our * caller, until there are no more children to report. For each * job, call JobFinish to finish things off. This will take care of * putting jobs on the stoppedJobs queue. * *----------------------------------------------------------------------- */ void Job_CatchChildren (block) Boolean block; /* TRUE if should block on the wait. */ { int pid; /* pid of dead child */ register Job *job; /* job descriptor for dead child */ LstNode jnode; /* list element for finding job */ union wait status; /* Exit/termination status */ /* * Don't even bother if we know there's no one around. */ if (nLocal == 0) { return; } while ((pid = wait3((int *)&status, (block?0:WNOHANG)|WUNTRACED, (struct rusage *)0)) > 0) { if (DEBUG(JOB)) printf("Process %d exited or stopped.\n", pid); jnode = Lst_Find (jobs, (ClientData)pid, JobCmpPid); if (jnode == NILLNODE) { if (WIFSIGNALED(status) && (status.w_termsig == SIGCONT)) { jnode = Lst_Find(stoppedJobs, (ClientData)pid, JobCmpPid); if (jnode == NILLNODE) { Error("Resumed child (%d) not in table", pid); continue; } job = (Job *)Lst_Datum(jnode); (void)Lst_Remove(stoppedJobs, jnode); } else { Error ("Child (%d) not in table?", pid); continue; } } else { job = (Job *) Lst_Datum (jnode); (void)Lst_Remove (jobs, jnode); nJobs -= 1; if (jobFull && DEBUG(JOB)) { printf("Job queue is no longer full.\n"); } jobFull = FALSE; nLocal -= 1; } JobFinish (job, status); } } /*- *----------------------------------------------------------------------- * Job_CatchOutput -- * Catch the output from our children, if we're using * pipes do so. Otherwise just block time until we get a * signal (most likely a SIGCHLD) since there's no point in * just spinning when there's nothing to do and the reaping * of a child can wait for a while. * * Results: * None * * Side Effects: * Output is read from pipes if we're piping. * ----------------------------------------------------------------------- */ void Job_CatchOutput () { int nfds; struct timeval timeout; fd_set readfds; register LstNode ln; register Job *job; int pnJobs; /* Previous nJobs */ fflush(stdout); #ifdef RMT_WILL_WATCH pnJobs = nJobs; /* * It is possible for us to be called with nJobs equal to 0. This happens * if all the jobs finish and a job that is stopped cannot be run * locally (eg if maxLocal is 0) and cannot be exported. The job will * be placed back on the stoppedJobs queue, Job_Empty() will return false, * Make_Run will call us again when there's nothing for which to wait. * nJobs never changes, so we loop forever. Hence the check. It could * be argued that we should sleep for a bit so as not to swamp the * exportation system with requests. Perhaps we should. * * NOTE: IT IS THE RESPONSIBILITY OF Rmt_Wait TO CALL Job_CatchChildren * IN A TIMELY FASHION TO CATCH ANY LOCALLY RUNNING JOBS THAT EXIT. * It may use the variable nLocal to determine if it needs to call * Job_CatchChildren (if nLocal is 0, there's nothing for which to * wait...) */ while (nJobs != 0 && pnJobs == nJobs) { Rmt_Wait(); } #else if (usePipes) { readfds = outputs; timeout.tv_sec = SEL_SEC; timeout.tv_usec = SEL_USEC; if ((nfds = select (FD_SETSIZE, &readfds, (int *) 0, (int *) 0, &timeout)) < 0) { return; } else { if (Lst_Open (jobs) == FAILURE) { Punt ("Cannot open job table"); } while (nfds && (ln = Lst_Next (jobs)) != NILLNODE) { job = (Job *) Lst_Datum (ln); if (FD_ISSET(job->inPipe, &readfds)) { JobDoOutput (job, FALSE); nfds -= 1; } } Lst_Close (jobs); } } #endif /* RMT_WILL_WATCH */ } /*- *----------------------------------------------------------------------- * Job_Make -- * Start the creation of a target. Basically a front-end for * JobStart used by the Make module. * * Results: * None. * * Side Effects: * Another job is started. * *----------------------------------------------------------------------- */ void Job_Make (gn) GNode *gn; { (void)JobStart (gn, 0, (Job *)NULL); } /*- *----------------------------------------------------------------------- * Job_Init -- * Initialize the process module * * Results: * none * * Side Effects: * lists and counters are initialized *----------------------------------------------------------------------- */ void Job_Init (maxproc, maxlocal) int maxproc; /* the greatest number of jobs which may be * running at one time */ int maxlocal; /* the greatest number of local jobs which may * be running at once. */ { GNode *begin; /* node for commands to do at the very start */ sprintf (tfile, "/tmp/make%05d", getpid()); jobs = Lst_Init (FALSE); stoppedJobs = Lst_Init(FALSE); maxJobs = maxproc; maxLocal = maxlocal; nJobs = 0; nLocal = 0; jobFull = FALSE; aborting = 0; errors = 0; lastNode = NILGNODE; if (maxJobs == 1) { /* * If only one job can run at a time, there's no need for a banner, * no is there? */ targFmt = ""; } else { targFmt = TARG_FMT; } if (shellPath == (char *) NULL) { /* * The user didn't specify a shell to use, so we are using the * default one... Both the absolute path and the last component * must be set. The last component is taken from the 'name' field * of the default shell description pointed-to by commandShell. * All default shells are located in _PATH_DEFSHELLDIR. */ shellName = commandShell->name; shellPath = str_concat (_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); } if (commandShell->exit == (char *)NULL) { commandShell->exit = ""; } if (commandShell->echo == (char *)NULL) { commandShell->echo = ""; } /* * Catch the four signals that POSIX specifies if they aren't ignored. * JobPassSig will take care of calling JobInterrupt if appropriate. */ if (signal (SIGINT, SIG_IGN) != SIG_IGN) { signal (SIGINT, JobPassSig); } if (signal (SIGHUP, SIG_IGN) != SIG_IGN) { signal (SIGHUP, JobPassSig); } if (signal (SIGQUIT, SIG_IGN) != SIG_IGN) { signal (SIGQUIT, JobPassSig); } if (signal (SIGTERM, SIG_IGN) != SIG_IGN) { signal (SIGTERM, JobPassSig); } /* * There are additional signals that need to be caught and passed if * either the export system wants to be told directly of signals or if * we're giving each job its own process group (since then it won't get * signals from the terminal driver as we own the terminal) */ #if defined(RMT_WANTS_SIGNALS) || defined(USE_PGRP) if (signal (SIGTSTP, SIG_IGN) != SIG_IGN) { signal (SIGTSTP, JobPassSig); } if (signal (SIGTTOU, SIG_IGN) != SIG_IGN) { signal (SIGTTOU, JobPassSig); } if (signal (SIGTTIN, SIG_IGN) != SIG_IGN) { signal (SIGTTIN, JobPassSig); } if (signal (SIGWINCH, SIG_IGN) != SIG_IGN) { signal (SIGWINCH, JobPassSig); } #endif begin = Targ_FindNode (".BEGIN", TARG_NOCREATE); if (begin != NILGNODE) { JobStart (begin, JOB_SPECIAL, (Job *)0); while (nJobs) { Job_CatchOutput(); #ifndef RMT_WILL_WATCH Job_CatchChildren (!usePipes); #endif /* RMT_WILL_WATCH */ } } postCommands = Targ_FindNode (".END", TARG_CREATE); } /*- *----------------------------------------------------------------------- * Job_Full -- * See if the job table is full. It is considered full if it is OR * if we are in the process of aborting OR if we have * reached/exceeded our local quota. This prevents any more jobs * from starting up. * * Results: * TRUE if the job table is full, FALSE otherwise * Side Effects: * None. *----------------------------------------------------------------------- */ Boolean Job_Full () { return (aborting || jobFull); } /*- *----------------------------------------------------------------------- * Job_Empty -- * See if the job table is empty. Because the local concurrency may * be set to 0, it is possible for the job table to become empty, * while the list of stoppedJobs remains non-empty. In such a case, * we want to restart as many jobs as we can. * * Results: * TRUE if it is. FALSE if it ain't. * * Side Effects: * None. * * ----------------------------------------------------------------------- */ Boolean Job_Empty () { if (nJobs == 0) { if (!Lst_IsEmpty(stoppedJobs) && !aborting) { /* * The job table is obviously not full if it has no jobs in * it...Try and restart the stopped jobs. */ jobFull = FALSE; while (!jobFull && !Lst_IsEmpty(stoppedJobs)) { JobRestart((Job *)Lst_DeQueue(stoppedJobs)); } return(FALSE); } else { return(TRUE); } } else { return(FALSE); } } /*- *----------------------------------------------------------------------- * JobMatchShell -- * Find a matching shell in 'shells' given its final component. * * Results: * A pointer to the Shell structure. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Shell * JobMatchShell (name) char *name; /* Final component of shell path */ { register Shell *sh; /* Pointer into shells table */ Shell *match; /* Longest-matching shell */ register char *cp1, *cp2; char *eoname; eoname = name + strlen (name); match = (Shell *) NULL; for (sh = shells; sh->name != NULL; sh++) { for (cp1 = eoname - strlen (sh->name), cp2 = sh->name; *cp1 != '\0' && *cp1 == *cp2; cp1++, cp2++) { continue; } if (*cp1 != *cp2) { continue; } else if (match == (Shell *) NULL || strlen (match->name) < strlen (sh->name)) { match = sh; } } return (match == (Shell *) NULL ? sh : match); } /*- *----------------------------------------------------------------------- * Job_ParseShell -- * Parse a shell specification and set up commandShell, shellPath * and shellName appropriately. * * Results: * FAILURE if the specification was incorrect. * * Side Effects: * commandShell points to a Shell structure (either predefined or * created from the shell spec), shellPath is the full path of the * shell described by commandShell, while shellName is just the * final component of shellPath. * * Notes: * A shell specification consists of a .SHELL target, with dependency * operator, followed by a series of blank-separated words. Double * quotes can be used to use blanks in words. A backslash escapes * anything (most notably a double-quote and a space) and * provides the functionality it does in C. Each word consists of * keyword and value separated by an equal sign. There should be no * unnecessary spaces in the word. The keywords are as follows: * name Name of shell. * path Location of shell. Overrides "name" if given * quiet Command to turn off echoing. * echo Command to turn echoing on * filter Result of turning off echoing that shouldn't be * printed. * echoFlag Flag to turn echoing on at the start * errFlag Flag to turn error checking on at the start * hasErrCtl True if shell has error checking control * check Command to turn on error checking if hasErrCtl * is TRUE or template of command to echo a command * for which error checking is off if hasErrCtl is * FALSE. * ignore Command to turn off error checking if hasErrCtl * is TRUE or template of command to execute a * command so as to ignore any errors it returns if * hasErrCtl is FALSE. * *----------------------------------------------------------------------- */ ReturnStatus Job_ParseShell (line) char *line; /* The shell spec */ { char **words; int wordCount; register char **argv; register int argc; char *path; Shell newShell; Boolean fullSpec = FALSE; while (isspace (*line)) { line++; } words = brk_string (line, &wordCount); bzero ((Address)&newShell, sizeof(newShell)); /* * Parse the specification by keyword */ for (path = (char *)NULL, argc = wordCount - 1, argv = words + 1; argc != 0; argc--, argv++) { if (strncmp (*argv, "path=", 5) == 0) { path = &argv[0][5]; } else if (strncmp (*argv, "name=", 5) == 0) { newShell.name = &argv[0][5]; } else { if (strncmp (*argv, "quiet=", 6) == 0) { newShell.echoOff = &argv[0][6]; } else if (strncmp (*argv, "echo=", 5) == 0) { newShell.echoOn = &argv[0][5]; } else if (strncmp (*argv, "filter=", 7) == 0) { newShell.noPrint = &argv[0][7]; newShell.noPLen = strlen(newShell.noPrint); } else if (strncmp (*argv, "echoFlag=", 9) == 0) { newShell.echo = &argv[0][9]; } else if (strncmp (*argv, "errFlag=", 8) == 0) { newShell.exit = &argv[0][8]; } else if (strncmp (*argv, "hasErrCtl=", 10) == 0) { char c = argv[0][10]; newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && (c != 'T') && (c != 't')); } else if (strncmp (*argv, "check=", 6) == 0) { newShell.errCheck = &argv[0][6]; } else if (strncmp (*argv, "ignore=", 7) == 0) { newShell.ignErr = &argv[0][7]; } else { Parse_Error (PARSE_FATAL, "Unknown keyword \"%s\"", *argv); return (FAILURE); } fullSpec = TRUE; } } if (path == (char *)NULL) { /* * If no path was given, the user wants one of the pre-defined shells, * yes? So we find the one s/he wants with the help of JobMatchShell * and set things up the right way. shellPath will be set up by * Job_Init. */ if (newShell.name == (char *)NULL) { Parse_Error (PARSE_FATAL, "Neither path nor name specified"); return (FAILURE); } else { commandShell = JobMatchShell (newShell.name); shellName = newShell.name; } } else { /* * The user provided a path. If s/he gave nothing else (fullSpec is * FALSE), try and find a matching shell in the ones we know of. * Else we just take the specification at its word and copy it * to a new location. In either case, we need to record the * path the user gave for the shell. */ shellPath = path; path = rindex (path, '/'); if (path == (char *)NULL) { path = shellPath; } else { path += 1; } if (newShell.name != (char *)NULL) { shellName = newShell.name; } else { shellName = path; } if (!fullSpec) { commandShell = JobMatchShell (shellName); } else { commandShell = (Shell *) emalloc(sizeof(Shell)); *commandShell = newShell; } } if (commandShell->echoOn && commandShell->echoOff) { commandShell->hasEchoCtl = TRUE; } if (!commandShell->hasErrCtl) { if (commandShell->errCheck == (char *)NULL) { commandShell->errCheck = ""; } if (commandShell->ignErr == (char *)NULL) { commandShell->ignErr = "%s\n"; } } /* * Do not free up the words themselves, since they might be in use by the * shell specification... */ free (words); return SUCCESS; } /*- *----------------------------------------------------------------------- * JobInterrupt -- * Handle the receipt of an interrupt. * * Results: * None * * Side Effects: * All children are killed. Another job will be started if the * .INTERRUPT target was given. *----------------------------------------------------------------------- */ static void JobInterrupt (runINTERRUPT) int runINTERRUPT; /* Non-zero if commands for the .INTERRUPT * target should be executed */ { LstNode ln; /* element in job table */ Job *job; /* job descriptor in that element */ GNode *interrupt; /* the node describing the .INTERRUPT target */ aborting = ABORT_INTERRUPT; (void)Lst_Open (jobs); while ((ln = Lst_Next (jobs)) != NILLNODE) { job = (Job *) Lst_Datum (ln); if (!Targ_Precious (job->node)) { char *file = (job->node->path == (char *)NULL ? job->node->name : job->node->path); if (unlink (file) == 0) { Error ("*** %s removed", file); } } #ifdef RMT_WANTS_SIGNALS if (job->flags & JOB_REMOTE) { /* * If job is remote, let the Rmt module do the killing. */ if (!Rmt_Signal(job, SIGINT)) { /* * If couldn't kill the thing, finish it out now with an * error code, since no exit report will come in likely. */ union wait status; status.w_status = 0; status.w_retcode = 1; JobFinish(job, status); } } else if (job->pid) { KILL(job->pid, SIGINT); } #else if (job->pid) { KILL(job->pid, SIGINT); } #endif /* RMT_WANTS_SIGNALS */ } Lst_Close (jobs); if (runINTERRUPT && !touchFlag) { interrupt = Targ_FindNode (".INTERRUPT", TARG_NOCREATE); if (interrupt != NILGNODE) { ignoreErrors = FALSE; JobStart (interrupt, JOB_IGNDOTS, (Job *)0); while (nJobs) { Job_CatchOutput(); #ifndef RMT_WILL_WATCH Job_CatchChildren (!usePipes); #endif /* RMT_WILL_WATCH */ } } } (void) unlink (tfile); exit (0); } /* *----------------------------------------------------------------------- * Job_End -- * Do final processing such as the running of the commands * attached to the .END target. * * Results: * Number of errors reported. * * Side Effects: * The process' temporary file (tfile) is removed if it still * existed. *----------------------------------------------------------------------- */ int Job_End () { if (postCommands != NILGNODE && !Lst_IsEmpty (postCommands->commands)) { if (errors) { Error ("Errors reported so .END ignored"); } else { JobStart (postCommands, JOB_SPECIAL | JOB_IGNDOTS, (Job *)0); while (nJobs) { Job_CatchOutput(); #ifndef RMT_WILL_WATCH Job_CatchChildren (!usePipes); #endif /* RMT_WILL_WATCH */ } } } (void) unlink (tfile); return(errors); } /*- *----------------------------------------------------------------------- * Job_Wait -- * Waits for all running jobs to finish and returns. Sets 'aborting' * to ABORT_WAIT to prevent other jobs from starting. * * Results: * None. * * Side Effects: * Currently running jobs finish. * *----------------------------------------------------------------------- */ void Job_Wait() { aborting = ABORT_WAIT; while (nJobs != 0) { Job_CatchOutput(); #ifndef RMT_WILL_WATCH Job_CatchChildren(!usePipes); #endif /* RMT_WILL_WATCH */ } aborting = 0; } /*- *----------------------------------------------------------------------- * Job_AbortAll -- * Abort all currently running jobs without handling output or anything. * This function is to be called only in the event of a major * error. Most definitely NOT to be called from JobInterrupt. * * Results: * None * * Side Effects: * All children are killed, not just the firstborn *----------------------------------------------------------------------- */ void Job_AbortAll () { LstNode ln; /* element in job table */ Job *job; /* the job descriptor in that element */ int foo; aborting = ABORT_ERROR; if (nJobs) { (void)Lst_Open (jobs); while ((ln = Lst_Next (jobs)) != NILLNODE) { job = (Job *) Lst_Datum (ln); /* * kill the child process with increasingly drastic signals to make * darn sure it's dead. */ #ifdef RMT_WANTS_SIGNALS if (job->flags & JOB_REMOTE) { Rmt_Signal(job, SIGINT); Rmt_Signal(job, SIGKILL); } else { KILL(job->pid, SIGINT); KILL(job->pid, SIGKILL); } #else KILL(job->pid, SIGINT); KILL(job->pid, SIGKILL); #endif /* RMT_WANTS_SIGNALS */ } } /* * Catch as many children as want to report in at first, then give up */ while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) { ; } (void) unlink (tfile); } pmake/job.h100600 1750 1750 24312 5431142064 11274 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)job.h 5.3 (Berkeley) 6/1/90 */ /*- * job.h -- * Definitions pertaining to the running of jobs in parallel mode. * Exported from job.c for the use of remote-execution modules. */ #ifndef _JOB_H_ #define _JOB_H_ #define TMPPAT "/tmp/makeXXXXX" /* * The SEL_ constants determine the maximum amount of time spent in select * before coming out to see if a child has finished. SEL_SEC is the number of * seconds and SEL_USEC is the number of micro-seconds */ #define SEL_SEC 0 #define SEL_USEC 500000 /*- * Job Table definitions. * * Each job has several things associated with it: * 1) The process id of the child shell * 2) The graph node describing the target being made by this job * 3) A LstNode for the first command to be saved after the job * completes. This is NILLNODE if there was no "..." in the job's * commands. * 4) An FILE* for writing out the commands. This is only * used before the job is actually started. * 5) A union of things used for handling the shell's output. Different * parts of the union are used based on the value of the usePipes * flag. If it is true, the output is being caught via a pipe and * the descriptors of our pipe, an array in which output is line * buffered and the current position in that buffer are all * maintained for each job. If, on the other hand, usePipes is false, * the output is routed to a temporary file and all that is kept * is the name of the file and the descriptor open to the file. * 6) An identifier provided by and for the exclusive use of the * Rmt module. * 7) A word of flags which determine how the module handles errors, * echoing, etc. for the job * * The job "table" is kept as a linked Lst in 'jobs', with the number of * active jobs maintained in the 'nJobs' variable. At no time will this * exceed the value of 'maxJobs', initialized by the Job_Init function. * * When a job is finished, the Make_Update function is called on each of the * parents of the node which was just remade. This takes care of the upward * traversal of the dependency graph. */ #define JOB_BUFSIZE 1024 typedef struct Job { int pid; /* The child's process ID */ GNode *node; /* The target the child is making */ LstNode tailCmds; /* The node of the first command to be * saved when the job has been run */ FILE *cmdFILE; /* When creating the shell script, this is * where the commands go */ char *rmtID; /* ID returned from Rmt module */ short flags; /* Flags to control treatment of job */ #define JOB_IGNERR 0x001 /* Ignore non-zero exits */ #define JOB_SILENT 0x002 /* no output */ #define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally * if we can't export it and maxLocal is 0 */ #define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing * commands */ #define JOB_REMOTE 0x010 /* Job is running remotely */ #define JOB_FIRST 0x020 /* Job is first job for the node */ #define JOB_REMIGRATE 0x040 /* Job needs to be remigrated */ #define JOB_RESTART 0x080 /* Job needs to be completely restarted */ #define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped, * for some reason */ #define JOB_CONTINUING 0x200 /* We are in the process of resuming this job. * Used to avoid infinite recursion between * JobFinish and JobRestart */ union { struct { int op_inPipe; /* Input side of pipe associated * with job's output channel */ int op_outPipe; /* Output side of pipe associated with * job's output channel */ char op_outBuf[JOB_BUFSIZE + 1]; /* Buffer for storing the output of the * job, line by line */ int op_curPos; /* Current position in op_outBuf */ } o_pipe; /* data used when catching the output via * a pipe */ struct { char of_outFile[sizeof(TMPPAT)+2]; /* Name of file to which shell output * was rerouted */ int of_outFd; /* Stream open to the output * file. Used to funnel all * from a single job to one file * while still allowing * multiple shell invocations */ } o_file; /* Data used when catching the output in * a temporary file */ } output; /* Data for tracking a shell's output */ } Job; #define outPipe output.o_pipe.op_outPipe #define inPipe output.o_pipe.op_inPipe #define outBuf output.o_pipe.op_outBuf #define curPos output.o_pipe.op_curPos #define outFile output.o_file.of_outFile #define outFd output.o_file.of_outFd /*- * Shell Specifications: * Each shell type has associated with it the following information: * 1) The string which must match the last character of the shell name * for the shell to be considered of this type. The longest match * wins. * 2) A command to issue to turn off echoing of command lines * 3) A command to issue to turn echoing back on again * 4) What the shell prints, and its length, when given the echo-off * command. This line will not be printed when received from the shell * 5) A boolean to tell if the shell has the ability to control * error checking for individual commands. * 6) The string to turn this checking on. * 7) The string to turn it off. * 8) The command-flag to give to cause the shell to start echoing * commands right away. * 9) The command-flag to cause the shell to Lib_Exit when an error is * detected in one of the commands. * * Some special stuff goes on if a shell doesn't have error control. In such * a case, errCheck becomes a printf template for echoing the command, * should echoing be on and ignErr becomes another printf template for * executing the command while ignoring the return status. If either of these * strings is empty when hasErrCtl is FALSE, the command will be executed * anyway as is and if it causes an error, so be it. */ typedef struct Shell { char *name; /* the name of the shell. For Bourne and C * shells, this is used only to find the * shell description when used as the single * source of a .SHELL target. For user-defined * shells, this is the full path of the shell. */ Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */ char *echoOff; /* command to turn off echo */ char *echoOn; /* command to turn it back on again */ char *noPrint; /* command to skip when printing output from * shell. This is usually the command which * was executed to turn off echoing */ int noPLen; /* length of noPrint command */ Boolean hasErrCtl; /* set if can control error checking for * individual commands */ char *errCheck; /* string to turn error checking on */ char *ignErr; /* string to turn off error checking */ /* * command-line flags */ char *echo; /* echo commands */ char *exit; /* exit on error */ } Shell; extern char *targFmt; /* Format string for banner that separates * output from multiple jobs. Contains a * single %s where the name of the node being * made should be put. */ extern GNode *lastNode; /* Last node for which a banner was printed. * If Rmt module finds it necessary to print * a banner, it should set this to the node * for which the banner was printed */ extern int nJobs; /* Number of jobs running (local and remote) */ extern int nLocal; /* Number of jobs running locally */ extern Lst jobs; /* List of active job descriptors */ extern Lst stoppedJobs; /* List of jobs that are stopped or didn't * quite get started */ extern Boolean jobFull; /* Non-zero if no more jobs should/will start*/ /* * These functions should be used only by an intelligent Rmt module, hence * their names do *not* include an underscore as they are not fully exported, * if you see what I mean. */ extern void JobDoOutput(/* job, final? */); /* Funnel output from * job->outPipe to the screen, * filtering out echo-off * strings etc. */ extern void JobFinish(/* job, status */); /* Finish out a job. If * status indicates job has * just stopped, not finished, * the descriptor is placed on * the stoppedJobs list. */ #endif /* _JOB_H_ */ pmake/list.h100600 1750 1750 24012 5431142064 11472 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)list.h 5.3 (Berkeley) 6/1/90 */ /* * list.h -- * * Structures, macros, and routines exported by the List module. */ #ifndef _LIST #define _LIST #ifndef _SPRITE #include "sprite.h" #endif _SPRITE /* * This module defines the list abstraction, which enables one to link * together arbitrary data structures. Lists are doubly-linked and * circular. A list contains a header followed by its real members, if * any. (An empty list therefore consists of a single element, the * header, whose nextPtr and prevPtr fields point to itself). To refer * to a list as a whole, the user keeps a pointer to the header; that * header is initialized by a call to List_Init(), which creates an empty * list given a pointer to a List_Links structure (described below). * * The links are contained in a two-element structure called List_Links. * A list joins List_Links records (that is, each List_Links structure * points to other List_Links structures), but if the List_Links is the * first field within a larger structure, then the larger structures are * effectively linked together as follows: * * header * (List_Links) first elt. second elt. * ----------------- ----------------- ----------------- * ..-> | nextPtr | ----> | List_Links | ----> | List_Links |----.. * | - - - - - - - | | | | | * ..-- | prevPtr | <---- | | <---- | |<---.. * ----------------- - --- --- --- - - --- --- --- - * | rest of | | rest of | * | structure | | structure | * | | | | * | ... | | ... | * ----------------- ----------------- * * It is possible to link structures through List_Links fields that are * not at the beginning of the larger structure, but it is then necessary * to perform pointer arithmetic to find the beginning of the larger * structure, given a pointer to some point within it. * * A typical structure might be something like: * * typedef struct { * List_Links links; * char ch; * integer flags; * } EditChar; * * Before an element is inserted in a list for the first time, it must * be initialized by calling the macro List_InitElement(). */ /* * data structure for lists */ typedef struct List_Links { struct List_Links *prevPtr; struct List_Links *nextPtr; } List_Links; /* * procedures */ void List_Init(); /* initialize a header to a list */ void List_Insert(); /* insert an element into a list */ void List_Remove(); /* remove an element from a list */ void List_Move(); /* move an element elsewhere in a list */ /* * ---------------------------------------------------------------------------- * * List_InitElement -- * * Initialize a list element. Must be called before an element is first * inserted into a list. * * ---------------------------------------------------------------------------- */ #define List_InitElement(elementPtr) \ (elementPtr)->prevPtr = (List_Links *) NIL; \ (elementPtr)->nextPtr = (List_Links *) NIL; /* * Macros for stepping through or selecting parts of lists */ /* * ---------------------------------------------------------------------------- * * LIST_FORALL -- * * Macro to loop through a list and perform an operation on each member. * * Usage: LIST_FORALL(headerPtr, itemPtr) { * / * * * operation on itemPtr, which points to successive members * * of the list * * * * It may be appropriate to first assign * * foobarPtr = (Foobar *) itemPtr; * * to refer to the entire Foobar structure. * * / * } * * Note: itemPtr must be a List_Links pointer variable, and headerPtr * must evaluate to a pointer to a List_Links structure. * * ---------------------------------------------------------------------------- */ #define LIST_FORALL(headerPtr, itemPtr) \ for (itemPtr = List_First(headerPtr); \ !List_IsAtEnd((headerPtr),itemPtr); \ itemPtr = List_Next(itemPtr)) /* * ---------------------------------------------------------------------------- * * List_IsEmpty -- * * Macro: Boolean value, TRUE if the given list does not contain any * members. * * Usage: if (List_IsEmpty(headerPtr)) ... * * ---------------------------------------------------------------------------- */ #define List_IsEmpty(headerPtr) \ ((headerPtr) == (headerPtr)->nextPtr) /* * ---------------------------------------------------------------------------- * * List_IsAtEnd -- * * Macro: Boolean value, TRUE if itemPtr is after the end of headerPtr * (i.e., itemPtr is the header of the list). * * Usage: if (List_IsAtEnd(headerPtr, itemPtr)) ... * * ---------------------------------------------------------------------------- */ #define List_IsAtEnd(headerPtr, itemPtr) \ ((itemPtr) == (headerPtr)) /* * ---------------------------------------------------------------------------- * * List_First -- * * Macro to return the first member in a list, which is the header if * the list is empty. * * Usage: firstPtr = List_First(headerPtr); * * ---------------------------------------------------------------------------- */ #define List_First(headerPtr) ((headerPtr)->nextPtr) /* * ---------------------------------------------------------------------------- * * List_Last -- * * Macro to return the last member in a list, which is the header if * the list is empty. * * Usage: lastPtr = List_Last(headerPtr); * * ---------------------------------------------------------------------------- */ #define List_Last(headerPtr) ((headerPtr)->prevPtr) /* * ---------------------------------------------------------------------------- * * List_Prev -- * * Macro to return the member preceding the given member in its list. * If the given list member is the first element in the list, List_Prev * returns the list header. * * Usage: prevPtr = List_Prev(itemPtr); * * ---------------------------------------------------------------------------- */ #define List_Prev(itemPtr) ((itemPtr)->prevPtr) /* * ---------------------------------------------------------------------------- * * List_Next -- * * Macro to return the member following the given member in its list. * If the given list member is the last element in the list, List_Next * returns the list header. * * Usage: nextPtr = List_Next(itemPtr); * * ---------------------------------------------------------------------------- */ #define List_Next(itemPtr) ((itemPtr)->nextPtr) /* * ---------------------------------------------------------------------------- * The List_Insert procedure takes two arguments. The first argument * is a pointer to the structure to be inserted into a list, and * the second argument is a pointer to the list member after which * the new element is to be inserted. Macros are used to determine * which existing member will precede the new one. * * The List_Move procedure takes a destination argument with the same * semantics as List_Insert. * * The following macros define where to insert the new element * in the list: * * LIST_AFTER(itemPtr) -- insert after itemPtr * LIST_BEFORE(itemPtr) -- insert before itemPtr * LIST_ATFRONT(headerPtr) -- insert at front of list * LIST_ATREAR(headerPtr) -- insert at end of list * * For example, * * List_Insert(itemPtr, LIST_AFTER(otherPtr)); * * will insert itemPtr following otherPtr in the list containing otherPtr. * ---------------------------------------------------------------------------- */ #define LIST_AFTER(itemPtr) ((List_Links *) itemPtr) #define LIST_BEFORE(itemPtr) (((List_Links *) itemPtr)->prevPtr) #define LIST_ATFRONT(headerPtr) ((List_Links *) headerPtr) #define LIST_ATREAR(headerPtr) (((List_Links *) headerPtr)->prevPtr) #endif _LIST pmake/lst.h100600 1750 1750 12545 5431142065 11332 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)lst.h 5.3 (Berkeley) 6/1/90 */ /*- * lst.h -- * Header for using the list library */ #ifndef _LST_H_ #define _LST_H_ #include /* * basic typedef. This is what the Lst_ functions handle */ typedef struct Lst *Lst; typedef struct LstNode *LstNode; #define NILLST ((Lst) NIL) #define NILLNODE ((LstNode) NIL) /* * NOFREE can be used as the freeProc to Lst_Destroy when the elements are * not to be freed. * NOCOPY performs similarly when given as the copyProc to Lst_Duplicate. */ #define NOFREE ((void (*)()) 0) #define NOCOPY ((ClientData (*)()) 0) #define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ #define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ /* * Creation/destruction functions */ Lst Lst_Init(); /* Create a new list */ Lst Lst_Duplicate(); /* Duplicate an existing list */ void Lst_Destroy(); /* Destroy an old one */ int Lst_Length(); /* Find the length of a list */ Boolean Lst_IsEmpty(); /* True if list is empty */ /* * Functions to modify a list */ ReturnStatus Lst_Insert(); /* Insert an element before another */ ReturnStatus Lst_Append(); /* Insert an element after another */ ReturnStatus Lst_AtFront(); /* Place an element at the front of * a lst. */ ReturnStatus Lst_AtEnd(); /* Place an element at the end of a * lst. */ ReturnStatus Lst_Remove(); /* Remove an element */ ReturnStatus Lst_Replace(); /* Replace a node with a new value */ ReturnStatus Lst_Move(); /* Move an element to another place */ ReturnStatus Lst_Concat(); /* Concatenate two lists */ /* * Node-specific functions */ LstNode Lst_First(); /* Return first element in list */ LstNode Lst_Last(); /* Return last element in list */ LstNode Lst_Succ(); /* Return successor to given element */ LstNode Lst_Pred(); /* Return predecessor to given * element */ ClientData Lst_Datum(); /* Get datum from LstNode */ /* * Functions for entire lists */ LstNode Lst_Find(); /* Find an element in a list */ LstNode Lst_FindFrom(); /* Find an element starting from * somewhere */ LstNode Lst_Member(); /* See if the given datum is on the * list. Returns the LstNode containing * the datum */ int Lst_Index(); /* Returns the index of a datum in the * list, starting from 0 */ void Lst_ForEach(); /* Apply a function to all elements of * a lst */ void Lst_ForEachFrom(); /* Apply a function to all elements of * a lst starting from a certain point. * If the list is circular, the * application will wrap around to the * beginning of the list again. */ /* * these functions are for dealing with a list as a table, of sorts. * An idea of the "current element" is kept and used by all the functions * between Lst_Open() and Lst_Close(). */ ReturnStatus Lst_Open(); /* Open the list */ LstNode Lst_Prev(); /* Previous element */ LstNode Lst_Cur(); /* The current element, please */ LstNode Lst_Next(); /* Next element please */ Boolean Lst_IsAtEnd(); /* Done yet? */ void Lst_Close(); /* Finish table access */ /* * for using the list as a queue */ ReturnStatus Lst_EnQueue(); /* Place an element at tail of queue */ ClientData Lst_DeQueue(); /* Remove an element from head of * queue */ #endif _LST_H_ pmake/main.c100600 1750 1750 47200 5431142065 11443 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint char copyright[] = "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ All rights reserved.\n"; #endif /* not lint */ #ifndef lint static char sccsid[] = "@(#)main.c 5.25 (Berkeley) 4/1/91"; #endif /* not lint */ /*- * main.c -- * The main file for this entire program. Exit routines etc * reside here. * * Utility functions defined in this file: * Main_ParseArgLine Takes a line of arguments, breaks them and * treats them as if they were given when first * invoked. Used by the parse module to implement * the .MFLAGS target. * * Error Print a tagged error message. The global * MAKE variable must have been defined. This * takes a format string and two optional * arguments for it. * * Fatal Print an error message and exit. Also takes * a format string and two arguments. * * Punt Aborts all jobs and exits with a message. Also * takes a format string and two arguments. * * Finish Finish things up by printing the number of * errors which occured, as passed to it, and * exiting. */ #include #include #include #include #include #include #include #include "make.h" #include "pathnames.h" #ifndef DEFMAXLOCAL #define DEFMAXLOCAL DEFMAXJOBS #endif DEFMAXLOCAL #define MAKEFLAGS ".MAKEFLAGS" #ifndef MACHINE #if defined(sun) #define MACHINE "SUN3" #elif defined(linux) #define MACHINE "Linux" #else #define MACHINE "Unknown" #endif /* Sun3 */ #endif Lst create; /* Targets to be made */ time_t now; /* Time at start of make */ GNode *DEFAULT; /* .DEFAULT node */ Boolean allPrecious; /* .PRECIOUS given on line by itself */ static Boolean noBuiltins; /* -r flag */ static Lst makefiles; /* ordered list of makefiles to read */ int maxJobs; /* -J argument */ static int maxLocal; /* -L argument */ Boolean debug; /* -d flag */ Boolean noExecute; /* -n flag */ Boolean keepgoing; /* -k flag */ Boolean queryFlag; /* -q flag */ Boolean touchFlag; /* -t flag */ Boolean usePipes; /* !-P flag */ Boolean ignoreErrors; /* -i flag */ Boolean beSilent; /* -s flag */ Boolean oldVars; /* variable substitution style */ Boolean checkEnvFirst; /* -e flag */ static Boolean jobsRunning; /* TRUE if the jobs might be running */ static Boolean ReadMakefile(); static char *curdir; /* if chdir'd for an architecture */ /*- * MainParseArgs -- * Parse a given argument vector. Called from main() and from * Main_ParseArgLine() when the .MAKEFLAGS target is used. * * XXX: Deal with command line overriding .MAKEFLAGS in makefile * * Results: * None * * Side Effects: * Various global and local flags will be set depending on the flags * given */ static void MainParseArgs(argc, argv) int argc; char **argv; { extern int optind; extern char *optarg; register int i; register char *cp; char c; optind = 1; /* since we're called more than once */ rearg: while((c = getopt(argc, argv, "D:I:d:ef:ij:knqrst")) != EOF) { switch(c) { case 'D': Var_Set(optarg, "1", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'I': Parse_AddIncludeDir(optarg); Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; #ifdef notdef case 'L': maxLocal = atoi(optarg); Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'P': usePipes = FALSE; Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL); break; case 'S': keepgoing = FALSE; Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); break; #endif case 'd': { char *modules = optarg; for (; *modules; ++modules) switch (*modules) { case 'A': debug = ~0; break; case 'a': debug |= DEBUG_ARCH; break; case 'c': debug |= DEBUG_COND; break; case 'd': debug |= DEBUG_DIR; break; case 'g': if (modules[1] == '1') { debug |= DEBUG_GRAPH1; ++modules; } else if (modules[1] == '2') { debug |= DEBUG_GRAPH2; ++modules; } break; case 'j': debug |= DEBUG_JOB; break; case 'm': debug |= DEBUG_MAKE; break; case 's': debug |= DEBUG_SUFF; break; case 't': debug |= DEBUG_TARG; break; case 'v': debug |= DEBUG_VAR; break; default: (void)fprintf(stderr, "make: illegal argument to d option -- %c\n", *modules); usage(); } Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; } case 'e': checkEnvFirst = TRUE; Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); break; case 'f': (void)Lst_AtEnd(makefiles, (ClientData)optarg); break; case 'i': ignoreErrors = TRUE; Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); break; case 'j': maxJobs = atoi(optarg); Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL); break; case 'k': keepgoing = TRUE; Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); break; case 'n': noExecute = TRUE; Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); break; case 'q': queryFlag = TRUE; /* Kind of nonsensical, wot? */ Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); break; case 'r': noBuiltins = TRUE; Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); break; case 's': beSilent = TRUE; Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); break; case 't': touchFlag = TRUE; Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); break; default: case '?': usage(); } } oldVars = TRUE; /* * See if the rest of the arguments are variable assignments and * perform them if so. Else take them to be targets and stuff them * on the end of the "create" list. */ for (argv += optind, argc -= optind; *argv; ++argv, --argc) if (Parse_IsVar(*argv)) Parse_DoVar(*argv, VAR_CMD); else { if (!**argv) Punt("illegal (null) argument."); if (**argv == '-') { optind = 0; goto rearg; } (void)Lst_AtEnd(create, (ClientData)*argv); } } /*- * Main_ParseArgLine -- * Used by the parse module when a .MFLAGS or .MAKEFLAGS target * is encountered and by main() when reading the .MAKEFLAGS envariable. * Takes a line of arguments and breaks it into its * component words and passes those words and the number of them to the * MainParseArgs function. * The line should have all its leading whitespace removed. * * Results: * None * * Side Effects: * Only those that come from the various arguments. */ void Main_ParseArgLine(line) char *line; /* Line to fracture */ { char **argv; /* Manufactured argument vector */ int argc; /* Number of arguments in argv */ if (line == NULL) return; for (; *line == ' '; ++line); if (!*line) return; argv = brk_string(line, &argc); MainParseArgs(argc, argv); } /*- * main -- * The main function, for obvious reasons. Initializes variables * and a few modules, then parses the arguments give it in the * environment and on the command line. Reads the system makefile * followed by either Makefile, makefile or the file given by the * -f argument. Sets the .MAKEFLAGS PMake variable based on all the * flags it has received by then uses either the Make or the Compat * module to create the initial list of targets. * * Results: * If -q was given, exits -1 if anything was out-of-date. Else it exits * 0. * * Side Effects: * The program exits when done. Targets are created. etc. etc. etc. */ main(argc, argv) int argc; char **argv; { Lst targs; /* target nodes to create -- passed to Make_Init */ Boolean outOfDate; /* FALSE if all targets up to date */ struct stat sb; char *p, *path, *getenv(); /* * if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory * exists, change into it and build there. Once things are * initted, have to add the original directory to the search path, * and modify the paths for the Makefiles apropriately. The * current directory is also placed as a variable for make scripts. */ if (!(path = getenv("MAKEOBJDIR"))) path = _PATH_OBJDIR; if (!lstat(path, &sb)) { if (S_ISDIR(sb.st_mode)) curdir = ".."; else { curdir = emalloc((u_int)MAXPATHLEN + 1); if (!getwd(curdir)) { (void)fprintf(stderr, "make: %s.\n", curdir); exit(2); } } if (chdir(path)) { (void)fprintf(stderr, "make: %s: %s.\n", path, strerror(errno)); exit(2); } } create = Lst_Init(FALSE); makefiles = Lst_Init(FALSE); beSilent = FALSE; /* Print commands as executed */ ignoreErrors = FALSE; /* Pay attention to non-zero returns */ noExecute = FALSE; /* Execute all commands */ keepgoing = FALSE; /* Stop on error */ allPrecious = FALSE; /* Remove targets when interrupted */ queryFlag = FALSE; /* This is not just a check-run */ noBuiltins = FALSE; /* Read the built-in rules */ touchFlag = FALSE; /* Actually update targets */ usePipes = TRUE; /* Catch child output in pipes */ debug = 0; /* No debug verbosity, please. */ jobsRunning = FALSE; maxJobs = DEFMAXJOBS; /* Set default max concurrency */ maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */ /* * Initialize the parsing, directory and variable modules to prepare * for the reading of inclusion paths and variable settings on the * command line */ Dir_Init(); /* Initialize directory structures so -I flags * can be processed correctly */ Parse_Init(); /* Need to initialize the paths of #include * directories */ Var_Init(); /* As well as the lists of variables for * parsing arguments */ if (curdir) { Dir_AddDir(dirSearchPath, curdir); Var_Set(".CURDIR", curdir, VAR_GLOBAL); } else Var_Set(".CURDIR", ".", VAR_GLOBAL); /* * Initialize various variables. * MAKE also gets this name, for compatibility * .MAKEFLAGS gets set to the empty string just in case. * MFLAGS also gets initialized empty, for compatibility. */ Var_Set("MAKE", argv[0], VAR_GLOBAL); Var_Set(MAKEFLAGS, "", VAR_GLOBAL); Var_Set("MFLAGS", "", VAR_GLOBAL); Var_Set("MACHINE", MACHINE, VAR_GLOBAL); /* * First snag any flags out of the MAKE environment variable. * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's * in a different format). */ #ifdef POSIX Main_ParseArgLine(getenv("MAKEFLAGS")); #else Main_ParseArgLine(getenv("MAKE")); #endif MainParseArgs(argc, argv); /* * Initialize archive, target and suffix modules in preparation for * parsing the makefile(s) */ Arch_Init(); Targ_Init(); Suff_Init(); DEFAULT = NILGNODE; (void)time(&now); /* * Set up the .TARGETS variable to contain the list of targets to be * created. If none specified, make the variable empty -- the parser * will fill the thing in with the default or .MAIN target. */ if (!Lst_IsEmpty(create)) { LstNode ln; for (ln = Lst_First(create); ln != NILLNODE; ln = Lst_Succ(ln)) { char *name = (char *)Lst_Datum(ln); Var_Append(".TARGETS", name, VAR_GLOBAL); } } else Var_Set(".TARGETS", "", VAR_GLOBAL); /* * Read in the built-in rules first, followed by the specified makefile, * if it was (makefile != (char *) NULL), or the default Makefile and * makefile, in that order, if it wasn't. */ if (!noBuiltins && !ReadMakefile(_PATH_DEFSYSMK)) Fatal("make: no system rules (%s).", _PATH_DEFSYSMK); if (!Lst_IsEmpty(makefiles)) { LstNode ln; ln = Lst_Find(makefiles, (ClientData)NULL, ReadMakefile); if (ln != NILLNODE) Fatal("make: cannot open %s.", (char *)Lst_Datum(ln)); } else if (!ReadMakefile("makefile")) (void)ReadMakefile("Makefile"); (void)ReadMakefile(".depend"); Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL), VAR_GLOBAL); /* Install all the flags into the MAKE envariable. */ if ((p = Var_Value(MAKEFLAGS, VAR_GLOBAL)) && *p) #ifdef POSIX setenv("MAKEFLAGS", p, 1); #else setenv("MAKE", p, 1); #endif /* * For compatibility, look at the directories in the VPATH variable * and add them to the search path, if the variable is defined. The * variable's value is in the same format as the PATH envariable, i.e. * ::... */ if (Var_Exists("VPATH", VAR_CMD)) { char *vpath, *path, *cp, savec; /* * GCC stores string constants in read-only memory, but * Var_Subst will want to write this thing, so store it * in an array */ static char VPATH[] = "${VPATH}"; vpath = Var_Subst(VPATH, VAR_CMD, FALSE); path = vpath; do { /* skip to end of directory */ for (cp = path; *cp != ':' && *cp != '\0'; cp++); /* Save terminator character so know when to stop */ savec = *cp; *cp = '\0'; /* Add directory to search path */ Dir_AddDir(dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); (void)free((Address)vpath); } /* * Now that all search paths have been read for suffixes et al, it's * time to add the default search path to their lists... */ Suff_DoPaths(); /* print the initial graph, if the user requested it */ if (DEBUG(GRAPH1)) Targ_PrintGraph(1); /* * Have now read the entire graph and need to make a list of targets * to create. If none was given on the command line, we consult the * parsing module to find the main target(s) to create. */ if (Lst_IsEmpty(create)) targs = Parse_MainName(); else targs = Targ_FindList(create, TARG_CREATE); /* * this was original amMake -- want to allow parallelism, so put this * back in, eventually. */ if (0) { /* * Initialize job module before traversing the graph, now that * any .BEGIN and .END targets have been read. This is done * only if the -q flag wasn't given (to prevent the .BEGIN from * being executed should it exist). */ if (!queryFlag) { if (maxLocal == -1) maxLocal = maxJobs; Job_Init(maxJobs, maxLocal); jobsRunning = TRUE; } /* Traverse the graph, checking on all the targets */ outOfDate = Make_Run(targs); } else /* * Compat_Init will take care of creating all the targets as * well as initializing the module. */ Compat_Run(targs); /* print the graph now it's been processed if the user requested it */ if (DEBUG(GRAPH2)) Targ_PrintGraph(2); if (queryFlag && outOfDate) exit(1); else exit(0); } /*- * ReadMakefile -- * Open and parse the given makefile. * * Results: * TRUE if ok. FALSE if couldn't open file. * * Side Effects: * lots */ static Boolean ReadMakefile(fname) char *fname; /* makefile to read */ { extern Lst parseIncPath, sysIncPath; FILE *stream; char *name, path[MAXPATHLEN + 1]; if (!strcmp(fname, "-")) { Parse_File("(stdin)", stdin); Var_Set("MAKEFILE", "", VAR_GLOBAL); } else { if (stream = fopen(fname, "r")) goto found; /* if we've chdir'd, rebuild the path name */ if (curdir && *fname != '/') { (void)sprintf(path, "%s/%s", curdir, fname); if (stream = fopen(path, "r")) { fname = path; goto found; } } /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (!name) name = Dir_FindFile(fname, sysIncPath); if (!name || !(stream = fopen(name, "r"))) return(FALSE); fname = name; /* * set the MAKEFILE variable desired by System V fans -- the * placement of the setting here means it gets set to the last * makefile specified, as it is set by SysV make. */ found: Var_Set("MAKEFILE", fname, VAR_GLOBAL); Parse_File(fname, stream); (void)fclose(stream); } return(TRUE); } /*- * Error -- * Print an error message given its format. * * Results: * None. * * Side Effects: * The message is printed. */ /* VARARGS */ void Error(va_alist) va_dcl { va_list ap; char *fmt; va_start(ap); fmt = va_arg(ap, char *); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); } /*- * Fatal -- * Produce a Fatal error message. If jobs are running, waits for them * to finish. * * Results: * None * * Side Effects: * The program exits */ /* VARARGS */ void Fatal(va_alist) va_dcl { va_list ap; char *fmt; if (jobsRunning) Job_Wait(); va_start(ap); fmt = va_arg(ap, char *); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); if (DEBUG(GRAPH2)) Targ_PrintGraph(2); exit(2); /* Not 1 so -q can distinguish error */ } /* * Punt -- * Major exception once jobs are being created. Kills all jobs, prints * a message and exits. * * Results: * None * * Side Effects: * All children are killed indiscriminately and the program Lib_Exits */ /* VARARGS */ void Punt(va_alist) va_dcl { va_list ap; char *fmt; (void)fprintf(stderr, "make: "); va_start(ap); fmt = va_arg(ap, char *); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); DieHorribly(); } /*- * DieHorribly -- * Exit without giving a message. * * Results: * None * * Side Effects: * A big one... */ void DieHorribly() { if (jobsRunning) Job_AbortAll(); if (DEBUG(GRAPH2)) Targ_PrintGraph(2); exit(2); /* Not 1, so -q can distinguish error */ } /* * Finish -- * Called when aborting due to errors in child shell to signal * abnormal exit. * * Results: * None * * Side Effects: * The program exits */ void Finish(errors) int errors; /* number of errors encountered in Make_Make */ { Fatal("%d error%s", errors, errors == 1 ? "" : "s"); } /* * emalloc -- * malloc, but die on error. */ char * emalloc(len) u_int len; { char *p, *malloc(); if (!(p = malloc(len))) enomem(); return(p); } /* * enomem -- * die when out of memory. */ enomem() { (void)fprintf(stderr, "make: %s.\n", strerror(errno)); exit(2); } /* * usage -- * exit with usage message */ usage() { (void)fprintf(stderr, "usage: make [-eiknqrst] [-D variable] [-d flags] [-f makefile ]\n\ [-I directory] [-j max_jobs] [variable=value]\n"); exit(2); } pmake/make.1100600 1750 1750 54337 5431142066 11364 0ustar karlkarl.\" Copyright (c) 1990 The Regents of the University of California. .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. All advertising materials mentioning features or use of this software .\" must display the following acknowledgement: .\" This product includes software developed by the University of .\" California, Berkeley and its contributors. .\" 4. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)make.1 5.7 (Berkeley) 7/24/91 .\" .Dd July 24, 1991 .Dt MAKE 1 .Os .Sh NAME .Nm make .Nd maintain program dependencies .Sh SYNOPSIS .Nm make .Op Fl eiknqrstv .Op Fl D Ar variable .Op Fl d Ar flags .Op Fl f Ar makefile .Op Fl I Ar directory .Bk -words .Op Fl j Ar max_jobs .Ek .Op Ar variable=value .Op Ar target ... .Sh DESCRIPTION .Nm Make is a program designed to simplify the maintenance of other programs. Its input is a list of specifications as to the files upon which programs and other files depend. If the file .Ql Pa makefile exists, it is read for this list of specifications. If it does not exist, the file .Ql Pa Makefile is read. If the file .Ql Pa .depend exists, it is read (see .Xr mkdep 1) . .Pp This manual page is intended as a reference document only. For a more thorough description of .Nm make and makefiles, please refer to .%T "Make \- A Tutorial" . .Pp The options are as follows: .Bl -tag -width Ds .It Fl D Ar variable Define Ar variable to be 1, in the global context. .It Fl d Ar flags Turn on debugging, and specify which portions of .Nm make are to print debugging information. .Ar Flags is one or more of the following: .Bl -tag -width Ds .It Ar A Print all possible debugging information; equivalent to specifying all of the debugging flags. .It Ar a Print debugging information about archive searching and caching. .It Ar c Print debugging information about conditional evaluation. .It Ar d Print debugging information about directory searching and caching. .It Ar "g1" Print the input graph before making anything. .It Ar "g2" Print the input graph after making everything, or before exiting on error. .It Ar j Print debugging information about running multiple shells. .It Ar m Print debugging information about making targets, including modification dates. .It Ar s Print debugging information about suffix-transformation rules. .It Ar t Print debugging information about target list maintenance. .It Ar v Print debugging information about variable assignment. .El .It Fl e Specify that environmental variables override macro assignments within makefiles. .It Fl f Ar makefile Specify a makefile to read instead of the default .Ql Pa makefile and .Ql Pa Makefile . If .Ar makefile is .Ql Fl , standard input is read. Multiple makefile's may be specified, and are read in the order specified. .It Fl I Ar directory Specify a directory in which to search for makefiles and included makefiles. The system makefile directory is automatically included as part of this list. .It Fl i Ignore non-zero exit of shell commands in the makefile. Equivalent to specifying .Ql Fl before each command line in the makefile. .It Fl j Ar max_jobs Specify the maximum number of jobs that .Nm make may have running at any one time. .It Fl k Continue processing after errors are encountered, but only on those targets that do not depend on the target whose creation caused the error. .It Fl n Display the commands that would have been executed, but do not actually execute them. .It Fl q Do not execute any commands, but exit 0 if the specified targets are up-to-date and 1, otherwise. .It Fl r Do not use the built-in rules specified in the system makefile. .It Fl s Do not echo any commands as they are executed. Equivalent to specifying .Ql Ic @ before each command line in the makefile. .It Fl t Rather than re-building a target as specified in the makefile, create it or update its modification time to make it appear up-to-date. .It Ar variable=value Set the value of the variable .Ar variable to .Ar value . .El .Pp There are six different types of lines in a makefile: file dependency specifications, shell commands, variable assignments, include statements, conditional directives, and comments. .Pp In general, lines may be continued from one line to the next by ending them with a backslash .Pq Ql \e . The trailing newline character and initial whitespace on the following line are compressed into a single space. .Sh FILE DEPENDENCY SPECIFICATIONS Dependency lines consist of one or more targets, an operator, and zero or more sources. This creates a relationship where the targets ``depend'' on the sources and are usually created from them. The exact relationship between the target and the source is determined by the operator that separates them. The three operators are as follows: .Bl -tag -width flag .It Ic \&: A target is considered out-of-date if its modification time is less than those of any of its sources. Sources for a target accumulate over dependency lines when this operator is used. The target is removed if .Nm make is interrupted. .It Ic \&! Targets are always re-created, but not until all sources have been examined and re-created as necessary. Sources for a target accumulate over dependency lines when this operator is used. The target is removed if .Nm make is interrupted. .It Ic \&:: If no sources are specified, the target is always re-created. Otherwise, a target is considered out-of-date if any of its sources has been modified more recently than the target. Sources for a target do not accumulate over dependency lines when this operator is used. The target will not be removed if .Nm make is interrupted. .El .Pp Targets and sources may contain the shell wildcard values .Ql ? , .Ql * , .Ql [] and .Ql {} . The values .Ql ? , .Ql * and .Ql [] may only be used as part of the final component of the target or source, and must be used to describe existing files. The value .Ql {} need not necessarily be used to describe existing files. Expansion is in directory order, not alphabetically as done in the shell. .Sh SHELL COMMANDS Each target may have associated with it a series of shell commands, normally used to create the target. Each of the commands in this script .Em must be preceded by a tab. While any target may appear on a dependency line, only one of these dependencies may be followed by a creation script, unless the .Ql Ic :: operator is used. .Pp If the first or first two characters of the command line are .Ql Ic @ and/or .Ql Ic \- , the command is treated specially. A .Ql Ic @ causes the command not to be echoed before it is executed. A .Ql Ic \- causes any non-zero exit status of the command line to be ignored. .Sh VARIABLE ASSIGNMENTS Variables in make are much like variables in the shell, and, by tradition, consist of all upper-case letters. The five operators that can be used to assign values to variables are as follows: .Bl -tag -width Ds .It Ic \&= Assign the value to the variable. Any previous value is overridden. .It Ic \&+= Append the value to the current value of the variable. .It Ic \&?= Assign the value to the variable if it is not already defined. .It Ic \&:= Assign with expansion, i.e. expand the value before assigning it to the variable. Normally, expansion is not done until the variable is referenced. .It Ic \&!= Expand the value and pass it to the shell for execution and assign the result to the variable. Any newlines in the result are replaced with spaces. .El .Pp Any white-space before the assigned .Ar value is removed; if the value is being appended, a single space is inserted between the previous contents of the variable and the appended value. .Pp Variables are expanded by surrounding the variable name with either curly braces .Pq Ql {} or parenthesis .Pq Ql () and preceding it with a dollar sign .Pq Ql \&$ . If the variable name contains only a single letter, the surrounding braces or parenthesis are not required. This shorter form is not recommended. .Pp Variable substitution occurs at two distinct times, depending on where the variable is being used. Variables in dependency lines are expanded as the line is read. Variables in shell commands are expanded when the shell command is executed. .Pp The four different classes of variables (in order of increasing precedence) are: .Bl -tag -width Ds .It Environment variables Variables defined as part of .Nm make Ns 's environment. .It Global variables Variables defined in the makefile or in included makefiles. .It Command line variables Variables defined as part of the command line. .It Local variables Variables that are defined specific to a certain target. The seven local variables are as follows: .Bl -tag -width ".ARCHIVE" .It Va .ALLSRC The list of all sources for this target; also known as .Ql Va \&> . .It Va .ARCHIVE The name of the archive file. .It Va .IMPSRC The name/path of the source from which the target is to be transformed (the ``implied'' source); also known as .Ql Va \&< . .It Va .MEMBER The name of the archive member. .It Va .OODATE The list of sources for this target that were deemed out-of-date; also known as .Ql Va \&? . .It Va .PREFIX The file prefix of the file, containing only the file portion, no suffix or preceding directory components; also known as .Ql Va * . .It Va .TARGET The name of the target; also known as .Ql Va @ . .El .Pp The shorter forms .Ql Va @ , .Ql Va ? , .Ql Va \&> and .Ql Va * are permitted for backward compatibility with historical makefiles and are not recommended. The six variables .Ql Va "@F" , .Ql Va "@D" , .Ql Va " or .Ql .include \*qfile\*q . Variables between the angle brackets or double quotes are expanded to form the file name. If angle brackets are used, the included makefile is expected to be in the system makefile directory. If double quotes are used, the including makefile's directory and any directories specified using the .Fl I option are searched before the system makefile directory. .Pp Conditional expressions are also preceded by a single dot as the first chraracter of a line. The possible conditionals are as follows: .Bl -tag -width Ds .It Ic .undef Ar variable Un-define the specified global variable. Only global variables may be un-defined. .It Xo .Ic \&.if .Oo \&! Oc Ns Ar expression .Op Ar operator expression ... .Xc Test the value of an expression. .It Xo .Ic .ifdef .Oo \&! Oc Ns Ar variable .Op Ar operator variable ... .Xc Test the value of an variable. .It Xo .Ic .ifndef .Oo \&! Oc Ns Ar variable .Op Ar operator variable ... .Xc Test the value of an variable. .It Xo .Ic .ifmake .Oo \&! Oc Ns Ar target .Op Ar operator target ... .Xc Test the the target being built. .It Xo .Ic .ifnmake .Oo \&! Oc Ar target .Op Ar operator target ... .Xc Test the target being built. .It Ic .else Reverse the sense of the last conditional. .It Xo .Ic .elif .Oo \&! Oc Ar expression .Op Ar operator expression ... .Xc A combination of .Ql Ic .else followed by .Ql Ic .if . .It Xo .Ic .elifdef .Oo \&! Oc Ns Ar variable .Op Ar operator variable ... .Xc A combination of .Ql Ic .else followed by .Ql Ic .ifdef . .It Xo .Ic .elifndef .Oo \&! Oc Ns Ar variable .Op Ar operator variable ... .Xc A combination of .Ql Ic .else followed by .Ql Ic .ifndef . .It Xo .Ic .elifmake .Oo \&! Oc Ns Ar target .Op Ar operator target ... .Xc A combination of .Ql Ic .else followed by .Ql Ic .ifmake . .It Xo .Ic .elifnmake .Oo \&! Oc Ns Ar target .Op Ar operator target ... .Xc A combination of .Ql Ic .else followed by .Ql Ic .ifnmake . .It Ic .endif End the body of the conditional. .El .Pp The .Ar operator may be any one of the following: .Bl -tag -width "Cm XX" .It Cm \&|\&| logical OR .It Cm \&&& Logical .Tn AND ; of higher precedence than .Dq . .El .Pp As in C, .Nm make will only evaluate a conditional as far as is necessary to determine its value. Parenthesis may be used to change the order of evaluation. The boolean operator .Ql Ic \&! may be used to logically negate an entire conditional. It is of higher precendence than .Ql Ic \&&& . .Pp The value of .Ar expression may be any of the following: .Bl -tag -width Ic defined .It Ic defined Takes a variable name as an argument and evaluates to true if the variable has been defined. .It Ic make Takes a target name as an argument and evaluates to true if the target was specified as part of .Nm make Ns 's command line or was declared the default target (either implicitly or explicitly, see .Va .MAIN ) before the line containing the conditional. .It Ic empty Takes a variable, with possible modifiers, and evalutes to true if the expansion of the variable would result in an empty string. .It Ic exists Takes a file name as an argument and evaluates to true if the file exists. The file is searched for on the system search path (see .Va .PATH ) . .It Ic target Takes a target name as an argument and evaluates to true if the target has been defined. .El .Pp .Ar Expression may also be an arithmetic or string comparison, with the left-hand side being a variable expansion. The standard C relational operators are all supported, and the usual number/base conversion is performed. Note, octal numbers are not supported. If the righthand value of a .Ql Ic == or .Ql Ic "!=" operator begins with a quotation mark .Pq Ql \*q a string comparison is done between the expanded variable and the text between the quotation marks. If no relational operator is given, it is assumed that the expanded variable is being compared against 0. .Pp When .Nm make is evaluating one of these conditional expression, and it encounters a word it doesn't recognize, either the ``make'' or ``defined'' expression is applied to it, depending on the form of the conditional. If the form is .Ql Ic .ifdef or .Ql Ic .ifndef , the ``defined'' expression is applied. Similarly, if the form is .Ql Ic .ifmake or .Ql Ic .ifnmake , the ``make'' expression is applied. .Pp If the conditional evaluates to true the parsing of the makefile continues as before. If it evaluates to false, the following lines are skipped. In both cases this continues until a .Ql Ic .else or .Ql Ic .endif is found. .Sh COMMENTS Comments begin with a hash .Pq Ql \&# character, anywhere but in a shell command line, and continue to the end of the line. .Sh SPECIAL SOURCES .Bl -tag -width Ic .IGNORE .It Ic .IGNORE Ignore any errors from the commands associated with this target, exactly as if they all were preceded by a dash .Pq Ql \- . .It Ic .MAKE Execute the commands associated with this target even if the .Fl n or .Fl t options were specified. Normally used to mark recursive .Nm make Ns 's . .It Ic .NOTMAIN Normally .Nm make selects the first target it encounters as the default target to be built if no target was specified. This source prevents this target from being selected. .It Ic .OPTIONAL If a target is marked with this attribute and .Nm make can't figure out how to create it, it will ignore this fact and assume the file isn't needed or already exists. .It Ic .PRECIOUS When .Nm make is interrupted, it removes any partially made targets. This source prevents the target from being removed. .It Ic .SILENT Do not echo any of the commands associated with this target, exactly as if they all were preceded by an at sign .Pq Ql @ . .It Ic .USE Turn the target into .Nm make Ns 's . version of a macro. When the target is used as a source for another target, the other target acquires the commands, sources, and attributes (except for .Ic .USE ) of the source. If the target already has commands, the .Ic .USE target's commands are appended to them. .El .Sh "SPECIAL TARGETS" Special targets may not be included with other targets, i.e. they must be the only target specified. .Bl -tag -width Ic .BEGIN .It Ic .BEGIN Any command lines attached to this target are executed before anything else is done. .It Ic .DEFAULT This is sort of a .Ic .USE rule for any target (that was used only as a source) that .Nm make can't figure out any other way to create. Only the shell script is used. The .Ic .IMPSRC variable of a target that inherits .Ic .DEFAULT Ns 's commands is set to the target's own name. .It Ic .END Any command lines attached to this target are executed after everything else is done. .It Ic .IGNORE Mark each of the sources with the .Ic .IGNORE attribute. If no sources are specified, this is the equivalent of specifying the .Fl i option. .It Ic .INTERRUPT If .Nm make is interrupted, the commands for this target will be executed. .It Ic .MAIN If no target is specified when .Nm make is invoked, this target will be built. .It Ic .MAKEFLAGS This target provides a way to specify flags for .Nm make when the makefile is used. The flags are as if typed to the shell, though the .Fl f option will have no effect. .It Ic .PATH The sources are directories which are to be searched for files not found in the current directory. If no sources are specified, any previously specified directories are deleted. .It Ic .PRECIOUS Apply the .Ic .PRECIOUS attribute to any specified sources. If no sources are specified, the .Ic .PRECIOUS attribute is applied to every target in the file. .It Ic .SILENT Apply the .Ic .SILENT attribute to any specified sources. If no sources are specified, the .Ic .SILENT attribute is applied to every command in the file. .It Ic .SUFFIXES Each source specifies a suffix to .Nm make . If no sources are specified, any previous specifies suffices are deleted. .Sh ENVIRONMENT .Nm Make utilizes the following environment variables, if they exist: .Ev MAKE , .Ev MAKEFLAGS and .Ev MAKEOBJDIR . .Sh FILES .Bl -tag -width /usr/share/mk -compact .It .depend list of dependencies .It Makefile list of dependencies .It makefile list of dependencies .It sys.mk system makefile .It /usr/share/mk system makefile directory .El .Sh SEE ALSO .Xr mkdep 1 .Sh HISTORY A .Nm Make command appeared in .At v7 . pmake/make.c100600 1750 1750 65202 5431142067 11440 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)make.c 5.3 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * make.c -- * The functions which perform the examination of targets and * their suitability for creation * * Interface: * Make_Run Initialize things for the module and recreate * whatever needs recreating. Returns TRUE if * work was (or would have been) done and FALSE * otherwise. * * Make_Update Update all parents of a given child. Performs * various bookkeeping chores like the updating * of the cmtime field of the parent, filling * of the IMPSRC context variable, etc. It will * place the parent on the toBeMade queue if it * should be. * * Make_TimeStamp Function to set the parent's cmtime field * based on a child's modification time. * * Make_DoAllVar Set up the various local variables for a * target, including the .ALLSRC variable, making * sure that any variable that needs to exist * at the very least has the empty value. * * Make_OODate Determine if a target is out-of-date. * * Make_HandleUse See if a child is a .USE node for a parent * and perform the .USE actions if so. */ #include "make.h" static Lst toBeMade; /* The current fringe of the graph. These * are nodes which await examination by * MakeOODate. It is added to by * Make_Update and subtracted from by * MakeStartJobs */ static int numNodes; /* Number of nodes to be processed. If this * is non-zero when Job_Empty() returns * TRUE, there's a cycle in the graph */ /*- *----------------------------------------------------------------------- * Make_TimeStamp -- * Set the cmtime field of a parent node based on the mtime stamp in its * child. Called from MakeOODate via Lst_ForEach. * * Results: * Always returns 0. * * Side Effects: * The cmtime of the parent node will be changed if the mtime * field of the child is greater than it. *----------------------------------------------------------------------- */ int Make_TimeStamp (pgn, cgn) register GNode *pgn; /* the current parent */ register GNode *cgn; /* the child we've just examined */ { if (cgn->mtime > pgn->cmtime) { pgn->cmtime = cgn->mtime; } return (0); } /*- *----------------------------------------------------------------------- * Make_OODate -- * See if a given node is out of date with respect to its sources. * Used by Make_Run when deciding which nodes to place on the * toBeMade queue initially and by Make_Update to screen out USE and * EXEC nodes. In the latter case, however, any other sort of node * must be considered out-of-date since at least one of its children * will have been recreated. * * Results: * TRUE if the node is out of date. FALSE otherwise. * * Side Effects: * The mtime field of the node and the cmtime field of its parents * will/may be changed. *----------------------------------------------------------------------- */ Boolean Make_OODate (gn) register GNode *gn; /* the node to check */ { Boolean oodate; /* * Certain types of targets needn't even be sought as their datedness * doesn't depend on their modification time... */ if ((gn->type & (OP_JOIN|OP_USE|OP_EXEC)) == 0) { (void) Dir_MTime (gn); if (DEBUG(MAKE)) { if (gn->mtime != 0) { printf ("modified %s...", Targ_FmtTime(gn->mtime)); } else { printf ("non-existent..."); } } } /* * A target is remade in one of the following circumstances: * its modification time is smaller than that of its youngest child * and it would actually be run (has commands or type OP_NOP) * it's the object of a force operator * it has no children, was on the lhs of an operator and doesn't exist * already. * * Libraries are only considered out-of-date if the archive module says * they are. * * These weird rules are brought to you by Backward-Compatability and * the strange people who wrote 'Make'. */ if (gn->type & OP_USE) { /* * If the node is a USE node it is *never* out of date * no matter *what*. */ if (DEBUG(MAKE)) { printf(".USE node..."); } oodate = FALSE; } else if (gn->type & OP_LIB) { if (DEBUG(MAKE)) { printf("library..."); } oodate = Arch_LibOODate (gn); } else if (gn->type & OP_JOIN) { /* * A target with the .JOIN attribute is only considered * out-of-date if any of its children was out-of-date. */ if (DEBUG(MAKE)) { printf(".JOIN node..."); } oodate = gn->childMade; } else if (gn->type & (OP_FORCE|OP_EXEC)) { /* * A node which is the object of the force (!) operator or which has * the .EXEC attribute is always considered out-of-date. */ if (DEBUG(MAKE)) { if (gn->type & OP_FORCE) { printf("! operator..."); } else { printf(".EXEC node..."); } } oodate = TRUE; } else if ((gn->mtime < gn->cmtime) || ((gn->cmtime == 0) && ((gn->mtime==0) || (gn->type & OP_DOUBLEDEP)))) { /* * A node whose modification time is less than that of its * youngest child or that has no children (cmtime == 0) and * either doesn't exist (mtime == 0) or was the object of a * :: operator is out-of-date. Why? Because that's the way Make does * it. */ if (DEBUG(MAKE)) { if (gn->mtime < gn->cmtime) { printf("modified before source..."); } else if (gn->mtime == 0) { printf("non-existent and no sources..."); } else { printf(":: operator and no sources..."); } } oodate = TRUE; } else { #if 0 /* WHY? */ if (DEBUG(MAKE)) { printf("source %smade...", gn->childMade ? "" : "not "); } oodate = gn->childMade; #else oodate = FALSE; #endif /* 0 */ } /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date * but aren't, because they have no commands and aren't of type OP_NOP, * have their mtime stay below their children's mtime to keep parents from * thinking they're out-of-date. */ if (!oodate) { Lst_ForEach (gn->parents, Make_TimeStamp, (ClientData)gn); } return (oodate); } /*- *----------------------------------------------------------------------- * MakeAddChild -- * Function used by Make_Run to add a child to the list l. * It will only add the child if its make field is FALSE. * * Results: * Always returns 0 * * Side Effects: * The given list is extended *----------------------------------------------------------------------- */ static int MakeAddChild (gn, l) GNode *gn; /* the node to add */ Lst l; /* the list to which to add it */ { if (!gn->make && !(gn->type & OP_USE)) { (void)Lst_EnQueue (l, (ClientData)gn); } return (0); } /*- *----------------------------------------------------------------------- * Make_HandleUse -- * Function called by Make_Run and SuffApplyTransform on the downward * pass to handle .USE and transformation nodes. A callback function * for Lst_ForEach, it implements the .USE and transformation * functionality by copying the node's commands, type flags * and children to the parent node. Should be called before the * children are enqueued to be looked at by MakeAddChild. * * A .USE node is much like an explicit transformation rule, except * its commands are always added to the target node, even if the * target already has commands. * * Results: * returns 0. * * Side Effects: * Children and commands may be added to the parent and the parent's * type may be changed. * *----------------------------------------------------------------------- */ int Make_HandleUse (cgn, pgn) register GNode *cgn; /* The .USE node */ register GNode *pgn; /* The target of the .USE node */ { register GNode *gn; /* A child of the .USE node */ register LstNode ln; /* An element in the children list */ if (cgn->type & (OP_USE|OP_TRANSFORM)) { if ((cgn->type & OP_USE) || Lst_IsEmpty(pgn->commands)) { /* * .USE or transformation and target has no commands -- append * the child's commands to the parent. */ (void) Lst_Concat (pgn->commands, cgn->commands, LST_CONCNEW); } if (Lst_Open (cgn->children) == SUCCESS) { while ((ln = Lst_Next (cgn->children)) != NILLNODE) { gn = (GNode *)Lst_Datum (ln); if (Lst_Member (pgn->children, gn) == NILLNODE) { (void) Lst_AtEnd (pgn->children, gn); (void) Lst_AtEnd (gn->parents, pgn); pgn->unmade += 1; } } Lst_Close (cgn->children); } pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM); /* * This child node is now "made", so we decrement the count of * unmade children in the parent... We also remove the child * from the parent's list to accurately reflect the number of decent * children the parent has. This is used by Make_Run to decide * whether to queue the parent or examine its children... */ if (cgn->type & OP_USE) { pgn->unmade -= 1; } } return (0); } /*- *----------------------------------------------------------------------- * Make_Update -- * Perform update on the parents of a node. Used by JobFinish once * a node has been dealt with and by MakeStartJobs if it finds an * up-to-date node. * * Results: * Always returns 0 * * Side Effects: * The unmade field of pgn is decremented and pgn may be placed on * the toBeMade queue if this field becomes 0. * * If the child was made, the parent's childMade field will be set true * and its cmtime set to now. * * If the child wasn't made, the cmtime field of the parent will be * altered if the child's mtime is big enough. * * Finally, if the child is the implied source for the parent, the * parent's IMPSRC variable is set appropriately. * *----------------------------------------------------------------------- */ void Make_Update (cgn) register GNode *cgn; /* the child node */ { register GNode *pgn; /* the parent node */ register char *cname; /* the child's name */ register LstNode ln; /* Element in parents and iParents lists */ cname = Var_Value (TARGET, cgn); /* * If the child was actually made, see what its modification time is * now -- some rules won't actually update the file. If the file still * doesn't exist, make its mtime now. */ if (cgn->made != UPTODATE) { #ifndef RECHECK /* * We can't re-stat the thing, but we can at least take care of rules * where a target depends on a source that actually creates the * target, but only if it has changed, e.g. * * parse.h : parse.o * * parse.o : parse.y * yacc -d parse.y * cc -c y.tab.c * mv y.tab.o parse.o * cmp -s y.tab.h parse.h || mv y.tab.h parse.h * * In this case, if the definitions produced by yacc haven't changed * from before, parse.h won't have been updated and cgn->mtime will * reflect the current modification time for parse.h. This is * something of a kludge, I admit, but it's a useful one.. * XXX: People like to use a rule like * * FRC: * * To force things that depend on FRC to be made, so we have to * check for gn->children being empty as well... */ if (!Lst_IsEmpty(cgn->commands) || Lst_IsEmpty(cgn->children)) { cgn->mtime = now; } #else /* * This is what Make does and it's actually a good thing, as it * allows rules like * * cmp -s y.tab.h parse.h || cp y.tab.h parse.h * * to function as intended. Unfortunately, thanks to the stateless * nature of NFS (by which I mean the loose coupling of two clients * using the same file from a common server), there are times * when the modification time of a file created on a remote * machine will not be modified before the local stat() implied by * the Dir_MTime occurs, thus leading us to believe that the file * is unchanged, wreaking havoc with files that depend on this one. * * I have decided it is better to make too much than to make too * little, so this stuff is commented out unless you're sure it's ok. * -- ardeb 1/12/88 */ if (noExecute || Dir_MTime(cgn) == 0) { cgn->mtime = now; } if (DEBUG(MAKE)) { printf("update time: %s\n", Targ_FmtTime(cgn->mtime)); } #endif } if (Lst_Open (cgn->parents) == SUCCESS) { while ((ln = Lst_Next (cgn->parents)) != NILLNODE) { pgn = (GNode *)Lst_Datum (ln); if (pgn->make) { pgn->unmade -= 1; if ( ! (cgn->type & (OP_EXEC|OP_USE))) { if (cgn->made == MADE) { pgn->childMade = TRUE; if (pgn->cmtime < cgn->mtime) { pgn->cmtime = cgn->mtime; } } else { (void)Make_TimeStamp (pgn, cgn); } } if (pgn->unmade == 0) { /* * Queue the node up -- any unmade predecessors will * be dealt with in MakeStartJobs. */ (void)Lst_EnQueue (toBeMade, (ClientData)pgn); } else if (pgn->unmade < 0) { Error ("Graph cycles through %s", pgn->name); } } } Lst_Close (cgn->parents); } /* * Deal with successor nodes. If any is marked for making and has an unmade * count of 0, has not been made and isn't in the examination queue, * it means we need to place it in the queue as it restrained itself * before. */ for (ln = Lst_First(cgn->successors); ln != NILLNODE; ln = Lst_Succ(ln)) { GNode *succ = (GNode *)Lst_Datum(ln); if (succ->make && succ->unmade == 0 && succ->made == UNMADE && Lst_Member(toBeMade, (ClientData)succ) == NILLNODE) { (void)Lst_EnQueue(toBeMade, (ClientData)succ); } } /* * Set the .PREFIX and .IMPSRC variables for all the implied parents * of this node. */ if (Lst_Open (cgn->iParents) == SUCCESS) { char *cpref = Var_Value(PREFIX, cgn); while ((ln = Lst_Next (cgn->iParents)) != NILLNODE) { pgn = (GNode *)Lst_Datum (ln); if (pgn->make) { Var_Set (IMPSRC, cname, pgn); Var_Set (PREFIX, cpref, pgn); } } Lst_Close (cgn->iParents); } } /*- *----------------------------------------------------------------------- * MakeAddAllSrc -- * Add a child's name to the ALLSRC and OODATE variables of the given * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. * .EXEC and .USE children are very rarely going to be files, so... * A child is added to the OODATE variable if its modification time is * later than that of its parent, as defined by Make, except if the * parent is a .JOIN node. In that case, it is only added to the OODATE * variable if it was actually made (since .JOIN nodes don't have * modification times, the comparison is rather unfair...).. * * Results: * Always returns 0 * * Side Effects: * The ALLSRC variable for the given node is extended. *----------------------------------------------------------------------- */ static int MakeAddAllSrc (cgn, pgn) GNode *cgn; /* The child to add */ GNode *pgn; /* The parent to whose ALLSRC variable it should be */ /* added */ { if ((cgn->type & (OP_EXEC|OP_USE|OP_INVISIBLE)) == 0) { register char *child; child = Var_Value(TARGET, cgn); Var_Append (ALLSRC, child, pgn); if (pgn->type & OP_JOIN) { if (cgn->made == MADE) { Var_Append(OODATE, child, pgn); } } else if ((pgn->mtime < cgn->mtime) || (cgn->mtime >= now && cgn->made == MADE)) { /* * It goes in the OODATE variable if the parent is younger than the * child or if the child has been modified more recently than * the start of the make. This is to keep pmake from getting * confused if something else updates the parent after the * make starts (shouldn't happen, I know, but sometimes it * does). In such a case, if we've updated the kid, the parent * is likely to have a modification time later than that of * the kid and anything that relies on the OODATE variable will * be hosed. * * XXX: This will cause all made children to go in the OODATE * variable, even if they're not touched, if RECHECK isn't defined, * since cgn->mtime is set to now in Make_Update. According to * some people, this is good... */ Var_Append(OODATE, child, pgn); } } return (0); } /*- *----------------------------------------------------------------------- * Make_DoAllVar -- * Set up the ALLSRC and OODATE variables. Sad to say, it must be * done separately, rather than while traversing the graph. This is * because Make defined OODATE to contain all sources whose modification * times were later than that of the target, *not* those sources that * were out-of-date. Since in both compatibility and native modes, * the modification time of the parent isn't found until the child * has been dealt with, we have to wait until now to fill in the * variable. As for ALLSRC, the ordering is important and not * guaranteed when in native mode, so it must be set here, too. * * Results: * None * * Side Effects: * The ALLSRC and OODATE variables of the given node is filled in. * If the node is a .JOIN node, its TARGET variable will be set to * match its ALLSRC variable. *----------------------------------------------------------------------- */ void Make_DoAllVar (gn) GNode *gn; { Lst_ForEach (gn->children, MakeAddAllSrc, gn); if (!Var_Exists (OODATE, gn)) { Var_Set (OODATE, "", gn); } if (!Var_Exists (ALLSRC, gn)) { Var_Set (ALLSRC, "", gn); } if (gn->type & OP_JOIN) { Var_Set (TARGET, Var_Value (ALLSRC, gn), gn); } } /*- *----------------------------------------------------------------------- * MakeStartJobs -- * Start as many jobs as possible. * * Results: * If the query flag was given to pmake, no job will be started, * but as soon as an out-of-date target is found, this function * returns TRUE. At all other times, this function returns FALSE. * * Side Effects: * Nodes are removed from the toBeMade queue and job table slots * are filled. * *----------------------------------------------------------------------- */ static Boolean MakeStartJobs () { register GNode *gn; while (!Job_Full() && !Lst_IsEmpty (toBeMade)) { gn = (GNode *) Lst_DeQueue (toBeMade); if (DEBUG(MAKE)) { printf ("Examining %s...", gn->name); } /* * Make sure any and all predecessors that are going to be made, * have been. */ if (!Lst_IsEmpty(gn->preds)) { LstNode ln; for (ln = Lst_First(gn->preds); ln != NILLNODE; ln = Lst_Succ(ln)){ GNode *pgn = (GNode *)Lst_Datum(ln); if (pgn->make && pgn->made == UNMADE) { if (DEBUG(MAKE)) { printf("predecessor %s not made yet.\n", pgn->name); } break; } } /* * If ln isn't nil, there's a predecessor as yet unmade, so we * just drop this node on the floor. When the node in question * has been made, it will notice this node as being ready to * make but as yet unmade and will place the node on the queue. */ if (ln != NILLNODE) { continue; } } numNodes--; if (Make_OODate (gn)) { if (DEBUG(MAKE)) { printf ("out-of-date\n"); } if (queryFlag) { return (TRUE); } Make_DoAllVar (gn); Job_Make (gn); } else { if (DEBUG(MAKE)) { printf ("up-to-date\n"); } gn->made = UPTODATE; if (gn->type & OP_JOIN) { /* * Even for an up-to-date .JOIN node, we need it to have its * context variables so references to it get the correct * value for .TARGET when building up the context variables * of its parent(s)... */ Make_DoAllVar (gn); } Make_Update (gn); } } return (FALSE); } /*- *----------------------------------------------------------------------- * MakePrintStatus -- * Print the status of a top-level node, viz. it being up-to-date * already or not created due to an error in a lower level. * Callback function for Make_Run via Lst_ForEach. * * Results: * Always returns 0. * * Side Effects: * A message may be printed. * *----------------------------------------------------------------------- */ static int MakePrintStatus(gn, cycle) GNode *gn; /* Node to examine */ Boolean cycle; /* True if gn->unmade being non-zero implies * a cycle in the graph, not an error in an * inferior */ { if (gn->made == UPTODATE) { printf ("`%s' is up to date.\n", gn->name); } else if (gn->unmade != 0) { if (cycle) { /* * If printing cycles and came to one that has unmade children, * print out the cycle by recursing on its children. Note a * cycle like: * a : b * b : c * c : b * will cause this to erroneously complain about a being in * the cycle, but this is a good approximation. */ if (gn->made == CYCLE) { Error("Graph cycles through `%s'", gn->name); gn->made = ENDCYCLE; Lst_ForEach(gn->children, MakePrintStatus, (ClientData)TRUE); gn->made = UNMADE; } else if (gn->made != ENDCYCLE) { gn->made = CYCLE; Lst_ForEach(gn->children, MakePrintStatus, (ClientData)TRUE); } } else { printf ("`%s' not remade because of errors.\n", gn->name); } } return (0); } /*- *----------------------------------------------------------------------- * Make_Run -- * Initialize the nodes to remake and the list of nodes which are * ready to be made by doing a breadth-first traversal of the graph * starting from the nodes in the given list. Once this traversal * is finished, all the 'leaves' of the graph are in the toBeMade * queue. * Using this queue and the Job module, work back up the graph, * calling on MakeStartJobs to keep the job table as full as * possible. * * Results: * TRUE if work was done. FALSE otherwise. * * Side Effects: * The make field of all nodes involved in the creation of the given * targets is set to 1. The toBeMade list is set to contain all the * 'leaves' of these subgraphs. *----------------------------------------------------------------------- */ Boolean Make_Run (targs) Lst targs; /* the initial list of targets */ { register GNode *gn; /* a temporary pointer */ register Lst examine; /* List of targets to examine */ int errors; /* Number of errors the Job module reports */ toBeMade = Lst_Init (FALSE); examine = Lst_Duplicate(targs, NOCOPY); numNodes = 0; /* * Make an initial downward pass over the graph, marking nodes to be made * as we go down. We call Suff_FindDeps to find where a node is and * to get some children for it if it has none and also has no commands. * If the node is a leaf, we stick it on the toBeMade queue to * be looked at in a minute, otherwise we add its children to our queue * and go on about our business. */ while (!Lst_IsEmpty (examine)) { gn = (GNode *) Lst_DeQueue (examine); if (!gn->make) { gn->make = TRUE; numNodes++; /* * Apply any .USE rules before looking for implicit dependencies * to make sure everything has commands that should... */ Lst_ForEach (gn->children, Make_HandleUse, (ClientData)gn); Suff_FindDeps (gn); if (gn->unmade != 0) { Lst_ForEach (gn->children, MakeAddChild, (ClientData)examine); } else { (void)Lst_EnQueue (toBeMade, (ClientData)gn); } } } Lst_Destroy (examine, NOFREE); if (queryFlag) { /* * We wouldn't do any work unless we could start some jobs in the * next loop... (we won't actually start any, of course, this is just * to see if any of the targets was out of date) */ return (MakeStartJobs()); } else { /* * Initialization. At the moment, no jobs are running and until some * get started, nothing will happen since the remaining upward * traversal of the graph is performed by the routines in job.c upon * the finishing of a job. So we fill the Job table as much as we can * before going into our loop. */ (void) MakeStartJobs(); } /* * Main Loop: The idea here is that the ending of jobs will take * care of the maintenance of data structures and the waiting for output * will cause us to be idle most of the time while our children run as * much as possible. Because the job table is kept as full as possible, * the only time when it will be empty is when all the jobs which need * running have been run, so that is the end condition of this loop. * Note that the Job module will exit if there were any errors unless the * keepgoing flag was given. */ while (!Job_Empty ()) { Job_CatchOutput (); Job_CatchChildren (!usePipes); (void)MakeStartJobs(); } errors = Job_End(); /* * Print the final status of each target. E.g. if it wasn't made * because some inferior reported an error. */ Lst_ForEach(targs, MakePrintStatus, (ClientData)((errors == 0) && (numNodes != 0))); return (TRUE); } pmake/make.h100600 1750 1750 33764 5431142067 11455 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)make.h 5.13 (Berkeley) 3/1/91 */ /*- * make.h -- * The global definitions for pmake */ #ifndef _MAKE_H_ #define _MAKE_H_ #include #include #include #include "sprite.h" #include "lst.h" #include "config.h" /*- * The structure for an individual graph node. Each node has several * pieces of data associated with it. * 1) the name of the target it describes * 2) the location of the target file in the file system. * 3) the type of operator used to define its sources (qv. parse.c) * 4) whether it is involved in this invocation of make * 5) whether the target has been remade * 6) whether any of its children has been remade * 7) the number of its children that are, as yet, unmade * 8) its modification time * 9) the modification time of its youngest child (qv. make.c) * 10) a list of nodes for which this is a source * 11) a list of nodes on which this depends * 12) a list of nodes that depend on this, as gleaned from the * transformation rules. * 13) a list of nodes of the same name created by the :: operator * 14) a list of nodes that must be made (if they're made) before * this node can be, but that do no enter into the datedness of * this node. * 15) a list of nodes that must be made (if they're made) after * this node is, but that do not depend on this node, in the * normal sense. * 16) a Lst of ``local'' variables that are specific to this target * and this target only (qv. var.c [$@ $< $?, etc.]) * 17) a Lst of strings that are commands to be given to a shell * to create this target. */ typedef struct GNode { char *name; /* The target's name */ char *path; /* The full pathname of the file */ int type; /* Its type (see the OP flags, below) */ Boolean make; /* TRUE if this target needs to be remade */ enum { UNMADE, BEINGMADE, MADE, UPTODATE, ERROR, ABORTED, CYCLE, ENDCYCLE, } made; /* Set to reflect the state of processing * on this node: * UNMADE - Not examined yet * BEINGMADE - Target is already being made. * Indicates a cycle in the graph. (compat * mode only) * MADE - Was out-of-date and has been made * UPTODATE - Was already up-to-date * ERROR - An error occured while it was being * made (used only in compat mode) * ABORTED - The target was aborted due to * an error making an inferior (compat). * CYCLE - Marked as potentially being part of * a graph cycle. If we come back to a * node marked this way, it is printed * and 'made' is changed to ENDCYCLE. * ENDCYCLE - the cycle has been completely * printed. Go back and unmark all its * members. */ Boolean childMade; /* TRUE if one of this target's children was * made */ int unmade; /* The number of unmade children */ int mtime; /* Its modification time */ int cmtime; /* The modification time of its youngest * child */ Lst iParents; /* Links to parents for which this is an * implied source, if any */ Lst cohorts; /* Other nodes for the :: operator */ Lst parents; /* Nodes that depend on this one */ Lst children; /* Nodes on which this one depends */ Lst successors; /* Nodes that must be made after this one */ Lst preds; /* Nodes that must be made before this one */ Lst context; /* The local variables */ Lst commands; /* Creation commands */ struct _Suff *suffix; /* Suffix for the node (determined by * Suff_FindDeps and opaque to everyone * but the Suff module) */ } GNode; /* * Manifest constants */ #define NILGNODE ((GNode *) NIL) /* * The OP_ constants are used when parsing a dependency line as a way of * communicating to other parts of the program the way in which a target * should be made. These constants are bitwise-OR'ed together and * placed in the 'type' field of each node. Any node that has * a 'type' field which satisfies the OP_NOP function was never never on * the lefthand side of an operator, though it may have been on the * righthand side... */ #define OP_DEPENDS 0x00000001 /* Execution of commands depends on * kids (:) */ #define OP_FORCE 0x00000002 /* Always execute commands (!) */ #define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids * per line (::) */ #define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) #define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't * exist and can't be created */ #define OP_USE 0x00000010 /* Use associated commands for parents */ #define OP_EXEC 0x00000020 /* Target is never out of date, but always * execute commands anyway. Its time * doesn't matter, so it has none...sort * of */ #define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ #define OP_PRECIOUS 0x00000080 /* Don't remove the target when * interrupted */ #define OP_SILENT 0x00000100 /* Don't echo commands when executed */ #define OP_MAKE 0x00000200 /* Target is a recurrsive make so its * commands should always be executed when * it is out of date, regardless of the * state of the -n or -t flags */ #define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its * children was out-of-date */ #define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. * I.e. it doesn't show up in the parents's * local variables. */ #define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main * target' processing in parse.c */ /* Attributes applied by PMake */ #define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ #define OP_MEMBER 0x40000000 /* Target is a member of an archive */ #define OP_LIB 0x20000000 /* Target is a library */ #define OP_ARCHV 0x10000000 /* Target is an archive construct */ #define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. * Used when parsing to catch multiple * commands for a target */ #define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ #define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ /* * OP_NOP will return TRUE if the node with the given type was not the * object of a dependency operator */ #define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) /* * The TARG_ constants are used when calling the Targ_FindNode and * Targ_FindList functions in targ.c. They simply tell the functions what to * do if the desired node(s) is (are) not found. If the TARG_CREATE constant * is given, a new, empty node will be created for the target, placed in the * table of all targets and its address returned. If TARG_NOCREATE is given, * a NIL pointer will be returned. */ #define TARG_CREATE 0x01 /* create node if not found */ #define TARG_NOCREATE 0x00 /* don't create it */ /* * There are several places where expandable buffers are used (parse.c and * var.c). This constant is merely the starting point for those buffers. If * lines tend to be much shorter than this, it would be best to reduce BSIZE. * If longer, it should be increased. Reducing it will cause more copying to * be done for longer lines, but will save space for shorter ones. In any * case, it ought to be a power of two simply because most storage allocation * schemes allocate in powers of two. */ #define BSIZE 256 /* starting size for expandable buffers */ /* * These constants are all used by the Str_Concat function to decide how the * final string should look. If STR_ADDSPACE is given, a space will be * placed between the two strings. If STR_ADDSLASH is given, a '/' will * be used instead of a space. If neither is given, no intervening characters * will be placed between the two strings in the final output. If the * STR_DOFREE bit is set, the two input strings will be freed before * Str_Concat returns. */ #define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ #define STR_DOFREE 0x02 /* free source strings after concatenation */ #define STR_ADDSLASH 0x04 /* add a slash when Str_Concat'ing */ /* * Error levels for parsing. PARSE_FATAL means the process cannot continue * once the makefile has been parsed. PARSE_WARNING means it can. Passed * as the first argument to Parse_Error. */ #define PARSE_WARNING 2 #define PARSE_FATAL 1 /* * Values returned by Cond_Eval. */ #define COND_PARSE 0 /* Parse the next lines */ #define COND_SKIP 1 /* Skip the next lines */ #define COND_INVALID 2 /* Not a conditional statement */ /* * Definitions for the "local" variables. Used only for clarity. */ #define TARGET "@" /* Target of dependency */ #define OODATE "?" /* All out-of-date sources */ #define ALLSRC ">" /* All sources */ #define IMPSRC "<" /* Source implied by transformation */ #define PREFIX "*" /* Common prefix */ #define ARCHIVE "!" /* Archive in "archive(member)" syntax */ #define MEMBER "%" /* Member in "archive(member)" syntax */ #define FTARGET "@F" /* file part of TARGET */ #define DTARGET "@D" /* directory part of TARGET */ #define FIMPSRC " are kept in the 'sysIncPath' Lst. The * targets currently being defined are kept in the 'targets' Lst. * * The variables 'fname' and 'lineno' are used to track the name * of the current file and the line number in that file so that error * messages can be more meaningful. * * Interface: * Parse_Init Initialization function which must be * called before anything else in this module * is used. * * Parse_File Function used to parse a makefile. It must * be given the name of the file, which should * already have been opened, and a function * to call to read a character from the file. * * Parse_IsVar Returns TRUE if the given line is a * variable assignment. Used by MainParseArgs * to determine if an argument is a target * or a variable assignment. Used internally * for pretty much the same thing... * * Parse_Error Function called when an error occurs in * parsing. Used by the variable and * conditional modules. * Parse_MainName Returns a Lst of the main target to create. */ #include #include #include #include "make.h" #include "buf.h" #include "pathnames.h" /* * These values are returned by ParseEOF to tell Parse_File whether to * CONTINUE parsing, i.e. it had only reached the end of an include file, * or if it's DONE. */ #define CONTINUE 1 #define DONE 0 static int ParseEOF(); static Lst targets; /* targets we're working on */ static Boolean inLine; /* true if currently in a dependency * line or its commands */ static char *fname; /* name of current file (for errors) */ static int lineno; /* line number in current file */ static FILE *curFILE; /* current makefile */ static int fatals = 0; static GNode *mainNode; /* The main target to create. This is the * first target on the first dependency * line in the first makefile */ /* * Definitions for handling #include specifications */ typedef struct IFile { char *fname; /* name of previous file */ int lineno; /* saved line number */ FILE * F; /* the open stream */ } IFile; static Lst includes; /* stack of IFiles generated by * #includes */ Lst parseIncPath; /* list of directories for "..." includes */ Lst sysIncPath; /* list of directories for <...> includes */ /*- * specType contains the SPECial TYPE of the current target. It is * Not if the target is unspecial. If it *is* special, however, the children * are linked as children of the parent but not vice versa. This variable is * set in ParseDoDependency */ typedef enum { Begin, /* .BEGIN */ Default, /* .DEFAULT */ End, /* .END */ Ignore, /* .IGNORE */ Includes, /* .INCLUDES */ Interrupt, /* .INTERRUPT */ Libs, /* .LIBS */ MFlags, /* .MFLAGS or .MAKEFLAGS */ Main, /* .MAIN and we don't have anything user-specified to * make */ Not, /* Not special */ NotParallel, /* .NOTPARALELL */ Null, /* .NULL */ Order, /* .ORDER */ Path, /* .PATH */ Precious, /* .PRECIOUS */ Shell, /* .SHELL */ Silent, /* .SILENT */ SingleShell, /* .SINGLESHELL */ Suffixes, /* .SUFFIXES */ Attribute, /* Generic attribute */ } ParseSpecial; ParseSpecial specType; /* * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER * seen, then set to each successive source on the line. */ static GNode *predecessor; /* * The parseKeywords table is searched using binary search when deciding * if a target or source is special. The 'spec' field is the ParseSpecial * type of the keyword ("Not" if the keyword isn't special as a target) while * the 'op' field is the operator to apply to the list of targets if the * keyword is used as a source ("0" if the keyword isn't special as a source) */ static struct { char *name; /* Name of keyword */ ParseSpecial spec; /* Type when used as a target */ int op; /* Operator when used as a source */ } parseKeywords[] = { { ".BEGIN", Begin, 0 }, { ".DEFAULT", Default, 0 }, { ".OPTIONAL", Attribute, OP_OPTIONAL }, { ".END", End, 0 }, { ".EXEC", Attribute, OP_EXEC }, { ".IGNORE", Ignore, OP_IGNORE }, { ".INCLUDES", Includes, 0 }, { ".INTERRUPT", Interrupt, 0 }, { ".INVISIBLE", Attribute, OP_INVISIBLE }, { ".JOIN", Attribute, OP_JOIN }, { ".LIBS", Libs, 0 }, { ".MAIN", Main, 0 }, { ".MAKE", Attribute, OP_MAKE }, { ".MAKEFLAGS", MFlags, 0 }, { ".MFLAGS", MFlags, 0 }, { ".NOTMAIN", Attribute, OP_NOTMAIN }, { ".NOTPARALLEL", NotParallel, 0 }, { ".NULL", Null, 0 }, { ".ORDER", Order, 0 }, { ".PATH", Path, 0 }, { ".PRECIOUS", Precious, OP_PRECIOUS }, { ".RECURSIVE", Attribute, OP_MAKE }, { ".SHELL", Shell, 0 }, { ".SILENT", Silent, OP_SILENT }, { ".SINGLESHELL", SingleShell, 0 }, { ".SUFFIXES", Suffixes, 0 }, { ".USE", Attribute, OP_USE }, }; /*- *---------------------------------------------------------------------- * ParseFindKeyword -- * Look in the table of keywords for one matching the given string. * * Results: * The index of the keyword, or -1 if it isn't there. * * Side Effects: * None *---------------------------------------------------------------------- */ static int ParseFindKeyword (str) char *str; /* String to find */ { register int start, end, cur; register int diff; start = 0; end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; do { cur = start + ((end - start) / 2); diff = strcmp (str, parseKeywords[cur].name); if (diff == 0) { return (cur); } else if (diff < 0) { end = cur - 1; } else { start = cur + 1; } } while (start <= end); return (-1); } /*- * Parse_Error -- * Error message abort function for parsing. Prints out the context * of the error (line number and file) as well as the message with * two optional arguments. * * Results: * None * * Side Effects: * "fatals" is incremented if the level is PARSE_FATAL. */ /* VARARGS */ void Parse_Error(type, va_alist) int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */ va_dcl { va_list ap; char *fmt; (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno); if (type == PARSE_WARNING) (void)fprintf(stderr, "warning: "); va_start(ap); fmt = va_arg(ap, char *); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fprintf(stderr, "\n"); (void)fflush(stderr); if (type == PARSE_FATAL) fatals += 1; } /*- *--------------------------------------------------------------------- * ParseLinkSrc -- * Link the parent node to its new child. Used in a Lst_ForEach by * ParseDoDependency. If the specType isn't 'Not', the parent * isn't linked as a parent of the child. * * Results: * Always = 0 * * Side Effects: * New elements are added to the parents list of cgn and the * children list of cgn. the unmade field of pgn is updated * to reflect the additional child. *--------------------------------------------------------------------- */ static int ParseLinkSrc (pgn, cgn) GNode *pgn; /* The parent node */ GNode *cgn; /* The child node */ { if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) { (void)Lst_AtEnd (pgn->children, (ClientData)cgn); if (specType == Not) { (void)Lst_AtEnd (cgn->parents, (ClientData)pgn); } pgn->unmade += 1; } return (0); } /*- *--------------------------------------------------------------------- * ParseDoOp -- * Apply the parsed operator to the given target node. Used in a * Lst_ForEach call by ParseDoDependency once all targets have * been found and their operator parsed. If the previous and new * operators are incompatible, a major error is taken. * * Results: * Always 0 * * Side Effects: * The type field of the node is altered to reflect any new bits in * the op. *--------------------------------------------------------------------- */ static int ParseDoOp (gn, op) GNode *gn; /* The node to which the operator is to be * applied */ int op; /* The operator to apply */ { /* * If the dependency mask of the operator and the node don't match and * the node has actually had an operator applied to it before, and * the operator actually has some dependency information in it, complain. */ if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && !OP_NOP(gn->type) && !OP_NOP(op)) { Parse_Error (PARSE_FATAL, "Inconsistent operator for %s", gn->name); return (1); } if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { /* * If the node was the object of a :: operator, we need to create a * new instance of it for the children and commands on this dependency * line. The new instance is placed on the 'cohorts' list of the * initial one (note the initial one is not on its own cohorts list) * and the new instance is linked to all parents of the initial * instance. */ register GNode *cohort; LstNode ln; cohort = Targ_NewGN(gn->name); /* * Duplicate links to parents so graph traversal is simple. Perhaps * some type bits should be duplicated? * * Make the cohort invisible as well to avoid duplicating it into * other variables. True, parents of this target won't tend to do * anything with their local variables, but better safe than * sorry. */ Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort); cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort); /* * Replace the node in the targets list with the new copy */ ln = Lst_Member(targets, (ClientData)gn); Lst_Replace(ln, (ClientData)cohort); gn = cohort; } /* * We don't want to nuke any previous flags (whatever they were) so we * just OR the new operator into the old */ gn->type |= op; return (0); } /*- *--------------------------------------------------------------------- * ParseDoSrc -- * Given the name of a source, figure out if it is an attribute * and apply it to the targets if it is. Else decide if there is * some attribute which should be applied *to* the source because * of some special target and apply it if so. Otherwise, make the * source be a child of the targets in the list 'targets' * * Results: * None * * Side Effects: * Operator bits may be added to the list of targets or to the source. * The targets may have a new source added to their lists of children. *--------------------------------------------------------------------- */ static void ParseDoSrc (tOp, src) int tOp; /* operator (if any) from special targets */ char *src; /* name of the source to handle */ { int op; /* operator (if any) from special source */ GNode *gn; op = 0; if (*src == '.' && isupper (src[1])) { int keywd = ParseFindKeyword(src); if (keywd != -1) { op = parseKeywords[keywd].op; } } if (op != 0) { Lst_ForEach (targets, ParseDoOp, (ClientData)op); } else if (specType == Main) { /* * If we have noted the existence of a .MAIN, it means we need * to add the sources of said target to the list of things * to create. The string 'src' is likely to be free, so we * must make a new copy of it. Note that this will only be * invoked if the user didn't specify a target on the command * line. This is to allow #ifmake's to succeed, or something... */ (void) Lst_AtEnd (create, (ClientData)strdup(src)); /* * Add the name to the .TARGETS variable as well, so the user cna * employ that, if desired. */ Var_Append(".TARGETS", src, VAR_GLOBAL); } else if (specType == Order) { /* * Create proper predecessor/successor links between the previous * source and the current one. */ gn = Targ_FindNode(src, TARG_CREATE); if (predecessor != NILGNODE) { (void)Lst_AtEnd(predecessor->successors, (ClientData)gn); (void)Lst_AtEnd(gn->preds, (ClientData)predecessor); } /* * The current source now becomes the predecessor for the next one. */ predecessor = gn; } else { /* * If the source is not an attribute, we need to find/create * a node for it. After that we can apply any operator to it * from a special target or link it to its parents, as * appropriate. * * In the case of a source that was the object of a :: operator, * the attribute is applied to all of its instances (as kept in * the 'cohorts' list of the node) or all the cohorts are linked * to all the targets. */ gn = Targ_FindNode (src, TARG_CREATE); if (tOp) { gn->type |= tOp; } else { Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn); } if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { register GNode *cohort; register LstNode ln; for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){ cohort = (GNode *)Lst_Datum(ln); if (tOp) { cohort->type |= tOp; } else { Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort); } } } } } /*- *----------------------------------------------------------------------- * ParseFindMain -- * Find a real target in the list and set it to be the main one. * Called by ParseDoDependency when a main target hasn't been found * yet. * * Results: * 0 if main not found yet, 1 if it is. * * Side Effects: * mainNode is changed and Targ_SetMain is called. * *----------------------------------------------------------------------- */ static int ParseFindMain(gn) GNode *gn; /* Node to examine */ { if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) { mainNode = gn; Targ_SetMain(gn); return (1); } else { return (0); } } /*- *----------------------------------------------------------------------- * ParseAddDir -- * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_AddDir. * *----------------------------------------------------------------------- */ static int ParseAddDir(path, name) Lst path; char *name; { Dir_AddDir(path, name); return(0); } /*- *----------------------------------------------------------------------- * ParseClearPath -- * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going * * Results: * === 0 * * Side Effects: * See Dir_ClearPath * *----------------------------------------------------------------------- */ static int ParseClearPath(path) Lst path; { Dir_ClearPath(path); return(0); } /*- *--------------------------------------------------------------------- * ParseDoDependency -- * Parse the dependency line in line. * * Results: * None * * Side Effects: * The nodes of the sources are linked as children to the nodes of the * targets. Some nodes may be created. * * We parse a dependency line by first extracting words from the line and * finding nodes in the list of all targets with that name. This is done * until a character is encountered which is an operator character. Currently * these are only ! and :. At this point the operator is parsed and the * pointer into the line advanced until the first source is encountered. * The parsed operator is applied to each node in the 'targets' list, * which is where the nodes found for the targets are kept, by means of * the ParseDoOp function. * The sources are read in much the same way as the targets were except * that now they are expanded using the wildcarding scheme of the C-Shell * and all instances of the resulting words in the list of all targets * are found. Each of the resulting nodes is then linked to each of the * targets as one of its children. * Certain targets are handled specially. These are the ones detailed * by the specType variable. * The storing of transformation rules is also taken care of here. * A target is recognized as a transformation rule by calling * Suff_IsTransform. If it is a transformation rule, its node is gotten * from the suffix module via Suff_AddTransform rather than the standard * Targ_FindNode in the target module. *--------------------------------------------------------------------- */ static void ParseDoDependency (line) char *line; /* the line to parse */ { register char *cp; /* our current position */ register GNode *gn; /* a general purpose temporary node */ register int op; /* the operator on the line */ char savec; /* a place to save a character */ Lst paths; /* List of search paths to alter when parsing * a list of .PATH targets */ int tOp; /* operator from special target */ Lst sources; /* list of source names after expansion */ Lst curTargs; /* list of target names to be found and added * to the targets list */ tOp = 0; specType = Not; paths = (Lst)NULL; curTargs = Lst_Init(FALSE); do { for (cp = line; *cp && !isspace (*cp) && (*cp != '!') && (*cp != ':') && (*cp != '('); cp++) { if (*cp == '$') { /* * Must be a dynamic source (would have been expanded * otherwise), so call the Var module to parse the puppy * so we can safely advance beyond it...There should be * no errors in this, as they would have been discovered * in the initial Var_Subst and we wouldn't be here. */ int length; Boolean freeIt; char *result; result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); if (freeIt) { free(result); } cp += length-1; } continue; } if (*cp == '(') { /* * Archives must be handled specially to make sure the OP_ARCHV * flag is set in their 'type' field, for one thing, and because * things like "archive(file1.o file2.o file3.o)" are permissible. * Arch_ParseArchive will set 'line' to be the first non-blank * after the archive-spec. It creates/finds nodes for the members * and places them on the given list, returning SUCCESS if all * went well and FAILURE if there was an error in the * specification. On error, line should remain untouched. */ if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) { Parse_Error (PARSE_FATAL, "Error in archive specification: \"%s\"", line); return; } else { continue; } } savec = *cp; if (!*cp) { /* * Ending a dependency line without an operator is a Bozo * no-no */ Parse_Error (PARSE_FATAL, "Need an operator"); return; } *cp = '\0'; /* * Have a word in line. See if it's a special target and set * specType to match it. */ if (*line == '.' && isupper (line[1])) { /* * See if the target is a special target that must have it * or its sources handled specially. */ int keywd = ParseFindKeyword(line); if (keywd != -1) { if (specType == Path && parseKeywords[keywd].spec != Path) { Parse_Error(PARSE_FATAL, "Mismatched special targets"); return; } specType = parseKeywords[keywd].spec; tOp = parseKeywords[keywd].op; /* * Certain special targets have special semantics: * .PATH Have to set the dirSearchPath * variable too * .MAIN Its sources are only used if * nothing has been specified to * create. * .DEFAULT Need to create a node to hang * commands on, but we don't want * it in the graph, nor do we want * it to be the Main Target, so we * create it, set OP_NOTMAIN and * add it to the list, setting * DEFAULT to the new node for * later use. We claim the node is * A transformation rule to make * life easier later, when we'll * use Make_HandleUse to actually * apply the .DEFAULT commands. * .BEGIN * .END * .INTERRUPT Are not to be considered the * main target. * .NOTPARALLEL Make only one target at a time. * .SINGLESHELL Create a shell for each command. * .ORDER Must set initial predecessor to NIL */ switch (specType) { case Path: if (paths == NULL) { paths = Lst_Init(FALSE); } (void)Lst_AtEnd(paths, (ClientData)dirSearchPath); break; case Main: if (!Lst_IsEmpty(create)) { specType = Not; } break; case Begin: case End: case Interrupt: gn = Targ_FindNode(line, TARG_CREATE); gn->type |= OP_NOTMAIN; (void)Lst_AtEnd(targets, (ClientData)gn); break; case Default: gn = Targ_NewGN(".DEFAULT"); gn->type |= (OP_NOTMAIN|OP_TRANSFORM); (void)Lst_AtEnd(targets, (ClientData)gn); DEFAULT = gn; break; case NotParallel: { extern int maxJobs; maxJobs = 1; break; } case SingleShell: /* backwards = 1; */ break; case Order: predecessor = NILGNODE; break; } } else if (strncmp (line, ".PATH", 5) == 0) { /* * .PATH has to be handled specially. * Call on the suffix module to give us a path to * modify. */ Lst path; specType = Path; path = Suff_GetPath (&line[5]); if (path == NILLST) { Parse_Error (PARSE_FATAL, "Suffix '%s' not defined (yet)", &line[5]); return; } else { if (paths == (Lst)NULL) { paths = Lst_Init(FALSE); } (void)Lst_AtEnd(paths, (ClientData)path); } } } /* * Have word in line. Get or create its node and stick it at * the end of the targets list */ if ((specType == Not) && (*line != '\0')) { if (Dir_HasWildcards(line)) { /* * Targets are to be sought only in the current directory, * so create an empty path for the thing. Note we need to * use Dir_Destroy in the destruction of the path as the * Dir module could have added a directory to the path... */ Lst emptyPath = Lst_Init(FALSE); Dir_Expand(line, emptyPath, curTargs); Lst_Destroy(emptyPath, Dir_Destroy); } else { /* * No wildcards, but we want to avoid code duplication, * so create a list with the word on it. */ (void)Lst_AtEnd(curTargs, (ClientData)line); } while(!Lst_IsEmpty(curTargs)) { char *targName = (char *)Lst_DeQueue(curTargs); if (!Suff_IsTransform (targName)) { gn = Targ_FindNode (targName, TARG_CREATE); } else { gn = Suff_AddTransform (targName); } (void)Lst_AtEnd (targets, (ClientData)gn); } } else if (specType == Path && *line != '.' && *line != '\0') { Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); } *cp = savec; /* * If it is a special type and not .PATH, it's the only target we * allow on this line... */ if (specType != Not && specType != Path) { Boolean warn = FALSE; while ((*cp != '!') && (*cp != ':') && *cp) { if (*cp != ' ' && *cp != '\t') { warn = TRUE; } cp++; } if (warn) { Parse_Error(PARSE_WARNING, "Extra target ignored"); } } else { while (*cp && isspace (*cp)) { cp++; } } line = cp; } while ((*line != '!') && (*line != ':') && *line); /* * Don't need the list of target names anymore... */ Lst_Destroy(curTargs, NOFREE); if (!Lst_IsEmpty(targets)) { switch(specType) { default: Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); break; case Default: case Begin: case End: case Interrupt: /* * These four create nodes on which to hang commands, so * targets shouldn't be empty... */ case Not: /* * Nothing special here -- targets can be empty if it wants. */ break; } } /* * Have now parsed all the target names. Must parse the operator next. The * result is left in op . */ if (*cp == '!') { op = OP_FORCE; } else if (*cp == ':') { if (cp[1] == ':') { op = OP_DOUBLEDEP; cp++; } else { op = OP_DEPENDS; } } else { Parse_Error (PARSE_FATAL, "Missing dependency operator"); return; } cp++; /* Advance beyond operator */ Lst_ForEach (targets, ParseDoOp, (ClientData)op); /* * Get to the first source */ while (*cp && isspace (*cp)) { cp++; } line = cp; /* * Several special targets take different actions if present with no * sources: * a .SUFFIXES line with no sources clears out all old suffixes * a .PRECIOUS line makes all targets precious * a .IGNORE line ignores errors for all targets * a .SILENT line creates silence when making all targets * a .PATH removes all directories from the search path(s). */ if (!*line) { switch (specType) { case Suffixes: Suff_ClearSuffixes (); break; case Precious: allPrecious = TRUE; break; case Ignore: ignoreErrors = TRUE; break; case Silent: beSilent = TRUE; break; case Path: Lst_ForEach(paths, ParseClearPath, (ClientData)NULL); break; } } else if (specType == MFlags) { /* * Call on functions in main.c to deal with these arguments and * set the initial character to a null-character so the loop to * get sources won't get anything */ Main_ParseArgLine (line); *line = '\0'; } else if (specType == Shell) { if (Job_ParseShell (line) != SUCCESS) { Parse_Error (PARSE_FATAL, "improper shell specification"); return; } *line = '\0'; } else if ((specType == NotParallel) || (specType == SingleShell)) { *line = '\0'; } /* * NOW GO FOR THE SOURCES */ if ((specType == Suffixes) || (specType == Path) || (specType == Includes) || (specType == Libs) || (specType == Null)) { while (*line) { /* * If the target was one that doesn't take files as its sources * but takes something like suffixes, we take each * space-separated word on the line as a something and deal * with it accordingly. * * If the target was .SUFFIXES, we take each source as a * suffix and add it to the list of suffixes maintained by the * Suff module. * * If the target was a .PATH, we add the source as a directory * to search on the search path. * * If it was .INCLUDES, the source is taken to be the suffix of * files which will be #included and whose search path should * be present in the .INCLUDES variable. * * If it was .LIBS, the source is taken to be the suffix of * files which are considered libraries and whose search path * should be present in the .LIBS variable. * * If it was .NULL, the source is the suffix to use when a file * has no valid suffix. */ char savec; while (*cp && !isspace (*cp)) { cp++; } savec = *cp; *cp = '\0'; switch (specType) { case Suffixes: Suff_AddSuffix (line); break; case Path: Lst_ForEach(paths, ParseAddDir, (ClientData)line); break; case Includes: Suff_AddInclude (line); break; case Libs: Suff_AddLib (line); break; case Null: Suff_SetNull (line); break; } *cp = savec; if (savec != '\0') { cp++; } while (*cp && isspace (*cp)) { cp++; } line = cp; } if (paths) { Lst_Destroy(paths, NOFREE); } } else { while (*line) { /* * The targets take real sources, so we must beware of archive * specifications (i.e. things with left parentheses in them) * and handle them accordingly. */ while (*cp && !isspace (*cp)) { if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) { /* * Only stop for a left parenthesis if it isn't at the * start of a word (that'll be for variable changes * later) and isn't preceded by a dollar sign (a dynamic * source). */ break; } else { cp++; } } if (*cp == '(') { GNode *gn; sources = Lst_Init (FALSE); if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) { Parse_Error (PARSE_FATAL, "Error in source archive spec \"%s\"", line); return; } while (!Lst_IsEmpty (sources)) { gn = (GNode *) Lst_DeQueue (sources); ParseDoSrc (tOp, gn->name); } Lst_Destroy (sources, NOFREE); cp = line; } else { if (*cp) { *cp = '\0'; cp += 1; } ParseDoSrc (tOp, line); } while (*cp && isspace (*cp)) { cp++; } line = cp; } } if (mainNode == NILGNODE) { /* * If we have yet to decide on a main target to make, in the * absence of any user input, we want the first target on * the first dependency line that is actually a real target * (i.e. isn't a .USE or .EXEC rule) to be made. */ Lst_ForEach (targets, ParseFindMain, (ClientData)0); } } /*- *--------------------------------------------------------------------- * Parse_IsVar -- * Return TRUE if the passed line is a variable assignment. A variable * assignment consists of a single word followed by optional whitespace * followed by either a += or an = operator. * This function is used both by the Parse_File function and main when * parsing the command-line arguments. * * Results: * TRUE if it is. FALSE if it ain't * * Side Effects: * none *--------------------------------------------------------------------- */ Boolean Parse_IsVar (line) register char *line; /* the line to check */ { register Boolean wasSpace = FALSE; /* set TRUE if found a space */ register Boolean haveName = FALSE; /* Set TRUE if have a variable name */ /* * Skip to variable name */ while ((*line == ' ') || (*line == '\t')) { line++; } while (*line != '=') { if (*line == '\0') { /* * end-of-line -- can't be a variable assignment. */ return (FALSE); } else if ((*line == ' ') || (*line == '\t')) { /* * there can be as much white space as desired so long as there is * only one word before the operator */ wasSpace = TRUE; } else if (wasSpace && haveName) { /* * Stop when an = operator is found. */ if ((*line == '+') || (*line == ':') || (*line == '?') || (*line == '!')) { break; } /* * This is the start of another word, so not assignment. */ return (FALSE); } else { haveName = TRUE; wasSpace = FALSE; } line++; } /* * A final check: if we stopped on a +, ?, ! or :, the next character must * be an = or it ain't a valid assignment */ if (((*line == '+') || (*line == '?') || (*line == ':') || (*line == '!')) && (line[1] != '=')) { return (FALSE); } else { return (haveName); } } /*- *--------------------------------------------------------------------- * Parse_DoVar -- * Take the variable assignment in the passed line and do it in the * global context. * * Note: There is a lexical ambiguity with assignment modifier characters * in variable names. This routine interprets the character before the = * as a modifier. Therefore, an assignment like * C++=/usr/bin/CC * is interpreted as "C+ +=" instead of "C++ =". * * Results: * none * * Side Effects: * the variable structure of the given variable name is altered in the * global context. *--------------------------------------------------------------------- */ void Parse_DoVar (line, ctxt) char *line; /* a line guaranteed to be a variable * assignment. This reduces error checks */ GNode *ctxt; /* Context in which to do the assignment */ { register char *cp; /* pointer into line */ enum { VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL } type; /* Type of assignment */ char *opc; /* ptr to operator character to * null-terminate the variable name */ /* * Skip to variable name */ while ((*line == ' ') || (*line == '\t')) { line++; } /* * Skip to operator character, nulling out whitespace as we go */ for (cp = line + 1; *cp != '='; cp++) { if (isspace (*cp)) { *cp = '\0'; } } opc = cp-1; /* operator is the previous character */ *cp++ = '\0'; /* nuke the = */ /* * Check operator type */ switch (*opc) { case '+': type = VAR_APPEND; *opc = '\0'; break; case '?': /* * If the variable already has a value, we don't do anything. */ *opc = '\0'; if (Var_Exists(line, ctxt)) { return; } else { type = VAR_NORMAL; } break; case ':': type = VAR_SUBST; *opc = '\0'; break; case '!': type = VAR_SHELL; *opc = '\0'; break; default: type = VAR_NORMAL; break; } while (isspace (*cp)) { cp++; } if (type == VAR_APPEND) { Var_Append (line, cp, ctxt); } else if (type == VAR_SUBST) { /* * Allow variables in the old value to be undefined, but leave their * invocation alone -- this is done by forcing oldVars to be false. * XXX: This can cause recursive variables, but that's not hard to do, * and this allows someone to do something like * * CFLAGS = $(.INCLUDES) * CFLAGS := -I.. $(CFLAGS) * * And not get an error. */ Boolean oldOldVars = oldVars; oldVars = FALSE; cp = Var_Subst(cp, ctxt, FALSE); oldVars = oldOldVars; Var_Set(line, cp, ctxt); free(cp); } else if (type == VAR_SHELL) { char result[BUFSIZ]; /* Result of command */ char *args[4]; /* Args for invoking the shell */ int fds[2]; /* Pipe streams */ int cpid; /* Child PID */ int pid; /* PID from wait() */ Boolean freeCmd; /* TRUE if the command needs to be freed, i.e. * if any variable expansion was performed */ /* * Set up arguments for shell */ args[0] = "sh"; args[1] = "-c"; if (index(cp, '$') != (char *)NULL) { /* * There's a dollar sign in the command, so perform variable * expansion on the whole thing. The resulting string will need * freeing when we're done, so set freeCmd to TRUE. */ args[2] = Var_Subst(cp, VAR_CMD, TRUE); freeCmd = TRUE; } else { args[2] = cp; freeCmd = FALSE; } args[3] = (char *)NULL; /* * Open a pipe for fetching its output */ pipe(fds); /* * Fork */ cpid = vfork(); if (cpid == 0) { /* * Close input side of pipe */ close(fds[0]); /* * Duplicate the output stream to the shell's output, then * shut the extra thing down. Note we don't fetch the error * stream...why not? Why? */ dup2(fds[1], 1); close(fds[1]); execv("/bin/sh", args); _exit(1); } else if (cpid < 0) { /* * Couldn't fork -- tell the user and make the variable null */ Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp); Var_Set(line, "", ctxt); } else { int status; int cc; /* * No need for the writing half */ close(fds[1]); /* * Wait for the process to exit. * * XXX: If the child writes more than a pipe's worth, we will * deadlock. */ while(((pid = wait(&status)) != cpid) && (pid >= 0)) { ; } /* * Read all the characters the child wrote. */ cc = read(fds[0], result, sizeof(result)); if (cc < 0) { /* * Couldn't read the child's output -- tell the user and * set the variable to null */ Parse_Error(PARSE_WARNING, "Couldn't read shell's output"); cc = 0; } if (status) { /* * Child returned an error -- tell the user but still use * the result. */ Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp); } /* * Null-terminate the result, convert newlines to spaces and * install it in the variable. */ result[cc] = '\0'; cp = &result[cc] - 1; if (*cp == '\n') { /* * A final newline is just stripped */ *cp-- = '\0'; } while (cp >= result) { if (*cp == '\n') { *cp = ' '; } cp--; } Var_Set(line, result, ctxt); /* * Close the input side of the pipe. */ close(fds[0]); } if (freeCmd) { free(args[2]); } } else { /* * Normal assignment -- just do it. */ Var_Set (line, cp, ctxt); } } /*- * ParseAddCmd -- * Lst_ForEach function to add a command line to all targets * * Results: * Always 0 * * Side Effects: * A new element is added to the commands list of the node. */ static ParseAddCmd(gn, cmd) GNode *gn; /* the node to which the command is to be added */ char *cmd; /* the command to add */ { /* if target already supplied, ignore commands */ if (!(gn->type & OP_HAS_COMMANDS)) (void)Lst_AtEnd(gn->commands, (ClientData)cmd); return(0); } /*- *----------------------------------------------------------------------- * ParseHasCommands -- * Callback procedure for Parse_File when destroying the list of * targets on the last dependency line. Marks a target as already * having commands if it does, to keep from having shell commands * on multiple dependency lines. * * Results: * Always 0. * * Side Effects: * OP_HAS_COMMANDS may be set for the target. * *----------------------------------------------------------------------- */ static int ParseHasCommands(gn) GNode *gn; /* Node to examine */ { if (!Lst_IsEmpty(gn->commands)) { gn->type |= OP_HAS_COMMANDS; } return(0); } /*- *----------------------------------------------------------------------- * Parse_AddIncludeDir -- * Add a directory to the path searched for included makefiles * bracketed by double-quotes. Used by functions in main.c * * Results: * None. * * Side Effects: * The directory is appended to the list. * *----------------------------------------------------------------------- */ void Parse_AddIncludeDir (dir) char *dir; /* The name of the directory to add */ { Dir_AddDir (parseIncPath, dir); } /*- *--------------------------------------------------------------------- * ParseDoInclude -- * Push to another file. * * The input is the line minus the #include. A file spec is a string * enclosed in <> or "". The former is looked for only in sysIncPath. * The latter in . and the directories specified by -I command line * options * * Results: * None * * Side Effects: * A structure is added to the includes Lst and readProc, lineno, * fname and curFILE are altered for the new file *--------------------------------------------------------------------- */ static void ParseDoInclude (file) char *file; /* file specification */ { char *fullname; /* full pathname of file */ IFile *oldFile; /* state associated with current file */ Lst path; /* the path to use to find the file */ char endc; /* the character which ends the file spec */ char *cp; /* current position in file spec */ Boolean isSystem; /* TRUE if makefile is a system makefile */ /* * Skip to delimiter character so we know where to look */ while ((*file == ' ') || (*file == '\t')) { file++; } if ((*file != '"') && (*file != '<')) { Parse_Error (PARSE_FATAL, ".include filename must be delimited by '\"' or '<'"); return; } /* * Set the search path on which to find the include file based on the * characters which bracket its name. Angle-brackets imply it's * a system Makefile while double-quotes imply it's a user makefile */ if (*file == '<') { isSystem = TRUE; endc = '>'; } else { isSystem = FALSE; endc = '"'; } /* * Skip to matching delimiter */ for (cp = ++file; *cp && *cp != endc; cp++) { continue; } if (*cp != endc) { Parse_Error (PARSE_FATAL, "Unclosed .include filename. '%c' expected", endc); return; } *cp = '\0'; /* * Substitute for any variables in the file name before trying to * find the thing. */ file = Var_Subst (file, VAR_CMD, FALSE); /* * Now we know the file's name and its search path, we attempt to * find the durn thing. A return of NULL indicates the file don't * exist. */ if (!isSystem) { /* * Include files contained in double-quotes are first searched for * relative to the including file's location. We don't want to * cd there, of course, so we just tack on the old file's * leading path components and call Dir_FindFile to see if * we can locate the beast. */ char *prefEnd; prefEnd = rindex (fname, '/'); if (prefEnd != (char *)NULL) { char *newName; *prefEnd = '\0'; newName = str_concat (fname, file, STR_ADDSLASH); fullname = Dir_FindFile (newName, parseIncPath); if (fullname == (char *)NULL) { fullname = Dir_FindFile(newName, dirSearchPath); } free (newName); *prefEnd = '/'; } else { fullname = (char *)NULL; } } else { fullname = (char *)NULL; } if (fullname == (char *)NULL) { /* * System makefile or makefile wasn't found in same directory as * included makefile. Search for it first on the -I search path, * then on the .PATH search path, if not found in a -I directory. * XXX: Suffix specific? */ fullname = Dir_FindFile (file, parseIncPath); if (fullname == (char *)NULL) { fullname = Dir_FindFile(file, dirSearchPath); } } if (fullname == (char *)NULL) { /* * Still haven't found the makefile. Look for it on the system * path as a last resort. */ fullname = Dir_FindFile(file, sysIncPath); } if (fullname == (char *) NULL) { *cp = endc; Parse_Error (PARSE_FATAL, "Could not find %s", file); return; } /* * Once we find the absolute path to the file, we get to save all the * state from the current file before we can start reading this * include file. The state is stored in an IFile structure which * is placed on a list with other IFile structures. The list makes * a very nice stack to track how we got here... */ oldFile = (IFile *) emalloc (sizeof (IFile)); oldFile->fname = fname; oldFile->F = curFILE; oldFile->lineno = lineno; (void) Lst_AtFront (includes, (ClientData)oldFile); /* * Once the previous state has been saved, we can get down to reading * the new file. We set up the name of the file to be the absolute * name of the include file so error messages refer to the right * place. Naturally enough, we start reading at line number 0. */ fname = fullname; lineno = 0; curFILE = fopen (fullname, "r"); if (curFILE == (FILE * ) NULL) { Parse_Error (PARSE_FATAL, "Cannot open %s", fullname); /* * Pop to previous file */ (void) ParseEOF(0); } } /*- *--------------------------------------------------------------------- * ParseEOF -- * Called when EOF is reached in the current file. If we were reading * an include file, the includes stack is popped and things set up * to go back to reading the previous file at the previous location. * * Results: * CONTINUE if there's more to do. DONE if not. * * Side Effects: * The old curFILE, is closed. The includes list is shortened. * lineno, curFILE, and fname are changed if CONTINUE is returned. *--------------------------------------------------------------------- */ static int ParseEOF (opened) int opened; { IFile *ifile; /* the state on the top of the includes stack */ if (Lst_IsEmpty (includes)) { return (DONE); } ifile = (IFile *) Lst_DeQueue (includes); free (fname); fname = ifile->fname; lineno = ifile->lineno; if (opened) (void) fclose (curFILE); curFILE = ifile->F; free ((Address)ifile); return (CONTINUE); } /*- *--------------------------------------------------------------------- * ParseReadc -- * Read a character from the current file and update the line number * counter as necessary * * Results: * The character that was read * * Side Effects: * The lineno counter is incremented if the character is a newline *--------------------------------------------------------------------- */ #ifdef notdef static int parseReadChar; #define ParseReadc() (((parseReadChar = getc(curFILE)) == '\n') ? \ (lineno++, '\n') : parseReadChar) #else #define ParseReadc() (getc(curFILE)) #endif /* notdef */ /*- *--------------------------------------------------------------------- * ParseReadLine -- * Read an entire line from the input file. Called only by Parse_File. * To facilitate escaped newlines and what have you, a character is * buffered in 'lastc', which is '\0' when no characters have been * read. When we break out of the loop, c holds the terminating * character and lastc holds a character that should be added to * the line (unless we don't read anything but a terminator). * * Results: * A line w/o its newline * * Side Effects: * Only those associated with reading a character *--------------------------------------------------------------------- */ static char * ParseReadLine () { Buffer buf; /* Buffer for current line */ register int c; /* the current character */ register int lastc; /* The most-recent character */ Boolean semiNL; /* treat semi-colons as newlines */ Boolean ignDepOp; /* TRUE if should ignore dependency operators * for the purposes of setting semiNL */ Boolean ignComment; /* TRUE if should ignore comments (in a * shell command */ char *line; /* Result */ int lineLength; /* Length of result */ semiNL = FALSE; ignDepOp = FALSE; ignComment = FALSE; /* * Handle special-characters at the beginning of the line. Either a * leading tab (shell command) or pound-sign (possible conditional) * forces us to ignore comments and dependency operators and treat * semi-colons as semi-colons (by leaving semiNL FALSE). This also * discards completely blank lines. */ while(1) { c = ParseReadc(); if (c == '\t') { ignComment = ignDepOp = TRUE; break; } else if (c == '.') { ignComment = TRUE; break; } else if (c == '\n') { lineno++; } else if (c == '#') { ungetc(c, curFILE); break; } else { /* * Anything else breaks out without doing anything */ break; } } if (c != EOF) { lastc = c; buf = Buf_Init(BSIZE); while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) && (c != EOF)) { test_char: switch(c) { case '\n': /* * Escaped newline: read characters until a non-space or an * unescaped newline and replace them all by a single space. * This is done by storing the space over the backslash and * dropping through with the next nonspace. If it is a * semi-colon and semiNL is TRUE, it will be recognized as a * newline in the code below this... */ lineno++; lastc = ' '; while ((c = ParseReadc ()) == ' ' || c == '\t') { continue; } if (c == EOF || c == '\n') { goto line_read; } else { /* * Check for comments, semiNL's, etc. -- easier than * ungetc(c, curFILE); continue; */ goto test_char; } break; case ';': /* * Semi-colon: Need to see if it should be interpreted as a * newline */ if (semiNL) { /* * To make sure the command that may be following this * semi-colon begins with a tab, we push one back into the * input stream. This will overwrite the semi-colon in the * buffer. If there is no command following, this does no * harm, since the newline remains in the buffer and the * whole line is ignored. */ ungetc('\t', curFILE); goto line_read; } break; case '=': if (!semiNL) { /* * Haven't seen a dependency operator before this, so this * must be a variable assignment -- don't pay attention to * dependency operators after this. */ ignDepOp = TRUE; } else if (lastc == ':' || lastc == '!') { /* * Well, we've seen a dependency operator already, but it * was the previous character, so this is really just an * expanded variable assignment. Revert semi-colons to * being just semi-colons again and ignore any more * dependency operators. * * XXX: Note that a line like "foo : a:=b" will blow up, * but who'd write a line like that anyway? */ ignDepOp = TRUE; semiNL = FALSE; } break; case '#': if (!ignComment) { /* * If the character is a hash mark and it isn't escaped * (or we're being compatible), the thing is a comment. * Skip to the end of the line. */ do { c = ParseReadc(); } while ((c != '\n') && (c != EOF)); goto line_read; } break; case ':': case '!': if (!ignDepOp && (c == ':' || c == '!')) { /* * A semi-colon is recognized as a newline only on * dependency lines. Dependency lines are lines with a * colon or an exclamation point. Ergo... */ semiNL = TRUE; } break; } /* * Copy in the previous character and save this one in lastc. */ Buf_AddByte (buf, (Byte)lastc); lastc = c; } line_read: lineno++; if (lastc != '\0') { Buf_AddByte (buf, (Byte)lastc); } Buf_AddByte (buf, (Byte)'\0'); line = (char *)Buf_GetAll (buf, &lineLength); Buf_Destroy (buf, FALSE); if (line[0] == '.') { /* * The line might be a conditional. Ask the conditional module * about it and act accordingly */ switch (Cond_Eval (line)) { case COND_SKIP: do { /* * Skip to next conditional that evaluates to COND_PARSE. */ free (line); c = ParseReadc(); /* * Skip lines until get to one that begins with a * special char. */ while ((c != '.') && (c != EOF)) { while (((c != '\n') || (lastc == '\\')) && (c != EOF)) { /* * Advance to next unescaped newline */ if ((lastc = c) == '\n') { lineno++; } c = ParseReadc(); } lineno++; lastc = c; c = ParseReadc (); } if (c == EOF) { Parse_Error (PARSE_FATAL, "Unclosed conditional"); return ((char *)NULL); } /* * Read the entire line into buf */ buf = Buf_Init (BSIZE); do { Buf_AddByte (buf, (Byte)c); c = ParseReadc(); } while ((c != '\n') && (c != EOF)); lineno++; Buf_AddByte (buf, (Byte)'\0'); line = (char *)Buf_GetAll (buf, &lineLength); Buf_Destroy (buf, FALSE); } while (Cond_Eval(line) != COND_PARSE); /*FALLTHRU*/ case COND_PARSE: free (line); line = ParseReadLine(); break; } } return (line); } else { /* * Hit end-of-file, so return a NULL line to indicate this. */ return((char *)NULL); } } /*- *----------------------------------------------------------------------- * ParseFinishLine -- * Handle the end of a dependency group. * * Results: * Nothing. * * Side Effects: * inLine set FALSE. 'targets' list destroyed. * *----------------------------------------------------------------------- */ static void ParseFinishLine() { extern int Suff_EndTransform(); if (inLine) { Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL); Lst_Destroy (targets, ParseHasCommands); inLine = FALSE; } } /*- *--------------------------------------------------------------------- * Parse_File -- * Parse a file into its component parts, incorporating it into the * current dependency graph. This is the main function and controls * almost every other function in this module * * Results: * None * * Side Effects: * Loads. Nodes are added to the list of all targets, nodes and links * are added to the dependency graph. etc. etc. etc. *--------------------------------------------------------------------- */ void Parse_File(name, stream) char *name; /* the name of the file being read */ FILE * stream; /* Stream open to makefile to parse */ { register char *cp, /* pointer into the line */ *line; /* the line we're working on */ inLine = FALSE; fname = name; curFILE = stream; lineno = 0; fatals = 0; do { while (line = ParseReadLine ()) { if (*line == '.') { /* * Lines that begin with the special character are either * include or undef directives. */ for (cp = line + 1; isspace (*cp); cp++) { continue; } if (strncmp (cp, "include", 7) == 0) { ParseDoInclude (cp + 7); goto nextLine; } else if (strncmp(cp, "undef", 5) == 0) { char *cp2; for (cp += 5; isspace(*cp); cp++) { continue; } for (cp2 = cp; !isspace(*cp2) && (*cp2 != '\0'); cp2++) { continue; } *cp2 = '\0'; Var_Delete(cp, VAR_GLOBAL); goto nextLine; } } if (*line == '#') { /* If we're this far, the line must be a comment. */ goto nextLine; } if (*line == '\t' #ifdef POSIX || *line == ' ' #endif ) { /* * If a line starts with a tab (or space in POSIX-land), it * can only hope to be a creation command. */ shellCommand: for (cp = line + 1; isspace (*cp); cp++) { continue; } if (*cp) { if (inLine) { /* * So long as it's not a blank line and we're actually * in a dependency spec, add the command to the list of * commands of all targets in the dependency spec */ Lst_ForEach (targets, ParseAddCmd, (ClientData)cp); continue; } else { Parse_Error (PARSE_FATAL, "Unassociated shell command \"%.20s\"", cp); } } } else if (Parse_IsVar (line)) { ParseFinishLine(); Parse_DoVar (line, VAR_GLOBAL); } else { /* * We now know it's a dependency line so it needs to have all * variables expanded before being parsed. Tell the variable * module to complain if some variable is undefined... * To make life easier on novices, if the line is indented we * first make sure the line has a dependency operator in it. * If it doesn't have an operator and we're in a dependency * line's script, we assume it's actually a shell command * and add it to the current list of targets. * * Note that POSIX declares all lines that start with * whitespace are shell commands, so there's no need to check * here... */ Boolean nonSpace = FALSE; cp = line; #ifndef POSIX if (line[0] == ' ') { while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) { if (!isspace(*cp)) { nonSpace = TRUE; } cp++; } } if (*cp == '\0') { if (inLine) { Parse_Error (PARSE_WARNING, "Shell command needs a leading tab"); goto shellCommand; } else if (nonSpace) { Parse_Error (PARSE_FATAL, "Missing operator"); } } else { #endif ParseFinishLine(); cp = Var_Subst (line, VAR_CMD, TRUE); free (line); line = cp; /* * Need a non-circular list for the target nodes */ targets = Lst_Init (FALSE); inLine = TRUE; ParseDoDependency (line); #ifndef POSIX } #endif } nextLine: free (line); } /* * Reached EOF, but it may be just EOF of an include file... */ } while (ParseEOF(1) == CONTINUE); /* * Make sure conditionals are clean */ Cond_End(); if (fatals) { fprintf (stderr, "Fatal errors encountered -- cannot continue\n"); exit (1); } } /*- *--------------------------------------------------------------------- * Parse_Init -- * initialize the parsing module * * Results: * none * * Side Effects: * the parseIncPath list is initialized... *--------------------------------------------------------------------- */ Parse_Init () { char *cp, *start; /* avoid faults on read-only strings */ static char syspath[] = _PATH_DEFSYSPATH; mainNode = NILGNODE; parseIncPath = Lst_Init (FALSE); sysIncPath = Lst_Init (FALSE); includes = Lst_Init (FALSE); /* * Add the directories from the DEFSYSPATH (more than one may be given * as dir1:...:dirn) to the system include path. */ for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) { ; } if (*cp == '\0') { Dir_AddDir(sysIncPath, start); } else { *cp++ = '\0'; Dir_AddDir(sysIncPath, start); } } } /*- *----------------------------------------------------------------------- * Parse_MainName -- * Return a Lst of the main target to create for main()'s sake. If * no such target exists, we Punt with an obnoxious error message. * * Results: * A Lst of the single node to create. * * Side Effects: * None. * *----------------------------------------------------------------------- */ Lst Parse_MainName() { Lst main; /* result list */ main = Lst_Init (FALSE); if (mainNode == NILGNODE) { Punt ("make: no target to make.\n"); /*NOTREACHED*/ } else if (mainNode->type & OP_DOUBLEDEP) { Lst_Concat(main, mainNode->cohorts, LST_CONCNEW); } (void) Lst_AtEnd (main, (ClientData)mainNode); return (main); } pmake/pathnames.h100600 1750 1750 3742 5431142072 12465 0ustar karlkarl/* * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pathnames.h 5.2 (Berkeley) 6/1/90 */ #define _PATH_OBJDIR "obj" #define _PATH_DEFSHELLDIR "/bin" #define _PATH_DEFSYSMK "/usr/share/mk/sys.mk" #define _PATH_DEFSYSPATH "/usr/share/mk" pmake/sprite.h100600 1750 1750 6713 5431142072 12014 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)sprite.h 5.3 (Berkeley) 6/1/90 */ /* * sprite.h -- * * Common constants and type declarations for Sprite. */ #ifndef _SPRITE #define _SPRITE /* * A boolean type is defined as an integer, not an enum. This allows a * boolean argument to be an expression that isn't strictly 0 or 1 valued. */ typedef int Boolean; #ifndef TRUE #define TRUE 1 #endif TRUE #ifndef FALSE #define FALSE 0 #endif FALSE /* * Functions that must return a status can return a ReturnStatus to * indicate success or type of failure. */ typedef int ReturnStatus; /* * The following statuses overlap with the first 2 generic statuses * defined in status.h: * * SUCCESS There was no error. * FAILURE There was a general error. */ #define SUCCESS 0x00000000 #define FAILURE 0x00000001 /* * A nil pointer must be something that will cause an exception if * referenced. There are two nils: the kernels nil and the nil used * by user processes. */ #define NIL 0xFFFFFFFF #define USER_NIL 0 #ifndef NULL #define NULL 0 #endif NULL /* * An address is just a pointer in C. It is defined as a character pointer * so that address arithmetic will work properly, a byte at a time. */ typedef char *Address; /* * ClientData is an uninterpreted word. It is defined as an int so that * kdbx will not interpret client data as a string. Unlike an "Address", * client data will generally not be used in arithmetic. */ typedef int *ClientData; #ifdef notdef #include "status.h" #endif #endif _SPRITE pmake/str.c100600 1750 1750 20720 5431142072 11323 0ustar karlkarl/*- * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; #endif /* not lint */ #include "make.h" /*- * str_concat -- * concatenate the two strings, inserting a space or slash between them, * freeing them if requested. * * returns -- * the resulting string in allocated space. */ char * str_concat(s1, s2, flags) char *s1, *s2; int flags; { register int len1, len2; register char *result; /* get the length of both strings */ len1 = strlen(s1); len2 = strlen(s2); /* allocate length plus separator plus EOS */ result = emalloc((u_int)(len1 + len2 + 2)); /* copy first string into place */ bcopy(s1, result, len1); /* add separator character */ if (flags & STR_ADDSPACE) { result[len1] = ' '; ++len1; } else if (flags & STR_ADDSLASH) { result[len1] = '/'; ++len1; } /* copy second string plus EOS into place */ bcopy(s2, result + len1, len2 + 1); /* free original strings */ if (flags & STR_DOFREE) { (void)free(s1); (void)free(s2); } return(result); } /*- * brk_string -- * Fracture a string into an array of words (as delineated by tabs or * spaces) taking quotation marks into account. Leading tabs/spaces * are ignored. * * returns -- * Pointer to the array of pointers to the words. To make life easier, * the first word is always the value of the .MAKE variable. */ char ** brk_string(str, store_argc) register char *str; int *store_argc; { static int argmax, curlen; static char **argv, *buf; register int argc, ch; register char inquote, *p, *start, *t; int len; /* save off pmake variable */ if (!argv) { argv = (char **)emalloc((argmax = 50) * sizeof(char *)); argv[0] = Var_Value(".MAKE", VAR_GLOBAL); } /* skip leading space chars. for (; *str == ' ' || *str == '\t'; ++str); /* allocate room for a copy of the string */ if ((len = strlen(str) + 1) > curlen) buf = emalloc(curlen = len); /* * copy the string; at the same time, parse backslashes, * quotes and build the argument list. */ argc = 1; inquote = '\0'; for (p = str, start = t = buf;; ++p) { switch(ch = *p) { case '"': case '\'': if (inquote) if (inquote == ch) inquote = NULL; else break; else inquote = ch; continue; case ' ': case '\t': if (inquote) break; if (!start) continue; /* FALLTHROUGH */ case '\n': case '\0': /* * end of a token -- make sure there's enough argv * space and save off a pointer. */ *t++ = '\0'; if (argc == argmax) { argmax *= 2; /* ramp up fast */ if (!(argv = (char **)realloc(argv, argmax * sizeof(char *)))) enomem(); } argv[argc++] = start; start = (char *)NULL; if (ch == '\n' || ch == '\0') goto done; continue; case '\\': switch (ch = *++p) { case '\0': case '\n': /* hmmm; fix it up as best we can */ ch = '\\'; --p; break; case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; } break; } if (!start) start = t; *t++ = ch; } done: argv[argc] = (char *)NULL; *store_argc = argc; return(argv); } /* * Str_FindSubstring -- See if a string contains a particular substring. * * Results: If string contains substring, the return value is the location of * the first matching instance of substring in string. If string doesn't * contain substring, the return value is NULL. Matching is done on an exact * character-for-character basis with no wildcards or special characters. * * Side effects: None. */ char * Str_FindSubstring(string, substring) register char *string; /* String to search. */ char *substring; /* Substring to find in string */ { register char *a, *b; /* * First scan quickly through the two strings looking for a single- * character match. When it's found, then compare the rest of the * substring. */ for (b = substring; *string != 0; string += 1) { if (*string != *b) continue; a = string; for (;;) { if (*b == 0) return(string); if (*a++ != *b++) break; } b = substring; } return((char *) NULL); } /* * Str_Match -- * * See if a particular string matches a particular pattern. * * Results: Non-zero is returned if string matches pattern, 0 otherwise. The * matching operation permits the following special characters in the * pattern: *?\[] (see the man page for details on what these mean). * * Side effects: None. */ Str_Match(string, pattern) register char *string; /* String */ register char *pattern; /* Pattern */ { char c2; for (;;) { /* * See if we're at the end of both the pattern and the * string. If, we succeeded. If we're at the end of the * pattern but not at the end of the string, we failed. */ if (*pattern == 0) return(!*string); if (*string == 0 && *pattern != '*') return(0); /* * Check for a "*" as the next pattern character. It matches * any substring. We handle this by calling ourselves * recursively for each postfix of string, until either we * match or we reach the end of the string. */ if (*pattern == '*') { pattern += 1; if (*pattern == 0) return(1); while (*string != 0) { if (Str_Match(string, pattern)) return(1); ++string; } return(0); } /* * Check for a "?" as the next pattern character. It matches * any single character. */ if (*pattern == '?') goto thisCharOK; /* * Check for a "[" as the next pattern character. It is * followed by a list of characters that are acceptable, or * by a range (two characters separated by "-"). */ if (*pattern == '[') { ++pattern; for (;;) { if ((*pattern == ']') || (*pattern == 0)) return(0); if (*pattern == *string) break; if (pattern[1] == '-') { c2 = pattern[2]; if (c2 == 0) return(0); if ((*pattern <= *string) && (c2 >= *string)) break; if ((*pattern >= *string) && (c2 <= *string)) break; pattern += 2; } ++pattern; } while ((*pattern != ']') && (*pattern != 0)) ++pattern; goto thisCharOK; } /* * If the next pattern character is '/', just strip off the * '/' so we do exact matching on the character that follows. */ if (*pattern == '\\') { ++pattern; if (*pattern == 0) return(0); } /* * There's no special character. Just make sure that the * next characters of each string match. */ if (*pattern != *string) return(0); thisCharOK: ++pattern; ++string; } } pmake/suff.c100600 1750 1750 166141 5431142074 11510 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)suff.c 5.6 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * suff.c -- * Functions to maintain suffix lists and find implicit dependents * using suffix transformation rules * * Interface: * Suff_Init Initialize all things to do with suffixes. * * Suff_DoPaths This function is used to make life easier * when searching for a file according to its * suffix. It takes the global search path, * as defined using the .PATH: target, and appends * its directories to the path of each of the * defined suffixes, as specified using * .PATH: targets. In addition, all * directories given for suffixes labeled as * include files or libraries, using the .INCLUDES * or .LIBS targets, are played with using * Dir_MakeFlags to create the .INCLUDES and * .LIBS global variables. * * Suff_ClearSuffixes Clear out all the suffixes and defined * transformations. * * Suff_IsTransform Return TRUE if the passed string is the lhs * of a transformation rule. * * Suff_AddSuffix Add the passed string as another known suffix. * * Suff_GetPath Return the search path for the given suffix. * * Suff_AddInclude Mark the given suffix as denoting an include * file. * * Suff_AddLib Mark the given suffix as denoting a library. * * Suff_AddTransform Add another transformation to the suffix * graph. Returns GNode suitable for framing, I * mean, tacking commands, attributes, etc. on. * * Suff_SetNull Define the suffix to consider the suffix of * any file that doesn't have a known one. * * Suff_FindDeps Find implicit sources for and the location of * a target based on its suffix. Returns the * bottom-most node added to the graph or NILGNODE * if the target had no implicit sources. */ #include #include "make.h" #include "bit.h" static Lst sufflist; /* Lst of suffixes */ static Lst transforms; /* Lst of transformation rules */ static int sNum = 0; /* Counter for assigning suffix numbers */ /* * Structure describing an individual suffix. */ typedef struct _Suff { char *name; /* The suffix itself */ int nameLen; /* Length of the suffix */ short flags; /* Type of suffix */ #define SUFF_INCLUDE 0x01 /* One which is #include'd */ #define SUFF_LIBRARY 0x02 /* One which contains a library */ #define SUFF_NULL 0x04 /* The empty suffix */ Lst searchPath; /* The path along which files of this suffix * may be found */ int sNum; /* The suffix number */ Lst parents; /* Suffixes we have a transformation to */ Lst children; /* Suffixes we have a transformation from */ } Suff; /* * Structure used in the search for implied sources. */ typedef struct _Src { char *file; /* The file to look for */ char *pref; /* Prefix from which file was formed */ Suff *suff; /* The suffix on the file */ struct _Src *parent; /* The Src for which this is a source */ GNode *node; /* The node describing the file */ int children; /* Count of existing children (so we don't free * this thing too early or never nuke it) */ } Src; static Suff *suffNull; /* The NULL suffix for this run */ static Suff *emptySuff; /* The empty suffix required for POSIX * single-suffix transformation rules */ /*************** Lst Predicates ****************/ /*- *----------------------------------------------------------------------- * SuffStrIsPrefix -- * See if pref is a prefix of str. * * Results: * NULL if it ain't, pointer to character in str after prefix if so * * Side Effects: * None *----------------------------------------------------------------------- */ static char * SuffStrIsPrefix (pref, str) register char *pref; /* possible prefix */ register char *str; /* string to check */ { while (*str && *pref == *str) { pref++; str++; } return (*pref ? NULL : str); } /*- *----------------------------------------------------------------------- * SuffSuffIsSuffix -- * See if suff is a suffix of str. Str should point to THE END of the * string to check. (THE END == the null byte) * * Results: * NULL if it ain't, pointer to character in str before suffix if * it is. * * Side Effects: * None *----------------------------------------------------------------------- */ static char * SuffSuffIsSuffix (s, str) register Suff *s; /* possible suffix */ char *str; /* string to examine */ { register char *p1; /* Pointer into suffix name */ register char *p2; /* Pointer into string being examined */ p1 = s->name + s->nameLen; p2 = str; while (p1 >= s->name && *p1 == *p2) { p1--; p2--; } return (p1 == s->name - 1 ? p2 : NULL); } /*- *----------------------------------------------------------------------- * SuffSuffIsSuffixP -- * Predicate form of SuffSuffIsSuffix. Passed as the callback function * to Lst_Find. * * Results: * 0 if the suffix is the one desired, non-zero if not. * * Side Effects: * None. * *----------------------------------------------------------------------- */ SuffSuffIsSuffixP(s, str) Suff *s; char *str; { return(!SuffSuffIsSuffix(s, str)); } /*- *----------------------------------------------------------------------- * SuffSuffHasNameP -- * Callback procedure for finding a suffix based on its name. Used by * Suff_GetPath. * * Results: * 0 if the suffix is of the given name. non-zero otherwise. * * Side Effects: * None *----------------------------------------------------------------------- */ static int SuffSuffHasNameP (s, sname) Suff *s; /* Suffix to check */ char *sname; /* Desired name */ { return (strcmp (sname, s->name)); } /*- *----------------------------------------------------------------------- * SuffSuffIsPrefix -- * See if the suffix described by s is a prefix of the string. Care * must be taken when using this to search for transformations and * what-not, since there could well be two suffixes, one of which * is a prefix of the other... * * Results: * 0 if s is a prefix of str. non-zero otherwise * * Side Effects: * None *----------------------------------------------------------------------- */ static int SuffSuffIsPrefix (s, str) Suff *s; /* suffix to compare */ char *str; /* string to examine */ { return (SuffStrIsPrefix (s->name, str) == NULL ? 1 : 0); } /*- *----------------------------------------------------------------------- * SuffGNHasNameP -- * See if the graph node has the desired name * * Results: * 0 if it does. non-zero if it doesn't * * Side Effects: * None *----------------------------------------------------------------------- */ static int SuffGNHasNameP (gn, name) GNode *gn; /* current node we're looking at */ char *name; /* name we're looking for */ { return (strcmp (name, gn->name)); } /*********** Maintenance Functions ************/ /*- *----------------------------------------------------------------------- * SuffFree -- * Free up all memory associated with the given suffix structure. * * Results: * none * * Side Effects: * the suffix entry is detroyed *----------------------------------------------------------------------- */ static void SuffFree (s) Suff *s; { Lst_Destroy (s->children, NOFREE); Lst_Destroy (s->parents, NOFREE); Lst_Destroy (s->searchPath, Dir_Destroy); free ((Address)s->name); free ((Address)s); } /*- *----------------------------------------------------------------------- * SuffInsert -- * Insert the suffix into the list keeping the list ordered by suffix * numbers. * * Results: * None * * Side Effects: * Not really *----------------------------------------------------------------------- */ static void SuffInsert (l, s) Lst l; /* the list where in s should be inserted */ Suff *s; /* the suffix to insert */ { LstNode ln; /* current element in l we're examining */ Suff *s2; /* the suffix descriptor in this element */ if (Lst_Open (l) == FAILURE) { return; } while ((ln = Lst_Next (l)) != NILLNODE) { s2 = (Suff *) Lst_Datum (ln); if (s2->sNum >= s->sNum) { break; } } Lst_Close (l); if (DEBUG(SUFF)) { printf("inserting %s(%d)...", s->name, s->sNum); } if (ln == NILLNODE) { if (DEBUG(SUFF)) { printf("at end of list\n"); } (void)Lst_AtEnd (l, (ClientData)s); } else if (s2->sNum != s->sNum) { if (DEBUG(SUFF)) { printf("before %s(%d)\n", s2->name, s2->sNum); } (void)Lst_Insert (l, ln, (ClientData)s); } else if (DEBUG(SUFF)) { printf("already there\n"); } } /*- *----------------------------------------------------------------------- * Suff_ClearSuffixes -- * This is gross. Nuke the list of suffixes but keep all transformation * rules around. The transformation graph is destroyed in this process, * but we leave the list of rules so when a new graph is formed the rules * will remain. * This function is called from the parse module when a * .SUFFIXES:\n line is encountered. * * Results: * none * * Side Effects: * the sufflist and its graph nodes are destroyed *----------------------------------------------------------------------- */ void Suff_ClearSuffixes () { Lst_Destroy (sufflist, SuffFree); sufflist = Lst_Init(FALSE); sNum = 0; suffNull = emptySuff; } /*- *----------------------------------------------------------------------- * SuffParseTransform -- * Parse a transformation string to find its two component suffixes. * * Results: * TRUE if the string is a valid transformation and FALSE otherwise. * * Side Effects: * The passed pointers are overwritten. * *----------------------------------------------------------------------- */ static Boolean SuffParseTransform(str, srcPtr, targPtr) char *str; /* String being parsed */ Suff **srcPtr; /* Place to store source of trans. */ Suff **targPtr; /* Place to store target of trans. */ { register LstNode srcLn; /* element in suffix list of trans source*/ register Suff *src; /* Source of transformation */ register LstNode targLn; /* element in suffix list of trans target*/ register char *str2; /* Extra pointer (maybe target suffix) */ LstNode singleLn; /* element in suffix list of any suffix * that exactly matches str */ Suff *single; /* Source of possible transformation to * null suffix */ srcLn = NILLNODE; singleLn = NILLNODE; /* * Loop looking first for a suffix that matches the start of the * string and then for one that exactly matches the rest of it. If * we can find two that meet these criteria, we've successfully * parsed the string. */ while (1) { if (srcLn == NILLNODE) { srcLn = Lst_Find(sufflist, (ClientData)str, SuffSuffIsPrefix); } else { srcLn = Lst_FindFrom (sufflist, Lst_Succ(srcLn), (ClientData)str, SuffSuffIsPrefix); } if (srcLn == NILLNODE) { /* * Ran out of source suffixes -- no such rule */ if (singleLn != NILLNODE) { /* * Not so fast Mr. Smith! There was a suffix that encompassed * the entire string, so we assume it was a transformation * to the null suffix (thank you POSIX). We still prefer to * find a double rule over a singleton, hence we leave this * check until the end. * * XXX: Use emptySuff over suffNull? */ *srcPtr = single; *targPtr = suffNull; return(TRUE); } return (FALSE); } src = (Suff *) Lst_Datum (srcLn); str2 = str + src->nameLen; if (*str2 == '\0') { single = src; singleLn = srcLn; } else { targLn = Lst_Find(sufflist, (ClientData)str2, SuffSuffHasNameP); if (targLn != NILLNODE) { *srcPtr = src; *targPtr = (Suff *)Lst_Datum(targLn); return (TRUE); } } } } /*- *----------------------------------------------------------------------- * Suff_IsTransform -- * Return TRUE if the given string is a transformation rule * * * Results: * TRUE if the string is a concatenation of two known suffixes. * FALSE otherwise * * Side Effects: * None *----------------------------------------------------------------------- */ Boolean Suff_IsTransform (str) char *str; /* string to check */ { Suff *src, *targ; return (SuffParseTransform(str, &src, &targ)); } /*- *----------------------------------------------------------------------- * Suff_AddTransform -- * Add the transformation rule described by the line to the * list of rules and place the transformation itself in the graph * * Results: * The node created for the transformation in the transforms list * * Side Effects: * The node is placed on the end of the transforms Lst and links are * made between the two suffixes mentioned in the target name *----------------------------------------------------------------------- */ GNode * Suff_AddTransform (line) char *line; /* name of transformation to add */ { GNode *gn; /* GNode of transformation rule */ Suff *s, /* source suffix */ *t; /* target suffix */ LstNode ln; /* Node for existing transformation */ ln = Lst_Find (transforms, (ClientData)line, SuffGNHasNameP); if (ln == NILLNODE) { /* * Make a new graph node for the transformation. It will be filled in * by the Parse module. */ gn = Targ_NewGN (line); (void)Lst_AtEnd (transforms, (ClientData)gn); } else { /* * New specification for transformation rule. Just nuke the old list * of commands so they can be filled in again... We don't actually * free the commands themselves, because a given command can be * attached to several different transformations. */ gn = (GNode *) Lst_Datum (ln); Lst_Destroy (gn->commands, NOFREE); Lst_Destroy (gn->children, NOFREE); gn->commands = Lst_Init (FALSE); gn->children = Lst_Init (FALSE); } gn->type = OP_TRANSFORM; (void)SuffParseTransform(line, &s, &t); /* * link the two together in the proper relationship and order */ if (DEBUG(SUFF)) { printf("defining transformation from `%s' to `%s'\n", s->name, t->name); } SuffInsert (t->children, s); SuffInsert (s->parents, t); return (gn); } /*- *----------------------------------------------------------------------- * Suff_EndTransform -- * Handle the finish of a transformation definition, removing the * transformation from the graph if it has neither commands nor * sources. This is a callback procedure for the Parse module via * Lst_ForEach * * Results: * === 0 * * Side Effects: * If the node has no commands or children, the children and parents * lists of the affected suffices are altered. * *----------------------------------------------------------------------- */ int Suff_EndTransform(gn) GNode *gn; /* Node for transformation */ { if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) && Lst_IsEmpty(gn->children)) { Suff *s, *t; LstNode ln; (void)SuffParseTransform(gn->name, &s, &t); if (DEBUG(SUFF)) { printf("deleting transformation from %s to %s\n", s->name, t->name); } /* * Remove the source from the target's children list. We check for a * nil return to handle a beanhead saying something like * .c.o .c.o: * * We'll be called twice when the next target is seen, but .c and .o * are only linked once... */ ln = Lst_Member(t->children, (ClientData)s); if (ln != NILLNODE) { (void)Lst_Remove(t->children, ln); } /* * Remove the target from the source's parents list */ ln = Lst_Member(s->parents, (ClientData)t); if (ln != NILLNODE) { (void)Lst_Remove(s->parents, ln); } } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { printf("transformation %s complete\n", gn->name); } return(0); } /*- *----------------------------------------------------------------------- * SuffRebuildGraph -- * Called from Suff_AddSuffix via Lst_ForEach to search through the * list of existing transformation rules and rebuild the transformation * graph when it has been destroyed by Suff_ClearSuffixes. If the * given rule is a transformation involving this suffix and another, * existing suffix, the proper relationship is established between * the two. * * Results: * Always 0. * * Side Effects: * The appropriate links will be made between this suffix and * others if transformation rules exist for it. * *----------------------------------------------------------------------- */ static int SuffRebuildGraph(transform, s) GNode *transform; /* Transformation to test */ Suff *s; /* Suffix to rebuild */ { register char *cp; register LstNode ln; register Suff *s2; /* * First see if it is a transformation from this suffix. */ cp = SuffStrIsPrefix(s->name, transform->name); if (cp != (char *)NULL) { ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffHasNameP); if (ln != NILLNODE) { /* * Found target. Link in and return, since it can't be anything * else. */ s2 = (Suff *)Lst_Datum(ln); SuffInsert(s2->children, s); SuffInsert(s->parents, s2); return(0); } } /* * Not from, maybe to? */ cp = SuffSuffIsSuffix(s, transform->name + strlen(transform->name)); if (cp != (char *)NULL) { /* * Null-terminate the source suffix in order to find it. */ cp[1] = '\0'; ln = Lst_Find(sufflist, (ClientData)transform->name, SuffSuffHasNameP); /* * Replace the start of the target suffix */ cp[1] = s->name[0]; if (ln != NILLNODE) { /* * Found it -- establish the proper relationship */ s2 = (Suff *)Lst_Datum(ln); SuffInsert(s->children, s2); SuffInsert(s2->parents, s); } } return(0); } /*- *----------------------------------------------------------------------- * Suff_AddSuffix -- * Add the suffix in string to the end of the list of known suffixes. * Should we restructure the suffix graph? Make doesn't... * * Results: * None * * Side Effects: * A GNode is created for the suffix and a Suff structure is created and * added to the suffixes list unless the suffix was already known. *----------------------------------------------------------------------- */ void Suff_AddSuffix (str) char *str; /* the name of the suffix to add */ { Suff *s; /* new suffix descriptor */ LstNode ln; ln = Lst_Find (sufflist, (ClientData)str, SuffSuffHasNameP); if (ln == NILLNODE) { s = (Suff *) emalloc (sizeof (Suff)); s->name = strdup (str); s->nameLen = strlen (s->name); s->searchPath = Lst_Init (FALSE); s->children = Lst_Init (FALSE); s->parents = Lst_Init (FALSE); s->sNum = sNum++; s->flags = 0; (void)Lst_AtEnd (sufflist, (ClientData)s); /* * Look for any existing transformations from or to this suffix. * XXX: Only do this after a Suff_ClearSuffixes? */ Lst_ForEach (transforms, SuffRebuildGraph, (ClientData)s); } } /*- *----------------------------------------------------------------------- * Suff_GetPath -- * Return the search path for the given suffix, if it's defined. * * Results: * The searchPath for the desired suffix or NILLST if the suffix isn't * defined. * * Side Effects: * None *----------------------------------------------------------------------- */ Lst Suff_GetPath (sname) char *sname; { LstNode ln; Suff *s; ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); if (ln == NILLNODE) { return (NILLST); } else { s = (Suff *) Lst_Datum (ln); return (s->searchPath); } } /*- *----------------------------------------------------------------------- * Suff_DoPaths -- * Extend the search paths for all suffixes to include the default * search path. * * Results: * None. * * Side Effects: * The searchPath field of all the suffixes is extended by the * directories in dirSearchPath. If paths were specified for the * ".h" suffix, the directories are stuffed into a global variable * called ".INCLUDES" with each directory preceeded by a -I. The same * is done for the ".a" suffix, except the variable is called * ".LIBS" and the flag is -L. *----------------------------------------------------------------------- */ void Suff_DoPaths() { register Suff *s; register LstNode ln; Lst inIncludes; /* Cumulative .INCLUDES path */ Lst inLibs; /* Cumulative .LIBS path */ if (Lst_Open (sufflist) == FAILURE) { return; } inIncludes = Lst_Init(FALSE); inLibs = Lst_Init(FALSE); while ((ln = Lst_Next (sufflist)) != NILLNODE) { s = (Suff *) Lst_Datum (ln); if (!Lst_IsEmpty (s->searchPath)) { #ifdef INCLUDES if (s->flags & SUFF_INCLUDE) { Dir_Concat(inIncludes, s->searchPath); } #endif /* INCLUDES */ #ifdef LIBRARIES if (s->flags & SUFF_LIBRARY) { Dir_Concat(inLibs, s->searchPath); } #endif /* LIBRARIES */ Dir_Concat(s->searchPath, dirSearchPath); } else { Lst_Destroy (s->searchPath, Dir_Destroy); s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); } } Var_Set(".INCLUDES", Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL); Var_Set(".LIBS", Dir_MakeFlags("-L", inLibs), VAR_GLOBAL); Lst_Destroy(inIncludes, Dir_Destroy); Lst_Destroy(inLibs, Dir_Destroy); Lst_Close (sufflist); } /*- *----------------------------------------------------------------------- * Suff_AddInclude -- * Add the given suffix as a type of file which gets included. * Called from the parse module when a .INCLUDES line is parsed. * The suffix must have already been defined. * * Results: * None. * * Side Effects: * The SUFF_INCLUDE bit is set in the suffix's flags field * *----------------------------------------------------------------------- */ void Suff_AddInclude (sname) char *sname; /* Name of suffix to mark */ { LstNode ln; Suff *s; ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); if (ln != NILLNODE) { s = (Suff *) Lst_Datum (ln); s->flags |= SUFF_INCLUDE; } } /*- *----------------------------------------------------------------------- * Suff_AddLib -- * Add the given suffix as a type of file which is a library. * Called from the parse module when parsing a .LIBS line. The * suffix must have been defined via .SUFFIXES before this is * called. * * Results: * None. * * Side Effects: * The SUFF_LIBRARY bit is set in the suffix's flags field * *----------------------------------------------------------------------- */ void Suff_AddLib (sname) char *sname; /* Name of suffix to mark */ { LstNode ln; Suff *s; ln = Lst_Find (sufflist, (ClientData)sname, SuffSuffHasNameP); if (ln != NILLNODE) { s = (Suff *) Lst_Datum (ln); s->flags |= SUFF_LIBRARY; } } /********** Implicit Source Search Functions *********/ /* * A structure for passing more than one argument to the Lst-library-invoked * function... */ typedef struct { Lst l; Src *s; } LstSrc; /*- *----------------------------------------------------------------------- * SuffAddSrc -- * Add a suffix as a Src structure to the given list with its parent * being the given Src structure. If the suffix is the null suffix, * the prefix is used unaltered as the file name in the Src structure. * * Results: * always returns 0 * * Side Effects: * A Src structure is created and tacked onto the end of the list *----------------------------------------------------------------------- */ static int SuffAddSrc (s, ls) Suff *s; /* suffix for which to create a Src structure */ LstSrc *ls; /* list and parent for the new Src */ { Src *s2; /* new Src structure */ Src *targ; /* Target structure */ targ = ls->s; if ((s->flags & SUFF_NULL) && (*s->name != '\0')) { /* * If the suffix has been marked as the NULL suffix, also create a Src * structure for a file with no suffix attached. Two birds, and all * that... */ s2 = (Src *) emalloc (sizeof (Src)); s2->file = strdup(targ->pref); s2->pref = targ->pref; s2->parent = targ; s2->node = NILGNODE; s2->suff = s; s2->children = 0; targ->children += 1; (void)Lst_AtEnd (ls->l, (ClientData)s2); } s2 = (Src *) emalloc (sizeof (Src)); s2->file = str_concat (targ->pref, s->name, 0); s2->pref = targ->pref; s2->parent = targ; s2->node = NILGNODE; s2->suff = s; s2->children = 0; targ->children += 1; (void)Lst_AtEnd (ls->l, (ClientData)s2); return(0); } /*- *----------------------------------------------------------------------- * SuffAddLevel -- * Add all the children of targ as Src structures to the given list * * Results: * None * * Side Effects: * Lots of structures are created and added to the list *----------------------------------------------------------------------- */ static void SuffAddLevel (l, targ) Lst l; /* list to which to add the new level */ Src *targ; /* Src structure to use as the parent */ { LstSrc ls; ls.s = targ; ls.l = l; Lst_ForEach (targ->suff->children, SuffAddSrc, (ClientData)&ls); } /*- *---------------------------------------------------------------------- * SuffFreeSrc -- * Free all memory associated with a Src structure * * Results: * None * * Side Effects: * The memory is free'd. *---------------------------------------------------------------------- */ static void SuffFreeSrc (s) Src *s; { free ((Address)s->file); if (!s->parent) { free((Address)s->pref); } else if (--s->parent->children == 0 && s->parent->parent) { /* * Parent has no more children, now we're gone, and it's not * at the top of the tree, so blow it away too. */ SuffFreeSrc(s->parent); } free ((Address)s); } /*- *----------------------------------------------------------------------- * SuffFindThem -- * Find the first existing file/target in the list srcs * * Results: * The lowest structure in the chain of transformations * * Side Effects: * None *----------------------------------------------------------------------- */ static Src * SuffFindThem (srcs) Lst srcs; /* list of Src structures to search through */ { Src *s; /* current Src */ Src *rs; /* returned Src */ rs = (Src *) NULL; while (!Lst_IsEmpty (srcs)) { s = (Src *) Lst_DeQueue (srcs); if (DEBUG(SUFF)) { printf ("\ttrying %s...", s->file); } /* * A file is considered to exist if either a node exists in the * graph for it or the file actually exists. */ if ((Targ_FindNode(s->file, TARG_NOCREATE) != NILGNODE) || (Dir_FindFile (s->file, s->suff->searchPath) != (char *) NULL)) { if (DEBUG(SUFF)) { printf ("got it\n"); } rs = s; break; } else { if (DEBUG(SUFF)) { printf ("not there\n"); } SuffAddLevel (srcs, s); } } return (rs); } /*- *----------------------------------------------------------------------- * SuffFindCmds -- * See if any of the children of the target in the Src structure is * one from which the target can be transformed. If there is one, * a Src structure is put together for it and returned. * * Results: * The Src structure of the "winning" child, or NIL if no such beast. * * Side Effects: * A Src structure may be allocated. * *----------------------------------------------------------------------- */ static Src * SuffFindCmds (targ) Src *targ; /* Src structure to play with */ { LstNode ln; /* General-purpose list node */ register GNode *t, /* Target GNode */ *s; /* Source GNode */ int prefLen;/* The length of the defined prefix */ Suff *suff; /* Suffix on matching beastie */ Src *ret; /* Return value */ char *cp; t = targ->node; (void) Lst_Open (t->children); prefLen = strlen (targ->pref); while ((ln = Lst_Next (t->children)) != NILLNODE) { s = (GNode *)Lst_Datum (ln); cp = rindex (s->name, '/'); if (cp == (char *)NULL) { cp = s->name; } else { cp++; } if (strncmp (cp, targ->pref, prefLen) == 0) { /* * The node matches the prefix ok, see if it has a known * suffix. */ ln = Lst_Find (sufflist, (ClientData)&cp[prefLen], SuffSuffHasNameP); if (ln != NILLNODE) { /* * It even has a known suffix, see if there's a transformation * defined between the node's suffix and the target's suffix. * * XXX: Handle multi-stage transformations here, too. */ suff = (Suff *)Lst_Datum (ln); if (Lst_Member (suff->parents, (ClientData)targ->suff) != NILLNODE) { /* * Hot Damn! Create a new Src structure to describe * this transformation (making sure to duplicate the * source node's name so Suff_FindDeps can free it * again (ick)), and return the new structure. */ ret = (Src *)emalloc (sizeof(Src)); ret->file = strdup(s->name); ret->pref = targ->pref; ret->suff = suff; ret->parent = targ; ret->node = s; ret->children = 0; targ->children += 1; if (DEBUG(SUFF)) { printf ("\tusing existing source %s\n", s->name); } return (ret); } } } } Lst_Close (t->children); return ((Src *)NULL); } /*- *----------------------------------------------------------------------- * SuffExpandChildren -- * Expand the names of any children of a given node that contain * variable invocations or file wildcards into actual targets. * * Results: * === 0 (continue) * * Side Effects: * The expanded node is removed from the parent's list of children, * and the parent's unmade counter is decremented, but other nodes * may be added. * *----------------------------------------------------------------------- */ static int SuffExpandChildren(cgn, pgn) GNode *cgn; /* Child to examine */ GNode *pgn; /* Parent node being processed */ { GNode *gn; /* New source 8) */ LstNode prevLN; /* Node after which new source should be put */ LstNode ln; /* List element for old source */ char *cp; /* Expanded value */ /* * New nodes effectively take the place of the child, so place them * after the child */ prevLN = Lst_Member(pgn->children, (ClientData)cgn); /* * First do variable expansion -- this takes precedence over * wildcard expansion. If the result contains wildcards, they'll be gotten * to later since the resulting words are tacked on to the end of * the children list. */ if (index(cgn->name, '$') != (char *)NULL) { if (DEBUG(SUFF)) { printf("Expanding \"%s\"...", cgn->name); } cp = Var_Subst(cgn->name, pgn, TRUE); if (cp != (char *)NULL) { Lst members = Lst_Init(FALSE); if (cgn->type & OP_ARCHV) { /* * Node was an archive(member) target, so we want to call * on the Arch module to find the nodes for us, expanding * variables in the parent's context. */ char *sacrifice = cp; (void)Arch_ParseArchive(&sacrifice, members, pgn); } else { /* * Break the result into a vector of strings whose nodes * we can find, then add those nodes to the members list. * Unfortunately, we can't use brk_string b/c it * doesn't understand about variable specifications with * spaces in them... */ char *start; char *initcp = cp; /* For freeing... */ for (start = cp; *start == ' ' || *start == '\t'; start++) { ; } for (cp = start; *cp != '\0'; cp++) { if (*cp == ' ' || *cp == '\t') { /* * White-space -- terminate element, find the node, * add it, skip any further spaces. */ *cp++ = '\0'; gn = Targ_FindNode(start, TARG_CREATE); (void)Lst_AtEnd(members, (ClientData)gn); while (*cp == ' ' || *cp == '\t') { cp++; } /* * Adjust cp for increment at start of loop, but * set start to first non-space. */ start = cp--; } else if (*cp == '$') { /* * Start of a variable spec -- contact variable module * to find the end so we can skip over it. */ char *junk; int len; Boolean doFree; junk = Var_Parse(cp, pgn, TRUE, &len, &doFree); if (junk != var_Error) { cp += len - 1; } if (doFree) { free(junk); } } else if (*cp == '\\' && *cp != '\0') { /* * Escaped something -- skip over it */ cp++; } } if (cp != start) { /* * Stuff left over -- add it to the list too */ gn = Targ_FindNode(start, TARG_CREATE); (void)Lst_AtEnd(members, (ClientData)gn); } /* * Point cp back at the beginning again so the variable value * can be freed. */ cp = initcp; } /* * Add all elements of the members list to the parent node. */ while(!Lst_IsEmpty(members)) { gn = (GNode *)Lst_DeQueue(members); if (DEBUG(SUFF)) { printf("%s...", gn->name); } if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); prevLN = Lst_Succ(prevLN); (void)Lst_AtEnd(gn->parents, (ClientData)pgn); pgn->unmade++; } } Lst_Destroy(members, NOFREE); /* * Free the result */ free((char *)cp); } /* * Now the source is expanded, remove it from the list of children to * keep it from being processed. */ ln = Lst_Member(pgn->children, (ClientData)cgn); pgn->unmade--; Lst_Remove(pgn->children, ln); if (DEBUG(SUFF)) { printf("\n"); } } else if (Dir_HasWildcards(cgn->name)) { Lst exp; /* List of expansions */ Lst path; /* Search path along which to expand */ /* * Find a path along which to expand the word. * * If the word has a known suffix, use that path. * If it has no known suffix and we're allowed to use the null * suffix, use its path. * Else use the default system search path. */ cp = cgn->name + strlen(cgn->name); ln = Lst_Find(sufflist, (ClientData)cp, SuffSuffIsSuffixP); if (DEBUG(SUFF)) { printf("Wildcard expanding \"%s\"...", cgn->name); } if (ln != NILLNODE) { Suff *s = (Suff *)Lst_Datum(ln); if (DEBUG(SUFF)) { printf("suffix is \"%s\"...", s->name); } path = s->searchPath; } else { /* * Use default search path */ path = dirSearchPath; } /* * Expand the word along the chosen path */ exp = Lst_Init(FALSE); Dir_Expand(cgn->name, path, exp); while (!Lst_IsEmpty(exp)) { /* * Fetch next expansion off the list and find its GNode */ cp = (char *)Lst_DeQueue(exp); if (DEBUG(SUFF)) { printf("%s...", cp); } gn = Targ_FindNode(cp, TARG_CREATE); /* * If gn isn't already a child of the parent, make it so and * up the parent's count of unmade children. */ if (Lst_Member(pgn->children, (ClientData)gn) == NILLNODE) { (void)Lst_Append(pgn->children, prevLN, (ClientData)gn); prevLN = Lst_Succ(prevLN); (void)Lst_AtEnd(gn->parents, (ClientData)pgn); pgn->unmade++; } } /* * Nuke what's left of the list */ Lst_Destroy(exp, NOFREE); /* * Now the source is expanded, remove it from the list of children to * keep it from being processed. */ ln = Lst_Member(pgn->children, (ClientData)cgn); pgn->unmade--; Lst_Remove(pgn->children, ln); if (DEBUG(SUFF)) { printf("\n"); } } return(0); } /*- *----------------------------------------------------------------------- * SuffApplyTransform -- * Apply a transformation rule, given the source and target nodes * and suffixes. * * Results: * TRUE if successful, FALSE if not. * * Side Effects: * The source and target are linked and the commands from the * transformation are added to the target node's commands list. * All attributes but OP_DEPMASK and OP_TRANSFORM are applied * to the target. The target also inherits all the sources for * the transformation rule. * *----------------------------------------------------------------------- */ static Boolean SuffApplyTransform(tGn, sGn, t, s) GNode *tGn; /* Target node */ GNode *sGn; /* Source node */ Suff *t; /* Target suffix */ Suff *s; /* Source suffix */ { LstNode ln; /* General node */ char *tname; /* Name of transformation rule */ GNode *gn; /* Node for same */ if (Lst_Member(tGn->children, (ClientData)sGn) == NILLNODE) { /* * Not already linked, so form the proper links between the * target and source. */ (void)Lst_AtEnd(tGn->children, (ClientData)sGn); (void)Lst_AtEnd(sGn->parents, (ClientData)tGn); tGn->unmade += 1; } if ((sGn->type & OP_OPMASK) == OP_DOUBLEDEP) { /* * When a :: node is used as the implied source of a node, we have * to link all its cohorts in as sources as well. Only the initial * sGn gets the target in its iParents list, however, as that * will be sufficient to get the .IMPSRC variable set for tGn */ for (ln=Lst_First(sGn->cohorts); ln != NILLNODE; ln=Lst_Succ(ln)) { gn = (GNode *)Lst_Datum(ln); if (Lst_Member(tGn->children, (ClientData)gn) == NILLNODE) { /* * Not already linked, so form the proper links between the * target and source. */ (void)Lst_AtEnd(tGn->children, (ClientData)gn); (void)Lst_AtEnd(gn->parents, (ClientData)tGn); tGn->unmade += 1; } } } /* * Locate the transformation rule itself */ tname = str_concat(s->name, t->name, 0); ln = Lst_Find(transforms, (ClientData)tname, SuffGNHasNameP); free(tname); if (ln == NILLNODE) { /* * Not really such a transformation rule (can happen when we're * called to link an OP_MEMBER and OP_ARCHV node), so return * FALSE. */ return(FALSE); } gn = (GNode *)Lst_Datum(ln); if (DEBUG(SUFF)) { printf("\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); } /* * Record last child for expansion purposes */ ln = Lst_Last(tGn->children); /* * Pass the buck to Make_HandleUse to apply the rule */ (void)Make_HandleUse(gn, tGn); /* * Deal with wildcards and variables in any acquired sources */ ln = Lst_Succ(ln); if (ln != NILLNODE) { Lst_ForEachFrom(tGn->children, ln, SuffExpandChildren, (ClientData)tGn); } /* * Keep track of another parent to which this beast is transformed so * the .IMPSRC variable can be set correctly for the parent. */ (void)Lst_AtEnd(sGn->iParents, (ClientData)tGn); return(TRUE); } /*- *----------------------------------------------------------------------- * SuffFindArchiveDeps -- * Locate dependencies for an OP_ARCHV node. * * Results: * None * * Side Effects: * Same as Suff_FindDeps * *----------------------------------------------------------------------- */ static void SuffFindArchiveDeps(gn) GNode *gn; /* Node for which to locate dependencies */ { char *eoarch; /* End of archive portion */ char *eoname; /* End of member portion */ GNode *mem; /* Node for member */ static char *copy[] = { /* Variables to be copied from the member node */ TARGET, /* Must be first */ PREFIX, /* Must be second */ }; char *vals[sizeof(copy)/sizeof(copy[0])]; int i; /* Index into copy and vals */ char *cp; /* Suffix for member */ Suff *ms; /* Suffix descriptor for member */ char *name; /* Start of member's name */ /* * The node is an archive(member) pair. so we must find a * suffix for both of them. */ eoarch = index (gn->name, '('); eoname = index (eoarch, ')'); *eoname = '\0'; /* Nuke parentheses during suffix search */ *eoarch = '\0'; /* So a suffix can be found */ name = eoarch + 1; /* * To simplify things, call Suff_FindDeps recursively on the member now, * so we can simply compare the member's .PREFIX and .TARGET variables * to locate its suffix. This allows us to figure out the suffix to * use for the archive without having to do a quadratic search over the * suffix list, backtracking for each one... */ mem = Targ_FindNode(name, TARG_CREATE); Suff_FindDeps(mem); /* * Create the link between the two nodes right off */ if (Lst_Member(gn->children, (ClientData)mem) == NILLNODE) { (void)Lst_AtEnd(gn->children, (ClientData)mem); (void)Lst_AtEnd(mem->parents, (ClientData)gn); gn->unmade += 1; } /* * Copy in the variables from the member node to this one. */ for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) { vals[i] = Var_Value(copy[i], mem); Var_Set(copy[i], vals[i], gn); } ms = mem->suffix; if (ms == NULL) { /* * Didn't know what it was -- use .NULL suffix if not in make mode */ if (DEBUG(SUFF)) { printf("using null suffix\n"); } ms = suffNull; } /* * Set the other two local variables required for this target. */ Var_Set (MEMBER, name, gn); Var_Set (ARCHIVE, gn->name, gn); if (ms != NULL) { /* * Member has a known suffix, so look for a transformation rule from * it to a possible suffix of the archive. Rather than searching * through the entire list, we just look at suffixes to which the * member's suffix may be transformed... */ LstNode ln; /* * Use first matching suffix... */ ln = Lst_Find(ms->parents, eoarch, SuffSuffIsSuffixP); if (ln != NILLNODE) { /* * Got one -- apply it */ if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && DEBUG(SUFF)) { printf("\tNo transformation from %s -> %s\n", ms->name, ((Suff *)Lst_Datum(ln))->name); } } } /* * Replace the opening and closing parens now we've no need of the separate * pieces. */ *eoarch = '('; *eoname = ')'; /* * Pretend gn appeared to the left of a dependency operator so * the user needn't provide a transformation from the member to the * archive. */ if (OP_NOP(gn->type)) { gn->type |= OP_DEPENDS; } /* * Flag the member as such so we remember to look in the archive for * its modification time. */ mem->type |= OP_MEMBER; } /*- *----------------------------------------------------------------------- * SuffFindNormalDeps -- * Locate implicit dependencies for regular targets. * * Results: * None. * * Side Effects: * Same as Suff_FindDeps... * *----------------------------------------------------------------------- */ static void SuffFindNormalDeps(gn) GNode *gn; /* Node for which to find sources */ { char *eoname; /* End of name */ char *sopref; /* Start of prefix */ Suff *s; /* Current suffix */ LstNode ln; /* Next suffix node to check */ Lst srcs; /* List of sources at which to look */ Lst targs; /* List of targets to which things can be * transformed. They all have the same file, * but different suff and pref fields */ Src *bottom; /* Start of found transformation path */ Src *src; /* General Src pointer */ char *pref; /* Prefix to use */ Src *targ; /* General Src target pointer */ eoname = gn->name + strlen(gn->name); sopref = gn->name; /* * Begin at the beginning... */ ln = Lst_First(sufflist); srcs = Lst_Init(FALSE); targs = Lst_Init(FALSE); /* * We're caught in a catch-22 here. On the one hand, we want to use any * transformation implied by the target's sources, but we can't examine * the sources until we've expanded any variables/wildcards they may hold, * and we can't do that until we've set up the target's local variables * and we can't do that until we know what the proper suffix for the * target is (in case there are two suffixes one of which is a suffix of * the other) and we can't know that until we've found its implied * source, which we may not want to use if there's an existing source * that implies a different transformation. * * In an attempt to get around this, which may not work all the time, * but should work most of the time, we look for implied sources first, * checking transformations to all possible suffixes of the target, * use what we find to set the target's local variables, expand the * children, then look for any overriding transformations they imply. * Should we find one, we discard the one we found before. */ while(ln != NILLNODE) { /* * Look for next possible suffix... */ ln = Lst_FindFrom(sufflist, ln, eoname, SuffSuffIsSuffixP); if (ln != NILLNODE) { int prefLen; /* Length of the prefix */ Src *targ; /* * Allocate a Src structure to which things can be transformed */ targ = (Src *)emalloc(sizeof(Src)); targ->file = strdup(gn->name); targ->suff = (Suff *)Lst_Datum(ln); targ->node = gn; targ->parent = (Src *)NULL; /* * Allocate room for the prefix, whose end is found by subtracting * the length of the suffix from the end of the name. */ prefLen = (eoname - targ->suff->nameLen) - sopref; targ->pref = emalloc(prefLen + 1); bcopy(sopref, targ->pref, prefLen); targ->pref[prefLen] = '\0'; /* * Add nodes from which the target can be made */ SuffAddLevel(srcs, targ); /* * Record the target so we can nuke it */ (void)Lst_AtEnd(targs, (ClientData)targ); /* * Search from this suffix's successor... */ ln = Lst_Succ(ln); } } /* * Handle target of unknown suffix... */ if (Lst_IsEmpty(targs) && suffNull != NULL) { if (DEBUG(SUFF)) { printf("\tNo known suffix on %s. Using .NULL suffix\n", gn->name); } targ = (Src *)emalloc(sizeof(Src)); targ->file = strdup(gn->name); targ->suff = suffNull; targ->node = gn; targ->parent = (Src *)NULL; targ->pref = strdup(sopref); SuffAddLevel(srcs, targ); (void)Lst_AtEnd(targs, (ClientData)targ); } /* * Using the list of possible sources built up from the target suffix(es), * try and find an existing file/target that matches. */ bottom = SuffFindThem(srcs); if (bottom == (Src *)NULL) { /* * No known transformations -- use the first suffix found for setting * the local variables. */ if (!Lst_IsEmpty(targs)) { targ = (Src *)Lst_Datum(Lst_First(targs)); } else { targ = (Src *)NULL; } } else { /* * Work up the transformation path to find the suffix of the * target to which the transformation was made. */ for (targ = bottom; targ->parent != NULL; targ = targ->parent) { ; } } /* * The .TARGET variable we always set to be the name at this point, * since it's only set to the path if the thing is only a source and * if it's only a source, it doesn't matter what we put here as far * as expanding sources is concerned, since it has none... */ Var_Set(TARGET, gn->name, gn); pref = (targ != NULL) ? targ->pref : gn->name; Var_Set(PREFIX, pref, gn); /* * Now we've got the important local variables set, expand any sources * that still contain variables or wildcards in their names. */ Lst_ForEach(gn->children, SuffExpandChildren, (ClientData)gn); if (targ == NULL) { if (DEBUG(SUFF)) { printf("\tNo valid suffix on %s\n", gn->name); } sfnd_abort: /* * Deal with finding the thing on the default search path if the * node is only a source (not on the lhs of a dependency operator * or [XXX] it has neither children or commands). */ if (OP_NOP(gn->type) || (Lst_IsEmpty(gn->children) && Lst_IsEmpty(gn->commands))) { gn->path = Dir_FindFile(gn->name, (targ == NULL ? dirSearchPath : targ->suff->searchPath)); if (gn->path != NULL) { Var_Set(TARGET, gn->path, gn); if (targ != NULL) { /* * Suffix known for the thing -- trim the suffix off * the path to form the proper .PREFIX variable. */ int len = strlen(gn->path); char savec; gn->suffix = targ->suff; savec = gn->path[len-targ->suff->nameLen]; gn->path[len-targ->suff->nameLen] = '\0'; Var_Set(PREFIX, gn->path, gn); gn->path[len-targ->suff->nameLen] = savec; } else { /* * The .PREFIX gets the full path if the target has * no known suffix. */ gn->suffix = NULL; Var_Set(PREFIX, gn->path, gn); } } } else { /* * Not appropriate to search for the thing -- set the * path to be the name so Dir_MTime won't go grovelling for * it. */ gn->suffix = (targ == NULL) ? NULL : targ->suff; gn->path = gn->name; } goto sfnd_return; } /* * If the suffix indicates that the target is a library, mark that in * the node's type field. */ if (targ->suff->flags & SUFF_LIBRARY) { gn->type |= OP_LIB; } /* * Check for overriding transformation rule implied by sources */ if (!Lst_IsEmpty(gn->children)) { src = SuffFindCmds(targ); if (src != (Src *)NULL) { /* * Free up all the Src structures in the transformation path * up to, but not including, the parent node. */ while (bottom && bottom->parent != NULL) { Src *p = bottom->parent; SuffFreeSrc(bottom); bottom = p; } bottom = src; } } if (bottom == NULL) { /* * No idea from where it can come -- return now. */ goto sfnd_abort; } /* * We now have a list of Src structures headed by 'bottom' and linked via * their 'parent' pointers. What we do next is create links between * source and target nodes (which may or may not have been created) * and set the necessary local variables in each target. The * commands for each target are set from the commands of the * transformation rule used to get from the src suffix to the targ * suffix. Note that this causes the commands list of the original * node, gn, to be replaced by the commands of the final * transformation rule. Also, the unmade field of gn is incremented. * Etc. */ if (bottom->node == NILGNODE) { bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); } for (src = bottom; src->parent != (Src *)NULL; src = src->parent) { targ = src->parent; src->node->suffix = src->suff; if (targ->node == NILGNODE) { targ->node = Targ_FindNode(targ->file, TARG_CREATE); } SuffApplyTransform(targ->node, src->node, targ->suff, src->suff); if (targ->node != gn) { /* * Finish off the dependency-search process for any nodes * between bottom and gn (no point in questing around the * filesystem for their implicit source when it's already * known). Note that the node can't have any sources that * need expanding, since SuffFindThem will stop on an existing * node, so all we need to do is set the standard and System V * variables. */ targ->node->type |= OP_DEPS_FOUND; Var_Set(PREFIX, targ->pref, targ->node); Var_Set(TARGET, targ->node->name, targ->node); } } gn->suffix = src->suff; /* * So Dir_MTime doesn't go questing for it... */ gn->path = gn->name; /* * Nuke the transformation path and the Src structures left over in the * two lists. */ SuffFreeSrc(bottom); sfnd_return: Lst_Destroy(srcs, SuffFreeSrc); Lst_Destroy(targs, SuffFreeSrc); } /*- *----------------------------------------------------------------------- * Suff_FindDeps -- * Find implicit sources for the target described by the graph node * gn * * Results: * Nothing. * * Side Effects: * Nodes are added to the graph below the passed-in node. The nodes * are marked to have their IMPSRC variable filled in. The * PREFIX variable is set for the given node and all its * implied children. * * Notes: * The path found by this target is the shortest path in the * transformation graph, which may pass through non-existent targets, * to an existing target. The search continues on all paths from the * root suffix until a file is found. I.e. if there's a path * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but * the .c and .l files don't, the search will branch out in * all directions from .o and again from all the nodes on the * next level until the .l,v node is encountered. * *----------------------------------------------------------------------- */ void Suff_FindDeps (gn) GNode *gn; /* node we're dealing with */ { if (gn->type & OP_DEPS_FOUND) { /* * If dependencies already found, no need to do it again... */ return; } else { gn->type |= OP_DEPS_FOUND; } if (DEBUG(SUFF)) { printf ("Suff_FindDeps (%s)\n", gn->name); } if (gn->type & OP_ARCHV) { SuffFindArchiveDeps(gn); } else if (gn->type & OP_LIB) { /* * If the node is a library, it is the arch module's job to find it * and set the TARGET variable accordingly. We merely provide the * search path, assuming all libraries end in ".a" (if the suffix * hasn't been defined, there's nothing we can do for it, so we just * set the TARGET variable to the node's name in order to give it a * value). */ LstNode ln; Suff *s; ln = Lst_Find (sufflist, (ClientData)LIBSUFF, SuffSuffHasNameP); if (ln != NILLNODE) { gn->suffix = s = (Suff *) Lst_Datum (ln); Arch_FindLib (gn, s->searchPath); } else { gn->suffix = NULL; Var_Set (TARGET, gn->name, gn); } /* * Because a library (-lfoo) target doesn't follow the standard * filesystem conventions, we don't set the regular variables for * the thing. .PREFIX is simply made empty... */ Var_Set(PREFIX, "", gn); } else { SuffFindNormalDeps(gn); } } /*- *----------------------------------------------------------------------- * Suff_SetNull -- * Define which suffix is the null suffix. * * Results: * None. * * Side Effects: * 'suffNull' is altered. * * Notes: * Need to handle the changing of the null suffix gracefully so the * old transformation rules don't just go away. * *----------------------------------------------------------------------- */ void Suff_SetNull(name) char *name; /* Name of null suffix */ { Suff *s; LstNode ln; ln = Lst_Find(sufflist, (ClientData)name, SuffSuffHasNameP); if (ln != NILLNODE) { s = (Suff *)Lst_Datum(ln); if (suffNull != (Suff *)NULL) { suffNull->flags &= ~SUFF_NULL; } s->flags |= SUFF_NULL; /* * XXX: Here's where the transformation mangling would take place */ suffNull = s; } else { Parse_Error (PARSE_WARNING, "Desired null suffix %s not defined.", name); } } /*- *----------------------------------------------------------------------- * Suff_Init -- * Initialize suffixes module * * Results: * None * * Side Effects: * Many *----------------------------------------------------------------------- */ void Suff_Init () { sufflist = Lst_Init (FALSE); transforms = Lst_Init (FALSE); sNum = 0; /* * Create null suffix for single-suffix rules (POSIX). The thing doesn't * actually go on the suffix list or everyone will think that's its * suffix. */ emptySuff = suffNull = (Suff *) emalloc (sizeof (Suff)); suffNull->name = strdup (""); suffNull->nameLen = 0; suffNull->searchPath = Lst_Init (FALSE); suffNull->children = Lst_Init (FALSE); suffNull->parents = Lst_Init (FALSE); suffNull->sNum = sNum++; suffNull->flags = SUFF_NULL; } /********************* DEBUGGING FUNCTIONS **********************/ static int SuffPrintName(s) Suff *s; {printf ("%s ", s->name); return (0);} static int SuffPrintSuff (s) Suff *s; { int flags; int flag; printf ("# `%s'", s->name); flags = s->flags; if (flags) { fputs (" (", stdout); while (flags) { flag = 1 << (ffs(flags) - 1); flags &= ~flag; switch (flag) { case SUFF_NULL: printf ("NULL"); break; case SUFF_INCLUDE: printf ("INCLUDE"); break; case SUFF_LIBRARY: printf ("LIBRARY"); break; } putc(flags ? '|' : ')', stdout); } } putc ('\n', stdout); printf ("#\tTo: "); Lst_ForEach (s->parents, SuffPrintName, (ClientData)0); putc ('\n', stdout); printf ("#\tFrom: "); Lst_ForEach (s->children, SuffPrintName, (ClientData)0); putc ('\n', stdout); printf ("#\tSearch Path: "); Dir_PrintPath (s->searchPath); putc ('\n', stdout); return (0); } static int SuffPrintTrans (t) GNode *t; { extern int Targ_PrintCmd(); printf ("%-16s: ", t->name); Targ_PrintType (t->type); putc ('\n', stdout); Lst_ForEach (t->commands, Targ_PrintCmd, (ClientData)0); putc ('\n', stdout); return(0); } Suff_PrintAll() { printf ("#*** Suffixes:\n"); Lst_ForEach (sufflist, SuffPrintSuff, (ClientData)0); printf ("#*** Transformations:\n"); Lst_ForEach (transforms, SuffPrintTrans, (ClientData)0); } pmake/targ.c100600 1750 1750 37404 5431142075 11462 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)targ.c 5.9 (Berkeley) 3/1/91"; #endif /* not lint */ /*- * targ.c -- * Functions for maintaining the Lst allTargets. Target nodes are * kept in two structures: a Lst, maintained by the list library, and a * hash table, maintained by the hash library. * * Interface: * Targ_Init Initialization procedure. * * Targ_NewGN Create a new GNode for the passed target * (string). The node is *not* placed in the * hash table, though all its fields are * initialized. * * Targ_FindNode Find the node for a given target, creating * and storing it if it doesn't exist and the * flags are right (TARG_CREATE) * * Targ_FindList Given a list of names, find nodes for all * of them. If a name doesn't exist and the * TARG_NOCREATE flag was given, an error message * is printed. Else, if a name doesn't exist, * its node is created. * * Targ_Ignore Return TRUE if errors should be ignored when * creating the given target. * * Targ_Silent Return TRUE if we should be silent when * creating the given target. * * Targ_Precious Return TRUE if the target is precious and * should not be removed if we are interrupted. * * Debugging: * Targ_PrintGraph Print out the entire graphm all variables * and statistics for the directory cache. Should * print something for suffixes, too, but... */ #include #include #include "make.h" #include "hash.h" static Lst allTargets; /* the list of all targets found so far */ static Hash_Table targets; /* a hash table of same */ #define HTSIZE 191 /* initial size of hash table */ /*- *----------------------------------------------------------------------- * Targ_Init -- * Initialize this module * * Results: * None * * Side Effects: * The allTargets list and the targets hash table are initialized *----------------------------------------------------------------------- */ void Targ_Init () { allTargets = Lst_Init (FALSE); Hash_InitTable (&targets, HTSIZE); } /*- *----------------------------------------------------------------------- * Targ_NewGN -- * Create and initialize a new graph node * * Results: * An initialized graph node with the name field filled with a copy * of the passed name * * Side Effects: * None. *----------------------------------------------------------------------- */ GNode * Targ_NewGN (name) char *name; /* the name to stick in the new node */ { register GNode *gn; gn = (GNode *) emalloc (sizeof (GNode)); gn->name = strdup (name); gn->path = (char *) 0; if (name[0] == '-' && name[1] == 'l') { gn->type = OP_LIB; } else { gn->type = 0; } gn->unmade = 0; gn->make = FALSE; gn->made = UNMADE; gn->childMade = FALSE; gn->mtime = gn->cmtime = 0; gn->iParents = Lst_Init (FALSE); gn->cohorts = Lst_Init (FALSE); gn->parents = Lst_Init (FALSE); gn->children = Lst_Init (FALSE); gn->successors = Lst_Init(FALSE); gn->preds = Lst_Init(FALSE); gn->context = Lst_Init (FALSE); gn->commands = Lst_Init (FALSE); return (gn); } /*- *----------------------------------------------------------------------- * Targ_FindNode -- * Find a node in the list using the given name for matching * * Results: * The node in the list if it was. If it wasn't, return NILGNODE of * flags was TARG_NOCREATE or the newly created and initialized node * if it was TARG_CREATE * * Side Effects: * Sometimes a node is created and added to the list *----------------------------------------------------------------------- */ GNode * Targ_FindNode (name, flags) char *name; /* the name to find */ int flags; /* flags governing events when target not * found */ { GNode *gn; /* node in that element */ Hash_Entry *he; /* New or used hash entry for node */ Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ /* an entry for the node */ if (flags & TARG_CREATE) { he = Hash_CreateEntry (&targets, name, &isNew); if (isNew) { gn = Targ_NewGN (name); Hash_SetValue (he, gn); (void) Lst_AtEnd (allTargets, (ClientData)gn); } } else { he = Hash_FindEntry (&targets, name); } if (he == (Hash_Entry *) NULL) { return (NILGNODE); } else { return ((GNode *) Hash_GetValue (he)); } } /*- *----------------------------------------------------------------------- * Targ_FindList -- * Make a complete list of GNodes from the given list of names * * Results: * A complete list of graph nodes corresponding to all instances of all * the names in names. * * Side Effects: * If flags is TARG_CREATE, nodes will be created for all names in * names which do not yet have graph nodes. If flags is TARG_NOCREATE, * an error message will be printed for each name which can't be found. * ----------------------------------------------------------------------- */ Lst Targ_FindList (names, flags) Lst names; /* list of names to find */ int flags; /* flags used if no node is found for a given * name */ { Lst nodes; /* result list */ register LstNode ln; /* name list element */ register GNode *gn; /* node in tLn */ char *name; nodes = Lst_Init (FALSE); if (Lst_Open (names) == FAILURE) { return (nodes); } while ((ln = Lst_Next (names)) != NILLNODE) { name = (char *)Lst_Datum(ln); gn = Targ_FindNode (name, flags); if (gn != NILGNODE) { /* * Note: Lst_AtEnd must come before the Lst_Concat so the nodes * are added to the list in the order in which they were * encountered in the makefile. */ (void) Lst_AtEnd (nodes, (ClientData)gn); if (gn->type & OP_DOUBLEDEP) { (void)Lst_Concat (nodes, gn->cohorts, LST_CONCNEW); } } else if (flags == TARG_NOCREATE) { Error ("\"%s\" -- target unknown.", name); } } Lst_Close (names); return (nodes); } /*- *----------------------------------------------------------------------- * Targ_Ignore -- * Return true if should ignore errors when creating gn * * Results: * TRUE if should ignore errors * * Side Effects: * None *----------------------------------------------------------------------- */ Boolean Targ_Ignore (gn) GNode *gn; /* node to check for */ { if (ignoreErrors || gn->type & OP_IGNORE) { return (TRUE); } else { return (FALSE); } } /*- *----------------------------------------------------------------------- * Targ_Silent -- * Return true if be silent when creating gn * * Results: * TRUE if should be silent * * Side Effects: * None *----------------------------------------------------------------------- */ Boolean Targ_Silent (gn) GNode *gn; /* node to check for */ { if (beSilent || gn->type & OP_SILENT) { return (TRUE); } else { return (FALSE); } } /*- *----------------------------------------------------------------------- * Targ_Precious -- * See if the given target is precious * * Results: * TRUE if it is precious. FALSE otherwise * * Side Effects: * None *----------------------------------------------------------------------- */ Boolean Targ_Precious (gn) GNode *gn; /* the node to check */ { if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { return (TRUE); } else { return (FALSE); } } /******************* DEBUG INFO PRINTING ****************/ static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ /*- *----------------------------------------------------------------------- * Targ_SetMain -- * Set our idea of the main target we'll be creating. Used for * debugging output. * * Results: * None. * * Side Effects: * "mainTarg" is set to the main target's node. *----------------------------------------------------------------------- */ void Targ_SetMain (gn) GNode *gn; /* The main target we'll create */ { mainTarg = gn; } static int TargPrintName (gn, ppath) GNode *gn; int ppath; { printf ("%s ", gn->name); #ifdef notdef if (ppath) { if (gn->path) { printf ("[%s] ", gn->path); } if (gn == mainTarg) { printf ("(MAIN NAME) "); } } #endif notdef return (0); } int Targ_PrintCmd (cmd) char *cmd; { printf ("\t%s\n", cmd); return (0); } /*- *----------------------------------------------------------------------- * Targ_FmtTime -- * Format a modification time in some reasonable way and return it. * * Results: * The time reformatted. * * Side Effects: * The time is placed in a static area, so it is overwritten * with each call. * *----------------------------------------------------------------------- */ char * Targ_FmtTime (time) time_t time; { struct tm *parts; static char buf[40]; static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; parts = localtime(&time); sprintf (buf, "%d:%02d:%02d %s %d, 19%d", parts->tm_hour, parts->tm_min, parts->tm_sec, months[parts->tm_mon], parts->tm_mday, parts->tm_year); return(buf); } /*- *----------------------------------------------------------------------- * Targ_PrintType -- * Print out a type field giving only those attributes the user can * set. * * Results: * * Side Effects: * *----------------------------------------------------------------------- */ void Targ_PrintType (type) register int type; { register int tbit; #ifdef __STDC__ #define PRINTBIT(attr) case CONCAT(OP_,attr): printf("." #attr " "); break #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf("." #attr " "); break #else #define PRINTBIT(attr) case CONCAT(OP_,attr): printf(".attr "); break #define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG)) printf(".attr "); break #endif /* __STDC__ */ type &= ~OP_OPMASK; while (type) { tbit = 1 << (ffs(type) - 1); type &= ~tbit; switch(tbit) { PRINTBIT(OPTIONAL); PRINTBIT(USE); PRINTBIT(EXEC); PRINTBIT(IGNORE); PRINTBIT(PRECIOUS); PRINTBIT(SILENT); PRINTBIT(MAKE); PRINTBIT(JOIN); PRINTBIT(INVISIBLE); PRINTBIT(NOTMAIN); PRINTDBIT(LIB); /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ case OP_MEMBER: if (DEBUG(TARG)) printf(".MEMBER "); break; PRINTDBIT(ARCHV); } } } /*- *----------------------------------------------------------------------- * TargPrintNode -- * print the contents of a node *----------------------------------------------------------------------- */ static int TargPrintNode (gn, pass) GNode *gn; int pass; { if (!OP_NOP(gn->type)) { printf("#\n"); if (gn == mainTarg) { printf("# *** MAIN TARGET ***\n"); } if (pass == 2) { if (gn->unmade) { printf("# %d unmade children\n", gn->unmade); } else { printf("# No unmade children\n"); } if (! (gn->type & (OP_JOIN|OP_USE|OP_EXEC))) { if (gn->mtime != 0) { printf("# last modified %s: %s\n", Targ_FmtTime(gn->mtime), (gn->made == UNMADE ? "unmade" : (gn->made == MADE ? "made" : (gn->made == UPTODATE ? "up-to-date" : "error when made")))); } else if (gn->made != UNMADE) { printf("# non-existent (maybe): %s\n", (gn->made == MADE ? "made" : (gn->made == UPTODATE ? "up-to-date" : (gn->made == ERROR ? "error when made" : "aborted")))); } else { printf("# unmade\n"); } } if (!Lst_IsEmpty (gn->iParents)) { printf("# implicit parents: "); Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0); putc ('\n', stdout); } } if (!Lst_IsEmpty (gn->parents)) { printf("# parents: "); Lst_ForEach (gn->parents, TargPrintName, (ClientData)0); putc ('\n', stdout); } printf("%-16s", gn->name); switch (gn->type & OP_OPMASK) { case OP_DEPENDS: printf(": "); break; case OP_FORCE: printf("! "); break; case OP_DOUBLEDEP: printf(":: "); break; } Targ_PrintType (gn->type); Lst_ForEach (gn->children, TargPrintName, (ClientData)0); putc ('\n', stdout); Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0); printf("\n\n"); if (gn->type & OP_DOUBLEDEP) { Lst_ForEach (gn->cohorts, TargPrintNode, (ClientData)pass); } } return (0); } /*- *----------------------------------------------------------------------- * TargPrintOnlySrc -- * Print only those targets that are just a source. * * Results: * 0. * * Side Effects: * The name of each file is printed preceeded by #\t * *----------------------------------------------------------------------- */ static int TargPrintOnlySrc(gn) GNode *gn; { if (OP_NOP(gn->type)) { printf("#\t%s [%s]\n", gn->name, gn->path ? gn->path : gn->name); } return (0); } /*- *----------------------------------------------------------------------- * Targ_PrintGraph -- * print the entire graph. heh heh * * Results: * none * * Side Effects: * lots o' output *----------------------------------------------------------------------- */ Targ_PrintGraph (pass) int pass; /* Which pass this is. 1 => no processing * 2 => processing done */ { printf("#*** Input graph:\n"); Lst_ForEach (allTargets, TargPrintNode, (ClientData)pass); printf("\n\n"); printf("#\n# Files that are only sources:\n"); Lst_ForEach (allTargets, TargPrintOnlySrc); printf("#*** Global Variables:\n"); Var_Dump (VAR_GLOBAL); printf("#*** Command-line Variables:\n"); Var_Dump (VAR_CMD); printf("\n"); Dir_PrintDirectories(); printf("\n"); Suff_PrintAll(); } pmake/var.c100600 1750 1750 147062 5431142076 11340 0ustar karlkarl/* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989 by Adam de Boor * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * * This code is derived from software contributed to Berkeley by * Adam de Boor. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef lint static char sccsid[] = "@(#)var.c 5.7 (Berkeley) 6/1/90"; #endif /* not lint */ /*- * var.c -- * Variable-handling functions * * Interface: * Var_Set Set the value of a variable in the given * context. The variable is created if it doesn't * yet exist. The value and variable name need not * be preserved. * * Var_Append Append more characters to an existing variable * in the given context. The variable needn't * exist already -- it will be created if it doesn't. * A space is placed between the old value and the * new one. * * Var_Exists See if a variable exists. * * Var_Value Return the value of a variable in a context or * NULL if the variable is undefined. * * Var_Subst Substitute for all variables in a string using * the given context as the top-most one. If the * third argument is non-zero, Parse_Error is * called if any variables are undefined. * * Var_Parse Parse a variable expansion from a string and * return the result and the number of characters * consumed. * * Var_Delete Delete a variable in a context. * * Var_Init Initialize this module. * * Debugging: * Var_Dump Print out all variables defined in the given * context. * * XXX: There's a lot of duplication in these functions. */ #include #include "make.h" #include "buf.h" extern char *getenv(); /* * This is a harmless return value for Var_Parse that can be used by Var_Subst * to determine if there was an error in parsing -- easier than returning * a flag, as things outside this module don't give a hoot. */ char var_Error[] = ""; /* * Similar to var_Error, but returned when the 'err' flag for Var_Parse is * set false. Why not just use a constant? Well, gcc likes to condense * identical string instances... */ char varNoError[] = ""; /* * Internally, variables are contained in four different contexts. * 1) the environment. They may not be changed. If an environment * variable is appended-to, the result is placed in the global * context. * 2) the global context. Variables set in the Makefile are located in * the global context. It is the penultimate context searched when * substituting. * 3) the command-line context. All variables set on the command line * are placed in this context. They are UNALTERABLE once placed here. * 4) the local context. Each target has associated with it a context * list. On this list are located the structures describing such * local variables as $(@) and $(*) * The four contexts are searched in the reverse order from which they are * listed. */ GNode *VAR_GLOBAL; /* variables from the makefile */ GNode *VAR_CMD; /* variables defined on the command-line */ #define FIND_CMD 0x1 /* look in VAR_CMD when searching */ #define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ #define FIND_ENV 0x4 /* look in the environment also */ typedef struct Var { char *name; /* the variable's name */ Buffer val; /* its value */ int flags; /* miscellaneous status flags */ #define VAR_IN_USE 1 /* Variable's value currently being used. * Used to avoid recursion */ #define VAR_FROM_ENV 2 /* Variable comes from the environment */ #define VAR_JUNK 4 /* Variable is a junk variable that * should be destroyed when done with * it. Used by Var_Parse for undefined, * modified variables */ } Var; /*- *----------------------------------------------------------------------- * VarCmp -- * See if the given variable matches the named one. Called from * Lst_Find when searching for a variable of a given name. * * Results: * 0 if they match. non-zero otherwise. * * Side Effects: * none *----------------------------------------------------------------------- */ static int VarCmp (v, name) Var *v; /* VAR structure to compare */ char *name; /* name to look for */ { return (strcmp (name, v->name)); } /*- *----------------------------------------------------------------------- * VarFind -- * Find the given variable in the given context and any other contexts * indicated. * * Results: * A pointer to the structure describing the desired variable or * NIL if the variable does not exist. * * Side Effects: * None *----------------------------------------------------------------------- */ static Var * VarFind (name, ctxt, flags) char *name; /* name to find */ GNode *ctxt; /* context in which to find it */ int flags; /* FIND_GLOBAL set means to look in the * VAR_GLOBAL context as well. * FIND_CMD set means to look in the VAR_CMD * context also. * FIND_ENV set means to look in the * environment */ { LstNode var; Var *v; /* * If the variable name begins with a '.', it could very well be one of * the local ones. We check the name against all the local variables * and substitute the short version in for 'name' if it matches one of * them. */ if (*name == '.' && isupper(name[1])) switch (name[1]) { case 'A': if (!strcmp(name, ".ALLSRC")) name = ALLSRC; if (!strcmp(name, ".ARCHIVE")) name = ARCHIVE; break; case 'I': if (!strcmp(name, ".IMPSRC")) name = IMPSRC; break; case 'M': if (!strcmp(name, ".MEMBER")) name = MEMBER; break; case 'O': if (!strcmp(name, ".OODATE")) name = OODATE; break; case 'P': if (!strcmp(name, ".PREFIX")) name = PREFIX; break; case 'T': if (!strcmp(name, ".TARGET")) name = TARGET; break; } /* * First look for the variable in the given context. If it's not there, * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, * depending on the FIND_* flags in 'flags' */ var = Lst_Find (ctxt->context, (ClientData)name, VarCmp); if ((var == NILLNODE) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { var = Lst_Find (VAR_CMD->context, (ClientData)name, VarCmp); } if (!checkEnvFirst && (var == NILLNODE) && (flags & FIND_GLOBAL) && (ctxt != VAR_GLOBAL)) { var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); } if ((var == NILLNODE) && (flags & FIND_ENV)) { char *env; if ((env = getenv (name)) != NULL) { /* * If the variable is found in the environment, we only duplicate * its value (since eVarVal was allocated on the stack). The name * doesn't need duplication since it's always in the environment */ int len; v = (Var *) emalloc(sizeof(Var)); v->name = name; len = strlen(env); v->val = Buf_Init(len); Buf_AddBytes(v->val, len, (Byte *)env); v->flags = VAR_FROM_ENV; return (v); } else if (checkEnvFirst && (flags & FIND_GLOBAL) && (ctxt != VAR_GLOBAL)) { var = Lst_Find (VAR_GLOBAL->context, (ClientData)name, VarCmp); if (var == NILLNODE) { return ((Var *) NIL); } else { return ((Var *)Lst_Datum(var)); } } else { return((Var *)NIL); } } else if (var == NILLNODE) { return ((Var *) NIL); } else { return ((Var *) Lst_Datum (var)); } } /*- *----------------------------------------------------------------------- * VarAdd -- * Add a new variable of name name and value val to the given context * * Results: * None * * Side Effects: * The new variable is placed at the front of the given context * The name and val arguments are duplicated so they may * safely be freed. *----------------------------------------------------------------------- */ static VarAdd (name, val, ctxt) char *name; /* name of variable to add */ char *val; /* value to set it to */ GNode *ctxt; /* context in which to set it */ { register Var *v; int len; v = (Var *) emalloc (sizeof (Var)); v->name = strdup (name); len = strlen(val); v->val = Buf_Init(len+1); Buf_AddBytes(v->val, len, (Byte *)val); v->flags = 0; (void) Lst_AtFront (ctxt->context, (ClientData)v); if (DEBUG(VAR)) { printf("%s:%s = %s\n", ctxt->name, name, val); } } /*- *----------------------------------------------------------------------- * Var_Delete -- * Remove a variable from a context. * * Results: * None. * * Side Effects: * The Var structure is removed and freed. * *----------------------------------------------------------------------- */ void Var_Delete(name, ctxt) char *name; GNode *ctxt; { LstNode ln; if (DEBUG(VAR)) { printf("%s:delete %s\n", ctxt->name, name); } ln = Lst_Find(ctxt->context, (ClientData)name, VarCmp); if (ln != NILLNODE) { register Var *v; v = (Var *)Lst_Datum(ln); Lst_Remove(ctxt->context, ln); Buf_Destroy(v->val, TRUE); free(v->name); free((char *)v); } } /*- *----------------------------------------------------------------------- * Var_Set -- * Set the variable name to the value val in the given context. * * Results: * None. * * Side Effects: * If the variable doesn't yet exist, a new record is created for it. * Else the old value is freed and the new one stuck in its place * * Notes: * The variable is searched for only in its context before being * created in that context. I.e. if the context is VAR_GLOBAL, * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only * VAR_CMD->context is searched. This is done to avoid the literally * thousands of unnecessary strcmp's that used to be done to * set, say, $(@) or $(<). *----------------------------------------------------------------------- */ void Var_Set (name, val, ctxt) char *name; /* name of variable to set */ char *val; /* value to give to the variable */ GNode *ctxt; /* context in which to set it */ { register Var *v; /* * We only look for a variable in the given context since anything set * here will override anything in a lower context, so there's not much * point in searching them all just to save a bit of memory... */ v = VarFind (name, ctxt, 0); if (v == (Var *) NIL) { VarAdd (name, val, ctxt); } else { Buf_Discard(v->val, Buf_Size(v->val)); Buf_AddBytes(v->val, strlen(val), (Byte *)val); if (DEBUG(VAR)) { printf("%s:%s = %s\n", ctxt->name, name, val); } } /* * Any variables given on the command line are automatically exported * to the environment (as per POSIX standard) */ if (ctxt == VAR_CMD) { setenv(name, val); } } /*- *----------------------------------------------------------------------- * Var_Append -- * The variable of the given name has the given value appended to it in * the given context. * * Results: * None * * Side Effects: * If the variable doesn't exist, it is created. Else the strings * are concatenated (with a space in between). * * Notes: * Only if the variable is being sought in the global context is the * environment searched. * XXX: Knows its calling circumstances in that if called with ctxt * an actual target, it will only search that context since only * a local variable could be being appended to. This is actually * a big win and must be tolerated. *----------------------------------------------------------------------- */ void Var_Append (name, val, ctxt) char *name; /* Name of variable to modify */ char *val; /* String to append to it */ GNode *ctxt; /* Context in which this should occur */ { register Var *v; register char *cp; v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); if (v == (Var *) NIL) { VarAdd (name, val, ctxt); } else { Buf_AddByte(v->val, (Byte)' '); Buf_AddBytes(v->val, strlen(val), (Byte *)val); if (DEBUG(VAR)) { printf("%s:%s = %s\n", ctxt->name, name, Buf_GetAll(v->val, (int *)NULL)); } if (v->flags & VAR_FROM_ENV) { /* * If the original variable came from the environment, we * have to install it in the global context (we could place * it in the environment, but then we should provide a way to * export other variables...) */ v->flags &= ~VAR_FROM_ENV; Lst_AtFront(ctxt->context, (ClientData)v); } } } /*- *----------------------------------------------------------------------- * Var_Exists -- * See if the given variable exists. * * Results: * TRUE if it does, FALSE if it doesn't * * Side Effects: * None. * *----------------------------------------------------------------------- */ Boolean Var_Exists(name, ctxt) char *name; /* Variable to find */ GNode *ctxt; /* Context in which to start search */ { Var *v; v = VarFind(name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); if (v == (Var *)NIL) { return(FALSE); } else if (v->flags & VAR_FROM_ENV) { Buf_Destroy(v->val, TRUE); free((char *)v); } return(TRUE); } /*- *----------------------------------------------------------------------- * Var_Value -- * Return the value of the named variable in the given context * * Results: * The value if the variable exists, NULL if it doesn't * * Side Effects: * None *----------------------------------------------------------------------- */ char * Var_Value (name, ctxt) char *name; /* name to find */ GNode *ctxt; /* context in which to search for it */ { Var *v; v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if (v != (Var *) NIL) { return ((char *)Buf_GetAll(v->val, (int *)NULL)); } else { return ((char *) NULL); } } /*- *----------------------------------------------------------------------- * VarHead -- * Remove the tail of the given word and place the result in the given * buffer. * * Results: * TRUE if characters were added to the buffer (a space needs to be * added to the buffer before the next word). * * Side Effects: * The trimmed word is added to the buffer. * *----------------------------------------------------------------------- */ static Boolean VarHead (word, addSpace, buf) char *word; /* Word to trim */ Boolean addSpace; /* True if need to add a space to the buffer * before sticking in the head */ Buffer buf; /* Buffer in which to store it */ { register char *slash; slash = rindex (word, '/'); if (slash != (char *)NULL) { if (addSpace) { Buf_AddByte (buf, (Byte)' '); } *slash = '\0'; Buf_AddBytes (buf, strlen (word), (Byte *)word); *slash = '/'; return (TRUE); } else { /* * If no directory part, give . (q.v. the POSIX standard) */ if (addSpace) { Buf_AddBytes(buf, 2, (Byte *)" ."); } else { Buf_AddByte(buf, (Byte)'.'); } return(TRUE); } } /*- *----------------------------------------------------------------------- * VarTail -- * Remove the head of the given word and place the result in the given * buffer. * * Results: * TRUE if characters were added to the buffer (a space needs to be * added to the buffer before the next word). * * Side Effects: * The trimmed word is added to the buffer. * *----------------------------------------------------------------------- */ static Boolean VarTail (word, addSpace, buf) char *word; /* Word to trim */ Boolean addSpace; /* TRUE if need to stick a space in the * buffer before adding the tail */ Buffer buf; /* Buffer in which to store it */ { register char *slash; if (addSpace) { Buf_AddByte (buf, (Byte)' '); } slash = rindex (word, '/'); if (slash != (char *)NULL) { *slash++ = '\0'; Buf_AddBytes (buf, strlen(slash), (Byte *)slash); slash[-1] = '/'; } else { Buf_AddBytes (buf, strlen(word), (Byte *)word); } return (TRUE); } /*- *----------------------------------------------------------------------- * VarSuffix -- * Place the suffix of the given word in the given buffer. * * Results: * TRUE if characters were added to the buffer (a space needs to be * added to the buffer before the next word). * * Side Effects: * The suffix from the word is placed in the buffer. * *----------------------------------------------------------------------- */ static Boolean VarSuffix (word, addSpace, buf) char *word; /* Word to trim */ Boolean addSpace; /* TRUE if need to add a space before placing * the suffix in the buffer */ Buffer buf; /* Buffer in which to store it */ { register char *dot; dot = rindex (word, '.'); if (dot != (char *)NULL) { if (addSpace) { Buf_AddByte (buf, (Byte)' '); } *dot++ = '\0'; Buf_AddBytes (buf, strlen (dot), (Byte *)dot); dot[-1] = '.'; return (TRUE); } else { return (addSpace); } } /*- *----------------------------------------------------------------------- * VarRoot -- * Remove the suffix of the given word and place the result in the * buffer. * * Results: * TRUE if characters were added to the buffer (a space needs to be * added to the buffer before the next word). * * Side Effects: * The trimmed word is added to the buffer. * *----------------------------------------------------------------------- */ static Boolean VarRoot (word, addSpace, buf) char *word; /* Word to trim */ Boolean addSpace; /* TRUE if need to add a space to the buffer * before placing the root in it */ Buffer buf; /* Buffer in which to store it */ { register char *dot; if (addSpace) { Buf_AddByte (buf, (Byte)' '); } dot = rindex (word, '.'); if (dot != (char *)NULL) { *dot = '\0'; Buf_AddBytes (buf, strlen (word), (Byte *)word); *dot = '.'; } else { Buf_AddBytes (buf, strlen(word), (Byte *)word); } return (TRUE); } /*- *----------------------------------------------------------------------- * VarMatch -- * Place the word in the buffer if it matches the given pattern. * Callback function for VarModify to implement the :M modifier. * * Results: * TRUE if a space should be placed in the buffer before the next * word. * * Side Effects: * The word may be copied to the buffer. * *----------------------------------------------------------------------- */ static Boolean VarMatch (word, addSpace, buf, pattern) char *word; /* Word to examine */ Boolean addSpace; /* TRUE if need to add a space to the * buffer before adding the word, if it * matches */ Buffer buf; /* Buffer in which to store it */ char *pattern; /* Pattern the word must match */ { if (Str_Match(word, pattern)) { if (addSpace) { Buf_AddByte(buf, (Byte)' '); } addSpace = TRUE; Buf_AddBytes(buf, strlen(word), (Byte *)word); } return(addSpace); } /*- *----------------------------------------------------------------------- * VarNoMatch -- * Place the word in the buffer if it doesn't match the given pattern. * Callback function for VarModify to implement the :N modifier. * * Results: * TRUE if a space should be placed in the buffer before the next * word. * * Side Effects: * The word may be copied to the buffer. * *----------------------------------------------------------------------- */ static Boolean VarNoMatch (word, addSpace, buf, pattern) char *word; /* Word to examine */ Boolean addSpace; /* TRUE if need to add a space to the * buffer before adding the word, if it * matches */ Buffer buf; /* Buffer in which to store it */ char *pattern; /* Pattern the word must match */ { if (!Str_Match(word, pattern)) { if (addSpace) { Buf_AddByte(buf, (Byte)' '); } addSpace = TRUE; Buf_AddBytes(buf, strlen(word), (Byte *)word); } return(addSpace); } typedef struct { char *lhs; /* String to match */ int leftLen; /* Length of string */ char *rhs; /* Replacement string (w/ &'s removed) */ int rightLen; /* Length of replacement */ int flags; #define VAR_SUB_GLOBAL 1 /* Apply substitution globally */ #define VAR_MATCH_START 2 /* Match at start of word */ #define VAR_MATCH_END 4 /* Match at end of word */ #define VAR_NO_SUB 8 /* Substitution is non-global and already done */ } VarPattern; /*- *----------------------------------------------------------------------- * VarSubstitute -- * Perform a string-substitution on the given word, placing the * result in the passed buffer. * * Results: * TRUE if a space is needed before more characters are added. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static Boolean VarSubstitute (word, addSpace, buf, pattern) char *word; /* Word to modify */ Boolean addSpace; /* True if space should be added before * other characters */ Buffer buf; /* Buffer for result */ register VarPattern *pattern; /* Pattern for substitution */ { register int wordLen; /* Length of word */ register char *cp; /* General pointer */ wordLen = strlen(word); if ((pattern->flags & VAR_NO_SUB) == 0) { /* * Still substituting -- break it down into simple anchored cases * and if none of them fits, perform the general substitution case. */ if ((pattern->flags & VAR_MATCH_START) && (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { /* * Anchored at start and beginning of word matches pattern */ if ((pattern->flags & VAR_MATCH_END) && (wordLen == pattern->leftLen)) { /* * Also anchored at end and matches to the end (word * is same length as pattern) add space and rhs only * if rhs is non-null. */ if (pattern->rightLen != 0) { if (addSpace) { Buf_AddByte(buf, (Byte)' '); } addSpace = TRUE; Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); } } else if (pattern->flags & VAR_MATCH_END) { /* * Doesn't match to end -- copy word wholesale */ goto nosub; } else { /* * Matches at start but need to copy in trailing characters */ if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ if (addSpace) { Buf_AddByte(buf, (Byte)' '); } addSpace = TRUE; } Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); Buf_AddBytes(buf, wordLen - pattern->leftLen, (Byte *)(word + pattern->leftLen)); } } else if (pattern->flags & VAR_MATCH_START) { /* * Had to match at start of word and didn't -- copy whole word. */ goto nosub; } else if (pattern->flags & VAR_MATCH_END) { /* * Anchored at end, Find only place match could occur (leftLen * characters from the end of the word) and see if it does. Note * that because the $ will be left at the end of the lhs, we have * to use strncmp. */ cp = word + (wordLen - pattern->leftLen); if ((cp >= word) && (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { /* * Match found. If we will place characters in the buffer, * add a space before hand as indicated by addSpace, then * stuff in the initial, unmatched part of the word followed * by the right-hand-side. */ if (((cp - word) + pattern->rightLen) != 0) { if (addSpace) { Buf_AddByte(buf, (Byte)' '); } addSpace = TRUE; } Buf_AddBytes(buf, cp - word, (Byte *)word); Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); } else { /* * Had to match at end and didn't. Copy entire word. */ goto nosub; } } else { /* * Pattern is unanchored: search for the pattern in the word using * String_FindSubstring, copying unmatched portions and the * right-hand-side for each match found, handling non-global * subsititutions correctly, etc. When the loop is done, any * remaining part of the word (word and wordLen are adjusted * accordingly through the loop) is copied straight into the * buffer. * addSpace is set FALSE as soon as a space is added to the * buffer. */ register Boolean done; int origSize; done = FALSE; origSize = Buf_Size(buf); while (!done) { cp = Str_FindSubstring(word, pattern->lhs); if (cp != (char *)NULL) { if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ Buf_AddByte(buf, (Byte)' '); addSpace = FALSE; } Buf_AddBytes(buf, cp-word, (Byte *)word); Buf_AddBytes(buf, pattern->rightLen, (Byte *)pattern->rhs); wordLen -= (cp - word) + pattern->leftLen; word = cp + pattern->leftLen; if (wordLen == 0) { done = TRUE; } if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { done = TRUE; pattern->flags |= VAR_NO_SUB; } } else { done = TRUE; } } if (wordLen != 0) { if (addSpace) { Buf_AddByte(buf, (Byte)' '); } Buf_AddBytes(buf, wordLen, (Byte *)word); } /* * If added characters to the buffer, need to add a space * before we add any more. If we didn't add any, just return * the previous value of addSpace. */ return ((Buf_Size(buf) != origSize) || addSpace); } /* * Common code for anchored substitutions: if performed a substitution * and it's not supposed to be global, mark the pattern as requiring * no more substitutions. addSpace was set TRUE if characters were * added to the buffer. */ if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { pattern->flags |= VAR_NO_SUB; } return (addSpace); } nosub: if (addSpace) { Buf_AddByte(buf, (Byte)' '); } Buf_AddBytes(buf, wordLen, (Byte *)word); return(TRUE); } /*- *----------------------------------------------------------------------- * VarModify -- * Modify each of the words of the passed string using the given * function. Used to implement all modifiers. * * Results: * A string of all the words modified appropriately. * * Side Effects: * None. * *----------------------------------------------------------------------- */ static char * VarModify (str, modProc, datum) char *str; /* String whose words should be trimmed */ Boolean (*modProc)(); /* Function to use to modify them */ ClientData datum; /* Datum to pass it */ { Buffer buf; /* Buffer for the new string */ register char *cp; /* Pointer to end of current word */ char endc; /* Character that ended the word */ Boolean addSpace; /* TRUE if need to add a space to the * buffer before adding the trimmed * word */ buf = Buf_Init (0); cp = str; addSpace = FALSE; while (1) { /* * Skip to next word and place cp at its end. */ while (isspace (*str)) { str++; } for (cp = str; *cp != '\0' && !isspace (*cp); cp++) { /* void */ ; } if (cp == str) { /* * If we didn't go anywhere, we must be done! */ Buf_AddByte (buf, '\0'); str = (char *)Buf_GetAll (buf, (int *)NULL); Buf_Destroy (buf, FALSE); return (str); } /* * Nuke terminating character, but save it in endc b/c if str was * some variable's value, it would not be good to screw it * over... */ endc = *cp; *cp = '\0'; addSpace = (* modProc) (str, addSpace, buf, datum); if (endc) { *cp++ = endc; } str = cp; } } /*- *----------------------------------------------------------------------- * Var_Parse -- * Given the start of a variable invocation, extract the variable * name and find its value, then modify it according to the * specification. * * Results: * The (possibly-modified) value of the variable or var_Error if the * specification is invalid. The length of the specification is * placed in *lengthPtr (for invalid specifications, this is just * 2...?). * A Boolean in *freePtr telling whether the returned string should * be freed by the caller. * * Side Effects: * None. * *----------------------------------------------------------------------- */ char * Var_Parse (str, ctxt, err, lengthPtr, freePtr) char *str; /* The string to parse */ GNode *ctxt; /* The context for the variable */ Boolean err; /* TRUE if undefined variables are an error */ int *lengthPtr; /* OUT: The length of the specification */ Boolean *freePtr; /* OUT: TRUE if caller should free result */ { register char *tstr; /* Pointer into str */ Var *v; /* Variable in invocation */ register char *cp; /* Secondary pointer into str (place marker * for tstr) */ Boolean haveModifier;/* TRUE if have modifiers for the variable */ register char endc; /* Ending character when variable in parens * or braces */ char *start; Boolean dynamic; /* TRUE if the variable is local and we're * expanding it in a non-local context. This * is done to support dynamic sources. The * result is just the invocation, unaltered */ *freePtr = FALSE; dynamic = FALSE; start = str; if (str[1] != '(' && str[1] != '{') { /* * If it's not bounded by braces of some sort, life is much simpler. * We just need to check for the first character and return the * value if it exists. */ char name[2]; name[0] = str[1]; name[1] = '\0'; v = VarFind (name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if (v == (Var *)NIL) { *lengthPtr = 2; if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { /* * If substituting a local variable in a non-local context, * assume it's for dynamic source stuff. We have to handle * this specially and return the longhand for the variable * with the dollar sign escaped so it makes it back to the * caller. Only four of the local variables are treated * specially as they are the only four that will be set * when dynamic sources are expanded. */ switch (str[1]) { case '@': return("$(.TARGET)"); case '%': return("$(.ARCHIVE)"); case '*': return("$(.PREFIX)"); case '!': return("$(.MEMBER)"); } } /* * Error */ return (err ? var_Error : varNoError); } else { haveModifier = FALSE; tstr = &str[1]; endc = str[1]; } } else { endc = str[1] == '(' ? ')' : '}'; /* * Skip to the end character or a colon, whichever comes first. */ for (tstr = str + 2; *tstr != '\0' && *tstr != endc && *tstr != ':'; tstr++) { continue; } if (*tstr == ':') { haveModifier = TRUE; } else if (*tstr != '\0') { haveModifier = FALSE; } else { /* * If we never did find the end character, return NULL * right now, setting the length to be the distance to * the end of the string, since that's what make does. */ *lengthPtr = tstr - str; return (var_Error); } *tstr = '\0'; v = VarFind (str + 2, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); if ((v == (Var *)NIL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && ((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')) { /* * Check for bogus D and F forms of local variables since we're * in a local context and the name is the right length. */ switch(str[2]) { case '@': case '%': case '*': case '!': case '>': case '<': { char vname[2]; char *val; /* * Well, it's local -- go look for it. */ vname[0] = str[2]; vname[1] = '\0'; v = VarFind(vname, ctxt, 0); if (v != (Var *)NIL) { /* * No need for nested expansion or anything, as we're * the only one who sets these things and we sure don't * but nested invocations in them... */ val = (char *)Buf_GetAll(v->val, (int *)NULL); if (str[3] == 'D') { val = VarModify(val, VarHead, (ClientData)0); } else { val = VarModify(val, VarTail, (ClientData)0); } /* * Resulting string is dynamically allocated, so * tell caller to free it. */ *freePtr = TRUE; *lengthPtr = tstr-start+1; *tstr = endc; return(val); } break; } } } if (v == (Var *)NIL) { if ((((tstr-str) == 3) || ((((tstr-str) == 4) && (str[3] == 'F' || str[3] == 'D')))) && ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) { /* * If substituting a local variable in a non-local context, * assume it's for dynamic source stuff. We have to handle * this specially and return the longhand for the variable * with the dollar sign escaped so it makes it back to the * caller. Only four of the local variables are treated * specially as they are the only four that will be set * when dynamic sources are expanded. */ switch (str[2]) { case '@': case '%': case '*': case '!': dynamic = TRUE; break; } } else if (((tstr-str) > 4) && (str[2] == '.') && isupper(str[3]) && ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) { int len; len = (tstr-str) - 3; if ((strncmp(str+2, ".TARGET", len) == 0) || (strncmp(str+2, ".ARCHIVE", len) == 0) || (strncmp(str+2, ".PREFIX", len) == 0) || (strncmp(str+2, ".MEMBER", len) == 0)) { dynamic = TRUE; } } if (!haveModifier) { /* * No modifiers -- have specification length so we can return * now. */ *lengthPtr = tstr - start + 1; *tstr = endc; if (dynamic) { str = emalloc(*lengthPtr + 1); strncpy(str, start, *lengthPtr); str[*lengthPtr] = '\0'; *freePtr = TRUE; return(str); } else { return (err ? var_Error : varNoError); } } else { /* * Still need to get to the end of the variable specification, * so kludge up a Var structure for the modifications */ v = (Var *) emalloc(sizeof(Var)); v->name = &str[1]; v->val = Buf_Init(1); v->flags = VAR_JUNK; } } } if (v->flags & VAR_IN_USE) { Fatal("Variable %s is recursive.", v->name); /*NOTREACHED*/ } else { v->flags |= VAR_IN_USE; } /* * Before doing any modification, we have to make sure the value * has been fully expanded. If it looks like recursion might be * necessary (there's a dollar sign somewhere in the variable's value) * we just call Var_Subst to do any other substitutions that are * necessary. Note that the value returned by Var_Subst will have * been dynamically-allocated, so it will need freeing when we * return. */ str = (char *)Buf_GetAll(v->val, (int *)NULL); if (index (str, '$') != (char *)NULL) { str = Var_Subst(str, ctxt, err); *freePtr = TRUE; } v->flags &= ~VAR_IN_USE; /* * Now we need to apply any modifiers the user wants applied. * These are: * :M words which match the given . * is of the standard file * wildcarding form. * :S[g] * Substitute for in the value * :H Substitute the head of each word * :T Substitute the tail of each word * :E Substitute the extension (minus '.') of * each word * :R Substitute the root of each word * (pathname minus the suffix). * :lhs=rhs Like :S, but the rhs goes to the end of * the invocation. */ if ((str != (char *)NULL) && haveModifier) { /* * Skip initial colon while putting it back. */ *tstr++ = ':'; while (*tstr != endc) { char *newStr; /* New value to return */ char termc; /* Character which terminated scan */ if (DEBUG(VAR)) { printf("Applying :%c to \"%s\"\n", *tstr, str); } switch (*tstr) { case 'N': case 'M': { char *pattern; char *cp2; Boolean copy; copy = FALSE; for (cp = tstr + 1; *cp != '\0' && *cp != ':' && *cp != endc; cp++) { if (*cp == '\\' && (cp[1] == ':' || cp[1] == endc)){ copy = TRUE; cp++; } } termc = *cp; *cp = '\0'; if (copy) { /* * Need to compress the \:'s out of the pattern, so * allocate enough room to hold the uncompressed * pattern (note that cp started at tstr+1, so * cp - tstr takes the null byte into account) and * compress the pattern into the space. */ pattern = emalloc(cp - tstr); for (cp2 = pattern, cp = tstr + 1; *cp != '\0'; cp++, cp2++) { if ((*cp == '\\') && (cp[1] == ':' || cp[1] == endc)) { cp++; } *cp2 = *cp; } *cp2 = '\0'; } else { pattern = &tstr[1]; } if (*tstr == 'M' || *tstr == 'm') { newStr = VarModify(str, VarMatch, (ClientData)pattern); } else { newStr = VarModify(str, VarNoMatch, (ClientData)pattern); } if (copy) { free(pattern); } break; } case 'S': { VarPattern pattern; register char delim; Buffer buf; /* Buffer for patterns */ register char *cp2; int lefts; pattern.flags = 0; delim = tstr[1]; tstr += 2; /* * If pattern begins with '^', it is anchored to the * start of the word -- skip over it and flag pattern. */ if (*tstr == '^') { pattern.flags |= VAR_MATCH_START; tstr += 1; } buf = Buf_Init(0); /* * Pass through the lhs looking for 1) escaped delimiters, * '$'s and backslashes (place the escaped character in * uninterpreted) and 2) unescaped $'s that aren't before * the delimiter (expand the variable substitution). * The result is left in the Buffer buf. */ for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { if ((*cp == '\\') && ((cp[1] == delim) || (cp[1] == '$') || (cp[1] == '\\'))) { Buf_AddByte(buf, (Byte)cp[1]); cp++; } else if (*cp == '$') { if (cp[1] != delim) { /* * If unescaped dollar sign not before the * delimiter, assume it's a variable * substitution and recurse. */ char *cp2; int len; Boolean freeIt; cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); if (freeIt) { free(cp2); } cp += len - 1; } else { /* * Unescaped $ at end of pattern => anchor * pattern at end. */ pattern.flags |= VAR_MATCH_END; } } else { Buf_AddByte(buf, (Byte)*cp); } } Buf_AddByte(buf, (Byte)'\0'); /* * If lhs didn't end with the delimiter, complain and * return NULL */ if (*cp != delim) { *lengthPtr = cp - start + 1; if (*freePtr) { free(str); } Buf_Destroy(buf, TRUE); Error("Unclosed substitution for %s (%c missing)", v->name, delim); return (var_Error); } /* * Fetch pattern and destroy buffer, but preserve the data * in it, since that's our lhs. Note that Buf_GetAll * will return the actual number of bytes, which includes * the null byte, so we have to decrement the length by * one. */ pattern.lhs = (char *)Buf_GetAll(buf, &pattern.leftLen); pattern.leftLen--; Buf_Destroy(buf, FALSE); /* * Now comes the replacement string. Three things need to * be done here: 1) need to compress escaped delimiters and * ampersands and 2) need to replace unescaped ampersands * with the l.h.s. (since this isn't regexp, we can do * it right here) and 3) expand any variable substitutions. */ buf = Buf_Init(0); tstr = cp + 1; for (cp = tstr; *cp != '\0' && *cp != delim; cp++) { if ((*cp == '\\') && ((cp[1] == delim) || (cp[1] == '&') || (cp[1] == '\\') || (cp[1] == '$'))) { Buf_AddByte(buf, (Byte)cp[1]); cp++; } else if ((*cp == '$') && (cp[1] != delim)) { char *cp2; int len; Boolean freeIt; cp2 = Var_Parse(cp, ctxt, err, &len, &freeIt); Buf_AddBytes(buf, strlen(cp2), (Byte *)cp2); cp += len - 1; if (freeIt) { free(cp2); } } else if (*cp == '&') { Buf_AddBytes(buf, pattern.leftLen, (Byte *)pattern.lhs); } else { Buf_AddByte(buf, (Byte)*cp); } } Buf_AddByte(buf, (Byte)'\0'); /* * If didn't end in delimiter character, complain */ if (*cp != delim) { *lengthPtr = cp - start + 1; if (*freePtr) { free(str); } Buf_Destroy(buf, TRUE); Error("Unclosed substitution for %s (%c missing)", v->name, delim); return (var_Error); } pattern.rhs = (char *)Buf_GetAll(buf, &pattern.rightLen); pattern.rightLen--; Buf_Destroy(buf, FALSE); /* * Check for global substitution. If 'g' after the final * delimiter, substitution is global and is marked that * way. */ cp++; if (*cp == 'g') { pattern.flags |= VAR_SUB_GLOBAL; cp++; } termc = *cp; newStr = VarModify(str, VarSubstitute, (ClientData)&pattern); /* * Free the two strings. */ free(pattern.lhs); free(pattern.rhs); break; } case 'T': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarTail, (ClientData)0); cp = tstr + 1; termc = *cp; break; } /*FALLTHRU*/ case 'H': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarHead, (ClientData)0); cp = tstr + 1; termc = *cp; break; } /*FALLTHRU*/ case 'E': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarSuffix, (ClientData)0); cp = tstr + 1; termc = *cp; break; } /*FALLTHRU*/ case 'R': if (tstr[1] == endc || tstr[1] == ':') { newStr = VarModify (str, VarRoot, (ClientData)0); cp = tstr + 1; termc = *cp; break; } /*FALLTHRU*/ default: { /* * This can either be a bogus modifier or a System-V * substitution command. */ VarPattern pattern; Boolean eqFound; pattern.flags = 0; eqFound = FALSE; /* * First we make a pass through the string trying * to verify it is a SYSV-make-style translation: * it must be: =) */ for (cp = tstr; *cp != '\0' && *cp != endc; cp++) { if (*cp == '=') { eqFound = TRUE; /* continue looking for endc */ } } if (*cp == endc && eqFound) { /* * Now we break this sucker into the lhs and * rhs. We must null terminate them of course. */ for (cp = tstr; *cp != '='; cp++) { ; } pattern.lhs = tstr; pattern.leftLen = cp - tstr; *cp++ = '\0'; pattern.rhs = cp; while (*cp != endc) { cp++; } pattern.rightLen = cp - pattern.rhs; *cp = '\0'; /* * SYSV modifications happen through the whole * string. Note the pattern is anchored at the end. */ pattern.flags |= VAR_SUB_GLOBAL|VAR_MATCH_END; newStr = VarModify(str, VarSubstitute, (ClientData)&pattern); /* * Restore the nulled characters */ pattern.lhs[pattern.leftLen] = '='; pattern.rhs[pattern.rightLen] = endc; termc = endc; } else { Error ("Unknown modifier '%c'\n", *tstr); for (cp = tstr+1; *cp != ':' && *cp != endc && *cp != '\0'; cp++) { ; } termc = *cp; newStr = var_Error; } } } if (DEBUG(VAR)) { printf("Result is \"%s\"\n", newStr); } if (*freePtr) { free (str); } str = newStr; if (str != var_Error) { *freePtr = TRUE; } else { *freePtr = FALSE; } if (termc == '\0') { Error("Unclosed variable specification for %s", v->name); } else if (termc == ':') { *cp++ = termc; } else { *cp = termc; } tstr = cp; } *lengthPtr = tstr - start + 1; } else { *lengthPtr = tstr - start + 1; *tstr = endc; } if (v->flags & VAR_FROM_ENV) { Boolean destroy = FALSE; if (str != (char *)Buf_GetAll(v->val, (int *)NULL)) { destroy = TRUE; } else { /* * Returning the value unmodified, so tell the caller to free * the thing. */ *freePtr = TRUE; } Buf_Destroy(v->val, destroy); free((Address)v); } else if (v->flags & VAR_JUNK) { /* * Perform any free'ing needed and set *freePtr to FALSE so the caller * doesn't try to free a static pointer. */ if (*freePtr) { free(str); } *freePtr = FALSE; free((Address)v); if (dynamic) { str = emalloc(*lengthPtr + 1); strncpy(str, start, *lengthPtr); str[*lengthPtr] = '\0'; *freePtr = TRUE; } else { str = var_Error; } } return (str); } /*- *----------------------------------------------------------------------- * Var_Subst -- * Substitute for all variables in the given string in the given context * If undefErr is TRUE, Parse_Error will be called when an undefined * variable is encountered. * * Results: * The resulting string. * * Side Effects: * None. The old string must be freed by the caller *----------------------------------------------------------------------- */ char * Var_Subst (str, ctxt, undefErr) register char *str; /* the string in which to substitute */ GNode *ctxt; /* the context wherein to find variables */ Boolean undefErr; /* TRUE if undefineds are an error */ { Buffer buf; /* Buffer for forming things */ char *val; /* Value to substitute for a variable */ int length; /* Length of the variable invocation */ Boolean doFree; /* Set true if val should be freed */ static Boolean errorReported; /* Set true if an error has already * been reported to prevent a plethora * of messages when recursing */ buf = Buf_Init (BSIZE); errorReported = FALSE; while (*str) { if ((*str == '$') && (str[1] == '$')) { /* * A dollar sign may be escaped either with another dollar sign. * In such a case, we skip over the escape character and store the * dollar sign into the buffer directly. */ str++; Buf_AddByte(buf, (Byte)*str); str++; } else if (*str != '$') { /* * Skip as many characters as possible -- either to the end of * the string or to the next dollar sign (variable invocation). */ char *cp; for (cp = str++; *str != '$' && *str != '\0'; str++) { ; } Buf_AddBytes(buf, str - cp, (Byte *)cp); } else { val = Var_Parse (str, ctxt, undefErr, &length, &doFree); /* * When we come down here, val should either point to the * value of this variable, suitably modified, or be NULL. * Length should be the total length of the potential * variable invocation (from $ to end character...) */ if (val == var_Error || val == varNoError) { /* * If performing old-time variable substitution, skip over * the variable and continue with the substitution. Otherwise, * store the dollar sign and advance str so we continue with * the string... */ if (oldVars) { str += length; } else if (undefErr) { /* * If variable is undefined, complain and skip the * variable. The complaint will stop us from doing anything * when the file is parsed. */ if (!errorReported) { Parse_Error (PARSE_FATAL, "Undefined variable \"%.*s\"",length,str); } str += length; errorReported = TRUE; } else { Buf_AddByte (buf, (Byte)*str); str += 1; } } else { /* * We've now got a variable structure to store in. But first, * advance the string pointer. */ str += length; /* * Copy all the characters from the variable value straight * into the new string. */ Buf_AddBytes (buf, strlen (val), (Byte *)val); if (doFree) { free ((Address)val); } } } } Buf_AddByte (buf, '\0'); str = (char *)Buf_GetAll (buf, (int *)NULL); Buf_Destroy (buf, FALSE); return (str); } /*- *----------------------------------------------------------------------- * Var_GetTail -- * Return the tail from each of a list of words. Used to set the * System V local variables. * * Results: * The resulting string. * * Side Effects: * None. * *----------------------------------------------------------------------- */ char * Var_GetTail(file) char *file; /* Filename to modify */ { return(VarModify(file, VarTail, (ClientData)0)); } /*- *----------------------------------------------------------------------- * Var_GetHead -- * Find the leading components of a (list of) filename(s). * XXX: VarHead does not replace foo by ., as (sun) System V make * does. * * Results: * The leading components. * * Side Effects: * None. * *----------------------------------------------------------------------- */ char * Var_GetHead(file) char *file; /* Filename to manipulate */ { return(VarModify(file, VarHead, (ClientData)0)); } /*- *----------------------------------------------------------------------- * Var_Init -- * Initialize the module * * Results: * None * * Side Effects: * The VAR_CMD and VAR_GLOBAL contexts are created *----------------------------------------------------------------------- */ void Var_Init () { VAR_GLOBAL = Targ_NewGN ("Global"); VAR_CMD = Targ_NewGN ("Command"); } /****************** PRINT DEBUGGING INFO *****************/ static VarPrintVar (v) Var *v; { printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, (int *)NULL)); return (0); } /*- *----------------------------------------------------------------------- * Var_Dump -- * print all variables in a context *----------------------------------------------------------------------- */ Var_Dump (ctxt) GNode *ctxt; { Lst_ForEach (ctxt->context, VarPrintVar); }