# include <stdio.h>
# include <stdlib.h>
# include <ctype.h>
# include <fcntl.h>
# include <sys/stat.h>
# include <sys/errno.h>


/* defines */
# define MAXPATH	1024
# define BUFSIZE	4096
# define DIRPREFIX	"DIRECTORY: disc0:/"
# define FILEPREFIX	"FILE: disc0:/"
# define SIZEPREFIX	"SIZE: "

# define BLOCKSIZE	4096

/* prototypes */
void usage (void);
char *getoneline (FILE *);
void unfold (char *);
void handle_dir (char *);
void handle_file (char *, FILE *);

/* globals */
static char *me;
static char *turn = "-\\|/";

main (int argc, char **argv)
{
    char *file = NULL;
    char *p;

    me = *argv;
    argc--;
    argv++;

    if (p = strrchr (me, '/')) {
        me = p + 1;
    }
    while (argc) {
        if (**argv == '-') {
	    usage ();
	}
	else if (file) {
	    usage ();
	}
	else {
	    file = *argv;
	}
	argc--;
	argv++;
    }
    if (!file) {
        usage ();
    }
    unfold (file);
}

void usage (void)
{
    fprintf (stderr, "Usage: %s <ripfile>\n", me);
    exit (1);
}

char *getoneline (FILE *fp)
{
    static char ret [MAXPATH];
    int c;
    char *p = &ret [0];

    while ((c = getc (fp)) != EOF) {
        *p++ = c;
	if (c == '\n') {
	    p--;
	    *p = '\0';
	    p--;
	    if (*p == '\r') {
		*p = '\0';
	    }
	    return ret;
	}
	else if (p - &ret[0] >= MAXPATH - 2) {
	    ret [31] = 0;
	    fprintf (stderr, "%s: line too long: %s...\n", me, ret);
	    exit (1);
	}
    }
    // on EOF
    return (NULL);
}

void unfold (char *file)
{
    FILE *fp;
    char *line;
    char oneline [MAXPATH];

    if ((fp = fopen (file, "rb")) == NULL) {
        perror (file);
        exit (1);
    }

    while (1) {
	if (!(line = getoneline (fp))) {
	    break;
	}
	strncpy (oneline, line, MAXPATH);
	if (!strncmp (oneline, DIRPREFIX, strlen (DIRPREFIX))) {
	    handle_dir (&oneline[strlen (DIRPREFIX)]);
	}
	else if (!strncmp (oneline, FILEPREFIX, strlen (FILEPREFIX))) {
	    handle_file (&oneline[strlen (FILEPREFIX)], fp);
	}
	else {
	    fprintf (stderr, "%s: unknown word in ripfile: %s\n", me, line);
	    exit (1);
	}
    }
   
    fclose (fp);
}

void handle_dir (char *path)
{
    if (mkdir (path, 0777) < 0) {
        if (errno = EEXIST) {
	    fprintf (stderr, "%s: directory %s already exists\n", me, path);
	    return;
	}
	perror ("mkdir failed");
	fprintf (stderr, "%s: failed to create directory %s, exiting.\n", path);
	exit (1);
    }
}


void write_file (char *path, FILE *fp, size_t size)
{
    register size_t i;
    int c;
    int ofd;
    char buffer [BLOCKSIZE];
    int count;
    char turnit = 0;
    /*
    printf ("(skipping %ld bytes) ", size);
    if (fseek (fp, size, SEEK_CUR)) {
        fprintf (stderr, "%s: fseek problem!\n", me);
	exit (1);
    }
    */
    fprintf (stderr, "File \"%s\": ", path);
    if ((ofd = open (path, O_WRONLY|O_CREAT|O_TRUNC, 0777)) < 0) {
        perror ("creat failed");
	fprintf (stderr, "%s: error creating %s, exiting.\n", me, path);
	exit (1);
    }
    fprintf (stderr, "writing %d bytes ", size);
    while (size > 0) {
        count = (BLOCKSIZE < size) ? BLOCKSIZE : size;
	for (i = 0; i < count; i++) {
	    if ((c = getc (fp)) == EOF) {
	        fprintf (stderr, "%s: unexpected EOF in input, exiting\n", me);
	        exit (1);
	    }
	    buffer [i] = c;
	}
	if (write (ofd, buffer, count) != count) {
	    perror ("write failed");
	    fprintf (stderr, "%s: unexpected write error, exiting\n", me);
	    exit (1);
	}
        size -= count;
	fprintf (stderr, "%c\b", turn [turnit]);
	fflush (stderr);
	turnit ++; turnit &= 3;
    }
    fprintf (stderr, "done!\n");
    close (ofd);
}

void handle_file (char *path, FILE *fp)
{
    int c;
    char *line;
    size_t size;

    // fprintf (stderr, "Found file \"%s\"\n", path);
    if (!(line = getoneline (fp))) {
        fprintf (stderr, "%s: could not read SIZE: line - exiting.\n", me);
	exit (1);
    }
    else if (strncmp (line, SIZEPREFIX, strlen (SIZEPREFIX))) {
        fprintf (stderr, "%s: expected SIZE, found \"%s\"\n", me, line);
	exit (1);
    }
    line += strlen (SIZEPREFIX);
    if (!isdigit (*line)) {
        fprintf (stderr, "%s: expected digits after SIZE, found \"%s\"\n", me, line);
	exit (1);
    }
    size = atol (line);
    if ((c = getc (fp)) != '\r') {
        fprintf (stderr, "%s: expected \\r after SIZE, found \"0x%X\"\n", me, c);
    }
    if ((c = getc (fp)) != '\n') {
        fprintf (stderr, "%s: expected \\n after SIZE, found \"0x%X\"\n", me, c);
    }

    write_file (path, fp, size);

    if ((c = getc (fp)) != '\r') {
        fprintf (stderr, "%s: expected \\r after SIZE, found \"0x%X\"\n", me, c);
    }
    if ((c = getc (fp)) != '\n') {
        fprintf (stderr, "%s: expected \\n after SIZE, found \"0x%X\"\n", me, c);
    }
    if ((c = getc (fp)) != '\r') {
        fprintf (stderr, "%s: expected \\r after SIZE, found \"0x%X\"\n", me, c);
    }
    if ((c = getc (fp)) != '\n') {
        fprintf (stderr, "%s: expected \\n after SIZE, found \"0x%X\"\n", me, c);
    }
}
