sorcery.cc
sorcery.hh
u.cc
+util.c
+util.h
victrix-abyssi.hh
include features.mk
GENERATED_OBJS:=permobj.o
-GENERATED_SOURCE:=permobj.cc pobj_id.hh dirs.mk features.mk
-HANDWRITTEN_OBJS:=combat.o display-nc.o fov.o log.o main.o map.o misc.o monsters.o mon2.o notify-local-tty.o objects.o permons.o pmon2.o rng.o sorcery.o u.o
+GENERATED_SOURCE:=permobj.cc pobj_id.hh
+GENERATED_MAKES:=dirs.mk features.mk
+HANDWRITTEN_OBJS:=combat.o display-nc.o fov.o log.o main.o map.o misc.o monsters.o mon2.o notify-local-tty.o objects.o permons.o pmon2.o rng.o sorcery.o u.o util.o
OBJS:=$(GENERATED_OBJS) $(HANDWRITTEN_OBJS)
GAME:=victrix-abyssi
MAJVERS:=1
MINVERS:=0
-COMMON_CXXFLAGS:=-c -Wall -Wwrite-strings -Wno-unused-but-set-variable -Wredundant-decls -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -std=gnu++11 -D_FORTIFY_SOURCE=2
+COMMON_CFLAGS:=-Wall -Wwrite-strings -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -std=gnu11 -D_FORTIFY_SOURCE=2
+COMMON_CXXFLAGS:=-Wall -Wwrite-strings -Wno-unused-but-set-variable -Wredundant-decls -Wunreachable-code -Wformat -Werror=format-security -fstack-protector --param=ssp-buffer-size=4 -DMAJVERS=$(MAJVERS) -DMINVERS=$(MINVERS) -std=gnu++11 -D_FORTIFY_SOURCE=2
+PRODUCTION_CFLAGS:=$(COMMON_CFLAGS)
+DEVELOPMENT_CFLAGS:=$(COMMON_CFLAGS) -g -Werror
PRODUCTION_CXXFLAGS:=$(COMMON_CXXFLAGS)
DEVELOPMENT_CXXFLAGS:=$(COMMON_CXXFLAGS) -g -Werror
LIBS=-lpanelw -lncursesw -lxdg-basedir -lm
ARCHIVEDIR:=victrix-abyssi-$(MAJVERS).$(MINVERS)
ARCHIVENAME:=victrix-abyssi_$(MAJVERS).$(MINVERS)
+COMMON_LDFLAGS:=-Wl,-z,relro
+DEVELOPMENT_LDFLAGS:=$(COMMON_LDFLAGS) -g
## PHONY targets in this section, please
-.PHONY: all archive clean code-docs debianize-archive install my-debworkflow spotless
+.PHONY: all archive clean code-docs code-docs-clean debianize-archive install my-debworkflow my-debclean spotless
all: $(GAME)
debianize-archive: archive
mv $(ARCHIVENAME).tar.gz $(ARCHIVENAME).orig.tar.gz
-my-debworkflow: debianize-archive
+my-debworkflow: my-debclean debianize-archive
mkdir archive-test
cp $(ARCHIVENAME).orig.tar.gz archive-test
(cd archive-test && tar xzf $(ARCHIVENAME).orig.tar.gz)
code-docs-clean:
-rm -rf html latex
+gitclean: spotless
+ -rm -rf $(GENERATED_MAKES)
+
+generated-clean:
+ -rm -f $(GENERATED_SOURCE)
+
install: all
echo "man6dir is $(man6dir)"
install -D $(GAME) $(DESTDIR)$(gamesdir)/$(GAME)
install -D $(GAME).6 $(DESTDIR)$(man6dir)/$(GAME).6
-spotless: clean
- -rm -f $(GENERATED_SOURCE)
+spotless: clean code-docs-clean my-debclean generated-clean
## Real targets only after this point please
$(GAME): $(OBJS)
- $(CXX) $(LDFLAGS) $(OBJS) $(LIBS) -o $(GAME)
+ $(CXX) $(DEVELOPMENT_LDFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $(GAME)
.cc.o:
- $(CXX) $(DEVELOPMENT_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) $< -o $@
+ $(CXX) -c $(DEVELOPMENT_CXXFLAGS) $(CPPFLAGS) $(CXXFLAGS) $< -o $@
+
+.c.o:
+ $(CC) -c $(DEVELOPMENT_CFLAGS) $(CPPFLAGS) $(CFLAGS) $< -o $@
## Dependencies for autogeneration
permobj.cc pobj_id.hh: default.permobjs $(srcdir)/pobj_comp
u.o: u.cc combat.hh victrix-abyssi.hh notify.hh monsters.hh objects.hh pobj_id.hh
+util.o: util.c util.h
+
# vim:ts=8:sts=8:sw=8:noexpandtab
* The lt/le/ge/gt operators impose a total ordering, allowing Offset to
* be used as the key-type of std::map.
*/
-struct Offset
+class Offset
{
+public:
int y;
int x;
bool ecardinal(void) const { return ((y && !x) || (x && !y)); }
* The lt/le/ge/gt operators impose a total ordering, allowing Coord to
* be used as the key-type of std::map.
*/
-struct Coord
+class Coord
{
+public:
int y;
int x;
int dist_cheb(Coord const& right) const { return std::max(myabs(y - right.y), myabs(x - right.x)); }
#define Visflag_central 0x02u
typedef uint8_t Vision_flags; /* 8 bits should do for now */
-struct radiance_data
+class radiance_data
{
+public:
Vision_flags affected[FOV_SIDE][FOV_SIDE];
Coord centre;
int radius;
/* \file log.cc
- * \brief death log handler for Victrix Abyssi
+ * \brief File I/O functions for Victrix Abyssi
*/
/* Copyright 2014 Martin Read
*/
#include "victrix-abyssi.hh"
+#include "util.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <limits.h>
-#include <deque>
-#include <basedir.h>
+#include <map>
+static void rebuild_mapobjs(void);
+static void rebuild_mapmons(void);
+
+int datadir_fd;
+int confdir_fd;
+
+/*! \brief Read configuration files.
+ *
+ * \todo Implement configuration files.
+ */
+void load_config(void)
+{
+ int i;
+ i = setup_dirs("com.blackswordsonics", "victrix-abyssi", &datadir_fd, &confdir_fd);
+ if (i < 0)
+ {
+ perror("victrix-abyssi: couldn't access configuration directories");
+ exit(1);
+ }
+ // TODO write this function
+}
+
+/*! \brief Rewrite configuration files.
+ *
+ * \todo Implement configuration files.
+ */
+int rewrite_config(void)
+{
+ // TODO write this function
+ return 0;
+}
+
+/*! \brief Get a list of extant saved games and character names
+ *
+ * Fill in the map<string,string> container *dest using character names
+ * as keys and filenames as values.
+ *
+ * \param dest Pointer to map<string,string> container to fill with data
+ * \return Number of charname/filename pairs written to container
+ * \todo Basic implementation
+ * \todo Deal gracefully with a savefile going away before we open it
+ */
+int get_savefile_list(std::map<std::string, std::string> *dest)
+{
+ // TODO write this function
+ return 0;
+}
+
+/*! \brief Record the details of the Princess's death to the log
+ */
void log_death(Death d, char const *what)
{
FILE *fp;
}
}
+void wrapped_system(char const *cmd)
+{
+ int i = system(cmd);
+ if (i != 0)
+ {
+ throw(i);
+ }
+}
+
+void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *fp)
+{
+ size_t i = fread(buf, size, nmemb, fp);
+ if (i != nmemb)
+ {
+ throw(i);
+ }
+}
+
+void save_game(void)
+{
+ FILE *fp;
+ int fd;
+ uint32_t tmp;
+ fd = openat(datadir_fd, "victrix-abyssi.sav", O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1)
+ {
+ return;
+ }
+ fp = fdopen(fd, "wb");
+ if (!fp)
+ {
+ close(fd);
+ return;
+ }
+ /* Write out the player. */
+ fwrite(&u, 1, sizeof u, fp);
+ tmp = htonl(depth);
+ fwrite(&tmp, 1, sizeof tmp, fp);
+ tmp = htonl(game_tick);
+ fwrite(&tmp, 1, sizeof tmp, fp);
+ serialize_level(fp, &lvl);
+ fwrite(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp);
+ fwrite(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp);
+ fwrite(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp);
+ /* Clean up */
+ fflush(fp);
+ fsync(fd);
+ fclose(fp);
+ game_finished = 1;
+ return;
+}
+
+/*! \brief Monster map reinitialization after reload
+ */
+static void rebuild_mapmons(void)
+{
+ int i;
+ for (i = 0; i < 100; i++)
+ {
+ if (monsters[i].used)
+ {
+ lvl.set_mon_at(monsters[i].pos, i);
+ }
+ }
+}
+
+/*! \brief Object map reinitialization after reload
+ */
+static void rebuild_mapobjs(void)
+{
+ int i;
+ for (i = 0; i < 100; i++)
+ {
+ if (objects[i].used && !objects[i].with_you)
+ {
+ lvl.set_obj_at(objects[i].pos, i);
+ }
+ }
+}
+
+int load_game(void)
+{
+ int fd;
+ FILE *fp;
+ fd = openat(datadir_fd, "victrix-abyssi.sav", O_RDONLY, 0);
+ if (fd == -1)
+ {
+ return -1;
+ }
+ fp = fdopen(fd, "rb");
+ if (!fp)
+ {
+ return -1;
+ }
+ wrapped_fread(&u, 1, sizeof u, fp);
+ wrapped_fread(&depth, 1, sizeof depth, fp);
+ depth = ntohl(depth);
+ wrapped_fread(&game_tick, 1, sizeof game_tick, fp);
+ game_tick = ntohl(game_tick);
+ deserialize_level(fp, &lvl);
+ wrapped_fread(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp);
+ wrapped_fread(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp);
+ wrapped_fread(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp);
+ rebuild_mapobjs();
+ rebuild_mapmons();
+ fclose(fp);
+ look_around_you();
+ status_updated = 1;
+ map_updated = 1;
+ hard_redraw = 1;
+ unlink("victrix-abyssi.sav");
+ return 0;
+}
+
+void write_char_dump(void)
+{
+ FILE *fp;
+ char filename[32];
+ int i;
+ snprintf(filename, 31, "%s.dump", u.name);
+ fp = fopen(filename, "w");
+ if (fp == NULL)
+ {
+ debug_dump_write_failed();
+ return;
+ }
+ fprintf(fp, "%s, level %d princess (%d XP)\n", u.name, u.level, u.experience);
+ fprintf(fp, "%d of %d hit points.\n", u.hpcur, u.hpmax);
+ fprintf(fp, "Body %d (%d damage).\n", u.body, u.bdam);
+ fprintf(fp, "Agility %d (%d damage).\n", u.agility, u.adam);
+ fprintf(fp, "Defence %d.\n", u.defence);
+ fprintf(fp, "Inventory:\n");
+ for (i = 0; i < 19; i++)
+ {
+ if (u.inventory[i] != NO_OBJ)
+ {
+ fprint_obj_name(fp, u.inventory[i]);
+ fputc('\n', fp);
+ }
+ }
+ fflush(fp);
+ fclose(fp);
+}
+
/* log.cc */
// vim:cindent:expandtab
Offset const Stationary = { 0, 0 };
void save_game(void);
-static void rebuild_mapobjs(void);
-static void rebuild_mapmons(void);
static void main_loop(void);
bool game_finished;
int32_t game_tick;
bool wizard_mode = WIZARD_MODE;
-/*! \brief Monster map reinitialization after reload
+/*! \brief pick a random Chebyshev cardinal
*/
-static void rebuild_mapmons(void)
-{
- int i;
- for (i = 0; i < 100; i++)
- {
- if (monsters[i].used)
- {
- lvl.set_mon_at(monsters[i].pos, i);
- }
- }
-}
-
-/*! \brief Object map reinitialization after reload
- */
-static void rebuild_mapobjs(void)
-{
- int i;
- for (i = 0; i < 100; i++)
- {
- if (objects[i].used && !objects[i].with_you)
- {
- lvl.set_obj_at(objects[i].pos, i);
- }
- }
-}
-
-void wrapped_system(char const *cmd)
-{
- int i = system(cmd);
- if (i != 0)
- {
- throw(i);
- }
-}
-
-void wrapped_fread(void *buf, size_t size, size_t nmemb, FILE *fp)
-{
- size_t i = fread(buf, size, nmemb, fp);
- if (i != nmemb)
- {
- throw(i);
- }
-}
-
-void save_game(void)
-{
- FILE *fp;
- uint32_t tmp;
- fp = fopen("victrix-abyssi.sav", "wb");
- /* Write out the player. */
- fwrite(&u, 1, sizeof u, fp);
- tmp = htonl(depth);
- fwrite(&tmp, 1, sizeof tmp, fp);
- tmp = htonl(game_tick);
- fwrite(&tmp, 1, sizeof tmp, fp);
- serialize_level(fp, &lvl);
- fwrite(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp);
- fwrite(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp);
- fwrite(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp);
- /* Clean up */
- fflush(fp);
- fclose(fp);
- game_finished = 1;
- return;
-}
-
-int load_game(void)
-{
- FILE *fp;
- fp = fopen("victrix-abyssi.sav", "rb");
- if (!fp)
- {
- return -1;
- }
- wrapped_fread(&u, 1, sizeof u, fp);
- wrapped_fread(&depth, 1, sizeof depth, fp);
- depth = ntohl(depth);
- wrapped_fread(&game_tick, 1, sizeof game_tick, fp);
- game_tick = ntohl(game_tick);
- deserialize_level(fp, &lvl);
- wrapped_fread(permobjs, sizeof (Permobj), NUM_OF_PERMOBJS, fp);
- wrapped_fread(monsters, sizeof (Mon), MONSTERS_IN_PLAY, fp);
- wrapped_fread(objects, sizeof (Obj), OBJECTS_IN_PLAY, fp);
- rebuild_mapobjs();
- rebuild_mapmons();
- fclose(fp);
- look_around_you();
- status_updated = 1;
- map_updated = 1;
- hard_redraw = 1;
- unlink("victrix-abyssi.sav");
- return 0;
-}
-
Offset random_step(void)
{
switch (zero_die(8))
}
}
-xdgHandle victrix_xdg_handle;
-char const * victrix_data_home;
-char const * victrix_config_home;
-std::string our_data_dir;
-std::string our_config_dir;
-
-void setup_dirs(void)
-{
- xdgHandle * foo = xdgInitHandle(&victrix_xdg_handle);
- if (!foo)
- {
- fputs("FATAL ERROR: libxdg-basedir handle initialization failed\n", stderr);
- exit(1);
- }
- victrix_data_home = xdgDataHome(foo);
- victrix_config_home = xdgConfigHome(foo);
- if (!victrix_data_home || !victrix_config_home)
- {
- fputs("FATAL ERROR: libxdg-basedir functions failed to provide data and config homes\n", stderr);
- exit(1);
- }
- our_data_dir = victrix_data_home;
- our_data_dir += "/com.blackswordsonics/victrix-abyssi";
- /* TODO Make sure the directory exists. */
- our_config_dir = victrix_config_home;
- our_config_dir += "/com.blackswordsonics/victrix-abyssi";
- /* TODO Make sure the directory exists. */
-}
-
-void load_config(void)
-{
- // TODO actually do something here.
-}
-
int main(void)
{
- setup_dirs();
load_config();
rng_init();
launch_user_interface();
#define CHUNK_AREA (CHUNK_EDGE * CHUNK_EDGE)
//! The fundamental object-like unit of cartography
-struct Chunk
+class Chunk
{
+public:
int objs[CHUNK_EDGE][CHUNK_EDGE];
int mons[CHUNK_EDGE][CHUNK_EDGE];
Terrain terrain[CHUNK_EDGE][CHUNK_EDGE];
};
//! The top-tier object for describing everything about a level
-struct Level
+class Level
{
+public:
Chunk ***chunks;
Offset origin_off;
int chunks_high;
/* XXX struct mon */
#define MONSTERS_IN_PLAY 100
-struct Mon {
+class Mon {
+public:
int mon_id;
Coord pos;
Coord ai_lastpos;
/* XXX Obj */
#define OBJ_MAX_DUR 100
#define OBJECTS_IN_PLAY 100
-struct Obj {
+class Obj {
+public:
int obj_id;
bool used; /* Entry is occupied. */
bool with_you; /* Preserved when item DB is reaped on level change. */
* See http://burtleburtle.net/bob/rand/smallprng.html for an explanation
* of this generator.
*/
-struct Smallprng
+class Smallprng
{
+private:
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
+public:
enum { magic = 0xf1ea5eed };
void init(uint32_t seed)
{
return really;
}
-void write_char_dump(void)
-{
- FILE *fp;
- char filename[32];
- int i;
- snprintf(filename, 31, "%s.dump", u.name);
- fp = fopen(filename, "w");
- if (fp == NULL)
- {
- debug_dump_write_failed();
- return;
- }
- fprintf(fp, "%s, level %d princess (%d XP)\n", u.name, u.level, u.experience);
- fprintf(fp, "%d of %d hit points.\n", u.hpcur, u.hpmax);
- fprintf(fp, "Body %d (%d damage).\n", u.body, u.bdam);
- fprintf(fp, "Agility %d (%d damage).\n", u.agility, u.adam);
- fprintf(fp, "Defence %d.\n", u.defence);
- fprintf(fp, "Inventory:\n");
- for (i = 0; i < 19; i++)
- {
- if (u.inventory[i] != NO_OBJ)
- {
- fprint_obj_name(fp, u.inventory[i]);
- fputc('\n', fp);
- }
- }
- fflush(fp);
- fclose(fp);
-}
-
void u_init(char const *name)
{
if (!name || !*name)
--- /dev/null
+/* \file util.c
+ * \brief Various helper functions in pure C, originally for Victrix Abyssi
+ */
+
+/* Copyright 2014 Martin Read
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "util.h"
+#include <basedir.h>
+
+#undef UTIL_DEBUG
+//#define UTIL_DEBUG
+
+#define UTIL_PATHSEP_CHAR '/'
+#define UTIL_PATHSEP_STRING "/"
+
+static char *app_suffix(char const *vendor, char const *app);
+
+/*! \brief Check whether a specified path points to an existing directory.
+ */
+int verify_dir_available(char const *path)
+{
+ struct stat s;
+ int i;
+ if (!path || !*path)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ i = stat(path, &s);
+ if (i != 0)
+ {
+ return -1;
+ }
+ if ((s.st_mode & S_IFMT) != S_IFDIR)
+ {
+ errno = ENOTDIR;
+ return -1;
+ }
+ return 0;
+}
+
+static xdgHandle util_xdg_handle;
+static char const *util_data_home;
+static char const *util_config_home;
+static char *util_data_dir;
+static char *util_config_dir;
+static char *util_app_subdir;
+static DIR *util_data_dp;
+static DIR *util_config_dp;
+
+/*! \brief Set up directory access for config and data files
+ *
+ * This function uses libxdg-basedir to obtain the XDG-specified configuration
+ * and data home directories, then autovivifies app-specific subdirectories
+ * within those directories.
+ *
+ * \param vendor "Vendor name" for building directory suffix
+ * \param app "Application name" for building directory suffix
+ * \param data_fd Location to store data directory fd
+ * \param config_fd Location to store config directory fd
+ */
+int setup_dirs(char const *vendor, char const *app, int *data_fd, int *config_fd)
+{
+ int i;
+ xdgHandle *x;
+ if (!data_fd || !config_fd)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!util_data_dp || !util_config_dp)
+ {
+ x = xdgInitHandle(&util_xdg_handle);
+ if (!x)
+ {
+ return -1;
+ }
+ if (!util_app_subdir)
+ {
+ if (!vendor || !app || !*vendor || !*app)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ util_app_subdir = app_suffix(vendor, app);
+ if (!util_app_subdir)
+ {
+ return -1;
+ }
+ }
+ if (!util_data_home)
+ {
+ util_data_home = xdgDataHome(x);
+ if (!util_data_home)
+ {
+ return -1;
+ }
+ }
+ if (!util_config_home)
+ {
+ util_config_home = xdgConfigHome(x);
+ if (!util_config_home)
+ {
+ return -1;
+ }
+ }
+ if (!util_data_dir)
+ {
+ util_data_dir = strbuild(util_data_home, util_app_subdir);
+ if (!util_data_dir)
+ {
+ return -1;
+ }
+ }
+ if (!util_config_dir)
+ {
+ util_config_dir = strbuild(util_config_home, util_app_subdir);
+ if (!util_config_dir)
+ {
+ return -1;
+ }
+ }
+ if (!util_config_dp)
+ {
+ i = verify_dir_available(util_config_dir);
+ if (i != 0)
+ {
+ if (errno == ENOENT)
+ {
+ i = my_makepath(util_config_dir, S_IRWXU);
+ if (i != 0)
+ {
+ return -1;
+ }
+ }
+ }
+ util_config_dp = opendir(util_config_dir);
+ if (!util_config_dp)
+ {
+ return -1;
+ }
+ }
+ if (!util_data_dp)
+ {
+ i = verify_dir_available(util_data_dir);
+ if (i != 0)
+ {
+ if (errno == ENOENT)
+ {
+ i = my_makepath(util_data_dir, S_IRWXU);
+ if (i != 0)
+ {
+ return -1;
+ }
+ }
+ }
+ util_data_dp = opendir(util_data_dir);
+ if (!util_data_dp)
+ {
+ return -1;
+ }
+ }
+ }
+ *data_fd = dirfd(util_data_dp);
+ *config_fd = dirfd(util_config_dp);
+ return 0;
+}
+
+/*! \brief Create a C string that is a concatenation of two existing strings
+ *
+ * The string returned by this function has been allocated with malloc() and
+ * should be released with free() when it is no longer required.
+ */
+char *strbuild(char const *left, char const *right)
+{
+ if (!left || !right)
+ {
+ return NULL;
+ }
+ size_t s1 = strlen(left);
+ size_t s2 = strlen(right);
+ size_t s = s1 + s2 + 1;
+ char *str = (char *) malloc(s);
+ if (str)
+ {
+ strcpy(str, left);
+ strcpy(str + s1, right);
+ }
+ return str;
+}
+
+/*! \brief Iteratively build the directories of a given path.
+ *
+ * This function exists because there is no POSIX (or Linux) syscall that does
+ * the equivalent of system("mkdir -p my/path/here") - which is really
+ * unfortunate, because this means that creating such a directory structure
+ * is inherently racy.
+ */
+int my_makepath(char const *path, int mode)
+{
+ if (!path)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "my_makepath(\"%s\", %d)\n", path, mode);
+#endif
+ char *realpath = strdup(path);
+ char *pathtrak = ((realpath[0] == '/') ? realpath + 1 : realpath);
+ /* this will FUBAR on Windows, but since I'm not responsible for the
+ * Windows port, whoever *is* responsible for the Windows port can have
+ * the pleasure of figuring out how to do the equivalent job. */
+ int rv = -1;
+ do
+ {
+ struct stat s;
+ int i;
+ pathtrak = strchr(pathtrak, '/');
+ if (pathtrak)
+ {
+ pathtrak[0] = '\0';
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "Checking %s (pathtrak offset %d)\n", realpath, (int) (pathtrak - realpath));
+#endif
+ i = stat(realpath, &s);
+ if (i == 0)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "stat(\"%s\") returned 0\n", realpath);
+#endif
+ if ((s.st_mode & S_IFMT) == S_IFDIR)
+ {
+ pathtrak[0] = '/';
+ ++pathtrak;
+ }
+ else
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "not a directory\n");
+#endif
+ errno = ENOTDIR;
+ rv = -1;
+ break;
+ }
+ }
+ else
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "stat(\"%s\") returned %d, errno %d (%s)\n",
+ realpath, i, errno, strerror(errno));
+#endif
+ if (errno == ENOENT)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "mkdir(\"%s\", %d)\n", realpath, mode);
+#endif
+ i = mkdir(realpath, mode);
+ if (i != 0)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "mkdir(\"%s\", %d) returned %d, errno %d (%s)\n", realpath, mode, i, errno, strerror(errno));
+#endif
+ rv = -1;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ i = stat(realpath, &s);
+ if (i == 0)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "stat(\"%s\") returned 0\n", realpath);
+#endif
+ if ((s.st_mode & S_IFMT) == S_IFDIR)
+ {
+ rv = 0;
+ }
+ else
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "not a directory\n");
+#endif
+ errno = ENOTDIR;
+ rv = -1;
+ }
+ }
+ else
+ {
+ if (errno == ENOENT)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "mkdir(\"%s\", %d)\n", realpath, mode);
+#endif
+ i = mkdir(realpath, mode);
+ if (i != 0)
+ {
+#ifdef UTIL_DEBUG
+ fprintf(stderr, "mkdir(\"%s\", %d) returned %d, errno %d (%s)\n", realpath, mode, i, errno, strerror(errno));
+#endif
+ rv = -1;
+ }
+ else
+ {
+ rv = 0;
+ }
+ }
+ }
+ }
+ } while (pathtrak);
+ free(realpath);
+ return rv;
+}
+
+/*! \brief compose a directory suffix from a vendor name and an app name
+ */
+static char *app_suffix(char const *vendor, char const *app)
+{
+ size_t s1;
+ size_t s2;
+ size_t s3;
+ size_t s4;
+ char *str;
+ if (!vendor || !*vendor || !app || !*app)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+ // TODO include proper sanitization of vendor and app strings
+ s1 = strlen(vendor);
+ s2 = strlen(UTIL_PATHSEP_STRING);
+ s3 = strlen(app);
+ s4 = 1 + s3 + 2*s2 + s1;
+ str = (char *) malloc(s4);
+ if (str)
+ {
+ strcpy(str, UTIL_PATHSEP_STRING);
+ strcpy(str + s2, vendor);
+ strcpy(str + s1 + s2, UTIL_PATHSEP_STRING);
+ strcpy(str + s1 + 2*s2, app);
+ }
+ return str;
+}
+
+/*
+ * vim:ts=8:sts=4:sw=4:expandtab:cindent
+ */
+/* util.c */
--- /dev/null
+/* \file util.h
+ * \brief Header for pure C helper functions
+ */
+
+/* Copyright 2014 Martin Read
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int setup_dirs(char const *vendor, char const *app, int *data_fd, int *config_fd);
+int verify_dir_available(char const *path);
+int my_makepath(char const *path, int mode);
+char *strbuild(char const *left, char const *right);
+int stdio_mode_to_unistd_flags(char const *mode);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
+
+/*
+ * vim:ts=8:sts=4:sw=4:expandtab:cindent
+ */
+/* util.h */
/* XXX STRUCTURES XXX */
/* XXX struct player */
-struct Player {
+class Player {
+public:
char name[17]; /* including '\0' the fencepost. */
Coord pos;
int body; /* determines mace to-hit, melee damage, max 99 */
extern int evasion_penalty(int obj);
/* XXX log.cc */
-void log_death(Death d, char const *what);
+extern void log_death(Death d, char const *what);
+extern void load_config(void);
/* XXX u.c data and funcs */
extern void u_init(char const *name);
extern void write_char_dump(void);
extern void update_player(void);
extern struct Player u;
+
#endif
/* victrix-abyssi.hh */