/*%cc -o push % -ldk
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <libc.h>
#include <ipc.h>

char targetdir[128];

char SERVER[]="/usr/lib/Rpull";
int server=2;	/* should be >1 until proven guilty */

char *
basename(s)
	register char *s;
{
	extern char *strrchr();
	register char *t;
	t=strrchr(s, '/');
	return(t? t+1 : s);
}

int errors=0;
int vflag;

main(argc, argv)
	char *argv[];
{
	register i;
	int status;
	register pid;
	char buf[129];
	if(argc>1 && strcmp(argv[1], "-v")==0){
		vflag=1;
		argv++;
		--argc;
	}
	if(argc<4){
		error("Usage: push machine <files|dirs> remotedir>", "");
		exit(1);
	}
	(void)strcat(strcpy(targetdir, argv[argc-1]), "/");
	if(basename(argv[0])[0]=='R')
		server=1;
	else{
		/* Set up the remote process */
		sprintf(buf, "%s %s", SERVER, argv[argc-1]);
		server=ipcexec(argv[1], "heavy delim hup", buf);
		if(server<0){
			fprintf(stderr, "push: can't execute %s (%s)\n", SERVER,
				errstr);
			exit(1);
		}
		/* Must print remote errors locally */
		if((pid=fork())==0){
			while((i=read(server, buf, sizeof buf))>0)
				report(buf, i);
			report("done\n", 5);
			exit(0);
		}else if(pid==-1){
			error("can't fork", "try again");
			exit(1);
		}
	}
	for(i=2; i<argc-1; i++)
		sendfile(argv[i]);
	write(server, buf, 0);		/* End of file */
	if(server>1)
		wait(&status);		/* To get all the error messages */
	return(errors);
}

#define	NDIRSTACK 20
char directory[128];		/* Name buffer for directories */
char *dirstack[NDIRSTACK];
char *basep=directory;
int dirstkp=0;

pushdir(s, m)
	register char *s;
{
	char buf[128];
	if(dirstkp>=NDIRSTACK){
		error("directory stack overflow", "");
		exit(1);
	}
	dirstack[dirstkp++]=directory+strlen(directory);
	(void)strcat(directory, s);
	if(dirstkp==1)	/* First entry on stack */
		basep=basename(directory);
	sprintf(buf, "%03o%s%s", m, targetdir, basep);
	message('D', buf);
	(void)strcat(directory, "/");
}

popdir()
{
	if(dirstkp<=0){
		error("directory stack underflow", "");
		exit(1);
	}
	*dirstack[--dirstkp]='\0';
	if(dirstkp<=0)
		basep=directory;
}


sendfile(s)
	register char *s;
{
	struct stat statbuf;
	char filebuf[128];
	char remotebuf[128];
	(void)strcat(strcpy(filebuf, directory), s);
	if(stat(filebuf, &statbuf)<0){
		error("can't stat", s);
		return;
	}
	if(server>1 && vflag)
		printf("push: send %s\n", filebuf);
	switch(statbuf.st_mode&S_IFMT){
	case S_IFDIR:
		senddir(filebuf, s, statbuf.st_mode&0777);
		break;
	case S_IFREG:
		sprintf(remotebuf, "%03o%s%s%s", statbuf.st_mode&0777,
			targetdir, basep, basename(s));
		senddata(filebuf, remotebuf);
		break;
	case S_IFBLK:
	case S_IFCHR:
		error(s, "is a special file; can't send it");
		break;
	}
}

senddata(s, remote)
	register char *s, *remote;
{
	char buf[512];
	extern errno;
	register f, cc;
	int i;
	if((f=open(s, 0))<0){
		error("can't open", s);
		return;
	}
	message('F', remote);
	i = 0;
	while((cc=read(f, buf, sizeof buf))>0) {
		write(server, buf, cc);
		if (i%200 == 0)
			fprintf(stderr, ".");
		i++;
	}
	if (cc<0) {
		fprintf(stderr, read error %d\n", errno);
	}
	fprintf(stderr, "push done\n");
	write(server, buf, 0);
	close(f);
}

senddir(fullname, localname, m)
	register char *fullname, *localname;
{
	/* Same as <sys/dir.h> but with DIRSIZ+1 chars */
	struct direct15{
		ino_t	d_ino;
		char	d_name[DIRSIZ+1];
	}direct15;
	register df;
	if((df=open(fullname, 0))<0){
		error("can't open directory", fullname);
		return;
	}
	pushdir(localname, m);
	direct15.d_name[14]='\0';
	while(read(df, (char *)&direct15, sizeof(struct direct))==sizeof(struct direct)){
		if(direct15.d_ino && strcmp(direct15.d_name, ".")!=0
				  && strcmp(direct15.d_name, "..")!=0)
			sendfile(direct15.d_name);
	}
	popdir();
	close(df);
}
error(s, t)
	char *s, *t;
{
	char ebuf[512];
	errors=1;
	if(server>1)
		fprintf(stderr, "push: %s %s\n", s, t);
	else{
		sprintf(ebuf, "%s %s", s, t);
		message('E', ebuf);
	}
}

report(s, n)
	register char *s;
{
	static char msgbuf[512];
	static char *msgp=msgbuf;
	register i;
	fprintf(stderr, "report: %d: ", n);
	for(i=0; i<n; i++){
		*msgp= *s++;
		if(*msgp++=='\n'){
			write(2, "push: (remote) ", 14);
			write(2, msgbuf, msgp-msgbuf);
			msgp=msgbuf;
		}
	}
}

message(c, s)
	register char *s;
{
	char buf[128];
	buf[0]=c;
	strcpy(buf+1, s);
	write(server, buf, strlen(buf));
	write(server, buf, 0);		/* Terminate & synchronize */
}
