static char rcsid[] = "@(#)$Id: elmregister.c,v 1.42.2.2 2007/09/02 13:31:06 hurtta Exp $";

/******************************************************************************
 *  The Elm (ME+) Mail System  -  $Revision: 1.42.2.2 $   $State: Exp $
 *
 *  Author: Kari Hurtta <hurtta+elm@posti.FMI.FI> (was hurtta+elm@ozone.FMI.FI)
 *****************************************************************************/

#include "defs_major.h"
#include "patchlevel.h"

#include "reghelper.h"

/*  Protocol in PIPE:  
 *
 *        file descriptor given on $ELM_INSTALLER
 *        installer root given on  $ELM_ROOT
 *
 *         filenames are relative to $ELM_ROOT
 *
 * -- module
 * 'm' module  {rest of line}
 *
 * -- copy file
 *
 * 'F' user group mode filename '\n'
 * 'd' user group mode filename '\n'          (document)
 * 's' user group mode filename '\n'          (stage file)
 *
 * -- create directory
 *
 * 'M' user group mode filename '\n'
 *
 * --  execute
 * {n}  user group mode filename '\n'
 * {n}  user group mode filename'\n'
 *
 * 'E' filename  {n} xx yy {n} '\n'
 *
 * -- config file touched on replay time
 *
 * 'C' user group mode filename'\n'
 *
 * -- remove files
 *
 * 'R' file file file '\n'
 *
 * -- link file
 *
 * 'L' file file '\n'
 *
 */


#define IMPARGDIR "{--implicit-stage-directory--}"


/* putenv.c */
#ifndef PUTENV
extern int putenv   P_((char *));
#endif

/* strstr.c */
#ifndef STRSTR
extern char *strstr P_((char *,char *)); 
#endif

#ifdef PWDINSYS
#  include <sys/pwd.h>
#  include <sys/grp.h>                /* TODO: Not necessary correct .... */
#else
#  include <pwd.h>
#  include <grp.h>
#endif

#include <errno.h>
#ifndef ANSI_C
extern int errno;
#endif

#if defined(BSD_TYPE) && !defined(WEXITSTATUS)
typedef union wait S__;
#else
typedef int        S__;
#endif

static char  * filelist  = SYSTEM_DEFAULT_FILELIST;
static char  * stage_dir = STAGE_DIR;
static char  * register_pipe   = "$ELM_INSTALLER";  /* To be assigned */
int    register_fd     = -1;
static char  * installer_root  = "$ELM_ROOT";       /* To be assigned */

char * program_name = "elmregister";         /* To be assigned */
char * register_module = NULL;

static int create_pipe P_((int argc, char *argv[], int x, int *PID,
			   char *root, char *module)); 
static int create_pipe(argc,argv,x,PID,root,module) 
     int argc; 
     char *argv[]; 
     int x; 
     int *PID;
     char *root;
     char *module;
{
    static char array[] = "ELM_INSTALLER=XXXX";
    int filedes[2];
    int z = pipe(filedes);
    char *command;
    int pid;

    if (z < 0) {
	fprintf(stderr,"%s: pipe() failed\n",program_name);
	exit(1);
    }

    if (x >= argc) {
	fprintf(stderr,"%s: No command given\n",program_name);
	exit(1);
    }
    command = argv[x];

    fflush(stdout);

    pid = fork();    /* BRANCH child process */

    if (0 == pid) {
	int l   = strlen(root) + 12;
	char *array2 = malloc(l);


	sprintf(array,"ELM_INSTALLER=%d",filedes[1]);
    
	if (!array2) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",program_name,l);
	    exit(1);

	}
	sprintf(array2,"ELM_ROOT=%s",root);

	if (0 != putenv(array)) {
	    fprintf(stderr,"%s: putenv() failed\n",program_name);
	    exit(1);
	}
	if (0 != putenv(array2)) {
	    fprintf(stderr,"%s: putenv() failed\n",program_name);
	    exit(1);
	}

	if (module) {
	    int l   = strlen(root) + 15;
	    char *array3 = malloc(l);

	    sprintf(array3,"ELM_MODULE=%s",module);

	    if (0 != putenv(array3)) {
		fprintf(stderr,"%s: putenv() failed\n",program_name);
		exit(1);
	    }

	}

	close(filedes[0]);   /* read end */

	fprintf(stdout,"%s: Running %s ...\n",
		program_name,command);

	execvp(command,argv+x);
	fprintf(stderr,
		"%s: Failed to EXECute %s\n",program_name,command); 
	fflush(stderr);
	_exit(127);
    }

    if (pid < 0) {
	fprintf(stderr,"%s: fork() failed\n",program_name);
	exit(1);
    }
    close(filedes[1]);   /*write end */

    *PID=pid;
    return filedes[0];
}

#define MAX_ARGS       300

static void make_package_dir P_((char *y,char *root));
static void make_package_dir(y,root)
     char *y;
     char *root;
{
    int l = strlen(root);

    char *Z;
    char *x;

    if (l < 2)
	return;

    Z = strdup(y);
    if (0 != strncmp(Z,root,l)) {
	free(Z);
	return ;
    }

    for (x = Z + l; *x; x++) {

	if ('/' == *x) {
	    *x = '\0';

	    if (0 == mkdir(Z,0755)) {
		fprintf(stdout,
			"%s: parent directory %s created (of %s)\n",
			program_name,Z,y);
	    }


	    *x = '/';
	}


    } 

    free(Z);
}

struct log_record {
    char * buffer;
    int    buffer_len;

    char  * module_ptr;

    int   command_letter;
    char  * command_ptr;

    /* Following points to space on buffer ... */

    char  * user_ptr;
    char  * group_ptr;
    char  * mode_ptr;

    char  ** arg_ptrs;
}; 


struct log_entries {
    char                * version;
    struct log_record   * log;
    int                   log_count;
};

struct log_instance {
    FILE *file;
    char *filename;
    struct log_entries * entries;
    int entries_count;
};

static void clear_log_instance P_((struct log_instance *LI));
static void clear_log_instance(LI)
     struct log_instance *LI;
{
    LI->file          = NULL;
    LI->filename      = NULL;
    LI->entries       = NULL;
    LI->entries_count = 0;
}

static void add_bytes P_((FILE *F,char * buffer, int *ptr));
static void add_bytes(F,buffer,ptr)
     FILE *F;
     char * buffer; 
     int *ptr;
{


    while (1) {
	int c = fgetc(F);
	    
	if (EOF == c) {
	    fprintf(stderr,"%s: Failed to read from pipe or file (or EOF)\n",
		    program_name);
	    exit(1);
	}

	if (*ptr > MAX_PIPE_LOG) {
	    fprintf(stderr,"%s: Record from pipe or file too long\n",
		    program_name);
	    exit(1);
	}

	buffer[*ptr] = c;
	(*ptr)++;

	if (0 == c) {
	    /* END OF RECORD MARK */
	    return;
	}
    }

}

static void parse_log_tail P_((
			struct log_record *l,
			char buffer1[MAX_PIPE_LOG +10 ],   
			
			int ptr,

			int module_ptr,
			int command_ptr,
			int user_ptr,
			int group_ptr,
			int mode_ptr,
			
			int args_ptr[MAX_ARGS],
			int argnum));


static void parse_log P_((FILE *F, struct log_record *l));
static void parse_log(F,l)
     FILE *F; 
     struct log_record *l;
{
    char buffer1[MAX_PIPE_LOG +10 ];   /* write routines quarantee that
					  this is enough! */

    int ptr = 0;

    int c = fgetc(F);

    int module_ptr = -1;
    int command_ptr = -1;
    int user_ptr   = -1;
    int group_ptr  = -1;
    int mode_ptr   = -1;

    int args_ptr[MAX_ARGS];
    int argnum = 0;

    l->buffer     = NULL;
    l->buffer_len = 0;


    l->module_ptr  = NULL;
    l->command_ptr = NULL;
    l->user_ptr  = NULL;
    l->group_ptr = NULL;
    l->mode_ptr  = NULL;
    l->arg_ptrs  = NULL;

    if (EOF == c) {
	/* EOF or error */

	l->command_letter = EOF;

	return;
    }

    ungetc(c,F);  /* add_bytes reread character */

    command_ptr = ptr;  add_bytes(F,buffer1,&ptr);

    if (buffer1[0] != c) {
	fprintf(stderr,"%s: Character %c not buffered (reading from pipe or file)\n",
		program_name,c);
	exit(1);	
    }

    if ('m' == c) {
      
	module_ptr  = ptr;  add_bytes(F,buffer1,&ptr);   /* module  */
	command_ptr = ptr;  add_bytes(F,buffer1,&ptr);   /* command */
	c = buffer1[command_ptr];

    }

    l->command_letter = c;

    switch (c) {
    case 'F':     
    case 'd':     
	user_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* user  */
	group_ptr = ptr;   add_bytes(F,buffer1,&ptr);   /* group */
	mode_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* mode */
	args_ptr[0] = ptr; add_bytes(F,buffer1,&ptr);   /* target file */
	argnum = 1;
	break;

    case '{':   
	/* .... filename variable already readed */
	user_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* user  */
	group_ptr = ptr;   add_bytes(F,buffer1,&ptr);   /* group */
	mode_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* mode */
	args_ptr[0] = ptr; add_bytes(F,buffer1,&ptr);   /* target file */
	argnum = 1;
	break;

    case 'C':
    case 'M':    
    case 's':
	user_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* user  */
	group_ptr = ptr;   add_bytes(F,buffer1,&ptr);   /* group */
	mode_ptr  = ptr;   add_bytes(F,buffer1,&ptr);   /* mode */
	args_ptr[0] = ptr; add_bytes(F,buffer1,&ptr);   /* target file */
	argnum = 1;
	break;

    case 'L':
	args_ptr[0] = ptr; add_bytes(F,buffer1,&ptr);   /* file1 file */
	args_ptr[1] = ptr; add_bytes(F,buffer1,&ptr);   /* file2 file */
	argnum = 2;
	break;

    case 'R':
    case 'E': 

	do {
	    int n = ptr;
	    c = fgetc(F);

	    if (EOF == c) {
		fprintf(stderr,"%s: unexpected EOF\n",
			program_name);
		exit(1);
	    }

	    ungetc(c,F);  /* add_bytes reread character */

	    if ('\n' == c) 
		break;

	    if (argnum >= MAX_ARGS) {
		fprintf(stderr,"%s: Too many arguments on pipe or file\n",
			program_name);
		exit(1);	

	    }

	    args_ptr[argnum++] = ptr; add_bytes(F,buffer1,&ptr);

	    if (buffer1[n] != c) {
		fprintf(stderr,"%s: Character %c not buffered (reading from pipe or file)\n",
			program_name,c);
		exit(1);	
	    }
	    
	} while(1);

	
	break;

    default:
	fprintf(stderr,"%s: Bad command letter: %c (reading from pipe or file)\n",
		program_name,c);
	exit(1);	
    }

    c = fgetc(F);

    if ('\n' != c) {
	fprintf(stderr,"%s: newline expected: %c (reading from pipe or file)\n",
		program_name,c);
	exit(1);	
    }
    

    l->buffer_len = ptr;

    l->buffer     = malloc(l->buffer_len);
    if (!  l->buffer ) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		program_name,l->buffer_len);
	exit(1);   /* FAILURE */

    }

    parse_log_tail(l,buffer1,ptr,module_ptr,command_ptr,user_ptr,
		   group_ptr,mode_ptr,args_ptr,argnum);

}

static void parse_log_tail(l,buffer1,ptr,module_ptr,command_ptr,user_ptr,
			   group_ptr,mode_ptr,args_ptr,argnum)
     struct log_record *l;
     char buffer1[MAX_PIPE_LOG +10 ];   /* write routines quarantee that
					  this is enough! */

     int ptr;

     int module_ptr;
     int command_ptr;
     int user_ptr;
     int group_ptr;
     int mode_ptr;

     int args_ptr[MAX_ARGS];
     int argnum;
{

    memcpy(l->buffer,buffer1,l->buffer_len);

    if (module_ptr != -1)
	l->module_ptr  =  l->buffer + module_ptr;

    if (command_ptr != -1)
	l->command_ptr  =  l->buffer + command_ptr;

    if (user_ptr != -1)
	l->user_ptr  =  l->buffer + user_ptr;

    if (group_ptr != -1)
	l->group_ptr =  l->buffer + group_ptr;

    if (mode_ptr != -1)
	l->mode_ptr  = l->buffer  + mode_ptr;

    if (argnum > 0) {
	int i;

	l->arg_ptrs = malloc((argnum +1) * sizeof (l->arg_ptrs[0]));

	if (!l->arg_ptrs) {
	    fprintf(stderr,"%s: malloc %d items failed\n",
		    program_name,argnum+1);
	    exit(1);   /* FAILURE */
	}

	for (i = 0; i < argnum; i++) 
	    l->arg_ptrs[i] = l->buffer  + args_ptr[i];
	
	l->arg_ptrs[argnum] = NULL;
    }

    
}

static void add_entries P_((struct log_instance *LI));
static void add_entries(LI)
     struct log_instance *LI;
{
    struct log_entries * last = NULL;


    do {
	int c = fgetc(LI->file);

	if (EOF == c) {
	    /* EOF or error */
	    
	    return;
	}
	
	ungetc(c,LI->file);  /* add_bytes reread character */

	if ('[' == c) {
	    char version_buffer[MAX_PIPE_LOG];
	    int ptr = 0;
	 
	    add_bytes(LI->file,version_buffer,&ptr);

	    if (version_buffer[0] != c) {
		fprintf(stderr,"%s: Character %c not buffered (reading from file %s)\n",
			program_name,c,LI->filename);
		exit(1);	
	    }


	    if (ptr <3 || version_buffer[ptr-2] != ']') {
		fprintf(stderr,
			"%s: File %s is not elmregister filelist\n",
			program_name,LI->filename);
		exit(1);
	    }

	    c = fgetc(LI->file);
	    if ('\n' != c) {
		fprintf(stderr,"%s: newline expected: %c (reading from file %s)\n",
			program_name,c,LI->filename);
		exit(1);	
	    }

	    LI->entries = realloc(LI->entries,(LI->entries_count+1) * sizeof (LI->entries[0]));

	    if (! LI->entries) {
		fprintf(stderr,
			"%s: Failed to realloc %d items (file %s)\n",
			program_name,LI->entries_count+1,LI->filename);
		exit(1);
	    }
	    
	    last = & (LI->entries[LI->entries_count]);

	    LI->entries_count++;

	    last -> version = strdup(version_buffer);

	    if (!last -> version) {
		fprintf(stderr,
			"%s: Failed to strdup string (%d bytes?): %s (file %s)\n",
			program_name,ptr,version_buffer,LI->filename);
		exit(1);

	    }
	    last -> log       = NULL;
	    last -> log_count = 0;

	} else if (!last) {
	    fprintf(stderr,"%s: OOPS (file %s)!\n",
		    program_name,LI->filename);
	    exit(1);	
	} else {

	    last -> log = realloc(last -> log, 
				  (last -> log_count+1) * sizeof (last -> log[0]));
	    		
	    if (! last->log) {
		fprintf(stderr,
			"%s: Failed to realloc %d items (file %s)\n",
			program_name,last -> log_count+1,LI->filename);
		exit(1);
	    }

	    parse_log(LI->file,&( last -> log[last -> log_count] ));

	    last -> log_count++;
	    
	}
    } while (1);

}


static struct log_entries * add_last P_((struct log_instance *LI));
static struct log_entries * add_last(LI)
     struct log_instance *LI;
{
    char buffer[ MAX_PIPE_LOG];
    char *ptr;
    
    struct log_entries * last = NULL;
    
#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];
    
    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    /* Write version header --- */
    ptr = buffer;
    add_to_buffer(buffer, sizeof buffer,&ptr,my_version,strlen (my_version));
    *ptr++ = '\n';   /* Terminating NL on line */

    if (ptr-buffer != fwrite(buffer,1,ptr-buffer,LI->file)) {
	fprintf(stderr,"%s: write error (file %s)\n",
		program_name,LI->filename);
	fclose(LI->file);
	
	exit (1);  /* FAILURE */	
    }


    LI->entries = 
	realloc(LI->entries,(LI->entries_count+1) * 
		sizeof (LI->entries[0]));

    if (!LI->entries) {
	fprintf(stderr,
		"%s: Failed to realloc %d items (file %s)\n",
		program_name,LI->entries_count+1,LI->filename);
	
	fclose(LI->file);

	exit (1);  /* FAILURE */	
    }

    last = & (LI->entries[LI->entries_count]);
    
    LI->entries_count++;
    
    last -> version = strdup(my_version);
    
    if (!last -> version) {
	fprintf(stderr,
		"%s: Failed to strdup string (%d bytes?): %s\n",
		program_name,ptr-buffer-1,my_version);
	
	fclose(LI->file);
	exit(1);  /* FAILURE */	       	
    }
    last -> log       = NULL;
    last -> log_count = 0;

    return last;
}

static int take_arg P_((char *buffer, char *ptr, int L));
static int take_arg(buffer,ptr,L)
     char *buffer; 
     char *ptr;
     int L;
{
    int n;

    if (!ptr)
	return -1;
    n = ptr - buffer;
    if (n < 0 || n >= L) {
	fprintf(stderr,"%s: [take_arg] bad argument\n",program_name);
	exit(1);
    }
    return n;
}

static void move_record P_((struct log_instance *LI,struct log_entries * last,
			    struct log_record *L));
static void move_record(LI,last,L)
     struct log_instance *LI;
     struct log_entries * last;
     struct log_record *L;
{
    int module_ptr;
    int command_ptr;
    int user_ptr;
    int group_ptr;
    int mode_ptr;
    int args_ptr[MAX_ARGS];
    int argnum = 0;

    last -> log = realloc(last -> log, 
			  (last -> log_count+1) * sizeof (last -> log[0]));
	    
    last -> log[last -> log_count].buffer = malloc(L->buffer_len);
    if (!last -> log[last -> log_count].buffer) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",program_name,
		L->buffer_len);
	exit(1);
    }

    last -> log[last -> log_count].buffer_len = L->buffer_len;
    last -> log[last -> log_count].module_ptr = NULL;
    last -> log[last -> log_count].command_letter = L->command_letter;
    last -> log[last -> log_count].command_ptr = NULL;
    last -> log[last -> log_count].user_ptr    = NULL;
    last -> log[last -> log_count].group_ptr   = NULL;
    last -> log[last -> log_count].mode_ptr    = NULL;
    last -> log[last -> log_count].arg_ptrs     = NULL;
 
    module_ptr   = take_arg(L->buffer,L->module_ptr,L->buffer_len);
    command_ptr  = take_arg(L->buffer,L->command_ptr,L->buffer_len);
    user_ptr     = take_arg(L->buffer,L->user_ptr,L->buffer_len);
    group_ptr    = take_arg(L->buffer,L->group_ptr,L->buffer_len);
    mode_ptr     = take_arg(L->buffer,L->mode_ptr,L->buffer_len);

    while (L->arg_ptrs && L->arg_ptrs[argnum] && argnum < MAX_ARGS) {
	args_ptr[argnum] = take_arg(L->buffer,L->arg_ptrs[argnum],
				    L->buffer_len); 
	argnum++;
    }

    if (L->buffer_len != fwrite(L->buffer,1,L->buffer_len,LI->file)) {
	fprintf(stderr,
		"%s: write error (file %s)\n",
		program_name,LI->filename);
	fclose(LI->file);

        exit (1);  /* FAILURE */	
    }
    putc('\n',LI->file);

    parse_log_tail(& last -> log[last -> log_count],
		   L->buffer,
		   L->buffer_len,
		   module_ptr,
		   command_ptr,
		   user_ptr,
		   group_ptr,
		   mode_ptr,
		   args_ptr,
		   argnum);

    last -> log_count++;
}


static void log_copy_loop P_((FILE *fd, struct log_instance *target,
			      struct log_entries * last));
static void log_copy_loop(fd,target,last)
     FILE *fd; 
     struct log_instance *target;
     struct log_entries * last;
{

    while(1) {
	struct log_record L;
		
	parse_log(fd,&L);


	if (EOF != L.command_letter) {
	    
	    move_record(target,last,&L);

	}

	/* L.buffer and L.arg_ptrs are only think what are malloced */
	if (L.buffer) {
	    free(L.buffer);
	    L.buffer = NULL;
	}
	if (L.arg_ptrs) {
	    free(L.arg_ptrs);
	    L.arg_ptrs = NULL;
	}

	if (EOF == L.command_letter) {
	    fprintf(stderr,"%s: EOF from pipe\n",program_name);
	    break;
	}
    }    
}


static void remove_unlinked1 P_((struct log_instance *target,int Z,
				 char *relative, int *count));
static void remove_unlinked1(target,Z,relative,count)
     struct log_instance *target;
     int Z;
     char *relative; 
     int *count;
{
    int i;
    
    for (i = 0; i < target->entries_count && i < Z; i++) {

	struct log_record   * ptr2;

	for (ptr2 = target->entries[i].log; 
	     ptr2 < target->entries[i].log + 
		 target->entries[i].log_count; 
	     ptr2++) {

	    switch (ptr2->command_letter) {
	    case 'M':
	    case '{':  
	    case 's':  
	    case 'F':
	    case 'd':     
	    case 'C':
		
		if (ptr2->arg_ptrs && ptr2->arg_ptrs[0] &&
		    0 == strcmp(ptr2->arg_ptrs[0],relative)) {
		    (*count) ++;
		    
		    if (ptr2->buffer) {
			free(ptr2->buffer);
			ptr2->buffer   = NULL;
		    }
		    
		    if (ptr2->arg_ptrs) {
			free(ptr2->arg_ptrs);
			ptr2->arg_ptrs = NULL;
		    }
		    
		    ptr2->command_letter = '\0';   /* ENTRY DELETED */
		}
		break;

	    case 'L':  /* SECOND ARGUMENT IS NEW NAME */

		if (ptr2->arg_ptrs && ptr2->arg_ptrs[1] &&
		    0 == strcmp(ptr2->arg_ptrs[1],relative)) {
		    (*count) ++;
		    
		    if (ptr2->buffer) {
			free(ptr2->buffer);
			ptr2->buffer   = NULL;
		    }
		    
		    if (ptr2->arg_ptrs) {
			free(ptr2->arg_ptrs);
			ptr2->arg_ptrs = NULL;
		    }
		    
		    ptr2->command_letter = '\0';   /* ENTRY DELETED */
		}
	    

		break;
	    }

	}

    }
}

static char * make_rooted P_((struct log_record   * ptr, char *rel, char *root));
static char * make_rooted(ptr,rel,root)
     struct log_record   * ptr; 
     char *rel;
     char *root;
{
    int L = strlen(root);
    int l = strlen(rel) + 2 + L;
		    
    char * translated = malloc(l);

    if (!translated) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		program_name,l);
	exit (1);   /* FAILURE */
    }

    strcpy(translated,root);

    if ('/' == rel[0]) {
	fprintf(stderr,
		"%s: entry '%c' -- Not relative? %s\n",
		program_name,ptr->command_letter,rel);
    } else {
	if (L < 1 || root[L-1] != '/') {
	    translated[L] = '/';
	    translated[L+1] = '\0';
	}
    }
    strcat(translated,rel);
    
    return translated;
}

static void remove_unlinked P_((struct log_instance *target,char *root, 
				int *remove_count)); 
static void remove_unlinked(target,root,remove_count)
     struct log_instance *target;
     char *root;
     int *remove_count;
{
    int i;

    int y = 0;

    *remove_count = 0;
    
    for (i = 0; i < target->entries_count; i++) {

	struct log_record   * ptr2;

	for (ptr2 = target->entries[i].log; 
	     ptr2 < target->entries[i].log + 
		 target->entries[i].log_count; 
	     ptr2++) {

	    switch (ptr2->command_letter) {
		int j;

	    case 'R':   /* FILE DELETED */

		if (!y) {
		    y++;
		    fprintf(stdout,
			    "%s: Trimming unlinked files from %s\n",
			    program_name,target->filename);
		}

		for (j = 0; 
		     ptr2->arg_ptrs[j] && j < MAX_ARGS; 
		     j++) {

		    int count = 0;

		    char * translated =  
			make_rooted(ptr2,ptr2->arg_ptrs[j],root);
		    struct stat X;

		    if (stat(translated,&X) < 0) {
			
			remove_unlinked1(target,i,ptr2->arg_ptrs[j],
					 &count);

			if (count > 0) {
			    fprintf(stdout,
				    "%s: (%s) File %s is removed -- removing from log\n",
				    program_name,translated,ptr2->arg_ptrs[j]);
			    *remove_count += count;
			}
		    }

		    free(translated);
		}

		break;

	    }
	}	
    }

}

static void rewrite_list P_((struct log_instance *target)); 
static void rewrite_list(target)
     struct log_instance *target;
{
    FILE * new_fd;

    char *tempname;
    int l = strlen(target->filename);
    int i;

    tempname = malloc(l+4);
    if (!tempname) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		program_name,l);
	exit(1);   /* FAILURE */
    }
    strcpy(tempname,target->filename);
    strcat(tempname,"~N~");

    if (0 == unlink(tempname)) {
	fprintf(stderr,"%s: %s unlinked\n",program_name,tempname);
    }

    new_fd = fopen(tempname,"w+");
    if (!new_fd) {
    fail:
	fprintf(stderr,
		"%s: Failed to create %s -- %s not rewritten\n",
		program_name,tempname,target->filename);
	free(tempname);
	return;
    }

    fprintf(stdout,"%s: Rewriting %s\n",program_name,target->filename);

    for (i = 0; i < target->entries_count; i++) {
	char buffer[ MAX_PIPE_LOG];
	char *ptr;

	struct log_record   * ptr2;


	/* Write version header --- */
	ptr = buffer;
	add_to_buffer(buffer, sizeof buffer,&ptr,
		      target->entries[i].version,
		      strlen (target->entries[i].version));
	*ptr++ = '\n';   /* Terminating NL on line */

	if (ptr-buffer != fwrite(buffer,1,ptr-buffer,new_fd)) {
	    fprintf(stderr,"%s: write error (file %s)\n",
		    program_name,tempname);
	    fclose(new_fd);
	    goto fail;
	}

	for (ptr2 = target->entries[i].log; 
	     ptr2 < target->entries[i].log + 
		 target->entries[i].log_count; 
	     ptr2++) {
	    
	    switch (ptr2->command_letter) {
		
	    case '\0':   /* ENTRY DELETED */
		break;
		
	    default:
		if (ptr2->buffer) {
		    
		    if (ptr2->buffer_len != 
			fwrite(ptr2->buffer,1,ptr2->buffer_len,new_fd)) {
			fprintf(stderr,
				"%s: write error (file %s)\n",
				program_name,tempname);
			fclose(new_fd);
			goto fail;
		    }
		    putc('\n',new_fd);
		}
		
		break;
	    }
	    
	}
	
    }

    if (EOF == fflush(new_fd)) {
	fprintf(stderr,
		"%s: Failed to write %s\n",
		program_name,tempname);
	fclose(new_fd);
	goto fail;
    }

    if (-1 == rename(tempname,target->filename)) {
	fprintf(stderr,
		"%s: Failed to replace %s\n",
		program_name,target->filename);
	fclose(new_fd);
	goto fail;
    }

    fclose(target->file);
    target->file = new_fd;

    free(tempname);
}

static int master P_((int argc, char *argv[], int x));
static int master(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{

    int read_pipe;
    int t;
    int c;
    int PID;
    
    int w;
    S__ status;

    FILE *fd = NULL;
    struct log_entries * last = NULL;

    struct stat X;
    char *X1;
    int remove_count;

    struct log_instance LI;

    clear_log_instance(&LI);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {
	    
	    filelist = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {

	    installer_root = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {

	    register_module = argv[x+1];
	    x += 2;	

	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }




    if ('\0' == installer_root[0]) {
	fprintf(stderr,"%s: -R '' invalid\n",
		argv[0]);
	return 1;   /* FAILURE */
    }

    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (X.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */

    }


    /* We use open instead of fopen so that we can create file
       and not truncate existing file
    */

    LI.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI.filename = malloc(l);
	if (!LI.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI.filename,"%s%s",installer_root,filelist);
    }

    /* HACK */
    X1 = strrchr(LI.filename,'/');
    if (X1) {
	char *Z = strdup(LI.filename);

	if (Z) {
	    X1 = strrchr(Z,'/');
	    *X1 = '\0';

	    make_package_dir(Z,installer_root);

	    if (0 == mkdir(Z,0755)) {
		fprintf(stdout,
			"%s: Directory %s created\n",
			program_name,Z);
	    } else if (errno != EEXIST) {
		fprintf(stderr,
			"%s: Failed to create %s directory\n",
			program_name,Z);
		exit(1);
	    }
	    free(Z);
	}
    }

    t = open(LI.filename,O_RDWR|O_CREAT,0644);

    if (t < 0) {
	fprintf(stderr,
		"%s: Failed to create or open file %s on %s: %s\n",
		program_name,filelist,installer_root, LI.filename);
	return 1;  /* FAILURE */

    }

    fprintf(stdout,"%s: Creating %s\n",program_name,LI.filename);

    LI.file = fdopen(t,"r+");
    if (! LI.file) {
	fprintf(stderr,"%s: fdopen() failed\n",program_name);
	close(t);
	return 1;  /* FAILURE */
    }    
    

    c = fgetc(LI.file);

    if (EOF != c) {
	if (c != '[') {
	    fprintf(stderr,
		    "%s: File %s is not elmregister filelist\n",
		    program_name,LI.filename);
	    close(t);
	    return 1;  /* FAILURE */
	}

	ungetc(c,LI.file);  /* add_bytes reread character */
	add_entries(& LI);
    }
        	    
    last = add_last(&LI);


    

    read_pipe = create_pipe(argc,argv,x,&PID,installer_root,register_module);

    fd = fdopen(read_pipe,"r");
    if (!fd) {
	fprintf(stderr,"%s: fdopen() failed\n",program_name);
	close(read_pipe);
	return 1;  /* FAILURE */
    }



    log_copy_loop(fd,&LI,last);

    remove_unlinked(&LI,installer_root,&remove_count);
    if (remove_count > 0)
	rewrite_list(&LI);

    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Write(?) failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    fclose(fd);

    while ((w = 
#ifdef HASWAITPID
	    waitpid(PID,&status,0)
#else
	    wait(status)
#endif
	    ) != PID)
	if (w == -1 && errno != EINTR)
	    break;

    if (w != PID || status != 0) {
	fprintf(stderr,
		"%s: Failed to run %s\n",
		program_name,argv[x]);
	return 1;   /* FAILURE */	
    }
    
    /* NOTE: LI leaked ... */

    return 0;
}

static char  * rel_path = "";                     /* Path to prepended 
						     to log written on pipe 
						     ?? is this needed ??
						  */

/* Returns:  'S'         relpath is relative to source
             'T'         relpath is relative to installer_root


	     "//"   on arg is supposed to be mark for $ELM_ROOT
*/

static int split_file P_((char *arg, char **fullpath, char **relpath));       
static int split_file(arg,fullpath,relpath)
     char *arg;
     char **fullpath;
     char **relpath;                       /* relpath have rel_path prepended
					      is original was relative */
{
    char *ptr = strstr(arg,"//");

    int L = strlen(installer_root);
    

    while (L > 0 && '/' == installer_root[L-1])
	L--;

    if (ptr && '/' == arg[0]) {	  /* TARGET FILE -- installer prefix given */
	if (ptr-arg != L &&
	    0 != strncmp(arg,installer_root,L)) {
	    fprintf(stderr,
		    "%s: prefix %.*s on %s do not match with $ELM_ROOT=%s\n",
		    program_name,ptr-arg,arg,arg,installer_root);
	    exit(1);   /* FAILURE */
	}

	*fullpath = strdup(arg);
	if (!*fullpath) {
	    fprintf(stderr,
		    "%s: strdup failed\n",
		    program_name);
	    exit(1);   /* FAILURE */
	}

	while ('/' == *ptr)
	    ptr++;

	*relpath = strdup(ptr);
	if (!*relpath) {
	    fprintf(stderr,
		    "%s: strdup failed\n",
		    program_name);
	    exit(1);   /* FAILURE */
	}

	return 'T';   /* relpath is relative to installer_root */

    } else if ('/' == arg[0]) {	/* TARGET FILE -- installer prefix not given */
	int x = L + 3 + strlen(arg);
	char *Y = arg;

	while('/' == *Y)
	    Y++;
	
	*fullpath = malloc(x);
	if (!*fullpath) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,x);
	    exit(1);   /* FAILURE */
	}

	strncpy(*fullpath,installer_root,L);
	strcpy(*fullpath +L,"//");
	strcpy(*fullpath +L+2,Y);

	*relpath = strdup(Y);
	if (!*relpath) {
	    fprintf(stderr,
		    "%s: strdup failed\n",
		    program_name);
	    exit(1);   /* FAILURE */
	}

	return 'T';   /* relpath is relative to installer_root */
	

    } else {   /* SOURCE FILE ...                                            */
	int y = strlen(rel_path);
	int x = y + strlen(arg) + 1;

	*fullpath = strdup(arg);
	if (!*fullpath) {
	    fprintf(stderr,
		    "%s: strdup failed\n",
		    program_name);
	    exit(1);   /* FAILURE */
	}
	
	*relpath = malloc(x);
	if (!*relpath) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,x);
	    exit(1);   /* FAILURE */
	}

	strcpy(*relpath,rel_path);
	strcpy(*relpath + y, arg);

	return 'S';   /* relpath is relative to source */
    }
}



static char * gen_tempname P_((char * target));
static char * gen_tempname(target) 
     char * target;
{
    int l = strlen(target) + 10;

    char *tempname = malloc(l);
    if (!tempname) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		program_name,l);
	exit(1);   /* FAILURE */
    }

    strcpy(tempname,target);
    strcat(tempname,"~_~");

    return tempname;
}
			

/* 0 == OK */
static int install_file P_((char * source, char * target,
			    uid_t user, gid_t group, int mode));
static int install_file(source,target,user,group,mode)
     char * source; 
     char * target; 
     uid_t user; 
     gid_t group; 
     int mode;
{

    char * tempname;
    FILE * F, *S;
    int c;

    /* HACK */
    char *Z = strdup(target);
    if (Z) {
	char * X1 = strrchr(Z,'/');

	if (X1) {
	    *X1 = '\0';

	    if (0 == mkdir(Z,0755)) {
		int r = 0;
		if (user != (uid_t)-1 || group != (gid_t)-1) {
		    r = chown(Z,user,group);
		}

		fprintf(stdout,
			"%s: Directory %s created%s\n",
			program_name,Z,
			r == -1 ? " (failed to set owner)" : "");
	    } else if (errno != EEXIST) {
		fprintf(stderr,
			"%s: Failed to create %s directory\n",
			program_name,Z);
		free(Z);
		return 1;   /* FAILURE */
		
	    }
	}
	free(Z);
    }

    tempname = gen_tempname(target);
    
    if (0 == unlink(tempname)) {
	fprintf(stderr,"%s: %s unlinked\n",program_name,tempname);
    }

    F = fopen(tempname,"w");
    if (!F) {
	fprintf(stderr,"%s: Failed to create %s\n",
		program_name,tempname);
	return 1;   /* FAILURE */

    }

    S = fopen(source,"r");
    if (!F) {
	fprintf(stderr,"%s: Failed to read %s\n",
		program_name,source);

	fclose(F);
	unlink(tempname);
	return 1;   /* FAILURE */
    }
    
    while (EOF != (c = getc(S))) {
	if (putc(c,F) == EOF) {
	    fclose(F);
	    
	fail:
	    fprintf(stderr,"%s: Failed to write %s\n",
		    program_name,tempname);

	    fclose(S);
	    unlink(tempname);

	    return 1;   /* FAILURE */
	}
    }

    if (ferror(S)) {
	fprintf(stderr,"%s: Failed to read %s\n",
		program_name,source);

	fclose(S);
	fclose(F);
	unlink(tempname);

	return 1;   /* FAILURE */

    }

    if (EOF == fclose(F)) {
	goto fail;
    }

    if (-1 == rename(tempname,target)) {
	fprintf(stderr,
		"%s: Failed to replace %s\n",
		program_name,target);
	goto fail;
    }

    if (user != (uid_t)-1 || group != (gid_t)-1) {
	if (-1 == chown(target,user,group)) {
	    fprintf(stderr,"%s: Failed to set owner of %s\n",
		    program_name,target);
	    
	}
    }

    if (-1 == chmod(target,mode)) {
	fprintf(stderr,"%s: Failed to set mode of %s\n",
		program_name,target);	
    }


    fprintf(stdout,"%s: File %s installed\n",
	    program_name,target);

    fclose(S);
    free(tempname);
    return 0;
} 

static void copy_dir P_((char *src,char *trg, char *rel, 
			 struct log_instance *LI,
			 struct log_entries * last,
			 char *register_module));

#ifdef DIROPS

#if DIROPS == USE_DIRENT
#include <dirent.h>
#endif /* DIROPS == USE_DIRENT */
#if DIROPS == USE_SYSDIR
#include <sys/dir.h>
#endif /* DIROPS == USE_SYSDIR */

static void copy_dir(src,trg,rel,LI,last, register_module)
     char *src;
     char *trg;
     char *rel;
     struct log_instance *LI;
     struct log_entries * last;
     char *register_module;
{
    DIR *   handle;

#if DIROPS == USE_DIRENT
    struct dirent * Xptr;
#endif /* DIROPS == USE_DIRENT */
#if DIROPS == USE_SYSDIR
    struct direct * Xptr;
#endif /* DIROPS == USE_SYSDIR */

    int slen = strlen(src);
    int tlen = strlen(trg);
    int rlen = strlen(rel);

    handle = opendir(src);
    if (!handle) {
	fprintf(stderr,
		"%s: Failed to open directory %s\n",
		program_name,src);
	exit(1);   /* FAILURE */	
    }

    fprintf(stdout,
	    "%s: Copying directory %s -> %s:\n",
	    program_name,src,trg);

    if (0 == mkdir(trg,0755)) {
	
	fprintf(stdout,
		"%s: Directory %s created\n",
		program_name,trg);
    } else if (errno != EEXIST) {
	fprintf(stderr,
		"%s: Failed to create %s directory\n",
			program_name,trg);
		exit (1); /* FAILURE */
    }

    while (NULL != (Xptr = readdir(handle))) {
	if ('.' != Xptr->d_name[0]) {
	    int L;
	    struct stat X;
	    char *fullname;
	    int elen;

#if DIROPS == USE_DIRENT
	    char * entryname = strdup(Xptr->d_name);

	    if (!entryname) {
		fprintf(stderr,"%s: strdup failed: %s\n",
			program_name,Xptr->d_name);
		exit(1);   /* FAILURE */
	    }


#endif /* DIROPS == USE_DIRENT */
#if DIROPS == USE_SYSDIR
	    char * entryname = malloc(Xptr->d_namlen+1);

	    if (!entryname) {
		fprintf(stderr,"%s: malloc %d bytes failed\n",
			program_name,Xptr->d_namlen+1);
		exit(1);   /* FAILURE */
	    }

	    strncpy(entryname,Xptr->d_name,Xptr->d_namlen);
	    entryname[Xptr->d_namlen] = '\0';
#endif /* DIROPS == USE_SYSDIR */
	    
	    elen = strlen(entryname);
	    L = elen + 3 + slen;

	    fullname = malloc(L);
	    if (!fullname) {
		fprintf(stderr,"%s: malloc %d bytes failed\n",
			program_name,L);
		exit(1);   /* FAILURE */
	    }

	    strcpy(fullname,src);
	    strcat(fullname,"/");
	    strcat(fullname,entryname);

	    if (stat(fullname,&X) < 0) {
		fprintf(stderr,"%s: Can't stat %s\n",
			program_name,fullname);
		exit(1);   /* FAILURE */
	    }

	    if (
#ifdef S_ISREG
		S_ISREG(X.st_mode)
#else
		S_IFREG == (buf.st_mode & S_IFMT)
#endif
		) {
		
		int l2 = tlen + 3 + elen;

		char * fulltrgname = malloc(l2);
		char * relname;
		int l3;

		if (!fulltrgname) {
		    fprintf(stderr,"%s: malloc %d bytes failed\n",
			    program_name,l2);
		    exit(1);   /* FAILURE */		    
		}

		strcpy(fulltrgname,trg);
		strcat(fulltrgname,"/");
		strcat(fulltrgname,entryname);

		if (0 != install_file(fullname,fulltrgname, (uid_t)-1, (gid_t)-1, 
				      X.st_mode & 0777)) {
		    
		    fprintf(stderr,
			    "%s: Install %s -> %s failed\n",
			    program_name,
			    fullname,fulltrgname);
		    exit(1);   /* FAILURE */	
		}
		
		l3 = rlen + 3 + elen;
		relname = malloc(l3);
		if (!relname) {
		    fprintf(stderr,"%s: malloc %d bytes failed\n",
			    program_name,l3);
		    exit(1);   /* FAILURE */		    
		}

		strcpy(relname,rel);
		strcat(relname,"/");
		strcat(relname,entryname);

		if (!last)
		    log_it("s",relname,"-","-", X.st_mode & 0777,
			   register_module);
		else {
		    char buffer[ MAX_PIPE_LOG];
		    char *ptr;
		    char mode_s[6];
		    int rname = strlen(relname);
		    int L;
		    struct log_record L1;


		    int module_ptr  = -1;
		    int command_ptr = -1;
		    int user_ptr    = -1;
		    int group_ptr   = -1;
		    int mode_ptr    = -1;
		    int args_ptr[1];
		    int argnum      = 0;

		    sprintf(mode_s,"%05o",X.st_mode & 0777);


	       

		    ptr = buffer;
		    if (register_module) {
			int l = strlen(register_module);
			add_to_buffer(buffer, sizeof buffer,&ptr,"m",1);
			
			module_ptr = ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,
							       register_module,l);
		    }

		    command_ptr = ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,"s",1);
		    user_ptr    = ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,"-",1);
		    group_ptr   = ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,"-",1);
		    mode_ptr    = ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,mode_s,5);
		    args_ptr[argnum++]  = 
			ptr-buffer; add_to_buffer(buffer, sizeof buffer,&ptr,relname,rname);

		    /* NO terminating NL to buffer */

		    L = ptr - buffer;
			
		    if (L >  MAX_PIPE_LOG-2) {
			fprintf(stderr,"%s: Can't register file, string too long: %d\n",
				program_name,L);
			exit(1);
		    } 
		    L1.buffer =  malloc(L);
		    if (!L1.buffer) {
			fprintf(stderr,"%s: malloc %d bytes failed\n",program_name,
				L);
			exit(1);			
		    }
		    L1.buffer_len = L;		    

		    L1.command_letter = 's';
		    parse_log_tail(&L1,buffer,L,module_ptr,command_ptr,user_ptr,
				   group_ptr,mode_ptr,args_ptr,argnum);

		    last -> log = realloc(last -> log, 
					  (last -> log_count+1) * 
					  sizeof (last -> log[0]));
	    		
		    if (! last->log) {
			fprintf(stderr,
				"%s: Failed to realloc %d items (file %s)\n",
				program_name,last -> log_count+1,LI->filename);
			exit(1);
		    }

		    last -> log[last -> log_count] = L1;

		    last -> log_count++;

		    if (L1.buffer_len != fwrite(L1.buffer,1,L1.buffer_len,LI->file)) {
			fprintf(stderr,
				"%s: write error (file %s)\n",
				program_name,LI->filename);
			fclose(LI->file);
			
			exit (1);  /* FAILURE */	
		    }
		    putc('\n',LI->file);


		}
	
		free(fulltrgname);
		free(relname);
	
	    }

	    free(entryname);
	    free(fullname);
	}
    }

    closedir(handle);
}

#else

static void copy_dir(src,trg, char *rel,LI,last,register_module)
     char *src;
     char *trg;
     char *rel;
     struct log_instance *LI;
     struct log_entries * last;
     char *register_module;
{
    char * x = strrchr(trg,'/');
    int w;
    int pid;
    S__ status;

    if (!x) {
	fprintf(stderr,"%s: No / on path: %s\n",
		program_name,trg);
	exit(1);   /* FAILURE */	
    }
    
    fflush(stdout);
    pid = fork();

    if (-1 == pid) {
	fprintf(stderr,"%s: fork failed\n",
		program_name);
	exit(1);   /* FAILURE */	
    }

    if (0 == pid) {

	/* because this is fork()ed, this have
	   on own address space, and trg
	   on parent is NOT affected...
	*/

	*x = '\0';

	fprintf(stdout,
		"%s: Copying %s to %s(/%s) with %s -r\n",
		program_name,src,trg,x+1,ELMREGISTER_CP);
	fflush(stdout);

	execlp(ELMREGISTER_CP,ELMREGISTER_CP,"-r",
	       src,trg,NULL);

	fprintf(stderr,
		"%s: Failed to EXECute %s\n",program_name,ELMREGISTER_CP); 
	fflush(stderr);
	_exit(127);
    }

    while ((w = 
#ifdef HASWAITPID
	    waitpid(pid,&status,0)
#else
	    wait(status)
#endif
	    ) != pid)
	if (w == -1 && errno != EINTR)
		break;

    if (w != pid || status != 0) {
	fprintf(stderr,
		"%s: Failed to copy %s to %s (with using %s)\n",
		program_name,src,trg,ELMREGISTER_CP);
	exit(1);   /* FAILURE */	
    }
}

#endif /* DIROPS */

static void make_argcopy P_((char *arg, int arg_len, char * stage_subdir,
			     char *register_module));
static void make_argcopy(arg,arg_len, stage_subdir, register_module)
     char *arg; 
     int arg_len;
     char * stage_subdir;
     char *register_module;
{
    struct stat X;

    char bufferz[256];
    int p;
    int n;
    int R;
    char *y;

    char * source_f = NULL;
    char * source_r = NULL;

    char * target_f = NULL;
    char * target_r = NULL;


    if (arg_len >= 256) {
	fprintf(stderr,
		"%s: Argument too long: %s\n",
		program_name,arg);
	return; /* FAILURE */
    }


    strcpy(bufferz,arg+1);
    if (bufferz[arg_len-2] != '}') {
	fprintf(stderr,
		"%s: OOPS %s\n",
		program_name,
		bufferz);
	exit(1);
    }

    bufferz[arg_len-2] = '\0';

    R = stat(bufferz,&X);
   
    p = split_file(bufferz, &source_f, &source_r);

    if ('T' == p) {
	/* if ARG is already on target on, just register it ... 
	   May also be directory 
	*/


	if (0 != R) 
	    log_it(arg,source_r,"-", "-", 0444, register_module);
	else
	    log_it(arg,source_r,"-", "-", X.st_mode & 0777,
		   register_module);

	free(source_r);
	free(source_f);
	return;
    }

    if (0 != R) {
	fprintf(stderr,
		"%s: Argument %s (filename %s) does not exists?\n",
		program_name,arg, bufferz);
	exit(1);
    }

    
    if ('/' != stage_dir[0]) {
	fprintf(stderr,
		"%s: stage dir %s does not have absolute\n",
		program_name,stage_dir);
	exit(1);

    }
    
    n = strlen(stage_dir) + arg_len;
    if (stage_subdir) {
	n += strlen(stage_subdir) + 1;
    }
    target_r = malloc(n);
    if (!target_r) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,n);
	exit(1);   /* FAILURE */	
    }
    strcpy(target_r,stage_dir+1);
    strcat(target_r,"/");

    if (stage_subdir) {
	char * Y;
	int n1;

	strcat(target_r,stage_subdir);

	n1 = strlen(installer_root) + strlen(target_r) + 3;

	Y = malloc(n1);
	if (!Y) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,n1);
	    exit(1);   /* FAILURE */	
	}

	strcpy(Y,installer_root);
	strcat(Y,"/");
	strcat(Y,target_r);



	if (0 == mkdir(Y,0755)) {
	    fprintf(stdout,
		    "%s: Directory %s created\n",
		    program_name,Y);

	    /* HACK -- Do not use letter 'M', because it can break handling of
             *         stored argument list
             */
	    
	    log_it(IMPARGDIR,target_r,"-", "-", 0755,
		   register_module);
	   
	} else if (errno != EEXIST) {
	    fprintf(stderr,
		    "%s: Failed to create %s directory\n",
		    program_name,Y);
	    exit(1);
	}
	

	free(Y);

	strcat(target_r,"/");
    }

    y = strrchr(bufferz,'/');
    if (y)
	strcat(target_r,y+1);
    else
	strcat(target_r,bufferz);

    n = strlen(installer_root) + strlen(target_r) + 3;
    target_f = malloc(n);
    if (!target_f) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,n);
	exit(1);   /* FAILURE */	
    }
    strcpy(target_f,installer_root);
    strcat(target_f,"/");
    strcat(target_f,target_r);

    if (
#ifdef S_ISDIR
	S_ISDIR(X.st_mode)
#else
	S_IFDIR == (buf.st_mode & S_IFMT)
#endif
	) {

	copy_dir(bufferz,target_f,target_r,NULL,NULL,register_module);
	    
	log_it(arg,target_r,"-", "-", X.st_mode & 0777,
	       register_module);

    } 
    else {
	/* Just normal file */

	if (0 != install_file(source_f,target_f, (uid_t)-1, (gid_t)-1, 
			      X.st_mode & 0777)) {
	    fprintf(stderr,
		    "%s: Install %s -> %s failed\n",
		    program_name,
		    source_r,target_r);
	    exit(1);   /* FAILURE */	
	}

	log_it(arg,target_r,"-", "-", X.st_mode & 0777,
	       register_module);
    }


    free(source_r);
    free(source_f);
    free(target_r);
    free(target_f);


}

static int command P_((int argc, char *argv[], int x));
static int command(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{

    int fd;
    struct stat X;
    char * cmd;
    
    if (!register_pipe) {
	fprintf(stderr,"%s: $ELM_INSTALLER is missing\n",
		argv[0]);
	
	return 1; /* FAILURE */
    }
    
    if (!installer_root) {
	fprintf(stderr,"%s: $ELM_ROOT is missing\n",
		argv[0]);
	
	return 1;   /* FAILURE */
    }
        
    fd = atoi(register_pipe);	
    
    if (fd < 1) {
	fprintf(stderr,"%s: $ELM_INSTALLER=%s invalid\n",
		argv[0],register_pipe);
	
	return 1;   /* FAILURE */
	
    } 
    
    if (fstat(fd,&X) < 0) {
	fprintf(stderr,"%s: $ELM_INSTALLER=%s invalid\n",
		argv[0],register_pipe);
	
	return 1;   /* FAILURE */
    }


    register_fd = fd;

    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: $ELM_ROOT=%s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: $ELM_ROOT=%s invalid (not a directory)\n",
		argv[0],installer_root);
	
	
	return 1;   /* FAILURE */
    }
 
    while (x < argc && '-' == argv[x][0]) {
   
	if (x < argc -1 && 0 == strcmp("-R", argv[x])) {
	    int l;
	    
	    rel_path = argv[x+1];
	    x += 2;	
	    
	    l = strlen(rel_path);
	    
	    if (l > 0 && '/' != rel_path[l-1]) {
		char * X = malloc(l+2);
		if (!X) {
		    fprintf(stderr,"%s: malloc %d bytes failed\n",
			    argv[0],l+2);
		    return 1;   /* FAILURE */
		}
		
		strncpy(X,rel_path,l+2);
		X[l] = '/';
		
		fprintf(stdout,"%s: Adding / to -R %s: %s\n",
			argv[0],rel_path,X);
		rel_path = X;
	    }
	    
	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {

	    register_module = argv[x+1];
	    x += 2;	
	    
	} else {

	    break;

	}
    }


    /* COPY mode ?? */ 


    if (x < argc && 
	0 == strcmp("rm", argv[x])) { 

	char buffer[ MAX_PIPE_LOG];
	char *ptr;

	int j;

	char * translated [MAX_ARGS+1];
	char * relative [MAX_ARGS+1];
	int i;
	int L = 4;

	x++;

	if (argc - x >= MAX_ARGS) {
	    fprintf(stderr,
		    "%s: %s too many arguments\n",
		    argv[0], argv[x-1]);
	    return 1; /* FAILURE */
	}

	i = 0;
	for (j = x; j < argc;  j++) {
	    if ('T' != split_file(argv[j],& translated[i], & relative[i])) {
		fprintf(stderr,
			"%s: file %s does not look like remove target\n",
			argv[0],argv[x]);
		return 1; /* FAILURE */	    
	    }

	    i++;
	    L += strlen(argv[j]) +1;	    
	}
	translated[i] = NULL;
	relative[i] = NULL;

	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 

	for (j = 0; j < i; j++) {
	    if (0 == unlink(translated[j])) {
		fprintf(stdout,
			"%s: File %s removed\n",
			program_name,translated[j]);
	    }
	}

	ptr = buffer;

	if (register_module) {
	    int l = strlen(register_module);
	    add_to_buffer(buffer, sizeof buffer,&ptr,"m",1);
	    add_to_buffer(buffer, sizeof buffer,&ptr,register_module,l);
	}

	add_to_buffer(buffer, sizeof buffer,&ptr,"R",1);
	for (j = 0; j < i; j++) {
	    int l = strlen(relative[j]);
	    add_to_buffer(buffer, sizeof buffer,&ptr,relative[j],l);
	}
       
	*ptr++ = '\n';   /* Terminating NL on line */
	
	L = ptr - buffer;
	
	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 

	/* We have better to able write whole string at ONCE,
	   or otherwise two different writes from different processes
	   are mixed on pipe ... (ie. if paraller make)
	*/

	write_once(register_fd,buffer,L);

    } else if (x < argc && 
	       0 == strcmp("link", argv[x])) { 
	char buffer[ MAX_PIPE_LOG];
	char *ptr;

	char * translated [2];
	char * relative [2];
	char * tempname;
	int L = 4;
	int i,j;

	int Exit_it = 0;

	x++;

	if (argc - x != 2) {
	    fprintf(stderr,
		    "%s: %s: 2 arguments required\n",
		    argv[0], argv[x-1]);
	    return 1; /* FAILURE */
	}

	i = 0;
	for (j = x; j < argc;  j++) {
	    if ('T' != split_file(argv[j],& translated[i], & relative[i])) {
		fprintf(stderr,
			"%s: file %s does not look like link target\n",
			argv[0],argv[x]);
		return 1; /* FAILURE */	    
	    }

	    i++;
	    L += strlen(argv[j]) +1;	    
	}

	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 

	tempname = gen_tempname(translated[1]);
	
	if (0 == unlink(tempname)) {
	    fprintf(stderr,"%s: %s unlinked\n",program_name,tempname);
	}
	
	if (-1 == link(translated[0],tempname)) {
	    fprintf(stderr,
		    "%s: Failed to link %s to %s\n",
		    program_name,translated[0],tempname);

	    Exit_it = 1;
	    goto fail3;
	} 

	if (-1 == rename(tempname,translated[1])) {
	    fprintf(stderr,
		    "%s: Failed to replace %s\n",
		    program_name,translated[1]);
	    Exit_it = 1;
	    goto fail3;
	}

	fprintf(stdout,"%s: Link %s == %s installed\n",program_name,translated[1],translated[0]);
	
	    
	ptr = buffer;
	if (register_module) {
	    int l = strlen(register_module);
	    add_to_buffer(buffer, sizeof buffer,&ptr,"m",1);
	    add_to_buffer(buffer, sizeof buffer,&ptr,register_module,l);
	}
	add_to_buffer(buffer, sizeof buffer,&ptr,"L",1);
	for (j = 0; j < i; j++) {
	    int l = strlen(relative[j]);
	    add_to_buffer(buffer, sizeof buffer,&ptr,relative[j],l);
	}
       
	*ptr++ = '\n';   /* Terminating NL on line */
	
	L = ptr - buffer;
	
	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 

	/* We have better to able write whole string at ONCE,
	   or otherwise two different writes from different processes
	   are mixed on pipe ... (ie. if paraller make)
	*/

	write_once(register_fd,buffer,L);

    fail3:
	free(tempname);
	for (i =0; i < 2; i++) {
	    free(translated[i]);
	    free(relative[i]);
	}

	if (Exit_it)
	    return Exit_it; /* FAILURE */

    } else if (x < argc && 
	(cmd = argv[x]) && 
	(
	 0 == strcmp("copy", argv[x]) ||
	 0 == strcmp("copydoc", argv[x]) ||
	 0 == strcmp("mkdir", argv[x]) 
	 )) {
	uid_t  id_user  = (uid_t)-1;
	gid_t  id_group = (gid_t)-1;
	char * s_user  = "-";
	char * s_group = "-";
	mode_t f_mode   = 0;             /* Use mode of source file */

	x++;

	while (x < argc && '-' == argv[x][0]) {

	    if (0 == strcmp(argv[x],"-o")) {
		struct passwd * p;
		
		if ((uid_t)-1 != id_user) {
		    fprintf(stderr,
			    "%s: Duplicate -o option\n",
			    argv[0]);
		    
		    return 1;   /* FAILURE */
		    
		}
	    
		if (x+1 >= argc) {
		    fprintf(stderr,"%s: Missing user after -o option\n",
			    argv[0]);
		    
		    return 1; /* FAILURE */
		}

		p = getpwnam(argv[x+1]);
		if (!p) {
		    fprintf(stderr,
			    "%s: -o option: user %s do not found\n",
			    argv[0],
			    argv[x+1]);
		    return 1; /* FAILURE */
		}
	    
		id_user = p->pw_uid;
		s_user  = argv[x+1];

		if ((gid_t)-1 == id_group)
		    id_group = p->pw_gid;
		x += 2;	   

	    }
	    else if (0 == strcmp(argv[x],"-g")) {
		struct group *g;

		if (x+1 >= argc) {
		    fprintf(stderr,
			    "%s: Missing group after -g option\n",
			    argv[0]);
		    
		    return 1; /* FAILURE */
		}
		
		g = getgrnam(argv[x+1]);
		if (!g) {
		    fprintf(stderr,
			    "%s: -g option: group %s do not found\n",
			    argv[0],
			    argv[x+1]);
		    
		    return 1; /* FAILURE */
		}
		
		id_group = g->gr_gid;
		s_group  = argv[x+1];
		x += 2;	   

	    }
	    else if (0 == strcmp(argv[x],"-m")) {
		char * ptr;

		if (x+1 >= argc) {
		    fprintf(stderr,
			    "%s: Missing octal mode after -m option\n",
			    argv[0]);
		    
		    return 1; /* FAILURE */
		}

		f_mode = strtol(argv[x+1],&ptr,8);

		if (*ptr != '\0') {
		    fprintf(stderr,
			    "%s: -m option: bad octal mode %s (character %c) \n",
			    argv[0],
			    argv[x+1],*ptr);
		    
		    return 1; /* FAILURE */
		}
		
		x += 2;	   

	    } 
	    
	    else {
		fprintf(stderr,
			"%s: %s option unknown\n",
			argv[0],argv[x]);

		return 1; /* FAILURE */
	    }
	}
	    

	if (0 == strcmp(cmd,"mkdir")) {
	    char * target_f = NULL;
	    char * target_r = NULL;

	    if ('T' != split_file(argv[x],&target_f,&target_r)) {
		fprintf(stderr,
			"%s: directory %s does not look like install target\n",
			argv[0],argv[x]);
		return 1; /* FAILURE */	    
	    }
	    x++;
	    
	    if (x < argc) {
		fprintf(stderr,
			"%s: Too many arguments\n",argv[0]); 
		return 1; /* FAILURE */		
	    }
	    
	    if (0 == f_mode)
		f_mode = 0755;

	    make_package_dir(target_f,installer_root);

	    if (0 == mkdir(target_f,f_mode)) {
		int r = 0;

		if (id_user != (uid_t)-1 || id_group != (gid_t)-1) {
		    r = chown(target_f,id_user,id_group);
		}
		
		fprintf(stdout,
			"%s: Directory %s created%s\n",
			program_name,target_f,
			r == -1 ? " (failed to set owner)" : "");
	    } else if (errno != EEXIST) {
		fprintf(stderr,
			"%s: Failed to create %s directory\n",
			program_name,target_f);
		return 1; /* FAILURE */
	    }

	    log_it("M",target_r,s_user, s_group, f_mode,
		   register_module);
	    

	    free(target_r); free(target_f);

	} else {
	    struct stat X;

	    char * source_f = NULL;
	    char * source_r = NULL;
	    
	    char * target_f = NULL;
	    char * target_r = NULL;

	    if (x >= argc) {
		fprintf(stderr,
			"%s: source file not given\n",argv[0]); 
		return 1; /* FAILURE */
	    }
	    if ('S' != split_file(argv[x],&source_f,&source_r)) {
		fprintf(stderr,
			"%s: file %s does not look like install source\n",
			argv[0],argv[x]); 
		return 1; /* FAILURE */	    
	    }
	    x++;
	    
	    if ('T' != split_file(argv[x],&target_f,&target_r)) {
		fprintf(stderr,
			"%s: file %s does not look like install target\n",
			argv[0],argv[x]);
		return 1; /* FAILURE */	    
	    }
	    x++;

	    if (x < argc) {
		fprintf(stderr,
			"%s: Too many arguments\n",argv[0]); 
		return 1; /* FAILURE */
		
	    }
	    
	    if (0 != stat(source_f,&X)) {
		fprintf(stderr,
			"%s: File %s does not exists?\n",
			program_name,source_f);
		return 1; /* FAILURE */
	    }

	    if (
#ifdef S_ISDIR
		S_ISDIR(X.st_mode)
#else
		S_IFDIR == (buf.st_mode & S_IFMT)
#endif
		) {
		fprintf(stderr,
			"%s: %s is directory\n",
			program_name,source_f);
		return 1; /* FAILURE */	       		
	    }

	    if (0 == f_mode)
		f_mode = X.st_mode & 0777;
	   
	    if (f_mode > 07777) {
		fprintf(stderr,
			"%s: file mode %05o illegal\n",
			argv[0],
			f_mode);
		return 1; /* FAILURE */
	    }
	
	    if (0 != install_file(source_f,target_f, id_user, id_group, f_mode)) {
		fprintf(stderr,
			"%s: Install %s -> %s failed\n",
			argv[0],
			source_r,target_r);
		return 1; /* FAILURE */
	    }
	    
	    if (0 == strcmp("copydoc",cmd))
		log_it("d",target_r,s_user, s_group, f_mode, register_module);
	    else
		log_it("F",target_r,s_user, s_group, f_mode, register_module);
	    
	    free(source_r); free(source_f);
	    free(target_r); free(target_f);
	}

    } else {

	int j;
	/* Execute program ( actually delay executing )*/
	
	char buffer[ MAX_PIPE_LOG];
	char *ptr;
	char * stage_subdir = NULL; 

	int L = 4;

	if (x < argc -1 && 0 == strcmp("-s", argv[x])) {
	    int l;

	    stage_subdir = argv[x+1];
	    x += 2;	
	}


	if (argc - x >= MAX_ARGS) {
	    fprintf(stderr,
		    "%s: %s too many arguments\n",
		    argv[0], argv[x]);
	    return 1; /* FAILURE */
	}
	
	for (j = x; j < argc; j++) {
	    int z;
	    if ('{' == argv[j][0] && (z = strlen(argv[j])) && '}' == argv[j][z-1]) {		
		make_argcopy(argv[j],z, stage_subdir,register_module);
	    }

	    L += strlen(argv[j]) +1;
	}

	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 

	ptr = buffer;
	if (register_module) {
	    int l = strlen(register_module);
	    add_to_buffer(buffer, sizeof buffer,&ptr,"m",1);
	    add_to_buffer(buffer, sizeof buffer,&ptr,register_module,l);
	}
	add_to_buffer(buffer, sizeof buffer,&ptr,"E",1);
	for (j = x; j < argc; j++) {
	    int l = strlen(argv[j]);
	    add_to_buffer(buffer, sizeof buffer,&ptr,argv[j],l);
	}
       

	*ptr++ = '\n';   /* Terminating NL on line */
	
	L = ptr - buffer;
	
	if (L >  MAX_PIPE_LOG-2) {
	    fprintf(stderr,"%s: Can't register command, string too long: %d\n",
		    program_name,L);
	    return 1; /* FAILURE */
	} 
	
	
	/* We have better to able write whole string at ONCE,
	   or otherwise two different writes from different processes
	   are mixed on pipe ... (ie. if paraller make)
	*/

	write_once(register_fd,buffer,L);
	
    }
    
    return 0;
}


static void pick_one P_((struct log_record   * ptr,
			 char **src,
			 int L,
			 uid_t  *id_user,
			 gid_t  *id_group,
			 int *mode,
			 int *is_dir));
static void pick_one(ptr,src,L,id_user,id_group,mode,is_dir)
     struct log_record   * ptr;
     char **src;
     int L;
     uid_t  *id_user;
     gid_t  *id_group;
     int *mode;
     int *is_dir;
{

    struct passwd * p = NULL;
    struct group  * g = NULL;
    struct stat X;

    if (!ptr->arg_ptrs || !ptr->arg_ptrs[0]) {
	   
	fprintf(stderr,
		"%s: [pick_one] '%c' no arg 0 \n",
		program_name,ptr->command_letter);
	exit(1);  /* FAILURE */
    }

    *src = make_rooted(ptr,ptr->arg_ptrs[0],installer_root);

    if (ptr->user_ptr && 0 != strcmp(ptr->user_ptr,"-")) {
	p = getpwnam(ptr->user_ptr);

	if (!p) 
	    fprintf(stderr,
		    "%s: [pick_one] '%c' User %s not found\n",
		    program_name,ptr->command_letter,
		    ptr->user_ptr);
    }

    if (ptr->group_ptr && 0 != strcmp(ptr->group_ptr,"-")) {
	g = getgrnam(ptr->group_ptr);

	if (!g) 
	    fprintf(stderr,
		    "%s: [pick_one] '%c' Group %s not found\n",
		    program_name,ptr->command_letter,
		    ptr->group_ptr);
    }

    if (stat(*src,&X) < 0) {
	fprintf(stderr,
		"%s: (note) %s : %s not accessible\n",
		program_name,
		ptr->arg_ptrs[0],*src);
		

	*is_dir   = -1;
	*id_user  = (uid_t)-1;
	*id_group = (gid_t)-1;
	*mode     = 0555;
	return;
    }

    if (p)
	*id_user = p->pw_uid;
    else
	*id_user = X.st_uid;

    if (g)
	*id_group = g->gr_gid;
    else if (p)
	*id_group = p->pw_gid;
    else
	*id_group = X.st_gid;

    if (ptr->mode_ptr && 0 != strcmp(ptr->mode_ptr,"-") &&
	0 != strcmp(ptr->mode_ptr,"0")) {
	char * x;
	*mode = strtol(ptr->mode_ptr,&x,8);
	
	if (*x || 0 == *mode)
	    goto failmode;
    }
    else {
    failmode:
	*mode = X.st_mode & 0777; 
    }

    /* if directory ... */	    
    if (
#ifdef S_ISDIR
	S_ISDIR(X.st_mode)
#else
	S_IFDIR == (buf.st_mode & S_IFMT)
#endif
	) {

	
	*is_dir = 1;
    } else {
	/* REGULAR */
	*is_dir = 0;
	    	
    }
}

static char * make_absolute P_((struct log_record   * ptr, char *rel));
static char * make_absolute(ptr,rel)
     struct log_record   * ptr; 
     char *rel;
{
    int l = strlen(rel) + 2;
		    
    char * translated = malloc(l);

    if (!translated) {
	fprintf(stderr,"%s: malloc %d bytes failed\n",
		program_name,l);
	exit (1);   /* FAILURE */
    }

    if ('/' == rel[0]) {
	fprintf(stderr,
		"%s: entry '%c' -- Not relative? %s\n",
		program_name,ptr->command_letter,rel);
	translated[0] = '\0';
    } else {
	translated[0] = '/';
	translated[1] = '\0';
    }
    strcat(translated,rel);

    return translated;
}

static int filter_record P_((struct log_record *record,
			    char **module, int match_all));
static int filter_record(record,module,match_all)
     struct log_record *record;
     char **module;
     int match_all;
{
    int i;
    if (module && module[0] &&
	0 == strcmp(module[0], "all"))
	return 1;

    if (! record->module_ptr) {
	if (!module)
	    return 1;
	
	return 0;
    }

    if (match_all && 
	0 == strcmp(record->module_ptr,"all"))
	return 1;

    if (!module)
	return 0;

    for (i = 0; module[i]; i++) {
	if (0 == strcmp(record->module_ptr,
			module[i]))
	    return 1;
    }

    return 0;
}

static int copy_list P_((struct log_entries *entry, 
			 struct log_instance *source, 
			 struct log_instance *target,
			 struct log_entries *last,
			 char **module));
static int copy_list(entry,source,target,last, module)
     struct log_entries *entry; 
     struct log_instance *source; 
     struct log_instance *target;
     struct log_entries *last;
     char **module;
{
    struct log_record   * ptr;
    int L = strlen(installer_root);

    
    for (ptr = entry->log; ptr < entry->log + entry->log_count; ptr++) {

	char * src = NULL;

	uid_t  id_user  = (uid_t)-1;
	gid_t  id_group = (gid_t)-1;
	int mode        = 0555;
	int is_dir      = 0;

	if (! filter_record(ptr,module,1))
	    continue;

	switch (ptr->command_letter) {
	    char *trg;

	case '\0':   /* ENTRY DELETED */
	    break;

	case 'E':
	    /* We do not execute on copy ... */
	    break;
	    
	case 'R':
	    {
		int i;

		for (i = 0; ptr->arg_ptrs[i] && i < MAX_ARGS; i++) {

		    char * translated =  make_absolute(ptr,ptr->arg_ptrs[i]);

		    if (0 == unlink(translated)) {
			fprintf(stdout,
				"%s: File %s removed\n",
				program_name,translated);
		    }

		    free(translated);
		}

		move_record(target,last,ptr);

	    }
	    break;

	case 's':  
	    /* Copying of directory does this ... */
	    break;

	case '{':  /* MUST BE handled on execute ... */

	    break;

	case 'C':
	    fprintf(stdout,
		    "%s: Config file %s (%s) not copied\n",
		    program_name,ptr->arg_ptrs[0],src);
	    break;

	case 'L':
	    /* Handled now on execute ... */
		  
	    break;

	case 'M':

	    pick_one(ptr,&src,L,&id_user,&id_group,&mode,&is_dir);
			    
	    trg = src + L;

	    if (is_dir < 0) {
		fprintf(stderr,
			"%s: 'Source' directory %s does not exists\n",
			program_name, src);
		
	    } else if (!is_dir) {
		fprintf(stderr,
			"%s: [copy_list] '%c' %s is NOT directory\n",
			program_name,ptr->command_letter,
			src);

		free(src);
		return 1;  /* FAILURE */
	    }

	    if (0 == mkdir(trg,mode)) {
		int r = chown(trg,id_user,id_group);

		fprintf(r == -1 ? stderr : stdout,
			"%s: Directory %s created%s\n",
			program_name,trg,
			r == -1 ? " (failed to set owner)" : "");
				
		move_record(target,last,ptr);
		
	    } else if (errno != EEXIST) {
		fprintf(stderr,
			"%s: Failed to create %s directory\n",
			program_name,trg);
		free(src);
		return 1; /* FAILURE */
	    }
	    break;

	case 'F':
	case 'd':     

	    pick_one(ptr,&src,L,&id_user,&id_group,&mode,&is_dir);
			    
	    trg = src + L;

	    if (is_dir < 0) {
		fprintf(stderr,
			"%s: [copy_list] '%c' Source file %s does not exists\n",
			program_name,ptr->command_letter,
			src);

		free(src);
		return 1;  /* FAILURE */
	    } else if (is_dir) {
		fprintf(stderr,
			"%s: [copy_list] '%c' %s is directory\n",
			program_name,ptr->command_letter,
			src);

		free(src);
		return 1;  /* FAILURE */
	    }

	    if (0 != install_file(src,trg,id_user,id_group,mode)) {
		fprintf(stderr,
			"%s: [copy_list] '%c' Failed to install %s : %s -> %s\n",
			program_name,ptr->command_letter,
			ptr->arg_ptrs[0],src,trg);
		
		free(src);
		return 1;  /* FAILURE */
	    }
	    
	    move_record(target,last,ptr);
	    
	    break;
	    
	default:
	    fprintf(stderr,
		    "%s: [copy_list] Bad command letter: %c \n",
		    program_name,ptr->command_letter);
	    return 1;

	}

	if (src)
	    free(src);

    }

    return 0;
}


static struct log_record * on_other_module P_((struct log_instance *LI,
					       char *entry,
					       char **module));
static struct log_record * on_other_module(LI,entry,module)
     struct log_instance *LI;
     char *entry;
     char **module;
{
    int i;

    for (i = 0; i < LI->entries_count; i++) {

	struct log_record   * ptr;

	for (ptr = LI->entries[i].log; 
	     ptr < LI->entries[i].log + 
		 LI->entries[i].log_count; ptr++) {


	    /* Check if entry is mentioned on filtered module */

	    if (!filter_record(ptr,module,1)) {

		switch (ptr->command_letter) {
	    
		case '\0':   /* ENTRY DELETED */
		    break;

		case '{':  
		case 's':
		case 'F':
		case 'd':     
		case 'C':
		case 'M':
	    
		    if (ptr->arg_ptrs && ptr->arg_ptrs[0] &&
			0 == strcmp(ptr->arg_ptrs[0],entry))
			return ptr;

		    break;
		    
		case 'L':
		    
		    /* second argument is new name */

		    if (ptr->arg_ptrs && ptr->arg_ptrs[1] &&
			0 == strcmp(ptr->arg_ptrs[1],entry))
			return ptr;

		    break;
		    
		case 'E':
		    /* execute ... */
		    break;

		case 'R':
		    /* remove ... */
		    break;
		    		   
    
		default:
		    fprintf(stderr,
			    "%s: [on_other_module] Bad command letter: %c \n",
			    program_name,ptr->command_letter);
		    
		}
	    }
	}
    }
    
    return NULL;
}


static int on_current P_((struct log_instance *LI,char *entry));
static int on_current(LI,entry)
     struct log_instance *LI;
     char *entry;
{
    int i;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];
    
    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    for (i = 0; i < LI->entries_count; i++) {
	if (0 == strcmp(LI->entries[i].version, my_version)) {

	    struct log_record   * ptr;

	    for (ptr = LI->entries[i].log; 
		 ptr < LI->entries[i].log + 
		     LI->entries[i].log_count; ptr++) {

		switch (ptr->command_letter) {
	    
		case '\0':   /* ENTRY DELETED */
		    break;

		case '{':  
		case 's':
		case 'F':
		case 'd':     
		case 'C':
		case 'M':
	    
		    if (ptr->arg_ptrs && ptr->arg_ptrs[0] &&
			0 == strcmp(ptr->arg_ptrs[0],entry))
			return 1;

		    break;
		    
		case 'L':
		    
		    /* second argument is new name */

		    if (ptr->arg_ptrs && ptr->arg_ptrs[1] &&
			0 == strcmp(ptr->arg_ptrs[1],entry))
			return 1;

		    break;
		    
		case 'E':
		    /* execute ... */
		    break;

		case 'R':
		    /* remove ... */
		    break;
		    		   
    
		default:
		    fprintf(stderr,
			    "%s: [on_current] Bad command letter: %c \n",
			    program_name,ptr->command_letter);
		    
		}
	    }
	}
    }

    return 0;
}

static void trim_list P_((struct log_entries *entry, 
			  struct log_instance *source,
			  char **module));
static void trim_list(entry,source,module)
     struct log_entries *entry; 
     struct log_instance *source; 
     char **module;
{
    struct log_record   * ptr;
    
    for (ptr = entry->log; ptr < entry->log + entry->log_count; ptr++) {

	if (! filter_record(ptr,module,0))
	    continue;
	
	
	switch (ptr->command_letter) {
	    	    
	case '\0':   /* ENTRY DELETED */
	    break;	    

	case 'L':
	    
	    /* second arg is new name */

	    if (ptr->arg_ptrs && ptr->arg_ptrs[1]) {

		if (!on_current(source,ptr->arg_ptrs[1])) {
		    fprintf(stdout,
			    "%s: (%s) Keeping link %s from old version (was %s)\n",
			    program_name,source->filename,ptr->arg_ptrs[1],
			    ptr->arg_ptrs[0]
			    );
		    continue;
		}
	    }

	    goto trim;


	case 's':

	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {

		if (!on_current(source,ptr->arg_ptrs[0])) {
		    fprintf(stdout,
			    "%s: (%s) Stage file %s from old version found -- incomplete install?\n",
			    program_name,source->filename,ptr->arg_ptrs[0]);
		    continue;
		}
	    }
	    goto trim;

	case 'M':
	case '{':  
	case 'F':
	case 'd':     
	case 'C':
	    
	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {

		if (!on_current(source,ptr->arg_ptrs[0])) {
		    if ('M' != ptr->command_letter)
			fprintf(stdout,
				"%s: (%s) Keeping file %s from old version\n",
				program_name,source->filename,ptr->arg_ptrs[0]);
		    continue;
		}
	    }

	    /* FALLTHRU */

	case 'R':  /* Trim delete ... */

	case 'E':  /* Trim execute ... */
	trim:

	    if (ptr->buffer) {
		free(ptr->buffer);
		ptr->buffer   = NULL;
	    }

	    if (ptr->arg_ptrs) {
		free(ptr->arg_ptrs);
		ptr->arg_ptrs = NULL;
	    }

	    ptr->command_letter = '\0';   /* ENTRY DELETED */

	    break;

	default:
	    fprintf(stderr,
		    "%s: [trim_list] Bad command letter: %c \n",
		    program_name,ptr->command_letter);

	}
    }    
}


static int unstage_list P_((struct log_entries *entry, 
			    struct log_instance *source,
			    char **module));
static int unstage_list(entry,source,module)
     struct log_entries *entry; 
     struct log_instance *source; 
     char **module;
{
    int exit_stat = 0;
    struct log_record   * ptr;
    int X = strlen(stage_dir);

    for (ptr = entry->log; ptr < entry->log + entry->log_count; ptr++) {
	int ok = 0;

	if (! filter_record(ptr,module,0))
	    continue;


	switch (ptr->command_letter) {
		
	case '\0':   /* ENTRY DELETED */
	    break;

	case 'E':
	    ok = 1;  /* JUST CLEAN */
	    break;

	case 's':
	case '{':  

	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {
		char *ab = make_absolute(ptr,ptr->arg_ptrs[0]);


		if (0 == strncmp(stage_dir,ab,X)) {

		    struct log_record   *other = 
			on_other_module(source,ptr->arg_ptrs[0],module);
	      
		    struct stat X;

		    if (stat(ab,&X) < 0)
			ok = 1;  /* Assume that cleared */
		    else if (
#ifdef S_ISDIR
			      S_ISDIR(X.st_mode)
#else
			     S_IFDIR == (buf.st_mode & S_IFMT)
#endif
			     ) {

			if (0 == rmdir(ab)) {
			    fprintf(stdout,
				    "%s: Stage directory %s uninstalled\n",
				    program_name,ab);
			    ok = 1;
			} 
		    } else {

			if (other) {
			    
			    fprintf(stdout,
				    "%s: Keeping stage file %s, used by %s package/module\n",
				    program_name,ab,
				    other->module_ptr ? other->module_ptr : "main");

			    ok = 1;  /* Remove double reference */

			} else if (0 == unlink(ab)) {
			    fprintf(stdout,
				    "%s: Stage file %s uninstalled\n",
				    program_name,ab);
			    ok = 1;
			} else {
			    fprintf(stderr,
				    "%s: Can't unlink %s\n",
				    program_name,ab);
			    exit_stat = 1;
			}
		    }
		    
		} else {
		    fprintf(stdout,
			    "%s: Ignoring entry %s -- not in stage area (%s)\n",
			    program_name,ab,stage_dir);
		    ok = 1;  /* JUST CLEAN */
		}

		free(ab);
	    }
	    break;

	}
	
	if (ok) {
	    if (ptr->buffer) {
		free(ptr->buffer);
		ptr->buffer   = NULL;
	    }
	    
	    if (ptr->arg_ptrs) {
		free(ptr->arg_ptrs);
		ptr->arg_ptrs = NULL;
	    }
	    
	    ptr->command_letter = '\0';   /* ENTRY DELETED */
	}
	    

    }

    return exit_stat;

}

static dev_t this_program_dev = 0;
static ino_t this_program_ino = 0;
static int this_program = 0;

static int uninstall_list P_((struct log_entries *entry, 
			      struct log_instance *source,
			      char **module));
static int uninstall_list(entry,source,module)
     struct log_entries *entry; 
     struct log_instance *source; 
     char **module;
{
    int exit_stat = 0;
    struct log_record   * ptr;
    int X = strlen(stage_dir);

    for (ptr = entry->log; ptr < entry->log + entry->log_count; ptr++) {
	int ok = 0;

	if (! filter_record(ptr,module,0))
	    continue;

	switch (ptr->command_letter) {
		
	case '\0':   /* ENTRY DELETED */
	    break;

	case 'L':
	    
	    /* second arg is new name */

	    if (ptr->arg_ptrs && ptr->arg_ptrs[1]) {
		
		char *ab = make_absolute(ptr,ptr->arg_ptrs[1]);
		struct log_record   *other = 
		    on_other_module(source,ptr->arg_ptrs[1],module);

		struct stat X;

		if (stat(ab,&X) < 0) {

		    if (ENOENT == errno)
			ok = 1;
		    else {
			fprintf(stderr,
				"%s: Can't stat %s\n",
				program_name,ab);
			exit_stat = 1;			
		    }

		} else if (

#ifdef S_ISLNK
			   ! S_ISREG(X.st_mode)
#else
			   S_IFREG != (X.st_mode & S_IFMT)
#endif
			   ) {

		    fprintf(stderr,
			    "%s: Name %s is not regular file (link)\n",
			    program_name,ab);

		} else if (other) {

		    fprintf(stdout,
			    "%s: Keeping link %s, used by %s package/module\n",
			    program_name,ab,
			    other->module_ptr ? other->module_ptr : "main");

		    ok = 1;  /* Remove double reference */

		} else if ((!module || !module[0] || 0 != strcmp(module[0],"all")) &&
			(X.st_dev == this_program_dev &&
			 X.st_ino == this_program_ino ||
			 0 == strcmp(program_name,ab))) {

		    fprintf(stdout,
			    "%s: Keeping link %s, is this program\n",
			    program_name,ab);

		    this_program++;

		} else if (0 == unlink(ab)) {
		    fprintf(stdout,
			    "%s: Link %s uninstalled\n",
			    program_name,ab);
		    ok = 1;
		} else if (ENOENT == errno)
		    ok = 1;
		else {
		    fprintf(stderr,
			    "%s: Can't unlink %s\n",
			    program_name,ab);
		    exit_stat = 1;
		}

		free(ab);
	    }
	    break;

	case 'M':  /* No failure if directory can't be uninstalled */

	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {

		char *ab = make_absolute(ptr,ptr->arg_ptrs[0]);

		if (0 == rmdir(ab)) {
		    fprintf(stdout,
			    "%s: Directory %s uninstalled\n",
			    program_name,ab);
		    ok = 1;
		} else if (ENOENT == errno)
		    ok = 1;

		free(ab);
	    }
	    break;

	case 'E':
	case 'R':
	    ok = 1;  /* JUST CLEAN */
	    break;

	case 'F':
	case 'd':     
	case 's':   /* also handled by unstage_list() */

	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {
		char *ab = make_absolute(ptr,ptr->arg_ptrs[0]);

		struct log_record   *other = 
		    on_other_module(source,ptr->arg_ptrs[0],module);

		struct stat X;

		if (stat(ab,&X) < 0) {

		    if (ENOENT == errno)
			ok = 1;
		    else {
			fprintf(stderr,
				"%s: Can't stat %s\n",
				program_name,ab);
			exit_stat = 1;			
		    }

		} else if (

#ifdef S_ISREG
			   ! S_ISREG(X.st_mode)
#else
			   S_IFREG != (X.st_mode & S_IFMT)
#endif
			   ) {

		    fprintf(stderr,
			    "%s: Name %s is not regular file\n",
			    program_name,ab);

		} else if (other) {

		    fprintf(stdout,
			    "%s: Keeping file %s, used by %s package/module\n",
			    program_name,ab,
			    other->module_ptr ? other->module_ptr : "main");

		    ok = 1;  /* Remove double reference */

		} else if ((!module || !module[0] || 0 != strcmp(module[0],"all")) &&
			   (X.st_dev == this_program_dev &&
			    X.st_ino == this_program_ino ||
			    0 == strcmp(program_name,ab))) {
		    
		    fprintf(stdout,
			    "%s: Keeping file %s, is this program\n",
			    program_name,ab);

		    this_program++;

		} else if (0 == unlink(ab)) {
		    fprintf(stdout,
			    "%s: File %s uninstalled\n",
			    program_name,ab);
		    ok = 1;
		} else if (ENOENT == errno)
		    ok = 1;
		else {
		    fprintf(stdout,
			    "%s: Can't unlink %s\n",
			    program_name,ab);
		    exit_stat = 1;
		}

		free(ab);
	    }
	    break;

	case 'C':
	    
	    if (ptr->arg_ptrs && ptr->arg_ptrs[0]) {
		char *ab = make_absolute(ptr,ptr->arg_ptrs[0]);

		struct log_record   *other = 
		    on_other_module(source,ptr->arg_ptrs[0],module);

		struct stat X;

		if (stat(ab,&X) < 0) {

		    if (ENOENT == errno)
			ok = 1;
		    else {
			fprintf(stderr,
				"%s: Can't stat %s\n",
				program_name,ab);
			exit_stat = 1;			
		    }

		} else if (

#ifdef S_ISREG
			   ! S_ISREG(X.st_mode)
#else
			   S_IFREG != (X.st_mode & S_IFMT)
#endif
			   ) {

		    fprintf(stderr,
			    "%s: Name %s is not regular file\n",
			    program_name,ab);

		} else if (other) {

		    fprintf(stdout,
			    "%s: Keeping config file %s, used by %s package/module\n",
			    program_name,ab,
			    other->module_ptr ? other->module_ptr : "main");

		    ok = 1;  /* Remove double reference */

		} else if (0 == unlink(ab)) {
		    fprintf(stdout,
			    "%s: Config file %s uninstalled\n",
			    program_name,ab);
		    ok = 1;
		} else if (ENOENT == errno)
		    ok = 1;
		else {
		    fprintf(stdout,
			    "%s: Can't unlink %s\n",
			    program_name,ab);
		    exit_stat = 1;
		}

		free(ab);
	    }
	    break;

	case '{':  /* SHOULD ALREADY BE HANDLED unstage_list() */
	    
	    break;

	}
	
	if (ok) {
	    if (ptr->buffer) {
		free(ptr->buffer);
		ptr->buffer   = NULL;
	    }
	    
	    if (ptr->arg_ptrs) {
		free(ptr->arg_ptrs);
		ptr->arg_ptrs = NULL;
	    }
	    
	    ptr->command_letter = '\0';   /* ENTRY DELETED */
	}
	    

    }


    return exit_stat;
}


static int replay_list P_((struct log_entries *entry, 
			   struct log_instance *source, 
			   struct log_instance *target,
			   struct log_entries * last,
			   char **module));
static int replay_list(entry,source,target,last, module)
     struct log_entries *entry; 
     struct log_instance *source; 
     struct log_instance *target;
     struct log_entries * last;
     char **module;
{
    struct log_record   * ptr;
    struct log_record   * LAST_ARG = NULL;

    int Exit_status = 0;
    
    int L = strlen(installer_root);
    int n;


    for (n = 0; n < entry->log_count; n++) {
	/* Log copy loop adds new entries to array
	   so we can't just increment ptr
	*/
	ptr = & entry->log[n];

	if (! filter_record(ptr,module,1)) {
	    LAST_ARG = NULL;
	    continue;
	}

	switch (ptr->command_letter) {

	case '\0':   /* ENTRY DELETED */
	    break;

	case '{':  
	    if (!LAST_ARG) {
		LAST_ARG = ptr;
	    }
	    break;

	case 'F':
	case 'd':     
	case 'C':
	case 'M':
	case 'R':

	    LAST_ARG = NULL;
	    break;



	case 'L':    /* link */
	    {
		int Exit_it = 0;

		char * file1 =  make_absolute(ptr,ptr->arg_ptrs[0]);
		char * file2 =  make_absolute(ptr,ptr->arg_ptrs[1]);

		char * tempname = gen_tempname(file2);

		if (0 == unlink(tempname)) {
		    fprintf(stderr,"%s: %s unlinked\n",program_name,tempname);
		}

		if (-1 == link(file1,tempname)) {
		    fprintf(stderr,
			    "%s: Failed to link %s to %s\n",
			    program_name,file1,tempname);
		    
		    Exit_it = 1;
		    goto fail3;
		} 
		    

		if (-1 == rename(tempname,file2)) {
		    fprintf(stderr,
			    "%s: Failed to replace %s\n",
			    program_name,file2);
		    Exit_it = 1;
		    goto fail3;
		}

		fprintf(stdout,"%s: Link %s == %s installed\n",program_name,file2,file1);


	    fail3:

		free(tempname);
		free(file1);
		free(file2);

	       	if (Exit_it)
		    return Exit_it; /* FAILURE */
		   
		if (target != source)
		    move_record(target,last,ptr);

		LAST_ARG = NULL;
	    }
	    break;

	case 's':
	    /* NOT BREAK LAST_ARG -- is content of directory specified as arg */
	    break;

	case 'E':  /* EXECUTE ... */
	    {
		char * translated [MAX_ARGS+1];
		int i,j;
		   
		int pid,pipefd;
		FILE *fd;

		int w;
		S__ status;


		for (i = 0; ptr->arg_ptrs[i] && i < MAX_ARGS; i++) {

		    int z;

		    if ('{' == ptr->arg_ptrs[i][0] &&
			( z = strlen(ptr->arg_ptrs[i])) &&
			'}' == ptr->arg_ptrs[i][z-1]) {

			struct log_record   *k;

			if (!LAST_ARG) {
			    fprintf(stderr,
				    "%s: [replay_list] No arguments stored... %s\n",
				    program_name,ptr->arg_ptrs[i]);
			    return 1; /* FAILURE */
			}
		    
			for  (k = LAST_ARG; k < ptr; k++) {
			    if ('{' == k->command_letter &&
				k->command_ptr &&
				0 == strcmp(ptr->arg_ptrs[i],
					    k->command_ptr))
				break;
			}

			if (k == ptr) {
			    fprintf(stderr,
				    "%s: [replay_list] Argument %s not found\n",
				    program_name,ptr->arg_ptrs[i]);
			    return 1; /* FAILURE */
			}

			if (source != target) {  
			    /* NEED COPY */
			    char * src = NULL;
			    char * trg;

			    uid_t  id_user  = (uid_t)-1;
			    gid_t  id_group = (gid_t)-1;
			    int mode;
			    int is_dir;

			    pick_one(k,&src,L,&id_user,&id_group,&mode,&is_dir);

			    trg = src + L;

			    if (is_dir < 0) {
				/* Assumed to be target file */

			    } else if (is_dir) {
				
				copy_dir(src,trg,k->arg_ptrs[0],target,last,
					 k->module_ptr);
				
			    } else {
				
				if (0 != install_file(src,trg,id_user,id_group,mode)) {
				    fprintf(stderr,
					    "%s: [replay_list] '%c' Failed to install %s : %s -> %s\n",
					    program_name,k->command_letter,
					    k->arg_ptrs[0],src,trg);
				    
				    return 1; /* FAILURE */
				}	
			    }

			    move_record(target,last,k);
			    
			    translated[i] = strdup(trg);
			    if (!translated[i]) {
				fprintf(stderr,
					"%s: [replay_list] strdup failed: %s \n",
					program_name,trg);

				Exit_status = 1;   /* FAILURE */
				goto fail;
			    }


			    free(src);
			} else {

			    /* Just translate reference ... */
			    int l = strlen(k->arg_ptrs[0]) + 2;
			    translated[i] = malloc(l);

			    if (!translated[i]) {
				fprintf(stderr,
					"%s: [replay_list] malloc %d bytes failed\n",
					program_name,l);
				
				Exit_status =  1; /* FAILURE */
				goto fail;
			    }
			    strcpy(translated[i],"/");
			    strcat(translated[i],k->arg_ptrs[0]);
			    
			}

		    } else {
			translated[i] = strdup(ptr->arg_ptrs[i]);
			if (!translated[i]) {
			    fprintf(stderr,
				    "%s: [replay_list] strdup failed: %s \n",
				    program_name,ptr->arg_ptrs[i]);
			    Exit_status = 1;
			    goto fail;
			}
		    }
		}
		translated[i] = NULL;

		fprintf(stdout,
			"%s: Executing",program_name);
		for (j = 0; j < i; j++)
		    fprintf(stdout," %s",
			    ptr->arg_ptrs[j]);
		fprintf(stdout,
			"\n%s:        As",program_name);
		for (j = 0; j < i; j++)
		    fprintf(stdout," %s",
			    translated[j]);
		fprintf(stdout,"\n");
		fflush(stdout);

		pipefd = create_pipe(i,translated,0,&pid,"/",
				     ptr->module_ptr);

		fd = fdopen(pipefd,"r");
		if (!fd) {
		    fprintf(stderr,"%s: fdopen() failed\n",program_name);
		    close(pipefd);
		    Exit_status = 1;  /* FAILURE */
		    goto fail;
		}

		log_copy_loop(fd,target,last);

		fclose(fd);
		
		while ((w = 
#ifdef HASWAITPID
			waitpid(pid,&status,0)
#else
			wait(status)
#endif
			) != pid)
		    if (w == -1 && errno != EINTR)
			break;
		
		if (w != pid || status != 0) {
		    fprintf(stderr,
			    "%s: Failed to run %s\n",
			    program_name,translated[0]);
		    Exit_status = 1;   /* FAILURE */	
		}

	    fail:
		for (; i >= 0; i--) {
		    if (translated[i]) {
			free(translated[i]);

			translated[i] = NULL;
		    }
		}

		if (Exit_status)
		    return Exit_status;

	    }
	    LAST_ARG = NULL;
	    break;
	default:
	    fprintf(stderr,
		    "%s: [replay_list] Bad command letter: %c \n",
		    program_name,ptr->command_letter);
	    return 1;
	}
    }

    /* Fixing permissions */
    for (ptr = entry->log; 
	 ptr < entry->log + entry->log_count; 
	 ptr++) {

	if (! filter_record(ptr,module,1))
	    continue;

	switch (ptr->command_letter) {
	    char *X;
	    struct passwd * p;
	    struct group  * g;
	    struct stat  X1;
	    int mode;
	    uid_t  user;
	    gid_t  group;


	case 'M':
	case 'F':
	case 'd':     
	case 'C':

	    p = NULL;
	    g = NULL;
	    mode  = -1;
	    user  = (uid_t)-1;
	    group = (gid_t)-1; 

	    if (!ptr->arg_ptrs || !ptr->arg_ptrs[0]) {
		
		fprintf(stderr,
			"%s: [replay_list] '%c' no arg 0 \n",
			program_name,ptr->command_letter);
		exit(1);  /* FAILURE */
	    }
	    X = make_rooted(ptr,ptr->arg_ptrs[0],"/");

	    if (ptr->user_ptr && 0 != strcmp(ptr->user_ptr,"-")) {
		p = getpwnam(ptr->user_ptr);
		
		if (!p) 
		    fprintf(stderr,
			    "%s: [replay_list] '%c' User %s not found\n",
			    program_name,ptr->command_letter,
			    ptr->user_ptr);
		else {
		    user = p->pw_uid;
		    group = p->pw_gid;
		}
	    }

	    if (ptr->group_ptr && 0 != strcmp(ptr->group_ptr,"-")) {
		g = getgrnam(ptr->group_ptr);

		if (!g) 
		    fprintf(stderr,
			    "%s: [replay_list] '%c' Group %s not found\n",
			    program_name,ptr->command_letter,
			    ptr->group_ptr);
		else
		    group = g->gr_gid;
		    
	    }

	    if (ptr->mode_ptr && 0 != strcmp(ptr->mode_ptr,"-") &&
		0 != strcmp(ptr->mode_ptr,"0")) {
		char * x;
		mode = strtol(ptr->mode_ptr,&x,8);
		
		if (*x || 0 == mode)
		    mode = -1;
	    }

	    if (stat(X,&X1) < 0) {
		fprintf(stderr,
			"%s: (note) %s : %s not accessible\n",
			program_name,
			ptr->arg_ptrs[0],X);

		Exit_status = 1;  /* FAILURE */

		goto failure1;
	    }
		

	    if (
#ifdef S_ISDIR
		S_ISDIR(X1.st_mode)
#else
		S_IFDIR == (X1.st_mode & S_IFMT)
#endif
		) {

		if (ptr->command_letter != 'M') {
		    fprintf(stderr,
			    "%s: (note) %s : %s is directory when should not\n",
			    program_name,
			    ptr->arg_ptrs[0],X);
		    
		    Exit_status = 1; /* FAILURE */
		    
		    goto failure1;		
		}

	    } else {
    		if (ptr->command_letter == 'M') {
		    fprintf(stderr,
			    "%s: (note) %s : %s is not a directory\n",
			    program_name,
			    ptr->arg_ptrs[0],X);
		    
		    Exit_status = 1;   /* FAILURE */		    
		    goto failure1;				    
		}	    	
	    }

	    if (user != (uid_t)-1 || group != (gid_t)-1) {
		if (X1.st_uid != user || X1.st_gid != group) {
		    if (-1 == chown(X,user,group)) {
			fprintf(stderr,"%s: Failed to set owner/group of %s\n",
				program_name,X);
			Exit_status = 1;   /* FAILURE */
			goto failure1;				    
		    }
		} else {
		    fprintf(stdout,"%s: Fixed owner/group of %s\n",
			    program_name,X);
		}

		/* Recheck mode */
		if (stat(X,&X1) < 0) {
		    fprintf(stderr,
			    "%s: (note) %s : %s not accessible\n",
			    program_name,
			    ptr->arg_ptrs[0],X);
		    
		    Exit_status = 1;  /* FAILURE */
		    
		    goto failure1;
		}
	    }

	    if (mode > 0) {

		if ((X1.st_mode & 0777) != mode) {
		    if (-1 == chmod(X,mode)) {
			fprintf(stderr,"%s: Failed to set mode of %s\n",
				program_name,X);	
			Exit_status = 1;  /* FAILURE */
		    } else {
			fprintf(stdout,"%s: Mode of %s fixed\n",
				program_name,X);	
		    }
		}

	    }
	    
	failure1:
	    free(X); X = 0;
	    break;
	default:
	    break;
	}
    }

    return Exit_status;
}

static int replay P_((int argc, char *argv[], int x));
static int replay(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    struct stat X;
    struct log_instance * target = NULL;
    int i;
    int r = 0;

    struct log_instance LI_source;
    struct log_instance LI_target;
    struct log_entries * last = NULL;

    char **module_filter = NULL;
    int module_count = 0;

    int remove_count;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];
    
    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI_source);
    clear_log_instance(&LI_target);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {
	    
	    filelist = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {

	    installer_root = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {

	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));

	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }
	    

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	

	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    

    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    		     
    LI_source.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI_source.filename = malloc(l);
	if (!LI_source.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI_source.filename,"%s%s",installer_root,filelist);	
    }
   
    LI_source.file = fopen(LI_source.filename,"r+");
    if (!LI_source.file) {
	fprintf(stderr,
		"%s: Failed to open file %s on %s: %s\n",
		program_name,filelist,installer_root,LI_source.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI_source);
    
    if (LI_source.entries_count < 1 ||
	0 != strcmp(LI_source.entries[LI_source.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s on %s (file %s) is not %s\n",
		program_name,filelist,installer_root,
		LI_source.filename,
		my_version);
	fclose(LI_source.file);
	return 1;  /* FAILURE */
    }

    if (0 != strcmp(installer_root,"/")) {
	int i;
	int t;

	fprintf (stdout,
		 "%s:  Copying ELM files from %s to / \n",
		 program_name,installer_root);

	LI_target.filename = filelist;

	if (0 == mkdir(ETCHOME,0755)) {
	    fprintf(stdout,
		    "%s: Directory %s created\n",
		    program_name,ETCHOME);
	} else if (errno != EEXIST) {
		fprintf(stderr,
			"%s: Failed to create %s directory\n",
			program_name,ETCHOME);
		return 1; /* FAILURE */
	}

	t = open(LI_target.filename,O_RDWR|O_CREAT,0644);
	if (t < 0) {
	    fprintf(stderr,
		    "%s: Failed to open file %s on target (/): %s\n",
		    program_name,filelist,LI_target.filename);

	    fclose(LI_source.file);

	    return 1;  /* FAILURE */
	}

	LI_target.file = fdopen(t,"r+");
	if (!LI_target.file) {
	    fprintf(stderr,
		    "%s: Failed to fdopen file %s on target (/): %s\n",
		    program_name,filelist,LI_target.filename);

	    fclose(LI_source.file);
	    close(t);
	    return 1;  /* FAILURE */	    
	}
	
	add_entries(&LI_target);

	target = &LI_target;

	last = add_last(target);

	/* Copy files marked for current version ... 
	 * replay_list() will copy '{' files	
	 */
	for (i = 0; i < LI_source.entries_count; i++) {
	    if (0 == strcmp(LI_source.entries[i].version, my_version)) {

		r = copy_list(& LI_source.entries[i], &LI_source, &LI_target,
			      last, module_filter);
		if (0 != r)
		    goto fail;
	    } else {
		fprintf(stderr,
			"%s: On source area (%s) have files for version %s -- NOT COPIED\n",
			program_name,installer_root,
			LI_source.entries[i].version);
	    }
	}

	if (0 != fflush(LI_target.file)) {
	    fprintf(stderr,
		    "%s: Failed to write file %s on target (/): %s\n",
		    program_name,filelist,LI_target.filename);
	    
	    fclose(LI_source.file);
	    
	    return 1;  /* FAILURE */	    
	}
	
    } else {
	target = & LI_source;
	last   = & LI_source.entries[LI_source.entries_count-1];
    }

    /* EXECUTE commands and fix permissions */
    r = replay_list(&LI_source.entries[LI_source.entries_count-1],
		    &LI_source,target,last,
		    module_filter);
    
    /* Trim files marked for other versions ... 
       LAST entry is current so not need handle it ... 
    */

    for (i = 0; i < target->entries_count-1; i++) {
	if (0 != strcmp(target->entries[i].version, my_version)) {
	    
	    trim_list(& target->entries[i], target, module_filter);
	}
    }

    remove_unlinked(target,"/",&remove_count);

    rewrite_list(target);

    if (EOF == fclose(target->file)) {
	fprintf(stderr,
		"%s: Write(?) failed on closing of file: %s\n",
		program_name,target->filename);
	return 1;  /* FAILURE */
    }

 fail:
    return r;
}

static int uninstall P_((int argc, char *argv[], int x));
static int uninstall(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;

    struct log_instance LI;
    int remove_count;
    int i;

    char **module_filter = NULL;
    int module_count = 0;

    struct stat X;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];
    
    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);


    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {

	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));

	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	

	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    
    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }

    if (module_count != 1 ||
	0 != strcmp(module_filter[0],"all")) {

	if (stat(program_name,&X) < 0) {
	    fprintf(stderr,
		    "%s: Failed to stat this program name -- needed to avoid unistalling this program\n",
		    program_name);
	    
	    r = 1;
	} else {
	    this_program_dev = X.st_dev;
	    this_program_ino = X.st_ino;
	}
    }



    LI.filename = filelist;
    LI.file = fopen(LI.filename,"r+");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    remove_unlinked(&LI,"/",&remove_count);

    for (i = 0; i < LI.entries_count; i++) {
	if (0 != unstage_list(& LI.entries[i], &LI,module_filter))
	    r = 1;
	if (0 != uninstall_list(& LI.entries[i], &LI,module_filter))
	    r = 1;
    }

    rewrite_list(&LI);

    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Write(?) failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    if (this_program) {
	fprintf(stdout,
		"%s: To unistall this program use %s -M all\n",
		program_name,program_name);
    }



    return r;
}

static int unstage P_((int argc, char *argv[], int x));
static int unstage(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;

    struct log_instance LI;
    int remove_count;
    int i;

    char **module_filter = NULL;
    int module_count = 0;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];
    
    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);

    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {

	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));

	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	

	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }

    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    LI.filename = filelist;
    LI.file = fopen(LI.filename,"r+");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    remove_unlinked(&LI,"/",&remove_count);

    for (i = 0; i < LI.entries_count; i++) {
	if (0 != unstage_list(& LI.entries[i], &LI, module_filter))
	    r = 1;
    }

    rewrite_list(&LI);

    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Write(?) failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }


    return r;
}



static int dir_list P_((int argc, char *argv[], int x));
static int dir_list(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;
    struct stat X;

    struct log_instance LI;
    int i;

    char **module_filter = NULL;
    int module_count = 0;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];

    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {
	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {
	    
	    installer_root = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {
	    
	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));
	    
	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	
	    
	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    
    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }


    LI.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI.filename = malloc(l);
	if (!LI.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI.filename,"%s%s",installer_root,filelist);	
    }

    LI.file = fopen(LI.filename,"r");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    for (i = 0; i < LI.entries_count; i++) {
	struct log_entries *I = &(LI.entries[i]);
	int r;

	/* Skip error message if list is actually empty */
	if (I->log_count < 1)
	    continue;

	if (0 != strcmp(I->version,my_version)) {
	    fprintf(stderr,
		    "%s: File %s includes version %s entries (current %s)\n",
		    program_name,
		    LI.filename,
		    I->version,
		    my_version);

	}

	for (r = 0; r < I->log_count; r++) {
	    struct log_record * R = & (I->log[r]);
	    
	    char * file  = NULL;;
	    
	    if (! filter_record(R,module_filter,1))
		continue;
	    
	    if (R->arg_ptrs && R->arg_ptrs[0])
		file = R->arg_ptrs[0];
	    
	    if (!file)
		continue;
	    if ('/' == file[0])
		file++;
	    
	    switch (R->command_letter) {
	    case 'M':

		fputs(file,stdout);
		fputc('\n',stdout);
		
	    case 'd':  /* doc */
	    case 'F':
	    case 's':
	    case 'C':  /* config */
	    case 'E':
	    case 'R':
	    case 'L':
		break;
		
	    case '{': {
		char *F = make_rooted(R,file,installer_root);
		struct stat X;

		if (stat(F,&X) < 0) {
		    if (ENOENT != errno)
			fprintf(stderr,
				"%s: '%c' File %s (%s) not accessible\n",
				program_name,R->command_letter,
				file,F);
		    
		} else if (
#ifdef S_ISDIR
			   S_ISDIR(X.st_mode)
#else
			   S_IFDIR == (X.st_mode & S_IFMT)
#endif
			   ) {

		    fputs(file,stdout);
		    fputc('/',stdout);
		    fputc('\n',stdout);
		    
		}
		free(F);

	    }
		break;

	    default:
		fprintf(stderr,
			"%s: '%c' Bad command letter\n",
			program_name,R->command_letter);
				
	    }
	}
    }

    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    return r;
}

static int tar_list P_((int argc, char *argv[], int x));
static int tar_list(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;
    struct stat X;

    struct log_instance LI;
    int i;

    char **module_filter = NULL;
    int module_count = 0;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];

    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {
	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {
	    
	    installer_root = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {
	    
	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));
	    
	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	
	    
	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    
    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }


    LI.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI.filename = malloc(l);
	if (!LI.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI.filename,"%s%s",installer_root,filelist);	
    }

    LI.file = fopen(LI.filename,"r");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    for (i = 0; i < LI.entries_count; i++) {
	struct log_entries *I = &(LI.entries[i]);
	int r;

	/* Skip error message if list is actually empty */
	if (I->log_count < 1)
	    continue;

	if (0 != strcmp(I->version,my_version)) {
	    fprintf(stderr,
		    "%s: File %s includes version %s entries (current %s)\n",
		    program_name,
		    LI.filename,
		    I->version,
		    my_version);

	}

	for (r = 0; r < I->log_count; r++) {
	    struct log_record * R = & (I->log[r]);

	    char * file  = NULL;;

	    if (! filter_record(R,module_filter,1))
		continue;
	    
	    if (R->arg_ptrs && R->arg_ptrs[0])
		file = R->arg_ptrs[0];

	    if (!file)
		continue;
	    if ('/' == file[0])
		file++;

	    switch (R->command_letter) {
	    case 'M':   /* Directories not listed */
		break;

	    case 'd':  /* doc */
	    case 'F':
	    case 's':
	    case 'C':  /* config */
		
		fputs(file,stdout);
		fputc('\n',stdout);

		break;

	    case 'E':
		break;

	    case 'R':
		break;


	    case 'L':
		/* Hard links not listed (although OK) */
		break;
		
	    case '{': {
		char *F = make_rooted(R,file,installer_root);
		struct stat X;

		if (stat(F,&X) < 0) {
		    if (ENOENT != errno)
			fprintf(stderr,
				"%s: '%c' File %s (%s) not accessible\n",
				program_name,R->command_letter,
				file,F);
		    
		} else if (
#ifdef S_ISDIR
			   S_ISDIR(X.st_mode)
#else
			   S_IFDIR == (X.st_mode & S_IFMT)
#endif
			   ) {

#if 0
		    fprintf(stderr,
			    "%s: '%c' File %s (%s) is directory\n",
			    program_name,R->command_letter,
			    file,F);
#endif


#ifndef DIROPS
		    if (! R->command_ptr || 0 != strcmp(R->command_ptr,IMPARGDIR)) {

			/* If no DIROPS, then files are not listed, so include dir to listing */
			
			fputs(file,stdout);
			fputc('/',stdout);
			fputc('\n',stdout);
		    }
#endif

		} else {
		    
		    fputs(file,stdout);
		    fputc('\n',stdout);

		}

		free(F);
	    }
		break;

	    default:
		fprintf(stderr,
			"%s: '%c' Bad command letter\n",
			program_name,R->command_letter);
				
	    }
	}
    }


    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    return r;
}

static int rpm_list P_((int argc, char *argv[], int x));
static int rpm_list(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;
    struct stat X;

    struct log_instance LI;
    int i;
    char *user1  = "-";
    char *group1 = "-";

    char **module_filter = NULL;
    int module_count = 0;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];

    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	
	} else if (x < argc -1 && 0 == strcmp("-u", argv[x])) {
	    user1 = argv[x+1];
	    x += 2;	       
	} else if (x < argc -1 && 0 == strcmp("-g", argv[x])) {
	    group1 = argv[x+1];
	    x += 2;	       
	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {
	    
	    installer_root = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {
	    
	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));
	    
	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	
	    
	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    
    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }


    LI.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI.filename = malloc(l);
	if (!LI.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI.filename,"%s%s",installer_root,filelist);	
    }

    LI.file = fopen(LI.filename,"r");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    for (i = 0; i < LI.entries_count; i++) {
	struct log_entries *I = &(LI.entries[i]);
	int r;

	/* Skip error message if list is actually empty */
	if (I->log_count < 1)
	    continue;

	if (0 != strcmp(I->version,my_version)) {
	    fprintf(stderr,
		    "%s: File %s includes version %s entries (current %s)\n",
		    program_name,
		    LI.filename,
		    I->version,
		    my_version);

	}

	for (r = 0; r < I->log_count; r++) {
	    struct log_record * R = & (I->log[r]);

	    char * user  = R->user_ptr;
	    char * group = R->group_ptr;
	    char * mode  = R->mode_ptr;
	    char * file  = NULL;;

	    if (! filter_record(R,module_filter,1))
		continue;
	    
	    if (R->arg_ptrs && R->arg_ptrs[0])
		file = R->arg_ptrs[0];

	    if (!user || 0 == strcmp(user,"-"))
		user = user1;
	    if (!group || 0 == strcmp(group,"-"))
		group = group1;
	    if (!mode || 0 == atoi(mode))
		mode = "-";

	    if (!file)
		continue;

	    switch (R->command_letter) {
	    case 'M':
		
		fputs("%dir ",stdout);

		if (0 != strcmp(user,"-") ||
		    0 != strcmp(group,"-") ||
		    0 != strcmp(mode,"-")) 
		    printf("%%attr(%s,%s,%s) ",mode,user,group);

		printf("%s%s\n",
		       file[0] != '/' ? "/" : "",
		       file);
		break;

	    case 'd':
		
		fputs("%doc ",stdout);

		if (0 != strcmp(user,"-") ||
		    0 != strcmp(group,"-") ||
		    0 != strcmp(mode,"-")) 
		    printf("%%attr(%s,%s,%s) ",mode,user,group);

		printf("%s%s\n",
		       file[0] != '/' ? "/" : "",
		       file);
		break;

	    case 'F':
	    case 's':

		if (0 != strcmp(user,"-") ||
		    0 != strcmp(group,"-") ||
		    0 != strcmp(mode,"-")) 
		    printf("%%attr(%s,%s,%s) ",mode,user,group);
		
		printf("%s%s\n",
		       file[0] != '/' ? "/" : "",
		       file);
		break;

	    case 'C':

		fputs("%config ",stdout);

		if (0 != strcmp(user,"-") ||
		    0 != strcmp(group,"-") ||
		    0 != strcmp(mode,"-")) 
		    printf("%%attr(%s,%s,%s) ",mode,user,group);
		
		printf("%s%s\n",
		       file[0] != '/' ? "/" : "",
		       file);
		break;

	    case 'E':
		break;

	    case 'R':
		break;


	    case 'L':   /* SECOND ARGUMENT IS NEW NAME 
			    
			   hard links need not to put archive -- they are created on
			   elmregister replay
			*/

		if (R->arg_ptrs && R->arg_ptrs[1]) {
		    char *new_file = R->arg_ptrs[1];

		    printf("%%ghost %s%s\n",
			   new_file[0] != '/' ? "/" : "",
			   new_file);
		}
		break;

	    case '{': {
		char *F = make_rooted(R,file,installer_root);
		struct stat X;

		if (stat(F,&X) < 0) {
		    if (ENOENT != errno)
			fprintf(stderr,
				"%s: '%c' File %s (%s) not accessible\n",
				program_name,R->command_letter,
				file,F);
		    
		} else if (
#ifdef S_ISDIR
			   S_ISDIR(X.st_mode)
#else
			   S_IFDIR == (X.st_mode & S_IFMT)
#endif
			   ) {

#if 0
		    fprintf(stderr,
			    "%s: '%c' File %s (%s) is directory\n",
			    program_name,R->command_letter,
			    file,F);
#endif
		    
		    if (R->command_ptr && 0 == strcmp(R->command_ptr,IMPARGDIR))
			fputs("%dir ",stdout);
#ifdef DIROPS
		    else 			
			/* Files are already logged ... */
			fputs("%dir ",stdout);
#endif

		    if (0 != strcmp(user,"-") ||
			0 != strcmp(group,"-") ||
			0 != strcmp(mode,"-")) 
			printf("%%attr(%s,%s,%s) ",mode,user,group);
		    
		    printf("%s%s\n",
			   file[0] != '/' ? "/" : "",
			   file);


		} else {

		    if (0 != strcmp(user,"-") ||
			0 != strcmp(group,"-") ||
			0 != strcmp(mode,"-")) 
			printf("%%attr(%s,%s,%s) ",mode,user,group);
		    
		    printf("%s%s\n",
			   file[0] != '/' ? "/" : "",
			   file);
		    
		}

		free(F);
	    }
		break;
		
	    default:
		fprintf(stderr,
			"%s: '%c' Bad command letter\n",
			program_name,R->command_letter);
				
	    }
	}
    }


    if (EOF == fclose(LI.file)) {
	fprintf(stderr,
		"%s: Failed on closing of file: %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }


    return r;
}

static int filter P_((int argc, char *argv[], int x));
static int filter(argc, argv, x)
     int argc;
     char *argv[];
     int x;
{
    int r = 0;
    struct stat X;

    struct log_instance * target = NULL;
    struct log_entries * last = NULL;

    struct log_instance LI;
    struct log_instance LI_target;

    int i;
    
    char **module_filter = NULL;
    int module_count = 0;

#if ANSI_C
    char my_version[] = "[ELM" VERSION " " PATCHLEVEL "]";
#else
    char my_version[40];

    sprintf(my_version,"[ELM %s %s]",VERSION,PATCHLEVEL);
#endif

    clear_log_instance(&LI);
    clear_log_instance(&LI_target);

    installer_root = "/";

    while (x < argc && '-' == argv[x][0]) {

	if (x < argc -1 && 0 == strcmp("-F", argv[x])) {

	    filelist = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-w", argv[x])) {

	    char * file = argv[x+1];
	    int t;
	    x += 2;	

	    if (LI_target.filename) {
		fprintf(stderr,
			"%s: Duplicate -w option\n",
			program_name);
		exit(1);

	    }

	    LI_target.filename = file;

	    t = open(LI_target.filename,O_RDWR|O_CREAT,0644);
	    if (t < 0) {
		fprintf(stderr,
			"%s: Failed to open file %s on targe: %s\n",
			program_name,filelist,LI_target.filename);
		
		fclose(LI.file);

		return 1;  /* FAILURE */
	    }
	    

	    LI_target.file = fdopen(t,"r+");
	    if (!LI_target.file) {
		fprintf(stderr,
			"%s: Failed to fdopen file %s on target (/): %s\n",
			program_name,filelist,LI_target.filename);
		
		fclose(LI.file);
		close(t);
		return 1;  /* FAILURE */	    
	    }
	    
	    add_entries(&LI_target);
	    
	    target = &LI_target;
	    
	    last = add_last(target);


	} else if (x < argc -1 && 0 == strcmp("-R", argv[x])) {
	    
	    installer_root = argv[x+1];
	    x += 2;	

	} else if (x < argc -1 && 0 == strcmp("-M", argv[x])) {
	    
	    module_filter = realloc(module_filter,
				    (module_count+2) *
				    sizeof (module_filter[0]));
	    
	    if (! module_filter) {
		fprintf(stderr,
			"%s: Failed to realloc -M option\n",
			program_name);
		exit(1);
	    }

	    module_filter[module_count++] = argv[x+1]; 
	    module_filter[module_count] = NULL;
	    x += 2;	
	    
	} else {
	    fprintf(stderr,
		    "%s: Bad option %s\n",
		    program_name,argv[x]);
	    exit(1);
	}
    }
    
    if (x < argc) {
	fprintf(stderr,
		"%s: Bad option %s\n",
		program_name,argv[x]);
	exit(1);
    }


    if (stat(installer_root,&X) < 0) {
	fprintf(stderr,"%s: -R %s invalid\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }

    if (
#ifdef S_ISDIR
	! S_ISDIR(X.st_mode)
#else
	S_IFDIR != (buf.st_mode & S_IFMT)
#endif
	) {
	fprintf(stderr,"%s: -R %s invalid (not a directory)\n",
		argv[0],installer_root);
	
	return 1;   /* FAILURE */
    }


    LI.filename = filelist;
    if (0 != strcmp(installer_root,"/")) {
	int l = strlen(filelist) + strlen(installer_root) +2;
	LI.filename = malloc(l);
	if (!LI.filename) {
	    fprintf(stderr,"%s: malloc %d bytes failed\n",
		    program_name,l);
	    return 1;   /* FAILURE */
	}
	sprintf(LI.filename,"%s%s",installer_root,filelist);	
    }

    LI.file = fopen(LI.filename,"r");
    if (!LI.file) {
	fprintf(stderr,
		"%s: Failed to open file %s\n",
		program_name,LI.filename);
	return 1;  /* FAILURE */
    }

    add_entries(&LI);

    if (LI.entries_count < 1 ||
	0 != strcmp(LI.entries[LI.entries_count-1].version,
		    my_version)) {

	fprintf(stderr,
		"%s: Last version on file %s is not %s\n",
		program_name,
		LI.filename,
		my_version);
	r = 1;
    }

    
    if (target) {

	for (i = 0; i < LI.entries_count; i++) {
	    struct log_entries *I = &(LI.entries[i]);
	    int r;
	    
	    /* Skip error message if list is actually empty */
	    if (I->log_count < 1)
		continue;
	    
	    if (0 != strcmp(I->version,my_version)) {
		fprintf(stderr,
			"%s: File %s includes version %s entries (current %s)\n",
			program_name,
			LI.filename,
			I->version,
			my_version);
		
	    }
	    
	    for (r = 0; r < I->log_count; r++) {
		struct log_record * R = & (I->log[r]);
				
		if (! filter_record(R,module_filter,1))
		    continue;
				
		move_record(target,last,R);
		
	    }
	    
	}
	
	if (EOF == fclose(target->file)) {
	    fprintf(stderr,
		    "%s: Write(?) failed on closing of file: %s\n",
		    program_name,target->filename);
	    return 1;  /* FAILURE */
	}	
    }

    return r;
}


int main P_((int argc, char *argv[]));
int main(argc, argv)
     int argc;
     char *argv[];
{

    int x = 1;
    int r = 1;
    char *y;

    enum register_mode {
	register_master,
	register_command,
	register_replay,
	register_uninstall,
	register_unstage,
	register_dir_list,
	register_tar_list,
	register_rpm_list,
	register_filter
    } mode = register_command;

    register_pipe   = getenv("ELM_INSTALLER");
    installer_root  = getenv("ELM_ROOT");
    register_module = getenv("ELM_MODULE");

    program_name   = argv[0];

#if defined(ANSI_C) || defined(SETLINEBUF)
    setlinebuf(stdout);
#endif

#if 0    
    if (register_pipe)
	fprintf(stderr,"%s: $ELM_INSTALLER=%s\n",program_name,register_pipe);
    if (installer_root)
	fprintf(stderr,"%s: $ELM_ROOT=%s\n",program_name,installer_root);
#endif

    if (x < argc -1 && 0 == strcmp("-S", argv[x])) {

	stage_dir = argv[x+1];
	x += 2;	
    }

#if 0
    if (stage_dir)
	fprintf(stderr,"%s: STAGE_DIR=%s\n",program_name,stage_dir);
#endif


    if ((y = strrchr(program_name,'/')) &&
	0 == strcmp("/elmuninstall",y)) {
	mode = register_uninstall;
    } else if (x < argc && 0 == strcmp("uninstall", argv[x])) {

	x += 1;
	mode = register_uninstall;
    } if (x < argc && 0 == strcmp("master", argv[x])) {

	x += 1;
	mode = register_master;
    } else if (x < argc && 0 == strcmp("replay", argv[x])) {

	x += 1;
	mode = register_replay;       
    } else if (x < argc && 0 == strcmp("unstage", argv[x])) {

	x += 1;
	mode = register_unstage;
    } else if (x < argc && 0 == strcmp("list", argv[x])) {

	x += 1;
	mode = register_tar_list;
    } else if (x < argc && 0 == strcmp("list-directories", argv[x])) {

	x += 1;
	mode = register_dir_list;
    } else if (x < argc && 0 == strcmp("rpm-list", argv[x])) {

	x += 1;
	mode = register_rpm_list;
    } else if (x < argc && 0 == strcmp("filter", argv[x])) {

	x += 1;
	mode = register_filter;
    }    

    if (mode != register_command) {

	if (register_pipe) {
	    fprintf(stderr,
		    "%s: $ELM_INSTALLER=%s incompatible with arguments 'master' and 'replay', 'unstage'\n",
		    argv[0],register_pipe);
	    
	    return 1;   /* FAILURE */
	}

	if (installer_root) {
	    fprintf(stderr,"%s: $ELM_ROOT=%s incompatible with arguments 'master' and 'replay, 'unstage'\n",
		    argv[0],installer_root);
	    
	    return 1;   /* FAILURE */
	}

	if (installer_root) {
	    fprintf(stderr,"%s: $ELM_MODULE=%s incompatible with arguments 'master' and 'replay, 'unstage'\n",
		    argv[0],installer_root);
	    
	    return 1;   /* FAILURE */
	}


    }



    switch(mode) {
    case register_master:  r = master(argc,argv,x); break;
    case register_command: r = command(argc,argv,x); break;
    case register_replay:  r = replay(argc,argv,x); break;
    case register_uninstall: r = uninstall(argc,argv,x); break;
    case register_unstage:  r = unstage(argc,argv,x); break;
    case register_tar_list: r = tar_list(argc,argv,x); break;
    case register_dir_list: r = dir_list(argc,argv,x); break;
    case register_rpm_list: r = rpm_list(argc,argv,x); break;
    case register_filter:   r = filter(argc,argv,x); break;
    }
   
    return r;
}

/*
 * Local Variables:
 *  mode:c
 *  c-basic-offset:4
 *  buffer-file-coding-system: iso-8859-1
 * End:
 */


syntax highlighted by Code2HTML, v. 0.9.1