/* execute "time" and "rm";  passing on "get" to ftp.
 *	stdin = commands generated by "masterslave"
 *	argv[1] = <ftpcmd>   (typically ftp, but proxy-ftp at Bell Labs)
 *	argv[2] = <host>   (like netlib.att.com)
 *	argv[3] = <directory>  (like .)
 *	argv[4] = <compressed suffix list>  (typically /netlib/crc/net/compressed)
 */

/*
 * The author of this software is Eric Grosse.  Copyright (c) 1993 by AT&T.
 * Permission to use, copy, modify, and distribute this software for any
 * purpose without fee is hereby granted, provided that this entire notice
 * is included in all copies of any software which is or includes a copy
 * or modification of this software and in all copies of the supporting
 * documentation for such software.
 * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR AT&T MAKE ANY
 * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
 * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
 */

#define MESS_SIZE "%s had size %lu, expected %lu\n"
#define MESS_OWN "%s: not owner\n"
#define MESS_BIG "string too big: %s"

#define _POSIX_SOURCE
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <utime.h>
#include "suffix.h"

#ifndef LOOSE
typedef unsigned long ulong; /* some vendors, in violation of POSIX,
	define this in sys/types.h and don't allow matching redefinition. */
#endif
#ifndef NAME_MAX
#define NAME_MAX 255
#endif
#ifndef PATH_MAX
#define PATH_MAX 1023
#endif

extern int Create(char*,mode_t);
extern int Error(char*,...);

char *progname;
static int debug = 0;		/* if 1, print commands, don't actually do them */
static int uncompressed;		/* if 1, assume master site has no compression at all */
static int toftp;			/* is there any work for ftp? */
static char **ftparg;		/* argv[1] argv[2] argv[3] */
static char ftpdir[PATH_MAX+1];	/* relative to starting directory here
					and on remote.  No '/' on end. */
#define ftpcmd(cmd) printf("%s",cmd)


static int
chtime(char *name, ulong tim, ulong len)
{
	struct stat s;
	struct utimbuf u;

	if(stat(name,&s)!=0){		/* get actual time and length */
		fprintf(stderr,"can't stat %s\n",name);
		return -1;
	}
	if((off_t)len!=s.st_size){	/* verify length is same */
		fprintf(stderr,MESS_SIZE,name,(ulong)s.st_size,len);
		return -1;
	}
	u.actime = s.st_atime;
	u.modtime = (time_t)tim;
	if(utime(name,&u)!=0){		/* change mtime */
		if(errno==EPERM){
			fprintf(stderr,MESS_OWN,name);
			return -1;
		}
		fprintf(stderr,"can't utime %s\n",name);
		return -1;
	}
	return 0;
}

static void
walk(char *olddir, char *newdir)
{
	char *so, *sn;
	char cdcmd[5+2*PATH_MAX];

	/* see how far old and new agree */
	for( so=olddir, sn=newdir; *so==*sn; so++, sn++){
		if(*so=='/'){
			olddir = so+1;
			newdir = sn+1;
		}else if(*so=='\0'){
			olddir = so;
			newdir = sn;
			break;
		}
	}
	strcpy(cdcmd,"lcd ");
	while(so = strrchr(olddir,'/')){	/* walk up */
		strcat(cdcmd,"../");
		*so = '\0';
	}
	if(olddir[0]!='\0')
		strcat(cdcmd,"../");
	strcat(cdcmd,newdir);
	strcat(cdcmd,"\n");
	if(strcmp(cdcmd,"lcd \n")!=0){
		ftpcmd(cdcmd);		/* local cd */
		ftpcmd(cdcmd+1);	/* remote cd */
	}
	strcpy(olddir,newdir);
}

static int
get(char *name, ulong tim, ulong len, ulong sum, Suffix *suffixlist)
{
	char getcmd[5+NAME_MAX], work[PATH_MAX+1], *newdir, *basename, *suf;

	if(!debug)
		close(Create(name,0666));  /* make directories if necessary */

	if(!toftp){
		toftp = 1;
		printf("%s %s <<'!'\n",ftparg[0],ftparg[1]);  ftparg += 2;
		ftpcmd("prompt\n");
		ftpcmd("binary\n");
		ftpcmd("verbose\n");
		if(*ftparg){	/* starting directory on remote system */
			if(strlen(*ftparg)+5>PATH_MAX) Error(MESS_BIG,*ftparg);
			sprintf(work,"cd %s\n",*ftparg++);
			ftpcmd(work);
		}
	}

	/* cut path into directory and basename */
	if(strlen(name)+1>sizeof(work)) Error("filename overflow");
	strcpy(work,name);
	if(basename = strrchr(work,'/')){
		newdir = work;
		*basename++ = '\0';
	}else{
		newdir = "";
		basename = work;
	}
	walk(ftpdir,newdir);	/* cd as necessary */
	strcmp(ftpdir,newdir)==0 || Error("dir mismatch: can't happen");

	/* at last, get the file */
	suf = "";
	if(!uncompressed && need_compr(basename,suffixlist))
		suf = ".gz";
	sprintf(getcmd,"get %s%s\n",basename,suf);
	ftpcmd(getcmd);

	return 0;
}

int
main(int argc, char **argv)
{
#define DIE Error("can't %s %s",cmd,name)
	ulong tim, len, sum;
	char cmd[21], name[2001];
	Suffix *suffixlist;
	int err = 0;

	progname = argv[0];
	if(strcmp(argv[1],"-d")==0){
		debug = 1;
		argv++;
		argc--;
	}
	if(argc!=5)
		Error("args: <ftpcmd> <host> <directory> <suffixlist>\n"); 
	ftparg = &argv[1];
	uncompressed = read_suffix(argv[4],&suffixlist);
	while(5==fscanf(stdin,"%20s %2000s %lu %lu %lx",cmd,name,&tim,&len,&sum)){
		switch(*cmd){
		case 'g': /* get */
			get(name,tim,len,sum,suffixlist)==0 || DIE;
			break;
		case 'r': /* rm */
			if(!debug)
				unlink(name)==0 || DIE;
			break;
		case 't': /* time */
			if(!debug)
				chtime(name,tim,len);  /* don't DIE, just keep going */
			break;
		default:
			if(err++ < 10)
				fprintf(stderr,"? %s %s\n",cmd,name);
		}
	}
	if(toftp)
		printf("quit\n!\n");
	if(err)
		exit(1);
	exit(0);
}
