|
1 // mapfile.c - associative array interface to external files |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1999 Sven Wischnowsky, Peter Stephenson |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Sven Wischnowsky, Peter Stephenson or the Zsh Development |
|
18 * Group be liable to any party for direct, indirect, special, incidental, or |
|
19 * consequential damages arising out of the use of this software and its |
|
20 * documentation, even if Peter Stephenson, Sven Wischnowsky and the Zsh |
|
21 * Development Group have been advised of the possibility of such damage. |
|
22 * |
|
23 * Peter Stephenson, Sven Wischnowsky and the Zsh Development Group |
|
24 * specifically disclaim any warranties, including, but not limited to, the |
|
25 * implied warranties of merchantability and fitness for a particular purpose. |
|
26 * The softwareprovided hereunder is on an "as is" basis, and Peter |
|
27 * Stephenson, Sven Wischnowsky and the Zsh Development Group have no |
|
28 * obligation to provide maintenance, support, updates, enhancements, or |
|
29 * modifications. |
|
30 * |
|
31 */ |
|
32 |
|
33 /* |
|
34 * To do: worry about when keys of associative arrays get unmeta'd. |
|
35 */ |
|
36 #include "mapfile.mdh" |
|
37 #include "mapfile.pro" |
|
38 |
|
39 /* |
|
40 * Make sure we have all the bits I'm using for memory mapping, otherwise |
|
41 * I don't know what I'm doing. |
|
42 */ |
|
43 #if defined(HAVE_SYS_MMAN_H) && defined(HAVE_FTRUNCATE) |
|
44 #if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(HAVE_MSYNC) |
|
45 #define USE_MMAP 1 |
|
46 |
|
47 #include <sys/mman.h> |
|
48 |
|
49 #if !defined(MAP_VARIABLE) |
|
50 #define MAP_VARIABLE 0 |
|
51 #endif |
|
52 #if !defined(MAP_FILE) |
|
53 #define MAP_FILE 0 |
|
54 #endif |
|
55 #if !defined(MAP_NORESERVE) |
|
56 #define MAP_NORESERVE 0 |
|
57 #endif |
|
58 #define MMAP_ARGS (MAP_FILE | MAP_VARIABLE | MAP_SHARED | MAP_NORESERVE) |
|
59 |
|
60 #endif /* HAVE_MMAP && HAVE_MUNMAP && HAVE_MSYNC */ |
|
61 #endif /* HAVE_SYS_MMAN_H && HAVE_FTRUNCATE */ |
|
62 |
|
63 #ifdef __SYMBIAN32__ |
|
64 #ifdef __WINSCW__ |
|
65 #pragma warn_unusedarg off |
|
66 #endif//__WINSCW__ |
|
67 #endif//__SYMBIAN32__ |
|
68 |
|
69 /* |
|
70 * Name of the special parameter. If zmodload took arguments, |
|
71 * we could make this selectable. |
|
72 */ |
|
73 static char mapfile_nam[] = "mapfile"; |
|
74 |
|
75 static Param mapfile_pm; |
|
76 |
|
77 /* Empty dummy function for special hash parameters. */ |
|
78 |
|
79 /**/ |
|
80 static void |
|
81 shempty(void) |
|
82 { |
|
83 } |
|
84 |
|
85 static const struct gsu_hash mapfiles_gsu = |
|
86 { hashgetfn, setpmmapfiles, stdunsetfn }; |
|
87 |
|
88 /* Create the special hash parameter. */ |
|
89 |
|
90 /**/ |
|
91 static Param |
|
92 createmapfilehash() |
|
93 { |
|
94 Param pm; |
|
95 HashTable ht; |
|
96 |
|
97 unsetparam(mapfile_nam); |
|
98 mapfile_pm = NULL; |
|
99 |
|
100 if (!(pm = createparam(mapfile_nam, PM_SPECIAL|PM_HIDE|PM_HIDEVAL| |
|
101 PM_REMOVABLE|PM_HASHED))) |
|
102 return NULL; |
|
103 |
|
104 pm->level = pm->old ? locallevel : 0; |
|
105 pm->gsu.h = &mapfiles_gsu; |
|
106 pm->u.hash = ht = newhashtable(7, mapfile_nam, NULL); |
|
107 |
|
108 ht->hash = hasher; |
|
109 ht->emptytable = (TableFunc) shempty; |
|
110 ht->filltable = NULL; |
|
111 ht->addnode = (AddNodeFunc) shempty; |
|
112 ht->getnode = ht->getnode2 = getpmmapfile; |
|
113 ht->removenode = (RemoveNodeFunc) shempty; |
|
114 ht->disablenode = NULL; |
|
115 ht->enablenode = NULL; |
|
116 ht->freenode = (FreeNodeFunc) shempty; |
|
117 ht->printnode = printparamnode; |
|
118 ht->scantab = scanpmmapfile; |
|
119 |
|
120 return (mapfile_pm = pm); |
|
121 } |
|
122 |
|
123 /* Functions for the options special parameter. */ |
|
124 |
|
125 /**/ |
|
126 static void |
|
127 setpmmapfile(Param pm, char *value) |
|
128 { |
|
129 int fd = -1, len; |
|
130 char *name = ztrdup(pm->nam); |
|
131 #ifdef USE_MMAP |
|
132 caddr_t mmptr; |
|
133 #else |
|
134 FILE *fout; |
|
135 #endif |
|
136 |
|
137 /* |
|
138 * First unmetafy the value, and the name since we don't |
|
139 * where it's been. |
|
140 */ |
|
141 unmetafy(name, &len); |
|
142 unmetafy(value, &len); |
|
143 |
|
144 /* Open the file for writing */ |
|
145 #ifdef USE_MMAP |
|
146 if (!(pm->flags & PM_READONLY) && |
|
147 (fd = open(name, O_RDWR|O_CREAT|O_NOCTTY, 0666)) >= 0 && |
|
148 (mmptr = (caddr_t)mmap((caddr_t)0, len, PROT_READ | PROT_WRITE, |
|
149 MMAP_ARGS, fd, (off_t)0)) != (caddr_t)-1) { |
|
150 /* |
|
151 * First we need to make sure the file is long enough for |
|
152 * when we msync. On AIX, at least, we just get zeroes otherwise. |
|
153 */ |
|
154 ftruncate(fd, len); |
|
155 memcpy(mmptr, value, len); |
|
156 #ifndef MS_SYNC |
|
157 #define MS_SYNC 0 |
|
158 #endif |
|
159 msync(mmptr, len, MS_SYNC); |
|
160 /* |
|
161 * Then we need to truncate again, since mmap() always maps complete |
|
162 * pages. Honestly, I tried it without, and you need both. |
|
163 */ |
|
164 ftruncate(fd, len); |
|
165 munmap(mmptr, len); |
|
166 } |
|
167 #else /* don't USE_MMAP */ |
|
168 /* can't be bothered to do anything too clever here */ |
|
169 if ((fout = fopen(name, "w"))) { |
|
170 while (len--) |
|
171 putc(*value++, fout); |
|
172 fclose(fout); |
|
173 } |
|
174 #endif /* USE_MMAP */ |
|
175 if (fd >= 0) |
|
176 close(fd); |
|
177 free(name); |
|
178 free(value); |
|
179 } |
|
180 |
|
181 /**/ |
|
182 static void |
|
183 unsetpmmapfile(Param pm, UNUSED(int exp)) |
|
184 { |
|
185 /* Unlink the file given by pm->nam */ |
|
186 char *fname = ztrdup(pm->nam); |
|
187 int dummy; |
|
188 unmetafy(fname, &dummy); |
|
189 |
|
190 if (!(pm->flags & PM_READONLY)) |
|
191 unlink(fname); |
|
192 |
|
193 free(fname); |
|
194 } |
|
195 |
|
196 /**/ |
|
197 static void |
|
198 setpmmapfiles(Param pm, HashTable ht) |
|
199 { |
|
200 int i; |
|
201 HashNode hn; |
|
202 |
|
203 /* just to see if I've understood what's happening */ |
|
204 DPUTS(pm != mapfile_pm, "BUG: setpmmapfiles called for wrong param"); |
|
205 |
|
206 if (!ht) |
|
207 return; |
|
208 |
|
209 if (!(pm->flags & PM_READONLY)) |
|
210 for (i = 0; i < ht->hsize; i++) |
|
211 for (hn = ht->nodes[i]; hn; hn = hn->next) { |
|
212 struct value v; |
|
213 |
|
214 v.isarr = v.inv = v.start = 0; |
|
215 v.end = -1; |
|
216 v.arr = NULL; |
|
217 v.pm = (Param) hn; |
|
218 |
|
219 setpmmapfile(v.pm, ztrdup(getstrvalue(&v))); |
|
220 } |
|
221 deleteparamtable(ht); |
|
222 } |
|
223 |
|
224 /**/ |
|
225 static char * |
|
226 get_contents(char *fname) |
|
227 { |
|
228 int fd; |
|
229 #ifdef USE_MMAP |
|
230 caddr_t mmptr; |
|
231 struct stat sbuf; |
|
232 #endif |
|
233 char *val; |
|
234 unmetafy(fname = ztrdup(fname), &fd); |
|
235 |
|
236 #ifdef USE_MMAP |
|
237 if ((fd = open(fname, O_RDONLY | O_NOCTTY)) < 0 || |
|
238 fstat(fd, &sbuf) || |
|
239 (mmptr = (caddr_t)mmap((caddr_t)0, sbuf.st_size, PROT_READ, |
|
240 MMAP_ARGS, fd, (off_t)0)) == (caddr_t)-1) { |
|
241 if (fd >= 0) |
|
242 close(fd); |
|
243 free(fname); |
|
244 return NULL; |
|
245 } |
|
246 |
|
247 /* |
|
248 * Sadly, we need to copy the thing even if metafying doesn't |
|
249 * change it. We just don't know when we might get a chance to |
|
250 * munmap it, otherwise. |
|
251 */ |
|
252 val = metafy((char *)mmptr, sbuf.st_size, META_HEAPDUP); |
|
253 |
|
254 munmap(mmptr, sbuf.st_size); |
|
255 close(fd); |
|
256 #else /* don't USE_MMAP */ |
|
257 val = NULL; |
|
258 if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { |
|
259 LinkList ll; |
|
260 |
|
261 if ((ll = readoutput(fd, 1))) |
|
262 val = peekfirst(ll); |
|
263 } |
|
264 #endif /* USE_MMAP */ |
|
265 free(fname); |
|
266 return val; |
|
267 } |
|
268 |
|
269 static const struct gsu_scalar mapfile_gsu = |
|
270 { strgetfn, setpmmapfile, unsetpmmapfile }; |
|
271 |
|
272 /**/ |
|
273 static HashNode |
|
274 getpmmapfile(UNUSED(HashTable ht), char *name) |
|
275 { |
|
276 char *contents; |
|
277 Param pm = NULL; |
|
278 |
|
279 pm = (Param) hcalloc(sizeof(struct param)); |
|
280 pm->nam = dupstring(name); |
|
281 pm->flags = PM_SCALAR; |
|
282 pm->gsu.s = &mapfile_gsu; |
|
283 pm->flags |= (mapfile_pm->flags & PM_READONLY); |
|
284 |
|
285 /* Set u.str to contents of file given by name */ |
|
286 if ((contents = get_contents(pm->nam))) |
|
287 pm->u.str = contents; |
|
288 else { |
|
289 pm->u.str = ""; |
|
290 pm->flags |= PM_UNSET; |
|
291 } |
|
292 return (HashNode) pm; |
|
293 } |
|
294 |
|
295 |
|
296 /**/ |
|
297 static void |
|
298 scanpmmapfile(UNUSED(HashTable ht), ScanFunc func, int flags) |
|
299 { |
|
300 struct param pm; |
|
301 DIR *dir; |
|
302 |
|
303 if (!(dir = opendir("."))) |
|
304 return; |
|
305 |
|
306 memset((void *)&pm, 0, sizeof(struct param)); |
|
307 pm.flags = PM_SCALAR; |
|
308 pm.gsu.s = &mapfile_gsu; |
|
309 pm.flags |= (mapfile_pm->flags & PM_READONLY); |
|
310 |
|
311 /* Here we scan the current directory, calling func() for each file */ |
|
312 while ((pm.nam = zreaddir(dir, 1))) { |
|
313 /* |
|
314 * Hmmm, it's rather wasteful always to read the contents. |
|
315 * In fact, it's grotesequely wasteful, since that would mean |
|
316 * we always read the entire contents of every single file |
|
317 * in the directory into memory. Hence just leave it empty. |
|
318 */ |
|
319 pm.nam = dupstring(pm.nam); |
|
320 pm.u.str = ""; |
|
321 func((HashNode) &pm, flags); |
|
322 } |
|
323 closedir(dir); |
|
324 } |
|
325 |
|
326 /**/ |
|
327 int |
|
328 setup_(UNUSED(Module m)) |
|
329 { |
|
330 return 0; |
|
331 } |
|
332 |
|
333 /**/ |
|
334 int |
|
335 boot_(UNUSED(Module m)) |
|
336 { |
|
337 /* Create the special associative array. */ |
|
338 |
|
339 if (!createmapfilehash()) |
|
340 return 1; |
|
341 |
|
342 return 0; |
|
343 } |
|
344 |
|
345 /**/ |
|
346 int |
|
347 cleanup_(UNUSED(Module m)) |
|
348 { |
|
349 Param pm; |
|
350 |
|
351 /* Remove the special parameter if it is still the same. */ |
|
352 |
|
353 if ((pm = (Param) paramtab->getnode(paramtab, mapfile_nam)) && |
|
354 pm == mapfile_pm) { |
|
355 pm->flags &= ~PM_READONLY; |
|
356 unsetparam_pm(pm, 0, 1); |
|
357 } |
|
358 return 0; |
|
359 } |
|
360 |
|
361 /**/ |
|
362 int |
|
363 finish_(UNUSED(Module m)) |
|
364 { |
|
365 return 0; |
|
366 } |