add library error codes, and fix image fopen error handling
authorTero Marttila <terom@fixme.fi>
Tue, 29 Dec 2009 16:44:48 +0200
changeset 17 baf3fe7c6354
parent 16 6e781cf3d459
child 18 f92a24ab046e
add library error codes, and fix image fopen error handling
Makefile
include/pngtile.h
python/pngtile.pyx
src/lib/cache.c
src/lib/error.c
src/lib/error.h
src/lib/image.c
src/shared/util.c
src/util/main.c
--- a/Makefile	Tue Dec 29 15:38:31 2009 +0200
+++ b/Makefile	Tue Dec 29 16:44:48 2009 +0200
@@ -16,7 +16,7 @@
 all: depend lib/libpngtile.so bin/util
 
 lib/libpngtile.so : \
-	build/obj/lib/image.o build/obj/lib/cache.o \
+	build/obj/lib/image.o build/obj/lib/cache.o build/obj/lib/error.o \
 	build/obj/shared/util.o build/obj/shared/log.o
 
 lib/pypngtile.so : \
--- a/include/pngtile.h	Tue Dec 29 15:38:31 2009 +0200
+++ b/include/pngtile.h	Tue Dec 29 16:44:48 2009 +0200
@@ -111,4 +111,40 @@
  */
 void pt_image_destroy (struct pt_image *image);
 
+/**
+ * Error codes returned
+ */
+enum pt_error {
+    PT_SUCCESS = 0,
+    PT_ERR_MEM,
+
+    PT_ERR_PATH,
+    PT_ERR_OPEN_MODE,
+    
+    PT_ERR_IMG_STAT,
+    PT_ERR_IMG_FOPEN,
+    
+    PT_ERR_PNG_CREATE,
+    PT_ERR_PNG,
+   
+    PT_ERR_CACHE_STAT,
+    PT_ERR_CACHE_OPEN_READ,
+    PT_ERR_CACHE_OPEN_TMP,
+    PT_ERR_CACHE_SEEK,
+    PT_ERR_CACHE_READ,
+    PT_ERR_CACHE_WRITE,
+    PT_ERR_CACHE_TRUNC,
+    PT_ERR_CACHE_MMAP,
+    PT_ERR_CACHE_RENAME_TMP,
+
+    PT_ERR_TILE_CLIP,
+
+    PT_ERR_MAX,
+};
+
+/**
+ * Translate error code to short description
+ */
+const char *pt_strerror (int err);
+
 #endif
--- a/python/pngtile.pyx	Tue Dec 29 15:38:31 2009 +0200
+++ b/python/pngtile.pyx	Tue Dec 29 16:44:48 2009 +0200
@@ -2,6 +2,12 @@
     struct FILE :
         pass
 
+cdef extern from "errno.h" :
+    extern int errno
+
+cdef extern from "string.h" :
+    char* strerror (int err)
+
 cdef extern from "Python.h" :
     int PyFile_Check (object p)
     FILE* PyFile_AsFile (object p)
@@ -34,9 +40,12 @@
     int pt_image_open (pt_image **image_ptr, pt_ctx *ctx, char *png_path, int cache_mode)
     int pt_image_info_func "pt_image_info" (pt_image *image, pt_image_info **info_ptr)
     int pt_image_status (pt_image *image)
+    int pt_image_update (pt_image *image)
     int pt_image_tile (pt_image *image, pt_tile_info *info, FILE *out)
     void pt_image_destroy (pt_image *image)
 
+    char* pt_strerror (int err)
+
 OPEN_UPDATE = PT_OPEN_UPDATE
 CACHE_ERROR = PT_CACHE_ERROR
 CACHE_FRESH = PT_CACHE_FRESH
@@ -46,29 +55,39 @@
 class Error (BaseException) :
     pass
 
+cdef int trap_err (char *op, int ret) except -1 :
+    if ret < 0 :
+        raise Error("%s: %s: %s" % (op, pt_strerror(ret), strerror(errno)))
+
+    else :
+        return ret
+
 cdef class Image :
     cdef pt_image *image
 
     def __cinit__ (self, char *png_path, int cache_mode = 0) :
-        if pt_image_open(&self.image, NULL, png_path, cache_mode) < 0 :
-            raise Error("pt_image_open: " + png_path)
+        trap_err("pt_image_open", 
+            pt_image_open(&self.image, NULL, png_path, cache_mode)
+        )
     
     def info (self) :
         cdef pt_image_info *image_info
-
-        if pt_image_info_func(self.image, &image_info) < 0 :
-            raise Error("pt_image_info")
+        
+        trap_err("pt_image_info",
+            pt_image_info_func(self.image, &image_info)
+        )
 
         return (image_info.width, image_info.height)
     
     def status (self) :
-        cdef int status = pt_image_status(self.image)
-        
-        if status < 0 :
-            raise Error("pt_image_status")
-
-        else :
-            return status
+        return trap_err("pt_image_status", 
+            pt_image_status(self.image)
+        )
+    
+    def update (self) :
+        trap_err("pt_image_update", 
+            pt_image_update(self.image)
+        )
 
     def tile (self, size_t width, size_t height, size_t x, size_t y, object out) :
         cdef FILE *outf
@@ -87,8 +106,9 @@
         ti.x = x
         ti.y = y
         
-        if pt_image_tile(self.image, &ti, outf) < 0 :
-            raise Error("pt_image_tile")
+        trap_err("pt_image_tile", 
+            pt_image_tile(self.image, &ti, outf)
+        )
 
     def __dealloc__ (self) :
         if self.image :
--- a/src/lib/cache.c	Tue Dec 29 15:38:31 2009 +0200
+++ b/src/lib/cache.c	Tue Dec 29 16:44:48 2009 +0200
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "shared/util.h"
 #include "shared/log.h" // only LOG_DEBUG
+#include "error.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -15,13 +16,14 @@
 int pt_cache_new (struct pt_cache **cache_ptr, const char *path, int mode)
 {
     struct pt_cache *cache;
+    int err;
 
     // alloc
     if ((cache = calloc(1, sizeof(*cache))) == NULL)
-        return -1;
+        RETURN_ERROR(PT_ERR_MEM);
 
     if ((cache->path = strdup(path)) == NULL)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_MEM);
 
     // init
     cache->fd = -1;
@@ -37,7 +39,7 @@
     if (cache)
         pt_cache_destroy(cache);
 
-    return -1;
+    return err;
 }
 
 int pt_cache_status (struct pt_cache *cache, const char *img_path)
@@ -46,7 +48,7 @@
     
     // test original file
     if (stat(img_path, &st_img) < 0)
-        return -1;
+        RETURN_ERROR(PT_ERR_IMG_STAT);
     
     // test cache file
     if (stat(cache->path, &st_cache) < 0) {
@@ -54,7 +56,7 @@
         if (errno == ENOENT)
             return PT_CACHE_NONE;
         else
-            return -1;
+            RETURN_ERROR(PT_ERR_CACHE_STAT);
     }
 
     // compare mtime
@@ -67,9 +69,11 @@
 
 int pt_cache_info (struct pt_cache *cache, struct pt_image_info *info)
 {
+    int err;
+
     // ensure open
-    if (pt_cache_open(cache))
-        return -1;
+    if ((err = pt_cache_open(cache)))
+        return err;
 
     info->width = cache->header->width;
     info->height = cache->header->height;
@@ -107,7 +111,7 @@
     
     // actual open()
     if ((fd = open(cache->path, O_RDONLY)) < 0)
-        return -1;
+        RETURN_ERROR_ERRNO(PT_ERR_OPEN_MODE, EACCES);
 
     // ok
     *fd_ptr = fd;
@@ -125,12 +129,12 @@
     
     // get .tmp path
     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
-        return -1;
+        RETURN_ERROR(PT_ERR_PATH);
 
     // open for write, create
     // XXX: locking?
     if ((fd = open(tmp_path, O_RDWR | O_CREAT, 0644)) < 0)
-        return -1;
+        RETURN_ERROR(PT_ERR_CACHE_OPEN_TMP);
 
     // ok
     *fd_ptr = fd;
@@ -158,7 +162,7 @@
 
     // perform mmap() from second page on
     if ((addr = mmap(NULL, PT_CACHE_HEADER_SIZE + cache->size, prot, MAP_SHARED, cache->fd, 0)) == MAP_FAILED)
-        return -1;
+        RETURN_ERROR(PT_ERR_CACHE_MMAP);
 
     // ok
     *addr_ptr = addr;
@@ -176,15 +180,15 @@
 
     // seek to start
     if (lseek(cache->fd, 0, SEEK_SET) != 0)
-        return -1;
+        RETURN_ERROR(PT_ERR_CACHE_SEEK);
 
     // write out full header
     while (len) {
         ssize_t ret;
         
         // try and write out the header
-        if ((ret = read(cache->fd, buf, len)) < 0)
-            return -1;
+        if ((ret = read(cache->fd, buf, len)) <= 0)
+            RETURN_ERROR(PT_ERR_CACHE_READ);
 
         // update offset
         buf += ret;
@@ -205,15 +209,15 @@
 
     // seek to start
     if (lseek(cache->fd, 0, SEEK_SET) != 0)
-        return -1;
+        RETURN_ERROR(PT_ERR_CACHE_SEEK);
 
     // write out full header
     while (len) {
         ssize_t ret;
         
         // try and write out the header
-        if ((ret = write(cache->fd, buf, len)) < 0)
-            return -1;
+        if ((ret = write(cache->fd, buf, len)) <= 0)
+            RETURN_ERROR(PT_ERR_CACHE_WRITE);
 
         // update offset
         buf += ret;
@@ -230,35 +234,34 @@
 static int pt_cache_create (struct pt_cache *cache, struct pt_cache_header *header)
 {
     void *base;
+    int err;
 
     // no access
-    if (!(cache->mode & PT_OPEN_UPDATE)) {
-        errno = EPERM;
-        return -1;
-    }
+    if (!(cache->mode & PT_OPEN_UPDATE))
+        RETURN_ERROR(PT_ERR_OPEN_MODE);
 
     // open as .tmp
-    if (pt_cache_open_tmp_fd(cache, &cache->fd))
-        return -1;
+    if ((err = pt_cache_open_tmp_fd(cache, &cache->fd)))
+        return err;
 
     // calculate data size
     cache->size = sizeof(*header) + header->height * header->row_bytes;
 
     // grow file
     if (ftruncate(cache->fd, PT_CACHE_HEADER_SIZE + cache->size) < 0)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_CACHE_TRUNC);
 
     // mmap header and data
-    if (pt_cache_open_mmap(cache, &base, false))
-        goto error;
+    if ((err = pt_cache_open_mmap(cache, &base, false)))
+        JUMP_ERROR(err);
 
     cache->header = base;
     cache->data = base + PT_CACHE_HEADER_SIZE;
 
     // write header
     // XXX: should just mmap...
-    if (pt_cache_write_header(cache, header))
-        goto error;
+    if ((err = pt_cache_write_header(cache, header)))
+        JUMP_ERROR(err);
 
     // done
     return 0;
@@ -267,7 +270,7 @@
     // cleanup
     pt_cache_abort(cache);
 
-    return -1;
+    return err;
 }
 
 /**
@@ -279,11 +282,11 @@
     
     // get .tmp path
     if (path_with_fext(cache->path, tmp_path, sizeof(tmp_path), ".tmp"))
-        return -1;
+        RETURN_ERROR(PT_ERR_PATH);
 
     // rename
     if (rename(tmp_path, cache->path) < 0)
-        return -1;
+        RETURN_ERROR(PT_ERR_CACHE_RENAME_TMP);
 
     // ok
     return 0;
@@ -293,25 +296,26 @@
 {
     struct pt_cache_header header;
     void *base;
+    int err;
 
     // ignore if already open
     if (cache->header && cache->data)
         return 0;
 
     // open the .cache
-    if (pt_cache_open_read_fd(cache, &cache->fd))
-        return -1;
+    if ((err = pt_cache_open_read_fd(cache, &cache->fd)))
+        return err;
 
     // read in header
-    if (pt_cache_read_header(cache, &header))
-        return -1;
+    if ((err = pt_cache_read_header(cache, &header)))
+        JUMP_ERROR(err);
 
     // calculate data size
     cache->size = sizeof(header) + header.height * header.row_bytes;
 
     // mmap header and data
-    if (pt_cache_open_mmap(cache, &base, true))
-        goto error;
+    if ((err = pt_cache_open_mmap(cache, &base, true)))
+        JUMP_ERROR(err);
 
     cache->header = base;
     cache->data = base + PT_CACHE_HEADER_SIZE;
@@ -323,12 +327,13 @@
     // cleanup
     pt_cache_abort(cache);
 
-    return -1;
+    return err;
 }
 
 int pt_cache_update_png (struct pt_cache *cache, png_structp png, png_infop info)
 {
     struct pt_cache_header header;
+    int err;
     
     // XXX: check cache_mode
     // XXX: check image doesn't use any options we don't handle
@@ -366,7 +371,7 @@
 
         if (png_get_PLTE(png, info, &palette, &num_palette) == 0)
             // XXX: PLTE chunk not read?
-            return -1;
+            RETURN_ERROR(PT_ERR_PNG);
         
         // should only be 256 of them at most
         assert(num_palette <= PNG_MAX_PALETTE_LENGTH);
@@ -379,8 +384,8 @@
     }
 
     // create .tmp and write out header
-    if (pt_cache_create(cache, &header))
-        return -1;
+    if ((err = pt_cache_create(cache, &header)))
+        return err;
 
 
     // write out raw image data a row at a time
@@ -391,8 +396,8 @@
 
 
     // move from .tmp to .cache
-    if (pt_cache_create_done(cache))
-        return -1;
+    if ((err = pt_cache_create_done(cache)))
+        return err;
 
     // done!
     return 0;
@@ -456,7 +461,7 @@
 
     // allocate buffer for a single row of image data
     if ((rowbuf = malloc(ti->width * cache->header->col_bytes)) == NULL)
-        return -1;
+        RETURN_ERROR(PT_ERR_MEM);
 
     // how much data we actually have for each row, in px and bytes
     // from [(tile x)---](clip x)
@@ -492,15 +497,13 @@
     int err;
 
     // check within bounds
-    if (ti->x >= cache->header->width || ti->y >= cache->header->height) {
+    if (ti->x >= cache->header->width || ti->y >= cache->header->height)
         // completely outside
-        errno = EINVAL;
-        return -1;
-    }
+        RETURN_ERROR(PT_ERR_TILE_CLIP);
 
     // ensure open
-    if (pt_cache_open(cache))
-        return -1;
+    if ((err = pt_cache_open(cache)))
+        return err;
 
     // set basic info
     png_set_IHDR(png, info, ti->width, ti->height, cache->header->bit_depth, cache->header->color_type,
@@ -527,7 +530,7 @@
         err = write_png_data_clipped(cache, png, info, ti);
 
     if (err)
-        return -1;
+        return err;
     
     // done, flush remaining output
     png_write_flush(png);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/error.c	Tue Dec 29 16:44:48 2009 +0200
@@ -0,0 +1,38 @@
+#include "error.h"
+
+const char *error_names[PT_ERR_MAX] = {
+    [PT_SUCCESS]                = "Success",
+    [PT_ERR_MEM]                = "malloc()",
+
+    [PT_ERR_PATH]               = "path",
+    [PT_ERR_OPEN_MODE]          = "open_mode",
+    
+    [PT_ERR_IMG_STAT]           = "stat(.png)",
+    [PT_ERR_IMG_FOPEN]          = "fopen(.png)",
+    
+    [PT_ERR_PNG_CREATE]         = "png_create()",
+    [PT_ERR_PNG]                = "png_*()",
+   
+    [PT_ERR_CACHE_STAT]         = "stat(.cache)",
+    [PT_ERR_CACHE_OPEN_READ]    = "open(.cache)",
+    [PT_ERR_CACHE_OPEN_TMP]     = "open(.tmp)",
+    [PT_ERR_CACHE_SEEK]         = "seek(.cache)",
+    [PT_ERR_CACHE_READ]         = "read(.cache)",
+    [PT_ERR_CACHE_WRITE]        = "write(.cache)",
+    [PT_ERR_CACHE_TRUNC]        = "truncate(.cache)",
+    [PT_ERR_CACHE_MMAP]         = "mmap(.cache)",
+    [PT_ERR_CACHE_RENAME_TMP]   = "rename(.tmp, .cache)",
+
+    [PT_ERR_TILE_CLIP]          = "Tile outside of image",
+};
+
+const char *pt_strerror (int err)
+{
+    if (err < 0)
+        err = -err;
+
+    if (err < PT_SUCCESS || err >= PT_ERR_MAX)
+        return "Unknown error";
+    else
+        return error_names[err];
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/error.h	Tue Dec 29 16:44:48 2009 +0200
@@ -0,0 +1,13 @@
+#ifndef PNGTILE_ERROR_H
+#define PNGTILE_ERROR_H
+
+#include "pngtile.h"
+#include <errno.h>
+
+#define RETURN_ERROR(code) return -(code)
+#define RETURN_ERROR_ERRNO(code, errn) do { errno = (errn); return -(code); } while (0)
+#define JUMP_ERROR(err) do { goto error; } while (0)
+#define JUMP_SET_ERROR(err, code) do { err = -(code); goto error; } while (0)
+#define JUMP_SET_ERROR_ERRNO(err, code, errn) do { err = -(code); errno = (errn); goto error; } while (0)
+
+#endif
--- a/src/lib/image.c	Tue Dec 29 15:38:31 2009 +0200
+++ b/src/lib/image.c	Tue Dec 29 16:44:48 2009 +0200
@@ -1,5 +1,6 @@
 #include "image.h"
 #include "cache.h"
+#include "error.h"
 #include "shared/util.h"
 
 #include <stdlib.h>
@@ -7,41 +8,42 @@
 
 #include <png.h>
 
-static int pt_image_new (struct pt_image **img_ptr, struct pt_ctx *ctx, const char *path)
+static int pt_image_new (struct pt_image **image_ptr, struct pt_ctx *ctx, const char *path)
 {
-    struct pt_image *img;
+    struct pt_image *image;
+    int err = 0;
 
     // alloc
-    if ((img = calloc(1, sizeof(*img))) == NULL)
-        return -1;
+    if ((image = calloc(1, sizeof(*image))) == NULL)
+        JUMP_SET_ERROR(err, PT_ERR_MEM);
 
-    if ((img->path = strdup(path)) == NULL)
-        goto error;
+    if ((image->path = strdup(path)) == NULL)
+        JUMP_SET_ERROR(err, PT_ERR_MEM);
 
     // init
-    img->ctx = ctx;
+    image->ctx = ctx;
     
     // ok
-    *img_ptr = img;
+    *image_ptr = image;
 
     return 0;
 
 error:
-    pt_image_destroy(img);
-
-    return -1;
+    pt_image_destroy(image);
+    
+    return err;
 }
 
 /**
  * Open the image's FILE
  */
-static int pt_image_open_file (struct pt_image *img, FILE **file_ptr)
+static int pt_image_open_file (struct pt_image *image, FILE **file_ptr)
 {
     FILE *fp;
     
     // open
-    if ((fp = fopen(img->path, "rb")) < 0)
-        return -1;
+    if ((fp = fopen(image->path, "rb")) == NULL)
+        RETURN_ERROR(PT_ERR_IMG_FOPEN);
 
     // ok
     *file_ptr = fp;
@@ -52,33 +54,34 @@
 /**
  * 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)
+static int pt_image_open_png (struct pt_image *image, png_structp *png_ptr, png_infop *info_ptr)
 {
     FILE *fp = NULL;
     png_structp png = NULL;
     png_infop info = NULL;
+    int err;
     
     // open I/O
-    if (pt_image_open_file(img, &fp))
-        goto error;
+    if ((err = pt_image_open_file(image, &fp)))
+        JUMP_ERROR(err);
 
     // create the struct
     if ((png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG_CREATE);
 
     // create the info
     if ((info = png_create_info_struct(png)) == NULL)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG_CREATE);
 
     // setup error trap for the I/O
     if (setjmp(png_jmpbuf(png)))
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG);
     
     // setup I/O to FILE
     png_init_io(png, fp);
 
     // ok
-    // XXX: what to do with fp?
+    // XXX: what to do with fp? Should fclose() when done?
     *png_ptr = png;
     *info_ptr = info;
 
@@ -90,8 +93,8 @@
 
     // cleanup PNG state
     png_destroy_read_struct(&png, &info, NULL);
-
-    return -1;
+    
+    return err;
 }
 
 /**
@@ -115,45 +118,40 @@
 {
     png_structp png;
     png_infop info;
+    int err = 0;
 
     // pre-check enabled
-    if (!(image->cache->mode & PT_OPEN_UPDATE)) {
-        errno = EPERM;
-        return -1;
-    }
+    if (!(image->cache->mode & PT_OPEN_UPDATE))
+        RETURN_ERROR_ERRNO(PT_ERR_OPEN_MODE, EACCES);
 
     // open .png
-    if (pt_image_open_png(image, &png, &info))
-        return -1;
+    if ((err = pt_image_open_png(image, &png, &info)))
+        return err;
     
     // setup error trap
     if (setjmp(png_jmpbuf(png)))
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG);
 
     // read meta-info
     png_read_info(png, info);
 
     // update our meta-info
-    if (pt_image_update_info(image, png, info))
-        goto error;
+    if ((err = pt_image_update_info(image, png, info)))
+        JUMP_ERROR(err);
 
     // pass to cache object
-    if (pt_cache_update_png(image->cache, png, info))
-        goto error;
+    if ((err = pt_cache_update_png(image->cache, png, info)))
+        JUMP_ERROR(err);
 
     // finish off, ignore trailing data
     png_read_end(png, NULL);
 
+error:
     // clean up
+    // XXX: we need to close the fopen'd .png
     png_destroy_read_struct(&png, &info, NULL);
 
-    return 0;
-
-error:
-    // clean up
-    png_destroy_read_struct(&png, &info, NULL);
-
-    return -1;
+    return err;
 }
 
 /**
@@ -162,27 +160,31 @@
  */
 static int pt_image_cache_path (struct pt_image *image, char *buf, size_t len)
 {
-    return path_with_fext(image->path, buf, len, ".cache"); 
+    if (path_with_fext(image->path, buf, len, ".cache"))
+        RETURN_ERROR(PT_ERR_PATH);
+
+    return 0;
 }
 
 int pt_image_open (struct pt_image **image_ptr, struct pt_ctx *ctx, const char *path, int cache_mode)
 {
     struct pt_image *image;
     char cache_path[1024];
+    int err;
 
     // XXX: verify that the path exists and looks like a PNG file
 
     // alloc
-    if (pt_image_new(&image, ctx, path))
-        return -1;
+    if ((err = pt_image_new(&image, ctx, path)))
+        return err;
     
     // compute cache file path
-    if (pt_image_cache_path(image, cache_path, sizeof(cache_path)))
-        goto error;
+    if ((err = pt_image_cache_path(image, cache_path, sizeof(cache_path))))
+        JUMP_ERROR(err);
 
     // create the cache object for this image (doesn't yet open it)
-    if (pt_cache_new(&image->cache, cache_path, cache_mode))
-        goto error;
+    if ((err = pt_cache_new(&image->cache, cache_path, cache_mode)))
+        JUMP_ERROR(err);
     
     // ok, ready for access
     *image_ptr = image;
@@ -192,14 +194,16 @@
 error:
     pt_image_destroy(image);
 
-    return -1;
+    return err;
 }
 
 int pt_image_info (struct pt_image *image, const struct pt_image_info **info_ptr)
 {
+    int err;
+
     // update info
-    if (pt_cache_info(image->cache, &image->info))
-        return -1;
+    if ((err = pt_cache_info(image->cache, &image->info)))
+        return err;
     
     // return pointer
     *info_ptr = &image->info;
@@ -221,38 +225,34 @@
 {
     png_structp png = NULL;
     png_infop info = NULL;
+    int err = 0;
         
     // open PNG writer
     if ((png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) == NULL)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG_CREATE);
     
     if ((info = png_create_info_struct(png)) == NULL)
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG_CREATE);
 
     // libpng error trap
     if (setjmp(png_jmpbuf(png)))
-        goto error;
+        JUMP_SET_ERROR(err, PT_ERR_PNG);
     
     // setup IO
     png_init_io(png, out);
     
     // render tile
-    if (pt_cache_tile_png(image->cache, png, info, tile_info))
-        goto error;
+    if ((err = pt_cache_tile_png(image->cache, png, info, tile_info)))
+        JUMP_ERROR(err);
 
     // done
     png_write_end(png, info);
 
-    // cleanup
-    png_destroy_write_struct(&png, &info);
-
-    return 0;
-
 error:
     // cleanup
     png_destroy_write_struct(&png, &info);
 
-    return -1;
+    return err;
 }
 
 void pt_image_destroy (struct pt_image *image)
--- a/src/shared/util.c	Tue Dec 29 15:38:31 2009 +0200
+++ b/src/shared/util.c	Tue Dec 29 16:44:48 2009 +0200
@@ -1,18 +1,23 @@
 #include "util.h"
 
 #include <string.h>
+#include <errno.h>
 
 int chfext (char *buf, size_t len, const char *newext)
 {
     char *ext;
 
     // find .ext
-    if ((ext = strrchr(buf, '.')) == NULL)
+    if ((ext = strrchr(buf, '.')) == NULL) {
+        errno = EINVAL;
         return -1;
+    }
 
     // check length
-    if (ext + strlen(newext) >= buf + len)
+    if (ext + strlen(newext) >= buf + len) {
+        errno = ENAMETOOLONG;
         return -1;
+    }
 
     // change to .foo
     strcpy(ext, newext);
@@ -24,8 +29,10 @@
 int path_with_fext (const char *path, char *buf, size_t len, const char *newext)
 {
     // verify length
-    if (strlen(path) > len)
+    if (strlen(path) > len) {
+        errno = ENAMETOOLONG;
         return -1;
+    }
 
     // copy filename
     strcpy(buf, path);
--- a/src/util/main.c	Tue Dec 29 15:38:31 2009 +0200
+++ b/src/util/main.c	Tue Dec 29 16:44:48 2009 +0200
@@ -1,6 +1,7 @@
-#include "lib/pngtile.h"
 #include "shared/log.h"
 
+#include "pngtile.h"
+
 #include <getopt.h>
 #include <stdio.h>
 #include <stdbool.h>
@@ -47,6 +48,7 @@
     int opt;
     bool force_update = false;
     struct pt_tile_info ti = {0, 0, 0, 0};
+    int err;
     
     // parse arguments
     while ((opt = getopt_long(argc, argv, "hqvDUW:H:x:y:", options, NULL)) != -1) {
@@ -118,8 +120,8 @@
         log_debug("Loading image from: %s...", img_path);
 
         // open
-        if (pt_image_open(&image, ctx, img_path, PT_OPEN_UPDATE)) {
-            log_errno("pt_image_open: %s", img_path);
+        if ((err = pt_image_open(&image, ctx, img_path, PT_OPEN_UPDATE))) {
+            log_errno("pt_image_open: %s: %s", img_path, pt_strerror(err));
             continue;
         }
 
@@ -127,7 +129,7 @@
         
         // check if stale
         if ((status = pt_image_status(image)) < 0) {
-            log_errno("pt_image_status: %s", img_path);
+            log_errno("pt_image_status: %s: %s", img_path, pt_strerror(status));
             goto error;
         }
         
@@ -144,8 +146,8 @@
 
             log_debug("Updating image cache...");
 
-            if (pt_image_update(image)) {
-                log_warn_errno("pt_image_update: %s", img_path);
+            if ((err = pt_image_update(image))) {
+                log_warn_errno("pt_image_update: %s: %s", img_path, pt_strerror(err));
             }
 
             log_info("Updated image cache");
@@ -154,8 +156,8 @@
         // show info
         const struct pt_image_info *img_info;
         
-        if (pt_image_info(image, &img_info))
-            log_warn_errno("pt_image_info: %s", img_path);
+        if ((err = pt_image_info(image, &img_info)))
+            log_warn_errno("pt_image_info: %s: %s", img_path, pt_strerror(err));
 
         else
             log_info("\tImage dimensions: %zux%zu", img_info->width, img_info->height);
@@ -164,8 +166,8 @@
         if (ti.width && ti.height) {
             log_debug("Render tile %zux%zu@(%zu,%zu) -> stdout", ti.width, ti.height, ti.x, ti.y);
 
-            if (pt_image_tile(image, &ti, stdout))
-                log_errno("pt_image_tile: %s", img_path);
+            if ((err = pt_image_tile(image, &ti, stdout)))
+                log_errno("pt_image_tile: %s: %s", img_path, pt_strerror(err));
         }
 
 error: