#include <stdio.h>
#include <ctype.h>

/*
 * don't break in middle of word (which also can cause false sofit)
 * recognize P and S, do something.
 * Special combos OW, W.
 */

char *file_name;
int perek, pasuk;

main(argc, argv)
char **argv;
{
    FILE *fp;
    int arg;

    if (argc > 1)
	for (arg = 1; arg < argc; arg++) {
	    if ( (fp = fopen(argv[arg], "r")) == NULL) {
		(void) fprintf(stderr, "cannot open file %s\n", argv[arg]);
		exit();
	    }
	    file_name = argv[arg];
	    do_file(fp);
	    (void) fclose(fp);
	}
    else {
	file_name = "stdin";
	do_file(stdin);
    }
}

enum vowel {
    patah, kamatz, hirik, segol, tsere, holam, kibbuts, shurek, schwa,
    hatef_patah, hatef_kamatz, hatef_segol, no_vowel,
};

enum accent {
    sof_pasuk, segolta, zarka, pashta, pasek, yetib,
    dehi, mugrash, telisha_g, telisha_k,
    ole, geresh, garshajim, azla, illuj, shalshelet, zakef_k,
    rebia, sinnorit, pazer, karne_para, zakef_g, meteg1, mahpak,
    mereka, mereka_kepulah, tipha, munah, silluk, tebir, atnah,
    galgal, darga, meteg2, no_accent,
};

struct {
    int code;
    enum accent accent;
} accents[] = {
    { 00, sof_pasuk }, { 01, segolta }, { 02, zarka },
    { 03, pashta }, { 33, pashta }, { 05, pasek }, { 10, yetib }, { 13, dehi },
    { 11, mugrash }, { 60, ole }, { 61, geresh }, { 62, garshajim },
    { 04, telisha_k }, { 24, telisha_k }, { 14, telisha_g }, { 44, telisha_g },
    { 63, azla }, { 64, illuj }, { 65, shalshelet }, { 80, zakef_k },
    { 81, rebia }, { 82, sinnorit }, { 83, pazer }, { 84, karne_para },
    { 85, zakef_g }, { 35, meteg1 }, { 70, mahpak }, { 71, mereka },
    { 72, mereka_kepulah }, { 73, tipha }, { 74, munah }, { 75, silluk },
    { 91, tebir }, { 92, atnah }, { 93, galgal }, { 94, darga },
    { 95, meteg2 }, { -1, no_accent },
};

#define MAX_OUT 80
struct {
    short pasuk;
    unsigned char letter;
    char dagesh;
    char nekudah;
    enum vowel vowel;
    enum accent accent;
} out[MAX_OUT];
int first, o_ptr;

do_file(fp)
FILE *fp;
{
    char line[200], *p, *strchr();
    int last_space;

    perek = pasuk = 0;

    clear();

    while (fgets(line, sizeof line, fp)) {
	if (p = strchr(line, '\r'))
	    *p = '\0';
	if (p = strchr(line, '\n'))
	    *p = '\0';

	if (line[0] == '~') {
	    switch (line[1]) {
	      case 'a':
	      case 'x':
		flush();
		(void) printf("------Perek %d-----------\n", ++perek);
		set_pasuk(pasuk = 1);
		break;

	      case 'y':
		set_pasuk(++pasuk);
		break;
	    }
	    continue;
	}

	for (p = line, last_space = 0; *p; p++) {
	    switch (*p) {
	      case '*':
	      case '-':
	      case ' ':	set_letter(*p);	break;
	      case ')':	set_letter(''); break;
	      case 'B': set_letter(''); break;
	      case 'G': set_letter(''); break;
	      case 'D': set_letter(''); break;
	      case 'H': set_letter(''); break;
	      case 'W': set_letter(''); break;
	      case 'Z': set_letter(''); break;
	      case 'X': set_letter(''); break;
	      case '+': set_letter(''); break;
	      case 'Y': set_letter(''); break;
	      case 'K': set_letter(''); break;
	      case 'L': set_letter(''); break;
	      case 'M': set_letter(''); break;
	      case 'N': set_letter(''); break;
	      case 'S':
		if (last_space && p[1] == ' ')
		    set_letter('S');
		else
		    set_letter('');
		break;
	      case '(': set_letter(''); break;
	      case 'P':
		if (last_space && p[1] == ' ') {
		    set_letter('P');
		    flush();
		} else
		    set_letter('');
		break;
	      case 'C': set_letter(''); break;
	      case 'Q': set_letter(''); break;
	      case 'R': set_letter(''); break;
	      case '#': set_letter(''); break;
	      case '&': set_letter(''); break;
	      case '$': set_letter(''); break;
	      case 'T': set_letter(''); break;
	      case 'A': set_vowel(patah); break;
	      case 'F': set_vowel(kamatz); break;
	      case 'I': set_vowel(hirik); break;
	      case 'E': set_vowel(segol); break;
	      case '"': set_vowel(tsere); break;
	      case 'O': set_vowel(holam); break;
	      case 'U': set_vowel(kibbuts); break;
	      case ':':
		switch (p[1]) {
		  case 'A': set_vowel(hatef_patah); break;
		  case 'F': set_vowel(hatef_kamatz); break;
		  case 'E': set_vowel(hatef_segol); break;
		  default:
		    set_vowel(schwa); break;
		}
		break;

	      case '.': set_dagesh(); break;

	      case ',': break;	/* rafe */
	      case '/': break;	/* concordance citation */
	      case '?': flush(); break;	/* end of line */
	      case ']':		/* special code */
		switch (*++p) {
		  case '1':
/*
 * BHS has been faithful to the Leningrad Codex where there might
 * be a question of the validity of the form and we keep the same
 * form as BHS.
 */
		  case '2':
/* We have added a sop pasuq where L and BHS omit it. */
		  case '3':
/*
 * We read or understand L differently than BHS (1983 Edition).
 * Often this notation indicates a typographical error in BHS.
 */
		  case '4':
/*
 * Puncta Extraordaria -- a 52 is used to mark such marks in the
 * text when they are above the line and 53 when they are below
 * the line.
 */
		  case '5':
/* Large letter(s) */
		  case '6':
/* Small letter(s) */
		  case '7':
/* Suspended letter(s) */
		  case '8':
/* Inverted Nun     (N]8 in the text) */
		  case '9':
/*
 * BHS has abandoned L and we concur. All of these occurrences
 * are ketib/qere problems.
 */
		  case 'q':
/*
 * We have abandoned or added a ketib/qere relative to BHS.  In
 * doing this we agree with L against BHS.
 */
		  case 'a':
/*
 * Adaptations to a Qere which L and BHS, by their design, do not
 * indicate.
 */
		  case 'y':
/*
 * Yathir readings in L which we have designated as Qeres when
 * both Dothan and BHS list a Qere.
 */
		  case 'm':
/*
 * Miscellaneous notes to the text and occasions where more than
 * one bracket category applies.
 */
		    break;
		  default:
		    error("bad special code: ]%c", *p);
		}
		break;

	      default:
		if (isdigit(*p))
		    if (isdigit(p[1])) {
			int i, n;

			n = 10 * toint(p[0]) + toint(p[1]);

			if (n == 52 || n == 53)
			    set_nekudah();
			else {
			    for (i = 0; accents[i].code >= 0; i++)
				if (n == accents[i].code) {
				    set_accent(accents[i].accent);
				    break;
				}
			    if (accents[i].code < 0)
				error("bad accent: %d", n);
			}
			p++;
		    } else
			error("bad number: %c%c", p[0], p[1]);
		else
		    error("bad char: %c", *p);
	    }

	    last_space = *p == ' ';
	}
    }
    flush();
}

set_pasuk(n) {
    advance();
    out[o_ptr].pasuk = n;
}

set_letter(c) {
    advance();
    out[o_ptr].letter = c;
}

advance() {
    if (o_ptr >= MAX_OUT - 1) {
	error("line overflow");
	flush();
    }

    if ( !first)
	o_ptr++;

    first = 0;
}

set_vowel(v)
enum vowel v;
{
    out[o_ptr].vowel = v;
}

set_accent(a)
enum accent a;
{
    out[o_ptr].accent = a;
}

set_dagesh() {
    out[o_ptr].dagesh = 1;
}

set_nekudah() {
    out[o_ptr].nekudah = 1;
}

flush() {
    int o, c, last = ' ';

    if (o_ptr <= 0)
	return;

    if (out[o_ptr].letter == ' ')
	o_ptr--;
    for (; o_ptr >= 0; o_ptr--) {
	if (out[o_ptr].pasuk)
	    (void) printf(":%d ", out[o_ptr].pasuk);
	else {
	    c = out[o_ptr].letter;
	    if (strchr("", c) && (last == ' ' || last == '-'))
		c--;		/* sofit */
	    (void) putchar(c);
	    last = c;
	}
    }
    (void) putchar('\n');

    clear();
}

clear() {
    int o;

    first = 1;
    o_ptr = 0;

    for (o = 0; o < MAX_OUT; o++) {
	out[o].letter = 'X';
	out[o].pasuk = out[o].dagesh = out[o].nekudah = 0;
	out[o].vowel = no_vowel;
	out[o].accent = no_accent;
    }
}

#ifdef lint
/* VARARGS1 */ error(fmt) char *fmt; {fmt = fmt;}
#else

#include <varargs.h>

error(va_alist)
va_dcl
{
    va_list args;
    char *fmt;

    va_start(args);
    fmt = va_arg(args, char *);
    (void) fprintf(stderr, "%s(%d:%d) : ", file_name, perek, pasuk);
    (void) vfprintf(stderr, fmt, args);
    (void) fprintf(stderr, "\n");
    va_end(args);
}
#endif
