|
|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T s
Length: 15359 (0x3bff)
Types: TextFile
Names: »shar.c«
└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
└─⟦this⟧ »EUUGD11/euug-87hel/sec1/shar2/shar.c«
/*
* $Header$
*/
/********************************************************
* *
* shar.c *
* *
********************************************************/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/dir.h>
/*
* External functions.
*/
extern char *malloc();
extern int strlen();
extern char *strcpy();
extern int strcmp();
extern int getarg();
extern stat();
extern char strcat();
extern long atol();
/*
* External variables.
*/
extern int optind;
extern char *optarg;
/*
* File tree struture:
*
* Defines files or directories, and holds the processing
* state flags.
*/
typedef struct Leaf {
struct Leaf *Parent,*Sybling,*Child; /* tree linkage */
char *Name; /* leaf name */
char Flags; /* state flags */
u_short Mode; /* original permissions */
long Size; /* file size */
} Leaf;
#define F_FILE 01 /* file type */
#define F_DIR 02 /* directory type */
#define F_WALK 04 /* traversed */
#define F_USED 010 /* written */
#define F_BAD 020 /* unarchivable object */
/*
* Out file linkage.
*/
typedef struct Ref {
struct Ref *Next; /* pointer to next leaf reference */
struct Leaf *Leaf; /* associated leaf */
} Ref;
typedef struct Out {
struct Out *Next; /* pointer to next out file */
short Number; /* file number */
long Size; /* approximate file size */
struct Ref *Ref; /* list of contained leaves */
} Out;
/*
* Static variables.
*/
static char *RootName = NULL; /* output file name prefix */
static long Size = 60 * 1024; /* target file size */
static char Quiet = 0; /* generate quiet code */
static char Check = 0; /* check file sizes */
static char Perm = 0; /* use original permissions */
static char OverWrite = 0; /* overwrite existing files */
static char InStream = 0; /* take names from standard input */
static Leaf Dot = { /* relative tree */
NULL,NULL,NULL,".",F_DIR};
static Leaf Abs = { /* absolute tree */
NULL,NULL,NULL,"",F_DIR};
static char CharBuf[128]; /* useful string buffer */
static char *SourceName = NULL; /* original file name string */
static char PathName[1024]; /* pathname return string */
static int PathSp = 0; /* path stack pointer */
static char *PathStack[64]; /* path name stack */
static int FileCount = 0; /* output file number */
static Out *FileList = NULL; /* list of output files */
static Out *CurFile = NULL; /* current file */
static char *Terminal = /* file terminator */
"\\Rogue\\Monster\\";
static char Buffer[4096]; /* file data buffer */
/*
* Make path name:
*
* The passed tree leaf is converted to a path name string.
* The result is left in the global variable "PathName".
*/
static MakePathName(tre)
register Leaf *tre;
{
/*
* Climb the tree, pushing file names on to the stack.
*/
for (PathSp = 0; tre; tre = tre->Parent)
PathStack[PathSp++] = tre->Name;
/*
* Unwind the stack and form the path name.
*/
strcpy(PathName,PathStack[--PathSp]);
while (PathSp--){
strcat(PathName,"/");
strcat(PathName,PathStack[PathSp]);
}
}
/*
* Parse file name:
*
* This creates an entry in the file tree from the passed leaf.
* A "." has special meaning in that there is no movement down
* the tree. An attempt to move through a name which is not a
* directory also causes an error. A ".." is legal if a move outside
* the tree top is not attempted. A pointer to the terminal leaf
* is returned.
*/
static Leaf *ParseFileName(tre,nam)
register Leaf *tre;
register char *nam;
{
/*
* Locals.
*/
register int i;
/*
* Extract current file name.
*/
for (i = 0; *nam && *nam != '/';)
CharBuf[i++] = *nam++;
CharBuf[i] = '\0';
/*
* Handle the "." and ".." cases.
*/
if (!strcmp(CharBuf,".")){
if (*nam)
return (ParseFileName(tre,++nam));
return (tre);
} else if (!strcmp(CharBuf,"..")){
if (!tre->Parent){
fprintf(stderr,"untraversable path name: \"%s\"\n",SourceName);
return (NULL);
} else if (*nam)
return (ParseFileName(tre->Parent,++nam));
else
return (tre->Parent);
} if (!(tre->Flags & F_DIR)){
/*
* The current tree leaf is not a directory, give an error.
*/
fprintf(stderr,"\"%s\" in \"%s\" is not a directory\n",CharBuf,
SourceName);
return (NULL);
} else {
register Leaf *wlk,*owk;
/*
* See if the name is already in the tree.
*/
for (owk = NULL, wlk = tre->Child; wlk; wlk = (owk = wlk)->Sybling)
if (!strcmp(CharBuf,wlk->Name)){
if (owk){
owk->Sybling = wlk->Sybling;
wlk->Sybling = tre->Child;
tre->Child = wlk;
}
break;
}
/*
* If the name doesn't already exist in the tree, create it.
*/
if (!wlk){
struct stat fs;
wlk = (Leaf *) malloc(sizeof(Leaf));
wlk->Parent = tre;
wlk->Sybling = tre->Child;
tre->Child = wlk;
wlk->Child = NULL;
wlk->Name = strcpy(malloc(strlen(CharBuf) + 1),CharBuf);
/*
* Form the path name and get the file stats.
*/
MakePathName(wlk);
stat(PathName,&fs);
/*
* Only directories and files are acceptable; too late
* to fix things, but give an error and mark bad.
*/
wlk->Flags = 0;
if (fs.st_mode & S_IFDIR)
wlk->Flags |= F_DIR;
else if (fs.st_mode & S_IFREG){
wlk->Flags |= F_FILE;
wlk->Size = fs.st_size;
} else {
fprintf(stderr,"\"%s\" is not a file or directory\n",SourceName);
wlk->Flags |= F_BAD;
}
/*
* Save the original permissions.
*/
wlk->Mode = fs.st_mode;
}
/*
* If required, continue to parse the name.
*/
if (*nam)
return (ParseFileName(wlk,++nam));
else
return (wlk);
}
}
/*
* Expand filter:
*
* This removes the "." and ".." objects from the directory scans.
*/
static int ExpandFilter(dir)
register struct direct *dir;
{
/*
* Do gross string comparisons.
*/
return (strcmp(dir->d_name,".") && strcmp(dir->d_name,".."));
}
/*
* Expand tree:
*
* The passed leaf must be a directory. This function recursively
* expands the directory until its bottoms out.
*/
static ExpandTree(tre)
Leaf *tre;
{
/*
* Locals.
*/
register int i,Count;
struct direct **NameList;
/*
* Get the names of all objects in the directory.
*/
MakePathName(tre);
SourceName = PathName;
if ((Count = scandir(PathName,&NameList,ExpandFilter,NULL)) < 0){
fprintf(stderr,"Cannot access \"%s\"\n",SourceName);
tre->Flags = (tre->Flags & ~F_DIR) | F_BAD;
return;
} else if (!Count)
return;
/*
* See if a new entry needs to be defined. If so create it.
* If the object is a directory, expand its tree too.
*/
for (i = 0; i < Count; ++i){
register Leaf *obj;
struct stat fs;
for (obj = tre->Child; obj; obj = obj->Sybling)
if (!strcmp(obj->Name,NameList[i]->d_name))
break;
if (!obj){
/*
* Create the new entry.
*/
obj = (Leaf *) malloc(sizeof(Leaf));
obj->Parent = tre;
obj->Sybling = tre->Child;
tre->Child = obj;
obj->Child = NULL;
obj->Name = strcpy(malloc(strlen(NameList[i]->d_name) + 1),
NameList[i]->d_name);
/*
* Get the file status of the object.
*/
MakePathName(obj);
stat(PathName,&fs);
/*
* Determine the file type.
*/
obj->Flags = 0;
obj->Mode = fs.st_mode;
if (fs.st_mode & S_IFDIR)
obj->Flags |= F_DIR;
else if (fs.st_mode & S_IFREG){
obj->Flags |= F_FILE;
obj->Size = fs.st_size;
} else {
fprintf(stderr,"\"%s\" is not a file or directory\n",PathName);
obj->Flags |= F_BAD;
}
}
/*
* See if further expansion is required.
*/
free(NameList[i]);
if (obj->Flags & F_DIR)
ExpandTree(obj);
}
free(NameList);
}
/*
* Make out files:
*
* This runs recursively moves through the passed tree and
* adds file leaves to output files.
*/
static MakeOutFiles(tre)
register Leaf *tre;
{
/*
* Locals.
*/
register Leaf *wlk;
register Ref *ref;
/*
* Move through the children of the passed tree. Directories
* cause immediate descent and bad entries are ignored.
*/
for (wlk = tre->Child; wlk; wlk = wlk->Sybling)
if (wlk->Flags & F_DIR)
MakeOutFiles(wlk);
else if (wlk->Flags & F_FILE){
/*
* See if the leaf is too big for the current file, flush.
*/
if (CurFile && (CurFile->Size + wlk->Size) > Size){
CurFile->Number = ++FileCount;
CurFile->Next = FileList;
FileList = CurFile;
CurFile = NULL;
}
/*
* Make sure that a current file exists.
*/
if (!CurFile){
CurFile = (Out *) malloc(sizeof(Out));
CurFile->Size = 0;
CurFile->Ref = NULL;
}
/*
* Add the leaf to the current file.
*/
ref = (Ref *) malloc(sizeof(Ref));
ref->Next = CurFile->Ref;
CurFile->Ref = ref;
ref->Leaf = wlk;
CurFile->Size += wlk->Size;
}
}
/*
* Clear tree:
*
* The WALK bits in the passed tree are cleared.
*/
static ClearTree(tre)
register Leaf *tre;
{
/*
* Locals.
*/
register Leaf *wlk;
/*
* Clear the current tree. Move through the children, only directories
* need to be cleared.
*/
tre->Flags &= ~F_WALK;
for (wlk = tre->Child; wlk; wlk = wlk->Sybling)
if (wlk->Flags & F_DIR)
ClearTree(wlk);
}
/*
* Check tree:
*
* This makes sure that the directory structure is in place
* within the current archive file.
*/
static CheckTree(stm,tre)
FILE *stm;
register Leaf *tre;
{
/*
* Make sure this directory exists, and if so, is unmarked.
*/
if (!tre || !tre->Parent || (tre->Flags & F_WALK))
return;
/*
* Handle its parent then do it.
*/
CheckTree(stm,tre->Parent);
MakePathName(tre);
fprintf(stm,"if `test ! -d %s`\nthen\n mkdir %s\n",PathName,PathName);
if (!Quiet)
fprintf(stm," echo \"mkdir %s\"\n",PathName);
fprintf(stm,"fi\n");
tre->Flags |= F_WALK;
}
/*
* Output ref:
*
* The passed reference is written to the stream, using the
* user's rules.
*/
static OutputRef(stm,ref)
FILE *stm;
register Ref *ref;
{
/*
* Locals.
*/
register int count;
FILE *inp;
/*
* Make sure that the tree to support this file already defined.
*/
CheckTree(stm,ref->Leaf->Parent);
/*
* Put out the existance check header if required. Also do the
* file name echoing.
*/
MakePathName(ref->Leaf);
if (!OverWrite)
fprintf(stm,"if `test ! -s %s`\nthen\n",PathName);
if (!Quiet)
fprintf(stm,"echo \"writting %s\"\n",PathName);
/*
* Open the input file.
*/
if (!(inp = fopen(PathName,"r"))){
fprintf(stderr,"Unable to read \"%s\"\n",PathName);
return;
}
fprintf(stm,"cat > %s << '%s'\n",PathName,Terminal);
/*
* Move the file data into the archive.
*/
while (count = fread(Buffer,sizeof(char),sizeof(Buffer),inp))
fwrite(Buffer,sizeof(char),count,stm);
/*
* Take care of the tail end linkage.
*/
fprintf(stm,"%s\n",Terminal);
if (!OverWrite && !Quiet)
fprintf(stm,"else\n echo \"will not over write %s\"\nfi\n",PathName);
else if (!OverWrite)
fprintf(stm,"fi\n");
/*
* Handle the permission stuff.
*/
if (Perm)
fprintf(stm,"chmod %o %s\n",ref->Leaf->Mode & 07777,PathName);
/*
* Add the checking code.
*/
if (Check){
fprintf(stm,"if [ `wc -c %s | awk '{printf $1}'` -ne %d ]\nthen\n",
PathName,ref->Leaf->Size);
fprintf(stm,
"echo `wc -c %s | awk '{print \"Got \" $1 \", Expected \" %d}'`\nfi\n",
PathName,ref->Leaf->Size);
}
/*
* Close the input file.
*/
fclose(inp);
}
/*
* Pack archive:
*
* This turns the passed file specification into a packed archive.
*/
static PackArchive(fil)
Out *fil;
{
/*
* Locals.
*/
register Ref *ref;
FILE *stm;
/*
* Open the output file, failure here causes a return.
*/
sprintf(CharBuf,"%s.%d",RootName,fil->Number);
if (!(stm = fopen(CharBuf,"w"))){
fprintf(stderr,"Unable to open archive: %s\n",CharBuf);
return;
} else
fprintf(stderr,"\tForming archive: %s\n",CharBuf);
/*
* Put out the header.
*/
fprintf(stm,"#!/bin/sh\n");
fprintf(stm,"# to extract, remove the header and type \"sh filename\"\n");
/*
* Move through all file references and put them out.
*/
ClearTree(&Abs);
ClearTree(&Dot);
for (ref = fil->Ref; ref; ref = ref->Next)
OutputRef(stm,ref);
/*
* Close the output file before leaving.
*/
if (!Quiet)
fprintf(stm,"echo \"Finished archive %d of %d\"\n",fil->Number,FileCount);
fprintf(stm,"exit\n");
fclose(stm);
}
/*
* Main body:
*
* This parses the command line and executes its directives.
*/
main(argc,argv)
int argc;
char *argv[];
{
/*
* Locals.
*/
register int c;
Leaf *tre;
/*
* Command line switches:
*
* -c = add checking code
* -f <name> = output file name root
* -i = take names from standard input
* -m <number> = sets the target segment size
* -o = overwrite existing files
* -p = use original permissions
* -q = generate quite code
*/
while ((c = getarg(argc,argv,"cf:im:opq")) != EOF)
switch (c){
case 'c':
Check = 1;
break;
case 'f':
RootName = optarg;
break;
case 'i':
InStream = 1;
break;
case 'm':
Size = atol(optarg) * 19; /* reserve 5% for overhead */
Size /= 20;
break;
case 'o':
OverWrite = 1;
break;
case 'p':
Perm = 1;
break;
case 'q':
Quiet = 1;
break;
case '?':
fprintf(stderr,"Unknown switch: -%c\n",c);
break;
default:
/*
* File names can be absolute or relative, decide which
* tree the filename goes in.
*/
SourceName = optarg;
if (*optarg == '/')
tre = ParseFileName(&Abs,++optarg);
else
tre = ParseFileName(&Dot,optarg);
/*
* If the name results in a directory, expand the directory.
*/
if (tre && (tre->Flags & F_DIR))
ExpandTree(tre);
}
/*
* Do tests, additional tree building if required.
*/
if (!RootName){
fprintf(stderr,"A \"-f <filename>\" must be specified\n");
exit(1);
} else if (InStream){
char buf[1024];
while (scanf("%s",buf) == 1){ /* read names from stdin */
SourceName = buf;
if (buf[0] == '/')
tre = ParseFileName(&Abs,&buf[1]);
else
tre = ParseFileName(&Dot,buf);
/*
* If a directory, expand it.
*/
if (tre && (tre->Flags & F_DIR))
ExpandTree(tre);
}
}
/*
* Construct the list of output files.
*/
MakeOutFiles(&Abs);
MakeOutFiles(&Dot);
if (CurFile){
CurFile->Number = ++FileCount;
CurFile->Next = FileList;
FileList = CurFile;
}
fprintf(stderr,"*** Archive contains %d files ***\n",FileCount);
/*
* Move through the file list and pack up the data.
*/
for (CurFile = FileList; CurFile; CurFile = CurFile->Next)
PackArchive(CurFile);
}