/* * Copyright 2001-2004 Brandon Long * All Rights Reserved. * * ClearSilver Templating System * * This code is made available under the terms of the ClearSilver License. * http://www.clearsilver.net/license.hdf * */ #include "cs_config.h" #if HAVE_FEATURES_H #include <features.h> #endif #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "util/neo_misc.h" #include "util/neo_err.h" #include "cgi/cgiwrap.h" typedef struct _cgiwrapper { int argc; char **argv; char **envp; int env_count; READ_FUNC read_cb; WRITEF_FUNC writef_cb; WRITE_FUNC write_cb; GETENV_FUNC getenv_cb; PUTENV_FUNC putenv_cb; ITERENV_FUNC iterenv_cb; void *data; int emu_init; } CGIWRAPPER; static CGIWRAPPER GlobalWrapper = {0, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0}; void cgiwrap_init_std (int argc, char **argv, char **envp) { /* Allow setting of these even after cgiwrap_init_emu is called */ GlobalWrapper.argc = argc; GlobalWrapper.argv = argv; GlobalWrapper.envp = envp; GlobalWrapper.env_count = 0; while (envp[GlobalWrapper.env_count] != NULL) GlobalWrapper.env_count++; /* so you can compile the same code for embedded without mods. * Note that this setting is global for the lifetime of the program, so * you can never reset these values after calling cgiwrap_init_emu by * calling cgiwrap_init_std, you'll have to call cgiwrap_init_emu with NULL * values to reset */ if (GlobalWrapper.emu_init) return; GlobalWrapper.read_cb = NULL; GlobalWrapper.writef_cb = NULL; GlobalWrapper.write_cb = NULL; GlobalWrapper.getenv_cb = NULL; GlobalWrapper.putenv_cb = NULL; GlobalWrapper.iterenv_cb = NULL; GlobalWrapper.data = NULL; } void cgiwrap_init_emu (void *data, READ_FUNC read_cb, WRITEF_FUNC writef_cb, WRITE_FUNC write_cb, GETENV_FUNC getenv_cb, PUTENV_FUNC putenv_cb, ITERENV_FUNC iterenv_cb) { /* leave argc, argv, envp, env_count alone since we either don't use them, or * they are used by the default versions if any of these are passed as NULL. * Note that means that if you pass NULL for anything here, you'd better * have called cgiwrap_init_std first! */ GlobalWrapper.data = data; GlobalWrapper.read_cb = read_cb; GlobalWrapper.writef_cb = writef_cb; GlobalWrapper.write_cb = write_cb; GlobalWrapper.getenv_cb = getenv_cb; GlobalWrapper.putenv_cb = putenv_cb; GlobalWrapper.iterenv_cb = iterenv_cb; GlobalWrapper.emu_init = 1; } NEOERR *cgiwrap_getenv (const char *k, char **v) { if (GlobalWrapper.getenv_cb != NULL) { *v = GlobalWrapper.getenv_cb (GlobalWrapper.data, k); } else { char *s = getenv(k); if (s != NULL) { *v = strdup(s); if (*v == NULL) { return nerr_raise (NERR_NOMEM, "Unable to duplicate env var %s=%s", k, s); } } else { *v = NULL; } } return STATUS_OK; } NEOERR *cgiwrap_putenv (const char *k, const char *v) { if (GlobalWrapper.putenv_cb != NULL) { if (GlobalWrapper.putenv_cb(GlobalWrapper.data, k, v)) return nerr_raise(NERR_NOMEM, "putenv_cb says nomem when %s=%s", k, v); } else { char *buf; int l; l = strlen(k) + strlen(v) + 2; buf = (char *) malloc(sizeof(char) * l); if (buf == NULL) return nerr_raise(NERR_NOMEM, "Unable to allocate memory for putenv %s=%s", k, v); snprintf (buf, l, "%s=%s", k, v); if (putenv (buf)) return nerr_raise(NERR_NOMEM, "putenv says nomem when %s", buf); } return STATUS_OK; } NEOERR *cgiwrap_iterenv (int num, char **k, char **v) { *k = NULL; *v = NULL; if (GlobalWrapper.iterenv_cb != NULL) { int r; r = GlobalWrapper.iterenv_cb(GlobalWrapper.data, num, k, v); if (r) return nerr_raise(NERR_SYSTEM, "iterenv_cb returned %d", r); } else if (GlobalWrapper.envp != NULL && num < GlobalWrapper.env_count) { char *c, *s = GlobalWrapper.envp[num]; c = strchr (s, '='); if (c == NULL) return STATUS_OK; *c = '\0'; *k = strdup(s); *c = '='; if (*k == NULL) return nerr_raise(NERR_NOMEM, "iterenv says nomem for %s", s); *v = strdup(c+1); if (*v == NULL) { free(*k); *k = NULL; return nerr_raise(NERR_NOMEM, "iterenv says nomem for %s", s); } } return STATUS_OK; } NEOERR *cgiwrap_writef (const char *fmt, ...) { va_list ap; va_start (ap, fmt); cgiwrap_writevf (fmt, ap); va_end (ap); return STATUS_OK; } NEOERR *cgiwrap_writevf (const char *fmt, va_list ap) { int r; if (GlobalWrapper.writef_cb != NULL) { r = GlobalWrapper.writef_cb (GlobalWrapper.data, fmt, ap); if (r) return nerr_raise_errno (NERR_IO, "writef_cb returned %d", r); } else { vprintf (fmt, ap); /* vfprintf(stderr, fmt, ap); */ } return STATUS_OK; } NEOERR *cgiwrap_write (const char *buf, int buf_len) { int r; if (GlobalWrapper.write_cb != NULL) { r = GlobalWrapper.write_cb (GlobalWrapper.data, buf, buf_len); if (r != buf_len) return nerr_raise_errno (NERR_IO, "write_cb returned %d<%d", r, buf_len); } else { /* r = fwrite(buf, sizeof(char), buf_len, stderr); */ r = fwrite(buf, sizeof(char), buf_len, stdout); if (r != buf_len) return nerr_raise_errno (NERR_IO, "fwrite returned %d<%d", r, buf_len); } return STATUS_OK; } void cgiwrap_read (char *buf, int buf_len, int *read_len) { if (GlobalWrapper.read_cb != NULL) { *read_len = GlobalWrapper.read_cb (GlobalWrapper.data, buf, buf_len); } else { #ifdef __UCLIBC__ /* According to * http://cvs.uclinux.org/cgi-bin/cvsweb.cgi/uClibc/libc/stdio/stdio.c#rev1.28 * Note: there is a difference in behavior between glibc and uClibc here * regarding fread() on a tty stream. glibc's fread() seems to return * after reading all _available_ data even if not at end-of-file, while * uClibc's fread() continues reading until all requested or eof or error. * The latter behavior seems correct w.r.t. the standards. * * So, we use read on uClibc. This may be required on other platforms as * well. Using raw and buffered i/o interchangeably can be problematic, * but everyone should be going through the cgiwrap interfaces which only * provide this one read function. */ *read_len = read (fileno(stdin), buf, buf_len); #else *read_len = fread (buf, sizeof(char), buf_len, stdin); #endif } }