X-Git-Url: https://jxself.org/git/?p=open-adventure.git;a=blobdiff_plain;f=misc.c;h=a2626b4d3f02ae66ea69ed4dbf99c4c1939debe0;hp=30b3277d0e3a3bda2d0405ef18fb15e01be8cc14;hb=c35cf999660d640bcf839792fa280855cf45c31b;hpb=4b51b06b7327966f6e7ba893430dad7e2b09c7cd diff --git a/misc.c b/misc.c index 30b3277..a2626b4 100644 --- a/misc.c +++ b/misc.c @@ -10,19 +10,7 @@ #include "advent.h" #include "dungeon.h" -char* xstrdup(const char* s) -{ - char* ptr = strdup(s); - if (ptr == NULL) { - // LCOV_EXCL_START - // exclude from coverage analysis because we can't simulate an out of memory error in testing - fprintf(stderr, "Out of memory!\n"); - exit(EXIT_FAILURE); - } - return (ptr); -} - -void* xmalloc(size_t size) +static void* xmalloc(size_t size) { void* ptr = malloc(size); if (ptr == NULL) { @@ -35,7 +23,7 @@ void* xmalloc(size_t size) return (ptr); } -void packed_to_token(long packed, char token[TOKLEN+1]) +void packed_to_token(long packed, char token[TOKLEN + 1]) { // The advent->ascii mapping. const char advent_to_ascii[] = { @@ -93,7 +81,7 @@ long token_to_packed(const char token[]) size_t t_len = strlen(token); if (t_len > TOKLEN) - t_len = TOKLEN; + t_len = TOKLEN; long packed = 0; for (size_t i = 0; i < t_len; ++i) { char mapped = ascii_to_advent[(int) toupper(token[i])]; @@ -106,12 +94,36 @@ void tokenize(char* raw, struct command_t *cmd) { memset(cmd, '\0', sizeof(struct command_t)); - /* FIXME: put a bound prefix on the %s to prevent buffer overflow */ - int word_count = sscanf(raw, "%s%s", cmd->raw1, cmd->raw2); + /* Bound prefix on the %s would be needed to prevent buffer + * overflow. but we shortstop this more simply by making each + * raw-input buffer as long as the enrire inout buffer. */ + sscanf(raw, "%s%s", cmd->raw1, cmd->raw2); // pack the substrings cmd->wd1 = token_to_packed(cmd->raw1); cmd->wd2 = token_to_packed(cmd->raw2); + + /* (ESR) In oldstyle mode, simulate the uppercasing and truncating + * effect on raw tokens of packing them into sixbit characters, 5 + * to a 32-bit word. This is something the FORTRAN version did + * becuse archaic FORTRAN had no string types. Don Wood's + * mechanical translation of 2.5 to C retained the packing and + * thus this misfeature. + * + * It's philosophically questionable whether this is the right + * thing to do even in oldstyle mode. On one hand, the text + * mangling was not authorial intent, but a result of limitations + * in their tools. On the other, not simulating this misbehavior + * goes against the goal of making oldstyle as accurate as + * possible an emulation of the original UI. + */ + if (settings.oldstyle) { + cmd->raw1[TOKLEN + TOKLEN] = cmd->raw2[TOKLEN + TOKLEN] = '\0'; + for (size_t i = 0; i < strlen(cmd->raw1); i++) + cmd->raw1[i] = toupper(cmd->raw1[i]); + for (size_t i = 0; i < strlen(cmd->raw2); i++) + cmd->raw2[i] = toupper(cmd->raw2[i]); + } } /* Hide the fact that wods are corrently packed longs */ @@ -202,20 +214,6 @@ void vspeak(const char* msg, bool blank, va_list ap) size -= len; } - // All-lowercase specifier. - if (msg[i] == 'L' || - msg[i] == 'C') { - packed_to_token(arg, renderp); /* unpack directly to destination */ - int len = strlen(renderp); - for (int j = 0; j < len; ++j) { - renderp[j] = tolower(renderp[j]); - } - if (msg[i] == 'C') // First char uppercase, rest lowercase. - renderp[0] = toupper(renderp[0]); - renderp += len; - size -= len; - } - previous_arg = arg; } } @@ -293,19 +291,24 @@ void echo_input(FILE* destination, const char* input_prompt, const char* input) free(prompt_and_input); } -int word_count(char* s) +int word_count(char* str) { - char* copy = xstrdup(s); char delims[] = " \t"; int count = 0; - char* word; + int inblanks = true; + + for (char *s = str; *s; s++) + if (inblanks) { + if (strchr(delims, *s) == 0) { + ++count; + inblanks = false; + } + } else { + if (strchr(delims, *s) != 0) { + inblanks = true; + } + } - word = strtok(copy, delims); - while (word != NULL) { - word = strtok(NULL, delims); - ++count; - } - free(copy); return (count); } @@ -348,11 +351,10 @@ char* get_input() bool silent_yes() { - char* reply; bool outcome; for (;;) { - reply = get_input(); + char* reply = get_input(); if (reply == NULL) { // LCOV_EXCL_START // Should be unreachable. Reply should never be NULL @@ -395,13 +397,12 @@ bool yes(const char* question, const char* yes_response, const char* no_response /* Print message X, wait for yes/no answer. If yes, print Y and return true; * if no, print Z and return false. */ { - char* reply; bool outcome; for (;;) { speak(question); - reply = get_input(); + char* reply = get_input(); if (reply == NULL) { // LCOV_EXCL_START // Should be unreachable. Reply should never be NULL @@ -504,28 +505,32 @@ int get_special_vocab_id(const char* word) long get_vocab_id(const char* word) // Search the vocab categories in order for the supplied word. { + /* Check for an empty string */ + if (strncmp(word, "", sizeof("")) == 0) + return (WORD_EMPTY); + long ref_num; /* FIXME: Magic numbers related to vocabulary */ ref_num = get_motion_vocab_id(word); if (ref_num != WORD_NOT_FOUND) - return (ref_num + 0); // FIXME: replace with a proper hash + return MOTION_WORD(ref_num); ref_num = get_object_vocab_id(word); if (ref_num != WORD_NOT_FOUND) - return (ref_num + 1000); // FIXME: replace with a proper hash + return OBJECT_WORD(ref_num); ref_num = get_action_vocab_id(word); if (ref_num != WORD_NOT_FOUND) - return (ref_num + 2000); // FIXME: replace with a proper hash + return ACTION_WORD(ref_num); ref_num = get_special_vocab_id(word); if (ref_num != WORD_NOT_FOUND) - return (ref_num + 3000); // FIXME: replace with a proper hash + return SPECIAL_WORD(ref_num); // Check for the reservoir magic word. if (strcasecmp(word, game.zzword) == 0) - return (PART + 2000); // FIXME: replace with a proper hash + return ACTION_WORD(PART); return (WORD_NOT_FOUND); } @@ -666,7 +671,7 @@ long randrange(long range) return range * get_next_lcg_value() / game.lcg_m; } -void make_zzword(char zzword[TOKLEN+1]) +void make_zzword(char zzword[TOKLEN + 1]) { for (int i = 0; i < 5; ++i) { zzword[i] = 'A' + randrange(26);