/*
 * Copyright (C) 2000, Matias Atria
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <string.h>
#include <stdlib.h>
#include <getopt.h>

#include "mdvi.h"
#include "private.h"
#include "defaults.h"
#include "version.h"

#if defined(ENABLE_NLS) && defined(HAVE_LOCALE_H)
#include <locale.h>
#endif

int	profiling = 0;
int	batch_mode = 0;
int	registered_fonts = 0;

#if WITH_TYPE1_FONTS
extern char *T1_GetLibIdent();
#endif

extern char *kpathsea_version_string;
extern int init_x11(void);
extern void view_page(DviContext *, int);
extern int mdvi_create_window(DviContext *, const char *);
extern Ulong get_color_byname __PROTO((const char *));

/* font types */
extern DviFontInfo pk_font_info;
extern DviFontInfo pkn_font_info;
extern DviFontInfo gf_font_info;
extern DviFontInfo vf_font_info;
#ifdef WITH_OMEGA
extern DviFontInfo ovf_font_info;
#endif
#ifdef WITH_TRUETYPE_FONTS
extern DviFontInfo tt_font_info;
#endif
#ifdef WITH_TYPE1_FONTS
extern DviFontInfo t1_font_info;
#endif
#ifdef WITH_AFM_FILES
extern DviFontInfo afm_font_info;
#endif
extern DviFontInfo tfm_font_info;
#ifdef WITH_OMEGA
extern DviFontInfo ofm_font_info;
#endif

static struct fontinfo {
	DviFontInfo *info;
	char	*desc;
	int	klass;
} known_fonts[] = {
	{&vf_font_info, "Virtual fonts", 0},
#ifdef WITH_OMEGA
	{&ovf_font_info, "Omega's virtual fonts", 0},
#endif
#ifdef WITH_TRUETYPE_FONTS
	{&tt_font_info, "TrueType fonts", 0},
#endif
#ifdef WITH_TYPE1_FONTS
	{&t1_font_info, "Type1 PostScript fonts", 0},
#endif
	{&pk_font_info, "Packed bitmap (auto-generated)", 1},
	{&pkn_font_info, "Packed bitmap", -2},
	{&gf_font_info, "Metafont's generic font format", 1},
#ifdef WITH_OMEGA
	{&ofm_font_info, "Omega font metrics", -1},
#endif
	{&tfm_font_info, "TeX font metrics", -1},
#ifdef WITH_AFM_FILES
	{&afm_font_info, "Adobe font metrics", -1},
#endif
	{0, 0}
};

static int print_info_only = 0;
char	*logfile;
DviAppConfig *mdvi_app;
char	*program_name;

static char mdvi_banner[] = 
"mdvi " MDVI_VERSION " -- A TeX DVI previewer for Unix\n"
"Copyright (C) 2000 Matias Atria\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under certain conditions. Try `mdvi --info' for details\n\n";

static char mdvi_long_banner[] = 
"mdvi " MDVI_VERSION " -- A TeX DVI previewer for Unix\n"
"Copyright (C) 2000 Matias Atria\n\n"
"This program is free software; you can redistribute it and/or modify\n"
"it under the terms of the GNU General Public License as published by\n"
"the Free Software Foundation; either version 2 of the License, or\n"
"(at your option) any later version.\n\n"
"There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"
"PARTICULAR PURPOSE.\n\n";

static const char *orientstr[] = {
	"tblr", "tbrl", "btlr", "btrl",
	"rp90", "rm90", "irp90", "irm90"
};

static const char usage_help[] =
"Usage: mdvi [OPTION] ... DVIFILE\n\n"
"Options:\n"
"  -a, --font-types=FONTS\n"
"     specify which font types to use\n"
"  -A, --fonts-omitted[=FONTS]\n"
"     specify which font types not to use by default\n"
"  -b, --boxes\n"
"     see the world through TeX's eyes\n"
"  -B, --bg=COLOR\n"
"     set background color\n"
"  -d, --debug=MASK\n"
"     set debug mask (see --explain debug for more info)\n"
"  -D, --dpi=DPI\n"
"     set resolution to DPI pixels per inch\n"
"  -e, --explain=TOPIC\n"
"     give more detailed help on a particular topic, use topic `list' to\n"
"     see the list of known topics\n"
"  -f, --fallback-font=FONT-NAME\n"
"     font to use when one is missing\n"
"  -F, --fg=COLOR\n"
"     set foreground color\n"
"  -G, --gamma=NUMBER\n"
"     gamma correction for antialiased glyphs\n"
"  -h, --help[=TOPIC]\n"
"     display help on a command line option. TOPIC may be the long name of an\n"
"     option, or one of `papers' or `units'.\n"
"  -H, --horizontal-margin=UNIT\n"
"     size of horizontal margins\n"
"  -i, --info\n"
"     display licensing information and default settings\n"
"  -l, --log-file=[LEVEL:]FILE\n"
"     save all messages with priority at least LEVEL to FILE\n"
"  -m, --mf-mode=MODE\n"
"     Metafont mode to use when creating fonts\n"
"  -M, --magnification=NUMBER\n"
"     magnification factor (NUMBER = TeX's NUMBER/1000.0)\n"
"  -N, --mono\n"
"     to force monochrome configuration\n"
"  -o, --orientation=ORIENTATION\n"
"     set document on-screen orientation\n"
"  --orientation=help\n"
"     print option details and exit\n"
"  -p, --paper=NAME\n"
"     paper type to use\n"
"  --paper=list\n"
"     print known paper types and exit\n"
"  -P, --page=NUMBER\n"
"     start viewing page NUMBER\n"
"  -r, --page-range=RANGE\n"
"     specify which files should be read\n"
"  --page-range=help\n"
"     print option details and exit\n"
"  -s, --shrink=NUMBER\n"
"     shrinking factor for glyphs\n"
"  -S, --sort-pages[=TYPE]\n"
"     set page sorting type, where `TYPE' is one of up, down, random,\n"
"     dviup, dvidown or none. If `TYPE' is not specified, `up' is assumed\n"
"  -t, --pixel-density=NUMBER\n"
"     pixel density for antialiased glyphs (0 <= NUMBER <= 100)\n"
"  -T, --tex-page=NUMBER\n"
"     start viewing TeX's (i.e. document's) page NUMBER\n"
"  -V, --vertical-margin=UNIT\n"
"     size of vertical margins\n"
"  -x, --xdpi=DPI\n"
"     set horizontal resolution\n"
"  -y, --ydpi=DPI\n"
"     set vertical resolution\n"
"  -z, --batch\n"
"     run in batch mode (no UI)\n";

static const char debug_help[] =
"Debugging masks: -d MASK, --debug=MASK\n"
"    MASK is a numerical value, obtained by ORing the values given below\n"
"or a series of mask names separated by commas or by `|'. The masks known\n"
"to MDVI are:\n\n"
"  Name         Value  Description\n"
"------------------------------------------------------------------------\n"
"  opcode           1  DVI opcodes and arguments in `dvitype' style\n"
"  fonts            2  messages related to the management of fonts\n"
"  files            4  file operations\n"
"  dvi              8  operations on DVI files\n"
"  params          16  manipulation of DVI parameters\n"
"  special         32  DVI specials\n"
"  device          64  device-specific messages\n"
"  glyphs         128  loading of glyphs (lots of output)\n"
"  paths          256  paths configuration in kpathsea\n"
"  search         512  file searches by kpathsea\n"
"  vars          1024  traces environment variables (MDVI and kpathsea)\n"
"  bitops        2048  bitmap operations (scaling, flipping, etc)\n"
"  bitdata       4096  bitmap bits (MASSIVE amounts of output)\n"
"  type1         8192  management of Type1 fonts\n"
"  tt           16384  management of TrueType fonts\n"
"  ---          32768  unused\n"
"  fontmaps     65536  things involving font maps and encodings\n";

static const char orientation_help[] = 	
"Orientation options: -o ORIENTATION, --orienation=ORIENTATION\n"
"    ORIENTATION is one of TBLR, TBRL, BTLR, BTRL, RP90, RM90, IRP90 or\n"
"IRM90, where TB=top-to-bottom, LR=left-to-right, etc.,\n"
"RP=rotate-positively (i.e. counter-clockwise), RM=rotate-negatively,\n"
"and the last two are the same than these, after a reflection with\n"
"respect to an imaginary vertical axis. The values Portrait, Landscape\n"
"and Seascape are synonyms for TBLR, RM90 and RP90, respectively.\n";

static const char range_help[] = 
"Page range selection options:  -r RANGE-SPEC, --page-range=RANGE-SPEC\n"
"    RANGE-SPEC consists of up to 11 RANGE-LIST strings, separated by dots.\n"
"If there is only one list, it is assumed to apply to DVI page numbers.\n"
"Otherwise, each list applies to the corresponding TeX page \\counter (10 per\n"
"page). A RANGE-LIST is either empty or a comma-separated list of ranges, at\n"
"least one of which must contain the page counter in order for the page to be\n"
"processed. A range has the form LOWER:UPPER:STEP, which specifies the\n"
"sequence LOWER + STEP * n, for 0 <= n <= (UPPER - LOWER) / STEP. A\n"
"RANGE-LIST may optionally be written inside braces, for clarity, but no\n"
"white space is allowed anywhere in a RANGE-SPEC. Any part of a range (except\n"
"for the `:') may be omitted, in which case LOWER, UPPER and STEP take the\n"
"default values -INFINITY, +INFINITY and 1, respectively, except in two\n"
"cases: (1) The form ::STEP specifies the sequence `STEP * n' for `n' an\n"
"arbitrary integer, and (2) In the form :UPPER:STEP, LOWER is set to\n"
"`PREV_UPPER + 1' if the preceding range had a finite upper bound, and to\n"
"-INFINITY otherwise. An empty RANGE-LIST (written as `*') is equivalent\n"
"to {::}.\n"
"    If the first RANGE-LIST begins with `D' or `d', it is assumed to apply to \n"
"DVI page numbers, regardless of whether it is the only RANGE-LIST in the \n"
"RANGE-SPEC.\n"
"Examples:\n"
"      ::2   Matches all even DVI page numbers.\n"
"      1::2  Matches all odd DVI page numbers.\n"
"      {1:4}.*.{1:11:2,:15}.*\n"
"            Matches \\counter0=1,2,3,4, and \\counter1=1,3,5,7,11,12,13,14,15\n"
"      D{::2}.{5:}.*\n"
"            Matches all even DVI page numbers AND \\counter0 >= 5.\n";

static const char units_help[] = 
"Units:\n"
"    MDVI recognizes all TeX units, plus some more:\n"
"        Metric units: meters (mt), centimeters (cm), milimeters (mm)\n"
"        US units:     yard (yd), feet (ft), inches (in)\n"
"        TeX units:    points (pt), picas (pc), didot points (dd),\n"
"                      ciceros (cc), scaled points (sp)\n"
"        Other units:  big/PostScript points (bp), charspace (cs)\n";

static const char log_help[] = 
"Logfile option: -l, --log-file=[LEVEL:]FILE\n"
"     Tells MDVI to save all messages with priority at least LEVEL to FILE.\n"
"LEVEL is one of `info', `warn', `err' and `debug'. FILE can be the name\n"
"of a file or the values 1 (standard output) or 2 (standard error). The\n"
"name `-' is equivalent to `1'.\n";

static const char font_type_help[] =
"Font support option: -a, --font-types=FONTS\n"
"     Tells MDVI which font types should be supported. FONTS is a comma\n"
"separated list of font `families' that MDVI is to recognize during this\n"
"run. A type name can be preceded by a class ID, which will become the\n"
"default class for all fonts in the FONTS specification after that ID.\n"
"Examples:\n"
"     --font-types=0:vf,t1,tt,1:pk,m:tfm\n"
"This entry means: put the font types vf (virtual fonts), t1 (Type1) and\n"
"tt (TrueType) in class 0 (highest priority). In class 1, put type pk\n"
"(packet bitmap format). Finally, put type tfm (TeX font metric format)\n"
"in the metric class. Thus, MDVI will not lookup fonts other than these\n"
"to resolve the font references in the DVI file.\n"
"\n"
"To see the list of all font types known to MDVI, and their default class,\n"
"use `--font-types=list'. For a brief description of the font lookup\n"
"mechanism, use `--explain=fonts'.\n";

static struct {
	const char *topic;
	const char *desc;
	const char *string;
} topics[] = {
	{"log", "log file options", log_help},
	{"mdvi", "command line options", usage_help},
	{"units", "units supported by MDVI and their syntax", units_help},
	{"orientation", "syntax for specifying orientations", orientation_help},
	{"ranges", "syntax for specifying page ranges", range_help},
	{"debug", "debugging options", debug_help},
	{"font-types", "font support selection", font_type_help},
	{0, 0}
};

static void print_paper_specs(const char *title, DviPaperSpec *spec)
{
	int	i;
	char	buf[256];
	int	len = 0;
	
	printf("%s:\n", title);
	for(i = 0; spec[i].name; i++) {
		int	n;
		
		n = sprintf(buf, "  %10s: %6s x %s",
			spec[i].name,
			spec[i].width,
			spec[i].height);
		while(n < 30)
			buf[n++] = ' ';
		buf[n] = 0;
		printf("%s", buf);
		if(len || n >= 78) {
			fputc('\n', stdout);
			len = 0;
		} else
			len = n;
	}
	if(len) fputc('\n', stdout);
}

static void print_fonts(void)
{
	struct fontinfo *fi;
	
	printf("Known font formats:\n\n");
	printf("%8s  %-30s  Class\n", "Name", "Description");
	printf("-----------------------------------------------\n");
	for(fi = known_fonts; fi->info; fi++) {
		printf("%8s  %-30s    ", fi->info->name, fi->desc);
		if(fi->klass == -1)
			putchar('M');
		else if(fi->klass < -1)
			putchar('-');
		else
			printf("%d", fi->klass);
		putchar('\n');
	}
	putchar('\n');
	printf("Priority classes: 0 (highest) - %d (lowest), M (metric)\n",
		mdvi_get_font_classes());
}

static void print_papers(void)
{
	DviPaperSpec *spec;
	
	spec = mdvi_get_paper_specs(MDVI_PAPER_CLASS_ISO);
	if(spec != NULL) {
		print_paper_specs(_("ISO paper types"), spec);
		mdvi_free_paper_specs(spec);
	}
	spec = mdvi_get_paper_specs(MDVI_PAPER_CLASS_US);
	if(spec != NULL) {
		print_paper_specs(_("US paper types"), spec);
		mdvi_free_paper_specs(spec);
	}
	printf(
"Custom paper types:\n"
"  <NUMBER>x<NUMBER><UNIT>       (e.g., 210x292.7mm)\n"
"  <NUMBER><UNIT>,<NUMBER><UNIT> (e.g., 208.5mm,13in)\n");
}

static void usage(const char *string, int exit_code)
{
	const char *help = NULL;
	int	i;
	
	if(string == NULL)
		help = usage_help;
	else for(i = 0; topics[i].topic; i++) {
		if(STRCEQ(string, topics[i].topic)) {
			help = topics[i].string;
			break;
		}
	}
	
	printf("%s", mdvi_banner);
	if(help == NULL)
		fprintf(stderr, _("%s: I don't know about that\n"), string);
	else
		printf("%s", help);
	exit(exit_code);	
}


void	print_config(DviAppConfig *app)
{
	int	i;

	printf("%s", mdvi_long_banner);
	printf(
"Environment variables, and compiled defaults:\n"
"  MDVI_DPI            resolution (in pixels per inch)    %d\n"
"  MDVI_VDPI           vertical resolution                %d\n"
"  MDVI_MAGNIFICATION  magnification (1.0 = normal)       %.2f\n"
"  MDVI_SHRINKING      shrinking factor                   %d\n"
"  MDVI_HSHRINK        horizontal shrinking factor        %d\n"
"  MDVI_VSHRINK        vertical shrinking factor          %d\n"
"  MDVI_HDRIFT         horizontal drifting tolerance      %d\n"
"  MDVI_VDRIFT         vertical drifting tolerance        %d\n"
"  MDVI_PIXEL_DENSITY  pixel density                      %d\n"
"  MDVI_GAMMA          gamma correction factor            %.2f\n"
"  MDVI_PAPER          paper name                         %s\n"
"  MDVI_HMARGIN        horizontal margins around pages    %s\n"
"  MDVI_VMARGIN        vertical margins around pages      %s\n"
"  MDVI_FALLBACK_FONT  font to use when one is missing    %s\n"
"  MDVI_MFMODE         default metafont mode              %s\n"
"  MDVI_HRUNITS        units for horizontal rulers        %s\n"
"  MDVI_VRUNITS        units for vertical rulers          %s\n"
"  MDVI_ORIENTATION    page on-screen orientation         %s\n"
"  MDVI_FONTSPEC       default font configuration         builtin\n"
"  MDVI_FONTOMIT       fonts types omitted by default     PKN\n"
"  MDVI_FOREGROUND     foreground color                   %s\n"
"  MDVI_BACKGROUND     background color                   %s\n"
"  MDVI_DEBUG          debug mask\n",
  	MDVI_DPI,
  	MDVI_VDPI,
  	MDVI_MAGNIFICATION,
  	MDVI_DEFAULT_SHRINKING,
	-1, /* HSHRINK */
	-1, /* VSHRINK */
	-1, /* HDRIFT */
	-1, /* VDRIFT */
  	MDVI_DEFAULT_DENSITY,
  	MDVI_DEFAULT_GAMMA,
  	MDVI_PAPERNAME,
  	MDVI_HMARGIN,
  	MDVI_VMARGIN,
  	MDVI_FALLBACK_FONT,
  	MDVI_MFMODE ? MDVI_MFMODE : "auto",
  	MDVI_HRUNITS,
  	MDVI_VRUNITS,
  	MDVI_ORIENTATION,
  	MDVI_FOREGROUND,
  	MDVI_BACKGROUND);
	
	putchar('\n');
	printf("Current configuration:\n");
	printf("  dpi:             %d\n", app->params.dpi);
	printf("  vdpi:            %d\n", app->params.vdpi);
	printf("  magnification:   %.2f\n", app->params.mag);
	printf("  hshrink:         %d\n", app->params.hshrink);
	printf("  vshrink:         %d\n", app->params.vshrink);
	printf("  hdrift:          %d\n", app->params.hdrift);
	printf("  vdrift:          %d\n", app->params.vdrift);
	printf("  pixel density:   %d\n", app->params.density);
	printf("  gamma factor:    %.2f\n", app->params.gamma);
	printf("  paper type:      %s\n", app->paper.name);
	printf("  margins:         (%s,%s)\n", app->hmargin, app->vmargin);
	printf("  fallback font:   %s\n", app->fallback_font);
	printf("  Metafont mode:   %s\n", app->mfmode ? app->mfmode : "auto");
	printf("  ruler units:     (%s,%s)\n", app->hrule_units, app->vrule_units);
	printf("  orientation:     %s\n", orientstr[app->params.orientation]);
	printf("  font types:      %s\n", app->fontspec ? app->fontspec : "all");
	printf("  fonts omitted:   %s\n", app->fontunspec ? app->fontunspec : "none");
	printf("  foreground:      %s\n", app->fgcolor);
	printf("  background:      %s\n", app->bgcolor);
		
	putchar('\n');
	printf("Fonts supported in this configuration:\n");
	for(i = -1; i <= mdvi_get_font_classes(); i++) {
		char ** list;
		int	j;
		
		list = mdvi_list_font_class(i);
		if(list == NULL)
			break;
		printf("  Class ");
		if(i == -1)
			printf("M: ");
		else
			printf("%d: ", i);
		if(list[0] == NULL)
			printf("None");
		else for(j = 0; list[j]; j++) {
			printf("%s%s", j ? ", " : "", list[j]);
			xfree(list[j]);
		}
		xfree(list);
		putchar('\n');
	}
}

int	process_options(DviAppConfig *app, int argc, char **argv)
{
	static struct option dviopts[] = {
		{"batch", 0, 0, 'z'},
		{"debug", 1, 0, 'd'},
		{"help", 2, 0, 'h'},
		{"info", 0, 0, 'i'},
		{"bg", 1, 0, 'B'},
		{"boxes", 0, 0, 'b'},
		{"dpi", 1, 0, 'D'},
		{"debug", 2, 0, 'd'},
		{"explain", 1, 0, 'e'},
		{"fallback-font", 1, 0, 'f'},
		{"fg", 1, 0, 'F'},
		{"font-types", 2, 0, 'a'},
		{"fonts-omitted", 2, 0, 'A'},
		{"gamma", 1, 0, 'G'},
		{"horizontal-margin", 1, 0, 'H'},
		{"log-file", 1, 0, 'l'},
		{"magnification", 1, 0, 'M'},
		{"mf-mode", 1, 0, 'm'},
		{"mono", 0, 0, 'N'},
		{"orientation", 1, 0, 'o'},
		{"page", 1, 0, 'P'},
		{"page-range", 1, 0, 'r'},
		{"paper", 1, 0, 'p'},
		{"pixel-density", 1, 0, 't'},
		{"shrink", 1, 0, 's'},
		{"sort-pages", 2, 0, 'S'},
		{"tex-page", 1, 0, 'T'},
		{"vertical-margin", 1, 0, 'V'},
		{"xdpi", 1, 0, 'x'},
		{"ydpi", 1, 0, 'y'},
		{"version", 0, 0, 'v'},
		{0, 0, 0, 0}
	};

	while(1) {
		int	c;
		int	ndx;
		char	*arg;
		
		c = getopt_long(argc, argv, 
			"a::A::d:hiB:bD:e:f:F:G:H:il:M:m:No:P:r:p:t:s:S:T:V:x:y:zv",
			dviopts, &ndx);

		arg = optarg;		
		if(c == -1)
			break;

		switch(c) {
		case 'A':
		case 'a':
			if(arg && STRCEQ(arg, "list")) {
				printf("%s", mdvi_banner);
				print_fonts();
				exit(EXIT_SUCCESS);
			} else if(arg && STRCEQ(arg, "help"))
				usage("font-types", EXIT_SUCCESS);
			if(c == 'a')
				registered_fonts += configure_fonts(arg, 1);
			else
				configure_fonts(arg, 0);
			break;
		case 'b':
			app->params.flags |= MDVI_PARAM_CHARBOXES;
			break;
		case 'B':
			app->bgcolor = arg;
			break;
		case 'd': {
			long	mask;
			
			if(STREQ(arg, "list"))
				usage("debug", EXIT_SUCCESS);
			if(mdvi_getint(arg, &mask) < 0)
				mask = (long)mdvi_get_debugmask(arg);
			add_debug_mask((Uint32)mask);
			break;
		}
		case 'x':
		case 'y':
		case 'D': {
			long	dpi;
			
			if(mdvi_getint(arg, &dpi) < 0 || dpi <= 0)
				warning(_("resolution must be a positive integer\n"));
			else if(c == 'x')
				app->params.dpi = (Uint)dpi;
			else if(c == 'y')
				app->params.vdpi = (Uint)dpi;
			else
				app->params.dpi = app->params.vdpi = (Uint)dpi;
			break;
		}
		case 'e':
			if(STRCEQ(arg, "list")) {
				int	i;
				
				printf("%s", mdvi_banner);
				printf(_("Known topics\n"));
				for(i = 0; topics[i].topic; i++)
					printf("  %-12s: %s\n",
						topics[i].topic, topics[i].desc);
				exit(EXIT_SUCCESS);
			} else
				usage(arg, EXIT_SUCCESS);
			break;
		case 'f':
			app->fallback_font = arg;
			break;
		case 'F':
			app->fgcolor = arg;
				break;
		case 'g':
			/* UNUSED */
			break;
		case 'G':
			app->params.gamma = strtod(arg, NULL);
			break;
		case 'h':
			usage(NULL, EXIT_SUCCESS);
			break;
		case 'H':
			app->hmargin = arg;
			break;
		case 'i':
			print_info_only = 1;
			break;
		case 'l':
			if(STRNEQ(arg, "info:", 5)) {
				arg += 5;
				mdvi_set_loglevel(LOG_INFO);
			} else if(STRNEQ(arg, "warn:", 5)) {
				arg += 5;
				mdvi_set_loglevel(LOG_WARN);
			} else if(STRNEQ(arg, "err:", 4)) {
				arg += 4;
				mdvi_set_loglevel(LOG_ERROR);
			} else if(STRNEQ(arg, "debug:", 6)) {
				arg += 6;
				mdvi_set_loglevel(LOG_DEBUG);
			} else
				mdvi_set_loglevel(LOG_WARN);
			if(*arg == 0 || STREQ(arg, "-") || atoi(arg) == 1)
				mdvi_set_logstream(stdout);
			else if(atoi(arg) == 2)
				mdvi_set_logstream(stderr);
			else
				mdvi_set_logfile(arg);
			break;
		case 'm':
			app->mfmode = arg;
			break;
		case 'M': {
			double mag;
			
			if(mdvi_getfloat(arg, &mag) < 0 || mag <= 0) {
				warning(_("magnification must be positive\n"));
			} else
				app->params.mag = mag;
			break;
		}
		case 'N':
			app->params.flags &= ~MDVI_PARAM_ANTIALIASED;
			app->params.flags |= MDVI_PARAM_MONO;
			break;
		case 'o':
			if(STRCEQ(arg, "help"))
				usage("orientation", EXIT_SUCCESS);				
			app->params.orientation = mdvi_getorient(arg);
			break;
		case 'p':
			if(STRCEQ(arg, "help") || STRCEQ(arg, "list")) {
				print_papers();
				exit(EXIT_SUCCESS);
			}
			app->paper.name = arg;
			break;
		case 'P':
			if(mdvi_getint(arg, (long *)&app->first_page) < 0) {
				warning(_("page number must be a positive integer\n"));
				app->first_page = 0;
			} else
				app->use_tex_page = 0;
			break;
		case 'r':
			if(STRCEQ(arg, "help"))
				usage("ranges", EXIT_SUCCESS);
			app->page_ranges = arg;
			break;
		case 's': {
			long h, v;
				
			if(sscanf(arg, "%ld,%ld", &h, &v) == 2) {
				app->params.vshrink = (Uint)v;
				app->params.hshrink = (Uint)h;
			} else if(mdvi_getint(arg, &h) < 0 || h < 0) {
				warning(_("shrinking factor must be a non-negative number\n"));
			} else
				app->params.vshrink = 
				app->params.hshrink = (Uint)h;
			break;
		}
		case 'S': {
			int	v;
			
			if(arg == NULL)
				app->page_sort = MDVI_PAGE_SORT_UP;
			else if((v = mdvi_get_pagesort(arg)) < 0) {
				warning(_("unknown sorting type `%s'\n"),
					arg);
				app->page_sort = MDVI_PAGE_SORT_NONE;
			} else
				app->page_sort = v;
			break;
		}
		case 't': {
			long s;
			
			if(mdvi_getint(arg, &s) < 0 || s < 0)
				warning(_("pixel density must be a non-negative number\n"));
			else if(s > 100) {
				warning(_("pixel density should be less than 100\n"));
				s = 100;
			}
			app->params.density = (Uint)s;
			break;
		}
		case 'T': {
			long	s;
			
			if(mdvi_getint(arg, &s) < 0)
				warning(_("tex-page must be an integer\n"));
			else {
				app->first_page = (int)s;
				app->use_tex_page = 1;
			}
			break;
		}
		case 'V':
			app->vmargin = arg;
			break;
		case 'v': {
			char	*a, *b;
			
			printf("%s", mdvi_long_banner);
			/* Check the library versions and report any differences */
			a = strrchr(KPSE_VERSION, ' ');
			a = (a ? a + 1 : KPSE_VERSION);
			b = strrchr(kpathsea_version_string, ' ');
			b = (b ? b + 1 : kpathsea_version_string);
			printf("Compiled with kpathsea %s", a);
			if(!STREQ(a, b))
				printf(", using %s", b);
			fputc('\n', stdout);
			printf("Additional features:\n");
#ifdef WITH_TYPE1_FONTS
			printf("\tType1 fonts (t1lib %s)", T1LIB_VERSION);
			b = T1_GetLibIdent();
			if(!STREQ(T1LIB_VERSION, b))
				printf(", using %s", b);
			fputc('\n', stdout);
#endif
#ifdef WITH_TRUETYPE_FONTS
			printf("\tTrueType fonts (Freetype 1.x)\n");
#endif
#ifdef WITH_AFM_FILES
			printf("\tAFM files\n");
#endif
#ifdef WITH_OMEGA
			printf("\tOmega file formats\n");
#endif
#ifdef WITH_REGEX_SPECIALS
			printf("\tRegex \\specials\n");
#endif
			printf("Using %dbit bitmap units\n", BITMAP_BITS);
			exit(EXIT_SUCCESS);
			break;
		}
		case 'z':
			batch_mode = 1;
			break;
		default:
			usage(NULL, EXIT_FAILURE);
			break;
		}
	}
	return optind;
}

int	configure_fonts(const char *spec, int enabled)
{
	int	done = 0;
	int	klass = -2; /* unspecified */
	int	nclasses;
	const char *ptr;
	const char *text;
	struct fontinfo *type;
	int	count = 0;
	
	nclasses = mdvi_get_font_classes();	
	if(spec == NULL || STRCEQ(spec, "all")) {
		for(type = known_fonts; type->info; type++) {
			if(!enabled)
				type->klass = -2;
			else if(type->klass != -2)
				mdvi_register_font_type(type->info, type->klass);
			count++;
		}
		if(!enabled)
			DEBUG((DBG_FONTS, "all font types disabled by default\n"));
	} else for(ptr = text = spec; !done; ptr++) {
		int	len;
		int	k;
		int	status;
		char	*name;

		if(*ptr && *ptr != ',')
			continue;
		if(*ptr == 0)
			done = 1;
		if((len = ptr - text) == 0)
			continue;
		if(len >= 2 && text[1] == ':') {
			switch(text[0]) {
			case 'm': case 'M':
				k = -1;
				break;
			case '0': case '1':
			case '2': case '3':
			case '4': case '5':
			case '6': case '7':
			case '8': case '9':
				k = *text - '0';
				break;
			default:
				text -= 2; /* will be fixed later */
				break;
			}
			if(k < -1 || k > nclasses) {
				k = klass;
				warning("font class %d does not exist\n");
			}
			klass = k;
			text += 2;
		} else if(*text == ':')
			k = klass = -3;
		else
			k = klass;
		for(type = known_fonts; type->info; type++) {
			if(STRNCEQ(type->info->name, text, len))
				break;
		}
		text = ptr + 1;
		if(type->info == NULL)
			continue;
		name = type->info->name;
		if(!enabled) {
			DEBUG((DBG_FONTS, "%s fonts disabled by default\n",
				name));
			type->klass = -2;
			continue;
		}
		if(k < -2)
			k = type->klass;
		/* if the user wants to put a metric font in a real class,
		 * or the other way around, give a warning */
		if(type->klass < 0 && k >= 0) {
			warning("adding %s fonts to `real' class %d\n",
				name, k);
		} else if(type->klass >= 0 && k < 0) {
			warning("adding %s fonts to the metric class\n",
				name);
		}

		status = mdvi_register_font_type(type->info, k);
		if(status < 0) {
			warning("could not register font type `%s' in class %d\n",
				name, k);
		} else {
			DEBUG((DBG_FONTS, "%s: added to font class %d\n",
				name, k));
			count++;
		}
	}
	return count;
}

int	main(int argc, char **argv)
{
	DviPageSpec *spec;
	DviContext *ctx;
	char	*dvifile;
	char	*ptr;
	int	i;

#ifdef ENABLE_NLS
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
#endif

	program_name = strrchr(argv[0], '/');
	if(program_name == NULL)
		program_name = argv[0];
	else
		program_name++;
	/* get as integer */
	ptr = getenv("MDVI_DEBUG");
	if(ptr != NULL) {
		long	x;
		
		if(mdvi_getint(ptr, &x) < 0)
			x = (long)mdvi_get_debugmask(ptr);
		set_debug_mask((Uint32)x);
	}

	mdvi_app = mdvi_init_app(program_name, "mdvi");	
	
	if(mdvi_app->fontunspec)
		configure_fonts(mdvi_app->fontunspec, 0);

	i = process_options(mdvi_app, argc, argv);
	if(i < 0)
		exit(EXIT_FAILURE);

	dvifile = argv[i];

	/* check that everything is alright */
	if(mdvi_check_config(mdvi_app) < 0)
		exit(EXIT_FAILURE);
		
	if(mdvi_app->page_ranges != NULL)
		spec = mdvi_parse_page_spec(mdvi_app->page_ranges);
	else
		spec = NULL;
	
	if(mdvi_app->fallback_font == NULL)
		mdvi_set_fallback_font(mdvi_app->fallback_font);
#ifdef MDVI_FALLBACK_FONT
	else
		mdvi_set_fallback_font(MDVI_FALLBACK_FONT);
#endif

	/* if no fonts have been registered, do that now */
	if(!registered_fonts)
		registered_fonts = configure_fonts(mdvi_app->fontspec, 1);
	if(!registered_fonts)
		fatal("no font types registered\n");

	if(print_info_only) {
		print_config(mdvi_app);
		exit(EXIT_SUCCESS);
	}

	if(dvifile == NULL) {
		fprintf(stderr, _("%s: no DVI file specified\n"), 
			program_name);
		fprintf(stderr, _("Try `%s --help' for more information\n"),
			program_name);
		exit(EXIT_FAILURE);
	}
	
	ctx = mdvi_init_context(&mdvi_app->params, spec, dvifile);
	if(ctx == NULL)
		exit(1);
	ctx->paper = mdvi_app->paper;
	if(!batch_mode && init_x11() == -1)
		exit(EXIT_FAILURE);

	if(mdvi_app->page_sort != MDVI_PAGE_SORT_NONE)
		mdvi_sort_pages(ctx, mdvi_app->page_sort);
	
	if(mdvi_app->use_tex_page)
		mdvi_app->first_page = 
			mdvi_find_tex_page(ctx, mdvi_app->first_page);
	else if(mdvi_app->first_page < 0)
		mdvi_app->first_page += MDVI_LASTPAGE(ctx) + 1;
	
	if(!MDVI_VALIDPAGE(ctx, mdvi_app->first_page))
		mdvi_app->first_page = 0;

	if(batch_mode == 0) {
		/* beginning of device-specific code */
		ctx->params.fg = get_color_byname(mdvi_app->fgcolor);
		ctx->params.bg = get_color_byname(mdvi_app->bgcolor);
		mdvi_create_window(ctx, NULL);	
		mdvi_reset_color(ctx);
		view_page(ctx, mdvi_app->first_page);
		/* end of device-specific code */
	} else {
		ctx->params.fg = (Ulong)-1;
		ctx->params.bg = 0;
		if(DEBUGGING(DVI)) {
			printf("%s", ctx->filename);
			for(i = 0; i < ctx->npages; i++) {
				mdvi_dopage(ctx, i);
				printf(" [%d]", ctx->pagemap[i][1]);
			}
		} else 
			for(i = 0; i < ctx->npages; i++)
				mdvi_dopage(ctx, i);
		fputc('\n', stdout);
	}
	
	mdvi_destroy_context(ctx);
	mdvi_flush_fontmaps();
	mdvi_flush_encodings();
	mdvi_ps_flush_fonts();
	flush_color_table();

	exit(0);
}
