#ifdef linux
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/unistd.h>
static inline _syscall3(int,write,int,fd,const void *,buf,off_t,count)
static inline _syscall3(int,read,int,fd,void *,buf,off_t,count)
static inline _syscall2(int,fchmod,int,fd,mode_t,mode)
static inline _syscall1(int,close,int,fd)
extern void _exit (int);
#define exit _exit
extern int open (const char *, int, ...);
extern int printf (const char *, ...);
#else
#include <stdio.h>
#include <fcntl.h>
#endif

#include <sys/stat.h>

#ifdef linux
inline _syscall2(int,fstat,int,fd,struct stat *,buf)
#endif

#define MAX_NAMELEN  255

struct ftranbuf {
	off_t  len;
	size_t namelen;
	char   name[MAX_NAMELEN+1];
	mode_t mode;
};

char *argv0;

static void Usage (void) {
	printf ("Usage: %s -w file name outdev\n", argv0);
	printf ("   or: %s -r dev\n", argv0);
	printf ("\te.g. %s -w ls /bin/ls flat:df0\n", argv0);
	printf ("\tor   %s -r /dev/fd0\n", argv0);
	exit (1);
}

void dowrite (char *infile, char *name, char *outdev);
void doread (char *indev);

int main(int argc, char **argv)
{
	argv0 = argv[0];
	if (argc != 3 && argc != 5)
		Usage ();

	if (strcmp (argv[1], "-w") == 0 && argc == 5) {
		dowrite (argv[2], argv[3], argv[4]);
	} else if (strcmp (argv[1], "-r") == 0 && argc == 3) {
		doread (argv[2]);
	} else
		Usage ();

	exit (0);
}

void dowrite (char *infile, char *name, char *outdev)
{
	int infd, outfd;
	struct ftranbuf fbuf;
	static char buf[1024];
	static struct stat sbuf;
	off_t size;

	fbuf.namelen = strlen (name);
	if (!fbuf.namelen || fbuf.namelen > MAX_NAMELEN) {
		printf ("%s: name length must be > 0 and < %d\n", argv0,
			MAX_NAMELEN);
		exit (1);
	}
	strcpy (fbuf.name, name);

	if ((infd = open(infile, O_RDONLY)) == -1) {
		printf ("%s: Unable to open input file '%s'\n", argv0,
			infile);
		exit (1);
	}

	if (fstat(infd, &sbuf) == -1) {
		printf ("%s: Unable to stat input file\n", argv0);
		close (infd);
	}
	fbuf.len = size = sbuf.st_size;
	fbuf.mode = sbuf.st_mode;

	if ((outfd = open(outdev, O_WRONLY)) == -1) {
		printf ("%s: Unable to open output device '%s'\n", argv0,
			outdev);
		close (infd);
		exit (1);
	}

	if (write (outfd, &fbuf, sizeof (fbuf)) != sizeof(fbuf)) {
		printf ("%s: Unable to write file information\n", argv0);
		close (infd);
		close (outfd);
		exit (1);
	}

	while (size > 0) {
		int len = size > 1024 ? 1024 : size;

		if (read (infd, buf, len) != len) {
			printf ("%s: Unable to read file\n", argv0);
			close (infd);
			close (outfd);
			exit (1);
		}
		if (write (outfd, buf, len) != len) {
			printf ("%s: Unable to write file\n", argv0);
			close (infd);
			close (outfd);
			exit (1);
		}
		size -= len;
	}

	close (infd);
	close (outfd);

	printf ("%s: %d bytes of file '%s' written to device '%s' "
		"as name '%s'\n", argv0, sbuf.st_size, infile, outdev,
		 name);
}

void doread (char *indev)
{
	int infd, outfd;
	struct ftranbuf fbuf;
	static char buf[1024];
	off_t size;

	if ((infd = open(indev, O_RDONLY)) == -1) {
		printf ("%s: Unable to open input device '%s'\n", argv0,
			indev);
		exit (1);
	}

	if (read (infd, &fbuf, sizeof (fbuf)) != sizeof (fbuf)) {
		printf ("%s: Unable to read file information\n", argv0);
		close (infd);
		exit (1);
	}

	/* sanity check */
	if (!fbuf.namelen || fbuf.namelen > MAX_NAMELEN || !fbuf.len) {
		printf ("%s: bad information in file header\n", argv0);
		close (infd);
		exit (1);
	}

	if ((outfd = open (fbuf.name, O_CREAT | O_WRONLY, fbuf.mode)) == -1) {
		printf ("%s: Unable to open output file '%s'\n", argv0,
			fbuf.name);
		close (infd);
		exit (1);
	}
	fchmod (outfd, fbuf.mode);

	size = fbuf.len;
	while (size > 0) {
		int len = size > 1024 ? 1024 : size;

		if (read (infd, buf, len) != len) {
			printf ("%s: Unable to read file\n", argv0);
			close (infd);
			close (outfd);
			exit (1);
		}
		if (write (outfd, buf, len) != len) {
			printf ("%s: Unable to write file\n", argv0);
			close (infd);
			close (outfd);
			exit (1);
		}
		size -= len;
	}

	close (infd);
	close (outfd);

	printf ("%s: %ld bytes of file '%s' read from device '%s'\n",
		argv0, fbuf.len, fbuf.name, indev);
}

#ifdef linux
void _main (void) {}

#include <stdarg.h>

int printf (const char *fmt, ...)
{
    extern int vsprintf(char * buf, const char * fmt, va_list args);
    static char buf[256];
    va_list args;
    int i;

    va_start (args, fmt);
    i = vsprintf (buf, fmt, args);
    va_end (args);

    write (1, buf, i);

    return i;
}
#endif
