#include <stdio.h>
#include <ctype.h>
#include <dirent.h>
#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pkgstrct.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <pkglib.h>
#include "libadm.h"
#include "libinst.h"
extern int holdcinfo;
#define WRN_SCARYLINK "WARNING: <%s>, target of symlink <%s>, does not exist."
#define ERR_PATHLONG "path argument too long"
#define ERR_CLASSLONG "classname argument too long"
#define ERR_CLASSCHAR "bad character in classname"
#define ERR_STAT "unable to stat <%s>"
#define ERR_WRITE "write of entry failed"
#define ERR_POPEN "unable to create pipe to <%s>"
#define ERR_PCLOSE "unable to close pipe to <%s>"
#define ERR_RDLINK "unable to read link for <%s>"
#define ERR_MEMORY "memory allocation failure, errno=%d"
#define LINK 1
struct link {
char *path;
ino_t ino;
dev_t dev;
struct link *next;
};
static struct link *firstlink = (struct link *)0;
static struct link *lastlink = (struct link *)0;
static char *scan_raw_ln(char *targ_name, char *link_name);
static char *def_class = "none";
static int errflg = 0;
static int iflag = 0;
static int xflag = 0;
static int nflag = 0;
static char construction[PATH_MAX], mylocal[PATH_MAX];
static void findlink(struct cfent *ept, char *path, char *svpath);
static void follow(char *path);
static void output(char *path, int n, char *local);
static void usage(void);
int
main(int argc, char *argv[])
{
int c;
char *pt, path[PATH_MAX];
char *abi_sym_ptr;
extern char *optarg;
extern int optind;
(void) setlocale(LC_ALL, "");
#if !defined(TEXT_DOMAIN)
#define TEXT_DOMAIN "SYS_TEST"
#endif
(void) textdomain(TEXT_DOMAIN);
(void) set_prog_name(argv[0]);
while ((c = getopt(argc, argv, "xnic:?")) != EOF) {
switch (c) {
case 'x':
xflag++;
break;
case 'n':
nflag++;
break;
case 'c':
def_class = optarg;
if (strlen(def_class) > (size_t)CLSSIZ) {
progerr(gettext(ERR_CLASSLONG));
exit(1);
}
for (pt = def_class; *pt; pt++) {
if (!isalpha(*pt) && !isdigit(*pt)) {
progerr(gettext(ERR_CLASSCHAR));
exit(1);
}
}
break;
case 'i':
iflag++;
break;
default:
usage();
}
}
if (iflag) {
set_nonABI_symlinks();
} else {
abi_sym_ptr = getenv("PKG_NONABI_SYMLINKS");
if (abi_sym_ptr && strncasecmp(abi_sym_ptr, "TRUE", 4) == 0) {
set_nonABI_symlinks();
}
}
holdcinfo = !xflag;
if (optind == argc) {
while (fgets(path, sizeof (path), stdin) != (char *)NULL) {
output(path, 0, NULL);
}
} else {
while (optind < argc) {
follow(argv[optind++]);
}
}
return (errflg ? 1 : 0);
}
static void
output(char *path, int n, char *local)
{
char mypath[PATH_MAX];
int len;
int s;
struct cfent entry;
len = strlen(path);
while ((len > 0) && (path[len-1] == '\n')) {
path[--len] = '\0';
}
entry.volno = 0;
entry.ftype = '?';
entry.path = mypath;
(void) strlcpy(entry.pkg_class, def_class, sizeof (entry.pkg_class));
(void) strlcpy(entry.path, path, PATH_MAX);
entry.ainfo.local = NULL;
entry.ainfo.mode = BADMODE;
(void) strlcpy(entry.ainfo.owner, BADOWNER, sizeof (entry.ainfo.owner));
(void) strlcpy(entry.ainfo.group, BADGROUP, sizeof (entry.ainfo.group));
errflg = 0;
if (xflag) {
entry.ftype = '?';
if (cverify(0, &entry.ftype, path, &entry.cinfo, 1)) {
errflg++;
logerr(gettext("ERROR: %s"), path);
logerr(getErrbufAddr());
return;
}
}
if ((s = averify(0, &entry.ftype, path, &entry.ainfo)) == VE_EXIST &&
!iflag) {
entry.ftype = 's';
if ((s = readlink(path, mylocal, PATH_MAX)) > 0) {
mylocal[s] = '\000';
entry.ainfo.local = mylocal;
if (averify(0, &entry.ftype, path, &entry.ainfo)) {
errflg++;
} else
ptext(stderr, gettext(WRN_SCARYLINK),
mylocal, path);
} else {
errflg++;
}
} else if (s != 0 && s != VE_CONT)
errflg++;
if (errflg) {
logerr(gettext("ERROR: %s"), path);
logerr(getErrbufAddr());
return;
}
if (n) {
if (strchr("fev", entry.ftype)) {
entry.ainfo.local = mylocal;
(void) strlcpy(entry.ainfo.local, entry.path,
PATH_MAX);
canonize(entry.ainfo.local);
}
if (local[0]) {
entry.ainfo.local = mylocal;
(void) strlcpy(entry.path, local, PATH_MAX);
(void) strcat(entry.path, path+n);
} else
(void) strlcpy(entry.path,
(path[n] == '/') ? path+n+1 : path+n,
PATH_MAX);
}
canonize(entry.path);
if (entry.path[0]) {
findlink(&entry, path, entry.path);
if (strchr("dcbp", entry.ftype) ||
(nflag && !strchr("sl", entry.ftype)))
entry.ainfo.local = NULL;
if (ppkgmap(&entry, stdout)) {
progerr(gettext(ERR_WRITE));
exit(99);
}
}
}
static void
follow(char *path)
{
struct stat stbuf;
FILE *pp;
char *pt,
local[PATH_MAX],
newpath[PATH_MAX],
cmd[PATH_MAX+32];
int n;
errflg = 0;
if (pt = strchr(path, '=')) {
*pt++ = '\0';
n = ((unsigned int)pt - (unsigned int)path - 1);
if (n >= PATH_MAX) {
progerr(gettext(ERR_PATHLONG));
errflg++;
return;
}
n = strlen(pt);
if (n < PATH_MAX) {
(void) strlcpy(local, pt, sizeof (local));
n = strlen(path);
} else {
progerr(gettext(ERR_PATHLONG));
errflg++;
return;
}
} else {
n = 0;
local[0] = '\0';
}
if (stat(path, &stbuf)) {
progerr(gettext(ERR_STAT), path);
errflg++;
return;
}
if (stbuf.st_mode & S_IFDIR) {
(void) snprintf(cmd, sizeof (cmd), "find %s -print", path);
if ((pp = popen(cmd, "r")) == NULL) {
progerr(gettext(ERR_POPEN), cmd);
exit(1);
}
while (fscanf(pp, "%[^\n]\n", newpath) == 1)
output(newpath, n, local);
if (pclose(pp)) {
progerr(gettext(ERR_PCLOSE), cmd);
errflg++;
}
} else
output(path, n, local);
}
static char *
scan_raw_ln(char *targ_name, char *link_name)
{
char *const_ptr;
char *file_name;
char *this_dir;
char *next_dir;
char *targ_ptr;
const_ptr = targ_name;
if (RELATIVE(targ_name) &&
(file_name = strrchr(link_name, '/')) != NULL) {
targ_ptr = targ_name;
this_dir = targ_ptr;
file_name++;
do {
size_t str_size;
next_dir = strchr(targ_ptr, '/');
if (next_dir)
next_dir++;
else
next_dir = targ_ptr+strlen(targ_ptr);
str_size = ((ptrdiff_t)next_dir - (ptrdiff_t)this_dir);
if (strncmp(this_dir, link_name, str_size) == 0) {
const_ptr = this_dir = next_dir;
targ_ptr = (char *)(targ_ptr+str_size);
link_name = (char *)(link_name+str_size);
} else {
int d = 0;
char *dptr = link_name;
while ((dptr = strchr(dptr, '/')) != NULL) {
dptr++;
d++;
}
if (d) {
char *tptr;
const_ptr = tptr = construction;
while (d--) {
(void) strlcpy(tptr,
"../", PATH_MAX);
tptr += 3;
}
(void) strlcpy(tptr, targ_ptr,
PATH_MAX);
}
break;
}
} while (link_name != file_name);
}
return (const_ptr);
}
static void
findlink(struct cfent *ept, char *path, char *svpath)
{
struct stat statbuf;
struct link *link, *new;
char buf[PATH_MAX];
int n;
if (lstat(path, &statbuf)) {
progerr(gettext(ERR_STAT), path);
errflg++;
}
if ((statbuf.st_mode & S_IFMT) == S_IFLNK) {
if (!iflag) {
ept->ainfo.local = mylocal;
ept->ftype = 's';
n = readlink(path, buf, PATH_MAX);
if (n <= 0) {
progerr(gettext(ERR_RDLINK), path);
errflg++;
(void) strlcpy(ept->ainfo.local,
"unknown", PATH_MAX);
} else {
(void) strncpy(ept->ainfo.local, buf, n);
ept->ainfo.local[n] = '\0';
}
}
return;
}
if (stat(path, &statbuf))
return;
if (statbuf.st_nlink <= 1)
return;
for (link = firstlink; link; link = link->next) {
if ((statbuf.st_ino == link->ino) &&
(statbuf.st_dev == link->dev)) {
ept->ftype = 'l';
ept->ainfo.local = mylocal;
(void) strlcpy(ept->ainfo.local,
scan_raw_ln(link->path, ept->path),
PATH_MAX);
return;
}
}
if ((new = (struct link *)calloc(1, sizeof (struct link))) == NULL) {
progerr(gettext(ERR_MEMORY), errno);
exit(1);
}
if (firstlink) {
lastlink->next = new;
lastlink = new;
} else
firstlink = lastlink = new;
new->path = strdup(svpath);
new->ino = statbuf.st_ino;
new->dev = statbuf.st_dev;
}
static void
usage(void)
{
(void) fprintf(stderr,
gettext("usage: %s [-i] [-c class] [path ...]\n"), get_prog_name());
exit(1);
}