+/* $Header$ */
+
+/*
+ * Author: Peter J. Nicklin
+ */
+#include <stdio.h>
+#include "hash.h"
+#include "macro.h"
+#include "null.h"
+#include "path.h"
+#include "pdb.h"
+#include "pdset.h"
+#include "pdlist.h"
+#include "pdtyp.h"
+#include "pld.h"
+#include "slist.h"
+#include "slslist.h"
+#include "spms.h"
+#include "truefalse.h"
+#include "yesno.h"
+
+#define INCRPDIRS 50 /* amount to increase pdir ptr array */
+#define MAXPDIRS 100 /* initial size of proj dir ptr array */
+
+extern char *PGN; /* program name */
+extern PDTYP PDIRTYP; /* project directory type labels list */
+
+static int Ipdirs; /* attribute block array index */
+static int Maxpdirs = MAXPDIRS; /* maximum no. of project dirs */
+static int Ntypes; /* number of unique type labels */
+static PDSET **Pdarray; /* project dir attribute block array */
+static short *Maptyp; /* unique type label mapping array */
+static TYPES *Typstat; /* type label statistics */
+
+/*
+ * add_pdset() adds a project directory to the set of project directories
+ * which satisfy a boolean project directory type label expression. Only
+ * those type labels which satisfy the boolean expression are included with
+ * the project directory. To determine which type labels qualify, the
+ * postfix expression is scanned from right to left. Type labels within a
+ * negated expression are ignored. For example, in the postfix equivalent
+ * of expression "src & !(cmd | lib)" (that is: src cmd lib | ! &),
+ * even if both "src" and "cmd" are found, "src" is the only type label
+ * which could qualify.
+ */
+void
+add_pdset(postfix, ppathname, pathname, project)
+ register PDTYP *postfix; /* postfix expression struct */
+ char *ppathname; /* project directory project pathname */
+ char *pathname; /* project directory pathname */
+ char *project; /* project directory's project */
+{
+ register int i; /* postfix expression index */
+ register int opcount; /* count of expected operands */
+ char *pdtcpy(); /* copy project directory type label */
+ char *realloc(); /* reallocate memory block */
+ char type[TYPESIZE]; /* project dir type label buffer */
+ PDSET *savepdir(); /* save pdir attribute blk somewhere */
+ void savetype(); /* save type label */
+
+ if (Ipdirs >= Maxpdirs)
+ {
+ Maxpdirs += INCRPDIRS;
+ if ((Pdarray = (PDSET **) realloc((char *)Pdarray,
+ (unsigned)Maxpdirs*sizeof(PDSET *))) == NULL)
+ nomorecore();
+ }
+ Pdarray[Ipdirs] = savepdir(ppathname, pathname, project);
+
+ opcount = 0;
+ for (i = (postfix->pfxsize)-1; i >= 0;)
+ {
+ switch ((postfix->pfx)[i].p_class)
+ {
+ case B_ID:
+ if ((postfix->pfx)[i].p_sw == TRUE)
+ {
+ pdtcpy(type, (postfix->pfx)[i].p_label);
+ savetype(type, i);
+ }
+ break;
+ case B_OR:
+ case B_AND:
+ if ((postfix->pfx)[i].p_sw == FALSE)
+ opcount += 2;
+ break;
+ case B_NOT:
+ /* always skip !subexpr */
+ opcount += 1;
+ break;
+ }
+ /* skip false subexpression */
+ for (--i; opcount > 0; i--)
+ switch ((postfix->pfx)[i].p_class)
+ {
+ case B_ID:
+ opcount -= 1;
+ break;
+ case B_OR:
+ case B_AND:
+ opcount += 1;
+ break;
+ case B_NOT:
+ break;
+ }
+ }
+ Ipdirs++;
+}
+
+
+
+/*
+ * build_pdset() builds a set of project directories which satisfy a boolean
+ * project directory type label expression.
+ */
+build_pdset(ppathname, pathname)
+ char *ppathname; /* project root dir project pathname */
+ char *pathname; /* regular project root dir pathname */
+{
+ extern int ERRSTATUS; /* pexec error status */
+ extern SLIST *ENVLIST; /* project environment variable list */
+ char ppathbuf[PPATHSIZE]; /* project pathname buffer */
+ char *ppathcat(); /* project pathname concatenation */
+ char *pv; /* PROJECT environment variable */
+ char *slprepend(); /* prepend key */
+ char *slsprepend(); /* prepend key+string */
+ int closepdb(); /* close database */
+ int errpdb(); /* print database error message */
+ int pdtmatch(); /* match project dir type label expr */
+ int status = 0; /* return status */
+ PATH *pd; /* pathname struct pointer */
+ PATH *readpld(); /* read project link directory entry */
+ PDB *openpdb(); /* open database */
+ PDB *pldp; /* project link directory stream */
+ SLSBLK *pblk; /* project list block */
+ SLSLIST *plist; /* project list */
+ SLSLIST *slsinit(); /* initialize list */
+ void add_pdset(); /* add to set of project dirs */
+ void slsrm(); /* remove list item */
+
+ if ((pv = slprepend(pathname, ENVLIST)) == NULL)
+ pxexit();
+ plist = slsinit();
+
+ /* read PLDNAME project link directory */
+ if ((pldp = openpdb(PLDNAME, pathname, "r")) == NULL)
+ return(errpdb((PDB *) NULL));
+ while ((pd = readpld(pldp)) != NULL)
+ {
+ if (EQUAL(pd->p_alias, PARENTPROJECT))
+ continue;
+
+ if (EQUAL(pd->p_alias, CURPROJECT))
+ {
+ if (pdtmatch(&PDIRTYP, pd->p_type) == YES)
+ add_pdset(&PDIRTYP, ppathname, pd->p_path, pv);
+ }
+ else if (pd->p_mode == P_IFPROOT)
+ {
+ if (slsprepend(pd->p_alias, pd->p_path, plist) == NULL)
+ pxexit();
+ }
+ else if (pdtmatch(&PDIRTYP, pd->p_type) == YES)
+ {
+ ppathcat(ppathbuf, ppathname, pd->p_alias);
+ add_pdset(&PDIRTYP, ppathbuf, pd->p_path, pv);
+ }
+ }
+ if (closepdb(pldp) != 0)
+ status = ERRSTATUS;
+
+ /* build project directory type label tree for subprojects */
+ for (pblk = plist->head; pblk != NULL; pblk = pblk->next)
+ {
+ ppathcat(ppathbuf, ppathname, pblk->key);
+ status |= build_pdset(ppathbuf, pblk->string);
+ }
+ slsrm(CNULL, plist);
+
+ return(status);
+}
+
+
+
+/*
+ * check_pdset() detects conflicting type label priorities by
+ * checking that project directories are sorted into ascending
+ * order according to priority. An error message is printed and
+ * 1 returned on conflict, otherwise zero.
+ */
+check_pdset()
+{
+ register int iPd; /* proj dir block array index */
+ register int ityp; /* type label block index */
+ register int lastpr; /* previous type label priority */
+ register int prior; /* project dir type label priority */
+ register PDSET **ptrPd; /* Pdarray block array pointer */
+ int nsortyp; /* no. type label categories to sort */
+
+ nsortyp = 0;
+ for (ityp = 0; ityp < Ntypes; ityp++)
+ if (Typstat[ityp].t_sort)
+ nsortyp++;
+ if (nsortyp < 2)
+ return(0);
+
+ for (ityp = 0; ityp < Ntypes; ityp++)
+ if (Typstat[ityp].t_sort)
+ {
+ for (ptrPd=Pdarray, iPd=Ipdirs; iPd > 0; ptrPd++, iPd--)
+ if ((*ptrPd)->typblk[ityp].t_exist)
+ {
+ lastpr = (*ptrPd)->typblk[ityp].t_prior;
+ break;
+ }
+ for (ptrPd++, iPd--; iPd > 0; ptrPd++, iPd--)
+ {
+ if ((*ptrPd)->typblk[ityp].t_exist)
+ {
+ prior = (*ptrPd)->typblk[ityp].t_prior;
+ if (prior < lastpr)
+ goto conflict;
+ lastpr = prior;
+ }
+ }
+ }
+ return(0);
+conflict:
+ fprintf(stderr, "%s:", PGN);
+ for (ityp = Ntypes-1; ityp >= 0; ityp--)
+ if (Typstat[ityp].t_sort)
+ fprintf(stderr, (nsortyp-- > 1) ? " %s," :
+ " %s: conflicting type label priorities\n",
+ Typstat[ityp].t_name);
+ return(1);
+}
+
+
+
+/*
+ * debug_pdset() prints the sorted project directories together with
+ * the type labels that satisfy the boolean expression.
+ */
+void
+debug_pdset()
+{
+ int iPd; /* project dir block array index */
+ int ityp; /* type label statistics array index */
+
+ for (iPd = 0; iPd < Ipdirs; iPd++)
+ {
+ fprintf(stderr, "%s:", Pdarray[iPd]->ppath);
+ for (ityp = 0; ityp < Ntypes; ityp++)
+ if (Pdarray[iPd]->typblk[ityp].t_exist)
+ fprintf(stderr," %s.%d",Typstat[ityp].t_name,
+ Pdarray[iPd]->typblk[ityp].t_prior);
+ putc('\n', stderr);
+ }
+}
+
+
+
+/*
+ * exec_pdset() executes a set of project directories. Returns non-zero
+ * error status if error.
+ */
+exec_pdset()
+{
+ extern int ERRSTATUS; /* pexec error status */
+ extern int EXECUTE; /* execute command? */
+ extern int PRINT_HEADING; /* print headings for project dirs */
+ int ch_dir(); /* change current working directory */
+ int execcmd(); /* execute command in directory */
+ int iPd; /* project dir block array index */
+ int status = 0; /* return status */
+ void print_title(); /* print project directory title */
+
+ for (iPd = 0; iPd < Ipdirs; iPd++)
+ {
+ if (PRINT_HEADING == YES)
+ print_title(Pdarray[iPd]->ppath);
+ if (ch_dir(Pdarray[iPd]->rpath) == NO)
+ status = ERRSTATUS;
+ else if (EXECUTE == YES)
+ status |= execcmd(Pdarray[iPd]->project);
+ }
+ return(status);
+}
+
+
+
+/*
+ * init_pdset() allocates an array of pointers (Pdarray) to project
+ * directory attribute blocks, and calculates the maximum number of type
+ * labels to be stored with each project directory based on the number of
+ * unique type labels in the boolean postfix type expression. An array
+ * (Typstat) is also created to maintain statistics on each brand of
+ * label.
+ *
+ * Hash table lookup is used in forming unique type labels and a
+ * mapping array is used to map the labels from the boolean postfix
+ * type label expression to the unique representation.
+ */
+#define UNIQTYPHASHSIZE 41
+
+void
+init_pdset()
+{
+ register int i; /* postfix type expression index */
+ char *malloc(); /* memory allocator */
+ char *pfxcpy(); /* copy string prefix */
+ char type[TYPESIZE]; /* project dir type label buffer */
+ HASH *htinit(); /* initialize hash table */
+ HASH *uniqtyp; /* hash table of unique type labels */
+ HASHBLK *htb; /* hash table block pointer */
+ HASHBLK *htinstall(); /* install hash table entry */
+ HASHBLK *htlookup(); /* find hash table entry */
+ int nid; /* no. of ids in boolean expression */
+
+ /* project directory attribute block pointer array */
+ if ((Pdarray = (PDSET **) malloc((unsigned)Maxpdirs*sizeof(PDSET *))) == NULL)
+ nomorecore();
+
+ /* create postfix expression -> unique type label mapping array */
+ if ((Maptyp = (short *) malloc((unsigned)PDIRTYP.pfxsize*sizeof(short))) == NULL)
+ nomorecore();
+
+ /* create type label statistics array (estimate size first) */
+ nid = 0;
+ for (i = (PDIRTYP.pfxsize)-1; i >= 0; i--)
+ if ((PDIRTYP.pfx)[i].p_class == B_ID)
+ nid++;
+ if ((Typstat = (TYPES *) malloc((unsigned)nid*sizeof(TYPES))) == NULL)
+ nomorecore();
+
+ /* unique type label determination */
+ uniqtyp = htinit(UNIQTYPHASHSIZE);
+ for (i = (PDIRTYP.pfxsize)-1; i >= 0; i--)
+ {
+ if ((PDIRTYP.pfx)[i].p_class != B_ID)
+ continue;
+ pfxcpy(type, (PDIRTYP.pfx)[i].p_id);
+ if ((htb = htlookup(type, uniqtyp)) != NULL)
+ {
+ Maptyp[i] = htb->h_val;
+ }
+ else {
+ if ((htb = htinstall(type,"",Ntypes,uniqtyp)) == NULL)
+ nomorecore();
+ Maptyp[i] = Ntypes;
+ Typstat[Ntypes].t_name = htb->h_key;
+ Typstat[Ntypes].t_ntl = Typstat[Ntypes].t_sort = 0;
+ Ntypes++;
+ }
+ }
+}
+
+
+
+/*
+ * pdbcmp() compares the type label priorities and project pathnames
+ * for two project directories. Type label priorities override the
+ * lexicographical relationship of the project pathnames. Conflicting
+ * priorities are not detected. For example, a conflict occurs if the
+ * first directory has type labels print.1 and update.2, whereas the
+ * second directory has type labels print.2 and update.1. Returns
+ * an integer less than, equal to, or greater than zero, depending on
+ * the relative priorities of the type labels or lexicographical ordering
+ * of the project pathnames.
+ */
+pdbcmp(b1, b2)
+ PDSET **b1; /* project directory block pointer */
+ PDSET **b2; /* project directory block pointer */
+{
+ register TYPBLK *t1; /* type label block pointer */
+ register TYPBLK *t2; /* type label block pointer */
+ register TYPES *ty; /* type statistics array pointer */
+ register int comp; /* block comparison */
+ register int ityp; /* type label block index */
+ register int ntypes; /* number of unique type labels */
+ int strcmp(); /* string comparison */
+
+ comp = 0;
+ ntypes = Ntypes;
+ t1 = (*b1)->typblk;
+ t2 = (*b2)->typblk;
+ ty = Typstat;
+
+ for (ityp = 0; ityp < ntypes; ityp++)
+ {
+ if (ty->t_sort && t1->t_exist && t2->t_exist)
+ if ((comp = t1->t_prior - t2->t_prior) != 0)
+ return(comp);
+ t1++, t2++, ty++;
+ }
+ return(strcmp((*b1)->ppath, (*b2)->ppath));
+}
+
+
+
+/*
+ * savepdir() saves a block of project directory attributes somewhere
+ * and returns a pointer to the somewhere, or dies if out of memory.
+ */
+PDSET *
+savepdir(ppathname, pathname, project)
+ char *ppathname; /* project directory project pathname */
+ char *pathname; /* project directory regular pathname */
+ char *project; /* project directory's project */
+{
+ char *calloc(); /* initialize memory to zero */
+ char *malloc(); /* memory allocator */
+ char *strcpy(); /* string copy */
+ int strlen(); /* string length */
+ PDSET *pdbptr; /* pointer to proj directory block */
+
+ if ((pdbptr = (PDSET *) malloc(sizeof(PDSET))) == NULL ||
+ (pdbptr->ppath = malloc((unsigned)(strlen(ppathname)+1))) == NULL ||
+ (pdbptr->rpath = malloc((unsigned)(strlen(pathname)+1))) == NULL ||
+ (pdbptr->typblk = (TYPBLK *) calloc((unsigned)Ntypes,sizeof(TYPBLK))) == NULL)
+ nomorecore();
+ strcpy(pdbptr->rpath, pathname);
+ strcpy(pdbptr->ppath, ppathname);
+ pdbptr->project = project;
+ return(pdbptr);
+}
+
+
+
+/*
+ * savetype() records the priorities of the type labels attached to each
+ * directory, and also the total number of each type label.
+ */
+void
+savetype(type, idx)
+ char *type; /* project dir type label */
+ int idx; /* boolean type expression id index */
+{
+ register char *ptyptr; /* pointer to type label priority */
+ register int priority; /* type label priority */
+ register int uniqid; /* unique type label number */
+ char *index(); /* first occurrence of character */
+ int atoi(); /* string to decimal integer */
+
+ uniqid = Maptyp[idx];
+ ptyptr = index(type, '.');
+ priority = (ptyptr == NULL) ? 0 : atoi(++ptyptr);
+ if (Typstat[uniqid].t_ntl == 0)
+ {
+ Typstat[uniqid].t_itlp = priority;
+ }
+ else {
+ if (priority != Typstat[uniqid].t_itlp)
+ Typstat[uniqid].t_sort = 1;
+ }
+ Typstat[uniqid].t_ntl++;
+
+ Pdarray[Ipdirs]->typblk[uniqid].t_exist++;
+ Pdarray[Ipdirs]->typblk[uniqid].t_prior = priority;
+}
+
+
+
+/*
+ * sort_pdset() sorts the set of project directories alpahabetically
+ * and by type label priorities.
+ */
+void
+sort_pdset()
+{
+ int pdbcmp(); /* compare project dir blocks */
+
+ qsort((char *)Pdarray, Ipdirs, sizeof(PDSET *), pdbcmp);
+}