DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T s

⟦90fd47172⟧ TextFile

    Length: 15359 (0x3bff)
    Types: TextFile
    Names: »shar.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/shar2/shar.c« 

TextFile

/*
 *	$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);
}