More work on settings system.. still not functional.
[pazpar2-moved-to-github.git] / src / settings.c
1 // $Id: settings.c,v 1.2 2007-03-28 04:33:41 quinn Exp $
2 // This module implements a generic system of settings (attribute-value) that can 
3 // be associated with search targets. The system supports both default values,
4 // per-target overrides, and per-user settings.
5 //
6
7 #include <string.h>
8 #include <stdio.h>
9 #include <sys/types.h>
10 #include <dirent.h>
11 #include <stdlib.h>
12 #include <sys/stat.h>
13
14 #include <libxml/parser.h>
15 #include <libxml/tree.h>
16
17 #include <yaz/nmem.h>
18 #include <yaz/log.h>
19
20 static NMEM nmem = 0;
21
22 #define PZ_PIGGYBACK    0 
23 #define PZ_ELEMENTS     1
24 #define PZ_SYNTAX       2
25
26 static char *hard_settings[] = {
27     "pz:piggyback",
28     "pz:elements",
29     "pz::syntax",
30     0
31 };
32
33 struct setting
34 {
35     char *target;
36     char *name;
37     char *value;
38     char *user;
39 };
40
41 struct setting_dictionary
42 {
43     char **dict;
44     int size;
45     int num;
46 };
47
48 static int isdir(const char *path)
49 {
50     struct stat st;
51
52     if (stat(path, &st) < 0)
53     {
54         yaz_log(YLOG_FATAL|YLOG_ERRNO, "%s", path);
55         exit(1);
56     }
57     return st.st_mode & S_IFDIR;
58 }
59
60 static void read_settings_file(const char *path, void *context,
61         void (*fun)(void *context, struct setting *set))
62 {
63     xmlDoc *doc = xmlParseFile(path);
64     xmlNode *n;
65     //xmlChar *namea, *targeta, *valuea, *usera;
66
67     if (!doc)
68     {
69         yaz_log(YLOG_FATAL, "Failed to parse %s", path);
70         exit(1);
71     }
72     n = xmlDocGetRootElement(doc);
73
74     for (n = n->children; n; n = n->next)
75     {
76         fprintf(stderr, "Node name: %s\n", n->name);
77     }
78 }
79  
80 // Recursively read files in a directory structure, calling 
81 static void read_settings(const char *path, void *context,
82                 void (*fun)(void *context, struct setting *set))
83 {
84     DIR *d;
85     struct dirent *de;
86
87     if (!(d = opendir(path)))
88     {
89         yaz_log(YLOG_FATAL|YLOG_ERRNO, "%s", path);
90         exit(1);
91     }
92     while ((de = readdir(d)))
93     {
94         char tmp[1024];
95         if (*de->d_name == '.' || !strcmp(de->d_name, "CVS"))
96             continue;
97         sprintf(tmp, "%s/%s", path, de->d_name);
98         if (isdir(tmp))
99             read_settings(tmp, context, fun);
100         else
101         {
102             char *dot;
103             if ((dot = rindex(de->d_name, '.')) && !strcmp(dot + 1, "xml"))
104                 read_settings_file(tmp, context, fun);
105         }
106     }
107     closedir(d);
108 }
109
110 // Callback. Adds a new entry to the dictionary if necessary
111 // This is used in pass 1 to determine layout of dictionary
112 static void prepare_dictionary(void *context, struct setting *set)
113 {
114     struct setting_dictionary *dict = (struct setting_dictionary *) context;
115     int i;
116
117     for (i = 0; i < dict->num; i++)
118         if (!strcmp(set->name, set->name))
119             return;
120     // Create a new dictionary entry
121     // Grow dictionary if necessary
122     if (!dict->size)
123         dict->dict = nmem_malloc(nmem, (dict->size = 50) * sizeof(char*));
124     else if (dict->num + 1 > dict->size)
125     {
126         char **tmp = nmem_malloc(nmem, dict->size * 2 * sizeof(char*));
127         memcpy(tmp, dict->dict, dict->size * sizeof(char*));
128         dict->dict = tmp;
129         dict->size *= 2;
130     }
131     dict->dict[dict->num++] = nmem_strdup(nmem, set->name);
132 }
133
134 #ifdef GAGA
135 // Callback -- updates database records with dictionary entries as appropriate
136 static void update_databases(void *context, struct setting *set)
137 {
138 }
139 #endif
140
141 // This simply copies the 'hard' (application-specific) settings
142 // to the settings dictionary.
143 static void initialize_hard_settings(struct setting_dictionary *dict)
144 {
145     dict->dict = nmem_malloc(nmem, sizeof(hard_settings) - sizeof(char*));
146     dict->size = (sizeof(hard_settings) - sizeof(char*)) / sizeof(char*);
147     memcpy(dict->dict, hard_settings, dict->size * sizeof(char*));
148     dict->num = dict->size;
149 }
150
151 // If we ever decide we need to be able to specify multiple settings directories,
152 // the two calls to read_settings must be split -- so the dictionary is prepared
153 // for the contents of every directory before the databases are updated.
154 void settings_read(const char *path)
155 {
156     struct setting_dictionary *new;
157     if (!nmem)
158         nmem = nmem_create();
159     else
160         nmem_reset(nmem);
161     new = nmem_malloc(nmem, sizeof(*new));
162     initialize_hard_settings(new);
163     memset(new, sizeof(*new), 0);
164     read_settings(path, new, prepare_dictionary);
165     //read_settings(path, new, update_databases);
166 }
167
168 /*
169  * Local variables:
170  * c-basic-offset: 4
171  * indent-tabs-mode: nil
172  * End:
173  * vim: shiftwidth=4 tabstop=8 expandtab
174  */