Home | History | Annotate | Download | only in protocmp
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 
     30 #include <stdio.h>
     31 #include <sys/param.h>
     32 #include <fcntl.h>
     33 #include <stdlib.h>
     34 #include <strings.h>
     35 #include <errno.h>
     36 #include <dirent.h>
     37 #include <sys/stat.h>
     38 
     39 #include "list.h"
     40 #include "protodir.h"
     41 #include "arch.h"
     42 #include "exception_list.h"
     43 
     44 #define	FS	" \t\n"
     45 
     46 static char *
     47 resolve_relative(const char *source, const char *link)
     48 {
     49 	char	*p;
     50 	char	*l_next;
     51 	char	*l_pos;
     52 	static char	curpath[MAXPATHLEN];
     53 
     54 	/* if absolute path - no relocation required */
     55 	if (link[0] == '/')
     56 		return (strcpy(curpath, link));
     57 
     58 	(void) strcpy(curpath, source);
     59 	p = rindex(curpath, '/');
     60 	*p = '\0';
     61 	l_pos = (char *)link;
     62 	do {
     63 		l_next = index(l_pos, '/');
     64 		if (strncmp(l_pos, "../", 3) == 0) {
     65 			if ((p = rindex(curpath, '/')) != NULL)
     66 				*p = '\0';
     67 			else
     68 				curpath[0] = '\0';
     69 		} else if (strncmp(l_pos, "./", 2)) {
     70 			/* if not . then we process */
     71 			if (curpath[0])
     72 				(void) strcat(curpath, "/");
     73 			if (l_next) {
     74 				(void) strncat(curpath, l_pos,
     75 				    (l_next - l_pos));
     76 			} else
     77 				(void) strcat(curpath, l_pos);
     78 		}
     79 		l_pos = l_next + 1;
     80 	} while (l_next);
     81 
     82 	return (curpath);
     83 }
     84 
     85 
     86 static int
     87 parse_proto_line(const char *basedir, char *line, elem_list *list, short arch,
     88     const char *pkgname)
     89 {
     90 	char	*type, *class, *file, *src, *maj, *min, *perm, *owner, *group;
     91 	char	p_line[BUFSIZ];
     92 	elem	*dup;
     93 	static elem *e = NULL;
     94 
     95 	(void) strcpy(p_line, line);
     96 	if (!e)
     97 		e = (elem *)calloc(1, sizeof (elem));
     98 
     99 	e->flag = 0;
    100 
    101 	if (!(type  = strtok(p_line, FS))) {
    102 		(void) fprintf(stderr, "error: bad line(type) : %s\n", line);
    103 		return (-1);
    104 	}
    105 
    106 	e->file_type = type[0];
    107 
    108 	if ((class = strtok(NULL, FS)) == NULL) {
    109 		(void) fprintf(stderr, "error: bad line(class) : %s\n", line);
    110 		return (-1);
    111 	}
    112 
    113 	/*
    114 	 * Just ignore 'legacy' entries.  These are not present in the proto
    115 	 * area at all.  They're phantoms.
    116 	 */
    117 	if (strcmp(class, "legacy") == 0)
    118 		return (0);
    119 
    120 	if (!(file  = strtok(NULL, FS))) {
    121 		(void) fprintf(stderr, "error: bad line(file_name) : %s\n",
    122 		    line);
    123 		return (-1);
    124 	}
    125 
    126 	e->symsrc = NULL;
    127 	if ((src = index(file, '=')) != NULL) {
    128 		/*
    129 		 * The '=' operator is subtly different for link and non-link
    130 		 * entries.  For the hard or soft link case, the left hand side
    131 		 * exists in the proto area and is created by the package.
    132 		 *
    133 		 * When the file is an editable file, it's very likely that the
    134 		 * right hand side is only a fragment of that file, which is
    135 		 * delivered by multiple packages in the consolidation.  Thus it
    136 		 * can't exist in the proto area, and because we can't really
    137 		 * know where the file's root directory is, we should skip the
    138 		 * file.
    139 		 *
    140 		 * For all other filetypes, assume the right hand side is in the
    141 		 * proto area.
    142 		 */
    143 		if (e->file_type == SYM_LINK_T || e->file_type == LINK_T) {
    144 			*src++ = '\0';
    145 			e->symsrc = strdup(src);
    146 		} else if (e->file_type == EDIT_T) {
    147 			return (0);
    148 		} else {
    149 			file = src + 1;
    150 		}
    151 	}
    152 
    153 	/*
    154 	 * if a basedir has a value, prepend it to the filename
    155 	 */
    156 	if (basedir[0])
    157 		(void) strcat(strcat(strcpy(e->name, basedir), "/"), file);
    158 	else
    159 		(void) strcpy(e->name, file);
    160 
    161 	if (e->file_type != SYM_LINK_T) {
    162 		if ((e->file_type == CHAR_DEV_T) ||
    163 		    (e->file_type == BLOCK_DEV_T)) {
    164 			if (!(maj = strtok(NULL, FS))) {
    165 				(void) fprintf(stderr,
    166 				    "error: bad line(major number) : %s\n",
    167 				    line);
    168 				return (-1);
    169 			}
    170 			e->major = atoi(maj);
    171 
    172 			if (!(min = strtok(NULL, FS))) {
    173 				(void) fprintf(stderr,
    174 				    "error: bad line(minor number) : %s\n",
    175 				    line);
    176 				return (-1);
    177 			}
    178 			e->minor = atoi(min);
    179 		} else {
    180 			e->major = -1;
    181 			e->minor = -1;
    182 		}
    183 
    184 		if (!(perm = strtok(NULL, FS))) {
    185 			(void) fprintf(stderr,
    186 			    "error: bad line(permission) : %s\n", line);
    187 			return (-1);
    188 		}
    189 		e->perm = strtol(perm, NULL, 8);
    190 
    191 		if (!(owner = strtok(NULL, FS))) {
    192 			(void) fprintf(stderr,
    193 			    "error: bad line(owner) : %s\n", line);
    194 			return (-1);
    195 		}
    196 		(void) strcpy(e->owner, owner);
    197 
    198 		if (!(group = strtok(NULL, FS))) {
    199 			(void) fprintf(stderr,
    200 			    "error: bad line(group) : %s\n", line);
    201 			return (-1);
    202 		}
    203 		(void) strcpy(e->group, group);
    204 	}
    205 
    206 	e->inode = 0;
    207 	e->ref_cnt = 1;
    208 	e->arch = arch;
    209 	e->link_parent = NULL;
    210 
    211 	if (!(dup = find_elem(list, e, FOLLOW_LINK))) {
    212 		e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */
    213 		add_elem(list, e);
    214 		e = NULL;
    215 		return (1);
    216 	} else if (dup->file_type == DIR_T) {
    217 		if (!(dup->pkgs = add_pkg(dup->pkgs, pkgname))) {
    218 			/* add entry to pkgs */
    219 			(void) fprintf(stderr,
    220 			    "warning: %s: Duplicate entry for %s\n",
    221 			    pkgname, dup->name);
    222 			return (-1);
    223 		}
    224 		if (e->perm != dup->perm) {
    225 			(void) fprintf(stderr,
    226 			    "warning: %s: permissions %#o of %s do not match "
    227 			    "previous permissions %#o\n",
    228 			    pkgname, e->perm, dup->name, dup->perm);
    229 		}
    230 		if (strcmp(e->owner, dup->owner) != 0) {
    231 			(void) fprintf(stderr,
    232 			    "warning: %s: owner \"%s\" of %s does not match "
    233 			    "previous owner \"%s\"\n",
    234 			    pkgname, e->owner, dup->name, dup->owner);
    235 		}
    236 		if (strcmp(e->group, dup->group) != 0) {
    237 			(void) fprintf(stderr,
    238 			    "warning: %s: group \"%s\" of %s does not match "
    239 			    "previous group \"%s\"\n",
    240 			    pkgname, e->group, dup->name, dup->group);
    241 		}
    242 	} else {
    243 		/*
    244 		 * Signal an error only if this is something that's not on the
    245 		 * exception list.
    246 		 */
    247 		(void) strcpy(e->name, file);
    248 		if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) {
    249 			(void) fprintf(stderr,
    250 			    "warning: %s: duplicate entry for %s - ignored\n",
    251 			    pkgname, e->name);
    252 			return (-1);
    253 		}
    254 	}
    255 
    256 	return (0);
    257 }
    258 
    259 static int
    260 parse_proto_link(const char *basedir, char *line, elem_list *list, short arch,
    261     const char *pkgname)
    262 {
    263 	char	*type, *file, *src;
    264 	char	p_line[BUFSIZ];
    265 	elem	*p, *dup;
    266 	elem	key;
    267 	static elem	*e = NULL;
    268 
    269 
    270 	(void) strcpy(p_line, line);
    271 	if (!e)
    272 		e = (elem *)calloc(1, sizeof (elem));
    273 
    274 	e->flag = 0;
    275 	type = strtok(p_line, FS);
    276 	e->arch = arch;
    277 
    278 	e->file_type = type[0];
    279 	(void) strtok(NULL, FS);   /* burn class */
    280 
    281 	file = strtok(NULL, FS);
    282 	if ((src = index(file, '=')) != NULL) {
    283 		*src++ = '\0';
    284 		e->symsrc = strdup(src);
    285 	} else {
    286 		(void) fprintf(stderr,
    287 		    "error: %s: hard link does not have destination (%s)\n",
    288 		    pkgname, file);
    289 		return (0);
    290 	}
    291 
    292 	/*
    293 	 * if a basedir has a value, prepend it to the filename
    294 	 */
    295 	if (basedir[0])
    296 		(void) strcat(strcat(strcpy(e->name, basedir), "/"), file);
    297 	else
    298 		(void) strcpy(e->name, file);
    299 
    300 	/*
    301 	 * now we need to find the file that we link to - to do this
    302 	 * we build a key.
    303 	 */
    304 
    305 	src = resolve_relative(e->name, e->symsrc);
    306 	(void) strcpy(key.name, src);
    307 	key.arch = e->arch;
    308 	if ((p = find_elem(list, &key, NO_FOLLOW_LINK)) == NULL) {
    309 		(void) fprintf(stderr,
    310 		    "error: %s: hardlink to non-existent file: %s=%s\n",
    311 		    pkgname, e->name, e->symsrc);
    312 		return (0);
    313 	}
    314 	if ((p->file_type == SYM_LINK_T) || (p->file_type == LINK_T)) {
    315 		(void) fprintf(stderr,
    316 		    "error: %s: hardlink must link to a file or directory "
    317 		    "(not other links): %s=%s\n", pkgname, e->name, p->name);
    318 		return (0);
    319 	}
    320 	e->link_parent = p;
    321 	e->link_sib = p->link_sib;
    322 	p->link_sib = e;
    323 	p->ref_cnt++;
    324 	e->inode = p->inode;
    325 	e->perm = p->perm;
    326 	e->ref_cnt = p->ref_cnt;
    327 	e->major = p->major;
    328 	e->minor = p->minor;
    329 	(void) strcpy(e->owner, p->owner);
    330 	(void) strcpy(e->group, p->owner);
    331 
    332 	if (!(dup = find_elem(list, e, NO_FOLLOW_LINK))) {
    333 		e->pkgs = add_pkg(NULL, pkgname); /* init pkgs list */
    334 		e->link_sib = NULL;
    335 		add_elem(list, e);
    336 		e = NULL;
    337 		return (1);
    338 	} else {
    339 		/*
    340 		 * Signal an error only if this is something that's not on the
    341 		 * exception list.
    342 		 */
    343 		(void) strcpy(e->name, file);
    344 		if (find_elem(&exception_list, e, FOLLOW_LINK) == NULL) {
    345 			(void) fprintf(stderr,
    346 			    "warning: %s: duplicate entry for %s - ignored\n",
    347 			    pkgname, e->name);
    348 			return (-1);
    349 		}
    350 	}
    351 
    352 	return (0);
    353 }
    354 
    355 
    356 /*
    357  * open up the pkginfo file and find the ARCH= and the BASEDIR= macros.
    358  * I will set the arch and basedir variables based on these fields.
    359  */
    360 static void
    361 read_pkginfo(const char *protodir, short *arch, char *basedir)
    362 {
    363 	char	pkginfofile[MAXPATHLEN];
    364 	char	architecture[MAXPATHLEN];
    365 	char	buf[BUFSIZ];
    366 	FILE	*pkginfo_fp;
    367 	int	hits = 0;
    368 	int	i;
    369 	int	index;
    370 
    371 
    372 	architecture[0] = '\0';
    373 	basedir[0] = '\0';
    374 	*arch = P_ISA;
    375 
    376 	/*
    377 	 * determine whether the pkginfo file is a pkginfo.tmpl or
    378 	 * a pkginfo file
    379 	 */
    380 	(void) strcat(strcat(strcpy(pkginfofile, protodir), "/"),
    381 	    "pkginfo.tmpl");
    382 
    383 	if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) {
    384 		(void) strcat(strcat(strcpy(pkginfofile, protodir), "/"),
    385 		    "pkginfo");
    386 		if ((pkginfo_fp = fopen(pkginfofile, "r")) == NULL) {
    387 			perror(pkginfofile);
    388 			return;
    389 		}
    390 	}
    391 
    392 
    393 	while (fgets(buf, BUFSIZ, pkginfo_fp) && (hits != 3)) {
    394 		if (strncmp(buf, "ARCH=", 5) == 0) {
    395 			index = 0;
    396 			/*
    397 			 * remove any '"' in the ARCH field.
    398 			 */
    399 			for (i = 5; buf[i]; i++) {
    400 				if (buf[i] != '"')
    401 					architecture[index++] = buf[i];
    402 			}
    403 			/* -1 because above copy included '\n' */
    404 			architecture[index-1] = '\0';
    405 			hits += 1;
    406 		} else if (strncmp(buf, "BASEDIR=", 8) == 0) {
    407 			index = 0;
    408 			/*
    409 			 * remove any '"' in the BASEDIR field, and
    410 			 * strip off a leading '/' if present.
    411 			 */
    412 			for (i = 8; buf[i]; i++) {
    413 				if (buf[i] != '"' &&
    414 				    (buf[i] != '/' || index != 0)) {
    415 					buf[index++] = buf[i];
    416 				}
    417 			}
    418 			/* -1 because above copy included '\n' */
    419 			buf[index-1] = '\0';
    420 			(void) strcpy(basedir, &buf[0]);
    421 			hits += 2;
    422 		}
    423 	}
    424 	(void) fclose(pkginfo_fp);
    425 
    426 	if (architecture[0])
    427 		if ((*arch = assign_arch(architecture)) == NULL) {
    428 			(void) fprintf(stderr,
    429 			    "warning: Unknown architecture %s found in %s\n",
    430 			    architecture, pkginfofile);
    431 		}
    432 }
    433 
    434 /*
    435  * The first pass through the prototype file goes through and reads
    436  * in all the entries except 'hard links'.  Those must be processed
    437  * in a second pass.
    438  *
    439  * If any !includes are found in the prototype file this routine
    440  * will be called recursively.
    441  *
    442  * Args:
    443  *   protofile - full pathname to prototype file to be processed.
    444  *   protodir  - directory in which prototype file resides.
    445  *   list      - list to which elements will be added
    446  *   arch      - architecture of current prototype
    447  *   basedir   - basedir for package
    448  *   pkgname   - name of package
    449  *
    450  * Returns:
    451  *   returns number of items added to list.
    452  *
    453  */
    454 static int
    455 first_pass_prototype(const char *protofile, const char *protodir,
    456     elem_list *list, short arch, const char *basedir, const char *pkgname)
    457 {
    458 	int	elem_count = 0;
    459 	FILE	*proto_fp;
    460 	char	include_file[MAXPATHLEN];
    461 	char	buf[BUFSIZ];
    462 
    463 	if ((proto_fp = fopen(protofile, "r")) == NULL) {
    464 		perror(protofile);
    465 		return (0);
    466 	}
    467 
    468 	/*
    469 	 * first pass through file - process everything but
    470 	 * hard links.
    471 	 */
    472 	while (fgets(buf, BUFSIZ, proto_fp)) {
    473 		int	rc;
    474 
    475 		switch (buf[0]) {
    476 		case FILE_T:
    477 		case EDIT_T:
    478 		case VOLATILE_T:
    479 		case DIR_T:
    480 		case SYM_LINK_T:
    481 		case CHAR_DEV_T:
    482 		case BLOCK_DEV_T:
    483 			if ((rc = parse_proto_line(basedir, buf, list, arch,
    484 			    pkgname)) >= 0) {
    485 				elem_count += rc;
    486 			} else {
    487 				(void) fprintf(stderr,
    488 				    "error: Errors found in %s\n", protofile);
    489 			}
    490 			break;
    491 		case LINK_T:
    492 		case 'i':
    493 		case '#':
    494 		case '\n':
    495 			break;
    496 		case '!':
    497 			/* Is this an include statement - if so process */
    498 			if (strncmp(buf, "!include", 8) == 0) {
    499 				char *inc_file = (char *)(buf + 9);
    500 
    501 				/* burn white space */
    502 				while ((*inc_file == ' ') ||
    503 				    (*inc_file == '\t'))
    504 					inc_file++;
    505 				if (*inc_file) {
    506 					/* remove trailing \n */
    507 					inc_file[strlen(inc_file) - 1] = '\0';
    508 					(void) strcat(strcat(strcpy(
    509 					    include_file, protodir), "/"),
    510 					    inc_file);
    511 					elem_count +=
    512 					    first_pass_prototype(include_file,
    513 					    protodir, list, arch, basedir,
    514 					    pkgname);
    515 				} else {
    516 					(void) fprintf(stderr,
    517 					    "warning: bad !include statement "
    518 					    "in prototype %s : %s\n",
    519 					    protofile, buf);
    520 				}
    521 			} else {
    522 				(void) fprintf(stderr,
    523 				    "warning: unexpected ! notation in "
    524 				    "prototype %s : %s\n", protofile, buf);
    525 
    526 			}
    527 			break;
    528 		default:
    529 			(void) fprintf(stderr,
    530 			    "warning: unexpected line in prototype %s : %s\n",
    531 			    protofile, buf);
    532 			break;
    533 		}
    534 	}
    535 
    536 	(void) fclose(proto_fp);
    537 
    538 	return (elem_count);
    539 }
    540 
    541 /*
    542  * The second pass through the prototype file goes through and reads
    543  * and processes only the 'hard links' in the prototype file.  These
    544  * are resolved and added accordingly to the elements list(list).
    545  *
    546  * If any !includes are found in the prototype file this routine
    547  * will be called recursively.
    548  *
    549  * Args:
    550  *   protofile - full pathname to prototype file to be processed.
    551  *   protodir  - directory in which prototype file resides.
    552  *   list      - list to which elements will be added
    553  *   arch      - architecture of current prototype
    554  *   basedir   - basedir for package
    555  *   pkgname   - package name
    556  *
    557  * Returns:
    558  *   returns number of items added to list.
    559  *
    560  */
    561 static int
    562 second_pass_prototype(const char *protofile, const char *protodir,
    563     elem_list *list, short arch, const char *basedir, const char *pkgname)
    564 {
    565 	FILE	*proto_fp;
    566 	int	elem_count = 0;
    567 	char	include_file[MAXPATHLEN];
    568 	char	buf[BUFSIZ];
    569 
    570 	if ((proto_fp = fopen(protofile, "r")) == NULL) {
    571 		perror(protofile);
    572 		return (0);
    573 	}
    574 
    575 	/*
    576 	 * second pass through prototype file - process the hard links
    577 	 * now.
    578 	 */
    579 	while (fgets(buf, BUFSIZ, proto_fp))
    580 		if (buf[0] == LINK_T) {
    581 			int	rc;
    582 
    583 			if ((rc = parse_proto_link(basedir, buf, list, arch,
    584 			    pkgname)) >= 0) {
    585 				elem_count += rc;
    586 			} else {
    587 				(void) fprintf(stderr,
    588 				    "error: Errors found in %s\n", protofile);
    589 			}
    590 		} else if (strncmp(buf, "!include", 8) == 0) {
    591 			/*
    592 			 * This is a file to include
    593 			 */
    594 			char *inc_file = (char *)(buf + 9);
    595 
    596 			/* burn white space */
    597 			while ((*inc_file == ' ') || (*inc_file == '\t'))
    598 				inc_file++;
    599 
    600 			if (*inc_file) {
    601 				/* remove trailing \n */
    602 				inc_file[strlen(inc_file) - 1] = '\0';
    603 				/* build up include file name to be opened. */
    604 				(void) strcat(strcat(strcpy(include_file,
    605 				    protodir), "/"), inc_file);
    606 				/*
    607 				 * recursively call this routine to process the
    608 				 * !include file.
    609 				 */
    610 				elem_count +=
    611 				    second_pass_prototype(include_file,
    612 				    protodir, list, arch, basedir, pkgname);
    613 			} else {
    614 				(void) fprintf(stderr,
    615 				    "warning: Bad !include statement in "
    616 				    "prototype %s : %s\n", protofile, buf);
    617 			}
    618 		}
    619 
    620 		(void) fclose(proto_fp);
    621 
    622 		return (elem_count);
    623 }
    624 
    625 /*
    626  * Args:
    627  *    pkgname  - name of package being processed
    628  *    protodir - pathname to package defs directory
    629  *    list     - List of elements read in, elements are added to this
    630  *		 as they are read in.
    631  *    verbose  - verbose output
    632  *
    633  * Returns:
    634  *    number of elements added to list
    635  */
    636 int
    637 process_package_dir(const char *pkgname, const char *protodir,
    638     elem_list *list, int verbose)
    639 {
    640 	struct stat	st_buf;
    641 	char		protofile[MAXPATHLEN];
    642 	char		basedir[MAXPATHLEN];
    643 	short		arch;
    644 	int		count = 0;
    645 
    646 
    647 	/*
    648 	 * skip any packages we've already handled (because of
    649 	 * dependencies)
    650 	 */
    651 	if (processed_package(pkgname)) {
    652 		return (0);
    653 	}
    654 
    655 	/*
    656 	 * find the prototype file.  Legal forms of the name are:
    657 	 *		prototype
    658 	 *		prototype_<mach> (where mach == (sparc || i386 || ppc)
    659 	 */
    660 	(void) strcat(strcat(strcpy(protofile, protodir), "/"), "prototype");
    661 	if (stat(protofile, &st_buf) < 0) {
    662 		if (errno == ENOENT) {
    663 			(void) strcat(strcat(strcat(strcpy(protofile,
    664 			    protodir), "/"), "prototype"), PROTO_EXT);
    665 			if (stat(protofile, &st_buf) < 0) {
    666 				if (errno == ENOENT) {
    667 					if (verbose) {
    668 						(void) fprintf(stderr,
    669 						    "warning: no prototype "
    670 						    "file found in %s, "
    671 						    "skipping...\n",
    672 						    protodir);
    673 					}
    674 				} else
    675 					perror(protofile);
    676 				return (0);
    677 			}
    678 		} else {
    679 			perror(protofile);
    680 			return (0);
    681 		}
    682 	}
    683 
    684 	mark_processed(pkgname);
    685 
    686 	read_pkginfo(protodir, &arch, basedir);
    687 
    688 	count += first_pass_prototype(protofile, protodir, list, arch,
    689 	    basedir, pkgname);
    690 	count += second_pass_prototype(protofile, protodir, list, arch,
    691 	    basedir, pkgname);
    692 
    693 	/* print_list(list); */
    694 	return (count);
    695 }
    696 
    697 int
    698 read_in_protodir(const char *dir_name, elem_list *list, int verbose)
    699 {
    700 	DIR		*p_dir;
    701 	struct dirent	*dp;
    702 	char		protodir[MAXPATHLEN];
    703 	struct stat	st_buf;
    704 	int		count = 0;
    705 
    706 	if ((p_dir = opendir(dir_name)) == NULL) {
    707 		perror(dir_name);
    708 		exit(1);
    709 	}
    710 
    711 	list->type = PROTODIR_LIST;
    712 
    713 	while ((dp = readdir(p_dir)) != NULL) {
    714 		/*
    715 		 * let's not check "." and ".." - I don't really like this
    716 		 * but I wasn't really sure you could be sure that they
    717 		 * are always the first two entries in the directory
    718 		 * structure  - so I put it in the loop.
    719 		 *
    720 		 * Also - we skip all directories that are names .del-*.
    721 		 * and any SCCS directories too.
    722 		 */
    723 		if ((strcmp(dp->d_name, ".") == 0) ||
    724 		    (strcmp(dp->d_name, "..") == 0) ||
    725 		    (strncmp(dp->d_name, ".del-", 5) == 0) ||
    726 		    (strcmp(dp->d_name, "SCCS") == 0))
    727 			continue;
    728 
    729 		(void) strcat(strcat(strcpy(protodir, dir_name), "/"),
    730 		    dp->d_name);
    731 		if (stat(protodir, &st_buf) < 0) {
    732 			perror(protodir);
    733 			continue;
    734 		}
    735 		if (!S_ISDIR(st_buf.st_mode)) {
    736 			if (verbose) {
    737 				(void) fprintf(stderr,
    738 				    "warning: %s not a directory\n", protodir);
    739 			}
    740 			continue;
    741 		}
    742 
    743 		count += process_dependencies(dp->d_name, dir_name, list,
    744 		    verbose);
    745 
    746 		count += process_package_dir(dp->d_name, protodir, list,
    747 		    verbose);
    748 	}
    749 
    750 	if (verbose)
    751 		(void) printf("read in %d lines\n", count);
    752 
    753 	(void) closedir(p_dir);
    754 
    755 	return (count);
    756 }
    757