initial code
authorTero Marttila <terom@fixme.fi>
Sun, 27 Dec 2009 22:01:17 +0200
changeset 0 cff7fac35cc2
child 1 f3cde3db1fef
initial code
Makefile
src/lib/cache.c
src/lib/cache.h
src/lib/image.c
src/lib/image.h
src/lib/pngtile.h
src/shared/log.c
src/shared/log.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,80 @@
+# :set noexpandtab
+
+# warnings, and use C99 with GNU extensions
+CFLAGS = -Wall -std=gnu99 -g
+
+# preprocessor flags
+CPPFLAGS = -Isrc/
+
+# output name
+DIST_NAME = 78949E-as1
+DIST_RESOURCES = README "Learning Diary.pdf"
+
+all: depend bin/daemon lib/libnetdaemon.so bin/client
+
+bin/daemon : lib/libnetdaemon.so \
+	build/obj/daemon/daemon.o build/obj/daemon/service.o build/obj/daemon/client.o build/obj/daemon/commands.o \
+    build/obj/daemon/process.o \
+	build/obj/shared/select.o build/obj/shared/log.o build/obj/shared/util.o build/obj/shared/signal.o
+
+lib/libnetdaemon.so : \
+    build/obj/lib/client.o build/obj/lib/commands.o \
+    build/obj/shared/proto.o
+
+bin/client : lib/libnetdaemon.so build/obj/shared/log.o
+
+SRC_PATHS = $(wildcard src/*/*.c)
+SRC_NAMES = $(patsubst src/%,%,$(SRC_PATHS))
+SRC_DIRS = $(dir $(SRC_NAMES))
+
+.PHONY : dirs clean depend dist
+
+dirs: 
+	mkdir -p bin lib run dist
+	mkdir -p $(SRC_DIRS:%=build/deps/%)
+	mkdir -p $(SRC_DIRS:%=build/obj/%)
+
+clean:
+	rm -f build/obj/*/*.o build/deps/*/*.d
+	rm -f bin/* lib/*.so run/*
+	rm -rf dist/*
+
+# .h dependencies
+depend: $(SRC_NAMES:%.c=build/deps/%.d)
+
+build/deps/%.d : src/%.c
+	@set -e; rm -f $@; \
+	 $(CC) -MM -MT __ $(CPPFLAGS) $< > $@.$$$$; \
+	 sed 's,__[ :]*,build/obj/$*.o $@ : ,g' < $@.$$$$ > $@; \
+	 rm -f $@.$$$$
+
+include $(wildcard build/deps/*/*.d)
+
+# build (potential) library targets with specific cflags
+# XXX: just build everything with -fPIC?
+build/obj/shared/%.o : src/shared/%.c
+	$(CC) -c -fPIC $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+build/obj/lib/%.o : src/lib/%.c
+	$(CC) -c -fPIC $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+# general binary objects
+build/obj/%.o : src/%.c
+	$(CC) -c $(CPPFLAGS) $(CFLAGS) $< -o $@
+
+# output binaries
+bin/% : build/obj/%/main.o
+	$(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@
+
+# output libraries
+lib/lib%.so :
+	$(CC) -shared $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@
+
+dist:
+	mkdir -p dist/$(DIST_NAME)
+	cp -rv Makefile $(DIST_RESOURCES) src/ dist/$(DIST_NAME)/
+	rm dist/$(DIST_NAME)/src/*/.*.sw[op]
+	make -C dist/$(DIST_NAME) dirs
+	tar -C dist -czvf dist/$(DIST_NAME).tar.gz $(DIST_NAME)
+	@echo "*** Output at dist/$(DIST_NAME).tar.gz"
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/cache.c	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,55 @@
+#include "cache.h"
+
+static int pt_cache_new (struct pt_cache **cache_ptr)
+{
+    struct pt_cache *cache;
+
+    if ((cache = calloc(1, sizeof(*cache))) == NULL)
+        return -1;
+
+    // ok
+    *cache_ptr = cache;
+
+    return 0;
+}
+
+int pt_cache_open (struct pt_cache **cache_ptr, struct pt_image *img, int mode)
+{
+    struct pt_cache *cache;
+    
+    // alloc
+    if (pt_cache_new(&cache))
+        return -1;
+
+    
+}
+
+bool pt_cache_fresh (struct pt_cache *cache)
+{
+    // TODO: stat + mtime
+    return false;
+}
+
+/**
+ * Create a new cache file, open it, and write out the header.
+ */
+static int pt_cache_create (struct pt_cache *cache, struct pt_cache_header *header)
+{
+    
+}
+
+int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
+{
+    struct pt_cache_header header;
+
+    memset(&header, 0, sizeof(header));
+
+    // fill in basic info
+    header->width = png_get_image_width(png, info);
+    header->height = png_get_image_height(png, info);
+    header->bit_depth = png_get_bit_depth(png, info);
+    header->color_type = png_get_color_type(png, info);
+
+    // fill in other info
+    header->row_bytes = png_get_rowbytes(png, info);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/cache.h	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,47 @@
+#ifndef PNGTILE_CACHE_H
+#define PNGTILE_CACHE_H
+
+/**
+ * @file
+ *
+ * Internal image cache implementation
+ */
+#include "image.h"
+
+/**
+ * State for cache access
+ */
+struct pt_cache {
+
+};
+
+/**
+ * On-disk header
+ */
+struct pt_cache_header {
+    /** Pixel dimensions of image */
+    uint32_t width, height;
+    
+    /** Pixel format */
+    uint8_t bit_depth, color_type;
+
+    /** Convenience field for number of bytes per row */
+    uint32_t row_bytes;
+};
+
+/**
+ * Construct the image cache info object associated with the given image.
+ */
+int pt_cache_open (struct pt_cache **cache_ptr, struct pt_image *img, int mode);
+
+/**
+ * Verify if the cached data is still fresh compared to the original.
+ */
+bool pt_cache_fresh (struct pt_cache *cache);
+
+/**
+ * Update the cache data from the given PNG image.
+ */
+int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/image.c	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,152 @@
+#include "image.h"
+
+static int pt_image_new (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *png_path)
+{
+    struct pt_image *img;
+
+    // alloc
+    if ((img = calloc(1, sizeof(*img))) == NULL)
+        return -1;
+
+    if ((img->png_path = strdup(png_path)) == NULL)
+        goto error;
+
+    // init
+    img->ctx = ctx;
+    
+    // ok
+    *img_ptr = img;
+
+    return 0;
+
+error:
+    pt_image_destroy(img);
+
+    return -1;
+}
+
+/**
+ * Open the image's FILE
+ */
+static int pt_image_open_file (struct pt_image *img, FILE **file_ptr)
+{
+    FILE *fp;
+    
+    // open
+    if (fopen(img->png_path, "rb") < 0)
+        return -1;
+
+    // ok
+    *file_ptr = fp;
+
+    return 0;
+}
+
+/**
+ * Open the PNG image, setting up the I/O and returning the png_structp and png_infop
+ */
+static int pt_image_open_png (struct pt_image *img, png_structp *png_ptr, png_infop *info_ptr)
+{
+    FILE *fp = NULL;
+    png_structp png = NULL;
+    png_infop info = NULL;
+    
+    // open I/O
+    if (pt_image_open_file(img, &fp))
+        goto error;
+
+    // create the struct
+    if ((png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
+        goto error;
+
+    // create the info
+    if ((info = png_create_info_struct(png)) == NULL)
+        goto error;
+
+    // setup error trap for the I/O
+    if (setjmp(png_jmpbuf(png)))
+        goto error;
+    
+    // setup I/O to FILE
+    png_init_io(png, fp);
+
+    // ok
+    *png_ptr = png;
+    *info_ptr = info;
+
+    return 0;
+
+error:
+    // cleanup file
+    if (fp) fclose(fp);
+
+    // cleanup PNG state
+    png_destroy_read_struct(&png, &info, NULL);
+
+    return -1;
+}
+
+/**
+ * Open the PNG image, and write out to the cache
+ */
+static int pt_image_update_cache (struct pt_image *img)
+{
+    png_structp png;
+    png_infop info;
+
+    // open .png
+    if (pt_image_open_png(img, &png, &info))
+        return -1;
+    
+    // setup error trap
+    if (setjmp(png_jmpbuf(png)))
+        goto error;
+
+    // read meta-info
+    png_read_info(png, info);
+
+    // pass to cache object
+    if (pt_cache_update_png(img->cache, png, info))
+        goto error;
+
+    // finish off, ignore trailing data
+    png_read_end(png, NULL);
+
+    // clean up
+    png_destroy_read_struct(&png, &info);
+
+    return 0;
+
+error:
+    // clean up
+    png_destroy_read_struct(&png, &info);
+
+    return -1;
+}
+
+int pt_image_open (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *png_path, int cache_mode)
+{
+    struct pt_image *img;
+
+    // XXX: verify that the png_path exists and looks like a PNG file
+
+    // alloc
+    if (pt_image_new(&img, ctx, png_path))
+        return -1;
+
+    // open the cache object for this image
+    if (pt_cache_open(&img->cache, img, mode))
+        goto error;
+    
+    // update if not fresh
+    if (!pt_cache_fresh(img->cache))
+        pt_image_update_cache(img);
+    
+    // ok, ready for access
+    *img_ptr = img;
+
+    return 0;
+
+error:
+    pt_image_destroy(img);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/image.h	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,24 @@
+#ifndef PNGTILE_IMAGE_H
+#define PNGTILE_IMAGE_H
+
+/**
+ * @file
+ *
+ * Internal pt_image state
+ */
+#include "pngtile.h"
+
+struct pt_image {
+    /** Associated global state */
+    struct pt_ctx *ctx;
+
+    /** Path to .png */
+    char *png_path;
+    
+    /** Cache object */
+    struct pt_cache *cache;
+};
+
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/pngtile.h	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,40 @@
+#ifndef PNGTILE_H
+#define PNGTILE_H
+
+/**
+ * @file
+ *
+ * Tile-based access to large PNG images.
+ */
+
+/**
+ * "Global" context shared between images
+ */
+struct pt_ctx;
+
+/**
+ * Per-image state
+ */
+struct pt_image;
+
+enum pt_image_mode {
+    PT_IMG_READ     = 0x01,
+    PT_IMG_WRITE    = 0x02,
+};
+
+
+int pt_ctx_new (struct pt_ctx **ctx_ptr);
+
+/**
+ * Open a new pt_image for use.
+ *
+ * @param img_ptr returned pt_image handle
+ * @param ctx global state to use
+ * @param path filesystem path to .png file
+ * @param mode combination of PT_IMG_* flags
+ */
+int pt_image_open (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *png_path, int cache_mode);
+
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/shared/log.c	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,144 @@
+#include "log.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/**
+ * The global log level
+ */
+static enum log_level _log_level = LOG_LEVEL_DEFAULT;
+
+/**
+ * List of log level names
+ */
+const char *log_level_names[] = {
+    "DEBUG",
+    "INFO",
+    "WARN",
+    "ERROR",
+    "FATAL",
+    NULL
+};
+
+#define _LOG_LEVEL_NAME(ll) case LOG_ ## ll: return #ll;
+const char *log_level_name (enum log_level level)
+{
+    switch (level) {
+        _LOG_LEVEL_NAME(DEBUG)
+        _LOG_LEVEL_NAME(INFO)
+        _LOG_LEVEL_NAME(WARN)
+        _LOG_LEVEL_NAME(ERROR)
+        _LOG_LEVEL_NAME(FATAL)
+        default: return "???";
+    }
+}
+#undef _LOG_LEVEL_NAME
+
+void set_log_level (enum log_level level)
+{
+    // meep meep
+    _log_level = level;
+}
+
+size_t str_append_fmt_va (char *buf_ptr, size_t *buf_size, const char *fmt, va_list args)
+{
+    int ret;
+
+    if (*buf_size && (ret = vsnprintf(buf_ptr, *buf_size, fmt, args)) < 0)
+        return 0;
+
+    if (ret > *buf_size)
+        *buf_size = 0;
+
+    else
+        *buf_size -= ret;
+
+    return ret;
+}
+
+size_t str_append_fmt (char *buf_ptr, size_t *buf_size, const char *fmt, ...)
+{
+    va_list vargs;
+    size_t ret;
+    
+    va_start(vargs, fmt);
+    ret = str_append_fmt_va(buf_ptr, buf_size, fmt, vargs);
+    va_end(vargs);
+
+    return ret;
+}
+
+void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs)
+{
+    char buf[LOG_MSG_MAX], *buf_ptr = buf;
+    size_t buf_size = sizeof(buf);
+
+    // filter out?
+    if (level < _log_level)
+        return;
+
+    buf_ptr += str_append_fmt(buf_ptr, &buf_size, "[%5s] %20s : ", tag, func);
+
+    // output the user data
+    if (user_fmt)
+        buf_ptr += str_append_fmt_va(buf_ptr, &buf_size, user_fmt, user_fmtargs);
+    
+    // output the suffix
+    if (log_fmt)
+        buf_ptr += str_append_fmt_va(buf_ptr, &buf_size, log_fmt, log_fmtargs);
+
+    // display
+    // XXX: handle SIGINTR?
+    fprintf(stderr, "%s\n", buf);
+}
+
+void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
+{
+    va_list vargs;
+
+    va_start(vargs, log_fmt);
+    log_output_tag_va(level, tag, func, user_fmt, user_fmtargs, log_fmt, vargs);
+    va_end(vargs);
+}
+
+void _log_msg (enum log_level level, const char *func, const char *format, ...)
+{
+    va_list vargs;
+    
+    // formatted output: no suffix
+    va_start(vargs, format);
+    log_output_tag(level, log_level_name(level), func, format, vargs, NULL);
+    va_end(vargs);
+}
+
+void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2)
+{
+    log_output_tag_va(level, log_level_name(level), func, fmt1, fmtargs1, fmt2, fmtargs2);
+}
+
+void _log_errno (enum log_level level, const char *func, const char *format, ...)
+{
+    va_list vargs;
+
+    // formatted output: suffix strerror()
+    va_start(vargs, format);
+    log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", strerror(errno));
+    va_end(vargs);
+}
+
+void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
+{
+    va_list vargs;
+
+    // formatted output without any suffix
+    va_start(vargs, format);
+    log_output_tag(level, "EXIT", func, format, vargs, NULL);
+    va_end(vargs);
+
+    // exit
+    exit(exit_code);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/shared/log.h	Sun Dec 27 22:01:17 2009 +0200
@@ -0,0 +1,112 @@
+#ifndef SHARED_LOG_H
+#define SHARED_LOG_H
+
+#include <stdarg.h>
+
+/**
+ * Log level definitions
+ *
+ * XXX: these names conflict with <syslog.h>
+ */
+enum log_level {
+    LOG_DEBUG,
+    LOG_INFO,
+    LOG_WARN,
+    LOG_ERROR,
+    LOG_FATAL,
+};
+
+/**
+ * List of log_level values as strings, NULL-terminated
+ */
+extern const char *log_level_names[];
+
+/**
+ * The default log level
+ */
+#define LOG_LEVEL_DEFAULT LOG_INFO
+
+/**
+ * Maximum length of a log message, including terminating NUL
+ */
+#define LOG_MSG_MAX 1024
+
+/**
+ * Set the current log level to filter out messages below the given level
+ */
+void set_log_level (enum log_level level);
+/**
+ * Internal logging func, meant for using custom level tags. This performs the filtering of log levels.
+ *
+ * Format the full output line, and pass it to the log_output_func. The line will be of the form:
+ *  "[<tag>] <func> : <user_fmt> [ <log_fmt> ]"
+ */
+void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
+    __attribute__ ((format (printf, 6, 7)));
+
+/**
+ * va_list version of log_output_tag
+ */
+void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs);
+
+/**
+ * Log a message with the given level
+ */
+#define log_msg(level, ...) _log_msg(level, __func__, __VA_ARGS__)
+void _log_msg (enum log_level level, const char *func, const char *format, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+/**
+ * Log a message with the given level with the given format and varargs
+ */
+#define log_msg_va2(level, fmt1, vargs1, fmt2, vargs2) _log_msg_va(level, __func__, fmt1, vargs1, fmt2, vargs2)
+void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2);
+
+
+/**
+ * Shorthand for log_msg
+ */
+#define log_debug(...) log_msg(LOG_DEBUG, __VA_ARGS__)
+#define log_info(...)  log_msg(LOG_INFO,  __VA_ARGS__)
+#define log_warn(...)  log_msg(LOG_WARN,  __VA_ARGS__)
+#define log_error(...) log_msg(LOG_ERROR, __VA_ARGS__)
+#define log_fatal(...) log_msg(LOG_FATAL, __VA_ARGS__)
+
+/**
+ * Log using errno.
+ */
+#define log_errno(...) _log_errno(LOG_ERROR, __func__, __VA_ARGS__)
+#define log_warn_errno(...) _log_errno(LOG_WARN, __func__, __VA_ARGS__)
+void _log_errno (enum log_level level, const char *func, const char *format, ...)
+    __attribute__ ((format (printf, 3, 4)));
+
+/**
+ * Log with an [EXIT] tag at given level, and then exit with given exit code
+ */
+#define log_exit(level, exit_code, ...) _log_exit(level, exit_code, __func__, __VA_ARGS__)
+void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
+    __attribute__ ((format (printf, 4, 5)))
+    __attribute__ ((noreturn));
+
+// for abort()
+#include <stdlib.h>
+
+/**
+ * log_fatal + exit failure
+ */
+#define FATAL(...) do { log_fatal(__VA_ARGS__); abort(); } while (0)
+
+/**
+ * log_perr + exit failure
+ */
+#define FATAL_ERRNO(...) do { _log_errno(LOG_FATAL, __func__, __VA_ARGS__); abort(); } while (0)
+
+/**
+ * Exit with given code, also logging a message at LOG_... with anĀ [EXIT] tag
+ */
+#define EXIT_INFO(exit_code, ...)  log_exit(LOG_INFO,  exit_code, __VA_ARGS__)
+#define EXIT_WARN(exit_code, ...)  log_exit(LOG_WARN,  exit_code, __VA_ARGS__)
+#define EXIT_ERROR(exit_code, ...) log_exit(LOG_ERROR, exit_code, __VA_ARGS__)
+#define EXIT_FATAL(exit_code, ...) log_exit(LOG_FATAL, exit_code, __VA_ARGS__)
+
+#endif /* LOG_H */