/* mff.c 2.9.0 92/07/06 - run a METAFONT-GFtoPK job to make a font ----------------------------------------------------------------------- This program copyright (c) 1990, 1991 Damian Cugley. It is provided for free on an "as-is" basis. See the file COPYING for more information. See mff(1) for more info on using this program ----------------------------------------------------------------------- * * 2.0 - C version of shellscript * Created Damian Cugley Sat 13 Jan 1990 * 2.1 * DPIs, magnification and factor can be lists of floats * - pdc Sat 27 Jan 1990 * - pdc Mon 29 Jan 1990 * 2.2 - pdc Tue 30 Jan 1990 * Define size using font_size rather than design_size# * (more "standard") * 2.3 - pdc Fri 30 Mar 1990 * If the mode-def starts with `"', use `smode' rather than `mode'. * 2.4 - pdc Mon 16 Jul 1990 * 2 new options: -J and -j; act like -I, -i: extra user-defined suffixes * 2.5 - pdc Sun. 16 Sept. 1990 * Now uses a searchpath from env. var $MFFPATH (default = $MFINPUTS). * 2.6 - pdc Fri. 1 Mar. 1991 * Wrapping up for export; removed some old junk code. * 2.7 - pdc Wed. 24 Apr. 1991 * output from -n is /bin/sh commands; -v output has # * 2.8 - pdc Tue. 21 May 1991 * Renamed mff. * Can understand sizes with "p" as decimal point. * -R option to retain GF file. * The input of an empty file hack is removed. * -S option to not parse size part [not needed before] * some support for MS-DOS filesystems (Adrian's Atari ST) */ #include /* MAXPATHLEN */ #include /* etc. */ #include #include /* isdigit */ #include /* to calculate magsteps */ #ifdef BSD # include /* O_RDONLY etc */ #else # include #endif #include "stdc.h" #include "xstdio.h" /* plus prototypes */ #include "config.h" #include "argloop.h" #include "fatal.h" #include "searchpath.h" #include "assoc.h" #include "fx.h" #include "strmisc.h" /* includes */ #include "magstep.h" #include "version.h" /* version number goes here */ #ifdef FANCY_MALLOC # include int mallopt ARGS((int, int)); int mallocmap ARGS((void)); #endif #ifndef MAXPATHLEN # define MAXPATHLEN 1024 #endif #define MAX_MF_LINE 512 #define MAX_BASENAME 256 #define MAX_TEMPLATE 1024 static const char what[] = MFF_VERSION #ifdef __STDC__ " (compiled " __TIME__ " " __DATE__ ")" #endif , *version = what + 5; #define TRUE 1 #define FALSE 0 /* GLOBAL VARIABLES */ static const char *mffpath, /* the MFFPATH environment variable */ suffix[] = ".mff"; int dryrun = FALSE; /* if true, don't really do anything */ int verbose = 0; /* print more messages */ int retain_gf = FALSE; /* don't (attempt to) remove GF file */ int no_parse = FALSE; /* don't parse size etc. at all */ static Assoclist mode_alist, /* given "300", return "mode:=CanonCX" */ weight_alist, /* given "m", return "weight:=1.25" */ cndnsd_alist, /* given "xc", return "hratio:=0.5" */ italic_alist, /* given "pi", return "slant:=1/5" */ extra_alist; /* Extra Alist for user's extra suffixes */ /* Strings set by -z, -w, -h, -i, -g, -p options -- or NULL */ static char *ovr_mode, *ovr_weight, *ovr_cndnsd, *ovr_italic, *ovr_extra, *ovr_driver, *ovr_pkpxl; struct float_node { struct float_node *next; float val; } *dpi_list, *atsize_list, *mag_list, *pxlfactor_list; static float design_sizeF = 10.0; /* design-size in pt */ static int pxlB = FALSE; /* If true, multiply PK suffix by */ /* |pxlfactorF| */ /* Templates */ static char *mf_template, *gfto_template, *tfm_template, *pk_template; static const char *mode, *driver, /* either |basename| or |ovr_driver| */ *weight, *cndnsd, *italic, *extra, /* - pdc Mon 16 Jul 1990 */ *unit, /* either "pt" or "mm" */ *pkpxl; /* set by -p */ static char basename[MAX_BASENAME]; struct eqn_node { struct eqn_node *next; char *val; } *eqns, *eqns2; /* set with -e and -E resp. */ /* Both of the following set their arg to NULL: */ #define DISPOSE(L) do { char *t_; \ while (L) { t_ = (char *)(L); (L) = (L)->next; xfree(t_); } \ } while (0) #define FREE_DISPOSE(L) do { struct eqn_node *t_; \ while (L) { t_ = (struct eqn_node *)(L); (L) = (L)->next; \ xfree((addr)(t_->val)); xfree((addr)t_); } \ } while (0) /* * The do .. while(0) idiom is intended to get around the fact * that a compound statement can't be followed by a semicolon * when it is the "then" part of an if-then-else. * * Unfortunately lint does not recognise this and produces * spurious error messages. */ #ifndef lint # define NEW(T) ((T *)xmalloc(sizeof(T))) #else # define NEW(T) ((T *)0) #endif /* * Simple template routine. * This is the bit that handles %-substitutions. * * The table of substitution strings only has entries for u&lc letters */ static const char *table[52]; /* * These two lines will need changing if using a character * encoding in which the letters aren't contiguous. */ #define lc_ord(X) ((X) - 'a') #define uc_ord(X) ((X) - 'A') /* * Set entry in table -- X is a character, Y is a char * */ #define lc_set(X,Y) table[lc_ord(X)] = (Y) #define uc_set(X,Y) table[uc_ord(X) + 26] = (Y) /* * Print string |format| into |buf|, making %-subs from |table|. * If a %f substitution is made, the global variable |f_sub| is * incremented. (What a hack!!) */ int f_sub = 0; char * template(buf, format) char *buf, *format; { char *b = buf; for (; *format; format++) if (*format != '%') *b++ = *format; else if (*++format == '%') /* % followed by %? */ *b++ = '%'; else if (isalpha(*format)) /* % followed by letter? */ { const char *sub; if (sub = table[islower(*format) ? lc_ord(*format) : uc_ord(*format) + 26]) { if (*format == 'f') f_sub++; while (*sub) *b++ = *sub++; } else fprintf(stderr, "Warning -- %%%c ignored.\n", *format); } else if (!*format) /* % at end of string? */ { fprintf(stderr, "Warning -- %% at end of string ignored.\n"); --format; } else /* % followed by non-letter */ fprintf(stderr, "Warning -- %%%c skipped.\n", *format); *b++ = '\0'; return buf; } /* * OPERATIONS ON FILES */ extern int unlink ARGS((const char *)); static inline int /* return nonzero iff file exists */ file_exists_p(s) const char *s; { int access ARGS((const char *name, int flags)); return (access((s), 0) >= 0); } static inline void /* errors are fatal */ xmkdir(s) const char *s; { if (mkdir(s, 0777) < 0) pfatalf("could not create directory %s", s); } /* * Rename file called "old" to be called "new". * Will copy to new name and unlink if "new" is on a different filesystem. * (This suggested by Andrew Haylett of * GEC-Marconi Research.) */ static inline int /* return <0 iff unsuccessful */ rename(old, new) const char *old, *new; { extern int link ARGS((const char *, const char *)); if (file_exists_p(old, 0) < 0) pfatalf("%s should exist but does not", old); else { unlink(new); /* ignore any error */ if (link(old, new) < 0) { if (errno != EXDEV) pfatalf("%s cannot be linked to %s", old, new); else { /* attempted to cross-link between devices; copy instead */ int read ARGS((int, void *, sizeof_t)); int write ARGS((int, void *, sizeof_t)); int open ARGS((const char *, int)); int creat ARGS((const char *, int)); int close ARGS((int)); int in, out; /* file descriptors */ char buf[2048]; /* a sort of beige */ int nbytes; /* byte count from read() */ if ((in = open(old, O_RDONLY)) < 0) pfatalf("can't read %s", old); if ((out = creat(new, 0666)) < 0) pfatalf("can't create %s", new); while ((nbytes = read(in, buf, sizeof buf)) > 0) if (write(out, buf, nbytes) != nbytes) pfatalf("could not write %d bytes to %s", nbytes, new); if (nbytes < 0) pfatalf("could not read from %s", old); close(in); close(out); } } if (unlink(old) < 0) pfatalf("%s cannot be removed", old); } return 0; } static void install_file(file, tplate, tplate2) char *file, *tplate, *tplate2; { char scratch[MAXPATHLEN]; f_sub = 0; (void)template(scratch, tplate); if (!f_sub) { char *t = scratch + strlen(scratch); if (t > scratch && *(t - 1) != dir_sep_ch) *t++ = dir_sep_ch; (void)template(t, tplate2); } if (dryrun) { printf("mv %s %s\n", file, scratch); return; } else if (rename(file, scratch) >= 0) return; if (errno != ENOENT) pfatalf("can't rename %s to %s", file, scratch); else { /* * On some brain-damaged systems, names of the form foo.96pk * can't be accomodated and so they are split into a directory/name * pair -- on MS-DOS systems, either say 96PK\FOO or FOO\96PK. * In this case, we attempt to create the missing directory part. * This stuff may also be necessary for USG systems with their * 14-character filenames; I don't know. * * Assume path in form "xxx/f/g" where f & g are * simple filenames and xxx is a path. * If s/f does not exist but xxx does, then create directory xxx/f * and try again. */ char *sl1 = (char *)0, *sl2 = (char *)0; /* positions of slashes */ { register char *s; for (s = scratch; *s; s++) if (isdirsep(*s)) sl2 = sl1, sl1 = s; } if (sl2 && (*sl1 = '\0', !file_exists_p(scratch)) && (*sl2 = '\0', file_exists_p(scratch))) { /* xxx must be a directory, else would have got ENOTDIR earlier */ /* Not that this matters: mkdir would simply fail anyway */ *sl2 = dir_sep_ch; /* "xxx/f" */ if (verbose > 1) printf("# creating directory %s\n", scratch); xmkdir(scratch); /* create xxx/f */ *sl1 = dir_sep_ch; /* now "xxx/f/g" again */ if (rename(file, scratch) < 0) { *sl1 = '\0'; pfatalf("created %s but can't rename %s to %s", scratch, file, sl1 + 1); } } else { *sl1 = *sl2 = dir_sep_ch; pfatalf("don't know how to install %s as %s", file, scratch); } } } static void remove_file(file) const char *file; { if (dryrun) printf("rm %s\n", file); else if (unlink(file) < 0) pfatalf(file); } /* * CREATE A FONT */ /* X is evaluated twice in fol. */ #define is_one_char_string(X,C) (*(X) == (C) && !(X)[1]) #define is_just_hyphen(X) is_one_char_string((X), '-') #define SEMI() (*s++ = ';', *s++ = ' ') #define APPEND(S) for (e = (S); *e; *s++ = *e++) ; #define ADDEQN(E) if (E) { APPEND(E); SEMI(); } static char * mf_script(mode_name, magF) char *mode_name; double magF; { char *s; const char *e; static char the_line[MAX_MF_LINE]; struct eqn_node *p; if (no_parse) { sprintf(the_line, "\\%s = %s; mag = %g; ", (mode_name[0] == '"' ? "smode" : "mode"), mode_name, magF); s = the_line + strlen(the_line); } else { sprintf(the_line, "\\%s = %s; mag = %g; font_size %g%s#; ", (mode_name[0] == '"' ? "smode" : "mode"), mode_name, magF, design_sizeF, unit); s = the_line + strlen(the_line); ADDEQN(weight); ADDEQN(cndnsd); ADDEQN(italic); ADDEQN(extra); } for (p = eqns; p; p = p->next) ADDEQN(p->val); if (driver) { APPEND("input "); APPEND(driver); SEMI(); } for (p = eqns2; p; p = p->next) ADDEQN(p->val); APPEND("bye."); *s = '\0'; return the_line; } #define int_round(X) ((int)((X) + 0.5)) static void do_one_font(font_name, mode_name, dpiF, magF, pxlfactorF) char *font_name; double dpiF, magF, pxlfactorF; char *mode_name; { static char kdpiS[20], dpiS[20], mdpiS[20], scaleS[20], factorS[20]; /* These are static purely for the sake of -D */ char tfname[MAX_BASENAME]; /* place to generate non-directory filenames */ char scratch[MAX_TEMPLATE]; if (verbose) printf("#{{{ %s %s %g %g %g\n", font_name, mode_name, dpiF, magF, pxlfactorF); if (pxlB) sprintf(kdpiS, "%d", int_round(pxlfactorF * magF * dpiF)); else sprintf(kdpiS, "%d", int_round(magF * dpiF)); sprintf( dpiS, "%d", int_round( dpiF )); sprintf( mdpiS, "%d", int_round( magF * dpiF )); sprintf( scaleS, "%d", int_round( magF * 1000.0 )); sprintf( factorS, "%d", int_round( pxlfactorF * 1000.0 )); lc_set('b', basename); uc_set('D', mdpiS); /* magnified DPI, yes? */ lc_set('d', dpiS); lc_set('f', font_name); lc_set('g', driver); lc_set('k', factorS); /* pxl/gf ratio, times 1000 */ lc_set('n', kdpiS); lc_set('p', pkpxl); lc_set('s', scaleS); lc_set('u', unit); lc_set('z', mode_name); if (mf_template) { if (dryrun) printf("%s '%s'\n", template(scratch, mf_template), mf_script(mode_name, magF)); else { char *t = mf_script(mode_name, magF); fx(template(scratch, mf_template), 1, &t); } if (tfm_template) { sprintf(tfname, "%s.tfm", driver); install_file(tfname, tfm_template, "%f.tfm"); } } if (gfto_template) { char gffilename[MAX_BASENAME], *gfargs[2]; #ifdef SHORT_FILENAMES /* * On systems which truncate the part of a name follwoing a "." * to something unreasonably short, the files FOO.300PK and FOO.300GF * may not be distinct. Usually there will be some convention * for splitting names into a directory+filename -- FOOBAR12/300PK, * for example. * I am assuming that GFtoPK will be willing to produce * its output as FOO.PK. This is then renamed as per the * PK file template (-P option). */ char pkfilename[10]; sprintf(gffilename, "%s.%sgf", driver, mdpiS); sprintf(pkfilename, "%s.%s", driver, pkpxl); if (dryrun) printf("%s %s %s\n", template(scratch, gfto_template), gffilename, pkfilename); else { gfargs[0] = gffilename, gfargs[1] = pkfilename; fx(template(scratch, gfto_template), 2, gfargs); } if (pk_template) { static char tp[] = "%f?%n%p"; tp[2] = dir_sep_ch; install_file(pkfilename, pk_template, tp); } #else /* !SHORT_NAMES: */ sprintf(gffilename, "%s.%sgf", driver, mdpiS); if (dryrun) printf("%s %s\n", template(scratch, gfto_template), gffilename); else { gfargs[0] = gffilename; fx(template(scratch, gfto_template), 1, gfargs); } if (pk_template) { sprintf(tfname, "%s.%s%s", driver, kdpiS, pkpxl); install_file(tfname, pk_template, "%f.%n%p"); } #endif /* SHORT_NAMES */ if (!retain_gf) remove_file(gffilename); } if (verbose) printf("#}}}\n"); } /* * Set up everything (italicness etc etc) for one font name and * make fonts. This is where the parsing is done. */ static void do_font(fn) const char *fn; { struct float_node *pxl, *mag, *dpi; static double ascii2float ARGS((const char *s)); const char *p, *q; /* pointers into |fn| */ char *b; /* pointer into |basename| or |canonical| */ char *canonical; /* font name with any hyphen dealt with */ int had_decimal = FALSE; /* TRUE when had P or m representing a decimal point. */ char size_scratch[20]; /* size suffix copied into here */ /* * Find the design_size: */ for (p = fn; *p; ++p) ; /* p now points to the NUL at the end of fn */ if (p == fn) syntaxf("font name is empty"); canonical = (char *)xmalloc(p - fn + 1); q = p; /* get ready to scan from end of |fn| */ if (!no_parse) { /* * scan the SIZE part. * |b| points to last character added to size_scratch array * |q| points after first unscanned char in fn * For the sake of ANSI I avoid having |q| point to left of |fn|. * * A `p' is recognized as a decimal point iff there is a digit on * side of it -- 10p5 but not 10p or p5. * An `m' is recognized as a decimal point iff there is a digit * to its left -- 10m5 or 10m but not m5. * Note that this means that when using my Malvern font nmames, * neither "m" nor "p" can be used as encoding codes. That's life. */ b = size_scratch + sizeof size_scratch; *--b = '\0'; unit = "pt"; while (1) { if (verbose > 3) printf("## <<%s>> <<%s>> <<%s>>\n", fn, q, b); if (b == size_scratch || q == fn) break; else if (!had_decimal && ((*(q - 1) == 'p' && *b) /* (*b != 0) means at least 1 digit */ || *(q - 1) == 'm') && q > fn + 1 && isdigit(*(q - 2))) { had_decimal = TRUE; *--b = '.', --q; unit = (*q == 'p' ? "pt" : "mm"); } else if (isdigit(*(q - 1))) *--b = *--q; else break; } if (q != p) design_sizeF = ascii2float(b); if (verbose > 1) printf("# design size: %g%s\n", design_sizeF, unit); /* * If the at-size list is non-empty, then construct a * mag list from this list and the current design size: */ if (atsize_list) { struct float_node *t = mag_list = atsize_list; atsize_list = (struct float_node *)0; if (verbose) printf("# calculating mag(s) from at-size(s)...\n"); for (; t; t = t->next) t->val = stepmag(t->val / design_sizeF); } extra = assoc_match_backwards(extra_alist, fn, &q); italic = assoc_match_backwards(italic_alist, fn, &q); cndnsd = assoc_match_backwards(cndnsd_alist, fn, &q); weight = assoc_match_backwards(weight_alist, fn, &q); /* * Having parsed the part, all that's left is * the basic font name. */ } /* Canonical version of font name */ if (q > fn && *(q - 1) == '-') q--; /* drop hyphen at end of basename */ for (b = canonical, p = fn; p < q; *b++ = *p++) ; /* copy up to before q */ if (*p == '-') p++; /* skip hyphen */ while (*b++ = *p++) ; /* copy suffix + size parts including */ /* final null */ /* Make copy of basename */ for (b = basename, p = fn; p < q; *b++ = *p++) ; *b = '\0'; if (verbose > 1) printf("# full font name: \"%s\"\n# basic font name: \"%s\"\n", canonical, basename); /* * Now allow values set by suer to over-ride derived values: */ if (ovr_weight) weight = ovr_weight; if (ovr_cndnsd) cndnsd = ovr_cndnsd; if (ovr_italic) italic = ovr_italic; if (ovr_extra) extra = ovr_extra; if (ovr_driver) { if (is_just_hyphen(ovr_driver)) driver = (char *)NULL; /* `-g -' cancels driver altogether */ else driver = ovr_driver; } else driver = basename; if (ovr_pkpxl) pkpxl = ovr_pkpxl; else pkpxl = (pxlB ? "pxl" : "pk"); for (pxl = pxlfactor_list; pxl; pxl = pxl->next) { if (pxlB && verbose > 2) printf("# - factor: %g\n", pxl->val); for (mag = mag_list; mag; mag = mag->next) { if (verbose > 2) printf("# - mag: %g\n", mag->val); for (dpi = dpi_list; dpi; dpi = dpi->next) { if (ovr_mode) do_one_font(canonical, ovr_mode, dpi->val, mag->val, pxl->val); else { char scratch[20]; sprintf(scratch, "%d", int_round(dpi->val)); mode = assoc(mode_alist, scratch); if (verbose > 2) printf("# - mode_def: \"%s\"\n", mode); do_one_font(canonical, mode, dpi->val, mag->val, pxl->val); } } } } } /* FUNCTIONS FOR PROCESSING COMMAND-LINE ARGUMENTS */ /* Lookup table for variables changed by options */ typedef union { struct float_node *floats; struct eqn_node *eqns; char *string; int bool; Assoclist alist; } Param; static Param *opt_obj[] = { (Param *)&atsize_list, /* -a ATSIZEs */ 0, (Param *)&ovr_cndnsd, /* -c MF-EQN */ (Param *)&dpi_list, /* -d DPIs */ (Param *)&eqns, /* -e MF-EQN */ 0, /* -f FILE */ (Param *)&ovr_driver, /* -g DRIVER */ 0, (Param *)&ovr_italic, /* -i MF-EQN */ (Param *)&ovr_extra, /* -j MF-EQN */ (Param *)&pxlfactor_list, /* -k FACTORs */ 0, (Param *)&mag_list, /* -m MAGSTEPs */ (Param *)&dryrun, /* -n, +n */ 0, (Param *)&ovr_pkpxl, /* -p PIXEL */ 0, 0, (Param *)&mag_list, /* -s SCALED */ 0, 0, (Param *)&verbose, /* -v, +v */ (Param *)&ovr_weight, /* -w MF-EQNS */ (Param *)&pxlB, /* -x, +x */ 0, (Param *)&ovr_mode, /* -z MODE */ 0, 0, (Param *)&cndnsd_alist, /* -C SUFFIX=MF-EQN */ 0, (Param *)&eqns2, /* -E MF-EQN */ 0, (Param *)&gfto_template, /* -G TEMPLATE */ 0, (Param *)&italic_alist, /* -I SUFFIX=MF-EQN */ (Param *)&extra_alist, /* -J SUFFIX=MF-EQN */ 0, 0, (Param *)&mf_template, /* -M TEMPLATE */ 0, 0, (Param *)&pk_template, /* -P TEMPLATE */ 0, (Param *)&retain_gf, /* -R */ (Param *)&no_parse, /* -S */ (Param *)&tfm_template, /* -T TEMPLATE */ 0, 0, (Param *)&weight_alist, /* -W SUFFIX=MFEQN */ 0, 0, (Param *)&mode_alist /* -Z DPI=MODE */ }; #define PARAM(C) \ (opt_obj[isupper(C) ? uc_ord(C) + 26 : lc_ord(C)]) static char optstring[] = "a:C:c:Dd:e:E:f:G:g:hI:i:J:j:k:M:m:nP:p:R\ s:ST:VvW:w:xZ:z:?"; /* enough to be getting on with... */ static Argloop_options opts; static void usage(c) int c; { syntaxf("Don't understand flag -%c.\n\ USAGE:\t%s [ OPTION ... ] FONT ...\n\ \t%s -V | -h\n\ FONT is a TeX font name, such as \"cmbx12\".\n\ For a list of options, use `%s -h'", c, progname, progname, progname, progname); } /* before and after are brackets, first is format string for first element * and rest is format string for each of the rest. * * This has to be a macro, because it accesses the basetype of the list */ #define LIST_PRINT(T, ll, before, first, rest, after)\ { T *l = ll; \ fputs(before, stdout);\ if (l)\ {\ printf(first, l->val);\ for (l = l->next; l; l = l->next)\ printf(rest, l->val);\ }\ fputs(after, stdout);\ } #define TEMPLATE_PRINT(S, T, U) \ if (T) printf(S, T); else puts(U); static void do_flag(c, val) int c; int val; { Param *p = PARAM(c); switch (c) { case '?': case 'h': printf( "%s [ OPTION ... FONT ] ... \n\ %s -V | -h\n\ \nFONT is a TeX font name, such as \"cmbx12\".\n\ \nOPTIONS\n\ -V (version) -h (help) -D (dump tables) -v (verbose) -R (retain GF)\n\ -x (pXl) or +x (pk) -k FACTOR(S) -p PKSUFFIX\n\ -a ATSIZE(S) or -s SCALED(S) or -m MAGSTEP(S)\n\ -d DPI(S) -e EQN -E EQN\n\ \nTEMPLATES\n\ -M METAFONT -G GFtoPK -T TFMdirectory -P PKdirectory\n\ \t%%k = factor*1000 %%s = scaled*1000 %%p = pk or pxl\n\ \t%%d = dpi %%D = scaled*dpi %%n = scaled*factor*dpi\n\ \t%%z = mode %%b = basename %%g = driver %%f = font\n\ \nTABLES\n\ -W STRING=EQN -C STRING=EQN -I STRING=EQN -J STRING=EQN \ -Z DPI=MODENAME\n\ -w EQN -c EQN -i EQN -j EQN -z MODENAME\n\ ", progname, progname); break; case 'V': printf("%s\n", version); break; case 'D': printf("Z = "); assoc_print(mode_alist); if (ovr_mode) printf("overridden by \"%s\".\n", ovr_mode); printf("W = "); assoc_print(weight_alist); if (ovr_weight) printf("overridden by \"%s\".\n", ovr_weight); printf("C = "); assoc_print(cndnsd_alist); if (ovr_cndnsd) printf("overridden by \"%s\".\n", ovr_cndnsd); printf("I = "); assoc_print(italic_alist); if (ovr_italic) printf("overridden by \"%s\".\n", ovr_italic); printf("J = "); assoc_print(extra_alist); if (ovr_extra) printf("overridden by \"%s\".\n", ovr_extra); { int i; printf("template = {"); for (i = 0; i < 52; i++) if (table[i]) printf("\n\t%%%c\t%s", (i < 26 ? i + 'a' : i + 'A' - 26), /* assumes ASCII */ table[i]); printf(" }\n"); } if (eqns) LIST_PRINT(struct eqn_node, eqns, "-e ", "%s", "; %s", ".\n"); if (eqns2) LIST_PRINT(struct eqn_node, eqns2,"-E ", "%s", "; %s", ".\n"); LIST_PRINT(struct float_node, dpi_list, "DPI(s) = ", "%g", ", %g", ".\n"); LIST_PRINT(struct float_node, mag_list, "mag(s) = ", "%g", ", %g", ".\n"); if (atsize_list) LIST_PRINT(struct float_node, atsize_list, "atsize(s)= ", "%g", ", %g", ".\n"); if (pxlB) LIST_PRINT(struct float_node, pxlfactor_list, "factor(s)= ", "%g", ", %g", ".\n"); TEMPLATE_PRINT("METAFONT = \"%s\"\n", mf_template, "don't METAFONT"); TEMPLATE_PRINT("GFtoPK = \"%s\"\n", gfto_template, "don't GFtoPK"); TEMPLATE_PRINT("TFM dir. = \"%s\"\n", tfm_template, "no TFM directory"); TEMPLATE_PRINT("PK dir. = \"%s\"\n", pk_template, "no PK directory"); break; case 'n': case 'x': case 'R': case 'S': p->bool = val; break; case 'v': /* -vv is more verbose than -v */ if (val) verbose++; else verbose = 0; break; /* * Set the default value for certain flagged arguments: * (+F type flags) */ case 'd': DISPOSE(dpi_list); dpi_list = NEW(struct float_node); dpi_list->next = (struct float_node *)0; dpi_list->val = 200.0; /* only one defined in standard plain.mf */ break; case 'Z': case 'W': case 'C': case 'I': case 'J': p->alist = assoc_destroylist(p->alist); if (c == 'Z') { mode_alist = assoc_add(mode_alist, strdup("200=lowres")); } break; case 'z': case 'w': case 'c': case 'i': case 'j': case 'p': case 'g': case 'M': case 'G': case 'T': case 'P': if (p->string) xfree(p->string); switch (c) { case 'M': mf_template = strdup("mf"); break; case 'G': gfto_template = strdup("gfto%p"); break; case 'T': tfm_template = strdup("%f.tfm"); break; case 'P': pk_template = strdup("%f.%n%p"); break; default: p->string = (char *)NULL; break; } break; case 'e': case 'E': FREE_DISPOSE(p->eqns); break; case 'k': DISPOSE(pxlfactor_list); pxlfactor_list = NEW(struct float_node); pxlfactor_list->next = (struct float_node *)0; pxlfactor_list->val = 5.0; break; case 'a': case 's': case 'm': DISPOSE(mag_list); DISPOSE(atsize_list); mag_list = NEW(struct float_node); mag_list->next = (struct float_node *)0; mag_list->val = 1.0; break; default: syntaxf("Unexpected %c%c!", (val ? '-' : '+'), c); } } /* * This func is used below when changing comma-separated list into * a float_node list. */ static inline void build_list(p, s, f) register struct float_node **p; const char *s; register double (*f) ARGS((const char *)); { struct float_node *cur = 0; char *state, *t; DISPOSE(*p); s = stritem(t = strdup(s), ',', &state); /* s is now 1st item */ if (!s) errorf("warning: \"%s\" denotes the empty list", t); for (; s; s = stritem((char *)NULL, ',', &state)) { if (cur) cur = cur->next = NEW(struct float_node); else cur = *p = NEW(struct float_node); cur->val = f(s); } if (cur) cur->next = 0; xfree((char *)t); } static double ascii2float(s) /* this version complains if invalid */ const char *s; { const char *t = s; double f = 0.0, denom = 10; register int i = 0; if (!s || !*s) syntaxf("the empty string is not a valid number"); if (*s == '-') return -ascii2float(s + 1); /* This bit done in integer arithmetic for efficiency's sake: */ while (*s && isdigit(*s)) i = 10 * i + (*s++ - '0'); f = i; if (*s == '.') while (*++s && isdigit(*s)) f += (*s - '0')/denom, denom *= 10.0; if (*s) syntaxf("%s: invalid number", t); return f; } static double ascii2scale(s) const char *s; { register float f = ascii2float(s); /* aborts if invalid */ if (f <= 0.0) syntaxf("%g: invalid scale (must be positive floating-point number)", f); if (f > 100.0) return f/1000.0; return f; } static double ascii2magstep(s) const char *s; { if (*s == '-') return 1/ascii2magstep(s + 1); if (*s == 'h' && !s[1]) return MAGSTEPHALF; if (!isdigit(*s) || s[1]) syntaxf("%s: invalid magstep (must be h, -h or small integer)", s); return magstep(*s - '0'); } static void do_arg(c, s) /* Process one flagged argument */ int c; /* flag character */ const char *s; /* argument */ { Param *p = PARAM(c); switch (c) { /* * Options that store a string allow the special argument * `-' to set its value to (char *)NULL, meaning don't use it: */ case 'p': case 'z': case 'w': case 'c': case 'i': case 'j': /* - pdc Mon 16 Jul 1990 */ case 'M': case 'G': case 'T': case 'P': if (p->string) xfree(p->string); p->string = (!s || is_just_hyphen(s) ? (char *)NULL : strdup(s)); break; /* * ... except -g, which has to distinguish between NO driver * file (-g -, ovr_driver = "-"), DEFAULT driver file (+g, * ovr_driver = null) and SPECIAL driver file (-g string): */ case 'g': if (ovr_driver) xfree(ovr_driver); ovr_driver = strdup(s); /* ...even if s is just a hyphen */ break; case 'd': case 'a': build_list(&p->floats, s, ascii2float); break; case 's': DISPOSE(atsize_list); /* FALL THROUGH */ case 'k': build_list(&p->floats, s, ascii2scale); break; case 'm': DISPOSE(atsize_list); build_list(&p->floats, s, ascii2magstep); break; /* Add items to an alist: */ case 'Z': case 'W': case 'C': case 'I': case 'J': if (!s) syntaxf("expected argument after -%c", c); p->alist = assoc_add(p->alist, strdup(s)); break; /* Add strings to a list: */ case 'e': case 'E': if (!s || is_just_hyphen(s)) FREE_DISPOSE(p->eqns); else { struct eqn_node *new = NEW(struct eqn_node); new->val = strdup(s); new->next = p->eqns; p->eqns = new; } break; case 'f': /* read from file of commands */ { char scratch[MAXPATHLEN]; if (!s || is_just_hyphen(s)) { clearerr(stdin); argloop(al_file(stdin), &opts); } else if (!findfile(scratch, s, mffpath, suffix)) pfatalf("can't find command file \"%s\"", s); else argloop(al_file(fopen(scratch, "r")), &opts); } break; case '\0': do_font(s); break; default: syntaxf("Unexpected -%c %s!", c, s); } } int main(argc, argv) int argc; const char **argv; { const char *getenv(); char *t; #ifdef FANCY_MALLOC mallopt(M_MXFAST, 32); /* blocks up to 32 bytes allocated in batches */ #endif if (!(mffpath = getenv("MFFPATH"))) mffpath = "."; opts.optstring = optstring; opts.flag = do_flag; opts.arg = do_arg; opts.error = usage; argloop(al_string(t = strdup("+ZMGTPxdks")), &opts); xfree(t); /* Set the default value for all except */ /* things must have NULL as default */ #ifdef NO_ARGV0 argloop_init_files("mff", &opts); #else argloop_init_files(argv[0], &opts); #endif argloop(al_argv(argc, argv), &opts); return 0; }