/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "glk/adrift/scare.h" #include "glk/adrift/sxprotos.h" #include "common/debug.h" #include "common/str.h" #include "common/file.h" #include "common/textconsole.h" namespace Glk { namespace Adrift { /* * sx_trace() * * Debugging trace function; printf wrapper that writes to stdout. Note that * this differs from sc_trace(), which writes to stderr. We use stdout so * that trace output is synchronized to test expectation failure messages. */ void sx_trace(const sc_char *format, ...) { va_list ap; assert(format); va_start(ap, format); Common::String line = Common::String::vformat(format, ap); va_end(ap); debug("%s", line.c_str()); } /* * sx_error() * sx_fatal() * * Error reporting functions. sx_error() prints a message and continues. * sx_fatal() prints a message, then calls abort(). */ void sx_error(const sc_char *format, ...) { va_list ap; assert(format); va_start(ap, format); Common::String line = Common::String::vformat(format, ap); va_end(ap); warning("%s", line.c_str()); } void sx_fatal(const sc_char *format, ...) { va_list ap; assert(format); va_start(ap, format); Common::String line = Common::String::vformat(format, ap); va_end(ap); error("%s", line.c_str()); } /* Unique non-heap address for zero size malloc() and realloc() requests. */ static void *sx_zero_allocation = &sx_zero_allocation; /* * sx_malloc() * sx_realloc() * sx_free() * * Non-failing wrappers around malloc functions. Newly allocated memory is * cleared to zero. In ANSI/ISO C, zero byte allocations are implementation- * defined, so we have to take special care to get predictable behavior. */ void *sx_malloc(size_t size) { void *allocated; if (size == 0) return sx_zero_allocation; allocated = malloc(size); if (!allocated) sx_fatal("sx_malloc: requested %lu bytes\n", (sc_uint) size); else if (allocated == sx_zero_allocation) sx_fatal("sx_malloc: zero-byte allocation address returned\n"); memset(allocated, 0, size); return allocated; } void *sx_realloc(void *pointer, size_t size) { void *allocated; if (size == 0) { sx_free(pointer); return sx_zero_allocation; } if (pointer == sx_zero_allocation) pointer = NULL; allocated = realloc(pointer, size); if (!allocated) sx_fatal("sx_realloc: requested %lu bytes\n", (sc_uint) size); else if (allocated == sx_zero_allocation) sx_fatal("sx_realloc: zero-byte allocation address returned\n"); if (!pointer) memset(allocated, 0, size); return allocated; } void sx_free(void *pointer) { if (sx_zero_allocation != &sx_zero_allocation) sx_fatal("sx_free: write to zero-byte allocation address detected\n"); if (pointer && pointer != sx_zero_allocation) free(pointer); } /* * sx_fopen() * * Open a file for a given test name with the extension and mode supplied. * Returns NULL if unsuccessful. */ Common::SeekableReadStream *sx_fopen(const sc_char *name, const sc_char *extension, const sc_char *mode) { assert(name && extension && mode); Common::String filename = Common::String::format("%s.%s", name, extension); Common::File *f = new Common::File(); if (f->open(filename)) return f; delete f; return nullptr; } /* Miscellaneous general ascii constants. */ static const sc_char NUL = '\0'; /* * sx_isspace() * sx_isprint() * * Built in replacements for locale-sensitive libc ctype.h functions. */ static sc_bool sx_isspace(sc_char character) { static const sc_char *const WHITESPACE = "\t\n\v\f\r "; return character != NUL && strchr(WHITESPACE, character) != NULL; } static sc_bool sx_isprint(sc_char character) { static const sc_int MIN_PRINTABLE = ' ', MAX_PRINTABLE = '~'; return character >= MIN_PRINTABLE && character <= MAX_PRINTABLE; } /* * sx_trim_string() * * Trim leading and trailing whitespace from a string. Modifies the string * in place, and returns the string address for convenience. */ sc_char *sx_trim_string(sc_char *string) { sc_int index_; assert(string); for (index_ = strlen(string) - 1; index_ >= 0 && sx_isspace(string[index_]); index_--) string[index_] = NUL; for (index_ = 0; sx_isspace(string[index_]);) index_++; memmove(string, string + index_, strlen(string) - index_ + 1); return string; } /* * sx_normalize_string() * * Trim a string, set all runs of whitespace to a single space character, * and convert all non-printing characters to '?'. Modifies the string in * place, and returns the string address for convenience. */ sc_char *sx_normalize_string(sc_char *string) { sc_int index_; assert(string); string = sx_trim_string(string); for (index_ = 0; string[index_] != NUL; index_++) { if (sx_isspace(string[index_])) { sc_int cursor; string[index_] = ' '; for (cursor = index_ + 1; sx_isspace(string[cursor]);) cursor++; memmove(string + index_ + 1, string + cursor, strlen(string + cursor) + 1); } else if (!sx_isprint(string[index_])) string[index_] = '?'; } return string; } char *adrift_fgets(char *buf, int max, Common::SeekableReadStream *s) { char *ptr = buf; char c; while (s->pos() < s->size() && --max > 0) { c = s->readByte(); if (c == '\n' || c == '\0') break; *ptr++ = c; } *ptr++ = '\0'; return buf; } } // End of namespace Adrift } // End of namespace Glk