makescript
chris at umcp-cs.UUCP
chris at umcp-cs.UUCP
Mon Aug 1 01:42:52 AEST 1983
The following is a revised version of makescript, and its manual
entry. Makescript is designed for mailing code to UUCP, CSNet,
and ARPAnet sites. It gets around the "."-at-beginning-of-line
bug in many SMTP mailers, and around the file-contains-<<-marker
problem (which isn't usually a problem, but...).
This makescript generates shorter scripts than the earlier version
I submitted, and also works on 4.1a/4.1c systems (thanks to Marshall
Rose at UCI).
- Chris
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
all=TRUE
fi
/bin/echo 'Extracting makescript.c'
sed 's/^X//' <<'//go.sysin dd *' >makescript.c
#ifndef lint
static char sccsid[] = "@(#)makescript.c U of Maryland ACT 17-Dec-1982";
#endif
X/*
* Makescript - make a shell script which extracts files from itself
*
* Modified by Marshall Rose @ UCI to handle 4.1a directories using
* the directory library routines.
*
* Compilation command:
% cc -O -s -o makescript makescript.c
*/
X/* #define LIBNDIR /* to use directory library */
#define max(a,b) ((a) > (b) ? (a) : (b))
#include <stdio.h>
#include <a.out.h>
#include <pwd.h>
#include <sys/types.h>
#ifndef LIBNDIR
#include <sys/dir.h>
#else LIBNDIR
#include <ndir.h>
#endif LIBNDIR
#include <sys/stat.h>
int RootID, BinID, DaemonID;
char *progname; /* Name of this program (argv[0]) */
FILE *outfile; /* Script being built */
int Level; /* Nest level */
char WDir[BUFSIZ];
char *Tail ();
FILE *popen ();
main (argc, argv)
register argc;
register char **argv;
{
int Append = 0, existed, rflag = 1;
extern char _sobuf[];
setbuf (stdout, _sobuf);
--argc;
progname = *argv++;
while (argc > 1 && **argv == '-') {
argc--;
switch (argv++[0][1]) {
case 'a':
Append++;
break;
case 'p':
rflag = 0;
break;
default:
goto usage;
}
}
if (argc < 2) {
usage:
printf ("Usage: %s [-a] [-p] <scriptname> file1 [ file2... ]\n",
progname);
exit (1);
}
existed = !access (*argv, 0);
if (existed && !Append) { /* file exists... */
if (isatty (fileno (stdin))) {
char answer[20];
printf ("%s exists. Overwrite? ", *argv);
fflush (stdout);
fgets (answer, sizeof answer, stdin);
if (*answer != 'y' && *answer != 'Y') {
printf ("Whatever you say.\n");
exit (1);
}
}
}
outfile = fopen (*argv, Append ? "a" : "w");
if (outfile == NULL) {
printf ("%s: Can't open %s: ", progname, *argv);
psyserr ();
exit (1);
}
GetIDs ();
if (rflag) {
register FILE *f = popen ("/bin/pwd", "r");
fread (WDir, 1, sizeof WDir, f);
WDir[strlen(WDir) - 1] = 0;
pclose (f);
}
if (!existed || !Append)
fprintf (outfile, "\
: Run this shell script with \"sh\" not \"csh\"\n\
PATH=:/bin:/usr/bin:/usr/ucb\n\
export PATH\n\
all=FALSE\n\
if [ $1x = -ax ]; then\n\
\tall=TRUE\n\
fi\n");
printf ("%s:\n", existed && Append ? "Appending" : "Installing");
fflush (stdout);
++Level;
++argv;
while (--argc > 0)
if (rflag) {
register char *n = Tail (*argv++);
if (n)
Add (n);
}
else
Add (*argv++);
printf ("\nDone.\n");
exit (0);
}
X/* Return the trailing component of a pathname if it is absolute, sneakily
changing directories if necessary */
char *
Tail (n)
register char *n;
{
register char *p, *dir;
if (*n != '/') { /* Then it isnt absolute */
rel:
if (chdir (WDir)) {
printf ("\nCan't change to directory \"%s\"", WDir);
fflush (stdout);
}
return n;
}
dir = n;
for (p = n; *p;)
if (*p++ == '/' && *p)
n = p; /* Find last slash */
if (n == dir)
goto rel;
n[-1] = 0;
if (chdir (dir)) {
printf ("\nCan't change to directory \"%s\"", dir);
fflush (stdout);
return 0;
}
return n;
}
X/* Set up the ID numbers for Root, Bin, and Daemon */
GetIDs () {
register struct passwd *p;
struct passwd *getpwnam ();
/* RootID = 0; */ /* Already set */
if (p = getpwnam ("bin"))
BinID = p -> pw_uid;
if (p = getpwnam ("daemon"))
DaemonID = p -> pw_uid;
endpwent ();
}
X/* Write, to stdout, the system error message associated with the system
error number errno */
psyserr ()
{
extern errno, sys_nerr;
extern char *sys_errlist[];
register char *c;
c = errno < sys_nerr ? sys_errlist[errno] : "Unknown error";
printf ("%s\n", c);
fflush (stdout);
}
X/* Add the named file into the script, performing the right actions depending
on the type of the file, and changing the owner/mode as appropriate. */
Add (fn)
register char *fn;
{
struct exec test_exec;
struct stat st;
char *u;
register level = Level;
if (stat (fn, &st)) {
printf ("%s: Can't access \"%s\": ", progname, fn);
psyserr ();
return;
}
while (--level > 0)
putchar ('\t');
printf ("%s", fn);
fflush (stdout);
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (st.st_size >= sizeof test_exec) {
FILE *fp = fopen (fn, "r");
if (fp == 0) {
printf (": can't open\n");
fflush (stdout);
return;
}
if (fread (&test_exec, sizeof test_exec, 1, fp) == 1
&& !N_BADMAG (test_exec)) {
fclose (fp);
if (AddExec (fn, " (executable)"))
return;
break;
}
fclose (fp);
}
if (IsDataFile (fn) ? AddExec (fn, " (data)") : AddFile (fn))
return;
break;
case S_IFDIR:
if (AddDir (fn))
return;
break;
case S_IFCHR:
AddNode (fn, "character", st.st_dev);
break;
case S_IFBLK:
AddNode (fn, "block", st.st_dev);
break;
#ifdef S_IFMPC /* not in 4.1a */
case S_IFMPC:
case S_IFMPB:
printf (" Multiplexed!\n");
fflush (stdout);
return;
#endif S_IFMPC
}
if (st.st_uid == RootID) {
u = "root";
goto changeown;
}
if (st.st_uid == BinID) {
u = "bin";
goto changeown;
}
if (st.st_uid == DaemonID) {
u = "daemon";
changeown:
fprintf (outfile, "\
if [ $all = TRUE ]; then\n\
\t/bin/echo '\tChanging owner to \"%s\"'\n\
\t/etc/chown %s %s\n\
else\n\
\t/bin/echo '\tOriginal owner was \"%s\"'\n\
fi\n",
u, u, fn, u);
}
fprintf (outfile, "\
if [ $made = TRUE ]; then\n\
\t/bin/chmod %o %s\n\
\t/bin/echo -n '\t'; /bin/ls -ld %s\n\
fi\n",
st.st_mode & 07777, fn, fn);
if ((st.st_mode & S_IFMT) != S_IFDIR) {
putchar ('\n');
fflush (stdout);
}
}
X/* Add a character or block device */
AddNode (fn, type, dev)
register char *fn, *type;
dev_t dev;
{
register ma = major (dev), mi = minor (dev);
printf (" (%s special)", type);
fflush (stdout);
fprintf (outfile, "\
if [ $all = TRUE ]; then\n\
\t/bin/echo 'Making %s special device \"%s\" number (%d, %d)'\n\
\t/etc/mknod %s %c %d %d\n\
made=TRUE\n\
else\n\
\t/bin/echo 'Not making %s special device \"%s\" number (%d, %d)'\n\
made=FALSE\n\
fi\n",
type, fn, ma, mi, fn, *type, ma, mi, type, fn, ma, mi);
}
AddFile (fn)
register char *fn;
{
register c, wantx = 1;
register FILE *fp = fopen (fn, "r");
if (fp == NULL) {
printf (": can't open\n");
fflush (stdout);
return -1;
}
fprintf (outfile,
"/bin/echo 'Extracting %s'\nsed 's/^X//' <<'//go.sysin dd *' >%s\n",
fn, fn);
while ((c = getc (fp)) != EOF) {
if (wantx) {
#ifndef ALLX
if (c == 'X' || c == '.' || c == '/')
#endif ALLX
putc ('X', outfile);
wantx = 0;
}
putc (c, outfile);
if (c == '\n')
wantx++;
}
fclose (fp);
fprintf (outfile, "//go.sysin dd *\nmade=TRUE\n");
return 0;
}
IsDataFile (fn)
char *fn;
{
int pipes[2];
char buf[200];
if (pipe (pipes)) return -1;/* call it data anyway */
fflush (stdout), fflush (stderr);
if (vfork () == 0) {
close (0);
dup2 (pipes[1], 1);
close (pipes[0]);
close (pipes[1]);
execl ("/usr/bin/file", "file", fn, 0);
fprintf (stderr, " can't exec /usr/bin/file");
printf ("%s: data", fn);
fflush (stdout), fflush (stderr);
_exit (0);
}
read (pipes[0], buf, sizeof buf);
close (pipes[0]);
close (pipes[1]);
while (wait (0) != -1)
;
if (strlen (buf) <= strlen (fn)) return 1;
return !contains (buf + strlen (fn), "text");
}
AddExec (fn, type)
register char *fn;
char *type;
{
printf (type);
fflush (stdout);
fprintf (outfile,
"/bin/echo 'Decoding %s'\nuudecode << '//go.sysin dd *'\n",
fn);
fflush (outfile);
if (vfork () == 0) {
dup2 (fileno (outfile), 1);
execl ("/usr/ucb/uuencode", "uuencode", fn, fn, 0);
printf ("Help! Can't exec /usr/ucb/uuencode\n");
exit (0);
}
while (wait (0) != -1)
;
fprintf (outfile, "//go.sysin dd *\nmade=TRUE\n");
return 0;
}
X/* Compare function for qsort */
DirCmp (d1, d2)
register struct direct *d1, *d2;
{
#ifndef LIBNDIR
return strncmp (d1 -> d_name, d2 -> d_name, DIRSIZ);
#else LIBNDIR
return strncmp (d1 -> d_name, d2 -> d_name,
max (d1 -> d_namlen, d2 -> d_namlen));
#endif LIBNDIR
}
X/* Add a directory: recursion time */
#ifndef LIBNDIR
AddDir (fn)
char *fn;
{
int f;
struct stat st;
char nmbuf[BUFSIZ];
register char *cp;
struct direct *d;
register struct direct *p, *dp, *pp;
fprintf (outfile,
"/bin/echo 'Making directory \"%s\"'\nmkdir %s\n", fn, fn);
if ((f = open (fn, 0)) < 0) {
printf (": can't open\n");
fflush (stdout);
return -1;
}
printf (": directory");
fflush (stdout);
fstat (f, &st);
if (st.st_size == 0) {
printf (" (empty)");
fflush (stdout);
close (f);
return 0;
}
d = (struct direct *) malloc (st.st_size);
pp = dp = (struct direct *) malloc (st.st_size);
if (read (f, (char *) d, st.st_size) != st.st_size) {
printf (": read error\n");
fflush (stdout);
close (f);
return -1;
}
close (f);
putchar ('\n');
fflush (stdout);
Level++;
for (p = d; p < &d[st.st_size/sizeof *d]; p++) {
if (p -> d_name[0] == '.') {
if (p -> d_name[1] == 0)
continue;
if (p -> d_name[1] == '.' && p -> d_name[2] == 0)
continue;
}
if (p -> d_ino)
*pp++ = *p;
}
free (d);
qsort (dp, pp - dp, sizeof *dp, DirCmp);
for (cp = nmbuf; *cp++ = *fn++; )
;
cp[-1] = '/';
for (p = dp; p < pp; p++) {
strncpy (cp, p -> d_name, DIRSIZ);
cp[DIRSIZ] = 0;
Add (nmbuf);
}
Level--;
free (dp);
fprintf (outfile, "made=TRUE\n");
return 0;
}
#else LIBNDIR
AddDir (fn)
char *fn;
{
int f,g;
char nmbuf[BUFSIZ];
char *cp;
struct direct *d,
*dp,
*p;
DIR * dd;
fprintf (outfile,
"/bin/echo 'Making directory \"%s\"'\nmkdir %s\n", fn, fn);
if ((dd = opendir (fn)) == NULL) {
printf (": can't open\n");
fflush (stdout);
return (-1);
}
printf (": directory");
fflush (stdout);
for (f = 0; p = readdir (dd); f++)
continue;
if (f == 0) {
empty: ;
printf (" (empty)");
fflush (stdout);
closedir (dd);
return 0;
}
again: ;
f += 10;
d = (struct direct *) malloc ((unsigned) (f * sizeof (struct direct)));
rewinddir (dd);
for (dp = d, g = 0; p = readdir (dd);) {
if ((p -> d_namlen == 1 && !strcmp (p -> d_name, "."))
|| (p -> d_namlen == 2 && !strcmp (p -> d_name, "..")))
continue;
if (f <= g++) { /* directory grew!!! */
for (f = g; p = readdir (dd); f++)
continue;
goto again;
}
dp -> d_ino = p -> d_ino;
dp -> d_reclen = sizeof (struct direct);
dp -> d_namlen = p -> d_namlen;
strcpy (dp -> d_name, p -> d_name);
dp++;
}
if (d == dp) {
free (d);
goto empty;
}
closedir (dd);
putchar ('\n');
fflush (stdout);
Level++;
qsort (d, dp - d, sizeof *dp, DirCmp);
for (cp = nmbuf; *cp++ = *fn++;);
cp[-1] = '/';
for (p = d; p < dp; p++) {
strcpy (cp, p -> d_name);
Add (nmbuf);
}
Level--;
free (d);
fprintf (outfile, "made=TRUE\n");
return 0;
}
#endif LIBNDIR
X/* Return true if "little" is contained within "big" */
contains (big, little)
register char *big, *little;
{
register char *bp, *lp;
while (*big) {
if (*big++ != *little) continue;
bp = big, lp = little + 1;
while (*lp)
if (*bp++ != *lp++)
goto cont;
return 1;
cont:;
}
return 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 644 makescript.c
/bin/echo -n ' '; /bin/ls -ld makescript.c
fi
/bin/echo 'Extracting makescript.1l'
sed 's/^X//' <<'//go.sysin dd *' >makescript.1l
X.TH MAKESCRIPT 1L 15-February-83
X.SH NAME
makescript \- make a self-extracting archive shell script
X.SH SYNOPSIS
X.I makescript
[ -a ] [ -p ] scriptname file1 [ . . . filen ]
X.SH DESCRIPTION
X.I Makescript
writes a shell script to
X.I scriptname,
which when executed, extracts the files which were given as arguments.
It is intended for mailing sets of files to other systems.
Non-text files will be
X.I uuencode\c
-ed.
The
X.I -a
option causes
X.I makescript
to append to an existing script. The
X.I -p
option causes it to automatically use all full pathnames for files. If not
used, then all absolute filenames will be converted to their basenames only.
X.SH AUTHORS
Chris Torek, Fred Blonder
X.SH FILES
X/bin/ls, /bin/echo, /bin/sh, /bin/chmod, /usr/bin/sed, /usr/ucb/uuencode,
X/usr/ucb/uudecode
X.SH "SEE ALSO"
uuencode(1)
X.SH DIAGNOSTICS
X.SH BUGS
The programs: sh, echo, ls, sed, chmod and uudecode must exist on the receiving
system.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
/bin/chmod 644 makescript.1l
/bin/echo -n ' '; /bin/ls -ld makescript.1l
fi
--
In-Real-Life: Chris Torek, Univ of MD Comp Sci
UUCP: {seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet: chris at umcp-cs
ARPA: chris.umcp-cs at UDel-Relay
More information about the Comp.sources.unix
mailing list