/*
 * setfont.c - Eugene Crosser & aeb
 *
 * Version 0.85
 *
 */
#include <stdio.h>
#include <memory.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/kd.h>
#include "paths.h"

/* search for the font in these directories (with trailing /) */
char *dirpath[] = { "", DATADIR "/" FONTDIR "/", 0 };
char *suffixes[] = { "", ".psf", ".cp", ".fnt", 0 };
extern char pathname[];
extern FILE *findfile(char *fnam);
static void position_codepage(int iunit, FILE *fpi);

char *progname;

void
usage(void)
{
        fprintf(stderr, "\
Usage:  %s [-o oldfont] [-fontsize] [newfont]
If no -o is given, and no newfont, then the font \"default\" is loaded.
Options:
 -o filename	Write current font to file before loading a new one.
 -8, -14, -16	Choose the right font from a codepage that contains three fonts
                (or choose default font, e.g. \"default8x16\").
", progname);
	exit(1);
}

int
main(int argc, char *argv[])
{
	FILE *fpi, *fpo;
	char defname[20];
	char buf[8192];
	char *ifil, *ofil;
	int fd, i, iunit, unit, hdr, size;
	struct stat stbuf;

	progname = argv[0];

	/* we do not want to read, but only need some fd */
	if ((fd = open("/dev/console", 0)) < 0)
	    fd = 0;

	ifil = ofil = 0;
	iunit = 0;

	for (i = 1; i < argc; i++) {
	    if (!strcmp(argv[i], "-o")) {
		if (++i == argc || ofil)
		  usage();
		ofil = argv[i];
	    } else if(argv[i][0] == '-') {
		iunit = atoi(argv[i]+1);
		if(iunit <= 0 || iunit > 32)
		  usage();
	    } else {
		if (ifil)
		  usage();
		ifil = argv[i];
	    }
	}

	if (!ifil && !ofil) /* reset to some default */
	    ifil = "";

	if (ofil) {
	        if((fpo = fopen(ofil, "w")) == NULL) {
		    perror(ofil);
		    exit(1);
		}
		i = ioctl(fd,GIO_FONT,buf);
		if (i) {
		    perror("GIO_FONT ioctl error");
		    return(i);
		}

		/* save as efficiently as possible */
		for (unit = 32; unit > 0; unit--)
		    for (i = 0; i < 256; i++)
		      if (buf[32*i+unit-1])
			goto nonzero;
	      nonzero:
		if (unit == 0)
		    fprintf(stderr, "Found nothing to save\n");
		else {
		    for (i = 0; i < 256; i++)
		      if (fwrite(buf+(32*i), unit, 1, fpo) != 1) {
			  perror("Cannot write font file");
			  exit(1);
		      }
		    printf("Saved 8x%d font file on %s\n", unit, ofil);
		}
		fclose(fpo);
	}

	if (ifil) {
	    if (!*ifil) {
		/* try to find some default file */

		if (iunit < 0 || iunit > 32)
		  iunit = 0;
		if (iunit == 0) {
		    if ((fpi = findfile(ifil = "default")) == NULL &&
			(fpi = findfile(ifil = "default8x16")) == NULL &&
			(fpi = findfile(ifil = "default8x14")) == NULL &&
			(fpi = findfile(ifil = "default8x8")) == NULL) {
			fprintf(stderr, "Cannot find default font\n");
			exit(1);
		    }
		} else {
		    sprintf(defname, "default8x%d", iunit);
		    if ((fpi = findfile(ifil = defname)) == NULL) {
			fprintf(stderr, "Cannot find %s font\n", ifil);
			exit(1);
		    }
		}
	    } else {
		if ((fpi = findfile(ifil)) == NULL) {
		    fprintf(stderr, "Cannot open font file %s\n", ifil);
		    exit(1);
		}
	    }

	    if (stat(pathname, &stbuf)) {
		perror(pathname);
		exit(1);
	    }
	    size = stbuf.st_size;

	    if (size == 9780) {
		position_codepage(iunit, fpi);
		unit = iunit;
		goto readit;
	    }

	    hdr = (size & 0377);
	    unit = (size - hdr)/256;

	    if (hdr == 4) {
		char psfhdr[4];
		if (fread(psfhdr, 4, 1, fpi) != 1) {
		    perror("Error reading header input font");
		    exit(1);
		}
		/* note: this depends on endianness */
		if (psfhdr[1] != 0x04 || psfhdr[0] != 0x36) {
		    fprintf(stderr, "Unrecognized font format\n");
		    exit(1);
		}
		if (psfhdr[2] != 0) {
		    fprintf(stderr, "Unsupported psf file mode\n");
		    exit(1);
		}
		if(size != hdr + 256*psfhdr[3]) {
		    fprintf(stderr, "Input file: bad length\n");
		    exit(1);
		}
	    }

	    if ((hdr != 0 && hdr != 4) || unit < 1 || unit > 32) {
		fprintf(stderr, "Bad input file size\n");
		exit(1);
	    }

	  readit:
	    memset(buf,0,sizeof(buf));

	    for (i = 0; i < 256; i++)
	      if (fread(buf+(32*i), unit, 1, fpi) != 1) {
		  perror("Cannot read font from file");
		  exit(1);
	      }
	    fclose(fpi);

	    fprintf(stderr,"Loading 8x%d font from file %s\n", unit, pathname);

	    i = ioctl(fd,PIO_FONT,buf);
	    if (i) {
		perror("PIO_FONT ioctl error");
		exit(i);
	    }
	}

	return(0);
}

static void
position_codepage(int iunit, FILE *fpi) {
        int offset;

	/* code page: first 40 bytes, then 8x16 font,
	   then 6 bytes, then 8x14 font,
	   then 6 bytes, then 8x8 font */

	if (!iunit) {
	    fprintf(stderr, "\
This file contains 3 fonts: 8x8, 8x14 and 8x16. Please indicate
using an option -8 or -14 or -16 which one you want loaded.\n");
	    exit(1);
	}
	switch (iunit) {
	  case 8:
	    offset = 7732; break;
	  case 14:
	    offset = 4142; break;
	  case 16:
	    offset = 40; break;
	  default:
	    fprintf(stderr, "\
You asked for font size %d, but only 8, 14, 16 are possible here.\n",
		    iunit);
	    exit(1);
	}
	if(fseek(fpi,offset,0)) {
	    perror("seek error on input file");
	    exit(1);
	}
}
