| /* |
| * 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 |
| } |
| } |