From f28744d997c21a1d185869f86652a59d7f2a0f16 Mon Sep 17 00:00:00 2001 From: Martin Read Date: Mon, 10 Feb 2014 14:40:41 +0000 Subject: [PATCH] Save files now go in $HOME/.local/share/com.blackswordsonics/victrix-abyssi by default This commit also does some Neronic fiddling as an effort to make sloccount classify certain files correctly. --- MANIFEST | 2 + Makefile | 34 +++-- coord.hh | 6 +- fov.hh | 3 +- log.cc | 200 ++++++++++++++++++++++++++++- main.cc | 133 +------------------ map.hh | 6 +- monsters.hh | 3 +- objects.hh | 3 +- rng.hh | 4 +- u.cc | 30 ----- util.c | 373 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ util.h | 61 +++++++++ victrix-abyssi.hh | 7 +- 14 files changed, 681 insertions(+), 184 deletions(-) create mode 100644 util.c create mode 100644 util.h diff --git a/MANIFEST b/MANIFEST index f8cff9b..8fb85c1 100644 --- a/MANIFEST +++ b/MANIFEST @@ -35,4 +35,6 @@ rng.hh sorcery.cc sorcery.hh u.cc +util.c +util.h victrix-abyssi.hh diff --git a/Makefile b/Makefile index 827505f..bf3d61f 100644 --- 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 diff --git a/coord.hh b/coord.hh index 0a0db14..7f06491 100644 --- a/coord.hh +++ b/coord.hh @@ -45,8 +45,9 @@ template 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 --- 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 --- 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 #include #include @@ -35,9 +36,58 @@ #include #include #include -#include -#include +#include +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 container *dest using character names + * as keys and filenames as values. + * + * \param dest Pointer to map 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 *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 --- 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 --- 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; diff --git a/monsters.hh b/monsters.hh index e4add90..150f632 100644 --- a/monsters.hh +++ b/monsters.hh @@ -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; diff --git a/objects.hh b/objects.hh index 762b440..97e695f 100644 --- a/objects.hh +++ b/objects.hh @@ -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 --- 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 --- 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 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 + +#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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 */ diff --git a/victrix-abyssi.hh b/victrix-abyssi.hh index f7d5ac0..2b9c49a 100644 --- a/victrix-abyssi.hh +++ b/victrix-abyssi.hh @@ -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 */ -- 2.11.0