From 3fe24df1237a77549ba64c6331383a9a40aed1de Mon Sep 17 00:00:00 2001 From: emkael Date: Sat, 25 Nov 2017 16:15:54 +0100 Subject: Initial BigDeal sources --- output.c | 1132 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1132 insertions(+) create mode 100644 output.c (limited to 'output.c') diff --git a/output.c b/output.c new file mode 100644 index 0000000..b29bcbd --- /dev/null +++ b/output.c @@ -0,0 +1,1132 @@ +#include "types.h" +#include "bigdeal.h" +#include "binomial.h" +#include +#include + +static char rcsid[] = "$Header: /home/sater/bridge/bigdeal/RCS/output.c,v 1.20 2000/09/08 06:01:46 sater Exp $"; + +/* + * Next type is a tagged union to prevent compilers complaining + * Should check on tag + */ +typedef +struct hand_rep { + int hr_baseform; /* Which baseform is this in */ + union { + dl_num *hv_dnp; + dl_int *hv_dip; + dl_byh *hv_dbhp; + dl_byc *hv_dbcp; + } hr_value; +} hr_t, *hr_p; + +static progparams_p prparp; /* All parameters */ + +/* + * Information per output type is collected here + */ +typedef +struct output_format { + char * of_name; /* Name of format */ + int of_flags; /* Various flags */ + FILE * of_file; /* Output file to write it to */ + char * of_suffix; /* File suffix/type */ + int of_baseform; /* Which internal format it is printed from */ + char * of_help; /* Help description */ + void (*of_init)(FILE *f); /* Routine to init */ + void (*of_printit)(FILE *f, int boardno, hr_p arg); /* Routine to print it */ + void (*of_finish)(FILE *f); /* Routine to finish */ +} of_t, *of_p; + +/* + * Values for the of_flags struct member + */ +#define OFF_USEIT 0x1 /* Use the format this run */ +#define OFF_BINARY 0x2 /* Output is binary, not text */ + +static void +cnv_int_byh(dl_int *dip, dl_byh *dbhp) +/* + * Converts the internal handrepresentation from *dip to a "bri" like coding + * in *dbhp. This representation is as follows: + * The 52 cards are numbered 1: AS, 2 KS, .... , 52 2C + * and all thirteen slots for all four hands are filled in with one of + * the 52 cards. + * + * Given the way this program works the order of cards per hand will + * be strictly increasing. + */ +{ + int nextcard[NCARDSPERDECK+1]; + int i; + int card,prevcard; + int skip, skipcnt; + int compass; + + /* + * Setup a chain of 52 cards with a linked list type structure + */ + for (i=0; idi_hand[compass][0]; + else + skip=dip->di_hand[compass][i] - + dip->di_hand[compass][i-1]; + for (skipcnt=0; skipcntdh_hand[compass][i] = card; + /* + * Take card just selected out of the linked list + */ + card = nextcard[card]; + nextcard[prevcard] = card; + } + } +} + +static void +cnv_byh_byc(dl_byh *dbhp, dl_byc *dbcp) +/* + * Convert from a "bri" like coding to a cardposition type coding. + * Representation is an array of 52 cards, from AS to 2C, + * with as a value the compass position, 0 for N, 3 for W + * + * Note the -1 to convert from 1..52 cardnotation to 0..51 array index + */ +{ + int compass, card; + + for (compass = COMPASS_NORTH; compass <= COMPASS_WEST; compass++) + for (card = 0; card < NCARDSPERHAND; card++) + dbcp->dc_card[dbhp->dh_hand[compass][card] - 1] = + compass; +} + +static void +out_fill(FILE *out, char filler, int count) +/* + * Write filler character, count times + */ +{ + int i; + + for(i=0; idh_hand[compass][i]); +} + +/*ARGSUSED*/ +static void +print_bri(FILE *out, int boardno, hr_p hrp) +/* + * Writes out "bri" format, including filler + * + * It is unknown whether the strange half space, half zero fill + * is actually necessary. This is just the way I saw my first BRI file + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + out_bri(out, dbhp); + out_fill(out, ' ', 32); + out_fill(out, 0, 18); +} + +static void +out_cardrep(FILE *out, dl_byh *dbhp, char *suitstr[NCOMPASS][NSUIT], char *cardrep) +/* + * Write out a "dge" or "pbn" record, no filler + */ +{ + int compass; + int suit; + int i; + int card, card_in_suit; + + for (compass=COMPASS_NORTH; compass<=COMPASS_WEST; compass++) { + for (suit=SUIT_SPADES; suit<=SUIT_CLUBS; suit++) { + fputs(suitstr[compass][suit], out); + for (i=0; idh_hand[compass][i]; + card_in_suit = card - 13*suit; + if (card_in_suit > 0 && card_in_suit <= 13) + putc(cardrep[card_in_suit-1], out); + } + } + } +} + +/* + * International card representation + * + * Maybe this will need to be changed for local formats + */ +static char cardrep_int[13] = "AKQJT98765432"; + +/* + * The suit symbols in the DOS character set + */ +#define DOSCHAR_SPADE 6 +#define DOSCHAR_HEART 3 +#define DOSCHAR_DIAMOND 4 +#define DOSCHAR_CLUB 5 + +static char dosstr_spade[] = { DOSCHAR_SPADE, 0 }; +static char dosstr_heart[] = { DOSCHAR_HEART, 0 }; +static char dosstr_diamond[] = { DOSCHAR_DIAMOND, 0 }; +static char dosstr_club[] = { DOSCHAR_CLUB, 0 }; +static char *dge_suitstr[NCOMPASS][NSUIT] = { + { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club }, + { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club }, + { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club }, + { dosstr_spade, dosstr_heart, dosstr_diamond, dosstr_club }, +}; + +static void +out_dge(FILE *out, dl_byh *dbhp) +/* + * Write out the "dge" record, no filler + */ +{ + + out_cardrep(out, dbhp, dge_suitstr, cardrep_int); +} + +/*ARGSUSED*/ +static void +print_dge(FILE *out, int boardno, hr_p hrp) +/* + * Writes out "dge" format, including filler + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + out_dge(out, dbhp); + out_fill(out, 0, 60); +} + +/* + * Portable Bridge Notation + * + * We generate PBN "Import" format, according to the instructions + * from KGB + */ +static char pbn_colon[] = ":"; +static char pbn_space[] = " "; +static char pbn_dot[] = "."; +static char *pbn_suitstr[NCOMPASS][NSUIT] = { + { pbn_colon, pbn_dot, pbn_dot, pbn_dot }, + { pbn_space, pbn_dot, pbn_dot, pbn_dot }, + { pbn_space, pbn_dot, pbn_dot, pbn_dot }, + { pbn_space, pbn_dot, pbn_dot, pbn_dot }, +}; + +static void +out_pbn(FILE *out, dl_byh *dbhp) +/* + * Write out the "pbn" record, just the hand itself + */ +{ + + out_cardrep(out, dbhp, pbn_suitstr, cardrep_int); +} + +static char dealerchar[4] = "WNES"; +static char *pbn_vulnrep[] = { "None", "NS", "EW", "All" }; +static int vuln_index[16] = { + 2, 0, 1, 2, + 3, 1, 2, 3, + 0, 2, 3, 0, + 1, 3, 0, 1 +}; + +static void +init_pbn(FILE *out) +{ + + fprintf(out, "%% PBN 1.0\n"); + fprintf(out, "[Generator \"Big Deal version %d.%d\"]\n", + VERSION_MAJOR, VERSION_MINOR); +} + +static void +print_pbn(FILE *out, int boardno, hr_p hrp) +/* + * Write out the "pbn" record, with furnishings + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + fprintf(out, "[Board \"%d\"]\n", boardno); + fprintf(out, "[Dealer \"%c\"]\n", dealerchar[boardno%4]); + fprintf(out, "[Vulnerable \"%s\"]\n", + pbn_vulnrep[vuln_index[boardno%16]]); + fprintf(out, "[Deal \"N"); + out_pbn(out, dbhp); + fprintf(out, "\"]\n"); + fprintf(out, "\n"); +} + +/* + * CSV format, for database import + */ + +static char csv_begin[] = "\""; +static char csv_intra[] = "\",\""; +static char *csv_suitstr[NCOMPASS][NSUIT] = { + { csv_begin, csv_intra, csv_intra, csv_intra }, + { csv_intra, csv_intra, csv_intra, csv_intra }, + { csv_intra, csv_intra, csv_intra, csv_intra }, + { csv_intra, csv_intra, csv_intra, csv_intra }, +}; +static char *csv_vulnrep[] = { "-", "NS", "EW", "All" }; + +static void +print_csv(FILE *out, int boardno, hr_p hrp) +/* + * Write out a CSV record + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + out_cardrep(out, dbhp, csv_suitstr, cardrep_int); + fprintf(out, "\",\"%d\",\"%c/%s\"\n", + boardno, + dealerchar[boardno%4], + csv_vulnrep[vuln_index[boardno%16]]); +} + +/* + * DUP and DLM formats, for Jannersten Duplimate machine + */ + +static void +print_dup_common(FILE *out, int boardno, dl_byh *dbhp, int not_on_screen) +/* + * Write out a "dup" record + * This is actually a BRI, followed by a DGE, and some padding + */ +{ + + /* + * Horribly convoluted hack upon hack format + * + * Duplimate, generation X + */ + out_bri(out, dbhp); + if (boardno == 1 && not_on_screen) { + out_fill(out, ' ', 68); + } else { + out_dge(out, dbhp); + } + if (boardno == 1) { + putc('Y', out); /* Y to Random Hands */ + putc('N', out); /* N to reverse order */ + putc('1', out); /* Start at board 1 */ + putc(' ', out); + putc(' ', out); + putc('0', out); /* No copies immediate */ + putc(' ', out); + putc('0' + (prparp->pp_nboards/10)%10, out);/* tens of number */ + putc('0' + prparp->pp_nboards%10, out); /* units of number */ + putc(' ', out); + } else { + out_fill(out, 0, 10); + } +} + +/*ARGSUSED*/ +static void +print_dup(FILE *out, int boardno, hr_p hrp) +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + print_dup_common(out, boardno, dbhp, 0); +} + +/*ARGSUSED*/ +static void +print_dupblind(FILE *out, int boardno, hr_p hrp) +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + + print_dup_common(out, boardno, dbhp, 1); +} + +static void +init_dlmcommon(FILE *out, int show) +{ + int checksum; + + /* + * Very badly documented, sorry(not my fault) + */ + fprintf(out, "[Document]\n"); + fprintf(out, "Headline=Generated by Big Deal version %d.%d\n", + VERSION_MAJOR, VERSION_MINOR); + fprintf(out, "Status=%s\n", show ? "Show" : "Sealed"); + fprintf(out, "Duplicates=1\n"); + fprintf(out, "From board=1\n"); + fprintf(out, "To board=%d\n", + prparp->pp_nboards<=99 ? prparp->pp_nboards : 99); + fprintf(out, "Next board to duplimate=0\n"); + fprintf(out, "PrintOuts=0\n"); + fprintf(out, "Crypto key=0\n"); + checksum = prparp->pp_nboards; + if (show) + checksum ^= 1; /* XOR with 1 if show */ + fprintf(out, "Checksum=%d\n", checksum); +} + +static void +init_dlm(FILE *out) +{ + + init_dlmcommon(out, 1); +} + +static void +init_dlmblind(FILE *out) +{ + + init_dlmcommon(out, 0); +} + +static void +print_dlm(FILE *out, int boardno, hr_p hrp) +/* + * Writes out one DLM record, including "checksum" + * Record is mainly one lower case character per two cards + */ +{ + dl_byc *dbcp = hrp->hr_value.hv_dbcp; + int i; + int c; + int checksum; + + if (boardno>99) /* Format cannot do more than 99 */ + return; + fprintf(out, "Duplicates %02d=0\n", boardno); + fprintf(out, "Board %02d=", boardno); + checksum = boardno; + for(i=0; idc_card[2*i+0]<<2; + c += dbcp->dc_card[2*i+1]<<0; + putc(c, out); + checksum ^= c; + } + fprintf(out, "%03d\n", checksum); +} + +static void +finish_dlm(FILE *out) +/* + * For some reason a DLM file always contains exactly 99 hands + * fill it up with bogus hands here + */ +{ + int i; + int checksum; + + for (i=prparp->pp_nboards+1; i<=99; i++) { + checksum = i^14; /* KGB calculated */ + fprintf(out, "Duplicates %02d=0\n", i); + fprintf(out, "Board %02d=aaaaaabffffffkkkkkklpppppp%03d\n", + i, checksum); + } +} + +/*ARGSUSED*/ +static void +print_ber(FILE *out, int boardno, hr_p hrp) +/* + * Writes out "ber" format, no filler or separators + */ +{ + dl_byc *dbcp = hrp->hr_value.hv_dbcp; + int i; + + for(i=0; idc_card[i], out); +} + +/* + * Borel files contain lines of 52 characters [A-Za-z] + * A is AS to z is 2c + * For some reason the line does not start with the cards of North + * but the cards of the dealer. + */ +static void +init_borel(FILE *out) +{ + + fprintf(out, "\n"); +} + +static void +print_borel(FILE *out, int boardno, hr_p hrp) +/* + * Write out a "bhg" record + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + int i, j, compass, card; + + for (i=0; idh_hand[compass][j]; + putc(card <= 2*NCARDSPERHAND ? + 'A'+card-1 : 'a'+card-2*NCARDSPERHAND-1, + out); + } + } + fprintf(out, "\n"); +} + +#ifdef BIGDEALX + +#include "mp.h" + +/* + * A decimal number corresponding to a deal has maximum 29 digits + */ +#define MAXDECIMALS 29 +static byte powers_of_ten[MAXDECIMALS][L]; + +static void +init_goedel(FILE *out) +{ + int i; + byte ten[L]; + + /* + * Initialize the array with powers of ten + * We need them in the next function + */ + mp96_zero(ten); + ten[L-1] = 10; + mp96_one(powers_of_ten[0]); + for (i=1; ihr_value.hv_dnp; + byte value[L]; + int i; + int decimal; + int zero_suppress; + char outchar; + + mp96_assign(value, dnp->dn_num); + zero_suppress = 1; + for (i=MAXDECIMALS-1; i>=0; i--) { + /* + * Since we do not have division we do repeated subtraction + */ + decimal = 0; + while (mp96_cmp(value, powers_of_ten[i])>=0) { + mp96_sub(value, value, powers_of_ten[i]); + decimal++; + } + if (decimal) { + outchar = decimal + '0'; + zero_suppress = 0; + } else { + /* + * Suppress leading zeroes + * Special case all zeroes also handled + * That will be the day.... + */ + outchar = zero_suppress && i ? ' ' : '0'; + } + putc(outchar, out); + /* + * Intersperse with commas for readability + */ + if (i%3 == 0) { + putc(i==0 ? '\n' : zero_suppress ? ' ' : ',', out); + } + } +} + +/* + * Statistics computation: + * There is a strong argument *not* to print statistics of a set of deals + * since it leads itself to abuse only too easily. + * Therefore this is only included in the non-safe version. + */ + +static struct { + int st_sumsquare; + int st_hcp[NCOMPASS]; + int st_hcpfreq[38]; /* 0..37, maximum points per hand */ + int st_lsuitfreq[NCOMPASS][14]; /* 4..13, longest suit per compass */ + int st_longestsuit; /* longest suit in whole set */ +} stats; + +/* + * Theoretical values taken from Encyclopedia, fourth edition, page 278 + */ +static int theory_hcpfreq[38] = { /* times 10000 */ + 3639, /* 0 */ + 7884, /* 1 */ + 13561, /* 2 */ + 24624, /* 3 */ + 38454, /* 4 */ + 51862, /* 5 */ + 65541, /* 6 */ + 80281, /* 7 */ + 88922, /* 8 */ + 93562, /* 9 */ + 94051, /* 10 */ + 89447, /* 11 */ + 80269, /* 12 */ + 69143, /* 13 */ + 56933, /* 14 */ + 44237, /* 15 */ + 33109, /* 16 */ + 23617, /* 17 */ + 16051, /* 18 */ + 10362, /* 19 */ + 6435, /* 20 */ + 3779, /* 21 */ + 2100, /* 22 */ + 1119, /* 23 */ + 559, /* 24 */ + 264, /* 25 */ + 117, /* 26 */ + 49, /* 27 */ + 19, /* 28 */ + 7, /* 29 */ + 2, /* 30 */ + 1, 1, 1, 1, 1, 1, 1 /* 31..37 */ +}; + +static int theory_lsuitfreq[14] = { /* probability * 1000000 */ + 0, 0, 0, 0, /* cannot be 0 1 2 3 */ + 350805, /* 4 */ + 443596, /* 5 */ + 165477, /* 6 */ + 35265, /* 7 */ + 4668, /* 8 */ + 370, /* 9 */ + 16, /* 10 */ + 0, 0, 0 /* 11 .. 13 */ +}; + + +static int high_card_points[13] = { 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/*ARGSUSED*/ +static void +init_stat(FILE *out) +{ + + /* + * Warn of consequences of looking at statistics + */ + printf("\nStatistics will be produced in an accompanying file.\n"); + printf("Usage of these statistics for a decision on whether or when\n"); + printf("to play this set of deals is a violation of the spirit of the\n"); + printf("Laws of Duplicate Contract Bridge.\n\n"); + printf("STATISTICS ARE FOR DISPLAY PURPOSES ONLY!!\n\n"); + /* + * At this point initialize counters if non-zero + */ +} + +/*ARGSUSED*/ +static void +compute_stat(FILE *out, int boardno, hr_p hrp) +/* + * Compute statistics on this hand + */ +{ + dl_byh *dbhp = hrp->hr_value.hv_dbhp; + int i, compass, card; + int length, maxlength, suitlength[NCOMPASS][NSUIT]; + int hcp; + + for (compass=0; compassdh_hand[compass][i]-1; + /* + * card/13 should be suit + */ + suitlength[compass][card/13]++; + /* + * card%13 should be pip + */ + hcp += high_card_points[card%13]; + } + stats.st_hcp[compass] += hcp; + stats.st_hcpfreq[hcp]++; + maxlength = 0; + for (i=0; i maxlength) + maxlength = length; + stats.st_sumsquare += length*length; + } + if (maxlength > stats.st_longestsuit) + stats.st_longestsuit = maxlength; + stats.st_lsuitfreq[compass][maxlength]++; + } + /* + * Now suitlength[][] contains all suit lengths for all hands + * 16 numbers in all. + * Statistics about distribution to be added here. + */ +} + +static void +print_avg(FILE *out, int sum, int divisor, int decimals, char *termstr) +/* + * Prevent use of floating point + */ +{ + int power_of_ten; + int i; + int result; + + power_of_ten = 1; + for (i=0; ipp_nboards); + + fprintf(out, "Average sum of squares: "); + print_avg(out, stats.st_sumsquare, prparp->pp_nboards, 0, "\n"); + + fprintf(out, "Average points NESW: "); + print_avg(out, stats.st_hcp[0], prparp->pp_nboards, 2, " "); + print_avg(out, stats.st_hcp[1], prparp->pp_nboards, 2, " "); + print_avg(out, stats.st_hcp[2], prparp->pp_nboards, 2, " "); + print_avg(out, stats.st_hcp[3], prparp->pp_nboards, 2, "\n"); + + fprintf(out, "Frequency of high card points per hand:\n"); + for (i=0; i<38; i++) { + if (stats.st_hcpfreq[i] == 0) + continue; + fprintf(out, "%2d points: ", i); + print_avg(out, stats.st_hcpfreq[i]*100, prparp->pp_nboards*4, 4, "% ("); + print_avg(out, theory_hcpfreq[i], 10000, 4, "%)\n"); + } + + fprintf(out, "Frequency of longest suitlength per compass:\n"); + fprintf(out, "%8s%8s%8s%8s%8s Theory\n", "Length", + "North", "East", "South", "West"); + for(length = 4; length <= stats.st_longestsuit; length++) { + fprintf(out, "%8d%8d%8d%8d%8d ", length, + stats.st_lsuitfreq[COMPASS_NORTH][length], + stats.st_lsuitfreq[COMPASS_EAST][length], + stats.st_lsuitfreq[COMPASS_SOUTH][length], + stats.st_lsuitfreq[COMPASS_WEST][length]); + print_avg(out, 100*theory_lsuitfreq[length], + 100000000/prparp->pp_nboards, 1, "\n"); + } +} +#endif + +/* + * All formats are set to not in use. + * The output_specify_formats routine will set the wanted formats + */ +static +of_t output_formats[] = { + { "dup", OFF_BINARY, NULL, ".dup", BF_BYHAND, + "Duplimate format", + 0, print_dup, 0 }, + { "dupblind", OFF_BINARY, NULL, ".dup", BF_BYHAND, + "Duplimate format, no cards on screen", + 0, print_dupblind, 0 }, + { "dlm", 0, NULL, ".dlm", BF_BYCARD, + "New Duplimate format", + init_dlm, print_dlm, finish_dlm }, + { "dlmblind", 0, NULL, ".dlm", BF_BYCARD, + "New Duplimate format, no cards on screen", + init_dlmblind, print_dlm, finish_dlm }, + { "bri", OFF_BINARY, NULL, ".bri", BF_BYHAND, + "BRI format", + 0, print_bri, 0 }, + { "dge", OFF_BINARY, NULL, ".dge", BF_BYHAND, + "DGE format", + 0, print_dge, 0 }, + { "pbn", 0, NULL, ".pbn", BF_BYHAND, + "Portable Bridge Notation", + init_pbn, print_pbn, 0 }, + { "csv", 0, NULL, ".csv", BF_BYHAND, + "Comma Separated Values", + 0, print_csv, 0 }, + { "ber", OFF_BINARY, NULL, ".ber", BF_BYCARD, + "Bernasconi format", + 0, print_ber, 0 }, + { "borel", 0, NULL, ".bhg", BF_BYHAND, + "Borel Hand Generator format", + init_borel, print_borel, 0 }, +#ifdef BIGDEALX + { "goedel", 0, NULL, ".goe", BF_GOEDEL, + "Internal goedel number format(development only)", + init_goedel, print_goedel, 0 }, + { "stats", 0, NULL, ".txt", BF_BYHAND, + "Statistics on set", + init_stat, compute_stat, print_stat }, + { "stdout", 0, NULL, 0, BF_BYHAND, + "Portable Bridge Notation on standard output", + init_pbn, print_pbn, 0 }, +#endif + { 0 } +}; + +void +output_help() { + of_p ofp; + + printf("%-8s%8s Explanation\n\n", "Name", "Suffix"); + /* For all formats: */ + for (ofp=output_formats; ofp->of_name; ofp++) { + printf("%-8s%8s %s\n", + ofp->of_name, + ofp->of_suffix? ofp->of_suffix : "", + ofp->of_help); + } + printf("\n"); +} + +#define NTOKENS 10 +static char ** +tokenize(char *tokenstring, char separator) +{ + static char *tokenvec[NTOKENS+1]; + char *tsp, *sepp; + int tokencount; + + tsp = tokenstring; + tokencount = 0; + do { + tokenvec[tokencount++] = tsp; + if ((sepp = strchr(tsp, separator)) != 0) { + *sepp++ = 0; + tsp = sepp; + } + } while (sepp && tokencount < NTOKENS); + if (sepp) + fprintf(stderr, "too many words, \"%s\" ignored\n", sepp); + tokenvec[tokencount] = 0; + return tokenvec; +} + +static int +suf_in_use(char *name, char *suffix) +{ + of_p ofp; + + /* + * Check for potential duplicate suffix use + */ + for (ofp=output_formats; ofp->of_name; ofp++) { + if (!(ofp->of_flags&OFF_USEIT)) { + /* + * Format not in use, no problem + */ + continue; + } + if (ofp->of_suffix != 0) { + if (suffix == 0 || strcmp(ofp->of_suffix, suffix) != 0) { + /* + * Not same suffix, no problem + */ + continue; + } + } else { + /* + * To check suffix is 0 (stdout) + */ + if (suffix != 0) { + /* + * One 0, other not, no problem + */ + continue; + } + } + /* + * Ok, we have a problem + */ + fprintf(stderr, "Cannot use %s, because %s shares suffix %s\n", + name, ofp->of_name, suffix ? suffix : "NONE"); + return 1; + } + return 0; +} + +int +output_specify_formats(char *format_string, int for_real) +/* + * Comma separated list of output formats to use + * + * Return value is used to check validity of string + */ +{ + char **ofvector; + of_p ofp; + int found; + int retval; + + retval = 1; /* While all OK */ + for(ofvector = tokenize(format_string, ','); *ofvector; ofvector++) { + found = 0; + for (ofp=output_formats; ofp->of_name; ofp++) { + if (strcmp(ofp->of_name, *ofvector) == 0) { + if (suf_in_use(ofp->of_name, ofp->of_suffix)) { + retval = 0; + } else { + ofp->of_flags |= OFF_USEIT; + } + found++; + break; + } + } + if (!found) { + fprintf(stderr, "Format %s unknown\n", *ofvector); + retval = 0; + } + } + + if (!for_real) { + /* + * This was just a test, clean up + */ + for (ofp=output_formats; ofp->of_name; ofp++) { + ofp->of_flags &= ~OFF_USEIT; + } + } + + return retval; +} + +/* + * This next section is to support multiple internal formats, + * where there is a limited number of possible transformations + * and we try to minimize the internal formats generated + * Just generate what we need + */ +#define bit(x) (1<<(x)) +static int formatset; /* Bitmap of internal formats to support */ +static int formatclosure[NBASEFORM] = { + bit(BF_GOEDEL), + bit(BF_GOEDEL) | bit(BF_INTERNAL), + bit(BF_GOEDEL) | bit(BF_INTERNAL) | bit(BF_BYHAND), + bit(BF_GOEDEL) | bit(BF_INTERNAL) | bit(BF_BYHAND) | bit(BF_BYCARD), +}; + +void +output_createfiles(char *fileprefix, progparams_p ppp) +/* + * Create a file for every wanted output format + * Set the formatset variable + */ +{ + char filename[100]; + of_p ofp; + FILE *f; + + prparp = ppp; + /* For all formats: */ + for (ofp=output_formats; ofp->of_name; ofp++) { + /* If not in use forget it */ + if (!(ofp->of_flags&OFF_USEIT)) + continue; + + /* Make note of internal formats we have to make */ + formatset |= formatclosure[ofp->of_baseform]; + + /* If no suffix use Standard Output */ + if (ofp->of_suffix) { + if (strlen(fileprefix)+strlen(ofp->of_suffix)+1 > + sizeof(filename)) { + fprintf(stderr, "File name %s%s too long(cannot happen)\n", fileprefix, ofp->of_suffix); + exit(-1); + } + strcpy(filename, fileprefix); + strcat(filename, ofp->of_suffix); + f = fopen(filename, ofp->of_flags&OFF_BINARY ? "wb" : "w"); + if (f == NULL) { + fprintf(stderr, "Cannot create %s\n", filename); + exit(-1); + } + ofp->of_file = f; + } else { + ofp->of_file = stdout; + } + /* + * If there is an init routine call it now + */ + if (ofp->of_init) + (*ofp->of_init)(ofp->of_file); + } +} + +void +output_closefiles() +/* + * Close all output files + */ +{ + of_p ofp; + + /* For all formats: */ + for (ofp=output_formats; ofp->of_name; ofp++) { + /* If not in use forget it */ + if (ofp->of_file != NULL) { + /* + * If there is a finish routine call it first + */ + if (ofp->of_finish) + (*ofp->of_finish)(ofp->of_file); + if (ofp->of_suffix) /* So not STDOUT */ + fclose(ofp->of_file); + } + } +} + +void +output_hand(int boardno, dl_num *dnp) +/* + * Convert goedel number to various internal forms as necessary + * Output all desired formats + */ +{ + dl_int dinternal; + dl_byh dbyhand; + dl_byc dbycard; + of_p ofp; + hr_t handrep; + + /* + * First create all necessary internal forms + * Bits in formatset have been inited in output_createfiles + */ + if (formatset&bit(BF_INTERNAL)) + code_to_hand(dnp, &dinternal); + if (formatset&bit(BF_BYHAND)) + cnv_int_byh(&dinternal, &dbyhand); + if (formatset&bit(BF_BYCARD)) + cnv_byh_byc(&dbyhand, &dbycard); + + /* + * Now call output routines + */ + for(ofp = output_formats; ofp->of_name; ofp++) { + if (ofp->of_file == NULL) + continue; + handrep.hr_baseform = ofp->of_baseform; + switch (ofp->of_baseform) { + case BF_GOEDEL: + handrep.hr_value.hv_dnp = dnp; + break; + case BF_INTERNAL: + handrep.hr_value.hv_dip = &dinternal; + break; + case BF_BYHAND: + handrep.hr_value.hv_dbhp = &dbyhand; + break; + case BF_BYCARD: + handrep.hr_value.hv_dbcp = &dbycard; + break; + } + (*ofp->of_printit)(ofp->of_file, boardno, &handrep); + } +} -- cgit v1.2.3