1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
|
/*
* db_gdbm.c: low level gdbm interface routines for man.
*
* Copyright (C) 1994, 1995 Graeme W. Wilford. (Wilf.)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* Mon Aug 8 20:35:30 BST 1994 Wilf. (G.Wilford@ee.surrey.ac.uk)
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#ifdef GDBM
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "stat-time.h"
#include "timespec.h"
#include "manconfig.h"
#include "hashtable.h"
#include "cleanup.h"
#include "mydbm.h"
static struct hashtable *parent_sortkey_hash;
struct sortkey {
datum key;
struct sortkey *next;
};
/* setjmp/longjmp handling to defend against _gdbm_fatal exiting under our
* feet. Not thread-safe, but there is no plan for man-db to ever use
* threads.
*/
static jmp_buf open_env;
static int opening;
/* Mimic _gdbm_fatal's error output, but handle errors during open more
* gracefully than exiting.
*/
static void trap_error (const char *val)
{
if (opening) {
debug ("gdbm error: %s\n", val);
longjmp (open_env, 1);
} else
fprintf (stderr, "gdbm fatal: %s\n", val);
}
man_gdbm_wrapper man_gdbm_open_wrapper (const char *name, int flags)
{
man_gdbm_wrapper wrap;
GDBM_FILE file;
datum key, content;
opening = 1;
if (setjmp (open_env))
return NULL;
file = gdbm_open ((char *) name, BLK_SIZE, flags, DBMODE, trap_error);
if (!file)
return NULL;
wrap = xmalloc (sizeof *wrap);
wrap->name = xstrdup (name);
wrap->file = file;
if ((flags & ~GDBM_FAST) != GDBM_NEWDB) {
/* While the setjmp/longjmp guard is in effect, make sure we
* can read from the database at all.
*/
memset (&key, 0, sizeof key);
MYDBM_SET (key, xstrdup (VER_KEY));
content = MYDBM_FETCH (wrap, key);
MYDBM_FREE_DPTR (key);
MYDBM_FREE_DPTR (content);
}
opening = 0;
return wrap;
}
static void parent_sortkey_hashtable_free (void *defn)
{
/* Automatically free child hashtables on removal. */
hashtable_free ((struct hashtable *) defn);
}
static void sortkey_hashtable_free (void *defn)
{
struct sortkey *key = (struct sortkey *) defn;
MYDBM_FREE_DPTR (key->key);
free (key);
}
static int sortkey_compare (const void *a, const void *b)
{
const struct sortkey **left = (const struct sortkey **) a;
const struct sortkey **right = (const struct sortkey **) b;
int cmp;
size_t minsize;
/* Sentinel NULL elements sort to the end. */
if (!MYDBM_DPTR ((*left)->key))
return 1;
else if (!MYDBM_DPTR ((*right)->key))
return -1;
if (MYDBM_DSIZE ((*left)->key) < MYDBM_DSIZE ((*right)->key))
minsize = MYDBM_DSIZE ((*left)->key);
else
minsize = MYDBM_DSIZE ((*right)->key);
cmp = strncmp (MYDBM_DPTR ((*left)->key), MYDBM_DPTR ((*right)->key),
minsize);
if (cmp)
return cmp;
else if (MYDBM_DSIZE ((*left)->key) < MYDBM_DSIZE ((*right)->key))
return 1;
else if (MYDBM_DSIZE ((*left)->key) > MYDBM_DSIZE ((*right)->key))
return -1;
else
return 0;
}
static datum empty_datum = { NULL, 0 };
/* We keep a hashtable of filenames to sorted lists of keys. Each list is
* stored both with links from each element to the next and in a hashtable,
* so that both sequential access and random access are quick. This is
* necessary for a reasonable ordered implementation of nextkey.
*/
datum man_gdbm_firstkey (man_gdbm_wrapper wrap)
{
struct hashtable *sortkey_hash;
struct sortkey **keys, *firstkey;
int numkeys = 0, maxkeys = 256;
int i;
/* Build the raw list of keys and sort it. */
keys = xnmalloc (maxkeys, sizeof *keys);
keys[0] = xmalloc (sizeof **keys);
keys[0]->key = gdbm_firstkey (wrap->file);
while (MYDBM_DPTR (keys[numkeys]->key)) {
if (++numkeys >= maxkeys) {
maxkeys *= 2;
keys = xnrealloc (keys, maxkeys, sizeof *keys);
}
keys[numkeys] = xmalloc (sizeof **keys);
keys[numkeys]->key =
gdbm_nextkey (wrap->file, keys[numkeys - 1]->key);
}
free (keys[numkeys]);
keys[numkeys] = NULL; /* simplifies the empty case */
qsort (keys, numkeys, sizeof *keys, &sortkey_compare);
/* Link the elements together and insert them into a hash. */
sortkey_hash = hashtable_create (&sortkey_hashtable_free);
for (i = 0; i < numkeys; ++i) {
if (i < numkeys - 1)
keys[i]->next = keys[i + 1];
else
keys[i]->next = NULL;
hashtable_install (sortkey_hash,
MYDBM_DPTR (keys[i]->key),
MYDBM_DSIZE (keys[i]->key),
keys[i]);
}
firstkey = keys[0];
free (keys); /* element memory now owned by hashtable */
if (!parent_sortkey_hash) {
parent_sortkey_hash = hashtable_create
(&parent_sortkey_hashtable_free);
push_cleanup ((cleanup_fun) hashtable_free,
parent_sortkey_hash, 0);
}
/* Remember this structure for use by nextkey. */
hashtable_install (parent_sortkey_hash,
wrap->name, strlen (wrap->name), sortkey_hash);
if (firstkey)
return copy_datum (firstkey->key);
else
return empty_datum; /* dptr is NULL, so no copy needed */
}
datum man_gdbm_nextkey (man_gdbm_wrapper wrap, datum key)
{
struct hashtable *sortkey_hash;
struct sortkey *sortkey;
if (!parent_sortkey_hash)
return empty_datum;
sortkey_hash = hashtable_lookup (parent_sortkey_hash,
wrap->name, strlen (wrap->name));
if (!sortkey_hash)
return empty_datum;
sortkey = hashtable_lookup (sortkey_hash,
MYDBM_DPTR (key), MYDBM_DSIZE (key));
if (!sortkey || !sortkey->next)
return empty_datum;
return copy_datum (sortkey->next->key);
}
struct timespec man_gdbm_get_time (man_gdbm_wrapper wrap)
{
struct stat st;
if (fstat (gdbm_fdesc (wrap->file), &st) < 0) {
struct timespec t;
t.tv_sec = -1;
t.tv_nsec = -1;
return t;
}
return get_stat_mtime (&st);
}
void man_gdbm_set_time (man_gdbm_wrapper wrap, const struct timespec time)
{
struct timespec times[2];
times[0] = time;
times[1] = time;
futimens (gdbm_fdesc (wrap->file), times);
}
void man_gdbm_close (man_gdbm_wrapper wrap)
{
if (!wrap)
return;
if (parent_sortkey_hash) {
struct hashtable *sortkey_hash =
hashtable_lookup (parent_sortkey_hash,
wrap->name, strlen (wrap->name));
if (sortkey_hash)
hashtable_remove (parent_sortkey_hash,
wrap->name, strlen (wrap->name));
}
free (wrap->name);
gdbm_close (wrap->file);
free (wrap);
}
#ifndef HAVE_GDBM_EXISTS
int gdbm_exists (GDBM_FILE file, datum key)
{
char *memory;
memory = MYDBM_DPTR (gdbm_fetch (file, key));
if (memory) {
free (memory);
return 1;
}
return 0;
}
#endif /* !HAVE_GDBM_EXISTS */
#endif /* GDBM */
|