Save files now go in $HOME/.local/share/com.blackswordsonics/victrix-abyssi by default
authorMartin Read <martin@blackswordsonics.com>
Mon, 10 Feb 2014 14:40:41 +0000 (14:40 +0000)
committerMartin Read <martin@blackswordsonics.com>
Mon, 10 Feb 2014 14:40:41 +0000 (14:40 +0000)
This commit also does some Neronic fiddling as an effort to make sloccount
classify certain files correctly.

14 files changed:
MANIFEST
Makefile
coord.hh
fov.hh
log.cc
main.cc
map.hh
monsters.hh
objects.hh
rng.hh
u.cc
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]
victrix-abyssi.hh

index f8cff9b..8fb85c1 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -35,4 +35,6 @@ rng.hh
 sorcery.cc
 sorcery.hh
 u.cc
+util.c
+util.h
 victrix-abyssi.hh
index 827505f..bf3d61f 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -6,22 +6,28 @@ include dirs.mk
 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)
 
@@ -34,7 +40,7 @@ archive: clean permobj.cc pobj_id.hh
 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)
@@ -54,21 +60,29 @@ code-docs:
 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
@@ -106,4 +120,6 @@ sorcery.o: sorcery.cc victrix-abyssi.hh notify.hh sorcery.hh objects.hh monsters
 
 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
index 0a0db14..7f06491 100644 (file)
--- a/coord.hh
+++ b/coord.hh
@@ -45,8 +45,9 @@ template <typename T> inline T mysign(T val) { return (val < 0) ? -1 : ((val > 0
  *  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)); }
@@ -73,8 +74,9 @@ struct Offset
  *  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)); }
diff --git a/fov.hh b/fov.hh
index 21b40c2..8c344e8 100644 (file)
--- a/fov.hh
+++ b/fov.hh
@@ -52,8 +52,9 @@ typedef enum rad_eval_order Rad_eval_order;
 #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;
diff --git a/log.cc b/log.cc
index 86f60b4..965c209 100644 (file)
--- a/log.cc
+++ b/log.cc
@@ -1,5 +1,5 @@
 /* \file log.cc
- * \brief death log handler for Victrix Abyssi
+ * \brief File I/O functions for Victrix Abyssi
  */
 
 /* Copyright 2014 Martin Read
@@ -27,6 +27,7 @@
  */
 
 #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;
@@ -71,5 +121,149 @@ void log_death(Death d, char const *what)
     }
 }
 
+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
diff --git a/main.cc b/main.cc
index 0b050c3..9d5ffbb 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -52,109 +52,13 @@ Offset const Northwest = { -1, -1 };
 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))
@@ -471,43 +375,8 @@ void main_loop(void)
     }
 }
 
-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();
diff --git a/map.hh b/map.hh
index 1b22ddd..724bba3 100644 (file)
--- a/map.hh
+++ b/map.hh
@@ -102,8 +102,9 @@ extern Terrain_props terrain_props[NUM_TERRAINS];
 #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];
@@ -125,8 +126,9 @@ struct Chunk
 };
 
 //! The top-tier object for describing everything about a level
-struct Level
+class Level
 {
+public:
     Chunk ***chunks;
     Offset origin_off;
     int chunks_high;
index e4add90..150f632 100644 (file)
@@ -91,7 +91,8 @@ enum AI_mode {
 
 /* XXX struct mon */
 #define MONSTERS_IN_PLAY 100
-struct Mon {
+class Mon {
+public:
     int mon_id;
     Coord pos;
     Coord ai_lastpos;
index 762b440..97e695f 100644 (file)
@@ -82,7 +82,8 @@ extern Permobj permobjs[NUM_OF_PERMOBJS];
 /* 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. */
diff --git a/rng.hh b/rng.hh
index d46bd5c..408be11 100644 (file)
--- a/rng.hh
+++ b/rng.hh
@@ -53,12 +53,14 @@ extern Coord exclusive_boxed(Coord topleft, Coord botright);
  * 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)
     {
diff --git a/u.cc b/u.cc
index 4694063..8defccb 100644 (file)
--- a/u.cc
+++ b/u.cc
@@ -326,36 +326,6 @@ int do_death(Death d, char const *what)
     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)
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..d2f3d38
--- /dev/null
+++ b/util.c
@@ -0,0 +1,373 @@
+/* \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 */
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..126ecad
--- /dev/null
+++ b/util.h
@@ -0,0 +1,61 @@
+/* \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 */
index f7d5ac0..2b9c49a 100644 (file)
@@ -142,7 +142,8 @@ enum Fell_powers {
 /* 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 */
@@ -232,7 +233,8 @@ extern void damage_obj(int obj);
 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);
@@ -252,6 +254,7 @@ extern Pass_fail teleport_u(void);
 extern void update_player(void);
 
 extern struct Player u;
+
 #endif
 
 /* victrix-abyssi.hh */