/* omfonts.c: Main routine for ofm2opl, opl2ofm, ovf2ovp, ovp2ovf.

This file is part of Omega,
which is based on the web2c distribution of TeX,

Copyright (c) 1994--2001 John Plaice and Yannis Haralambous

Omega is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

Omega is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Omega; if not, write to the Free Software Foundation, Inc.,
59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.

*/

#define OMFONTS
#define OFM2OPL
#define OPL2OFM
#define OVP2OVF
#define OVF2OVP
#include "cpascal.h"
#include "parser.h"
#include "manifests.h"
#include "omfonts.h"
#include "list_routines.h"
#include "error_routines.h"
#include "header_routines.h"
#include "font_routines.h"
#include "param_routines.h"
#include "char_routines.h"
#include "dimen_routines.h"
#include "parse_ofm.h"
#include "out_ofm.h"

#define PROG_MIN		0
#define PROG_OFM2OPL	0
#define PROG_OPL2OFM	1
#define PROG_OVF2OVP	2
#define PROG_OVP2OVF	3
#define PROG_OMFONTS	4
#define PROG_NUM		5

#define cmdline(i) (argv[i])

int verbose_option;
int char_format = CHAR_CODE_NUM;
int num_format  = NUM_CODE_HEX;
int text_format = TEXT_CODE_MIXED;

int program;

const_string name_ofm = NULL;
const_string name_opl = NULL;
const_string name_ovp = NULL;
const_string name_ovf = NULL;

FILE *file_ofm = NULL;
FILE *file_opl = NULL;
FILE *file_ovp = NULL;
FILE *file_ovf = NULL;
FILE *file_output = NULL;

unsigned char *ofm = NULL;
unsigned char *ovf = NULL;

unsigned length_ofm = 0;
unsigned length_ovf = 0;

const_string name_program;

const_string OMFONTSHELP[] = {
    "Usage: omfonts -ofm2opl [OPTION]... FILE...",
    "       omfonts -opl2ofm [OPTION]... FILE...",
    "       omfonts -ovf2ovp [OPTION]... FILE...",
    "       omfonts -ovp2ovf [OPTION]... FILE...",
    "",
    "be ofm2opl, opl2ofm, ovf2ovp, or ovp2ovf",
    NULL
};

static const_string names_program[PROG_NUM] =
    { "ofm2opl", "opl2ofm", "ovf2ovp", "ovp2ovf", "omfonts" };
static const_string *names_help[PROG_NUM] =
    { OFM2OPLHELP, OPL2OFMHELP, OVF2OVPHELP, OVP2OVFHELP, OMFONTSHELP };
static const_string names_msg[PROG_NUM] = {
    "This is ofm2opl, Version 2.1",
    "This is opl2ofm, Version 2.1",
    "This is ovf2ovp, Version 2.1",
    "This is ovp2ovf, Version 2.1",
    "This is omfonts, Version 2.1"
};

int no_files=0;
const_string *files[3] = {NULL, NULL, NULL};
const_string suffixes[3] = {NULL, NULL, NULL};
const_string full_suffixes[3] = {NULL, NULL, NULL};

static struct option long_options[] = {
    {"verbose", 0, &verbose_option, 1},
    {"char-format", 1, 0, 0},
    {"num-format", 1, 0, 0},
    {"charcode-format", 1, 0, 0},
    {"text-format", 1, 0, 0},
    {"ofm2opl", 0, 0, 0},
    {"opl2ofm", 0, 0, 0},
    {"ovf2ovp", 0, 0, 0},
    {"ovp2ovf", 0, 0, 0},
    {"help", 0, 0, 0},
    {"version", 0, 0, 0},
    {0, 0, 0, 0}
};

static void read_in_whole(unsigned char **, unsigned *, FILE *, const_string );
static void init_tables(void);

int
main (int argc, string *argv)
{
    int getopt_return_val;
    int option_index = 0;

    name_program = kpse_program_basename(argv[0]);
    if (FILESTRCASEEQ(name_program, "ofm2opl"))
        program = PROG_OFM2OPL;
    else if (FILESTRCASEEQ(name_program, "opl2ofm"))
        program = PROG_OPL2OFM;
    else if (FILESTRCASEEQ(name_program, "ovf2ovp"))
        program = PROG_OVF2OVP;
    else if (FILESTRCASEEQ(name_program, "ovp2ovf"))
        program = PROG_OVP2OVF;
    else
        program = PROG_OMFONTS;

    do {
        getopt_return_val =
        getopt_long_only(argc, argv, "", long_options, &option_index) ;
        if (getopt_return_val == -1) { ; }
        else if ( getopt_return_val == '?' ) {
            usage (names_program[program]);
        } else if (!strcmp(long_options[option_index].name, "help")) {
            usagehelp (names_help[program], NULL);
        } else if (!strcmp(long_options[option_index ].name, "version")) {
            printversionandexit(names_msg[program], nil,
                "J. Plaice, Y. Haralambous, D.E. Knuth",
                nil);
        } else if (!strcmp(long_options[option_index ].name, "ofm2opl")) {
            if (program == PROG_OMFONTS) program = PROG_OFM2OPL;
            else usage (names_program[program]);
        } else if (!strcmp(long_options[option_index ].name, "opl2ofm")) {
            if (program == PROG_OMFONTS) program = PROG_OPL2OFM;
            else usage (names_program[program]);
        } else if (!strcmp(long_options[option_index ].name, "ovf2ovp")) {
            if (program == PROG_OMFONTS) program = PROG_OVF2OVP;
            else usage (names_program[program]);
        } else if (!strcmp(long_options[option_index ].name, "ovp2ovf")) {
            if (program == PROG_OMFONTS) program = PROG_OVP2OVF;
            else usage (names_program[program]);
        } else if (!strcmp(long_options[option_index ].name, "char-format")) {
            if (!strcmp(optarg, "ascii")) char_format = CHAR_CODE_ASCII;
            else if (!strcmp(optarg, "num")) char_format = CHAR_CODE_NUM;
            else warning_s("Bad character code format (%s)", optarg);
        } else if (!strcmp(long_options[option_index ].name, "num-format")) {
            if (!strcmp(optarg, "hex")) num_format = NUM_CODE_HEX;
            else if (!strcmp(optarg, "octal")) num_format = NUM_CODE_OCTAL;
            else warning_s("Bad number code format (%s)", optarg);
        } else if (!strcmp(long_options[option_index ].name, "charcode-format")) {
            if (!strcmp(optarg, "ascii")) {
                char_format = CHAR_CODE_ASCII;
                num_format = NUM_CODE_HEX;
            } else if (!strcmp(optarg, "hex")) {
                char_format = CHAR_CODE_NUM;
                num_format = NUM_CODE_HEX;
            } else if (!strcmp(optarg, "octal")) {
                char_format = CHAR_CODE_NUM;
                num_format = NUM_CODE_OCTAL;
            } else {
                warning_s("Bad character code format (%s)", optarg);
            }
        } else if (!strcmp(long_options[option_index ].name, "text-format")) {
            if (!strcmp(optarg, "upper")) text_format = TEXT_CODE_UPPER;
            else if (!strcmp(optarg, "mixed")) text_format = TEXT_CODE_MIXED;
            else warning_s("Bad text code format (%s)", optarg);
        }
    } while (getopt_return_val != -1);

    switch(program) {
        case PROG_OFM2OPL: {
            no_files = 2;
            files[0] = &name_ofm;
            files[1] = &name_opl;
            suffixes[0] = "ofm";
            suffixes[1] = "opl";
            full_suffixes[0] = ".ofm";
            full_suffixes[1] = ".opl";
            break;
        }
        case PROG_OPL2OFM: {
            no_files = 2;
            files[0] = &name_opl;
            files[1] = &name_ofm;
            suffixes[0] = "opl";
            suffixes[1] = "ofm";
            full_suffixes[0] = ".opl";
            full_suffixes[1] = ".ofm";
            break;
        }
        case PROG_OVF2OVP: {
            no_files = 3;
            files[0] = &name_ovf;
            files[1] = &name_ofm;
            files[2] = &name_ovp;
            suffixes[0] = "ovf";
            suffixes[1] = "ofm";
            suffixes[2] = "ovp";
            full_suffixes[0] = ".ovf";
            full_suffixes[1] = ".ofm";
            full_suffixes[2] = ".ovp";
            break;
        }
        case PROG_OVP2OVF: {
            no_files = 3;
            files[0] = &name_ovp;
            files[1] = &name_ovf;
            files[2] = &name_ofm;
            suffixes[0] = "ovp";
            suffixes[1] = "ovf";
            suffixes[2] = "ofm";
            full_suffixes[0] = ".ovp";
            full_suffixes[1] = ".ovf";
            full_suffixes[2] = ".ofm";
            break;
        }
        default: {
            fprintf(stderr , "Unrecognized program: %s\n", name_program);
            fprintf(stderr ,
            "This binary supports ofm2opl, opl2ofm, ovf2ovp, and ovp2ovf\n");
            usage ("omfonts");
        }
    }

    name_program = names_program[program];
    kpse_set_program_name(argv[0], name_program);
    kpse_init_prog(uppercasify(name_program), 0, nil, nil);

    if (((argc-optind) > no_files) || ((argc-optind) < 1)) {
        fprintf(stderr , "%s: %s\n", name_program,
                no_files == 2 ? "Need one or two file arguments."
                              : "Need one to three file arguments.");
        usage (name_program);
    }
    *(files[0]) = extend_filename(cmdline(optind) , suffixes[0]);
    if (optind+2 <= argc) {
        *(files[1]) = extend_filename(cmdline(optind+1) , suffixes[1]);
        if (no_files == 3) {
            if (optind+3 <= argc) {
                *(files[2]) = extend_filename(cmdline(optind+2) , suffixes[2]);
            } else if (program == PROG_OVP2OVF) {
                *(files[2]) = make_suffix(cmdline(optind+1), suffixes[2]);
            }
        }
    } else if (program != PROG_OFM2OPL) {
        *(files[1]) = basenamechangesuffix(*(files[0]),
                      full_suffixes[0], full_suffixes[1]);
        if ((no_files == 3) && (program == PROG_OVP2OVF)) {
            *(files[2]) = basenamechangesuffix(*(files[0]),
                      full_suffixes[0], full_suffixes[2]);
        }
    }

    switch(program) {
        case PROG_OFM2OPL: {
            file_ofm = kpse_open_file(name_ofm, kpse_ofm_format);
            read_in_whole(&ofm, &length_ofm, file_ofm, name_ofm);
            (void)fclose(file_ofm);
            if (name_opl==NULL) file_opl = stdout;
            else rewrite(file_opl, name_opl);
            file_output = file_opl;
            parse_ofm(FALSE);
            break;
        }
        case PROG_OVF2OVP: {
            file_ovf = kpse_open_file(name_ovf, kpse_ovf_format);
            read_in_whole(&ovf, &length_ovf, file_ovf, name_ovf);
            (void)fclose(file_ovf);
            file_ofm = kpse_open_file(name_ofm, kpse_ofm_format);
            read_in_whole(&ofm, &length_ofm, file_ofm, name_ofm);
            (void)fclose(file_ofm);
            if (name_ovp==NULL) file_ovp = stdout;
            else rewrite(file_ovp, name_ovp);
            file_output = file_ovp;
            parse_ofm(TRUE);
            break;
        }
        case PROG_OPL2OFM: {
            file_opl = kpse_open_file(name_opl, kpse_opl_format);
            rewritebin(file_ofm, name_ofm);
            init_tables();
            yyin = file_opl;
            (void)yyparse();
            output_ofm_file();
            (void)fclose(file_ofm);
            if (num_errors > 0) exit(1);
            break;
        }
        case PROG_OVP2OVF: {
            file_ovp = kpse_open_file(name_ovp, kpse_ovp_format);
            rewritebin(file_ovf, name_ovf);
            rewritebin(file_ofm, name_ofm);
            init_tables();
            yyin = file_ovp;
            (void)yyparse();
            output_ofm_file();
            (void)fclose(file_ofm);
            output_ovf_file();
            (void)fclose(file_ovf);
            if (num_errors > 0) exit(1);
            break;
        }
        default: {exit(1);}
    }
    exit(0);
}

#define BIG_BLOCK 0x20000
#define LITTLE_BLOCK 0x1000

static void
read_in_whole(unsigned char **contents_loc,
              unsigned *length_loc,
              FILE *file,
              const_string name)
{
    unsigned no_read;
    unsigned no_total_read = 0;
    unsigned size = BIG_BLOCK;
    string where;
    string current_block;

    current_block = (char *) xmalloc(size);
    where = current_block;

    while (1) {
        no_read = fread(where, 1, LITTLE_BLOCK, file);
        no_total_read += no_read;
        if ((no_total_read+LITTLE_BLOCK) > size) {
            size *= 2;
            current_block = (char *) xrealloc(current_block, size);
        }
        where = current_block + no_total_read;
        if (ferror(file)) {
            fatal_error_s("Error while reading file %s", name);
        } else if (feof(file)) {
            break;
        }
    }

    *contents_loc = (unsigned char *) current_block;
    *length_loc = no_total_read;
}

static void
init_tables(void)
{
    font_table_init(); /* subsidiary fonts in virtual fonts */
    init_header();
    init_planes();
    init_measures();
}

