219024f7650b6b261b55401b65cb12888899d17c
[egate.git] / kernel / persist.c
1 /* Gateway kernel - Z39.50 Persistence
2  * Europagate, 1995
3  *
4  * $Log: persist.c,v $
5  * Revision 1.2  1995/04/19 10:46:19  adam
6  * Persistency works much better now. New command: status - history-like
7  *
8  * Revision 1.1  1995/04/19  07:31:10  adam
9  * First work on Z39.50 persistence.
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <assert.h>
16 #include <ctype.h>
17 #include <string.h>
18 #include <unistd.h>
19 #include <fcntl.h>
20
21 #include "kernel.h"
22
23 static int fgetsx (char *buf, int size, FILE *inf)
24 {
25     char *cp;
26
27     if (!fgets (buf, size, inf))
28         return 0;
29     if ((cp = strchr (buf, '\n')))
30         *cp = '\0';
31     return 1;
32 }
33
34 static int set_change;
35
36 static int obtain_set (ZASS zass, struct gw_user_set *set)
37 {
38     const struct zass_searchent *p;
39
40     p = zass_search (zass, set->rpn, set->name, set->database);
41     if (!p)
42         return 2;
43     if (p->errcode != -1)
44         return 3;
45     if (p->num != set->hits)
46         set_change = 1;
47     set->present_flag = 1;
48     set->hits = p->num;
49     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s researched", set->name);
50     return 0;
51 }
52
53 static int obtain_sets (ZASS zass, struct ccl_rpn_node *rpn, 
54                         struct gw_user_set *sets)
55 {
56     struct gw_user_set *set;
57     int r;
58
59     switch (rpn->kind)
60     {
61     case CCL_RPN_AND:
62     case CCL_RPN_OR:
63     case CCL_RPN_NOT:
64     case CCL_RPN_PROX:
65         if ((r=obtain_sets (zass, rpn->u.p[0], sets)))
66             return r;
67         return obtain_sets (zass, rpn->u.p[1], sets);
68     case CCL_RPN_TERM:
69         return 0;
70     case CCL_RPN_SET:
71         set = user_set_search (rpn->u.setname);
72         if (!set)
73         {
74             gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s not there at all",
75                     rpn->u.setname);
76             return 1;
77         }
78         if (set->present_flag)
79         {
80             gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Set %s already there",
81                     rpn->u.setname);
82             return 0;
83         }
84     }
85     return obtain_set (zass, set);
86 }
87
88 const struct zass_searchent *zass_p_search (ZASS zass, 
89                         struct ccl_rpn_node *rpn, 
90                         const char *result_set,
91                         const char *database,
92                         struct gw_user_set *sets)
93 {
94     int r;
95
96     set_change = 0;
97     r = obtain_sets (zass, rpn, sets);
98     if (r)
99         return NULL;
100     return zass_search (zass, rpn, (char*) result_set, (char*) database);
101 }
102
103 const struct zass_presentent *zass_p_present (ZASS zass,
104                         const char *result_set, int offset, int number)
105 {
106     struct gw_user_set *set;
107
108     set = user_set_search (result_set);
109     if (!set)
110         return NULL;
111     if (!set->present_flag)
112     {
113         const struct zass_searchent *p;
114
115         p = zass_p_search (zass, set->rpn, result_set, set->database,
116                            info.sets);
117         if (!p)
118             return NULL;
119     }
120     return zass_present (zass, (char*) result_set, offset, number);
121 }
122
123 struct ccl_rpn_node *load_rpn (char *buf, FILE *inf)
124 {
125     struct ccl_rpn_node *rpn;
126     struct ccl_rpn_attr **attrp;
127     int type, value, no_read;
128     char *cp;
129
130     if (!fgetsx (buf, 1024, inf))
131         return NULL;
132     switch (*buf)
133     {
134     case 'A':
135         rpn = malloc (sizeof (*rpn));
136         if (!rpn)
137             return NULL;
138         rpn->kind = CCL_RPN_AND;
139         rpn->u.p[0] = load_rpn (buf, inf);
140         rpn->u.p[1] = load_rpn (buf, inf);
141         break;
142     case 'O':
143         rpn = malloc (sizeof (*rpn));
144         if (!rpn)
145             return NULL;
146         rpn->kind = CCL_RPN_OR;
147         rpn->u.p[0] = load_rpn (buf, inf);
148         rpn->u.p[1] = load_rpn (buf, inf);
149         break;
150     case 'N':
151         rpn = malloc (sizeof (*rpn));
152         if (!rpn)
153             return NULL;
154         rpn->kind = CCL_RPN_NOT;
155         rpn->u.p[0] = load_rpn (buf, inf);
156         rpn->u.p[1] = load_rpn (buf, inf);
157         break;
158     case 'P':
159         rpn = malloc (sizeof (*rpn));
160         if (!rpn)
161             return NULL;
162         rpn->kind = CCL_RPN_PROX;
163         rpn->u.p[0] = load_rpn (buf, inf);
164         rpn->u.p[1] = load_rpn (buf, inf);
165         break;
166     case 'T':
167         rpn = malloc (sizeof (*rpn));
168         if (!rpn)
169             return NULL;
170         rpn->kind = CCL_RPN_TERM;
171
172         rpn->u.t.term = gw_strdup (buf+2);
173         attrp = &rpn->u.t.attr_list;
174         if (!fgetsx (buf, 1024, inf))
175             return NULL;
176         cp = buf;
177         while (sscanf (cp, "%d %d%n", &type, &value, &no_read) > 1)
178         {
179             *attrp = malloc (sizeof(**attrp));
180             (*attrp)->type = type;
181             (*attrp)->value = value;
182             attrp = &(*attrp)->next;
183             cp += no_read; 
184         }
185         *attrp = NULL;
186         break;
187     case 'S':
188         rpn = malloc (sizeof (*rpn));
189         if (!rpn)
190             return NULL;
191         rpn->kind = CCL_RPN_SET;
192         rpn->u.setname = gw_strdup (buf+2);
193     }
194     return rpn;
195 }
196
197 int load_p_state (int userid)
198 {
199     FILE *inf;
200     char fname[128];
201     char fline[1025];
202     char database[1024];
203     char resultname[32];
204     int hits;
205     struct gw_user_set *set;
206
207     sprintf (fname, "persist.%d", userid);
208
209     inf = fopen (fname, "r");
210     if (!inf)
211     {
212         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, KERNEL_LOG, 
213                 "Couldn't open %s", fname);
214         return -1;
215     }
216     gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
217        "Reading persistence file %s", fname);
218
219     if (!fgetsx (fline, 1024, inf))
220         return -1;
221     if (sscanf (fline, "%s", info.target) != 1)
222         *info.target = '\0';
223     read_kernel_res ();
224
225     if (!fgetsx (fline, 1024, inf))
226         return -1;
227     free (info.database);
228     info.database = gw_strdup (fline);
229
230     if (!fgetsx (fline, 1024, inf))
231         return -1;
232     if (sscanf (fline, "%d", &info.setno) != 1)
233         return -1;
234     gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
235            "Reading persistence file %s (2)", fname);
236     reopen_target ();
237     while (fgetsx (fline, 1024, inf))
238     {
239         gw_log (GW_LOG_DEBUG, KERNEL_LOG, 
240               "Reading persistence file %s (3)", fname);
241         if (sscanf (fline, "%s %d %s", resultname, &hits, database) != 3)
242             return -1;
243         if (!fgetsx (fline, 1024, inf))        /* search string */
244             return -1;
245         gw_log (GW_LOG_DEBUG, KERNEL_LOG,
246                 "Adding %s, %d hits, database %s",
247                 resultname, hits, database);
248         gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Search string %s", fline);
249         set = user_set_add (resultname, hits, database, NULL, 0, fline);
250         set->rpn = load_rpn (fline, inf);
251         ccl_pr_tree (set->rpn, stderr);
252         fgetsx (fline, 1024, inf);
253     }
254     fclose (inf);
255     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Finished reading %s", fname);
256     return 0;
257 }
258
259 static void save_rpn (struct ccl_rpn_node *rpn, FILE *of)
260 {
261     struct ccl_rpn_attr *attr;
262
263     switch (rpn->kind)
264     {
265     case CCL_RPN_AND:
266         fprintf (of, "A\n");
267         save_rpn (rpn->u.p[0], of);
268         save_rpn (rpn->u.p[1], of);
269         break;
270     case CCL_RPN_OR:
271         fprintf (of, "O\n");
272         save_rpn (rpn->u.p[0], of);
273         save_rpn (rpn->u.p[1], of);
274         break;
275     case CCL_RPN_NOT:
276         fprintf (of, "N\n");
277         save_rpn (rpn->u.p[0], of);
278         save_rpn (rpn->u.p[1], of);
279         break;
280     case CCL_RPN_PROX:
281         fprintf (of, "P\n");
282         save_rpn (rpn->u.p[0], of);
283         save_rpn (rpn->u.p[1], of);
284         break;
285     case CCL_RPN_TERM:
286         fprintf (of, "T %s\n", rpn->u.t.term);
287         for (attr = rpn->u.t.attr_list; attr; attr = attr->next)
288             fprintf (of, "%d %d ", attr->type, attr->value);
289         fprintf (of, "\n");
290         break;
291     case CCL_RPN_SET:
292         fprintf (of, "S %s\n", rpn->u.setname); 
293     }
294 }
295
296 static void save_sets (FILE *of, struct gw_user_set *sp)
297 {
298     if (!sp)
299         return;
300     save_sets (of, sp->prev);
301     fprintf (of, "%s %d %s\n%s\n", sp->name, sp->hits, sp->database,
302              sp->search_str);
303     save_rpn (sp->rpn, of);
304     fprintf (of, "X\n");
305 }
306
307 int save_p_state (int userid)
308 {
309     FILE *of;
310     char fname[128];
311
312     sprintf (fname, "persist.%d", userid);
313
314     of = fopen (fname, "w");
315     if (!of)
316     {
317         gw_log (GW_LOG_WARN|GW_LOG_ERRNO, KERNEL_LOG, 
318                 "Couldn't open %s", fname);
319         return -1;
320     }
321     gw_log (GW_LOG_DEBUG, KERNEL_LOG, "Writing persistence file %s", fname);
322     fprintf (of, "%s\n%s\n%d\n", info.target, info.database, info.setno);
323     save_sets (of, info.sets);
324     fclose (of);
325     return 0;
326 }