summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiklas Hallqvist <niklas@cvs.openbsd.org>1996-06-04 07:56:15 +0000
committerNiklas Hallqvist <niklas@cvs.openbsd.org>1996-06-04 07:56:15 +0000
commit167be6f259825b0e88a4cfc7cd9228c9603fc603 (patch)
treec173305ec5afd2f63886e210fa18f7b9c69695b8
parentc8e3d32207db0af93ff3f660ded6b01fac993998 (diff)
add package tools from FreeBSD
-rw-r--r--usr.sbin/Makefile5
-rw-r--r--usr.sbin/pkg_install/Makefile4
-rw-r--r--usr.sbin/pkg_install/Makefile.inc3
-rw-r--r--usr.sbin/pkg_install/README8
-rw-r--r--usr.sbin/pkg_install/add/Makefile17
-rw-r--r--usr.sbin/pkg_install/add/add.h44
-rw-r--r--usr.sbin/pkg_install/add/extract.c186
-rw-r--r--usr.sbin/pkg_install/add/futil.c95
-rw-r--r--usr.sbin/pkg_install/add/main.c170
-rw-r--r--usr.sbin/pkg_install/add/perform.c467
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1355
-rw-r--r--usr.sbin/pkg_install/create/Makefile17
-rw-r--r--usr.sbin/pkg_install/create/create.h46
-rw-r--r--usr.sbin/pkg_install/create/main.c177
-rw-r--r--usr.sbin/pkg_install/create/perform.c286
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1381
-rw-r--r--usr.sbin/pkg_install/create/pl.c217
-rw-r--r--usr.sbin/pkg_install/delete/Makefile15
-rw-r--r--usr.sbin/pkg_install/delete/delete.h33
-rw-r--r--usr.sbin/pkg_install/delete/main.c124
-rw-r--r--usr.sbin/pkg_install/delete/perform.c216
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1185
-rw-r--r--usr.sbin/pkg_install/info/Makefile15
-rw-r--r--usr.sbin/pkg_install/info/info.h59
-rw-r--r--usr.sbin/pkg_install/info/main.c182
-rw-r--r--usr.sbin/pkg_install/info/perform.c211
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1136
-rw-r--r--usr.sbin/pkg_install/info/show.c195
-rw-r--r--usr.sbin/pkg_install/lib/Makefile11
-rw-r--r--usr.sbin/pkg_install/lib/exec.c61
-rw-r--r--usr.sbin/pkg_install/lib/file.c516
-rw-r--r--usr.sbin/pkg_install/lib/ftp.c424
-rw-r--r--usr.sbin/pkg_install/lib/ftp.h27
-rw-r--r--usr.sbin/pkg_install/lib/global.c34
-rw-r--r--usr.sbin/pkg_install/lib/lib.h173
-rw-r--r--usr.sbin/pkg_install/lib/msg.c102
-rw-r--r--usr.sbin/pkg_install/lib/pen.c144
-rw-r--r--usr.sbin/pkg_install/lib/plist.c448
-rw-r--r--usr.sbin/pkg_install/lib/str.c111
-rw-r--r--usr.sbin/pkg_install/tkpkg180
40 files changed, 6077 insertions, 3 deletions
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index 81e5ad42ed2..8eca7953fa4 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -1,5 +1,4 @@
-# from: @(#)Makefile 5.6.1.2 (Berkeley) 5/8/91
-# $Id: Makefile,v 1.11 1996/05/27 08:00:21 deraadt Exp $
+# $OpenBSD: Makefile,v 1.12 1996/06/04 07:56:00 niklas Exp $
# not yet done: catman
@@ -8,7 +7,7 @@ SUBDIR= ac accton arp bootpd bootpgw bootpef bootptest \
diskpart edquota gettable gspa htable inetd iostat \
ipftest ipmon ipsend kgmon \
kvm_mkdb lpr map-mbone mrinfo mrouted mtrace mtree named \
- netgroup_mkdb portmap pppd pstat pwd_mkdb quot quotaon \
+ netgroup_mkdb pkg_install portmap pppd pstat pwd_mkdb quot quotaon \
rarpd rbootd rdconfig rdate repquota rmt \
rpc.bootparamd rpc.pcnfsd rwhod \
sa sendmail sliplogin slstats spray sysctl \
diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile
new file mode 100644
index 00000000000..c8f4eab0dea
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,4 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:01 niklas Exp $
+SUBDIR=lib add create delete info
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc
new file mode 100644
index 00000000000..a67fb5c22af
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,3 @@
+# $OpenBSD: Makefile.inc,v 1.1 1996/06/04 07:56:01 niklas Exp $
+# Inherit BINDIR from one level up.
+.include "../Makefile.inc"
diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README
new file mode 100644
index 00000000000..a5a517d7752
--- /dev/null
+++ b/usr.sbin/pkg_install/README
@@ -0,0 +1,8 @@
+This is the pkg_install suite of tools for doing maintainance of
+software "packages". More documentation is available in the man pages
+for each individual command.
+
+This code was written by Jordan Hubbard for FreeBSD, snatched and
+mildly reshaped by John Kohl in NetBSD and the changes taken back into
+FreeBSD again by Jordan, who then proceeded to add another couple
+of dozen features on top. Whee! :-)
diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile
new file mode 100644
index 00000000000..6817ca75341
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:02 niklas Exp $
+PROG= pkg_add
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD+= -L${.CURDIR}/../lib/obj -linstall
+DPADD+= ${.CURDIR}/../lib/obj/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+
+SRCS= main.c perform.c futil.c extract.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h
new file mode 100644
index 00000000000..1475e474de9
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,44 @@
+/* $OpenBSD: add.h,v 1.1 1996/06/04 07:56:02 niklas Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the add command.
+ *
+ */
+
+#ifndef _INST_ADD_H_INCLUDE
+#define _INST_ADD_H_INCLUDE
+
+typedef enum { NORMAL, MASTER, SLAVE } add_mode_t;
+
+extern char *Prefix;
+extern Boolean NoInstall;
+extern Boolean NoRecord;
+extern Boolean Force;
+extern char *Mode;
+extern char *Owner;
+extern char *Group;
+extern char *Directory;
+extern char *PkgName;
+extern char FirstPen[];
+extern add_mode_t AddMode;
+
+int make_hierarchy(char *);
+void extract_plist(char *, Package *);
+void apply_perms(char *, char *);
+
+#endif /* _INST_ADD_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c
new file mode 100644
index 00000000000..605bfafbbd0
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,186 @@
+# $OpenBSD: extract.c,v 1.1 1996/06/04 07:56:03 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: extract.c,v 1.1 1996/06/04 07:56:03 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the package extraction code for the add module.
+ *
+ */
+
+#include "lib.h"
+#include "add.h"
+
+
+#define STARTSTRING "tar cf -"
+#define TOOBIG(str) ((strlen(str) + 6 + strlen(home) + where_count > maxargs) \
+ || (strlen(str) + 6 + strlen(home) + perm_count > maxargs))
+
+#define PUSHOUT(todir) /* push out string */ \
+ if (strlen(where_args) > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xf - -C "); \
+ strcat(where_args, todir); \
+ if (system(where_args)) \
+ barf("can't invoke tar pipeline"); \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ } \
+ if (perm_count) { \
+ if (!isdir(todir)) apply_perms(todir, perm_args); \
+ perm_args[0] = 0;\
+ perm_count = 0; \
+ }
+
+void
+extract_plist(char *home, Package *pkg)
+{
+ PackingList p = pkg->head;
+ char *last_file;
+ char *where_args, *perm_args, *last_chdir;
+ int maxargs, where_count = 0, perm_count = 0, add_count;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 64; /* some slop for the tar cmd text,
+ and sh -c */
+ where_args = malloc(maxargs);
+ if (!where_args)
+ barf("can't get argument list space");
+ perm_args = malloc(maxargs);
+ if (!perm_args)
+ barf("can't get argument list space");
+
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ perm_args[0] = 0;
+
+ last_chdir = 0;
+
+ /* Reset the world */
+ Owner = NULL;
+ Group = NULL;
+ Mode = NULL;
+ last_file = NULL;
+ Directory = home;
+
+ /* Do it */
+ while (p) {
+ char cmd[FILENAME_MAX];
+
+ switch(p->type) {
+ case PLIST_NAME:
+ PkgName = p->name;
+ if (Verbose)
+ printf("extract: Package name is %s\n", p->name);
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ if (Verbose)
+ printf("extract: %s/%s\n", Directory, p->name);
+ if (!Fake) {
+ char try[FILENAME_MAX];
+
+ /* first try to rename it into place */
+ sprintf(try, "%s/%s", Directory, p->name);
+ if (rename(p->name, try) == 0) {
+ /* try to add to list of perms to be changed,
+ and run in bulk. */
+ add_count = snprintf(&perm_args[perm_count],
+ maxargs - perm_count,
+ "%s ", p->name);
+ if (add_count > maxargs - perm_count)
+ barf("oops, miscounted strings!");
+ perm_count += add_count;
+ if (p->name[0] == '/') {
+ PUSHOUT(Directory);
+ }
+ } else {
+ /* rename failed, try copying with a big tar command */
+ if (p->name[0] == '/' ||
+ TOOBIG(p->name) ||
+ last_chdir != Directory) {
+ PUSHOUT(last_chdir);
+ last_chdir = Directory;
+ }
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s", p->name);
+ if (add_count > maxargs - where_count)
+ barf("oops, miscounted strings!");
+ where_count += add_count;
+ add_count = snprintf(&perm_args[perm_count],
+ maxargs - perm_count,
+ "%s ", p->name);
+ if (add_count > maxargs - perm_count)
+ barf("oops, miscounted strings!");
+ perm_count += add_count;
+ if (p->name[0] == '/') {
+ PUSHOUT(Directory);
+ }
+ }
+ }
+ break;
+
+ case PLIST_CWD:
+ if (Verbose)
+ printf("extract: CWD to %s\n", p->name);
+ PUSHOUT(Directory);
+ if (strcmp(p->name, ".")) {
+ if (!Fake && make_hierarchy(p->name) == FAIL)
+ barf("Unable make directory '%s'.", p->name);
+ Directory = p->name;
+ }
+ else
+ Directory = home;
+ break;
+
+ case PLIST_CMD:
+ format_cmd(cmd, p->name, Directory, last_file);
+ PUSHOUT(Directory);
+ if (Verbose)
+ printf("extract: execute '%s'\n", cmd);
+ if (!Fake && system(cmd))
+ whinge("Command '%s' failed.", cmd);
+ break;
+
+ case PLIST_CHMOD:
+ PUSHOUT(Directory);
+ Mode = p->name;
+ break;
+
+ case PLIST_CHOWN:
+ PUSHOUT(Directory);
+ Owner = p->name;
+ break;
+
+ case PLIST_CHGRP:
+ PUSHOUT(Directory);
+ Group = p->name;
+ break;
+
+ case PLIST_COMMENT:
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+ }
+ p = p->next;
+ }
+ PUSHOUT(Directory);
+}
diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c
new file mode 100644
index 00000000000..d78817679d5
--- /dev/null
+++ b/usr.sbin/pkg_install/add/futil.c
@@ -0,0 +1,95 @@
+# $OpenBSD: futil.c,v 1.1 1996/06/04 07:56:03 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: futil.c,v 1.1 1996/06/04 07:56:03 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include "lib.h"
+#include "add.h"
+
+/*
+ * Assuming dir is a desired directory name, make it and all intervening
+ * directories necessary.
+ */
+
+int
+make_hierarchy(char *dir)
+{
+ char *cp1, *cp2;
+
+ if (dir[0] == '/')
+ cp1 = cp2 = dir + 1;
+ else
+ cp1 = cp2 = dir;
+ while (cp2) {
+ if ((cp2 = index(cp1, '/')) !=NULL )
+ *cp2 = '\0';
+ if (fexists(dir)) {
+ if (!isdir(dir))
+ return FAIL;
+ }
+ else {
+ if (vsystem("mkdir %s", dir))
+ return FAIL;
+ apply_perms(NULL, dir);
+ }
+ /* Put it back */
+ if (cp2) {
+ *cp2 = '/';
+ cp1 = cp2 + 1;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Using permission defaults, apply them as necessary */
+void
+apply_perms(char *dir, char *arg)
+{
+ char *cd_to;
+
+ if (!dir || *arg == '/') /* absolute path? */
+ cd_to = "/";
+ else
+ cd_to = dir;
+
+ if (Mode)
+ if (vsystem("cd %s && chmod -R %s %s", cd_to, Mode, arg))
+ whinge("Couldn't change modes of '%s' to '%s'.",
+ arg, Mode);
+ if (Owner && Group) {
+ if (vsystem("cd %s && chown -R %s.%s %s", cd_to, Owner, Group, arg))
+ whinge("Couldn't change owner/group of '%s' to '%s.%s'.",
+ arg, Owner, Group);
+ return;
+ }
+ if (Owner) {
+ if (vsystem("cd %s && chown -R %s %s", cd_to, Owner, arg))
+ whinge("Couldn't change owner of '%s' to '%s'.",
+ arg, Owner);
+ return;
+ } else if (Group)
+ if (vsystem("cd %s && chgrp -R %s %s", cd_to, Group, arg))
+ whinge("Couldn't change group of '%s' to '%s'.",
+ arg, Group);
+}
+
diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c
new file mode 100644
index 00000000000..0e067103a12
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,170 @@
+# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:03 niklas Exp $
+#ifndef lint
+static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:03 niklas Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ *
+ */
+
+#include <sys/param.h>
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "hvIRfnp:SMt:";
+
+char *Prefix = NULL;
+Boolean NoInstall = FALSE;
+Boolean NoRecord = FALSE;
+Boolean Force = FALSE;
+
+char *Mode = NULL;
+char *Owner = NULL;
+char *Group = NULL;
+char *PkgName = NULL;
+char *Directory = NULL;
+char FirstPen[FILENAME_MAX];
+add_mode_t AddMode = NORMAL;
+
+#define MAX_PKGS 20
+char pkgnames[MAX_PKGS][MAXPATHLEN];
+char *pkgs[MAX_PKGS];
+
+int
+main(int argc, char **argv)
+{
+ int ch, err;
+ char **start;
+ char *prog_name = argv[0], *cp;
+
+ start = argv;
+ while ((ch = getopt(argc, argv, Options)) != EOF) {
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'I':
+ NoInstall = TRUE;
+ break;
+
+ case 'R':
+ NoRecord = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 't':
+ strcpy(FirstPen, optarg);
+ break;
+
+ case 'S':
+ AddMode = SLAVE;
+ break;
+
+ case 'M':
+ AddMode = MASTER;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage(prog_name, NULL);
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > MAX_PKGS) {
+ whinge("Too many packages (max %d).", MAX_PKGS);
+ return(1);
+ }
+
+ if (AddMode != SLAVE) {
+ for (ch = 0; ch < MAX_PKGS; pkgs[ch++] = NULL) ;
+
+ /* Get all the remaining package names, if any */
+ for (ch = 0; *argv; ch++, argv++) {
+ if (!strcmp(*argv, "-")) /* stdin? */
+ pkgs[ch] = "-";
+ else if (isURL(*argv)) /* preserve URLs */
+ pkgs[ch] = strcpy(pkgnames[ch], *argv);
+ else { /* expand all pathnames to fullnames */
+ if (fexists(*argv)) /* refers to a file directly */
+ pkgs[ch] = realpath(*argv, pkgnames[ch]);
+ else { /* look for the file in the expected places */
+ if (!(cp = fileFindByPath(NULL, *argv)))
+ whinge("Can't find package '%s'.", *argv);
+ else
+ pkgs[ch] = strcpy(pkgnames[ch], cp);
+ }
+ }
+ }
+ }
+ /* If no packages, yelp */
+ else if (!ch)
+ usage(prog_name, "Missing package name(s)");
+ else if (ch > 1 && AddMode == MASTER)
+ usage(prog_name, "Only one package name may be specified with master mode");
+ if ((err = pkg_perform(pkgs)) != NULL) {
+ if (Verbose)
+ fprintf(stderr, "%d package addition(s) failed.\n", err);
+ return err;
+ }
+ else
+ return 0;
+}
+
+void
+usage(const char *name, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt) {
+ fprintf(stderr, "%s: ", name);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n\n");
+ }
+ va_end(args);
+ fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name);
+ fprintf(stderr, "Where args are one or more of:\n\n");
+ fprintf(stderr, "-v verbose\n");
+ fprintf(stderr, "-p arg override prefix with arg\n");
+ fprintf(stderr, "-I don't execute pkg install script, if any\n");
+ fprintf(stderr, "-R don't record installation (can't delete!)\n");
+ fprintf(stderr, "-n don't actually install, just show steps\n");
+ fprintf(stderr, "-t temp use temp as template for mktemp()\n");
+ fprintf(stderr, "-S run in SLAVE mode\n");
+ fprintf(stderr, "-M run in MASTER mode\n");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/add/perform.c b/usr.sbin/pkg_install/add/perform.c
new file mode 100644
index 00000000000..cb9e8270419
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,467 @@
+# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:04 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:04 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the add module.
+ *
+ */
+
+#include "lib.h"
+#include "add.h"
+
+#include <signal.h>
+#include <sys/wait.h>
+
+static int pkg_do(char *);
+static int sanity_check(char *);
+static char LogDir[FILENAME_MAX];
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ if (AddMode == SLAVE)
+ err_cnt = pkg_do(NULL);
+ else {
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ }
+ return err_cnt;
+}
+
+static Package Plist;
+static char *Home;
+
+/*
+ * This is seriously ugly code following. Written very fast!
+ * [And subsequently made even worse.. Sigh! This code was just born
+ * to be hacked, I guess.. :) -jkh]
+ */
+static int
+pkg_do(char *pkg)
+{
+ char pkg_fullname[FILENAME_MAX];
+ char playpen[FILENAME_MAX];
+ char extract_contents[FILENAME_MAX];
+ char *where_to, *tmp, *extract;
+ FILE *cfile;
+ int code;
+ PackingList p;
+ struct stat sb;
+ char *isTMP = NULL;
+ char *cp;
+ int inPlace;
+
+ code = 0;
+ LogDir[0] = '\0';
+ strcpy(playpen, FirstPen);
+ inPlace = 0;
+
+ /* Are we coming in for a second pass, everything already extracted? */
+ if (!pkg) {
+ fgets(playpen, FILENAME_MAX, stdin);
+ playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */
+ if (chdir(playpen) == FAIL) {
+ whinge("pkg_add in SLAVE mode can't chdir to %s.", playpen);
+ return 1;
+ }
+ read_plist(&Plist, stdin);
+ where_to = playpen;
+ }
+ /* Nope - do it now */
+ else {
+ /* Is it an ftp://foo.bar.baz/file.tgz specification? */
+ if (isURL(pkg)) {
+ if (!(Home = fileGetURL(NULL, pkg))) {
+ whinge("Unable to fetch `%s' by URL.", pkg);
+ return 1;
+ }
+ where_to = Home;
+ strcpy(pkg_fullname, pkg);
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ whinge("Unable to open table of contents file `%s' - not a package?", CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ }
+ else {
+ strcpy(pkg_fullname, pkg); /* copy for sanity's sake, could remove pkg_fullname */
+ if (strcmp(pkg, "-")) {
+ if (stat(pkg_fullname, &sb) == FAIL) {
+ whinge("Can't stat package file '%s'.", pkg_fullname);
+ goto bomb;
+ }
+ sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME);
+ extract = extract_contents;
+ }
+ else
+ extract = NULL;
+ Home = make_playpen(playpen, sb.st_size * 4);
+ if (!Home)
+ whinge("Unable to make playpen for %d bytes.\n", sb.st_size * 4);
+ where_to = Home;
+ if (unpack(pkg_fullname, extract)) {
+ whinge("Unable to extract table of contents file from `%s' - not a package?.", pkg_fullname);
+ goto bomb;
+ }
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ whinge("Unable to open table of contents file `%s' - not a package?", CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+
+ /* Extract directly rather than moving? Oh goodie! */
+ if (find_plist_option(&Plist, "extract-in-place")) {
+ if (Verbose)
+ printf("Doing in-place extraction for %s\n", pkg_fullname);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (p) {
+ if (!isdir(p->name) && !Fake) {
+ if (Verbose)
+ printf("Desired prefix of %s does not exist, creating..\n", p->name);
+ vsystem("mkdir -p %s", p->name);
+ if (chdir(p->name) == -1) {
+ whinge("Unable to change directory to `%s' - no permission?", p->name);
+ perror("chdir");
+ goto bomb;
+ }
+ }
+ where_to = p->name;
+ inPlace = 1;
+ }
+ else {
+ whinge("No prefix specified in `%s' - this is a bad package!", pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, so multiply by 4 for good measure.
+ */
+
+ if (!inPlace && min_free(playpen) < sb.st_size * 4) {
+ whinge("Projected size of %d exceeds available free space.\n"
+ "Please set your PKG_TMPDIR variable to point to a location with more\n"
+ "free space and try again.", sb.st_size * 4);
+ whinge("Not extracting %s\ninto %s, sorry!", pkg_fullname, where_to);
+ goto bomb;
+ }
+
+ /* If this is a direct extract and we didn't want it, stop now */
+ if (inPlace && Fake)
+ goto success;
+
+ /* Finally unpack the whole mess */
+ if (unpack(pkg_fullname, NULL)) {
+ whinge("Unable to extract `%s'!", pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /* Check for sanity and dependencies */
+ if (sanity_check(pkg))
+ goto bomb;
+
+ /* If we're running in MASTER mode, just output the plist and return */
+ if (AddMode == MASTER) {
+ printf("%s\n", where_playpen());
+ write_plist(&Plist, stdout);
+ return 0;
+ }
+ }
+
+ /*
+ * If we have a prefix, delete the first one we see and add this
+ * one in place of it.
+ */
+ if (Prefix) {
+ delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&Plist, PLIST_CWD, Prefix);
+ }
+
+ setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
+ /* Protect against old packages with bogus @name fields */
+ PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous";
+
+ /* See if we're already registered */
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ if (isdir(LogDir)) {
+ char tmp[FILENAME_MAX];
+
+ whinge("Package `%s' already recorded as installed.\n", PkgName);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for dependencies */
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Package `%s' depends on `%s'.\n", PkgName, p->name);
+ if (!Fake && vsystem("pkg_info -e %s", p->name)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake && !isURL(pkg) && !getenv("PKG_ADD_BASE")) {
+ snprintf(path, FILENAME_MAX, "%s/%s.tgz", Home, p->name);
+ if (fexists(path))
+ cp = path;
+ else
+ cp = fileFindByPath(pkg, p->name);
+ if (cp) {
+ if (Verbose)
+ printf("Loading it from %s.\n", cp);
+ if (vsystem("pkg_add %s", cp)) {
+ whinge("Autoload of dependency `%s' failed%s", cp, Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ }
+ else if (!Fake && (cp = fileGetURL(pkg, p->name)) != NULL) {
+ if (Verbose)
+ printf("Finished loading %s over FTP.\n", p->name);
+ if (!Fake) {
+ if (!fexists("+CONTENTS"))
+ whinge("Autoloaded package %s has no +CONTENTS file?", p->name);
+ else
+ if (vsystem("(pwd; cat +CONTENTS) | pkg_add %s-S", Verbose ? "-v " : "")) {
+ whinge("pkg_add of dependency `%s' failed%s",
+ p->name, Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ else if (Verbose)
+ printf("\t`%s' loaded successfully.\n", p->name);
+ }
+ /* Nuke the temporary playpen */
+ leave_playpen(cp);
+ }
+ else {
+ if (Verbose)
+ printf("and was not found%s.\n", Force ? " (proceeding anyway)" : "");
+ else
+ printf("Package dependency %s for %s not found%s\n", p->name, pkg,
+ Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ else if (Verbose)
+ printf(" - already installed.\n");
+ }
+
+ /* Look for the requirements file */
+ if (fexists(REQUIRE_FNAME)) {
+ vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (Verbose)
+ printf("Running requirements file first for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) {
+ whinge("Package %s fails requirements %s", pkg_fullname,
+ Force ? "installing anyway" : "- not installed.");
+ if (!Force) {
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ }
+ }
+
+ /* If we're really installing, and have an installation file, run it */
+ if (!NoInstall && fexists(INSTALL_FNAME)) {
+ vsystem("chmod +x %s", INSTALL_FNAME); /* make sure */
+ if (Verbose)
+ printf("Running install with PRE-INSTALL for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s PRE-INSTALL", INSTALL_FNAME, PkgName)) {
+ whinge("Install script returned error status.");
+ unlink(INSTALL_FNAME);
+ code = 1;
+ goto success; /* nothing to uninstall yet */
+ }
+ }
+
+ /* Now finally extract the entire show if we're not going direct */
+ if (!inPlace && !Fake)
+ extract_plist(".", &Plist);
+
+ if (!Fake && fexists(MTREE_FNAME)) {
+ if (Verbose)
+ printf("Running mtree for %s..\n", PkgName);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (Verbose)
+ printf("mtree -U -f %s -d -e -p %s\n", MTREE_FNAME, p ? p->name : "/");
+ if (!Fake) {
+ if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s", MTREE_FNAME, p ? p->name : "/"))
+ whinge("mtree returned a non-zero status - continuing.");
+ }
+ unlink(MTREE_FNAME);
+ }
+
+ /* Run the installation script one last time? */
+ if (!NoInstall && fexists(INSTALL_FNAME)) {
+ if (Verbose)
+ printf("Running install with POST-INSTALL for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s POST-INSTALL", INSTALL_FNAME, PkgName)) {
+ whinge("Install script returned error status.");
+ unlink(INSTALL_FNAME);
+ code = 1;
+ goto fail;
+ }
+ unlink(INSTALL_FNAME);
+ }
+
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[FILENAME_MAX];
+ FILE *cfile;
+
+ umask(022);
+ if (getuid() != 0)
+ whinge("Not running as root - trying to record install anyway.");
+ if (!PkgName) {
+ whinge("No package name! Can't record package, sorry.");
+ code = 1;
+ goto success; /* well, partial anyway */
+ }
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ if (Verbose)
+ printf("Attempting to record package into %s..\n", LogDir);
+ if (make_hierarchy(LogDir)) {
+ whinge("Can't record package into '%s', you're on your own!",
+ LogDir);
+ bzero(LogDir, FILENAME_MAX);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ /* Make sure pkg_info can read the entry */
+ vsystem("chmod a+rx %s", LogDir);
+ if (fexists(DEINSTALL_FNAME))
+ move_file(".", DEINSTALL_FNAME, LogDir);
+ if (fexists(REQUIRE_FNAME))
+ move_file(".", REQUIRE_FNAME, LogDir);
+ sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
+ cfile = fopen(contents, "w");
+ if (!cfile) {
+ whinge("Can't open new contents file '%s'! Can't register pkg.", contents);
+ goto success; /* can't log, but still keep pkg */
+ }
+ write_plist(&Plist, cfile);
+ fclose(cfile);
+ move_file(".", DESC_FNAME, LogDir);
+ move_file(".", COMMENT_FNAME, LogDir);
+ if (fexists(DISPLAY_FNAME))
+ move_file(".", DISPLAY_FNAME, LogDir);
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to record dependency on package `%s'\n", p->name);
+ sprintf(contents, "%s/%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ basename_of(p->name), REQUIRED_BY_FNAME);
+ cfile = fopen(contents, "a");
+ if (!cfile)
+ whinge("Warning: Can't open dependency file '%s'!\n"
+ "\tDependency registration is incomplete.", contents);
+ else {
+ fprintf(cfile, "%s\n", PkgName);
+ if (fclose(cfile) == EOF)
+ warn("Cannot properly close file %s", contents);
+ }
+ }
+ if (Verbose)
+ printf("Package %s registered in %s\n", PkgName, LogDir);
+ }
+
+ if (p = find_plist(&Plist, PLIST_DISPLAY)) {
+ FILE *fp;
+ char buf[BUFSIZ];
+ fp = fopen(p->name, "r");
+ if (fp) {
+ putc('\n', stdout);
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stdout);
+ putc('\n', stdout);
+ (void) fclose(fp);
+ } else
+ warn("Cannot open display file `%s'.", p->name);
+ }
+
+ goto success;
+
+ bomb:
+ code = 1;
+ goto success;
+
+ fail:
+ /* Nuke the whole (installed) show, XXX but don't clean directories */
+ if (!Fake)
+ delete_package(FALSE, FALSE, &Plist);
+
+ success:
+ /* delete the packing list contents */
+ free_plist(&Plist);
+ leave_playpen(Home);
+ return code;
+}
+
+static int
+sanity_check(char *pkg)
+{
+ PackingList p;
+ int code = 0;
+
+ if (!fexists(CONTENTS_FNAME)) {
+ whinge("Package %s has no CONTENTS file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(COMMENT_FNAME)) {
+ whinge("Package %s has no COMMENT file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(DESC_FNAME)) {
+ whinge("Package %s has no DESC file!", pkg);
+ code = 1;
+ }
+ return code;
+}
+
+void
+cleanup(int signo)
+{
+ if (signo)
+ printf("Signal %d received, cleaning up..\n", signo);
+ if (Plist.head) {
+ if (!Fake)
+ delete_package(FALSE, FALSE, &Plist);
+ free_plist(&Plist);
+ }
+ if (!Fake && LogDir[0])
+ vsystem("%s -rf %s", REMOVE_CMD, LogDir);
+ leave_playpen(Home);
+}
diff --git a/usr.sbin/pkg_install/add/pkg_add.1 b/usr.sbin/pkg_install/add/pkg_add.1
new file mode 100644
index 00000000000..20c6b3e4af7
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,355 @@
+.\" $OpenBSD: pkg_add.1,v 1.1 1996/06/04 07:56:04 niklas Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" 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.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_add.1
+.\"
+.Dd November 25, 1994
+.Dt pkg_add 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing software package distributions.
+.Sh SYNOPSIS
+.Nm
+.Op Fl vInfRMS
+.Op Fl t Ar template
+.Op Fl p Ar prefix
+.Ar pkg-name [pkg-name ...]
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract packages that have been previously created
+with the
+.Xr pkg_create 1
+command.
+
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs contained within a package file,
+your system may be susceptible to ``trojan horses'' or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, use the
+.Fl M
+flag to extract the package file, and inspect its contents and scripts
+to insure it poses no danger to your system's integrity. Pay particular
+attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files,
+and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the package file.
+.Ef
+
+.Sh OPTIONS
+The following command line arguments are supported.
+.Bl -tag -width indent
+.It Ar pkg-name [... pkg-name]
+The named packages are installed. A package name of - will cause
+.Nm
+to read from stdin.
+.It Fl v
+Turns on verbose output.
+.Em "Optional."
+.It Fl I
+If an installation script exists for a given package, do not execute it.
+.Em "Optional."
+.It Fl n
+Don't actually install a package, just report the steps that
+would be taken if it was.
+.Em "Optional."
+.It Fl R
+Do not record the installation of a package. This means
+that you cannot deinstall it later, so only use this option if
+you know what you are doing!
+.Em "Optional."
+.It Fl f
+Forces installation to proceed even if prerequisite packages are not
+installed or the requirements script fails.
+.Em "Optional."
+.It Fl p Ar prefix
+Sets
+.Ar prefix
+as the directory in which to extract files from a package.
+If a package has set its default directory, it will be overridden
+by this flag. Note that only the first
+.Cm @cwd
+directive will be replaced, since
+.Nm
+has no way of knowing which directory settings are relative and
+which are absolute. It is rare, in any case, to see more than one
+directory transition made, but when such is the case you
+may then wish to look into the use of the
+.Cm MASTER
+and
+.Cm SLAVE
+modes (see the
+.Fl M
+and
+.Fl S
+options).
+.Em "Optional."
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a ``staging area.''
+By default, this is the string
+.Pa /var/tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Pp
+You can get a performance boost by setting the staging area
+.Ar template
+to reside on the same disk partition as target directories for package
+file installation; often this is
+.Pa /usr .
+.Em "Optional."
+.It Fl M
+Run in
+.Cm MASTER
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm SLAVE
+mode. When run in this mode,
+.Nm
+does no work beyond extracting the package into a temporary staging
+area (see the
+.Fl t
+option), reading in the packing list, and then dumping it (prefaced by
+the current staging area) to stdout where it may be filtered by a
+program such as
+.Xr sed 1 .
+When used in conjunction with
+.Cm SLAVE
+mode, it allows you to make radical changes to the package structure
+before acting on its contents.
+.It Fl S
+Run in
+.Cm SLAVE
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm MASTER
+mode. When run in this mode,
+.Nm
+expects the release contents to be already extracted and waiting
+in the staging area, the location of which is read as a string
+from stdin. The complete packing list is also read from stdin,
+and the contents then acted on as normal.
+.El
+On or more
+.Ar pkg-name
+arguments may be specified, each being either a file containing the
+package (these usually ending with the ``.tgz'' suffix) or a
+URL pointing at a file available on an ftp site. Thus you may
+extract files directly from their anonymous ftp locations (e.g.
+.Nm
+ftp://ftp.freebsd.org/pub/FreeBSD/packages/shells/bash-1.14.4.tgz).
+Note: If you wish to use
+.Bf -emphasis
+passive mode
+.Ef
+ftp in such transfers, set
+the variable
+.Bf -emphasis
+FTP_PASSIVE_MODE
+.Ef
+to some value in your environment. Otherwise, the more standard
+ACTIVE mode may be used. If
+.Nm
+consistently fails to fetch a package from a site known to work,
+it may be because you have a firewall that demands the usage of
+.Bf -emphasis
+passive mode
+.Ef
+ftp.
+.Sh TECHNICAL DETAILS
+.Nm
+is fairly simple. It extracts each packages' "packing list"
+into a special staging directory in /tmp (or $PKG_TMPDIR), parses it,
+then runs through the following sequence to fully extract the contents:
+.Bl -enum -indent indent
+.It
+Check if the package is already recorded as installed. If so,
+terminate installation.
+.It
+Scan all the package dependencies (from
+.Cm @pkgdep
+directives, see
+.Xr pkg_create 1 )
+and make sure each one is met. If not, print the missing dependencies and
+terminate the installation.
+.It
+Search for any
+.Cm @option
+directives which control how the package is added to the system.
+At the time of this writing, the only currently implemented option is
+.Cm @option extract-in-place
+which will cause the package to be extracted direcly into its
+prefix directory without moving through a staging area in /tmp.
+.It
+If
+.Cm @option extract-in-place
+is enabled, the package is now extracted directly into its
+final location, otherwise it is extracted into the staging area.
+.It
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ),
+then execute it with the following arguments:
+.Bd -filled -offset indent -compact
+.Ar <pkg-name>
+.Ar INSTALL
+.Ed
+where
+.Ar <pkg-name>
+is the name of the package in question and
+.Ar INSTALL
+is simply a keyword denoting that this is an installation requirements check.
+.It
+If an
+.Ar install
+script exists for the package, it is then executed with the following arguments:
+.Bd -filled -offset indent -compact
+.Ar <pkg-name>
+.Ar PRE-INSTALL
+.Ed
+where
+.Ar <pkg-name>
+is the name of the package in question and
+.Ar PRE-INSTALL
+is a keyword denoting that this is the preinstallation phase.
+.It
+If
+.Cm @option extract-in-place
+is not used, then the packing list (this is the
+.Pa +CONTENTS
+file) is now used as a guide for moving (or copying, as necessary) files from
+the staging area into their final locations.
+.It
+If the package contains an
+.Ar mtreefile
+file (see the
+.Fl m
+option to
+.Xr pkg_create 1 ),
+then mtree is invoked as
+.Bd -filled -offset indent -compact
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix
+.Ed
+where
+.Pa prefix
+is either the prefix specified with the
+.Fl p
+flag or, if no
+.Fl p
+flag was specified, the name of the first directory named by a
+.Cm @cwd
+directive within this package.
+.It
+If an
+.Ar install
+script exists for the package, it is then executed as
+.Bd -filled -offset indent -compact
+.Cm <script>
+.Ar <pkg-name>
+.Ar POST-INSTALL
+.Ed
+This all allows you to write a single
+.Ar install
+script that does both ``before and after'' actions.
+.It
+After installation is complete, a copy of the packing list,
+.Ar deinstall
+script, description, and display files are copied into
+.Pa /var/db/pkg/<pkg-name>
+for subsequent possible use by
+.Xr pkg_delete 1 .
+Any package dependencies are recorded in the other packages'
+.Pa /var/db/pkg/<other-pkg>/+REQUIRED_BY
+file
+(if the environment variable PKG_DBDIR is set, this overrides the
+.Pa /var/db/pkg/
+path shown above).
+.It
+Finally, the staging area is deleted and the program terminates.
+.El
+.Pp
+All the scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above). This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might change it with the
+.Fl p
+flag to
+.Cm pkg_add .
+.Sh SEE ALSO
+.Xr pkg_info 1 ,
+.Xr mktemp 3 ,
+.Xr sysconf 3 ,
+.Xr mtree 8 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.Sh BUGS
+Hard links between files in a distribution are only preserved if either
+(1) the staging area is on the same file system as the target directory of
+all the links to the file, or (2) all the links to the file are bracketed by
+.Cm @cwd
+directives in the contents file,
+.Em and
+and the link names are extracted with a single
+.Cm tar
+command (not split between
+invocations due to exec argument-space limitations--this depends on the
+value returned by
+.Fn sysconf _SC_ARG_MAX ) .
+.Pp
+Sure to be others.
diff --git a/usr.sbin/pkg_install/create/Makefile b/usr.sbin/pkg_install/create/Makefile
new file mode 100644
index 00000000000..c5029619028
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,17 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:05 niklas Exp $
+PROG= pkg_create
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD+= -L${.CURDIR}/../lib/obj -linstall
+DPADD+= ${.CURDIR}/../lib/obj/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+
+SRCS= main.c perform.c pl.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/create/create.h b/usr.sbin/pkg_install/create/create.h
new file mode 100644
index 00000000000..71c616c673a
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,46 @@
+/* $OpenBSD: create.h,v 1.1 1996/06/04 07:56:05 niklas Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the create command.
+ *
+ */
+
+#ifndef _INST_CREATE_H_INCLUDE
+#define _INST_CREATE_H_INCLUDE
+
+extern char *Prefix;
+extern char *Comment;
+extern char *Desc;
+extern char *Display;
+extern char *Install;
+extern char *DeInstall;
+extern char *Contents;
+extern char *Require;
+extern char PlayPen[];
+extern char *ExcludeFrom;
+extern char *Mtree;
+extern char *Pkgdeps;
+extern int Dereference;
+extern int PlistOnly;
+
+void check_list(char *, Package *);
+void usage(const char *, const char *, ...);
+int pkg_perform(char **);
+void copy_plist(char *, Package *);
+
+#endif /* _INST_CREATE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c
new file mode 100644
index 00000000000..19498a8e66c
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,177 @@
+# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:05 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:05 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the create module.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+
+static char Options[] = "YNOhvf:p:P:c:d:i:k:r:t:X:D:m:";
+
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *Display = NULL;
+char *Install = NULL;
+char *DeInstall = NULL;
+char *Contents = NULL;
+char *Require = NULL;
+char PlayPen[FILENAME_MAX];
+char *ExcludeFrom = NULL;
+char *Mtree = NULL;
+char *Pkgdeps = NULL;
+int Dereference = 0;
+int PlistOnly = 0;
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+ char *prog_name = argv[0];
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != EOF)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'N':
+ AutoAnswer = NO;
+ break;
+
+ case 'Y':
+ AutoAnswer = YES;
+ break;
+
+ case 'O':
+ PlistOnly = YES;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'f':
+ Contents = optarg;
+ break;
+
+ case 'c':
+ Comment = optarg;
+ break;
+
+ case 'd':
+ Desc = optarg;
+ break;
+
+ case 'i':
+ Install = optarg;
+ break;
+
+ case 'k':
+ DeInstall = optarg;
+ break;
+
+ case 'r':
+ Require = optarg;
+ break;
+
+ case 't':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'X':
+ ExcludeFrom = optarg;
+ break;
+
+ case 'h':
+ Dereference = 1;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'm':
+ Mtree = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ break;
+
+ case '?':
+ default:
+ usage(prog_name, NULL);
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start)
+ usage(prog_name, "Missing package name");
+ *pkgs = NULL;
+ if (start[1])
+ usage(prog_name, "Only one package name allowed\n\t('%s' extraneous)",
+ start[1]);
+ if (!pkg_perform(start)) {
+ if (Verbose)
+ fprintf(stderr, "Package creation failed.\n");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+void
+usage(const char *name, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt) {
+ fprintf(stderr, "%s: ", name);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n\n");
+ }
+ va_end(args);
+ fprintf(stderr, "Usage: %s [args] pkg\n\n", name);
+ fprintf(stderr, "Where args are one or more of:\n\n");
+
+ fprintf(stderr, "-c [-]file Get one-line comment from file (-or arg)\n");
+ fprintf(stderr, "-d [-]file Get description from file (-or arg)\n");
+ fprintf(stderr, "-f file get list of files from file (- for stdin)\n");
+ fprintf(stderr, "-h follow symbolic links\n");
+ fprintf(stderr, "-i script install script\n");
+ fprintf(stderr, "-k script de-install script\n");
+ fprintf(stderr, "-D file install notice\n");
+ fprintf(stderr, "-m file mtree spec for directories\n");
+ fprintf(stderr, "-P pkgs set package dependency list to pkgs\n");
+ fprintf(stderr, "-p prefix install prefix will be arg\n");
+ fprintf(stderr, "-r script pre/post requirements script\n");
+ fprintf(stderr, "-t temp use temp as template for mktemp()\n");
+ fprintf(stderr, "-X file exclude files listed in file\n");
+ fprintf(stderr, "-v verbose\n");
+ fprintf(stderr, "-Y assume `yes' answer to all questions\n");
+ fprintf(stderr, "-N assume `no' answer to all questions\n");
+ fprintf(stderr, "-O print a revised packing list and exit\n");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c
new file mode 100644
index 00000000000..f1b703299a2
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,286 @@
+# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:05 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:05 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the create module.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+
+#include <errno.h>
+#include <signal.h>
+#include <sys/syslimits.h>
+#include <unistd.h>
+
+static void sanity_check(void);
+static void make_dist(char *, char *, char *, Package *);
+
+static char *home;
+
+int
+pkg_perform(char **pkgs)
+{
+ char *pkg = *pkgs; /* Only one arg to create */
+ char *cp;
+ FILE *pkg_in, *fp;
+ Package plist;
+ char *suffix; /* What we tack on to the end of the finished package */
+
+ /* Preliminary setup */
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+ get_dash_string(&Comment);
+ get_dash_string(&Desc);
+ if (!strcmp(Contents, "-"))
+ pkg_in = stdin;
+ else {
+ pkg_in = fopen(Contents, "r");
+ if (!pkg_in)
+ barf("Unable to open contents file '%s' for input.", Contents);
+ }
+ plist.head = plist.tail = NULL;
+
+ /* Break the package name into base and desired suffix (if any) */
+ if ((cp = rindex(pkg, '.')) != NULL) {
+ suffix = cp + 1;
+ *cp = '\0';
+ }
+ else
+ suffix = "tgz";
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps) {
+ if (Verbose && !PlistOnly)
+ printf("Registering depends:");
+ while (Pkgdeps) {
+ cp = strsep(&Pkgdeps, " \t\n");
+ if (*cp) {
+ add_plist(&plist, PLIST_PKGDEP, cp);
+ if (Verbose && !PlistOnly)
+ printf(" %s", cp);
+ }
+ }
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+ /* Slurp in the packing list */
+ read_plist(&plist, pkg_in);
+
+ /* Prefix should override the packing list */
+ if (Prefix) {
+ delete_plist(&plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&plist, PLIST_CWD, Prefix);
+ }
+ /*
+ * Run down the list and see if we've named it, if not stick in a name
+ * at the top.
+ */
+ if (find_plist(&plist, PLIST_NAME) == NULL)
+ add_plist_top(&plist, PLIST_NAME, basename_of(pkg));
+
+ /*
+ * We're just here for to dump out a revised plist for the FreeBSD ports
+ * hack. It's not a real create in progress.
+ */
+ if (PlistOnly) {
+ write_plist(&plist, stdout);
+ exit(0);
+ }
+
+ /* Make a directory to stomp around in */
+ home = make_playpen(PlayPen, 0);
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ /* Make first "real contents" pass over it */
+ check_list(home, &plist);
+ (void) umask(022); /* make sure gen'ed directories, files don't have
+ group or other write bits. */
+ /* copy_plist(home, &plist); */
+ /* mark_plist(&plist); */
+
+ /* Now put the release specific items in */
+ add_plist(&plist, PLIST_CWD, ".");
+ write_file(COMMENT_FNAME, Comment);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
+ write_file(DESC_FNAME, Desc);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DESC_FNAME);
+
+ if (Install) {
+ copy_file(home, Install, INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
+ }
+ if (DeInstall) {
+ copy_file(home, DeInstall, DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
+ }
+ if (Require) {
+ copy_file(home, Require, REQUIRE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
+ }
+ if (Display) {
+ copy_file(home, Display, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
+ }
+ if (Mtree) {
+ copy_file(home, Mtree, MTREE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, MTREE_FNAME);
+ add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
+ }
+
+ /* Run through the list again, picking up extra "local" items */
+ /* check_list(".", &plist); */
+ /* copy_plist(".", &plist); */
+ /* mark_plist(&plist); */
+
+ /* Finally, write out the packing list */
+ fp = fopen(CONTENTS_FNAME, "w");
+ if (!fp)
+ barf("Can't open file %s for writing.", CONTENTS_FNAME);
+ write_plist(&plist, fp);
+ if (fclose(fp))
+ barf("Error while closing %s.", CONTENTS_FNAME);
+
+ /* And stick it into a tar ball */
+ make_dist(home, pkg, suffix, &plist);
+
+ /* Cleanup */
+ free(Comment);
+ free(Desc);
+ free_plist(&plist);
+ cleanup(0);
+ return TRUE; /* Success */
+}
+
+static void
+make_dist(char *home, char *pkg, char *suffix, Package *plist)
+{
+ char tball[FILENAME_MAX];
+ PackingList p;
+ int ret, max, len;
+ char *args[50]; /* Much more than enough. */
+ int nargs = 0;
+ int pipefds[2];
+ FILE *totar;
+ pid_t pid;
+
+ args[nargs++] = "tar"; /* argv[0] */
+
+ if (*pkg == '/')
+ snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suffix);
+ else
+ snprintf(tball, FILENAME_MAX, "%s/%s.%s", home, pkg, suffix);
+
+ args[nargs++] = "-c";
+ args[nargs++] = "-f";
+ args[nargs++] = tball;
+ if (index(suffix, 'z')) /* Compress/gzip? */
+ args[nargs++] = "-z";
+ if (Dereference)
+ args[nargs++] = "-h";
+ if (ExcludeFrom) {
+ args[nargs++] = "-X";
+ args[nargs++] = ExcludeFrom;
+ }
+ args[nargs++] = "-T"; /* Take filenames from file instead of args. */
+ args[nargs++] = "-"; /* Use stdin for the file. */
+ args[nargs] = NULL;
+
+ if (Verbose)
+ printf("Creating gzip'd tar ball in '%s'\n", tball);
+
+ /* Set up a pipe for passing the filenames, and fork off a tar process. */
+ if (pipe(pipefds) == -1)
+ barf("Cannot create pipe: %s", strerror(errno));
+ if ((pid = fork()) == -1)
+ barf("Cannot fork process for tar: %s", strerror(errno));
+ if (pid == 0) { /* The child */
+ dup2(pipefds[0], 0);
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execv("/usr/bin/tar", args);
+ barf("Failed to execute tar command: %s", strerror(errno));
+ }
+
+ /* Meanwhile, back in the parent process ... */
+ close(pipefds[0]);
+ if ((totar = fdopen(pipefds[1], "w")) == NULL)
+ barf("fdopen failed: %s", strerror(errno));
+
+ fprintf(totar, "%s\n", CONTENTS_FNAME);
+ fprintf(totar, "%s\n", COMMENT_FNAME);
+ fprintf(totar, "%s\n", DESC_FNAME);
+
+ if (Install)
+ fprintf(totar, "%s\n", INSTALL_FNAME);
+ if (DeInstall)
+ fprintf(totar, "%s\n", DEINSTALL_FNAME);
+ if (Require)
+ fprintf(totar, "%s\n", REQUIRE_FNAME);
+ if (Display)
+ fprintf(totar, "%s\n", DISPLAY_FNAME);
+ if (Mtree)
+ fprintf(totar, "%s\n", MTREE_FNAME);
+
+ for (p = plist->head; p; p = p->next) {
+ if (p->type == PLIST_FILE)
+ fprintf(totar, "%s\n", p->name);
+ else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
+ fprintf(totar, "-C\n%s\n", p->name);
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ }
+
+ fclose(totar);
+ wait(&ret);
+ /* assume either signal or bad exit is enough for us */
+ if (ret)
+ barf("tar command failed with code %d", ret);
+}
+
+static void
+sanity_check()
+{
+ if (!Comment)
+ barf("Required package comment string is missing (-c comment).");
+ if (!Desc)
+ barf("Required package description string is missing (-d desc).");
+ if (!Contents)
+ barf("Required package contents list is missing (-f [-]file).");
+}
+
+
+/* Clean up those things that would otherwise hang around */
+void
+cleanup(int sig)
+{
+ leave_playpen(home);
+}
diff --git a/usr.sbin/pkg_install/create/pkg_create.1 b/usr.sbin/pkg_install/create/pkg_create.1
new file mode 100644
index 00000000000..3715efdf0ad
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,381 @@
+.\" $OpenBSD: pkg_create.1,v 1.1 1996/06/04 07:56:06 niklas Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" 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.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_create.1
+.\"
+.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords,
+.\" added dependency tracking, etc.
+.\"
+.\" [jkh] Took John's changes back and made some additional extensions for
+.\" better integration with FreeBSD's new ports collection.
+.\"
+.Dd April 21, 1995
+.Dt pkg_create 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions.
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhv
+.Op Fl P Ar pkgs
+.Op Fl p Ar prefix
+.Op Fl f Ar contents
+.Op Fl i Ar iscript
+.Op Fl k Ar dscript
+.Op Fl r Ar rscript
+.Op Fl t Ar template
+.Op Fl X Ar excludefile
+.Op Fl D Ar displayfile
+.Op Fl m Ar mtreefile
+.Fl d Ar description
+.Fl f Ar packlist
+.Ar pkg-name
+.Sh DESCRIPTION
+The
+.Nm
+command is used to create packages that will subsequently be fed to
+one of the package extraction/info utilities. The input description
+and command line arguments for the creation of a package are not
+really meant to be human-generated, though it is easy enough to
+do so. It is more expected that you will use a front-end tool for
+the job rather than muddling through it yourself. Nonetheless, a short
+description of the input syntax is included in this document.
+.Sh OPTIONS
+The following command line options are supported.
+.Bl -tag -width indent
+.It Fl f Ar packinglist
+Fetch ``packing list'' for package from the file
+.Ar packinglist
+or
+.Cm stdin
+if
+.Ar packinglist
+is a
+.Cm -
+(dash).
+.Em "Mandatory."
+.It Fl c Ar [-]desc
+Fetch package ``one line description'' from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself. This string should also
+give some idea of which version of the product (if any) the package
+represents.
+.Em "Mandatory."
+.It Fl d Ar [-]desc
+Fetch long description for package from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+.Em "Mandatory."
+.It Fl Y
+Assume a default answer of `Yes' for any questions asked.
+.Em "Optional."
+.It Fl N
+Assume a default answer of `No' for any questions asked.
+.It Fl O
+Go into a `packing list Only' mode. This is a custom hack for the
+.Em "FreeBSD Ports Collection"
+and is used to do `fake pkg_add' operations when a port is installed.
+In such cases, it is necessary to know what the final, adjusted packing
+list will look like.
+.Em "Optional."
+.It Fl v
+Turns on verbose output.
+.Em "Optional."
+.It Fl h
+Forces tar to follow symbolic links, so that the files they point to
+are dumped, rather than the links themselves.
+.It Fl i Ar iscript
+Sets
+.Ar iscript
+to be the install procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+when the package is later installed.
+.Em "Optional."
+.It Fl P Ar pkgs
+Sets the initial package dependency list to
+.Ar pkgs.
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @pkgdep
+directives in the packing list (see PACKING LIST DETAILS section below).
+.Em "Optional."
+.It Fl p Ar prefix
+Sets
+.Ar prefix
+as the initial directory ``base'' to start from in selecting files for
+the package.
+.Em "Optional."
+.It Fl k Ar dscript
+Sets
+.Ar dscript
+to be the de-install procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+when the package is later (if ever) de-installed.
+.Em "Optional."
+.It Fl r Ar rscript
+Sets
+.Ar rscript
+to be the ``requirements'' procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+at installation/deinstallation time to determine whether or not
+installation/deinstallation should proceed.
+.Em "Optional."
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3 .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+ to fill in with a unique ID.
+.Em "Optional."
+.It Fl X Ar excludefile
+Pass
+.Ar excludefile
+as a
+.Fl exclude-from
+argument to
+.Cm tar
+when creating final package. See
+.Cm tar
+man page (or run
+.Cm tar
+with
+.Fl -help
+flag) for further information on using this flag.
+.It Fl D Ar displayfile
+Display the file (using
+.Xr more 1 )
+after installing the package. Useful for things like
+legal notices on almost-free software, etc.
+.It Fl m Ar mtreefile
+Run
+.Xr mtree 8
+with input from mtreefile before the package is installed.
+Mtree is invoked as
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix ,
+where
+.Pa prefix
+is the name of the first directory named by a
+.Cm @cwd
+directive.
+.El
+.Pp
+.Sh PACKING LIST DETAILS
+The ``packing list'' format (see
+.Fl f )
+is fairly simple, being
+nothing more than a single column of filenames to include in the
+package. However, since absolute pathnames are generally a bad idea
+for a package that could be installed potentially anywhere, there is
+another method of specifying where things are supposed to go
+and, optionally, what ownership and mode information they should be
+installed with. This is done by imbeding specialized command sequences
+in the packing list. Briefly described, these sequences are:
+.Bl -tag -width indent -compact
+.It Cm @cwd Ar directory
+Sets the internal directory pointer to point to
+.Ar directory .
+All subsequent filenames will be assumed relative to this directory.
+Note:
+.Cm @cd
+is also an alias for this command.
+.It Cm @srcdir Ar directory
+Sets the internal directory pointer for _creation only_ to
+.Ar directory .
+That is to say that it overrides
+.Cm @cwd
+for package creation but not extraction.
+.It Cm @exec Ar command
+Execute
+.Ar command
+as part of the unpacking process. If
+.Ar command
+contains a any of the following sequences somewhere in it, they will
+be expanded inline. For the following examples, assume that
+.Cm @cwd
+is set to
+.Pa /usr/local
+and the last extracted file was
+.Pa bin/emacs .
+.Bl -tag -width indent -compact
+.It Cm "%F"
+Expands to the last filename extracted (as specified), in the example case
+.Pa bin/emacs
+.It Cm "%D"
+Expands to the current directory prefix, as set with
+.Cm @cwd ,
+in the example case
+.Pa /usr/local .
+.It Cm "%B"
+Expands to the ``basename'' of the fully qualified filename, that
+is the current directory prefix, plus the last filespec, minus
+the trailing filename. In the example case, that would be
+.Pa /usr/local/bin .
+.It Cm "%f"
+Expands to the ``filename'' part of the fully qualified name, or
+the converse of
+.Cm %B ,
+being in the example case,
+.Pa emacs .
+.El
+.It Cm @unexec Ar command
+Execute
+.Ar command
+as part of the deinstallation process. Expansion of special
+.Cm %
+sequences is the same as for
+.Cm @exec .
+This command is not executed during the package add, as
+.Cm @exec
+is, but rather when the package is deleted. This is useful
+for deleting links and other ancillary files that were created
+as a result of adding the package, but not directly known to
+the package's table of contents (and hence not automatically
+removable). The advantage of using
+.Cm @unexec
+over a deinstallation script is that you can use the ``special
+sequence expansion'' to get at files regardless of where they've
+been potentially redirected (see
+.Fl p )
+.It Cm @mode Ar mode
+Sets default permission for all subsequently extracted files to
+.Ar mode .
+Format is the same as that used by the
+.Cm chmod
+command (well, considering that it's later handed off to it, that's
+no surprise). Use without an arg to set back to default (extraction)
+permissions.
+.It Cm @owner Ar user
+Sets default ownership for all subsequently extracted files to
+.Ar user .
+Use without an arg to set back to default (extraction)
+ownership.
+.It Cm @group Ar group
+Sets default group ownership for all subsequently extracted files to
+.Ar group .
+Use without an arg to set back to default (extraction)
+group ownership.
+.It Cm @comment Ar string
+Imbed a comment in the packing list. Useful in
+trying to document some particularly hairy sequence that
+may trip someone up later.
+.It Cm @ignore
+Used internally to tell extraction to ignore the next file (don't
+copy it anywhere), as it's used for some special purpose.
+.It Cm @ignore_inst
+Similar to
+.Cm @ignore ,
+but the ignoring of the next file is delayed one evaluation cycle. This
+makes it possible to use this directive in the
+.Ar packinglist
+file, so you can pack a
+specialized datafile in with a distribution for your install script (or
+something) yet have the installer ignore it.
+.It Cm @name Ar name
+Sets the name of the package. This is mandatory and is usually
+put at the top. This name is potentially different than the name of
+the file it came in, and is used when keeping track of the package
+for later deinstallation. Note that
+.Nm
+will derive this field from the package name and add it automatically
+if none is given.
+.It Cm @dirrm Ar name
+Declare directory
+.Pa name
+to be deleted at deinstall time. By default, directories created by a
+package installation are not deleted when the package is deinstalled;
+this provides an explicit directory cleanup method. This directive
+should appear at the end of the package list. If more than one
+.Cm @dirrm
+directives are used, the directories are removed in the order specified.
+The
+.Pa name
+directory will not be removed unless it is empty.
+.It Cm @mtree Ar name
+Declare
+.Pa name
+as an
+.Xr mtree 8
+input file to be used at install time (see
+.Fl m
+above). Only the first
+.Cm @mtree
+directive is honored.
+.It Cm @display Ar name
+Declare
+.Pa name
+as the file to be displayed at install time (see
+.Fl D
+above).
+.It Cm @pkgdep Ar pkgname
+Declares a dependency on the
+.Ar pkgname
+package. The
+.Ar pkgname
+package must be installed before this package may be
+installed, and this package must be deinstalled before the
+.Ar pkgname
+package is deinstalled. Multiple
+.Cm @pkgdep
+directives may be used if hte package depends on multiple other packages.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr sysconf 3 .
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD.
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.Sh BUGS
+Hard links between files in a distribution must be bracketed by
+.Cm @cwd
+directives in order to be preserved as hard links when the package is
+extracted. They additionally must not end up being split between
+.Cm tar
+invocations due to exec argument-space limitations (this depends on the
+value returned by
+.Fn sysconf _SC_ARG_MAX ) .
+.Pp
+Sure to be others.
diff --git a/usr.sbin/pkg_install/create/pl.c b/usr.sbin/pkg_install/create/pl.c
new file mode 100644
index 00000000000..39101035274
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,217 @@
+# $OpenBSD: pl.c,v 1.1 1996/06/04 07:56:06 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: pl.c,v 1.1 1996/06/04 07:56:06 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for dealing with the packing list.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+#include <errno.h>
+
+/* Check a list for files that require preconversion */
+void
+check_list(char *home, Package *pkg)
+{
+ char cmd[FILENAME_MAX];
+ char name[FILENAME_MAX];
+ char *where = home;
+ char *there = NULL;
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_SRC) {
+ there = p->name;
+ }
+ else if (p->type == PLIST_FILE) {
+ cmd[0] = '\0';
+ sprintf(name, "%s/%s", there ? there : where, p->name);
+
+ if (*cmd) {
+ if (Verbose)
+ printf("Uncompressing-> %s\n", cmd);
+ if (system(cmd))
+ barf("%s failed!", cmd);
+ nuke_suffix(p->name);
+ }
+ }
+ p = p->next;
+ }
+}
+
+static int
+trylink(const char *from, const char *to)
+{
+ if (link(from, to) == 0)
+ return 0;
+ if (errno == ENOENT) {
+ /* try making the container directory */
+ char *cp = strrchr(to, '/');
+ if (cp)
+ vsystem("mkdir -p %.*s", cp - to,
+ to);
+ return link(from, to);
+ }
+ return -1;
+}
+
+#define STARTSTRING "tar cf -"
+#define TOOBIG(str) strlen(str) + 6 + strlen(home) + where_count > maxargs
+#define PUSHOUT() /* push out string */ \
+ if (where_count > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xpf -"); \
+ if (system(where_args)) \
+ barf("can't invoke tar pipeline"); \
+ memset(where_args, 0, maxargs); \
+ last_chdir = NULL; \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ }
+
+/*
+ * Copy unmarked files in packing list to playpen - marked files
+ * have already been copied in an earlier pass through the list.
+ */
+void
+copy_plist(char *home, Package *plist)
+{
+ PackingList p = plist->head;
+ char *where = home;
+ char *there = NULL, *mythere;
+ char *where_args, *last_chdir, *root = "/";
+ int maxargs, where_count = 0, add_count;
+ struct stat stb;
+ dev_t curdir;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 64; /* some slop for the tar cmd text,
+ and sh -c */
+ where_args = malloc(maxargs);
+ if (!where_args)
+ barf("can't get argument list space");
+
+ memset(where_args, 0, maxargs);
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ last_chdir = 0;
+
+ if (stat(".", &stb) == 0)
+ curdir = stb.st_dev;
+ else
+ curdir = (dev_t) -1; /* It's ok if this is a valid dev_t;
+ this is just a hint for an
+ optimization. */
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_SRC)
+ there = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_FILE && !p->marked) {
+ char fn[FILENAME_MAX];
+
+
+ /* First, look for it in the "home" dir */
+ sprintf(fn, "%s/%s", home, p->name);
+ if (fexists(fn)) {
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /* if we can link it to the playpen, that avoids a copy
+ and saves time. */
+ if (p->name[0] != '/') {
+ /* don't link abspn stuff--it doesn't come from
+ local dir! */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ }
+ if (TOOBIG(fn)) {
+ PUSHOUT();
+ }
+ if (p->name[0] == '/') {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s %s",
+ last_chdir == root ? "" : "-C /",
+ p->name);
+ last_chdir = root;
+ } else {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s%s %s",
+ last_chdir == home ? "" : "-C ",
+ last_chdir == home ? "" : home,
+ p->name);
+ last_chdir = home;
+ }
+ if (add_count > maxargs - where_count)
+ barf("oops, miscounted strings!");
+ where_count += add_count;
+ }
+ /*
+ * Otherwise, try along the actual extraction path..
+ */
+ else {
+ if (p->name[0] == '/')
+ mythere = root;
+ else mythere = there;
+ sprintf(fn, "%s/%s", mythere ? mythere : where, p->name);
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /* if we can link it to the playpen, that avoids a copy
+ and saves time. */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ if (TOOBIG(p->name)) {
+ PUSHOUT();
+ }
+ if (last_chdir == (mythere ? mythere : where))
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s", p->name);
+ else
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " -C %s %s",
+ mythere ? mythere : where,
+ p->name);
+ if (add_count > maxargs - where_count)
+ barf("oops, miscounted strings!");
+ where_count += add_count;
+ last_chdir = (mythere ? mythere : where);
+ }
+ }
+ p = p->next;
+ }
+ PUSHOUT();
+ free(where_args);
+}
diff --git a/usr.sbin/pkg_install/delete/Makefile b/usr.sbin/pkg_install/delete/Makefile
new file mode 100644
index 00000000000..b612621f964
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:07 niklas Exp $
+PROG= pkg_delete
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD+= -L${.CURDIR}/../lib/obj -linstall
+DPADD+= ${.CURDIR}/../lib/obj/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+SRCS= main.c perform.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h
new file mode 100644
index 00000000000..e952ad1b453
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,33 @@
+/* $OpenBSD: delete.h,v 1.1 1996/06/04 07:56:07 niklas Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the delete command.
+ *
+ */
+
+#ifndef _INST_DELETE_H_INCLUDE
+#define _INST_DELETE_H_INCLUDE
+
+extern char *Prefix;
+extern Boolean NoDeInstall;
+extern Boolean CleanDirs;
+extern Boolean Force;
+extern char *Directory;
+extern char *PkgName;
+
+#endif /* _INST_DELETE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c
new file mode 100644
index 00000000000..ef64951d899
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,124 @@
+# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:07 niklas Exp $
+#ifndef lint
+static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:07 niklas Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the delete module.
+ *
+ */
+
+#include "lib.h"
+#include "delete.h"
+
+static char Options[] = "hvDdnfp:";
+
+char *Prefix = NULL;
+Boolean NoDeInstall = FALSE;
+Boolean CleanDirs = FALSE;
+Boolean Force = FALSE;
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **pkgs, **start;
+ char *prog_name = argv[0];
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != EOF)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'D':
+ NoDeInstall = TRUE;
+ break;
+
+ case 'd':
+ CleanDirs = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage(prog_name, NULL);
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start)
+ usage(prog_name, "Missing package name(s)");
+ *pkgs = NULL;
+ if (!Fake && getuid() != 0)
+ errx(1, "You must be root to delete packages.");
+ if ((error = pkg_perform(start)) != NULL) {
+ if (Verbose)
+ fprintf(stderr, "%d package deletion(s) failed.\n", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+void
+usage(const char *name, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt) {
+ fprintf(stderr, "%s: ", name);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n\n");
+ }
+ va_end(args);
+ fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name);
+ fprintf(stderr, "Where args are one or more of:\n\n");
+ fprintf(stderr, "-v verbose\n");
+ fprintf(stderr, "-p arg override prefix with arg\n");
+ fprintf(stderr, "-d delete empty directories when deinstalling\n");
+ fprintf(stderr, "-f force delete even if dependencies exist\n");
+ fprintf(stderr, " or deinstall/requirement checks fail\n");
+ fprintf(stderr, "-D don't execute pkg de-install script, if any\n");
+ fprintf(stderr, "-n don't actually de-install, just show steps\n");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c
new file mode 100644
index 00000000000..299291ddd14
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.c
@@ -0,0 +1,216 @@
+# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:08 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:08 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the delete module.
+ *
+ */
+
+#include "lib.h"
+#include "delete.h"
+
+static int pkg_do(char *);
+static void sanity_check(char *);
+static void undepend(PackingList, char *);
+static char LogDir[FILENAME_MAX];
+
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ return err_cnt;
+}
+
+static Package Plist;
+
+/* This is seriously ugly code following. Written very fast! */
+static int
+pkg_do(char *pkg)
+{
+ FILE *cfile;
+ char home[FILENAME_MAX];
+ PackingList p;
+ char *tmp;
+
+ /* Reset some state */
+ if (Plist.head)
+ free_plist(&Plist);
+
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+ if (!fexists(LogDir)) {
+ whinge("No such package '%s' installed.", pkg);
+ return 1;
+ }
+ if (!getcwd(home, FILENAME_MAX))
+ barf("Unable to get current working directory!");
+ if (chdir(LogDir) == FAIL) {
+ whinge("Unable to change directory to %s! Deinstall failed.", LogDir);
+ return 1;
+ }
+ if (!isemptyfile(REQUIRED_BY_FNAME)) {
+ char buf[512];
+ whinge("Package `%s' is required by these other packages", pkg);
+ whinge("and may not be deinstalled%s:", Force ? " (but I'll delete it anyway)" : "" );
+ cfile = fopen(REQUIRED_BY_FNAME, "r");
+ if (cfile) {
+ while (fgets(buf, sizeof(buf), cfile))
+ fprintf(stderr, "%s", buf);
+ fclose(cfile);
+ } else
+ whinge("cannot open requirements file `%s'", REQUIRED_BY_FNAME);
+ if (!Force)
+ return 1;
+ }
+ sanity_check(LogDir);
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ whinge("Unable to open '%s' file.", CONTENTS_FNAME);
+ return 1;
+ }
+ /* If we have a prefix, add it now */
+ if (Prefix)
+ add_plist(&Plist, PLIST_CWD, Prefix);
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (p)
+ setenv(PKG_PREFIX_VNAME, p->name, 1);
+ else
+ unsetenv(PKG_PREFIX_VNAME);
+ if (fexists(REQUIRE_FNAME)) {
+ if (Verbose)
+ printf("Executing 'require' script.\n");
+ vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
+ whinge("Package %s fails requirements %s", pkg,
+ Force ? "." : "- not deleted.");
+ if (!Force)
+ return 1;
+ }
+ }
+ if (!NoDeInstall && fexists(DEINSTALL_FNAME)) {
+ if (Fake)
+ printf("Would execute de-install script at this point.\n");
+ else {
+ vsystem("chmod +x %s", DEINSTALL_FNAME); /* make sure */
+ if (vsystem("./%s %s DEINSTALL", DEINSTALL_FNAME, pkg)) {
+ whinge("De-Install script returned error status.");
+ if (!Force)
+ return 1;
+ }
+ }
+ }
+ if (chdir(home) == FAIL)
+ barf("Toto! This doesn't look like Kansas anymore!");
+ if (!Fake) {
+ /* Some packages aren't packed right, so we need to just ignore delete_package()'s status. Ugh! :-( */
+ if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
+ warn("Couldn't entirely delete package (perhaps the packing list is\n"
+ "incorrectly specified?)\n");
+ if (vsystem("%s -r %s", REMOVE_CMD, LogDir)) {
+ whinge("Couldn't remove log entry in %s, de-install failed.", LogDir);
+ return 1;
+ }
+ }
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to remove dependency on package `%s'\n",
+ p->name);
+ if (!Fake)
+ undepend(p, pkg);
+ }
+ return 0;
+}
+
+static void
+sanity_check(char *pkg)
+{
+ if (!fexists(CONTENTS_FNAME))
+ barf("Installed package %s has no %s file!", pkg, CONTENTS_FNAME);
+}
+
+void
+cleanup(int sig)
+{
+ /* Nothing to do */
+}
+
+static void
+undepend(PackingList p, char *pkgname)
+{
+ char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
+ char fbuf[FILENAME_MAX];
+ FILE *fp, *fpwr;
+ char *tmp;
+ int s;
+
+ sprintf(fname, "%s/%s/%s",
+ (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ p->name, REQUIRED_BY_FNAME);
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ whinge("Couldn't open dependency file `%s'", fname);
+ return;
+ }
+ sprintf(ftmp, "%s.XXXXXX", fname);
+ s = mkstemp(ftmp);
+ if (s == -1) {
+ fclose(fp);
+ whinge("Couldn't open temp file `%s'", ftmp);
+ return;
+ }
+ fpwr = fdopen(s, "w");
+ if (fpwr == NULL) {
+ close(s);
+ fclose(fp);
+ whinge("Couldn't fdopen temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+ if (fbuf[strlen(fbuf)-1] == '\n')
+ fbuf[strlen(fbuf)-1] = '\0';
+ if (strcmp(fbuf, pkgname)) /* no match */
+ fputs(fbuf, fpwr), putc('\n', fpwr);
+ }
+ (void) fclose(fp);
+ if (fchmod(s, 0644) == FAIL) {
+ whinge("Error changing permission of temp file `%s'", ftmp);
+ fclose(fpwr);
+ remove(ftmp);
+ return;
+ }
+ if (fclose(fpwr) == EOF) {
+ whinge("Error closing temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ if (rename(ftmp, fname) == -1)
+ warn("Error renaming `%s' to `%s'", ftmp, fname);
+ remove(ftmp); /* just in case */
+ return;
+}
diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1
new file mode 100644
index 00000000000..3583f114769
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,185 @@
+.\" $OpenBSD: pkg_delete.1,v 1.1 1996/06/04 07:56:08 niklas Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" 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.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_delete.1
+.\"
+.Dd November 25, 1994
+.Dt pkg_delete 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions.
+.Sh SYNOPSIS
+.Nm
+.Op Fl vDdnf
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to delete packages that have been previously installed
+with the
+.Xr pkg_add 1
+command.
+
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs provided by a package file,
+your system may be susceptible to ``trojan horses'' or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, examine all
+the package control files in the package record directory (
+.Pa /var/db/pkg/<pkg-name>/ ).
+Pay particular
+attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files,
+and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the installed package control files.
+.Ef
+
+.Sh OPTIONS
+The following command line options are supported.
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are deinstalled.
+.It Fl v
+Turns on verbose output.
+.Em "Optional."
+.It Fl D
+If a deinstallation script exists for a given package, do not execute it.
+.Em "Optional."
+.It Fl n
+Don't actually deinstall a package, just report the steps that
+would be taken if it were.
+.Em "Optional."
+.It Fl p Ar prefix
+Sets
+.Ar prefix
+as the directory in which to delete files from any installed packages
+which do not explicitly set theirs. For most packages, the prefix will
+be set automatically to the installed location by
+.Xr pkg_add 1 .
+.Em "Optional."
+.It Fl d
+Remove empty directories created by file cleanup. By default, only
+files/directories explicitly listed in a package's contents (either as
+normal files/directories or with the
+.Cm @dirrm
+directive) will be removed at deinstallation time. This option tells
+.Nm
+to also remove any directories that were emptied as a result of removing
+the package.
+.Em "Optional."
+.It Fl f
+Force removal of the package, even if a dependency is recorded or the
+deinstall or require script fails.
+.Em "Optional."
+.El
+
+.Pp
+.Sh TECHNICAL DETAILS
+.Nm
+does pretty much what it says. It examines installed package records in
+.Pa /var/db/pkg/<pkg-name> ,
+deletes the package contents, and finally removes the package records.
+.Pp
+If a package is required by other installed packages,
+.Nm
+will list those dependent packages and refuse to delete the package
+(unless the
+.Fl f
+option is given).
+.Pp
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ),
+then this is executed first as
+.Bd -filled -offset indent -compact
+.Cm require
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+(where
+.Ar pkg-name
+is the name of the package in question and
+.I DEINSTALL
+is a keyword denoting that this is a deinstallation)
+to see whether or not deinstallation should continue. A non-zero exit
+status means no, unless the
+.Fl f
+option is specified.
+.Pp
+If a
+.Cm deinstall
+script exists for the package, it is executed before any files are removed.
+It is this script's responsibility to clean up any additional messy details
+around the package's installation, since all
+.Nm
+knows how to do is delete the files created in the original distribution.
+The
+.Nm deinstall
+script is called as:
+.Bd -filled -offset indent -compact
+.Cm deinstall
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+Passing the keyword
+.Ar DEINSTALL
+lets you potentially write only one program/script that handles all
+aspects of installation and deletion.
+.Pp
+All scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above). This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might have changed it by specifying the
+.Fl p
+option when running
+.Nm
+or
+.Cm pkg_add .
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8 .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/Makefile b/usr.sbin/pkg_install/info/Makefile
new file mode 100644
index 00000000000..854b0e4a9d3
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,15 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:09 niklas Exp $
+PROG= pkg_info
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.CURDIR}/../lib/obj)
+LDADD+= -L${.CURDIR}/../lib/obj -linstall
+DPADD+= ${.CURDIR}/../lib/obj/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+SRCS= main.c perform.c show.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h
new file mode 100644
index 00000000000..b8ad23bc0e1
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,59 @@
+/* $OpenBSD: info.h,v 1.1 1996/06/04 07:56:09 niklas Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 23 August 1993
+ *
+ * Include and define various things wanted by the info command.
+ *
+ */
+
+#ifndef _INST_INFO_H_INCLUDE
+#define _INST_INFO_H_INCLUDE
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 60
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 0x0001
+#define SHOW_DESC 0x0002
+#define SHOW_PLIST 0x0004
+#define SHOW_INSTALL 0x0008
+#define SHOW_DEINSTALL 0x0010
+#define SHOW_REQUIRE 0x0020
+#define SHOW_PREFIX 0x0040
+#define SHOW_INDEX 0x0080
+#define SHOW_FILES 0x0100
+#define SHOW_DISPLAY 0x0200
+#define SHOW_REQBY 0x0400
+#define SHOW_MTREE 0x0800
+
+extern int Flags;
+extern Boolean AllInstalled;
+extern Boolean Quiet;
+extern char *InfoPrefix;
+extern char PlayPen[];
+extern char *CheckPkg;
+
+extern void show_file(char *, char *);
+extern void show_plist(char *, Package *, plist_t);
+extern void show_files(char *, Package *);
+extern void show_index(char *, char *);
+
+#endif /* _INST_INFO_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c
new file mode 100644
index 00000000000..0564276ef86
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,182 @@
+# $OpenBSD: main.c,v 1.1 1996/06/04 07:56:09 niklas Exp $
+#ifndef lint
+static char *rcsid = "$OpenBSD: main.c,v 1.1 1996/06/04 07:56:09 niklas Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+static char Options[] = "acdDe:fikrRpLqImvhl:";
+
+int Flags = 0;
+Boolean AllInstalled = FALSE;
+Boolean Quiet = FALSE;
+char *InfoPrefix = "";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+ char *prog_name = argv[0];
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != EOF)
+ switch(ch) {
+ case 'a':
+ AllInstalled = TRUE;
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE;
+ break;
+
+ case 'I':
+ Flags |= SHOW_INDEX;
+ break;
+
+ case 'p':
+ Flags |= SHOW_PREFIX;
+ break;
+
+ case 'c':
+ Flags |= SHOW_COMMENT;
+ break;
+
+ case 'd':
+ Flags |= SHOW_DESC;
+ break;
+
+ case 'D':
+ Flags |= SHOW_DISPLAY;
+ break;
+
+ case 'f':
+ Flags |= SHOW_PLIST;
+ break;
+
+ case 'i':
+ Flags |= SHOW_INSTALL;
+ break;
+
+ case 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'r':
+ Flags |= SHOW_REQUIRE;
+ break;
+
+ case 'R':
+ Flags |= SHOW_REQBY;
+ break;
+
+ case 'L':
+ Flags |= SHOW_FILES;
+ break;
+
+ case 'm':
+ Flags |= SHOW_MTREE;
+ break;
+
+ case 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 't':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage(prog_name, NULL);
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Set some reasonable defaults */
+ if (!Flags)
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start && !AllInstalled && !CheckPkg)
+ usage(prog_name, "Missing package name(s)");
+ *pkgs = NULL;
+ return pkg_perform(start);
+}
+
+void
+usage(const char *name, const char *fmt, ...)
+{
+ va_list args;
+
+ va_start(args, fmt);
+ if (fmt) {
+ fprintf(stderr, "%s: ", name);
+ vfprintf(stderr, fmt, args);
+ fprintf(stderr, "\n\n");
+ }
+ va_end(args);
+ fprintf(stderr, "Usage: %s [args] pkg [ .. pkg ]\n", name);
+ fprintf(stderr, "Where args are one or more of:\n\n");
+ fprintf(stderr, "-a show all installed packages (if any)\n");
+ fprintf(stderr, "-I print 'index' of packages\n");
+ fprintf(stderr, "-c print `one line comment'\n");
+ fprintf(stderr, "-d print description\n");
+ fprintf(stderr, "-D print install notice\n");
+ fprintf(stderr, "-f show packing list\n");
+ fprintf(stderr, "-i show install script\n");
+ fprintf(stderr, "-k show deinstall script\n");
+ fprintf(stderr, "-r show requirements script\n");
+ fprintf(stderr, "-R show packages depending on this package\n");
+ fprintf(stderr, "-p show prefix\n");
+ fprintf(stderr, "-l <str> Prefix each info catagory with <str>\n");
+ fprintf(stderr, "-L show intalled files\n");
+ fprintf(stderr, "-q minimal output (``quiet'' mode)\n");
+ fprintf(stderr, "-v show all information\n");
+ fprintf(stderr, "-t temp use temp as template for mktemp()\n");
+ fprintf(stderr, "-e pkg returns 0 if pkg is installed, 1 otherwise\n");
+ fprintf(stderr, "\n[no args = -c -d -R]\n");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c
new file mode 100644
index 00000000000..84900147a91
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,211 @@
+# $OpenBSD: perform.c,v 1.1 1996/06/04 07:56:10 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: perform.c,v 1.1 1996/06/04 07:56:10 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * This is the main body of the info module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+#include <signal.h>
+
+static int pkg_do(char *);
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+ char *tmp;
+
+ signal(SIGINT, cleanup);
+
+ tmp = getenv(PKG_DBDIR);
+ if (!tmp)
+ tmp = DEF_LOG_DIR;
+ /* Overriding action? */
+ if (AllInstalled || CheckPkg) {
+ if (isdir(tmp)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(tmp);
+ if (dirp) {
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ if (CheckPkg) {
+ if (!strcmp(dp->d_name, CheckPkg))
+ return 0;
+ }
+ else
+ err_cnt += pkg_do(dp->d_name);
+ }
+ }
+ (void)closedir(dirp);
+ if (CheckPkg)
+ return 1;
+ }
+ else
+ ++err_cnt;
+ } else if (CheckPkg)
+ return 1; /* no dir -> not installed! */
+
+ }
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ return err_cnt;
+}
+
+static char *Home;
+
+static int
+pkg_do(char *pkg)
+{
+ Boolean installed = FALSE, isTMP = FALSE;
+ char log_dir[FILENAME_MAX];
+ char fname[FILENAME_MAX];
+ Package plist;
+ FILE *fp;
+ struct stat sb;
+ char *cp = NULL;
+ int code = 0;
+
+ if (isURL(pkg)) {
+ if ((cp = fileGetURL(NULL, pkg)) != NULL) {
+ strcpy(fname, cp);
+ isTMP = TRUE;
+ }
+ }
+ else if (fexists(pkg) && isfile(pkg)) {
+ int len;
+
+ if (*pkg != '/') {
+ if (!getcwd(fname, FILENAME_MAX))
+ upchuck("getcwd");
+ len = strlen(fname);
+ snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg);
+ }
+ else
+ strcpy(fname, pkg);
+ cp = fname;
+ }
+ else {
+ if ((cp = fileFindByPath(NULL, pkg)) != NULL)
+ strncpy(fname, cp, FILENAME_MAX);
+ }
+ if (cp) {
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, but we're only unpacking the + files so
+ * be very optimistic.
+ */
+ if (stat(fname, &sb) == FAIL) {
+ whinge("Can't stat package file '%s'.", fname);
+ code = 1;
+ goto bail;
+ }
+ Home = make_playpen(PlayPen, sb.st_size / 2);
+ if (unpack(fname, "+*")) {
+ whinge("Error during unpacking, no info for '%s' available.", pkg);
+ code = 1;
+ goto bail;
+ }
+ }
+ /* It's not an ininstalled package, try and find it among the installed */
+ else {
+ char *tmp;
+
+ sprintf(log_dir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+ if (!fexists(log_dir)) {
+ whinge("Can't find package `%s' installed or in a file!", pkg);
+ return 1;
+ }
+ if (chdir(log_dir) == FAIL) {
+ whinge("Can't change directory to '%s'!", log_dir);
+ return 1;
+ }
+ installed = TRUE;
+ }
+
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ whinge("Unable to open %s file.", CONTENTS_FNAME);
+ code = 1;
+ goto bail;
+ }
+ /* If we have a prefix, add it now */
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * Index is special info type that has to override all others to make
+ * any sense.
+ */
+ if (Flags & SHOW_INDEX) {
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
+ show_index(tmp, COMMENT_FNAME);
+ }
+ else {
+ /* Start showing the package contents */
+ if (!Quiet)
+ printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
+ if (Flags & SHOW_COMMENT)
+ show_file("Comment:\n", COMMENT_FNAME);
+ if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
+ show_file("Required by:\n", REQUIRED_BY_FNAME);
+ if (Flags & SHOW_DESC)
+ show_file("Description:\n", DESC_FNAME);
+ if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
+ show_file("Install notice:\n", DISPLAY_FNAME);
+ if (Flags & SHOW_PLIST)
+ show_plist("Packing list:\n", &plist, (plist_t)-1);
+ if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
+ show_file("Install script:\n", INSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
+ show_file("De-Install script:\n", DEINSTALL_FNAME);
+ if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
+ show_file("mtree file:\n", MTREE_FNAME);
+ if (Flags & SHOW_PREFIX)
+ show_plist("Prefix(s):\n", &plist, PLIST_CWD);
+ if (Flags & SHOW_FILES)
+ show_files("Files:\n", &plist);
+ if (!Quiet)
+ puts(InfoPrefix);
+ }
+ free_plist(&plist);
+ bail:
+ leave_playpen(Home);
+ if (isTMP)
+ unlink(fname);
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ leave_playpen(Home);
+}
diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1
new file mode 100644
index 00000000000..62c619db526
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,136 @@
+.\" $OpenBSD: pkg_info.1,v 1.1 1996/06/04 07:56:10 niklas Exp $
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" 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.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_info.1
+.\"
+.Dd November 25, 1994
+.Dt pkg_info 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages.
+.Sh SYNOPSIS
+.Nm pkg_info
+.Op Fl cdDikrRpLqImv
+.Op Fl e Ar package
+.Op Fl l Ar prefix
+.Ar pkg-name [pkg-name ...]
+.Nm pkg_info
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, either packed up in
+files or already installed on the system
+with the
+.Xr pkg_create 1
+command.
+.Sh OPTIONS
+The following command line options are supported.
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are described. A package name may either be the name of
+an installed package, the pathname to a package distribution file or a
+URL to an ftp available package.
+.It Fl a
+Show all currently installed packages.
+.It Fl v
+Turns on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q
+Be ``quiet'' in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl c
+Show the comment (one liner) field for each package.
+.It Fl d
+Show the long description field for each package.
+.It Fl D
+Show the install-message file for each package.
+.It Fl f
+Show the packing list instructions for each package.
+.It Fl i
+Show the install script (if any) for each package.
+.It Fl k
+Show the de-install script (if any) for each package.
+.It Fl r
+Show the requirements script (if any) for each package.
+.It Fl m
+Show the mtree file (if any) for each package.
+.It Fl L
+Show the files within each package. This is different from just
+viewing the packing list, since full pathnames for everything
+are generated.
+.It Fl e Ar pkg-name
+If the package identified by
+.Ar pkg-name
+is currently installed, return 0, otherwise return 1. This option
+allows you to easily test for the presence of another (perhaps
+prerequisite) package from a script.
+.It Fl l Ar str
+Prefix each information catagory header (see
+.Fl q )
+shown with
+.Ar str .
+This is primarily of use to front-end programs who want to request a
+lot of different information fields at once for a package, but don't
+necessary want the output intermingled in such a way that they can't
+organize it. This lets you add a special token to the start of
+each field.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a ``staging area.''
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Bd -filled -offset indent -compact
+Note: This should really not be necessary with pkg_info,
+since very little information is extracted from each package
+and one would have to have a very small
+.Pa /tmp
+indeed to overflow it.
+.Ed
+.Sh TECHNICAL DETAILS
+Package info is either extracted from package files named on the
+command line, or from already installed package information
+in
+.Pa /var/db/pkg/<pkg-name> .
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8 .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c
new file mode 100644
index 00000000000..3d38df6e20f
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,195 @@
+# $OpenBSD: show.c,v 1.1 1996/06/04 07:56:10 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: show.c,v 1.1 1996/06/04 07:56:10 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * Various display routines for the info module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+void
+show_file(char *title, char *fname)
+{
+ FILE *fp;
+ char line[1024];
+ int n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp)
+ printf("ERROR: show_file: Can't open '%s' for reading!\n", fname);
+ else {
+ while (n = fread(line, 1, 1024, fp))
+ fwrite(line, 1, n, stdout);
+ fclose(fp);
+ }
+ printf("\n"); /* just in case */
+}
+
+void
+show_index(char *title, char *fname)
+{
+ FILE *fp;
+ char line[MAXINDEXSIZE+2];
+ int i,n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ whinge("show_file: Can't open '%s' for reading.", fname);
+ return;
+ }
+ if(fgets(line, MAXINDEXSIZE+1, fp)) {
+ if(line[MAXINDEXSIZE-1] != '\n')
+ line[MAXINDEXSIZE] = '\n';
+ line[MAXINDEXSIZE+1] = 0;
+ fputs(line, stdout);
+ }
+ fclose(fp);
+}
+
+/* Show a packing list item type. If type is -1, show all */
+void
+show_plist(char *title, Package *plist, plist_t type)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ if (p->type != type && type != -1) {
+ p = p->next;
+ continue;
+ }
+ switch(p->type) {
+ case PLIST_FILE:
+ if (ign) {
+ printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name);
+ ign = FALSE;
+ }
+ else
+ printf(Quiet ? "%s\n" : "File: %s\n", p->name);
+ break;
+
+ case PLIST_CWD:
+ printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", p->name);
+ break;
+
+ case PLIST_SRC:
+ printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name);
+ break;
+
+ case PLIST_CMD:
+ printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_CHMOD:
+ printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHOWN:
+ printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHGRP:
+ printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_COMMENT:
+ printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name);
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ case PLIST_IGNORE_INST:
+ printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" :
+ "\tIgnore next file installation directive (doesn't belong)\n");
+ ign = TRUE;
+ break;
+
+ case PLIST_NAME:
+ printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name);
+ break;
+
+ case PLIST_DISPLAY:
+ printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name);
+ break;
+
+ case PLIST_PKGDEP:
+ printf(Quiet ? "@pkgdep %s\n" : "\tPackage depends on: %s\n", p->name);
+ break;
+
+ case PLIST_MTREE:
+ printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name);
+ break;
+
+ case PLIST_DIR_RM:
+ printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name);
+ break;
+
+ default:
+ barf("Unknown command type %d (%s)\n", p->type, p->name);
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Show all files in the packing list (except ignored ones) */
+void
+show_files(char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ char *dir = ".";
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ switch(p->type) {
+ case PLIST_FILE:
+ if (!ign)
+ printf("%s/%s\n", dir, p->name);
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+ }
+ p = p->next;
+ }
+}
diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile
new file mode 100644
index 00000000000..ff1e3e686e9
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,11 @@
+# $OpenBSD: Makefile,v 1.1 1996/06/04 07:56:11 niklas Exp $
+LIB= install
+SRCS= file.c ftp.c msg.c plist.c str.c exec.c global.c pen.c
+CFLAGS+= ${DEBUG}
+NOPROFILE= yes
+NOPIC= yes
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
new file mode 100644
index 00000000000..6f10c3ce944
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -0,0 +1,61 @@
+# $OpenBSD: exec.c,v 1.1 1996/06/04 07:56:11 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: exec.c,v 1.1 1996/06/04 07:56:11 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous system routines.
+ *
+ */
+
+#include "lib.h"
+
+/*
+ * Unusual system() substitute. Accepts format string and args,
+ * builds and executes command. Returns exit code.
+ */
+
+int
+vsystem(const char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+ int ret, maxargs;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = malloc(maxargs);
+ if (!cmd) {
+ whinge("vsystem can't alloc arg space");
+ return 1;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ whinge("vsystem args are too long");
+ return 1;
+ }
+#ifdef DEBUG
+printf("Executing %s\n", cmd);
+#endif
+ ret = system(cmd);
+ va_end(args);
+ free(cmd);
+ return ret;
+}
+
diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c
new file mode 100644
index 00000000000..d37af1e8a07
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,516 @@
+# $OpenBSD: file.c,v 1.1 1996/06/04 07:56:11 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: file.c,v 1.1 1996/06/04 07:56:11 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include "lib.h"
+#include "ftp.h"
+#include <pwd.h>
+#include <time.h>
+
+/* Quick check to see if a file exists */
+Boolean
+fexists(char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if something is a directory */
+Boolean
+isdir(char *fname)
+{
+ struct stat sb;
+
+ if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Check to see if file is a dir, and is empty */
+Boolean
+isemptydir(char *fname)
+{
+ if (isdir(fname)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(fname);
+ if (!dirp)
+ return FALSE; /* no perms, leave it alone */
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ closedir(dirp);
+ return FALSE;
+ }
+ }
+ (void)closedir(dirp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+Boolean
+isfile(char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/* Check to see if file is a file and is empty. If nonexistent or not
+ a file, say "it's empty", otherwise return TRUE if zero sized. */
+Boolean
+isemptyfile(char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
+ if (sb.st_size != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Returns TRUE if file is a URL specification */
+Boolean
+isURL(char *fname)
+{
+ /*
+ * I'm sure there are other types of URL specifications that I could
+ * also be looking for here, but for now I'll just be happy to get ftp
+ * working.
+ */
+ if (!fname)
+ return FALSE;
+ while (isspace(*fname))
+ ++fname;
+ if (!strncmp(fname, "ftp://", 6))
+ return TRUE;
+ return FALSE;
+}
+
+/* Returns the host part of a URL */
+char *
+fileURLHost(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if (ret = where) {
+ while (*fname && *fname != '/' && max--)
+ *where++ = *fname++;
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ ret = fname;
+ while (*fname && *fname != '/')
+ ++fname;
+ *fname = '\0';
+ return ret;
+}
+
+/* Returns the filename part of a URL */
+char *
+fileURLFilename(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if (ret = where) {
+ while (*fname && *fname != '/')
+ ++fname;
+ if (*fname == '/') {
+ while (*fname && max--)
+ *where++ = *fname++;
+ }
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ while (*fname && *fname != '/')
+ ++fname;
+ return fname;
+}
+
+#define HOSTNAME_MAX 64
+/*
+ * Try and fetch a file by URL, returning the directory name for where
+ * it's unpacked, if successful.
+ */
+char *
+fileGetURL(char *base, char *spec)
+{
+ char host[HOSTNAME_MAX], file[FILENAME_MAX], dir[FILENAME_MAX];
+ char pword[HOSTNAME_MAX + 40], *uname, *cp, *rp, *tmp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ struct passwd *pw;
+ FTP_t ftp;
+ pid_t tpid;
+ int fd, fd2, i, len = 0;
+ char ch;
+ time_t start, stop;
+ char *hint;
+
+ rp = NULL;
+ /* Special tip that sysinstall left for us */
+ hint = getenv("PKG_ADD_BASE");
+ if (!isURL(spec)) {
+ int len;
+
+ if (!base && !hint)
+ return NULL;
+ /* We've been given an existing URL (that's known-good) and now we need
+ to construct a composite one out of that and the basename we were
+ handed as a dependency. */
+ if (base) {
+ strcpy(fname, base);
+ /* Advance back two slashes to get to the root of the package hierarchy */
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, spec);
+ }
+ else
+ return NULL;
+ }
+ else {
+ /* Otherwise, we've been given an environment variable hinting at the right location from sysinstall */
+ strcpy(fname, hint);
+ strcat(fname, spec);
+ }
+ }
+ else
+ strcpy(fname, spec);
+ ftp = FtpInit();
+ cp = fileURLHost(fname, host, HOSTNAME_MAX);
+ if (!*cp) {
+ whinge("URL `%s' has bad host part!", fname);
+ return NULL;
+ }
+
+ cp = fileURLFilename(fname, file, FILENAME_MAX);
+ if (!*cp) {
+ whinge("URL `%s' has bad filename part!", fname);
+ return NULL;
+ }
+
+ /* Maybe change to ftp if this doesn't work */
+ uname = "anonymous";
+
+ /* Make up a convincing "password" */
+ pw = getpwuid(getuid());
+ if (!pw) {
+ whinge("Can't get user name for ID %d\n.", getuid());
+ strcpy(pword, "joe@");
+ }
+ else
+ snprintf(pword, HOSTNAME_MAX + 40, "%s@%s", pw->pw_name, host);
+
+ if (Verbose)
+ printf("Trying to log into %s as %s.\n", host, uname);
+ FtpOpen(ftp, host, uname, pword);
+ if (getenv("FTP_PASSIVE_MODE"))
+ FtpPassive(ftp, TRUE);
+
+ strcpy(dir, file);
+ for (i = strlen(dir); i && dir[i] != '/'; i--);
+ dir[i] = '\0';
+
+ if (dir[0]) {
+ if (Verbose) printf("FTP: chdir to %s\n", dir);
+ FtpChdir(ftp, dir);
+ }
+ FtpBinary(ftp, TRUE);
+ if (Verbose) printf("FTP: trying to get %s\n", basename_of(file));
+ tmp = basename_of(file);
+ if (!strstr(tmp, ".tgz"))
+ tmp = strconcat(tmp, ".tgz");
+ fd = FtpGet(ftp, tmp);
+ if (fd >= 0) {
+ pen[0] = '\0';
+ if (rp = make_playpen(pen, 0)) {
+ if (Verbose)
+ printf("Extracting from FTP connection into %s\n", pen);
+ tpid = fork();
+ if (!tpid) {
+ dup2(fd, 0);
+ i = execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-", 0);
+ if (Verbose)
+ printf("tar command returns %d status\n", i);
+ exit(i);
+ }
+ else {
+ int pstat;
+
+ close(fd);
+ tpid = waitpid(tpid, &pstat, 0);
+ }
+ }
+ else
+ printf("Error: Unable to construct a new playpen for FTP!\n");
+ }
+ else
+ printf("Error: FTP Unable to get %s\n", basename_of(file));
+ FtpEOF(ftp);
+ FtpClose(ftp);
+ return rp;
+}
+
+char *
+fileFindByPath(char *base, char *fname)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp;
+
+ if (fexists(fname) && isfile(fname)) {
+ strcpy(tmp, fname);
+ return tmp;
+ }
+ if (base) {
+ strcpy(tmp, base);
+
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, fname);
+ if (fexists(tmp))
+ return tmp;
+ }
+ }
+
+ cp = getenv("PKG_PATH");
+ while (cp) {
+ char *cp2 = strsep(&cp, ":");
+
+ snprintf(tmp, FILENAME_MAX, "%s/%s.tgz", cp2 ? cp2 : cp, fname);
+ if (fexists(tmp) && isfile(tmp))
+ return tmp;
+ }
+ return NULL;
+}
+
+char *
+fileGetContents(char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL)
+ barf("Can't stat '%s'.", fname);
+
+ contents = (char *)malloc(sb.st_size + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL)
+ barf("Unable to open '%s' for reading.", fname);
+ if (read(fd, contents, sb.st_size) != sb.st_size)
+ barf("Short read on '%s' - did not get %qd bytes.", fname, sb.st_size);
+ close(fd);
+ contents[sb.st_size] = '\0';
+ return contents;
+}
+
+/* Write the contents of "str" to a file */
+void
+write_file(char *name, char *str)
+{
+ FILE *fp;
+ int len;
+
+ fp = fopen(name, "w");
+ if (!fp)
+ barf("Can't fopen '%s' for writing.", name);
+ len = strlen(str);
+ if (fwrite(str, 1, len, fp) != len)
+ barf("Short fwrite on '%s', tried to write %d bytes.", name, len);
+ if (fclose(fp))
+ barf("failure to fclose '%s'.", name);
+}
+
+void
+copy_file(char *dir, char *fname, char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s/%s %s", dir, fname, to);
+ if (vsystem(cmd))
+ barf("Couldn't perform '%s'", cmd);
+}
+
+void
+move_file(char *dir, char *fname, char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
+ if (vsystem(cmd))
+ barf("Couldn't perform '%s'", cmd);
+}
+
+/*
+ * Copy a hierarchy (possibly from dir) to the current directory, or
+ * if "to" is TRUE, from the current directory to a location someplace
+ * else.
+ *
+ * Though slower, using tar to copy preserves symlinks and everything
+ * without me having to write some big hairy routine to do it.
+ */
+void
+copy_hierarchy(char *dir, char *fname, Boolean to)
+{
+ char cmd[FILENAME_MAX * 3];
+
+ if (!to) {
+ /* If absolute path, use it */
+ if (*fname == '/')
+ dir = "/";
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
+ dir, fname);
+ }
+ else
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
+ fname, dir);
+#ifdef DEBUG
+ printf("Using '%s' to copy trees.\n", cmd);
+#endif
+ if (system(cmd))
+ barf("copy_file: Couldn't perform '%s'", cmd);
+}
+
+/* Unpack a tar file */
+int
+unpack(char *pkg, char *flist)
+{
+ char args[10], suffix[80], *cp;
+
+ args[0] = '\0';
+ /*
+ * Figure out by a crude heuristic whether this or not this is probably
+ * compressed.
+ */
+ if (strcmp(pkg, "-")) {
+ cp = rindex(pkg, '.');
+ if (cp) {
+ strcpy(suffix, cp + 1);
+ if (index(suffix, 'z') || index(suffix, 'Z'))
+ strcpy(args, "-z");
+ }
+ }
+ else
+ strcpy(args, "z");
+ strcat(args, "xpf");
+ if (vsystem("tar %s %s %s", args, pkg, flist ? flist : "")) {
+ whinge("Tar extract of %s failed!", pkg);
+ return 1;
+ }
+ return 0;
+}
+
+/* Using fmt, replace all instances of:
+ *
+ * %F With the parameter "name"
+ * %D With the parameter "dir"
+ * %B Return the directory part ("base") of %D/%F
+ * %f Return the filename part of %D/%F
+ *
+ * Does not check for overflow - caution!
+ *
+ */
+void
+format_cmd(char *buf, char *fmt, char *dir, char *name)
+{
+ char *cp, scratch[FILENAME_MAX * 2];
+
+ while (*fmt) {
+ if (*fmt == '%') {
+ switch (*++fmt) {
+ case 'F':
+ strcpy(buf, name);
+ buf += strlen(name);
+ break;
+
+ case 'D':
+ strcpy(buf, dir);
+ buf += strlen(dir);
+ break;
+
+ case 'B':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *cp != '/')
+ --cp;
+ *cp = '\0';
+ strcpy(buf, scratch);
+ buf += strlen(scratch);
+ break;
+
+ case 'f':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *(cp - 1) != '/')
+ --cp;
+ strcpy(buf, cp);
+ buf += strlen(cp);
+ break;
+
+ default:
+ *buf++ = *fmt;
+ break;
+ }
+ ++fmt;
+ }
+ else
+ *buf++ = *fmt++;
+ }
+ *buf = '\0';
+}
diff --git a/usr.sbin/pkg_install/lib/ftp.c b/usr.sbin/pkg_install/lib/ftp.c
new file mode 100644
index 00000000000..ac507b6d6bd
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/ftp.c
@@ -0,0 +1,424 @@
+ * $OpenBSD: ftp.c,v 1.1 1996/06/04 07:56:12 niklas Exp $
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ *
+ * Return values have been sanitized:
+ * -1 error, but you (still) have a session.
+ * -2 error, your session is dead.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include "ftp.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+/* Handy global for us to stick the port # */
+int FtpPort;
+
+static void
+debug(FTP_t ftp, const char *fmt, ...)
+{
+ char p[BUFSIZ];
+ va_list ap;
+ va_start(ap, fmt);
+ strcpy(p,"LIBFTP: ");
+ (void) vsnprintf(p+strlen(p), sizeof p - strlen(p), fmt, ap);
+ va_end(ap);
+ write(ftp->fd_debug,p,strlen(p));
+}
+
+static int
+writes(int fd, char *s)
+{
+ int i = strlen(s);
+ if (i != write(fd,s,i))
+ return -2;
+ return 0;
+}
+
+static __inline char*
+get_a_line(FTP_t ftp)
+{
+ static char buf[BUFSIZ];
+ int i,j;
+
+ for(i=0;i<BUFSIZ;) {
+ j = read(ftp->fd_ctrl,buf+i,1);
+ if (j != 1)
+ return 0;
+ if (buf[i] == '\r' || buf[i] == '\n') {
+ if (!i)
+ continue;
+ buf[i] = '\0';
+ debug(ftp, "received <%s>\n",buf);
+ return buf;
+ }
+ i++;
+ }
+ return buf;
+}
+
+static int
+get_a_number(FTP_t ftp, char **q)
+{
+ char *p;
+ int i = -1,j;
+
+ while(1) {
+ p = get_a_line(ftp);
+ if (!p)
+ return -2;
+ if (!(isdigit(p[0]) && isdigit(p[1]) && isdigit(p[2])))
+ continue;
+ if (i == -1 && p[3] == '-') {
+ i = strtol(p, 0, 0);
+ continue;
+ }
+ if (p[3] != ' ' && p[3] != '\t')
+ continue;
+ j = strtol(p, 0, 0);
+ if (i == -1) {
+ if (q) *q = p+4;
+ return j;
+ } else if (j == i) {
+ if (q) *q = p+4;
+ return j;
+ }
+ }
+}
+
+static int
+zap(FTP_t ftp)
+{
+ int i;
+
+ i = writes(ftp->fd_ctrl,"QUIT\r\n");
+ debug(ftp, "Zapping ftp connection on %d returns %d\n", ftp->fd_ctrl, i);
+ close(ftp->fd_ctrl); ftp->fd_ctrl = -1;
+ close(ftp->fd_xfer); ftp->fd_xfer = -1;
+ ftp->state = init;
+ return -2;
+}
+
+static int
+botch(FTP_t ftp, char *func, char *state)
+{
+ debug(ftp, "Botch: %s called outside state %s\n",func,state);
+ return -2;
+}
+
+static int
+cmd(FTP_t ftp, const char *fmt, ...)
+{
+ char p[BUFSIZ];
+ int i;
+
+ va_list ap;
+ va_start(ap, fmt);
+ (void) vsnprintf(p, sizeof p, fmt, ap);
+ va_end(ap);
+
+ debug(ftp, "send <%s>\n",p);
+ strcat(p,"\r\n");
+ if (writes(ftp->fd_ctrl,p))
+ return -2;
+ i = get_a_number(ftp,0);
+ return i;
+}
+
+FTP_t
+FtpInit()
+{
+ FTP_t ftp;
+
+ ftp = malloc(sizeof *ftp);
+ if (!ftp)
+ return ftp;
+ memset(ftp, 0, sizeof *ftp);
+ ftp->fd_ctrl = -1;
+ ftp->fd_xfer = -1;
+ ftp->fd_debug = -1;
+ ftp->state = init;
+ return ftp;
+}
+
+void
+FtpDebug(FTP_t ftp, int i)
+{
+ ftp->fd_debug = i;
+}
+
+int
+FtpOpen(FTP_t ftp, char *host, char *user, char *passwd)
+{
+ struct hostent *he = NULL;
+ struct sockaddr_in sin;
+ int s;
+ unsigned long temp;
+ int i;
+
+ if (ftp->state != init)
+ return botch(ftp,"FtpOpen","init");
+
+ if (!user)
+ user = "ftp";
+
+ if (!passwd)
+ passwd = "??@??(FreeBSD:libftp)"; /* XXX */
+
+ debug(ftp, "FtpOpen(ftp, %s, %s, %s)\n", host, user, passwd);
+
+ temp = inet_addr(host);
+ if (temp != INADDR_NONE) {
+ debug(ftp, "Using dotted IP address `%s'\n", host);
+ ftp->addrtype = sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = temp;
+ }
+ else {
+ debug(ftp, "Trying to resolve `%s'\n", host);
+ he = gethostbyname(host);
+ if (!he) {
+ debug(ftp, "Lookup of `%s' failed!\n", host);
+ return zap(ftp);
+ }
+ ftp->addrtype = sin.sin_family = he->h_addrtype;
+ bcopy(he->h_addr, (char *)&sin.sin_addr, he->h_length);
+ }
+
+ sin.sin_port = htons(FtpPort ? FtpPort : 21);
+
+ if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0)
+ {
+ debug(ftp, "Socket open failed: %s (%i)\n", strerror(errno), errno);
+ return zap(ftp);
+ }
+
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ debug(ftp,"Connection failed: %s (%i)\n", strerror(errno), errno);
+ (void)close(s);
+ return zap(ftp);
+ }
+
+ ftp->fd_ctrl = s;
+
+ debug(ftp, "open (%d)\n",get_a_number(ftp,0));
+
+ i = cmd(ftp,"USER %s",user);
+ if (i >= 300 && i < 400)
+ i = cmd(ftp,"PASS %s",passwd);
+ if (i >= 299 || i < 0) {
+ close(ftp->fd_ctrl); ftp->fd_ctrl = -1;
+ return zap(ftp);
+ }
+ ftp->state = isopen;
+ return 0;
+}
+
+void
+FtpClose(FTP_t ftp)
+{
+ if (ftp->state != init)
+ return;
+
+ if (ftp->state != isopen)
+ botch(ftp,"FtpClose","open or init");
+
+ debug(ftp, "FtpClose(ftp)\n");
+ zap(ftp);
+}
+
+int
+FtpChdir(FTP_t ftp, char *dir)
+{
+ int i;
+ if (ftp->state != isopen)
+ return botch(ftp,"FtpChdir","open");
+ i = cmd(ftp,"CWD %s",dir);
+ if (i < 0)
+ return i;
+ else if (i != 250)
+ return -1;
+ return 0;
+}
+
+int
+FtpGet(FTP_t ftp, char *file)
+{
+ int i,s;
+ char *q;
+ unsigned char addr[64];
+ struct sockaddr_in sin;
+ u_long a;
+
+ debug(ftp, "FtpGet(ftp,%s)\n",file);
+ if (ftp->state != isopen)
+ return botch(ftp,"FtpGet","open");
+ if(ftp->binary) {
+ i = cmd(ftp,"TYPE I");
+ if (i < 0)
+ return zap(ftp);
+ if (i > 299)
+ return -1;
+ } else {
+ return -1;
+ }
+
+ if ((s = socket(ftp->addrtype, SOCK_STREAM, 0)) < 0)
+ return zap(ftp);
+
+ if (ftp->passive) {
+ debug(ftp, "send <%s>\n","PASV");
+ if (writes(ftp->fd_ctrl,"PASV\r\n"))
+ return zap(ftp);
+ i = get_a_number(ftp,&q);
+ if (i < 0)
+ return zap(ftp);
+ if (i != 227)
+ return zap(ftp);
+ while (*q && !isdigit(*q))
+ q++;
+ if (!*q)
+ return zap(ftp);
+ q--;
+ for(i=0;i<6;i++) {
+ q++;
+ addr[i] = strtol(q,&q,10);
+ }
+
+ sin.sin_family = ftp->addrtype;
+ bcopy(addr, (char *)&sin.sin_addr, 4);
+ bcopy(addr+4, (char *)&sin.sin_port, 2);
+ debug(ftp, "Opening active socket to %s : %u\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+ debug(ftp, "Connecting to %s:%u\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ (void)close(s);
+ debug(ftp, "connect: %s (%d)\n", strerror(errno), errno);
+ return -1;
+ }
+ ftp->fd_xfer = s;
+ i = cmd(ftp,"RETR %s",file);
+ if (i < 0) {
+ close(s);
+ return zap(ftp);
+ }
+ else if (i > 299) {
+ debug(ftp, "FTP: No such file %s, moving on.\n", file);
+ close(s);
+ return -1;
+ }
+ ftp->state = xfer;
+ return s;
+ } else {
+ i = sizeof sin;
+ getsockname(ftp->fd_ctrl,(struct sockaddr *)&sin,&i);
+ sin.sin_port = 0;
+ i = sizeof sin;
+ if (bind(s,(struct sockaddr *)&sin, i) < 0) {
+ close (s);
+ debug(ftp,"bind failed %d\n",errno);
+ return zap(ftp);
+ }
+ getsockname(s,(struct sockaddr *)&sin,&i);
+ if (listen(s,1) < 0) {
+ close (s);
+ debug(ftp,"listen failed %d\n",errno);
+ return zap(ftp);
+ }
+ a = ntohl(sin.sin_addr.s_addr);
+ i = cmd(ftp,"PORT %d,%d,%d,%d,%d,%d",
+ (a >> 24) & 0xff,
+ (a >> 16) & 0xff,
+ (a >> 8) & 0xff,
+ a & 0xff,
+ (ntohs(sin.sin_port) >> 8) & 0xff,
+ ntohs(sin.sin_port) & 0xff);
+ if (i != 200)
+ return -1;
+ i = cmd(ftp,"RETR %s",file);
+ if (i < 0) {
+ close(s);
+ return zap(ftp);
+ }
+ else if (i > 299) {
+ debug(ftp, "FTP: No such file %s, moving on.\n", file);
+ close(s);
+ return -1;
+ }
+ ftp->fd_xfer = accept(s, 0, 0);
+ if (ftp->fd_xfer < 0) {
+ close(s);
+ return zap(ftp);
+ }
+ ftp->state = xfer;
+ close(s);
+ return(ftp->fd_xfer);
+ }
+}
+
+int
+FtpEOF(FTP_t ftp)
+{
+ int i;
+
+ if (ftp->state != xfer)
+ return botch(ftp,"FtpEOF","xfer");
+ debug(ftp, "FtpEOF(ftp)\n");
+ close(ftp->fd_xfer); ftp->fd_xfer = -1;
+ ftp->state = isopen;
+ i = get_a_number(ftp,0);
+ if (i < 0)
+ return zap(ftp);
+ else if (i != 250 && i != 226)
+ return -1;
+ else
+ return 0;
+}
+
+#ifdef STANDALONE_FTP
+
+/* main.c */
+int
+main(int argc, char **argv)
+{
+ FTP_t ftp;
+ int i;
+ char c;
+
+ ftp = FtpInit();
+ if (!ftp)
+ err(1, "FtpInit()");
+
+ FtpDebug(ftp, 1);
+ i = FtpOpen(ftp, "freefall.cdrom.com", "ftp", "phk-libftp@");
+ FtpBinary(ftp, 1);
+ FtpPassive(ftp, 0);
+ FtpChdir(ftp, "/pub");
+ FtpChdir(ftp, "FreeBSD");
+ i = FtpGet(ftp, "README");
+ while (1 == read(i, &c, 1))
+ putchar(c);
+ FtpEOF(ftp);
+ return 0;
+}
+
+#endif /*STANDALONE_FTP*/
diff --git a/usr.sbin/pkg_install/lib/ftp.h b/usr.sbin/pkg_install/lib/ftp.h
new file mode 100644
index 00000000000..b272d1c62de
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/ftp.h
@@ -0,0 +1,27 @@
+# $OpenBSD: ftp.h,v 1.1 1996/06/04 07:56:12 niklas Exp $
+#ifndef _FTP_H_INCLUDE
+#define _FTP_H_INCLUDE
+
+typedef struct {
+ enum {init, isopen, xfer} state;
+ int fd_ctrl;
+ int fd_xfer;
+ int fd_debug;
+ int binary;
+ int passive;
+ int addrtype;
+ char *host;
+ char *file;
+} *FTP_t;
+
+FTP_t FtpInit();
+int FtpOpen(FTP_t, char *host, char *user, char *passwd);
+#define FtpBinary(ftp,bool) { (ftp)->binary = (bool); }
+#define FtpPassive(ftp,bool) { (ftp)->passive = (bool); }
+int FtpChdir(FTP_t, char *);
+int FtpGet(FTP_t, char *);
+int FtpEOF(FTP_t);
+void FtpClose(FTP_t);
+
+#endif
+/* _FTP_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/lib/global.c b/usr.sbin/pkg_install/lib/global.c
new file mode 100644
index 00000000000..36d1a433b7b
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,34 @@
+# $OpenBSD: global.c,v 1.1 1996/06/04 07:56:12 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: global.c,v 1.1 1996/06/04 07:56:12 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Semi-convenient place to stick some needed globals.
+ *
+ */
+
+#include "lib.h"
+
+/* These are global for all utils */
+Boolean Verbose = FALSE;
+Boolean Fake = FALSE;
+int AutoAnswer = FALSE;
+
+
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h
new file mode 100644
index 00000000000..f09d8228033
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,173 @@
+/* $OpenBSD: lib.h,v 1.1 1996/06/04 07:56:13 niklas Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the library routines.
+ *
+ */
+
+#ifndef _INST_LIB_LIB_H_
+#define _INST_LIB_LIB_H_
+
+/* Includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/file.h>
+
+/* Macros */
+#define SUCCESS (0)
+#define FAIL (-1)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define YES 2
+#define NO 1
+
+/* Usually "rm", but often "echo" during debugging! */
+#define REMOVE_CMD "rm"
+
+/* Usually "rm", but often "echo" during debugging! */
+#define RMDIR_CMD "rmdir"
+
+/* Where we put logging information by default, else ${PKG_DBDIR} if set */
+#define DEF_LOG_DIR "/var/db/pkg"
+/* just in case we change the environment variable name */
+#define PKG_DBDIR "PKG_DBDIR"
+
+/* The names of our "special" files */
+#define CONTENTS_FNAME "+CONTENTS"
+#define COMMENT_FNAME "+COMMENT"
+#define DESC_FNAME "+DESC"
+#define INSTALL_FNAME "+INSTALL"
+#define DEINSTALL_FNAME "+DEINSTALL"
+#define REQUIRE_FNAME "+REQUIRE"
+#define REQUIRED_BY_FNAME "+REQUIRED_BY"
+#define DISPLAY_FNAME "+DISPLAY"
+#define MTREE_FNAME "+MTREE_DIRS"
+
+#define CMD_CHAR '@' /* prefix for extended PLIST cmd */
+
+/* The name of the "prefix" environment variable given to scripts */
+#define PKG_PREFIX_VNAME "PKG_PREFIX"
+
+enum _plist_t {
+ PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD,
+ PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT,
+ PLIST_IGNORE, PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY,
+ PLIST_PKGDEP, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST,
+ PLIST_OPTION
+};
+typedef enum _plist_t plist_t;
+
+/* Types */
+typedef unsigned int Boolean;
+
+struct _plist {
+ struct _plist *prev, *next;
+ char *name;
+ Boolean marked;
+ plist_t type;
+};
+typedef struct _plist *PackingList;
+
+struct _pack {
+ struct _plist *head, *tail;
+};
+typedef struct _pack Package;
+
+/* Prototypes */
+/* Misc */
+int vsystem(const char *, ...);
+void cleanup(int);
+char *make_playpen(char *, size_t);
+char *where_playpen(void);
+void leave_playpen(char *);
+size_t min_free(char *);
+
+/* String */
+char *get_dash_string(char **);
+char *copy_string(char *);
+Boolean suffix(char *, char *);
+void nuke_suffix(char *);
+void str_lowercase(char *);
+char *basename_of(char *);
+char *strconcat(char *, char *);
+
+/* File */
+Boolean fexists(char *);
+Boolean isdir(char *);
+Boolean isfile(char *);
+Boolean isempty(char *);
+Boolean isURL(char *);
+char *fileGetURL(char *, char *);
+char *fileURLFilename(char *, char *, int);
+char *fileURLHost(char *, char *, int);
+char *fileFindByPath(char *, char *);
+char *fileGetContents(char *);
+void write_file(char *, char *);
+void copy_file(char *, char *, char *);
+void move_file(char *, char *, char *);
+void copy_hierarchy(char *, char *, Boolean);
+int delete_hierarchy(char *, Boolean, Boolean);
+int unpack(char *, char *);
+void format_cmd(char *, char *, char *, char *);
+
+/* Msg */
+void upchuck(const char *);
+void barf(const char *, ...);
+void whinge(const char *, ...);
+Boolean y_or_n(Boolean, const char *, ...);
+
+/* Packing list */
+PackingList new_plist_entry(void);
+PackingList last_plist(Package *);
+PackingList find_plist(Package *, plist_t);
+char *find_plist_option(Package *, char *name);
+void plist_delete(Package *, Boolean, plist_t, char *);
+void free_plist(Package *);
+void mark_plist(Package *);
+void csum_plist_entry(char *, PackingList);
+void add_plist(Package *, plist_t, char *);
+void add_plist_top(Package *, plist_t, char *);
+void write_plist(Package *, FILE *);
+void read_plist(Package *, FILE *);
+int plist_cmd(char *, char **);
+int delete_package(Boolean, Boolean, Package *);
+
+/* For all */
+void usage(const char *, const char *, ...);
+int pkg_perform(char **);
+
+/* Externs */
+extern Boolean Verbose;
+extern Boolean Fake;
+extern int AutoAnswer;
+
+#endif /* _INST_LIB_LIB_H_ */
diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c
new file mode 100644
index 00000000000..0ee3a57dbea
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/msg.c
@@ -0,0 +1,102 @@
+# $OpenBSD: msg.c,v 1.1 1996/06/04 07:56:13 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: msg.c,v 1.1 1996/06/04 07:56:13 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Miscellaneous message routines.
+ *
+ */
+
+#include "lib.h"
+
+/* Die a relatively simple death */
+void
+upchuck(const char *err)
+{
+ fprintf(stderr, "Fatal error during execution: ");
+ perror(err);
+ cleanup(0);
+ exit(1);
+}
+
+/* Die a more complex death */
+void
+barf(const char *err, ...)
+{
+ va_list args;
+
+ va_start(args, err);
+ vfprintf(stderr, err, args);
+ fputc('\n', stderr);
+ va_end(args);
+ cleanup(0);
+ exit(2);
+}
+
+/* Get annoyed about something but don't go to pieces over it */
+void
+whinge(const char *err, ...)
+{
+ va_list args;
+
+ va_start(args, err);
+ vfprintf(stderr, err, args);
+ fputc('\n', stderr);
+ va_end(args);
+}
+
+/*
+ * As a yes/no question, prompting from the varargs string and using
+ * default if user just hits return.
+ */
+Boolean
+y_or_n(Boolean def, const char *msg, ...)
+{
+ va_list args;
+ int ch = 0;
+ FILE *tty;
+
+ va_start(args, msg);
+ /*
+ * Need to open /dev/tty because file collection may have been
+ * collected on stdin
+ */
+ tty = fopen("/dev/tty", "r");
+ if (!tty)
+ barf("Can't open /dev/tty!\n");
+ while (ch != 'Y' && ch != 'N') {
+ vfprintf(stderr, msg, args);
+ if (def)
+ fprintf(stderr, " [yes]? ");
+ else
+ fprintf(stderr, " [no]? ");
+ fflush(stderr);
+ if (AutoAnswer) {
+ ch = (AutoAnswer == YES) ? 'Y' : 'N';
+ fprintf(stderr, "%c\n", ch);
+ }
+ else
+ ch = toupper(fgetc(tty));
+ if (ch == '\n')
+ ch = (def) ? 'Y' : 'N';
+ }
+ fclose(tty) ;
+ return (ch == 'Y') ? TRUE : FALSE;
+}
diff --git a/usr.sbin/pkg_install/lib/pen.c b/usr.sbin/pkg_install/lib/pen.c
new file mode 100644
index 00000000000..e26ad037617
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,144 @@
+# $OpenBSD: pen.c,v 1.1 1996/06/04 07:56:13 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: pen.c,v 1.1 1996/06/04 07:56:13 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for managing the "play pen".
+ *
+ */
+
+#include "lib.h"
+#include <sys/signal.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+/* For keeping track of where we are */
+static char Current[FILENAME_MAX];
+static char Previous[FILENAME_MAX];
+
+char *
+where_playpen(void)
+{
+ return Current;
+}
+
+/* Find a good place to play. */
+static char *
+find_play_pen(char *pen, size_t sz)
+{
+ char *cp;
+ struct stat sb;
+
+ if (pen[0] && stat(pen, &sb) != FAIL && (min_free(pen) >= sz))
+ return pen;
+ else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz)
+ strcpy(pen, "/var/tmp/instmp.XXXXXX");
+ else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz)
+ strcpy(pen, "/tmp/instmp.XXXXXX");
+ else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz)
+ strcpy(pen, "/usr/tmp/instmp.XXXXXX");
+ else {
+ barf("Can't find enough temporary space to extract the files, please set\n"
+ "your PKG_TMPDIR environment variable to a location with at least %d bytes\n"
+ "free.", sz);
+ return NULL;
+ }
+ return pen;
+}
+
+/*
+ * Make a temporary directory to play in and chdir() to it, returning
+ * pathname of previous working directory.
+ */
+char *
+make_playpen(char *pen, size_t sz)
+{
+ char *tmp;
+
+ if (!find_play_pen(pen, sz))
+ return NULL;
+
+ if (!mktemp(pen)) {
+ barf("Can't mktemp '%s'.", pen);
+ return NULL;
+ }
+ if (mkdir(pen, 0755) == FAIL) {
+ barf("Can't mkdir '%s'.", pen);
+ return NULL;
+ }
+ if (Verbose) {
+ if (sz)
+ fprintf(stderr, "Requested space: %d bytes, free space: %d bytes in %s\n", (int)sz, min_free(pen), pen);
+ }
+ if (min_free(pen) < sz) {
+ rmdir(pen);
+ barf("Not enough free space to create: `%s'\n"
+ "Please set your PKG_TMPDIR environment variable to a location\n"
+ "with more space and\ntry the command again.", pen);
+ return NULL;
+ }
+ if (Current[0])
+ strcpy(Previous, Current);
+ else if (!getcwd(Previous, FILENAME_MAX)) {
+ upchuck("getcwd");
+ return NULL;
+ }
+ if (chdir(pen) == FAIL)
+ barf("Can't chdir to '%s'.", pen);
+ strcpy(Current, pen);
+ return Previous;
+}
+
+/* Convenience routine for getting out of playpen */
+void
+leave_playpen(char *save)
+{
+ void (*oldsig)(int);
+
+ /* Don't interrupt while we're cleaning up */
+ oldsig = signal(SIGINT, SIG_IGN);
+ if (Previous[0] && chdir(Previous) == FAIL)
+ barf("Can't chdir back to '%s'.", Previous);
+ else if (Current[0] && strcmp(Current, Previous)) {
+ if (vsystem("rm -rf %s", Current))
+ whinge("Couldn't remove temporary dir '%s'", Current);
+ strcpy(Current, Previous);
+ }
+ if (save)
+ strcpy(Previous, save);
+ else
+ Previous[0] = '\0';
+ signal(SIGINT, oldsig);
+}
+
+size_t
+min_free(char *tmpdir)
+{
+ struct statfs buf;
+
+ if (statfs(tmpdir, &buf) != 0) {
+ perror("Error in statfs");
+ return -1;
+ }
+ return buf.f_bavail * buf.f_bsize;
+}
diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c
new file mode 100644
index 00000000000..a24e63176d9
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,448 @@
+# $OpenBSD: plist.c,v 1.1 1996/06/04 07:56:14 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD: plist.c,v 1.1 1996/06/04 07:56:14 niklas Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * General packing list routines.
+ *
+ */
+
+#include "lib.h"
+
+/* Add an item to a packing list */
+void
+add_plist(Package *p, plist_t type, char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->prev = p->tail;
+ p->tail->next = tmp;
+ p->tail = tmp;
+ }
+}
+
+void
+add_plist_top(Package *p, plist_t type, char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->next = p->head;
+ p->head->prev = tmp;
+ p->head = tmp;
+ }
+}
+
+/* Return the last (most recent) entry in a packing list */
+PackingList
+last_plist(Package *p)
+{
+ return p->tail;
+}
+
+/* Mark all items in a packing list to prevent iteration over them */
+void
+mark_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ p->marked = TRUE;
+ p = p->next;
+ }
+}
+
+/* Find a given item in a packing list and, if so, return it (else NULL) */
+PackingList
+find_plist(Package *pkg, plist_t type)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == type)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/* Look for a specific boolean option argument in the list */
+char *
+find_plist_option(Package *pkg, char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_OPTION && !strcmp(p->name, name))
+ return p->name;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Delete plist item 'type' in the list (if 'name' is non-null, match it
+ * too.) If 'all' is set, delete all items, not just the first occurance.
+ */
+void
+delete_plist(Package *pkg, Boolean all, plist_t type, char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList pnext = p->next;
+
+ if (p->type == type && (!name || !strcmp(name, p->name))) {
+ free(p->name);
+ if (p->prev)
+ p->prev->next = pnext;
+ else
+ pkg->head = pnext;
+ if (pnext)
+ pnext->prev = p->prev;
+ else
+ pkg->tail = p->prev;
+ free(p);
+ if (!all)
+ return;
+ p = pnext;
+ }
+ else
+ p = p->next;
+ }
+}
+
+/* Allocate a new packing list entry */
+PackingList
+new_plist_entry(void)
+{
+ PackingList ret;
+
+ ret = (PackingList)malloc(sizeof(struct _plist));
+ bzero(ret, sizeof(struct _plist));
+ return ret;
+}
+
+/* Free an entire packing list */
+void
+free_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList p1 = p->next;
+
+ free(p->name);
+ free(p);
+ p = p1;
+ }
+ pkg->head = pkg->tail = NULL;
+}
+
+/*
+ * For an ascii string denoting a plist command, return its code and
+ * optionally its argument(s)
+ */
+int
+plist_cmd(char *s, char **arg)
+{
+ char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */
+ char *cp, *sp;
+
+ strcpy(cmd, s);
+ str_lowercase(cmd);
+ cp = cmd;
+ sp = s;
+ while (*cp) {
+ if (isspace(*cp)) {
+ *cp = '\0';
+ while (isspace(*sp)) /* Never sure if macro, increment later */
+ ++sp;
+ break;
+ }
+ ++cp, ++sp;
+ }
+ if (arg)
+ *arg = sp;
+ if (!strcmp(cmd, "cwd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "srcdir"))
+ return PLIST_SRC;
+ else if (!strcmp(cmd, "cd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "exec"))
+ return PLIST_CMD;
+ else if (!strcmp(cmd, "unexec"))
+ return PLIST_UNEXEC;
+ else if (!strcmp(cmd, "mode"))
+ return PLIST_CHMOD;
+ else if (!strcmp(cmd, "owner"))
+ return PLIST_CHOWN;
+ else if (!strcmp(cmd, "group"))
+ return PLIST_CHGRP;
+ else if (!strcmp(cmd, "comment"))
+ return PLIST_COMMENT;
+ else if (!strcmp(cmd, "ignore"))
+ return PLIST_IGNORE;
+ else if (!strcmp(cmd, "ignore_inst"))
+ return PLIST_IGNORE_INST;
+ else if (!strcmp(cmd, "name"))
+ return PLIST_NAME;
+ else if (!strcmp(cmd, "display"))
+ return PLIST_DISPLAY;
+ else if (!strcmp(cmd, "pkgdep"))
+ return PLIST_PKGDEP;
+ else if (!strcmp(cmd, "mtree"))
+ return PLIST_MTREE;
+ else if (!strcmp(cmd, "dirrm"))
+ return PLIST_DIR_RM;
+ else if (!strcmp(cmd, "option"))
+ return PLIST_OPTION;
+ else
+ return FAIL;
+}
+
+/* Read a packing list from a file */
+void
+read_plist(Package *pkg, FILE *fp)
+{
+ char *cp, pline[FILENAME_MAX];
+ int cmd;
+
+ while (fgets(pline, FILENAME_MAX, fp)) {
+ int len = strlen(pline) - 1;
+
+ while (isspace(pline[len]))
+ pline[len--] = '\0';
+ if (len <= 0)
+ continue;
+ cp = pline;
+ if (pline[0] == CMD_CHAR) {
+ cmd = plist_cmd(pline + 1, &cp);
+ if (cmd == FAIL)
+ barf("Bad command '%s'", pline);
+ if (*cp == '\0')
+ cp = NULL;
+ }
+ else
+ cmd = PLIST_FILE;
+ add_plist(pkg, cmd, cp);
+ }
+}
+
+/* Write a packing list to a file, converting commands to ascii equivs */
+void
+write_plist(Package *pkg, FILE *fp)
+{
+ PackingList plist = pkg->head;
+
+ while (plist) {
+ switch(plist->type) {
+ case PLIST_FILE:
+ fprintf(fp, "%s\n", plist->name);
+ break;
+
+ case PLIST_CWD:
+ fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_SRC:
+ fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CMD:
+ fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_UNEXEC:
+ fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CHMOD:
+ fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHOWN:
+ fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHGRP:
+ fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_COMMENT:
+ fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_IGNORE:
+ case PLIST_IGNORE_INST: /* a one-time non-ignored file */
+ fprintf(fp, "%cignore\n", CMD_CHAR);
+ break;
+
+ case PLIST_NAME:
+ fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DISPLAY:
+ fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_PKGDEP:
+ fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_MTREE:
+ fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DIR_RM:
+ fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_OPTION:
+ fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
+ break;
+
+ default:
+ barf("Unknown command type %d (%s)\n", plist->type, plist->name);
+ break;
+ }
+ plist = plist->next;
+ }
+}
+
+/*
+ * Delete the results of a package installation.
+ *
+ * This is here rather than in the pkg_delete code because pkg_add needs to
+ * run it too in cases of failure.
+ */
+int
+delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
+{
+ PackingList p = pkg->head;
+ char *Where = ".", *last_file = "";
+ Boolean fail = SUCCESS;
+
+ if (!p)
+ return FAIL;
+ while (p) {
+ if (p->type == PLIST_CWD) {
+ Where = p->name;
+ if (Verbose)
+ printf("Change working directory to %s\n", Where);
+ }
+ else if (p->type == PLIST_UNEXEC) {
+ char cmd[FILENAME_MAX];
+
+ format_cmd(cmd, p->name, Where, last_file);
+ if (Verbose)
+ printf("Execute `%s'\n", cmd);
+ if (!Fake && system(cmd)) {
+ whinge("unexec command for `%s' failed.", cmd);
+ fail = FAIL;
+ }
+ }
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_FILE || p->type == PLIST_DIR_RM) {
+ char full_name[FILENAME_MAX];
+
+ sprintf(full_name, "%s/%s", Where, p->name);
+ if (isdir(full_name) && p->type == PLIST_FILE) {
+ warn("Attempting to delete directory `%s' as a file\n"
+ "This packing list is incorrect - ignoring delete request.\n", full_name);
+ }
+ else {
+ if (Verbose)
+ printf("Delete %s %s\n", !isdir(full_name) ? "file" : " directory", full_name);
+
+ if (!Fake && delete_hierarchy(full_name, ign_err, p->type == PLIST_DIR_RM ? FALSE : nukedirs)) {
+ whinge("Unable to completely remove file '%s'", full_name);
+ fail = FAIL;
+ }
+ }
+ last_file = p->name;
+ }
+ p = p->next;
+ }
+ return fail;
+}
+
+#ifdef DEBUG
+#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
+#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
+#else
+#define RMDIR rmdir
+#define REMOVE(file,ie) (remove(file) && !(ie))
+#endif
+
+/* Selectively delete a hierarchy */
+int
+delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs)
+{
+ char *cp1, *cp2;
+
+ cp1 = cp2 = dir;
+ if (!fexists(dir)) {
+ if (!ign_err)
+ whinge("%s `%s' doesn't really exist.", isdir(dir) ? "Directory" : "File", dir);
+ } else if (nukedirs) {
+ if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
+ return 1;
+ } else if (isdir(dir)) {
+ if (RMDIR(dir) && !ign_err)
+ return 1;
+ } else {
+ if (REMOVE(dir, ign_err))
+ return 1;
+ }
+
+ if (!nukedirs)
+ return 0;
+ while (cp2) {
+ if ((cp2 = rindex(cp1, '/')) != NULL)
+ *cp2 = '\0';
+ if (!isemptydir(dir))
+ return 0;
+ if (RMDIR(dir) && !ign_err)
+ if (!fexists(dir))
+ whinge("Directory `%s' doesn't really exist.", dir);
+ else
+ return 1;
+ /* back up the pathname one component */
+ if (cp2) {
+ cp1 = dir;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c
new file mode 100644
index 00000000000..1d8ec1c5c99
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/str.c
@@ -0,0 +1,111 @@
+# $OpenBSD: str.c,v 1.1 1996/06/04 07:56:14 niklas Exp $
+#ifndef lint
+static const char *rcsid = "$OpenBSD";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * 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.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous string utilities.
+ *
+ */
+
+#include "lib.h"
+
+/* Return the filename portion of a path */
+char *
+basename_of(char *str)
+{
+ char *basename = str + strlen(str) - 1;
+
+ while (basename != str && basename[-1] != '/')
+ --basename;
+ return basename;
+}
+
+char *
+strconcat(char *s1, char *s2)
+{
+ static char tmp[FILENAME_MAX];
+
+ tmp[0] = '\0';
+ strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX);
+ if (s1 && s2)
+ strncat(tmp, s2, FILENAME_MAX - strlen(tmp));
+ return tmp;
+}
+
+/* Get a string parameter as a file spec or as a "contents follow -" spec */
+char *
+get_dash_string(char **str)
+{
+ char *s = *str;
+
+ if (*s == '-')
+ *str = copy_string(s + 1);
+ else
+ *str = fileGetContents(s);
+ return *str;
+}
+
+/* Rather Obvious */
+char *
+copy_string(char *str)
+{
+ char *ret;
+
+ if (!str)
+ ret = NULL;
+ else {
+ ret = (char *)malloc(strlen(str) + 1);
+ strcpy(ret, str);
+ }
+ return ret;
+}
+
+/* Return TRUE if 'str' ends in suffix 'suff' */
+Boolean
+suffix(char *str, char *suff)
+{
+ char *idx;
+ Boolean ret = FALSE;
+
+ idx = rindex(str, '.');
+ if (idx && !strcmp(idx + 1, suff))
+ ret = TRUE;
+ return ret;
+}
+
+/* Assuming str has a suffix, brutally murder it! */
+void
+nuke_suffix(char *str)
+{
+ char *idx;
+
+ idx = rindex(str, '.');
+ if (idx)
+ *idx = '\0'; /* Yow! Don't try this on a const! */
+}
+
+/* Lowercase a whole string */
+void
+str_lowercase(char *str)
+{
+ while (*str) {
+ *str = tolower(*str);
+ ++str;
+ }
+}
diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg
new file mode 100644
index 00000000000..7e11719df70
--- /dev/null
+++ b/usr.sbin/pkg_install/tkpkg
@@ -0,0 +1,180 @@
+#!/usr/local/bin/wish -f
+#$OpenBSD: tkpkg,v 1.1 1996/06/04 07:56:02 niklas Exp $
+#
+#$Log: tkpkg,v $
+#Revision 1.1 1996/06/04 07:56:02 niklas
+#add package tools from FreeBSD
+#
+#Revision 1.2 1994/12/06 00:51:21 jkh
+#Many of John T. Kohl's patches from NetBSD. Thanks, John!
+#Submitted by: jkohl
+#
+# Revision 1.1 1994/01/06 08:16:20 jkh
+# Cleaning house.
+#
+# Revision 1.1 1993/09/04 17:06:09 jkh
+# Added Rich's wish front-end.
+#
+# Revision 1.6 1993/09/03 23:37:22 rich
+# warn user if no tar archives are found in the current directory.
+# removed the revision string from the lower text frame.
+#
+# Revision 1.5 1993/09/03 15:48:04 rich
+# glob for .tar.gz, .tar.z and .tar.Z looking for archives
+#
+# Revision 1.4 1993/08/28 15:53:59 rich
+# added version and date info to lower text window.
+#
+# Revision 1.3 1993/08/28 15:47:12 rich
+# filtered out ^Ls in pkg_* output.
+#
+#
+set pkgname ""
+wm title . "Package Installation"
+#--------------------------------------------------------------
+# The top level main window, consisting of a bar of buttons and a list
+# of packages and a description of the current package.
+#--------------------------------------------------------------
+frame .menu -relief raised -borderwidth 1
+frame .frame -borderwidth 4
+
+scrollbar .frame.scroll -relief sunken -command ".frame.list yview"
+listbox .frame.list -yscroll ".frame.scroll set" -relief sunken -setgrid 1
+pack append .frame .frame.scroll {right filly} \
+ .frame.list {left expand fill}
+
+# build the lower window shoing the complete description of a pacage
+frame .f -borderwidth 4
+text .f.t -width 80 -height 20 -yscrollcommand ".f.s set" -relief sunken
+
+# Initially display instructions in this window. Erase the
+# instructions and show the package description when the user clicks
+# on a package.
+#
+.f.t insert end "Double click on a package above to see its
+complete description here."
+scrollbar .f.s -relief sunken -command ".f.t yview"
+pack append .f .f.s {right filly} .f.t {left expand fill}
+
+bind .frame.list <Double-Button-1> \
+ {foreach i [selection get] {do_description $i}}
+pack append . .menu {top fill} \
+ .f {bottom expand fill} \
+ .frame {bottom expand fill}
+
+#----------------------------------------------------------------
+# Make menu bar:
+#----------------------------------------------------------------
+button .menu.inst -text "Install" \
+ -command "apply_to_pkg \"pkg_add -v\""
+button .menu.dein -text "Deinstall" \
+ -command "apply_to_pkg \"pkg_delete -v\""
+button .menu.installed -text "What is Installed?" \
+ -command "list_pkgs \"pkg_info -I -a |tr ' ' ' '\""
+button .menu.available -text "What can I install?" \
+ -command "list_pkgs \"pkg_info -I -c [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] |tr ' ' ' '\""
+button .menu.cont -text "Contents?" \
+ -command "apply_to_pkg \"pkg_info -d -v\""
+button .menu.quit -text "Quit" -command "destroy ."
+button .menu.help -text "Help" -command "do_help"
+
+pack append .menu \
+ .menu.inst left \
+ .menu.dein left \
+ .menu.installed left \
+ .menu.available left \
+ .menu.cont left \
+ .menu.quit left \
+ .menu.help right
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc list_pkgs {s} {
+ set line ""
+ set f [eval "open {| sh -c \"$s\" } r"]
+ .frame.list delete 0 end
+ while {[gets $f line] > 0} {
+ .frame.list insert end $line
+ }
+ close $f
+}
+
+# display the list of available packages
+set archives [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}]
+if {$archives == ""} {
+ .frame.list delete 0 end
+ .frame.list insert end "Warning: no compressed tar archives files found."
+} else {
+ list_pkgs "pkg_info -I -c $archives |tr ' ' ' '"
+}
+
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc do_description {s} {
+ global pkgname
+ regexp {[^ ]*} $s filename
+ set pkgname $filename
+ .f.t delete 0.0 end
+ set cmd "pkg_info -d $filename |tr -d ' '"
+ set f [eval "open {| csh -c \"$cmd\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f]
+ }
+}
+#-------------------------------------------------------
+# package install window.
+#-------------------------------------------------------
+proc do_help {{w .help}} {
+ catch {destroy $w}
+ toplevel $w
+ wm title $w "Help"
+ wm iconname $w "Help"
+ button $w.ok -text OK -command "destroy $w"
+ message $w.t -relief raised -bd 2 \
+ -text "You can install, deinstall and list info on the available packages. To select a package and see its complete description, press mouse button 1 over the package name. To install a selected package, press the Install button. To exit, press the \"Quit\" button."
+ pack append $w $w.ok {bottom fillx} $w.t {expand fill}
+}
+#-------------------------------------------------------
+# Apply a command to a package.
+#-------------------------------------------------------
+proc apply_to_pkg {s} {
+ apply_to_pkg_err $s ""
+}
+#-------------------------------------------------------
+# Apply a command to a package, with error stream redirection instructions.
+#-------------------------------------------------------
+proc apply_to_pkg_err {s errredir} {
+ global pkgname
+ .f.t delete 0.0 end
+ if {$pkgname == ""} {
+ .f.t insert end "You must double click on a package name first!"
+ } else {
+ apply_to_pkg_int "$s $pkgname" "2>&1"
+ }
+}
+proc apply_to_pkg_int {s errredir} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| sh -c \"$s $errredir\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+#-------------------------------------------------------
+# Invoke an arbitrary command.
+#-------------------------------------------------------
+proc do_command {s} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| $s} r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+# local variables:
+# mode: csh
+# compile-command: ""
+# comment-start: "# "
+# comment-start-skip: "# "
+# end: