--- /dev/null
+/*
+ gw-res.c: Implementation of resource management.
+
+ Europagate, 1994-1995.
+
+ $Log: gw-res.c,v $
+ Revision 1.1 1995/02/09 17:27:11 adam
+ Initial revision
+
+
+ Initial: Dec 8, 94 (Adam Dickmeiss)
+ Last update: Dec 19, 94 (Adam Dickmeiss)
+
+ */
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+#if HAVE_FLOCK
+#include <sys/file.h>
+#else
+#include <sys/types.h>
+#endif
+
+#include <gw-log.h>
+#include "gw-resp.h"
+
+/*
+ symtab_open: Create empty symbol table.
+ symbol table handle is returned.
+ */
+
+static struct res_symtab *symtab_open (void)
+{
+ struct res_symtab *symtab;
+
+ symtab = malloc (sizeof(*symtab));
+ if (!symtab)
+ return NULL;
+ symtab->next = NULL;
+ return symtab;
+}
+
+/*
+ symtab_close: Delete symbol table.
+ */
+static void symtab_close (struct res_symtab *symtab)
+{
+ struct res_sym_entry *entry, *entry1;
+
+ for (entry = symtab->next; entry; entry = entry1)
+ {
+ entry1 = entry->next;
+ free (entry);
+ }
+ free (symtab);
+}
+
+/*
+ symtab_override: Add symbol to table. 'rl' holds symbol
+ table entry info. If the symbol is already present it
+ will override old info.
+ */
+static int symtab_override (struct res_symtab *symtab,
+ struct res_line_info *rl)
+{
+ struct res_sym_entry *entry;
+
+ for (entry = symtab->next; entry; entry=entry->next)
+ if (!strcmp (entry->info->name, rl->name))
+ {
+ entry->info = rl;
+ return 1;
+ }
+ entry = malloc (sizeof(*entry));
+ if (!entry)
+ return -1;
+ entry->next = symtab->next;
+ symtab->next = entry;
+ entry->info = rl;
+ return 0;
+}
+
+/*
+ symtab_lookup: Symbol table lookup. If successful info is returned;
+ otherwise NULL is returned.
+ */
+static struct res_line_info *symtab_lookup (struct res_symtab *symtab,
+ const char *name)
+{
+ struct res_sym_entry *entry;
+
+ for (entry = symtab->next; entry; entry=entry->next)
+ if (!strcmp (entry->info->name, name))
+ return entry->info;
+ return NULL;
+}
+
+/*
+ lock_file: File locking using fcntl.
+ */
+#if !HAVE_FLOCK
+static void lock_file (int fd, int type)
+{
+ struct flock area;
+ area.l_type = type;
+ area.l_whence = SEEK_SET;
+ area.l_start = 0L;
+ area.l_len = 0L;
+ fcntl (fd, F_SETLKW, &area);
+}
+#endif
+
+/*
+ gw_res_init: A resource handle is returned by this function
+ describing empty resources.
+ */
+GwRes gw_res_init (void)
+{
+ GwRes p;
+
+ if (!(p = malloc (sizeof(*p))))
+ return p;
+ p->files = NULL;
+ p->symtab = symtab_open ();
+ if (!p->symtab)
+ {
+ free (p);
+ return NULL;
+ }
+ return p;
+}
+
+/*
+ gw_res_close: The resources described by 'id' are freed.
+ No further references to 'id' are allowed.
+ */
+void gw_res_close (GwRes id)
+{
+ struct res_line_info *rl, *rl1;
+ struct res_file_info *rf, *rf1;
+
+ assert (id);
+
+ symtab_close (id->symtab);
+ for (rf = id->files; rf; rf = rf1)
+ {
+ for (rl = rf->lines; rl; rl = rl1)
+ {
+ free (rl->name);
+ free (rl->value);
+ rl1 = rl->next;
+ free (rl);
+ }
+ free (rf->fname);
+ rf1 = rf->next;
+ free (rf);
+ }
+ free (id);
+}
+
+/*
+ add_name: add a node with line information.
+ */
+static struct res_line_info *add_name (enum res_kind kind,
+ const char *name, const char *value)
+{
+ struct res_line_info *rl;
+
+ if (!(rl = malloc (sizeof(*rl))))
+ return NULL;
+ rl->next = NULL;
+ rl->kind = kind;
+ if (name)
+ {
+ rl->name = gw_strdup (name);
+ if (!rl->name)
+ {
+ free (rl);
+ return NULL;
+ }
+ }
+ else
+ rl->name = NULL;
+ if (value)
+ {
+ rl->value = gw_strdup (value);
+ if (!rl->value)
+ {
+ free (rl->name);
+ free (rl);
+ return NULL;
+ }
+ }
+ else
+ rl->value = NULL;
+ return rl;
+}
+
+/*
+ gw_res_merge: The resources described by 'id' are merged by the contents
+ of 'filename'. If a resource is duplicated (in both resources 'id'
+ and the file) the resource is set to the value specified in 'filename'.
+ This function returns 0 on success; -1 on failure ('filename'
+ could not be read)
+ */
+int gw_res_merge (GwRes id, const char *filename)
+{
+ FILE *inf;
+ char buffer[1024];
+ char value[2048];
+ struct res_file_info *ri;
+ struct res_line_info **rlp, *rl;
+ struct res_line_info *rl_last = NULL;
+ int err = 0;
+
+ assert (id);
+
+ gw_log (GW_LOG_DEBUG, "res", "gw_res_merge");
+ gw_log (GW_LOG_DEBUG, "res", "checking %s", filename);
+ if (!(inf = fopen (filename, "r")))
+ return -1;
+#if HAVE_FLOCK
+ flock (fileno(inf), LOCK_SH);
+#else
+ lock_file (fileno (inf), F_RDLCK);
+#endif
+ if (!(ri = malloc (sizeof (*ri))))
+ {
+ fclose (inf);
+ return -1;
+ }
+ if (!(ri->fname = gw_strdup (filename)))
+ {
+ free (ri);
+ fclose (inf);
+ return -2;
+ }
+ gw_log (GW_LOG_DEBUG, "res", "reading %s", filename);
+ ri->next = id->files;
+ id->files = ri;
+ rlp = &ri->lines;
+ ri->lines = NULL;
+ while (fgets (buffer, sizeof(buffer)-1, inf))
+ {
+ char *cp;
+ while ((cp = strchr (buffer, '\n')))
+ *cp = '\0';
+ if (*buffer == '#')
+ { /* comment line */
+ rl = add_name (comment, NULL, buffer);
+ if (!rl)
+ {
+ err = -2;
+ break;
+ }
+ *rlp = rl;
+ rlp = &rl->next;
+ }
+ else if (*buffer == '\0' || *buffer == ' ' || *buffer == '\t')
+ {
+ int i = 0;
+ while (buffer[i] == ' ' || buffer[i] == '\t')
+ i++;
+ if (buffer[i] == '\0')
+ { /* empty line */
+ rl = add_name (blank, NULL, NULL);
+ if (!rl)
+ {
+ err = -2;
+ break;
+ }
+ *rlp = rl;
+ rlp = &rl->next;
+ }
+ else
+ { /* continuation line */
+ int j = strlen (buffer)-1;
+ /* strip trailing blanks */
+ while (buffer[j] == '\t' || buffer[j] == ' ')
+ --j;
+ buffer[j+1] = '\0';
+ if (rl_last)
+ {
+ if (strlen(value)+strlen(buffer+i) >= sizeof(value)-2)
+ {
+ gw_log (GW_LOG_WARN, "res", "Resource `%s' is "
+ " truncated", rl_last->name);
+ }
+ else
+ {
+ /* effectively add one blank, then buffer */
+ strcat (value, " ");
+ strcat (value, buffer+i);
+ }
+ }
+ else
+ gw_log (GW_LOG_WARN, "res", "Resource file has bad "
+ "continuation line");
+ }
+ }
+ else
+ { /* resource line */
+ int i = 0;
+ if (rl_last)
+ {
+ rl_last->value = gw_strdup (value);
+ rl_last = NULL;
+ }
+ while (buffer[i] && buffer[i] != ':')
+ i++;
+ if (buffer[i] == ':')
+ {
+ int j = strlen(buffer)-1;
+ buffer[i++] = '\0'; /* terminate name */
+ while (buffer[i] == ' ' || buffer[i] == '\t')
+ i++; /* skip blanks before */
+ while (buffer[j] == '\t' || buffer[j] == ' ')
+ --j; /* skip blanks after */
+ buffer[j+1] = '\0'; /* terminate value */
+ strcpy (value, buffer+i);
+ rl_last = add_name (resource, buffer, NULL);
+ if (!rl_last)
+ err = -2;
+ else
+ {
+ *rlp = rl_last;
+ rlp = &rl_last->next;
+ }
+ }
+ }
+ }
+ if (rl_last)
+ rl_last->value = gw_strdup (value);
+#if HAVE_FLOCK
+ flock (fileno (inf), LOCK_UN);
+#else
+ lock_file (fileno (inf), F_UNLCK);
+#endif
+ fclose (inf);
+ gw_log (GW_LOG_DEBUG, "res", "close of %s", filename);
+ for (rl = ri->lines; rl; rl = rl->next)
+ {
+ switch (rl->kind)
+ {
+ case comment:
+ gw_log (GW_LOG_DEBUG, "res", "%s", rl->value);
+ break;
+ case resource:
+ gw_log (GW_LOG_DEBUG, "res", "%s: %s", rl->name, rl->value);
+ if (symtab_override (id->symtab, rl) < 0)
+ err = -2;
+ break;
+ case blank:
+ gw_log (GW_LOG_DEBUG, "res", "");
+ break;
+ default:
+ assert (0);
+ }
+ }
+ gw_log (GW_LOG_DEBUG, "res", "gw_res_merge returned %d", err);
+ return err;
+}
+
+/*
+ gw_res_get: The resource with name 'name' is checked in the resources
+ represented by 'id'. If the resource is present a pointer to the
+ value (null-terminated string) is returned. If the value is not
+ present the value of 'def' is returned.
+ */
+const char *gw_res_get (GwRes id, const char *name, const char *def)
+{
+ struct res_line_info *rl;
+
+ assert (id);
+ rl = symtab_lookup (id->symtab, name);
+ if (!rl)
+ return def;
+ return rl->value;
+}
+
+/*
+ gw_res_put: Change a resource - modify if it exists - add if not
+ already there. The resource will have impact on the file name
+ 'fname'. Use gw_res_commit (see below) to actually write to the
+ resource file.
+ */
+int gw_res_put (GwRes id, const char *name, const char *value,
+ const char *fname)
+{
+ struct res_file_info *ri;
+ struct res_line_info **rlp;
+ assert (id);
+ assert (fname);
+
+ for (ri = id->files; ri; ri = ri->next)
+ if (!strcmp (ri->fname, fname))
+ break;
+ if (!ri)
+ {
+ if (!(ri = malloc (sizeof (*ri))))
+ return -1;
+ if (!(ri->fname = gw_strdup (fname)))
+ {
+ free (ri);
+ return -1;
+ }
+ ri->next = id->files;
+ id->files = ri;
+ ri->lines = NULL;
+ }
+ for (rlp = &ri->lines; *rlp; rlp = &(*rlp)->next)
+ if (!strcmp ((*rlp)->name, name))
+ break;
+ if (*rlp)
+ {
+ char *new_val = gw_strdup (value);
+ if (!new_val)
+ return -1;
+ free ((*rlp)->value);
+ (*rlp)->value = new_val;
+ }
+ else
+ {
+ *rlp = add_name (resource, name, value);
+ if (!*rlp)
+ return -1;
+ (*rlp)->next = NULL;
+ if (symtab_override (id->symtab, *rlp) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ gw_res_commit: Write the resource file 'fname'. If resources
+ are modified/added then these will be written now.
+ */
+int gw_res_commit (GwRes id, const char *fname)
+{
+ struct res_file_info *ri;
+ struct res_line_info *rl;
+ FILE *out;
+ int i, pos;
+
+ assert (id);
+ assert (fname);
+
+ for (ri = id->files; ri; ri = ri->next)
+ if (!strcmp (ri->fname, fname))
+ break;
+ if (!ri)
+ return -1;
+ if (!(out = fopen (fname, "w")))
+ return -1;
+#if HAVE_FLOCK
+ flock (fileno (out), LOCK_EX);
+#else
+ lock_file (fileno (out), F_WRLCK);
+#endif
+ for (rl = ri->lines; rl; rl = rl->next)
+ switch (rl->kind)
+ {
+ case comment:
+ fputs (rl->value, out);
+ case blank:
+ fputc ('\n', out);
+ break;
+ case resource:
+ fprintf (out, "%s: ", rl->name);
+ pos = strlen(rl->name)+2;
+ i = 0;
+ while (1)
+ {
+ int left = 78-pos;
+ if (strlen(rl->value+i) <= left)
+ break;
+ while (left > 0)
+ {
+ if (rl->value[i+left] == ' ')
+ break;
+
+ --left;
+ }
+ if (left > 0)
+ {
+ int j;
+ for (j = 0; j<left; j++)
+ fputc (rl->value[i+j], out);
+ i += left+1;
+ }
+ else
+ break;
+ fprintf (out, "\n ");
+ pos = 2;
+ }
+ fprintf (out, "%s\n", rl->value+i);
+ break;
+ default:
+ assert (0);
+ }
+ fflush (out);
+#if HAVE_FLOCK
+ flock (fileno (out), LOCK_UN);
+#else
+ lock_file (fileno (out), F_UNLCK);
+#endif
+ fclose (out);
+ return 0;
+}
+
+/*
+ gw_res_trav: Traverse resources associated with file 'fname'. For
+ each resource the handler 'tf' is invoked with name and value.
+ */
+int gw_res_trav (GwRes id, const char *fname, void (*tf)(const char *name,
+ const char *value))
+{
+ assert (id);
+ assert (tf);
+
+ if (fname)
+ {
+ struct res_file_info *ri;
+ struct res_line_info *rl;
+
+ for (ri = id->files; ri; ri = ri->next)
+ if (!strcmp (ri->fname, fname))
+ break;
+ if (!ri)
+ return -1;
+ for (rl = ri->lines; rl; rl = rl->next)
+ if (rl->kind == resource)
+ (*tf) (rl->name, rl->value);
+ }
+ else
+ {
+ struct res_sym_entry *entry;
+
+ for (entry = id->symtab->next; entry; entry=entry->next)
+ (*tf) (entry->info->name, entry->info->value);
+ }
+ return 0;
+}
+
+
+