#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) {
return (ptr);
}
-void packed_to_token(long packed, char token[6])
+void packed_to_token(long packed, char token[TOKLEN + 1])
{
// The advent->ascii mapping.
const char advent_to_ascii[] = {
}
}
-long token_to_packed(const char token[6])
+long token_to_packed(const char token[])
{
const char ascii_to_advent[] = {
63, 63, 63, 63, 63, 63, 63, 63,
};
size_t t_len = strlen(token);
+ if (t_len > TOKLEN)
+ t_len = TOKLEN;
long packed = 0;
for (size_t i = 0; i < t_len; ++i) {
- char mapped = ascii_to_advent[(int) token[i]];
+ char mapped = ascii_to_advent[(int) toupper(token[i])];
packed |= (mapped << (6 * i));
}
return (packed);
}
-void tokenize(char* raw, long tokens[4])
+void tokenize(char* raw, struct command_t *cmd)
{
- // set each token to 0
- for (int i = 0; i < 4; ++i)
- tokens[i] = 0;
+ memset(cmd, '\0', sizeof(struct command_t));
- // grab the first two words
- char* words[2];
- words[0] = (char*) xmalloc(strlen(raw) + 1);
- words[1] = (char*) xmalloc(strlen(raw) + 1);
- int word_count = sscanf(raw, "%s%s", words[0], words[1]);
-
- // make space for substrings and zero it out
- char chunk_data[][6] = {
- {"\0\0\0\0\0"},
- {"\0\0\0\0\0"},
- {"\0\0\0\0\0"},
- {"\0\0\0\0\0"},
- };
-
- // break the words into up to 4 5-char substrings
- sscanf(words[0], "%5s%5s", chunk_data[0], chunk_data[1]);
- if (word_count == 2)
- sscanf(words[1], "%5s%5s", chunk_data[2], chunk_data[3]);
- free(words[0]);
- free(words[1]);
-
- // uppercase all the substrings
- for (int i = 0; i < 4; ++i)
- for (unsigned int j = 0; j < strlen(chunk_data[i]); ++j)
- chunk_data[i][j] = (char) toupper(chunk_data[i][j]);
+ /* 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
- for (int i = 0; i < 4; ++i)
- tokens[i] = token_to_packed(chunk_data[i]);
+ 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 */
*v = 0;
}
-/* I/O routines (speak, pspeak, rspeak, get_input, yes) */
+/* I/O routines (speak, pspeak, rspeak, sspeak, get_input, yes) */
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;
}
}
va_end(ap);
}
+void sspeak(const long msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ fputc('\n', stdout);
+ vprintf(arbitrary_messages[msg], ap);
+ fputc('\n', stdout);
+ va_end(ap);
+}
+
void pspeak(vocab_t msg, enum speaktype mode, int skip, bool blank, ...)
/* Find the skip+1st message from msg and print it. Modes are:
* feel = for inventory, what you can touch
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);
}
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
/* 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
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);
}
-void juggle(long object)
+void juggle(obj_t object)
/* Juggle an object by picking it up and putting it down again, the purpose
* being to get the object to the front of the chain of things at its loc. */
{
- long i, j;
+ loc_t i, j;
i = game.place[object];
j = game.fixed[object];
move(object + NOBJECTS, j);
}
-void move(long object, long where)
+void move(obj_t object, loc_t where)
/* Place any object anywhere by picking it up and dropping it. May
* already be toting, in which case the carry is a no-op. Mustn't
* pick up objects which are not at any loc, since carry wants to
drop(object, where);
}
-long put(long object, long where, long pval)
-/* PUT is the same as MOVE, except it returns a value used to set up the
+long put(obj_t object, loc_t where, long pval)
+/* put() is the same as move(), except it returns a value used to set up the
* negated game.prop values for the repository objects. */
{
move(object, where);
- return (-1) - pval;;
+ return STASHED(pval);
}
-void carry(long object, long where)
+void carry(obj_t object, loc_t where)
/* Start toting an object, removing it from the list of things at its former
* location. Incr holdng unless it was already being toted. If object>NOBJECTS
* (moving "fixed" second loc), don't change game.place or game.holdng. */
game.link[temp] = game.link[object];
}
-void drop(long object, long where)
+void drop(obj_t object, loc_t where)
/* Place an object at a given loc, prefixing it onto the game.atloc list. Decr
* game.holdng if the object was being toted. */
{
game.atloc[where] = object;
}
-long atdwrf(long where)
+long atdwrf(loc_t where)
/* Return the index of first dwarf at the given location, zero if no dwarf is
* there (or if dwarves not active yet), -1 if all dwarves are dead. Ignore
* the pirate (6th dwarf). */
return range * get_next_lcg_value() / game.lcg_m;
}
-void make_zzword(char zzword[6])
+void make_zzword(char zzword[TOKLEN + 1])
{
for (int i = 0; i < 5; ++i) {
zzword[i] = 'A' + randrange(26);
// LCOV_EXCL_STOP
/* end */
+
+void state_change(obj_t obj, long state)
+/* Object must have a change-message list for this to be useful; only some do */
+{
+ game.prop[obj] = state;
+ pspeak(obj, change, state, true);
+}
+
+/* end */