/*
 * Copyright 1/5/95 Zhishun Alex Liu.  All Rights Reserved.
 *
 ***************************
 * WORK IN PROGRESS
 ***************************
 *
 * Written by Zhishun Alex Liu under direction of Professor William Kahan.
 * The author's current electronic mail address as of December 1994 is
 * Alex.Liu@Eng.Sun.COM.
 *
 * Use of this software is granted with the understanding that all recipients
 * should regard themselves as participants in an ongoing research project and
 * hence should feel obligated to report their experiences (good or bad) with
 * this elementary functions test suite to the author.
 */
#ifndef lint
static char sccsid[] = "@(#)phase1.c	1.1 (BeEF) 1/5/95";
#endif
#include <stdio.h>
#include <ctype.h>
#ifdef __STDC__
# include <string.h>
  extern int atoi(const char *);
  extern void exit(int);
# if defined(__unix) || defined(unix) || defined(__TURBOC__)
   extern int isatty(int);		/* POSIX has isatty(.) */
# endif
#else
  extern char *strcpy(), *strcat();
  extern int atoi(), isatty(), strcmp(), strlen();
  extern void exit();
#endif

#define	STRSIZE	128
#define	BUFSIZE	(4 * STRSIZE)
#define	SSTRSIZ	4
#define	MSTRSIZ	16
#define	LSTRSIZ	STRSIZE
#define	LOCAL_H	"local.h"
#define	SYS_TIMES_H "/usr/include/sys/times.h"
#define	RESOURCE_H "/usr/include/sys/resource.h"
#define	TABOVER(S) (void) fprintf(stderr, "%.*s", 8 - strlen(S) % 8, "        ");
#define	NUL '\0'
#define	COMMA ','
#define	PERIOD '.'

#define	isno(s) (s[0] == 'N' || s[0] == 'n')
#define	isyes(s) (s[0] == 'Y' || s[0] == 'y')

static char *prog;

static void
#ifdef __STDC__
ask_for(int ask, FILE *ifp, char **Qs, char *a, int la, int lvl)
#else
ask_for(ask, ifp, Qs, a, la, lvl)
int ask;
FILE *ifp;
char **Qs, *a;
int la, lvl;
#endif
{
	int i;
	char s[BUFSIZE], *sp, *tp;

	if (ask) {
		if (lvl <= 0 && *Qs)
			(void) fprintf(stderr, "\n");
		while (*Qs) {
			for (i = 0; i < lvl; i++)
				TABOVER("")
			(void) fprintf(stderr, "%s\n", *Qs++);
		}
		if (lvl < 0)
			return;
		for (i = 0; i <= lvl; i++)
			TABOVER("")
		(void) fprintf(stderr, "[%s] ", a);
	}
	else if (lvl < 0) {
		return;
	}
	if ((sp = fgets(s, BUFSIZE, ifp)) == (char *) 0) {
		(void) fprintf(stderr, "%s: EOF encountered, why?\n", prog);
		(void) exit(1);
	}
	while (*sp != NUL && isspace(*sp))
		sp++;
	if (*sp != NUL) {
		tp = sp;
		while (*tp != NUL)
			tp++;
		while (tp > sp && isspace(tp[-1]))
			tp--;
		*tp = NUL;
		if (strlen(sp) > la - 1) {
			(void) fprintf(stderr,
				"%s: text string \"%s\" longer than %d.  %s\n",
				prog, sp, la - 1, "Truncated.");
			sp[la - 1] = NUL;
		}
		(void) strcpy(a, sp);
	}
}

static void
#ifdef __STDC__
crank(FILE *fp, char *token, char *param, char *value)
#else
crank(fp, token, param, value)
FILE *fp;
char *token, *param, *value;
#endif
{
	(void) fprintf(fp, "#ifndef\t%s\n", token);
	(void) fprintf(fp, "#define\t%s%s\t%s\n", token, param, value);
	(void) fprintf(fp, "#endif\n");
}

static void
#ifdef __STDC__
crankfn(FILE *fp, char *suffix, char *v1, char *v2)
#else
crankfn(fp, suffix, v1, v2)
FILE *fp;
char *suffix, *v1, *v2;
#endif
{
	(void) fprintf(fp, "#if TEST_%s\n", suffix);
	(void) fprintf(fp, "#ifndef\tFUNC_%s\n", suffix);
	(void) fprintf(fp, "#define\tFUNC_%s(y, x)\t%s\n", suffix, v1);
	(void) fprintf(fp, "#define\tDECL_%s\t%s\n", suffix, v2);
	(void) fprintf(fp, "DECL_%s\n", suffix);
	(void) fprintf(fp, "#endif\n");
	(void) fprintf(fp, "#endif\t/* TEST_%s */\n", suffix);
}

static char *func_xxx[] = {
#define	NDX_SIN	0
	"sin",
#define	NDX_COS	1
	"cos",
	"atan",
	"exp",
	"expm1",
	"log",
	"log1p",
};
#define	NF	(sizeof(func_xxx) / sizeof(func_xxx[0]))

static char *cpp_xxx[] = {
	"SIN", "COS", "ATN", "EXP", "EM1", "LOG", "L1P",
};

static char q[BUFSIZE];

static char *Q_long[] = {
	"Name an integral data type supporting integers at least 32 bits wide?",
	(char *) 0,
};
static char A_long[MSTRSIZ];
static char S_long[] = "Integral data type supporting 32-bit integers";

static char *Q_generic[] = {
	"What floating-point data type (referred to as \"GENERIC\" below) will",
	"be used for arguments and results of the functions to be tested?",
	"For instance, IEEE 754 Single and VAX F_floating usually correspond",
	"to the \"float\" data type in C; IEEE 754 Double, VAX D_floating and",
	"G_floating correspond to \"double\"; IEEE 754 Double-extended corresponds",
	"to either \"long double\" in ANSI/ISO C or \"extended\" in Apple SANE.",
	(char *) 0,
} ;
static char A_generic[MSTRSIZ];
static char S_generic[] = "Floating-point data type (GENERIC) of interest";

static char *Q_prtype[] = {
	"What floating-point data type other than \"GENERIC\" may printf(.)",
	"require to get a floating-point number of type \"GENERIC\" printed?",
	(char *) 0,
};
static char A_prtype[MSTRSIZ];
static char S_prtype[] = "Data type for passing GENERIC to printf()";

static char *Q_prffmt[] = {
	"How does a variable width/precision %*.*f format number x get printed?",
	"Please show a complete C statement using \"w\" for width, \"p\" for precision",
	"and \"x\" for the number to be printed.  For instance, to get a ANSI/ISO C",
	"\"long double\" number printed in a fixed-point format, you say",
	"        (void) printf(\"%*.*Lf\", w, p, x);",
	(char *) 0,
};
static char A_prffmt[LSTRSIZ];
static char S_prffmt[] = "Print GENERIC (fixed-point)";

static char *Q_prefmt[] = {
	"How does a variable width/precision %*.*e format number x get printed?",
	"Please show a complete C statement using \"w\" for width, \"p\" for precision",
	"and \"x\" for the number to be printed.  For instance, to get a ANSI/ISO C",
	"\"long double\" number printed in a floating-point format, you say",
	"        (void) printf(\"%*.*Le\", w, p, x);",
	(char *) 0,
};
static char A_prefmt[LSTRSIZ];
static char S_prefmt[] = "Print GENERIC (floating-point)";

static char *Q_bits_m[] = {
	"What is the width in bits of the exponent field of a floating-point number",
	"of type \"GENERIC\"?  For instance, IEEE 754 Single exponents have width 8, ",
	"Double 11 and Double-extended 15.  On a VAX, F_floating/D_floating exponents",
	"have width 8, G_floating 11 and H_floating 15.",
	(char *) 0,
};
static char A_bits_m[SSTRSIZ];
static char S_bits_m[] = "Width of exponent field in GENERIC";

static char *Q_guard_against_overflow[] = {
	"Should our code guard against floating-point overflows?  As a rule of",
	"thumb, machines that implement fully IEEE 754 deserve the answer \"no\";",
	"but for VAX-like architectures that trap/fault on floating-point overflows, ",
	"you should answer \"yes\".",
	(char *) 0,
};
static char A_guard_against_overflow[SSTRSIZ];
static char S_guard_against_overflow[] = "Guard against floating-point overflow";

static char *Q_ghuge[] = {
	q,
	(char *) 0,
};
static char A_ghuge[LSTRSIZ];
static char S_ghuge[] = "Approx. largest finite GENERIC";

static char *Q_bsd_rusage[] = {
	"Does your machine run a version of BSD-flavored UN*X operating system?",
	"Specifically, is the system call getrusage() provided in a BSD-compatible",
	"manner?  If you're not certain, answer \"no\".",
	(char *) 0,
};
static char A_bsd_rusage[SSTRSIZ];
static char S_bsd_rusage[] = "BSD-flavored UN*X Operating System";

static char *Q_posix_times[] = {
	"Does your operating system support the POSIX.1 times() timing routine?",
	" If you're not certain, answer \"no\".",
	(char *) 0,
};
static char A_posix_times[SSTRSIZ];
static char S_posix_times[] = "POSIX.1 times() available";

static char *Q_machine_pi[] = {
	"If you intend to test \"sin\", \"cos\" or \"atan\", is the trig. argument",
	"reduction performed against a finite-precision (\"machine\") pi?",
	"If you think your software knows as many bits of true pi as it needs",
	"(and some software on VAXen, Suns and MIPS machines do know that), or",
	"if you're not certain, answer \"no\".",
	(char *) 0,
};
static char A_machine_pi[SSTRSIZ];
static char S_machine_pi[] = "Finite-precision \"machine\" pi in use";

static char *Q_intro_mpi[] = {
	"Please supply the finite-precision (\"machine\") value of pi/2 as a string",
	"of hexadecimal digits, naturally with a leading \"1.\" where the period is",
	"the hexadecimal point, followed by the hexadecimal representation of the",
	"fractional part of pi/2 in 20-bit (5 hex-digit) chunks delimited by commas.",
	"Due to the complexity of this question, a number of more popular answers are",
	"presented below as multiple choices.  Just select the desired item by its",
	"sequence number.",
	(char *) 0,
};

static struct {
	char *bits;
	char *hex_digits;
	char *comments;
} mpi2_p[] = {
    {"???", "User will supply the hex-digit string", "good luck!"},
     {"53", "1.921fb, 54442, d1800", "VAX G_floating or IEEE Double"},
     {"56", "1.921fb, 54442, d1840", "VAX D_floating"},
     {"64", "1.921fb, 54442, d1846, a0000", "IEEE Double-extended"},
     {"66", "1.921fb, 54442, d1846, 98000", "some 68881/2 or 80x87 software"},
     {"74", "1.921fb, 54442, d1846, 98980", "perhaps the MIPS or HP/UX math library"},
    {"113", "1.921fb, 54442, d1846, 9898c, c5170, 1b800", "VAX H_floating"},
};
#define	NA (sizeof(mpi2_p) / sizeof(mpi2_p[0]))

static char *Q_mpi2_p[] = {
	(char *) 0,
};
static char A_mpi2_p[SSTRSIZ];
static char A_hex_digits[LSTRSIZ];
static char S_hex_digits[] = "Value of \"machine\" pi in hex.";

static char *Q_intro_xxx[] = {
	"The following elementary functions can be tested if you so desire:",
	q,
	"For each of them in turn, you will be asked if you want to test it.",
	"If you answer \"yes\" you must answer two further questions concerning",
	"the calling sequences and the necessary declarations, answers to both must",
	"also be in the form of complete C statements.  If you wish to test either of",
	"sine and cosine, both must be selected.",
#ifdef __STDC__
	"",
	"Your compiler appears to support ANSI/ISO C; prototypes are required to",
	"pass arguments of type GENERIC unless GENERIC is merely of type \"double\".",
	"You will be prompted to confirm these prototypes.",
#endif
	(char *) 0,
};

static char *Q_test_xxx[] = {
	q,
	(char *) 0,
};
static char A_test_xxx[NF][SSTRSIZ];

static char *Q_func_xxx[] = {
	q,
	(char *) 0,
};
static char A_func_xxx[NF][LSTRSIZ];

static char *Q_cpp_xxx[] = {
	q,
	(char *) 0,
};
static char A_cpp_xxx[NF][LSTRSIZ];

/*
 * names of user-supplied functions as derived heuristically from A_func_xxx
 */
static char U_func_xxx[NF][LSTRSIZ];

static char *Q_happy[] = {
	"Are you happy with the present configuration?  Answer yes or no explicitly.",
	(char *) 0,
};
static char A_happy[SSTRSIZ];

static char *Q_intro_next[] = {
	"A C language header file named \"local.h\" has been prepared for you in the",
	"current working directory.  You can now safely proceed to the next step:",
	"building an executable module.  Good luck.",
	(char *) 0,
};

/*
 * guessname(.) recognizes exactly two expressions:
 *	y = FOO(x);
 *	y = FOO(&x);
 * For the former, *refp is set to 0; the latter, 1.
 * When recognized, the string "FOO" is returned; otherwise
 * (char *) 0 is returned and *refp is undefined.
 */
static char *
#ifdef __STDC__
guessname(const char *foo, int *refp)
#else
guessname(foo, refp)
char *foo;
int *refp;
#endif
{
#ifdef __STDC__
	const
#endif
	char *tp;
	static char s[LSTRSIZ];
	char *sp = s;

#define	SKIPSPACE(bar)	while (*(bar) != NUL && isspace(*(bar))) {(bar)++;}
	SKIPSPACE(foo)
	if (*foo++ != 'y') {
		return (char *) 0;
	}
	SKIPSPACE(foo)
	if (*foo++ != '=') {
		return (char *) 0;
	}
	SKIPSPACE(foo)
	tp = foo;
	if (*tp != '_' && !isalpha(*tp)) {
		return (char *) 0;
	}
	tp++;
	while (*tp != NUL && (*tp == '_' || isalnum(*tp))) {
		tp++;
	}
	if (*tp != '(' || tp == foo) {		/* ')' */
		return (char *) 0;
	}
	while (foo < tp) {
		*sp++ = *foo++;
	}
	*sp = NUL;
	foo++;
	SKIPSPACE(foo)
	if (*foo == '&') {
		*refp = 1;
		foo++;
	}
	else {
		*refp = 0;
	}
	SKIPSPACE(foo)
	if (*foo++ != 'x') {
		return (char *) 0;
	}
	SKIPSPACE(foo)
/* ( */	if (*foo++ != ')') {
		return (char *) 0;
	}
	SKIPSPACE(foo)
	if (*foo != ';') {
		return (char *) 0;
	}
	return s;
#undef SKIPSPACE
}

static void
#ifdef __STDC__
collect(int ask, int pass, FILE *ifp)
#else
collect(ask, pass, ifp)
int ask, pass;
FILE *ifp;
#endif
{
	int i, yes, ldbl, flt;
	char *sp;
	FILE *tfp;

	if (!pass)
		(void) strcpy(A_long, "long");
	ask_for(ask, ifp, Q_long, A_long, sizeof(A_long), 0);

	if (!pass)
		(void) strcpy(A_generic, "double");
	ask_for(ask, ifp, Q_generic, A_generic, sizeof(A_generic), 0);

#ifdef __STDC__
	if((ldbl = !strcmp(A_generic, "long double")) == 1 &&
		sizeof(long double) > sizeof(double)) {
		ldbl++;
	}
#else
	ldbl = 0;
#endif
	flt = !strcmp(A_generic, "float");
	if (!pass)
		(void) strcpy(A_prtype, ldbl ? "long double" : "double");
	ask_for(ask, ifp, Q_prtype, A_prtype, sizeof(A_prtype), 0);

	if (!pass)
		(void) sprintf(A_prffmt, "(void) printf(\"%%*.*%sf\", w, p, x);",
			ldbl ? "L" : "");
	ask_for(ask, ifp, Q_prffmt, A_prffmt, sizeof(A_prffmt), 0);

	if (!pass)
		(void) sprintf(A_prefmt, "(void) printf(\"%%*.*%se\", w, p, x);",
			ldbl ? "L" : "");
	ask_for(ask, ifp, Q_prefmt, A_prefmt, sizeof(A_prefmt), 0);

	if (!pass) {
#if !defined(vax) && !defined(tahoe)
		if (ldbl == 2)
			sp = "15";
		else if (!strcmp(A_generic, "double") || ldbl == 1)
			sp = "11";
		else
#endif
			sp = "8";
		(void) strcpy(A_bits_m, sp);
	}
	ask_for(ask, ifp, Q_bits_m, A_bits_m, sizeof(A_bits_m), 0);

	if (!pass) {
#if !defined(vax) && !defined(tahoe)
		sp = "no";
#else
		sp = "yes";
#endif
		(void) strcpy(A_guard_against_overflow, sp);
	}
	else
		yes = isyes(A_guard_against_overflow);
	ask_for(ask, ifp, Q_guard_against_overflow, A_guard_against_overflow,
		sizeof(A_guard_against_overflow), 0);
	if (isyes(A_guard_against_overflow)) {
		if (!pass || !yes)
			(void) strcpy(A_ghuge, "(GENERIC) 1.701411e+38");
		(void) sprintf(q, "An approximate largest finite \"%s\"?",
			A_generic);
		ask_for(ask, ifp, Q_ghuge, A_ghuge, sizeof(A_ghuge), 1);
	}

	if (!pass) {
		if ((tfp = fopen(SYS_TIMES_H, "r")) != (FILE *) 0) {
			(void) fclose(tfp);
			sp = "yes";
		}
		else {
			sp = "no";
		}
		(void) strcpy(A_posix_times, sp);
	}
	ask_for(ask, ifp, Q_posix_times, A_posix_times, sizeof(A_posix_times), 0);
	if (isyes(A_posix_times)) {
		if (!pass || pass && isyes(A_bsd_rusage)) {
			(void) strcpy(A_bsd_rusage, "no");
		}
	}
	else {
		if (!pass) {
			if ((tfp = fopen(RESOURCE_H, "r")) != (FILE *) 0) {
				(void) fclose(tfp);
				sp = "yes";
			}
			else {
				sp = "no";
			}
			(void) strcpy(A_bsd_rusage, sp);
		}
		ask_for(ask, ifp, Q_bsd_rusage, A_bsd_rusage, sizeof(A_bsd_rusage),
			0);
	}

	if (!pass)
		(void) strcpy(A_machine_pi, "yes");
	else
		yes = isyes(A_machine_pi);
	ask_for(ask, ifp, Q_machine_pi, A_machine_pi, sizeof(A_machine_pi), 0);
	if (isyes(A_machine_pi)) {
		int old_i;

		ask_for(ask, ifp, Q_intro_mpi, (char *) 0, 0, -1);
		if (ask) {
			for (i = 0; i < NA; i++) {
				(void) fprintf(stderr,
					"[%d] %3s-bit pi/2: %s  ... %s\n",
					i, mpi2_p[i].bits, mpi2_p[i].hex_digits,
					mpi2_p[i].comments);
			}
		}
		if (!pass || !yes)
			(void) strcpy(A_mpi2_p, "4");
		old_i = atoi(A_mpi2_p);
		ask_for(ask, ifp, Q_mpi2_p, A_mpi2_p, sizeof(A_mpi2_p), 0);
		i = atoi(A_mpi2_p);
		if (!i) {
			if (!pass || !yes) {
				(void) strcpy(A_hex_digits,
					mpi2_p[old_i ? old_i : 4].hex_digits);
			}
			ask_for(ask, ifp, Q_mpi2_p, A_hex_digits,
				sizeof(A_hex_digits), 0);
		}
		else if (i < 0 || i >= NA) {
			(void) fprintf(stderr, ">>> Bogus selection.\n");
			(void) exit(1);
		}
		else
			(void) strcpy(A_hex_digits, mpi2_p[i].hex_digits);
	}

	(void) strcpy(q, "        ");
	for (i = 0; i < NF; i++) {
		(void) strcat(q, "\"");
		(void) strcat(q, func_xxx[i]);
		if (ldbl)
			(void) strcat(q, "l");
		else if (flt)
			(void) strcat(q, "f");
		(void) strcat(q, "\"");
		(void) strcat(q, i < NF - 1 ? ", " : ".");
	}
	ask_for(ask, ifp, Q_intro_xxx, (char *) 0, 0, -1);
	for (i = 0; i < NF; i++) {			/* NDX_SIN < NDX_COS */
		int ref;
		if (!pass) {
			(void) strcpy(A_test_xxx[i], "yes");
			(void) sprintf(U_func_xxx[i], "%s%s", func_xxx[i],
				ldbl ? "l" : (flt ? "f" : ""));
		}
		else
			yes = isyes(A_test_xxx[i]);	/* true yes/no */
		if (i == NDX_COS) {
			if (isno(A_test_xxx[NDX_SIN])) {
				(void) strcpy(A_test_xxx[i], "no");
				continue;
			}
			else
				(void) strcpy(A_test_xxx[i], "yes");
		}
		(void) sprintf(q, "Would you like to test \"%s\"?",
			U_func_xxx[i]);
		ask_for(ask, ifp, Q_test_xxx, A_test_xxx[i], sizeof(A_test_xxx[i]),
			0);
		if (isno(A_test_xxx[i])) {
			if (i == NDX_COS)
				(void) strcpy(A_test_xxx[NDX_SIN], "no");
			continue;
		}
		if (!pass || !yes)
			(void) sprintf(A_func_xxx[i], "y = %s(x);", U_func_xxx[i]);
#define	Q "How do you evaluate your \"%s\" at x to get a result stored in y?"
		(void) sprintf(q, Q, U_func_xxx[i]);
#undef	Q
		ask_for(ask, ifp, Q_func_xxx, A_func_xxx[i], sizeof(A_func_xxx[i]),
			1);
		ref = 0;
		sp = guessname(A_func_xxx[i], &ref);
		if (!pass || !yes || sp != (char *) 0 &&
			strcmp(U_func_xxx[i], sp) != 0) {
			if (sp != (char *) 0) {
				(void) strcpy(U_func_xxx[i], sp);
			}
			(void) sprintf(A_cpp_xxx[i],
#ifdef __STDC__
				"extern GENERIC %s(GENERIC%s);",
				U_func_xxx[i], ref ? " *" : "");
#else
				"extern GENERIC %s();", U_func_xxx[i]);
#endif
		}
		(void) sprintf(q, "How do you declare your \"%s\"?",
			U_func_xxx[i]);
		ask_for(ask, ifp, Q_cpp_xxx, A_cpp_xxx[i], sizeof(A_cpp_xxx[i]), 1);
	}
}

static void
#ifdef __STDC__
prline(char *S, char *A)
#else
prline(S, A)
char *S, *A;
#endif
{
	(void) fprintf(stderr, "%s", S);
	TABOVER(S)
	TABOVER("")
	(void) fprintf(stderr, "[%s]\n", A);
}

static void
#ifdef __STDC__
summary(void)
#else
summary()		/* invoked *only* if ask is non-zero */
#endif
{
	int yes, i;

	(void) fprintf(stderr,
		"\nHere is a brief summary of the choices you made:\n\n");
	prline(S_long, A_long);
	prline(S_generic, A_generic);
	prline(S_prtype, A_prtype);
	prline(S_prffmt, A_prffmt);
	prline(S_prefmt, A_prefmt);
	prline(S_bits_m, A_bits_m);

	yes = isyes(A_guard_against_overflow);
	prline(S_guard_against_overflow, yes ? "yes" : "no");
	if (yes)
		prline(S_ghuge, A_ghuge);

	if (isyes(A_posix_times)) {
		prline(S_posix_times, "yes");
	}
	else {
		yes = isyes(A_bsd_rusage);
		prline(S_bsd_rusage, yes ? "yes" : "no");
	}

	yes = isyes(A_machine_pi);
	prline(S_machine_pi, yes ? "yes" : "no");
	if (yes)
		prline(S_hex_digits, A_hex_digits);

	(void) fprintf(stderr, "\n");
	for (i = 0; i < NF; i++) {
		(void) fprintf(stderr, "%s", func_xxx[i]);
		TABOVER(func_xxx[i])
		yes = isyes(A_test_xxx[i]);
		if (yes) {
			(void) fprintf(stderr, "[%s]", A_func_xxx[i]);
			TABOVER(A_func_xxx[i])
			(void) fprintf(stderr, "[%s]\n", A_cpp_xxx[i]);
		}
		else
			(void) fprintf(stderr, "[NOT TESTED]\n");
	}
}

static void
#ifdef __STDC__
dump(FILE *fp)
#else
dump(fp)
FILE *fp;
#endif
{
	int i;
	char token[STRSIZE], *sp, *tp;

	crank(fp, "LONG", "", A_long);
	crank(fp, "GENERIC", "", A_generic);
	crank(fp, "PRTYPE", "", A_prtype);
	crank(fp, "PRFFMT", "(w, p, x)", A_prffmt);
	crank(fp, "PREFMT", "(w, p, x)", A_prefmt);
	(void) sscanf(A_bits_m, "%d", &i);
	(void) sprintf(q, "%d", i - 1);
	crank(fp, "BITS_M", "", q);
	if (isno(A_guard_against_overflow))
		crank(fp, "GUARD_AGAINST_OVERFLOW", "", "0");
	else {
		crank(fp, "GUARD_AGAINST_OVERFLOW", "", "1");
		crank(fp, "GHUGE", "", A_ghuge);
	}
	crank(fp, "POSIX_TIMES", "", isno(A_posix_times) ? "0" : "1");
	crank(fp, "BSD_RUSAGE", "", isno(A_bsd_rusage) ? "0" : "1");
	if (isno(A_machine_pi))
		crank(fp, "MACHINE_PI", "", "0");
	else {
		crank(fp, "MACHINE_PI", "", "1");
		i = atoi(A_mpi2_p);
		for (tp = A_hex_digits; *tp != NUL; tp++);
		/*
		 * At this point we assume collect() already trimmed off
		 * all trailing spaces for us.
		 */
		if (tp > A_hex_digits && tp[-1] == COMMA) {
			tp[-1] = NUL;
		}
		(void) strcpy(q, "0x");
		for (tp = A_hex_digits; *tp != NUL && *tp != PERIOD; tp++);
		if (*tp == NUL) {
			(void) fprintf(stderr, ">>> Malformed hex-digit string.\n");
			(void) exit(1);
		}
		*tp++ = NUL;
		(void) strcat(q, A_hex_digits);
		while (*tp != NUL) {
			for (sp = tp; *tp != NUL && *tp != COMMA; tp++);
			if (*tp != NUL)
				*tp++ = NUL;
			(void) strcat(q, sp);
			if (*tp != NUL) {
				(void) strcat(q, ", 0x");
				while (*tp != NUL && isspace(*tp)) {
					tp++;
				}
			}
		}
		if (i) {
			(void) strcat(q, "\t/* ");
			(void) strcat(q, mpi2_p[i].bits);
			(void) strcat(q, "-bit pi/2 */");
		}
		crank(fp, "MPI2_P", "", q);
	}
	for (i = 0; i < NF; i++) {
		(void) sprintf(token, "TEST_%s", cpp_xxx[i]);
		(void) fprintf(fp, "\n");
		if (isno(A_test_xxx[i]))
			crank(fp, token, "", "0");
		else {
			crank(fp, token, "", "1");
			crankfn(fp, cpp_xxx[i], A_func_xxx[i], A_cpp_xxx[i]);
		}
	}
	(void) fclose(fp);
}

static void
#ifdef __STDC__
dump_afile(FILE *afp)
#else
dump_afile(afp)
FILE *afp;
#endif
{
	int i;

	(void) fprintf(afp, "%s\n", A_long);
	(void) fprintf(afp, "%s\n", A_generic);
	(void) fprintf(afp, "%s\n", A_prtype);
	(void) fprintf(afp, "%s\n", A_prffmt);
	(void) fprintf(afp, "%s\n", A_prefmt);
	(void) fprintf(afp, "%s\n", A_bits_m);
	(void) fprintf(afp, "%s\n", A_guard_against_overflow);
	if (isyes(A_guard_against_overflow)) {
		(void) fprintf(afp, "%s\n", A_ghuge);
	}
	(void) fprintf(afp, "%s\n", A_posix_times);
	if (!isyes(A_posix_times)) {
		(void) fprintf(afp, "%s\n", A_bsd_rusage);
	}
	(void) fprintf(afp, "%s\n", A_machine_pi);
	if (isyes(A_machine_pi)) {
		(void) fprintf(afp, "%s\n", A_mpi2_p);
		i = atoi(A_mpi2_p);
		if (!i) {
			(void) fprintf(afp, "%s\n", A_hex_digits);
		}
	}
	for (i = 0; i < NF; i++) {
		(void) fprintf(afp, "%s\n", A_test_xxx[i]);
		if (!isno(A_test_xxx[i])) {
			(void) fprintf(afp, "%s\n", A_func_xxx[i]);
			(void) fprintf(afp, "%s\n", A_cpp_xxx[i]);
		}
	}
}

/*
 * name may be either a name of a file or a null pointer;
 * mode may be either "r" or "w".
 */
static FILE *
#ifdef __STDC__
getfp(const char *name, const char *mode, int *retv)
#else
getfp(name, mode, retv)
char *name, *mode;
int *retv;
#endif
{
	FILE *fp;
	char *sp;

	if (!name || !strcmp(name, "-")) {
		fp = mode[0] == 'w' ? stdout : stdin;
	}
	else if ((fp = fopen(name, mode)) == (FILE *) 0) {
		(*retv)++;
		(void) fprintf(stderr, "%s: Cannot open \"%s\" for %s.  ",
			prog, name, mode[0] == 'w' ? "writing" : "reading");
		if (mode[0] != 'w' || (fp = fopen(sp = tmpnam((char *) 0),
			mode)) == (FILE *) 0) {
			(void) fprintf(stderr, "Use %s instead.\n",
				mode[0] == 'w' ? "stdout" : "stdin");
			fp = mode[0] == 'w' ? stdout : stdin;
		}
		else {
			(void) fprintf(stderr, "Use \"%s\" instead.\n", sp);
		}
	}
	return fp;
}

void
#ifdef __STDC__
main(int argc, char *argv[])
#else
main(argc, argv)
int argc;
char *argv[];
#endif
{
	int ask, pass, retv = 0;
	char *aload, *afile;
	FILE *fp;

	prog = *argv;
	argc--, argv++;
	aload = argc > 0 ? *argv : (char *) 0;
	argc--, argv++;
	afile = argc > 0 ? *argv : (char *) 0;
#if defined(__unix) || defined(unix) || defined(__TURBOC__)
	ask = isatty(0);
#else
	ask = 1;
#endif
	for (pass = 0; ; pass++) {
		if (!pass) {
			fp = getfp(aload, "r", &retv);
			collect(ask && fp == stdin, pass, fp);
			if (fp != stdin) {
				(void) fclose(fp);
			}
		}
		else {
			collect(ask, pass, stdin);
		}
		if (!ask)
			break;
		summary();
		(void) strcpy(A_happy, "");
		while (!A_happy[0])
			ask_for(ask, stdin, Q_happy, A_happy, sizeof(A_happy),
				0);
		if (isyes(A_happy))
			break;
	}
	fp = getfp(LOCAL_H, "w", &retv);
	dump(fp);
	if (fp != stdout) {
		(void) fclose(fp);
	}
	ask_for(ask, (FILE *) 0, Q_intro_next, (char *) 0, 0, -1);
	if (afile) {
		fp = getfp(afile, "w", &retv);
		dump_afile(fp);
		if (fp != stdout) {
			(void) fclose(fp);
		}
	}
	(void) exit(retv);
	/* NOTREACHED */
}
