/* fromdos.c  -  CRLF->NL / NL->CRLF conversion tool */

/* Written 1992,1993 by Werner Almesberger <almesber@bernina.ethz.ch> */

/*
 * Usage: fromdos [file...]
 *        todos [file...] 
 */


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>


#define BUFFER 8192


typedef struct {
    char *latin,*pc;
} TRANS;


static TRANS table[] = {
    { "#196","#142" },	/* Ae */
    { "#214","#153" },	/* Oe */
    { "#220","#134" },	/* Ue */
    { "#228","#132" },	/* ae */
    { "#246","#148" },	/* oe */
    { "#246","#148" },	/* alternate oe  */
    { "#252","#129" },	/* ue */
    { "#252","#129" },	/* alternate ue */
    { "#223","#225" },	/* sz */
    { "#192","A" },	/* A` */
    { "#193","A" },	/* A' */
    { "#194","A" },	/* A^ */
    { "#200","E" },	/* E` */
    { "#201","#144" },	/* E' */
    { "#202","E" },	/* E^ */
    { "#203","E" },	/* E: */
    { "#224","#133" },	/* a` */
    { "#225","#160" },	/* a' */
    { "#226","#131" },	/* a^ */
    { "#232","#138" },	/* e` */
    { "#233","#130" },	/* e' */
    { "#234","#136" },	/* e^ */
    { "#199","#128" },	/* C cedille */
    { "#231","#135" },	/* c cedille */
    { "#235","#137" },	/* e: */
    { "#238","#140" },	/* i^ */
    { "#238","#140" },	/* alternate i^ */
    { "#244","#147" },	/* o^ */
    { "#251","#150" },	/* u^ */
    { "#197","#143" },	/* Aa */
    { "#229","#134" },	/* aa */
    { "#209","#165" },	/* N~ */
    { "#241","#164" },	/* n~ */
    { "#204","I" },	/* I` */
    { "#205","I" },	/* I' */
    { "#210","O" },	/* O` */
    { "#211","O" },	/* O' */
    { "#217","U" },	/* U` */
    { "#218","U" },	/* U' */
    { "#236","#141" },	/* i` */
    { "#237","#161" },	/* i' */
    { "#242","#149" },	/* o` */
    { "#243","#162" },	/* o' */
    { "#249","#151" },	/* u` */
    { "#250","#163" },	/* u' */
    { "#255","#152" },	/* y: */
    { NULL,NULL }};

static unsigned char cvt[256];
static int fromdos;
static int latin = 0;


static unsigned char cvt_entry(char *entry)
{
    unsigned char value;

    if (*entry != '#') return (unsigned char) *entry;
    value = 0;
    while (*++entry) value = value*10+*entry-'0';
    return value;
}


static void build_table(int mode) /* mode: 0 = none, 1 = lat->pc, 2 = pc->lat */
{
    TRANS *walk;
    int n;
    unsigned char last,from,to;

    for (n = 0; n < 256; n++) cvt[n] = n;
    if (!mode) return;
    last = 0;
    for (walk = table; walk->latin; walk++) {
	from = cvt_entry(mode == 1 ? walk->latin : walk->pc);
	to = cvt_entry(mode == 1 ? walk->pc : walk->latin);
	if (from != last && from >= 128) {
	    cvt[from] = to;
	    last = from;
	}
    }
}


static int to_dos(int in,int out,char *in_name,char *out_name)
{
    char in_buf[BUFFER],out_buf[BUFFER*2];
    char *from,*to;
    int size;

    build_table(latin ? 1 : 0);
    while ((size = read(in,in_buf,BUFFER)) > 0) {
	to = out_buf;
	for (from = in_buf; size; size--) {
	    if (*from == '\n') *to++ = '\r';
	    *to++ = (char) cvt[(unsigned char) *from++];
	}
	if (write(out,out_buf,to-out_buf) != to-out_buf) {
	    perror(out_name);
	    return 1;
	}
    }
    if (!size) return 0;
    perror(in_name);
    return 1;
}


static int from_dos(int in,int out,char *in_name,char *out_name)
{
    char in_buf[BUFFER],out_buf[BUFFER];
    char *from,*to;
    int size,eof;

    build_table(latin ? 2 : 0);
    eof = 0;
    while (!eof && (size = read(in,in_buf,BUFFER)) > 0) {
	to = out_buf;
	for (from = in_buf; size--; from++) {
	    if (*from == 26) {
		eof = 1;
		break;
	    }
	    if (*from != '\r') *to++ = (char) cvt[(unsigned char) *from];
	}
	if (to != out_buf)
	    if (write(out,out_buf,to-out_buf) != to-out_buf) {
		perror(out_name);
		return 1;
	    }
    }
    if (size >= 0) return 0;
    perror(in_name);
    return 1;
}


static int convert(int in,int out,char *in_name,char *out_name)
{
    if (fromdos) return from_dos(in,out,in_name,out_name);
    return to_dos(in,out,in_name,out_name);
}


int main(int argc,char **argv)
{
    char temp[PATH_MAX+1];
    char *this;
    int in,out,test;

    if (this = strrchr(*argv,'/')) this++;
    else this = *argv;
    fromdos = strcmp(this,"todos");
    if (argc > 1 && !strcmp(argv[1],"-l")) {
	latin = 1;
	argc--;
	argv++;
    }
    if (argc == 1) return convert(0,1,"stdin","stdout");
    while (--argc) {
	if ((in = open(*++argv,O_RDONLY)) < 0) {
	    perror(*argv);
	    exit(1);
	}
	strcpy(temp,*argv);
	*temp = '~';
	if ((test = open(temp,O_RDONLY)) >= 0) {
	    (void) close(test);
	    fprintf(stderr,"Scratch file %s exists.\n",temp);
	    exit(1);
	}
	if ((out = creat(temp,0666)) < 0) {
	    perror(temp);
	    exit(1);
	}
	if (convert(in,out,*argv,temp)) {
	    (void) remove(temp);
	    exit(1);
	}
	(void) close(in);
	if (close(out) < 0) {
	    perror(temp);
	    (void) remove(temp);
	    exit(1);
	}
	(void) remove(*argv);
	if (rename(temp,*argv) < 0) {
	    perror("rename");
	    (void) remove(temp);
	    exit(1);
	}
    }
    return 0;
}
