/*
 *
 *  This file is part of
 *	MakeIndex - A formatter and format independent index processor
 *
 *  Copyright (C) 1998-2011 by the TeX Live project.
 *  Copyright (C) 1989 by Chen & Harrison International Systems, Inc.
 *  Copyright (C) 1988 by Olivetti Research Center
 *  Copyright (C) 1987 by Regents of the University of California
 *
 *  Author:
 *	Pehong Chen
 *	Chen & Harrison International Systems, Inc.
 *	Palo Alto, California
 *	USA
 *
 *  Contributors:
 *	Please refer to the CONTRIB file that comes with this release
 *	for a list of people who have contributed to this and/or previous
 *	release(s) of MakeIndex.
 *
 *  All rights reserved by the copyright holders.  See the copyright
 *  notice distributed with this software for a complete description of
 *  the conditions under which it is made available.
 *
 */

#include    "mkind.h"
#include    "scanst.h"

static int sty_lc = 0;                 /* line count */
static int sty_tc = 0;                 /* total count */
static int sty_ec = 0;                 /* error count */

char    idx_keyword[ARRAY_MAX] = IDX_KEYWORD;
char    idx_aopen = IDX_AOPEN;
char    idx_aclose = IDX_ACLOSE;
char    idx_level = IDX_LEVEL;
char    idx_ropen = IDX_ROPEN;
char    idx_rclose = IDX_RCLOSE;
char    idx_quote = IDX_QUOTE;
char    idx_actual = IDX_ACTUAL;
char    idx_encap = IDX_ENCAP;
char    idx_escape = IDX_ESCAPE;

char    preamble[ARRAY_MAX] = PREAMBLE_DEF;
char    postamble[ARRAY_MAX] = POSTAMBLE_DEF;
int     prelen = PREAMBLE_LEN;
int     postlen = POSTAMBLE_LEN;

char    setpage_open[ARRAY_MAX] = SETPAGEOPEN_DEF;
char    setpage_close[ARRAY_MAX] = SETPAGECLOSE_DEF;
int     setpagelen = SETPAGE_LEN;

char    group_skip[ARRAY_MAX] = GROUPSKIP_DEF;
int     skiplen = GROUPSKIP_LEN;

int     headings_flag = HEADINGSFLAG_DEF;
char    heading_pre[ARRAY_MAX] = HEADINGPRE_DEF;
char    heading_suf[ARRAY_MAX] = HEADINGSUF_DEF;
int     headprelen = HEADINGPRE_LEN;
int     headsuflen = HEADINGSUF_LEN;

char    symhead_pos[ARRAY_MAX] = SYMHEADPOS_DEF;
char    symhead_neg[ARRAY_MAX] = SYMHEADNEG_DEF;

char    numhead_pos[ARRAY_MAX] = NUMHEADPOS_DEF;
char    numhead_neg[ARRAY_MAX] = NUMHEADNEG_DEF;

char    item_r[FIELD_MAX][ARRAY_MAX] = {ITEM0_DEF, ITEM1_DEF, ITEM2_DEF};
char    item_u[FIELD_MAX][ARRAY_MAX] = {"", ITEM1_DEF, ITEM2_DEF};
char    item_x[FIELD_MAX][ARRAY_MAX] = {"", ITEM1_DEF, ITEM2_DEF};

int     ilen_r[FIELD_MAX] = {ITEM_LEN, ITEM_LEN, ITEM_LEN};
int     ilen_u[FIELD_MAX] = {0, ITEM_LEN, ITEM_LEN};
int     ilen_x[FIELD_MAX] = {0, ITEM_LEN, ITEM_LEN};

char    delim_p[FIELD_MAX][ARRAY_MAX] = {DELIM_DEF, DELIM_DEF, DELIM_DEF};
char    delim_n[ARRAY_MAX] = DELIM_DEF; /* page number separator */
char    delim_r[ARRAY_MAX] = DELIMR_DEF;/* page range designator */
char    delim_t[ARRAY_MAX] = DELIMT_DEF;/* page list terminating delimiter */

char    suffix_2p[ARRAY_MAX] = "";      /* suffix for two page ranges */
char    suffix_3p[ARRAY_MAX] = "";      /* suffix for three page ranges */
char    suffix_mp[ARRAY_MAX] = "";      /* suffix for multiple page ranges */

char    encap_p[ARRAY_MAX] = ENCAP0_DEF;/* encapsulator prefix */
char    encap_i[ARRAY_MAX] = ENCAP1_DEF;/* encapsulator infix */
char    encap_s[ARRAY_MAX] = ENCAP2_DEF;/* encapsulator postfix */

int     linemax = LINE_MAX;

int     indent_length = INDENTLEN_DEF;
char    indent_space[ARRAY_MAX] = INDENTSPC_DEF;

char    page_comp[ARRAY_MAX] = COMPOSITOR_DEF;
int     page_offset[PAGETYPE_MAX] = {
0,
ROMAN_LOWER_OFFSET,
ROMAN_LOWER_OFFSET + ARABIC_OFFSET,
ROMAN_LOWER_OFFSET + ARABIC_OFFSET + ALPHA_LOWER_OFFSET,
ROMAN_LOWER_OFFSET + ARABIC_OFFSET + ALPHA_LOWER_OFFSET + ROMAN_UPPER_OFFSET
};
char    page_prec[ARRAY_MAX] = PRECEDENCE_DEF;

static int put_dot;

static	int	count_lfd (char *str);
static	int	next_nonblank (void);
static	int	process_precedence (void);
static	int	scan_char (char *c);
static	int	scan_spec (char *spec);
static	int	scan_string (char *str);

void
scan_sty(void)
{
    char    spec[STRING_MAX];
    int     tmp;

    MESSAGE1("Scanning style file %s", sty_fn);
    while (scan_spec(spec)) {
	sty_tc++;
	put_dot = TRUE;

	/* output pre- and post-ambles */
	if (STREQ(spec, PREAMBLE)) {
	    (void) scan_string(preamble);
	    prelen = count_lfd(preamble);
	} else if (STREQ(spec, POSTAMBLE)) {
	    (void) scan_string(postamble);
	    postlen = count_lfd(postamble);
	} else if (STREQ(spec, GROUP_SKIP)) {
	    (void) scan_string(group_skip);
	    skiplen = count_lfd(group_skip);
	} else if (STREQ(spec, HEADINGS_FLAG)) {
	    SCAN_NO(&headings_flag);
	} else if (STREQ(spec, HEADING_PRE)) {
	    (void) scan_string(heading_pre);
	    headprelen = count_lfd(heading_pre);
	} else if (STREQ(spec, HEADING_SUF)) {
	    (void) scan_string(heading_suf);
	    headsuflen = count_lfd(heading_suf);
	} else if (STREQ(spec, SYMHEAD_POS)) {
	    (void) scan_string(symhead_pos);
	} else if (STREQ(spec, SYMHEAD_NEG)) {
	    (void) scan_string(symhead_neg);
	} else if (STREQ(spec, NUMHEAD_POS)) {
	    (void) scan_string(numhead_pos);
	} else if (STREQ(spec, NUMHEAD_NEG)) {
	    (void) scan_string(numhead_neg);
	} else if (STREQ(spec, SETPAGEOPEN)) {
	    (void) scan_string(setpage_open);
	    setpagelen = count_lfd(setpage_open);
	} else if (STREQ(spec, SETPAGECLOSE)) {
	    (void) scan_string(setpage_close);
	    setpagelen = count_lfd(setpage_close);
	    /* output index item commands */
	} else if (STREQ(spec, ITEM_0)) {
	    (void) scan_string(item_r[0]);
	    ilen_r[0] = count_lfd(item_r[0]);
	} else if (STREQ(spec, ITEM_1)) {
	    (void) scan_string(item_r[1]);
	    ilen_r[1] = count_lfd(item_r[1]);
	} else if (STREQ(spec, ITEM_2)) {
	    (void) scan_string(item_r[2]);
	    ilen_r[2] = count_lfd(item_r[2]);
	} else if (STREQ(spec, ITEM_01)) {
	    (void) scan_string(item_u[1]);
	    ilen_u[1] = count_lfd(item_u[1]);
	} else if (STREQ(spec, ITEM_12)) {
	    (void) scan_string(item_u[2]);
	    ilen_u[2] = count_lfd(item_u[2]);
	} else if (STREQ(spec, ITEM_x1)) {
	    (void) scan_string(item_x[1]);
	    ilen_x[1] = count_lfd(item_x[1]);
	} else if (STREQ(spec, ITEM_x2)) {
	    (void) scan_string(item_x[2]);
	    ilen_x[2] = count_lfd(item_x[2]);
	    /* output encapsulators */
	} else if (STREQ(spec, ENCAP_0))
	    (void) scan_string(encap_p);
	else if (STREQ(spec, ENCAP_1))
	    (void) scan_string(encap_i);
	else if (STREQ(spec, ENCAP_2))
	    (void) scan_string(encap_s);
	/* output delimiters */
	else if (STREQ(spec, DELIM_0))
	    (void) scan_string(delim_p[0]);
	else if (STREQ(spec, DELIM_1))
	    (void) scan_string(delim_p[1]);
	else if (STREQ(spec, DELIM_2))
	    (void) scan_string(delim_p[2]);
	else if (STREQ(spec, DELIM_N))
	    (void) scan_string(delim_n);
	else if (STREQ(spec, DELIM_R))
	    (void) scan_string(delim_r);
	else if (STREQ(spec, DELIM_T))
	    (void) scan_string(delim_t);
	else if (STREQ(spec, SUFFIX_2P))
	    (void) scan_string(suffix_2p);
	else if (STREQ(spec, SUFFIX_3P))
	    (void) scan_string(suffix_3p);
	else if (STREQ(spec, SUFFIX_MP))
	    (void) scan_string(suffix_mp);
	/* output line width */
	else if (STREQ(spec, LINEMAX)) {
	    SCAN_NO(&tmp);
	    if (tmp > 0)
		linemax = tmp;
	    else
		STY_ERROR2("%s must be positive (got %d)",
			   LINEMAX, tmp);
	    /* output line indentation length */
	} else if (STREQ(spec, INDENT_LENGTH)) {
	    SCAN_NO(&tmp);
	    if (tmp >= 0)
		indent_length = tmp;
	    else
		STY_ERROR2("%s must be nonnegative (got %d)",
			   INDENT_LENGTH, tmp);
	    /* output line indentation */
	} else if (STREQ(spec, INDENT_SPACE)) {
	    (void) scan_string(indent_space);
	    /* composite page delimiter */
	} else if (STREQ(spec, COMPOSITOR)) {
	    (void) scan_string(page_comp);
	    /* page precedence */
	} else if (STREQ(spec, PRECEDENCE)) {
	    (void) scan_string(page_prec);
	    /* index input format */
	} else if (STREQ(spec, KEYWORD))
	    (void) scan_string(idx_keyword);
	else if (STREQ(spec, AOPEN))
	    (void) scan_char(&idx_aopen);
	else if (STREQ(spec, ACLOSE))
	    (void) scan_char(&idx_aclose);
	else if (STREQ(spec, LEVEL))
	    (void) scan_char(&idx_level);
	else if (STREQ(spec, ROPEN))
	    (void) scan_char(&idx_ropen);
	else if (STREQ(spec, RCLOSE))
	    (void) scan_char(&idx_rclose);
	else if (STREQ(spec, QUOTE))
	    (void) scan_char(&idx_quote);
	else if (STREQ(spec, ACTUAL))
	    (void) scan_char(&idx_actual);
	else if (STREQ(spec, ENCAP))
	    (void) scan_char(&idx_encap);
	else if (STREQ(spec, ESCAPE))
	    (void) scan_char(&idx_escape);
	else {
	    (void) next_nonblank();
	    STY_SKIPLINE;
	    STY_ERROR1("Unknown specifier %s.\n", spec);
	    put_dot = FALSE;
	}
	if (put_dot) {
	    STY_DOT;
	}
    }
    /* page precedence */
    (void) process_precedence();

    /* check if quote and escape are distinct */
    if (idx_quote == idx_escape) {
	STY_ERROR1(
	       "Quote and escape symbols must be distinct (both `%c' now).\n",
	       idx_quote);
	idx_quote = IDX_QUOTE;
	idx_escape = IDX_ESCAPE;
    }
    DONE(sty_tc - sty_ec, "attributes redefined", sty_ec, "ignored");
    CLOSE(sty_fp);
}

static int
scan_spec(char spec[])
{
    int     i = 0;
    int     c;

    while (TRUE)
	if ((c = next_nonblank()) == -1)
	    return (FALSE);
	else if (c == COMMENT) {
	    STY_SKIPLINE;
	} else
	    break;

    spec[0] = TOLOWER(c);
    while ((i++ < STRING_MAX) && ((c = GET_CHAR(sty_fp)) != SPC) &&
	   (c != TAB) && (c != LFD) && (c != EOF))
	spec[i] = TOLOWER(c);
    if (i < STRING_MAX) {
	spec[i] = NUL;
	if (c == EOF) {
	    STY_ERROR1(
		      "No attribute for specifier %s (premature EOF)\n",
		      spec);
	    return (-1);
	}
	if (c == LFD)
	    sty_lc++;
	return (TRUE);
    } else {
	STY_ERROR2("Specifier %s too long (max %d).\n",
		   spec, STRING_MAX);
	return (FALSE);
    }
}


static int
next_nonblank(void)
{
    int     c;

    while (TRUE) {
	switch (c = GET_CHAR(sty_fp)) {
	case EOF:
	    return (-1);

	case LFD:
	    sty_lc++;
	case SPC:
	case TAB:
	    break;
	default:
	    return (c);
	}
    }
}


static int
scan_string(char str[])
{
    char    clone[ARRAY_MAX];
    int     i = 0;
    int     c;

    switch (c = next_nonblank()) {
    case STR_DELIM:
	while (TRUE)
	    switch (c = GET_CHAR(sty_fp)) {
	    case EOF:
		STY_ERROR1("No closing delimiter in %s.\n",
			  clone);
		return (FALSE);
	    case STR_DELIM:
		clone[i] = NUL;
		strcpy(str, clone);
		return (TRUE);
	    case BSH:
		switch (c = GET_CHAR(sty_fp)) {
		case 't':
		    clone[i++] = TAB;
		    break;
		case 'n':
		    clone[i++] = LFD;
		    break;

		default:
		    clone[i++] = (char) c;
		    break;
		}
		break;
	    default:
		if (c == LFD)
		    sty_lc++;
		if (i < ARRAY_MAX)
		    clone[i++] = (char) c;
		else {
		    STY_SKIPLINE;
		    STY_ERROR2(
			       "Attribute string %s too long (max %d).\n",
			       clone, ARRAY_MAX);
		    return (FALSE);
		}
		break;
	    }
	break;
    case COMMENT:
	STY_SKIPLINE;
	break;
    default:
	STY_SKIPLINE;
	STY_ERROR("No opening delimiter.\n");
	return (FALSE);
    }
    return (TRUE);                     /* function value no longer used */
}


static int
scan_char(char *c)
{
    int     clone;

    switch (clone = next_nonblank()) {
    case CHR_DELIM:
	switch (clone = GET_CHAR(sty_fp)) {
	case CHR_DELIM:
	    STY_SKIPLINE;
	    STY_ERROR("Premature closing delimiter.\n");
	    return (FALSE);
	case LFD:
	    sty_lc++;
	case EOF:
	    STY_ERROR("No character (premature EOF).\n");
	    return (FALSE);
	case BSH:
	    clone = GET_CHAR(sty_fp);
	default:
	    if (GET_CHAR(sty_fp) == CHR_DELIM) {
		*c = (char) clone;
		return (TRUE);
	    } else {
		STY_ERROR("No closing delimiter or too many letters.\n");
		return (FALSE);
	    }
	}
	/* break; */				/* NOT REACHED */
    case COMMENT:
	STY_SKIPLINE;
	break;
    default:
	STY_SKIPLINE;
	STY_ERROR("No opening delimiter.\n");
	return (FALSE);
    }
    return (TRUE);                     /* function value no longer used */
}


static int
count_lfd(char *str)
{
    int     i = 0;
    int     n = 0;

    while (str[i] != NUL) {
	if (str[i] == LFD)
	    n++;
	i++;
    }
    return (n);
}


static int
process_precedence(void)
{
    int     order[PAGETYPE_MAX];
    int     type[PAGETYPE_MAX];
    int     i = 0;
    int     last;
    int     roml = FALSE;
    int     romu = FALSE;
    int     arab = FALSE;
    int     alpl = FALSE;
    int     alpu = FALSE;

    /* check for llegal specifiers first */
    while ((i < PAGETYPE_MAX) && (page_prec[i] != NUL)) {
	switch (page_prec[i]) {
	case ROMAN_LOWER:
	    if (roml) {
		MULTIPLE(ROMAN_LOWER);
	    } else
		roml = TRUE;
	    break;
	case ROMAN_UPPER:
	    if (romu) {
		MULTIPLE(ROMAN_UPPER);
	    } else
		romu = TRUE;
	    break;
	case ARABIC:
	    if (arab) {
		MULTIPLE(ARABIC);
	    } else
		arab = TRUE;
	    break;
	case ALPHA_LOWER:
	    if (alpl) {
		MULTIPLE(ALPHA_UPPER);
	    } else
		alpl = TRUE;
	    break;
	case ALPHA_UPPER:
	    if (alpu) {
		MULTIPLE(ALPHA_UPPER);
	    } else
		alpu = TRUE;
	    break;
	default:
	    STY_SKIPLINE;
	    STY_ERROR1("Unknow type `%c' in page precedence specification.\n",
		      page_prec[i]);
	    return (FALSE);
	}
	i++;
    }
    if (page_prec[i] != NUL) {
	STY_SKIPLINE;
	STY_ERROR("Page precedence specification string too long.\n");
	return (FALSE);
    }
    last = i;
    switch (page_prec[0]) {
    case ROMAN_LOWER:
	order[0] = ROMAN_LOWER_OFFSET;
	type[0] = ROML;
	break;
    case ROMAN_UPPER:
	order[0] = ROMAN_UPPER_OFFSET;
	type[0] = ROMU;
	break;
    case ARABIC:
	order[0] = ARABIC_OFFSET;
	type[0] = ARAB;
	break;
    case ALPHA_LOWER:
	order[0] = ALPHA_LOWER_OFFSET;
	type[0] = ALPL;
	break;
    case ALPHA_UPPER:
	order[0] = ALPHA_LOWER_OFFSET;
	type[0] = ALPU;
	break;
    }

    i = 1;
    while (i < last) {
	switch (page_prec[i]) {
	case ROMAN_LOWER:
	    order[i] = order[i - 1] + ROMAN_LOWER_OFFSET;
	    type[i] = ROML;
	    break;
	case ROMAN_UPPER:
	    order[i] = order[i - 1] + ROMAN_UPPER_OFFSET;
	    type[i] = ROMU;
	    break;
	case ARABIC:
	    order[i] = order[i - 1] + ARABIC_OFFSET;
	    type[i] = ARAB;
	    break;
	case ALPHA_LOWER:
	    order[i] = order[i - 1] + ALPHA_LOWER_OFFSET;
	    type[i] = ALPL;
	    break;
	case ALPHA_UPPER:
	    order[i] = order[i - 1] + ALPHA_LOWER_OFFSET;
	    type[i] = ALPU;
	    break;
	}
	i++;
    }

    for (i = 0; i < PAGETYPE_MAX; i++) {
	page_offset[i] = -1;
    }
    page_offset[type[0]] = 0;
    for (i = 1; i < last; i++) {
	page_offset[type[i]] = order[i - 1];
    }
    for (i = 0; i < PAGETYPE_MAX; i++) {
	if (page_offset[i] == -1) {
	    switch (type[last - 1]) {
	    case ROML:
		order[last] = order[last - 1] + ROMAN_LOWER_OFFSET;
		break;
	    case ROMU:
		order[last] = order[last - 1] + ROMAN_UPPER_OFFSET;
		break;
	    case ARAB:
		order[last] = order[last - 1] + ARABIC_OFFSET;
		break;
	    case ALPL:
		order[last] = order[last - 1] + ALPHA_LOWER_OFFSET;
		break;
	    case ALPU:
		order[last] = order[last - 1] + ALPHA_UPPER_OFFSET;
		break;
	    }
	    type[last] = i;
	    page_offset[i] = order[last];
	    last++;
	}
    }
    return (TRUE);                     /* function value no longer used */
}
