/*	triple.c

	Very simple I/O routines for triple-res
	rgb-separated 24-bit color raw PPM file I/O

	February 1999 by H. Dietz
*/


#include <stdio.h>
#include <malloc.h>


typedef struct {
	int	xdim, ydim;	/* image dimensions */
	int	maxval;		/* maximum value */
	void	*data;		/* data */
	int	fd;		/* file descriptor */
	void	*map;		/* map map address */
	int	fsize;		/* mapped file size */
} info_P6;


extern info_P6
map_P6(register char *filename);

extern void
unmap_P6(info_P6 info);

extern int
write_P6(register char *filename,
register char *comment,
register int xdim,
register int ydim,
register int maxval,
register unsigned char *data);


main(register int argc,
register char **argv)
{
	register unsigned char *buf, *p, *q;
	register int i, j, k;

	/* Process command line... */
	if (argc != 3) {
		fprintf(stderr,
			"Usage: %s input_ppm output_ppm\n",
			argv[0]);
		exit(1);
	}

	/* Do the I/O */
	{
		info_P6 info;

		/* Read the input */
		info = map_P6(argv[1]);
		if (info.fd < 0) {
			fprintf(stderr,
				"%s: could not map P6 PPM file %s\n",
				argv[0],
				argv[1]);
			exit(2);
		}

		buf = malloc(info.xdim * info.ydim * (3 * 3 * 3));
		p = buf;
		for (i=0; i<info.ydim; ++i) {
			for (k=0; k<3; ++k) {
				q = (info.data + i * (3 * info.xdim));
				for (j=0; j<info.xdim; ++j) {
					*(p++) = *(q++);
					*(p++) = 0;
					*(p++) = 0;
					*(p++) = 0;
					*(p++) = *(q++);
					*(p++) = 0;
					*(p++) = 0;
					*(p++) = 0;
					*(p++) = *(q++);
				}
			}
		}
			

		/* Write the output */
		if (write_P6(argv[2],
			     "just a test",
			     info.xdim * 3,
			     info.ydim * 3,
			     info.maxval,
			     buf)) {
			fprintf(stderr,
				"%s: could not write %s\n",
				argv[0],
				argv[2]);
			exit(2);
		}
	}

	/* All's well, let's end well */
	exit(0);
}


#include <fcntl.h>
#include <sys/mman.h>


info_P6
map_P6(register char *filename)
{
	/* The following is a fast and sloppy way to
	   read a color raw PPM (P6) image file
	*/
	register int fd;
	register int fsize;
	register unsigned char *map;
	register unsigned char *p;
	info_P6 info;

	/* First, open the file... */
	if ((fd = open(filename, O_RDONLY)) < 0) {
		info.xdim = 0;
		info.ydim = 0;
		info.maxval = 0;
		info.data = 0;
		info.fd = -1;
		info.map = 0;
		info.fsize = 0;
		return(info);
	}

	/* Read size and map the whole file... */
	fsize = lseek(fd, ((off_t) 0), SEEK_END);
	map = ((unsigned char *)
	       mmap(0,		/* Put it anywhere */
		    fsize,	/* Map the whole file */
		    PROT_READ,	/* Read only */
		    MAP_SHARED,	/* Not just for me */
		    fd,		/* The file */
		    0));	/* Right from the start */
	if (((int) map) == -1) {
		info.xdim = 0;
		info.ydim = 0;
		info.maxval = 0;
		info.data = 0;
		info.fd = -2;
		info.map = 0;
		info.fsize = 0;
		return(info);
	}

	/* File should now be mapped; read magic value */
	p = map;
	if (*(p++) != 'P') goto ppm_exit;
	if (*(p++) != '6') goto ppm_exit;

#define	Eat_Space \
	while ((*p == ' ') || \
	       (*p == '\t') || \
	       (*p == '\n') || \
	       (*p == '\r') || \
	       (*p == '#')) { \
		if (*p == '#') while (*(++p) != '\n') ; \
		++p; \
	}

	Eat_Space;		/* Eat white space and comments */

#define	Get_Number(n) \
	{ \
		register int charval = *p; \
 \
		if ((charval < '0') || (charval > '9')) goto ppm_exit; \
 \
		n = (charval - '0'); \
		charval = *(++p); \
		while ((charval >= '0') && (charval <= '9')) { \
			n *= 10; \
			n += (charval - '0'); \
			charval = *(++p); \
		} \
	}

	Get_Number(info.xdim);		/* Get image width */

	Eat_Space;			/* Eat white space and comments */
	Get_Number(info.ydim);		/* Get image width */

	Eat_Space;			/* Eat white space and comments */
	Get_Number(info.maxval);	/* Get image max value */

	/* Should be 8-bit binary after one whitespace char... */
	if (info.maxval > 255) {
ppm_exit:
		close(fd);
		munmap(map, fsize);
		info.xdim = 0;
		info.ydim = 0;
		info.maxval = 0;
		info.data = 0;
		info.fd = -3;
		info.map = 0;
		info.fsize = 0;
		return(info);
	}
	if ((*p != ' ') &&
	    (*p != '\t') &&
	    (*p != '\n') &&
	    (*p != '\r')) goto ppm_exit;

	/* Here we are... next byte begins the 24-bit data */
	info.data = (p + 1);
	info.fd = fd;
	info.map = map;
	info.fsize = fsize;
	return(info);
}

#undef	Eat_Space
#undef	Get_Number


void
unmap_P6(info_P6 info)
{
	if (info.fd >= 0) {
		close(info.fd);
		munmap(info.map, info.fsize);
	}
}


int
write_P6(register char *filename,
register char *comment,
register int xdim,
register int ydim,
register int maxval,
register unsigned char *data)
{
	register int fd;
	register int len;
	char buf[512];

	/* First, open the file... */
	fd = open(filename, (O_WRONLY | O_CREAT), 0666);
	if (fd < 0) {
		return(1);
	}

	/* Then write the header... */
	sprintf(&(buf[0]),
		"P6\n#%s\n%d %d\n%d\n",
		(comment ?
		 comment :
		 filename),
		xdim,
		ydim,
		maxval);
	len = strlen(&(buf[0]));
	if (write(fd, &(buf[0]), len) != len) {
		close(fd);
		return(2);
	}

	/* Then write the data... */
	len = (xdim * ydim * 3);
	if (write(fd, data, len) != len) {
		close(fd);
		return(3);
	}

	/* All looks good */
	close(fd);
	return(0);
}
