1 /*
2 * db_gdbm.c - bindings for gdbm
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 2008 Clint Adams
7 * All rights reserved.
8 *
9 * Modifications copyright (c) 2017 Sebastian Gniazdowski
10 * All rights reserved.
11 *
12 * Permission is hereby granted, without written agreement and without
13 * license or royalty fees, to use, copy, modify, and distribute this
14 * software and to distribute modified versions of this software for any
15 * purpose, provided that the above copyright notice and the following
16 * two paragraphs appear in all copies of this software.
17 *
18 * In no event shall Clint Adams or the Zsh Development
19 * Group be liable to any party for direct, indirect, special, incidental, or
20 * consequential damages arising out of the use of this software and its
21 * documentation, even if Peter Stephenson, Sven Wischnowsky and the Zsh
22 * Development Group have been advised of the possibility of such damage.
23 *
24 * Clint Adams and the Zsh Development Group
25 * specifically disclaim any warranties, including, but not limited to, the
26 * implied warranties of merchantability and fitness for a particular purpose.
27 * The software provided hereunder is on an "as is" basis, and Peter
28 * Stephenson, Sven Wischnowsky and the Zsh Development Group have no
29 * obligation to provide maintenance, support, updates, enhancements, or
30 * modifications.
31 *
32 */
33
34 #include "db_gdbm.mdh"
35 #include "db_gdbm.pro"
36
37 #ifndef PM_UPTODATE
38 #define PM_UPTODATE (1<<19) /* Parameter has up-to-date data (e.g. loaded from DB) */
39 #endif
40
41 static Param createhash( char *name, int flags );
42 static int append_tied_name( const char *name );
43 static int remove_tied_name( const char *name );
44 static char *unmetafy_zalloc(const char *to_copy, int *new_len);
45 static void myfreeparamnode(HashNode hn);
46
47 static int no_database_action = 0;
48
49 /*
50 * Make sure we have all the bits I'm using for memory mapping, otherwise
51 * I don't know what I'm doing.
52 */
53 #if defined(HAVE_GDBM_H) && defined(HAVE_GDBM_OPEN)
54
55 #include <gdbm.h>
56
57 static char *backtype = "db/gdbm";
58
59 /*
60 * Longer GSU structure, to carry GDBM_FILE of owning
61 * database. Every parameter (hash value) receives GSU
62 * pointer and thus also receives GDBM_FILE - this way
63 * parameters can access proper database.
64 *
65 * Main HashTable parameter has the same instance of
66 * the custom GSU struct in u.hash->tmpdata field.
67 * When database is closed, `dbf` field is set to NULL
68 * and hash values know to not access database when
69 * being unset (total purge at zuntie).
70 *
71 * When database closing is ended, custom GSU struct
72 * is freed. Only new ztie creates new custom GSU
73 * struct instance.
74 */
75
76 struct gsu_scalar_ext {
77 struct gsu_scalar std; /* Size of three pointers */
78 GDBM_FILE dbf;
79 char *dbfile_path;
80 };
81
82 /* Source structure - will be copied to allocated one,
83 * with `dbf` filled. `dbf` allocation <-> gsu allocation. */
84 static const struct gsu_scalar_ext gdbm_gsu_ext =
85 { { gdbmgetfn, gdbmsetfn, gdbmunsetfn }, 0, 0 };
86
87 /**/
88 static const struct gsu_hash gdbm_hash_gsu =
89 { hashgetfn, gdbmhashsetfn, gdbmhashunsetfn };
90
91 static struct builtin bintab[] = {
92 BUILTIN("ztie", 0, bin_ztie, 1, -1, 0, "d:f:r", NULL),
93 BUILTIN("zuntie", 0, bin_zuntie, 1, -1, 0, "u", NULL),
94 BUILTIN("zgdbmpath", 0, bin_zgdbmpath, 1, -1, 0, "", NULL),
95 };
96
97 #define ROARRPARAMDEF(name, var) \
98 { name, PM_ARRAY | PM_READONLY, (void *) var, NULL, NULL, NULL, NULL }
99
100 /* Holds names of all tied parameters */
101 static char **zgdbm_tied;
102
103 static struct paramdef patab[] = {
104 ROARRPARAMDEF( "zgdbm_tied", &zgdbm_tied ),
105 };
106
107 /**/
108 static int
bin_ztie(char * nam,char ** args,Options ops,UNUSED (int func))109 bin_ztie(char *nam, char **args, Options ops, UNUSED(int func))
110 {
111 struct gsu_scalar_ext *dbf_carrier;
112 char *resource_name, *pmname;
113 GDBM_FILE dbf = NULL;
114 int read_write = GDBM_SYNC, pmflags = PM_REMOVABLE;
115 Param tied_param;
116
117 if(!OPT_ISSET(ops,'d')) {
118 zwarnnam(nam, "you must pass `-d %s'", backtype);
119 return 1;
120 }
121 if(!OPT_ISSET(ops,'f')) {
122 zwarnnam(nam, "you must pass `-f' with a filename", NULL);
123 return 1;
124 }
125 if (OPT_ISSET(ops,'r')) {
126 read_write |= GDBM_READER;
127 pmflags |= PM_READONLY;
128 } else {
129 read_write |= GDBM_WRCREAT;
130 }
131
132 /* Here should be a lookup of the backend type against
133 * a registry, if generam DB mechanism is to be added */
134 if (strcmp(OPT_ARG(ops, 'd'), backtype) != 0) {
135 zwarnnam(nam, "unsupported backend type `%s'", OPT_ARG(ops, 'd'));
136 return 1;
137 }
138
139 resource_name = OPT_ARG(ops, 'f');
140 pmname = *args;
141
142 if ((tied_param = (Param)paramtab->getnode(paramtab, pmname)) &&
143 !(tied_param->node.flags & PM_UNSET)) {
144 /*
145 * Unset any existing parameter. Note there's no implicit
146 * "local" here, but if the existing parameter is local
147 * then new parameter will be also local without following
148 * unset.
149 *
150 * We need to do this before attempting to open the DB
151 * in case this variable is already tied to a DB.
152 *
153 * This can fail if the variable is readonly or restricted.
154 * We could call unsetparam() and check errflag instead
155 * of the return status.
156 */
157 if (unsetparam_pm(tied_param, 0, 1))
158 return 1;
159 }
160
161 gdbm_errno=0;
162 dbf = gdbm_open(resource_name, 0, read_write, 0666, 0);
163 if(dbf == NULL) {
164 zwarnnam(nam, "error opening database file %s (%s)", resource_name, gdbm_strerror(gdbm_errno));
165 return 1;
166 }
167
168 if (!(tied_param = createhash(pmname, pmflags))) {
169 zwarnnam(nam, "cannot create the requested parameter %s", pmname);
170 gdbm_close(dbf);
171 return 1;
172 }
173
174 tied_param->gsu.h = &gdbm_hash_gsu;
175
176 /* Allocate parameter sub-gsu, fill dbf field.
177 * dbf allocation is 1 to 1 accompanied by
178 * gsu_scalar_ext allocation. */
179
180 dbf_carrier = (struct gsu_scalar_ext *) zalloc(sizeof(struct gsu_scalar_ext));
181 dbf_carrier->std = gdbm_gsu_ext.std;
182 dbf_carrier->dbf = dbf;
183 tied_param->u.hash->tmpdata = (void *)dbf_carrier;
184
185 /* Fill also file path field */
186 if (*resource_name != '/') {
187 /* Code copied from check_autoload() */
188 resource_name = zhtricat(metafy(zgetcwd(), -1, META_HEAPDUP), "/", resource_name);
189 resource_name = xsymlink(resource_name, 1);
190 }
191 dbf_carrier->dbfile_path = ztrdup(resource_name);
192
193 addmodulefd(gdbm_fdesc(dbf), FDT_INTERNAL);
194 append_tied_name(pmname);
195
196 return 0;
197 }
198
199 /**/
200 static int
bin_zuntie(char * nam,char ** args,Options ops,UNUSED (int func))201 bin_zuntie(char *nam, char **args, Options ops, UNUSED(int func))
202 {
203 Param pm;
204 char *pmname;
205 int ret = 0;
206
207 for (pmname = *args; *args++; pmname = *args) {
208 pm = (Param) paramtab->getnode(paramtab, pmname);
209 if(!pm) {
210 zwarnnam(nam, "cannot untie %s", pmname);
211 ret = 1;
212 continue;
213 }
214 if (pm->gsu.h != &gdbm_hash_gsu) {
215 zwarnnam(nam, "not a tied gdbm hash: %s", pmname);
216 ret = 1;
217 continue;
218 }
219
220 queue_signals();
221 if (OPT_ISSET(ops,'u')) {
222 pm->node.flags &= ~PM_READONLY;
223 }
224 if (unsetparam_pm(pm, 0, 1)) {
225 /* assume already reported */
226 ret = 1;
227 }
228 unqueue_signals();
229 }
230
231 return ret;
232 }
233
234 /**/
235 static int
bin_zgdbmpath(char * nam,char ** args,Options ops,UNUSED (int func))236 bin_zgdbmpath(char *nam, char **args, Options ops, UNUSED(int func))
237 {
238 Param pm;
239 char *pmname;
240
241 pmname = *args;
242
243 if (!pmname) {
244 zwarnnam(nam, "parameter name (whose path is to be written to $REPLY) is required");
245 return 1;
246 }
247
248 pm = (Param) paramtab->getnode(paramtab, pmname);
249 if(!pm) {
250 zwarnnam(nam, "no such parameter: %s", pmname);
251 return 1;
252 }
253
254 if (pm->gsu.h != &gdbm_hash_gsu) {
255 zwarnnam(nam, "not a tied gdbm parameter: %s", pmname);
256 return 1;
257 }
258
259 /* Paranoia, it *will* be always set */
260 if (((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path) {
261 setsparam("REPLY", ztrdup(((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbfile_path));
262 } else {
263 setsparam("REPLY", ztrdup(""));
264 }
265
266 return 0;
267 }
268
269 /*
270 * The param is actual param in hash – always, because
271 * getgdbmnode creates every new key seen. However, it
272 * might be not PM_UPTODATE - which means that database
273 * wasn't yet queried.
274 *
275 * It will be left in this state if database doesn't
276 * contain such key. That might be a drawback, maybe
277 * setting to empty value has sense.
278 */
279
280 /**/
281 static char *
gdbmgetfn(Param pm)282 gdbmgetfn(Param pm)
283 {
284 datum key, content;
285 int ret, umlen;
286 char *umkey;
287 GDBM_FILE dbf;
288
289 /* Key already retrieved? There is no sense of asking the
290 * database again, because:
291 * - there can be only multiple readers
292 * - so, no writer + reader use is allowed
293 *
294 * Thus:
295 * - if we are writers, we for sure have newest copy of data
296 * - if we are readers, we for sure have newest copy of data
297 */
298 if ( pm->node.flags & PM_UPTODATE ) {
299 return pm->u.str ? pm->u.str : "";
300 }
301
302 /* Unmetafy key. GDBM fits nice into this
303 * process, as it uses length of data */
304 umlen = 0;
305 umkey = unmetafy_zalloc(pm->node.nam,¨en);
306
307 key.dptr = umkey;
308 key.dsize = umlen;
309
310 dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
311
312 if((ret = gdbm_exists(dbf, key))) {
313 /* We have data – store it, return it */
314 pm->node.flags |= PM_UPTODATE;
315
316 content = gdbm_fetch(dbf, key);
317
318 /* Ensure there's no leak */
319 if (pm->u.str) {
320 zsfree(pm->u.str);
321 pm->u.str = NULL;
322 }
323
324 /* Metafy returned data. All fits - metafy
325 * can obtain data length to avoid using \0 */
326 pm->u.str = metafy(content.dptr, content.dsize, META_DUP);
327 /* gdbm allocates with malloc */
328 free(content.dptr);
329
330 /* Free key */
331 zfree(umkey, umlen+1);
332
333 /* Can return pointer, correctly saved inside hash */
334 return pm->u.str;
335 }
336
337 /* Free key */
338 zfree(umkey, umlen+1);
339
340 return "";
341 }
342
343 /**/
344 static void
gdbmsetfn(Param pm,char * val)345 gdbmsetfn(Param pm, char *val)
346 {
347 datum key, content;
348 GDBM_FILE dbf;
349
350 /* Set is done on parameter and on database.
351 * See the allowed workers / readers comment
352 * at gdbmgetfn() */
353
354 /* Parameter */
355 if (pm->u.str) {
356 zsfree(pm->u.str);
357 pm->u.str = NULL;
358 pm->node.flags &= ~(PM_UPTODATE);
359 }
360
361 if (val) {
362 pm->u.str = val;
363 pm->node.flags |= PM_UPTODATE;
364 }
365
366 /* Database */
367 dbf = ((struct gsu_scalar_ext *)pm->gsu.s)->dbf;
368 if (dbf && no_database_action == 0) {
369 int umlen = 0;
370 char *umkey = unmetafy_zalloc(pm->node.nam,¨en);
371
372 key.dptr = umkey;
373 key.dsize = umlen;
374
375 if (val) {
376 /* Unmetafy with exact zalloc size */
377 char *umval = unmetafy_zalloc(val,¨en);
378
379 /* Store */
380 content.dptr = umval;
381 content.dsize = umlen;
382 (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
383
384 /* Free */
385 zfree(umval, umlen+1);
386 } else {
387 (void)gdbm_delete(dbf, key);
388 }
389
390 /* Free key */
391 zfree(umkey, key.dsize+1);
392 }
393 }
394
395 /**/
396 static void
gdbmunsetfn(Param pm,UNUSED (int um))397 gdbmunsetfn(Param pm, UNUSED(int um))
398 {
399 /* Set with NULL */
400 gdbmsetfn(pm, NULL);
401 }
402
403 /**/
404 static HashNode
getgdbmnode(HashTable ht,const char * name)405 getgdbmnode(HashTable ht, const char *name)
406 {
407 HashNode hn = gethashnode2( ht, name );
408 Param val_pm = (Param) hn;
409
410 /* Entry for key doesn't exist? Create it now,
411 * it will be interfacing between the database
412 * and Zsh - through special gdbm_gsu. So, any
413 * seen key results in new interfacing parameter.
414 *
415 * Previous code was returning heap arena Param
416 * that wasn't actually added to the hash. It was
417 * plainly name / database-key holder. Here we
418 * add the Param to its hash, it is not PM_UPTODATE.
419 * It will be loaded from database *and filled*
420 * or left in that state if the database doesn't
421 * contain it.
422 *
423 * No heap arena memory is used, memory usage is
424 * now limited - by number of distinct keys seen,
425 * not by number of key *uses*.
426 * */
427
428 if ( ! val_pm ) {
429 val_pm = (Param) zshcalloc( sizeof (*val_pm) );
430 val_pm->node.flags = PM_SCALAR | PM_HASHELEM; /* no PM_UPTODATE */
431 val_pm->gsu.s = (GsuScalar) ht->tmpdata;
432 ht->addnode( ht, ztrdup( name ), val_pm ); /* sets pm->node.nam */
433 }
434
435 return (HashNode) val_pm;
436 }
437
438 /**/
439 static void
scangdbmkeys(HashTable ht,ScanFunc func,int flags)440 scangdbmkeys(HashTable ht, ScanFunc func, int flags)
441 {
442 datum key, prev_key;
443 GDBM_FILE dbf = ((struct gsu_scalar_ext *)ht->tmpdata)->dbf;
444
445 /* Iterate keys adding them to hash, so
446 * we have Param to use in `func` */
447 key = gdbm_firstkey(dbf);
448
449 while(key.dptr) {
450 /* This returns database-interfacing Param,
451 * it will return u.str or first fetch data
452 * if not PM_UPTODATE (newly created) */
453 char *zkey = metafy(key.dptr, key.dsize, META_DUP);
454 HashNode hn = getgdbmnode(ht, zkey);
455 zsfree( zkey );
456
457 func(hn, flags);
458
459 /* Iterate - no problem as interfacing Param
460 * will do at most only fetches, not stores */
461 prev_key = key;
462 key = gdbm_nextkey(dbf, key);
463 free(prev_key.dptr);
464 }
465
466 }
467
468 /*
469 * Replace database with new hash
470 */
471
472 /**/
473 static void
gdbmhashsetfn(Param pm,HashTable ht)474 gdbmhashsetfn(Param pm, HashTable ht)
475 {
476 int i;
477 HashNode hn;
478 GDBM_FILE dbf;
479 datum key, content;
480
481 if (!pm->u.hash || pm->u.hash == ht)
482 return;
483
484 if (!(dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf))
485 return;
486
487 key = gdbm_firstkey(dbf);
488 while (key.dptr) {
489 queue_signals();
490 (void)gdbm_delete(dbf, key);
491 free(key.dptr);
492 unqueue_signals();
493 key = gdbm_firstkey(dbf);
494 }
495
496 /* Just deleted everything, clean up if no new data.
497 * User can also reorganize via gdbmtool. */
498 if (!ht || ht->hsize == 0) {
499 (void)gdbm_reorganize(dbf);
500 }
501
502 no_database_action = 1;
503 emptyhashtable(pm->u.hash);
504 no_database_action = 0;
505
506 if (!ht)
507 return;
508
509 /* Put new strings into database, waiting
510 * for their interfacing-Params to be created */
511
512 for (i = 0; i < ht->hsize; i++) {
513 for (hn = ht->nodes[i]; hn; hn = hn->next) {
514 struct value v;
515 int umlen = 0;
516 char *umkey, *umval;
517
518 v.isarr = v.flags = v.start = 0;
519 v.end = -1;
520 v.arr = NULL;
521 v.pm = (Param) hn;
522
523 /* Unmetafy key */
524 umkey = unmetafy_zalloc(v.pm->node.nam,¨en);
525
526 key.dptr = umkey;
527 key.dsize = umlen;
528
529 queue_signals();
530
531 /* Unmetafy */
532 umval = unmetafy_zalloc(getstrvalue(&v),¨en);
533
534 /* Store */
535 content.dptr = umval;
536 content.dsize = umlen;
537 (void)gdbm_store(dbf, key, content, GDBM_REPLACE);
538
539 /* Free - unmetafy_zalloc allocates
540 * exact required space + 1 null byte */
541 zfree(umval, content.dsize+1);
542 zfree(umkey, key.dsize+1);
543
544 unqueue_signals();
545 }
546 }
547 /* We reuse our hash, the input is to be deleted */
548 deleteparamtable(ht);
549 }
550
551 /**/
552 static void
gdbmuntie(Param pm)553 gdbmuntie(Param pm)
554 {
555 GDBM_FILE dbf = ((struct gsu_scalar_ext *)pm->u.hash->tmpdata)->dbf;
556 HashTable ht = pm->u.hash;
557
558 if (dbf) { /* paranoia */
559 fdtable[gdbm_fdesc(dbf)] = FDT_UNUSED;
560 gdbm_close(dbf);
561
562 /* Let hash fields know there's no backend */
563 ((struct gsu_scalar_ext *)ht->tmpdata)->dbf = NULL;
564
565 /* Remove from list of tied parameters */
566 remove_tied_name(pm->node.nam);
567 }
568
569 /* for completeness ... createspecialhash() should have an inverse */
570 ht->getnode = ht->getnode2 = gethashnode2;
571 ht->scantab = NULL;
572
573 pm->node.flags &= ~(PM_SPECIAL|PM_READONLY);
574 pm->gsu.h = &stdhash_gsu;
575 }
576
577 /**/
578 static void
gdbmhashunsetfn(Param pm,UNUSED (int exp))579 gdbmhashunsetfn(Param pm, UNUSED(int exp))
580 {
581 struct gsu_scalar_ext * gsu_ext;
582
583 gdbmuntie(pm);
584
585 /* Remember custom GSU structure assigned to
586 * u.hash->tmpdata before hash gets deleted */
587 gsu_ext = pm->u.hash->tmpdata;
588
589 /* Uses normal unsetter (because gdbmuntie is called above).
590 * Will delete all owned field-parameters and also hashtable. */
591 pm->gsu.h->setfn(pm, NULL);
592
593 /* Don't need custom GSU structure with its
594 * GDBM_FILE pointer anymore */
595 zsfree( gsu_ext->dbfile_path );
596 zfree( gsu_ext, sizeof(struct gsu_scalar_ext));
597
598 pm->node.flags |= PM_UNSET;
599 }
600
601 static struct features module_features = {
602 bintab, sizeof(bintab)/sizeof(*bintab),
603 NULL, 0,
604 NULL, 0,
605 patab, sizeof(patab)/sizeof(*patab),
606 0
607 };
608
609 /**/
610 int
setup_(UNUSED (Module m))611 setup_(UNUSED(Module m))
612 {
613 return 0;
614 }
615
616 /**/
617 int
features_(Module m,char *** features)618 features_(Module m, char ***features)
619 {
620 *features = featuresarray(m, &module_features);
621 return 0;
622 }
623
624 /**/
625 int
enables_(Module m,int ** enables)626 enables_(Module m, int **enables)
627 {
628 return handlefeatures(m, &module_features, enables);
629 }
630
631 /**/
632 int
boot_(UNUSED (Module m))633 boot_(UNUSED(Module m))
634 {
635 zgdbm_tied = zshcalloc((1) * sizeof(char *));
636 return 0;
637 }
638
639 /**/
640 int
cleanup_(Module m)641 cleanup_(Module m)
642 {
643 /* This frees `zgdbm_tied` */
644 return setfeatureenables(m, &module_features, NULL);
645 }
646
647 /**/
648 int
finish_(UNUSED (Module m))649 finish_(UNUSED(Module m))
650 {
651 return 0;
652 }
653
654 /*********************
655 * Utility functions *
656 *********************/
657
createhash(char * name,int flags)658 static Param createhash( char *name, int flags ) {
659 Param pm;
660 HashTable ht;
661
662 pm = createparam(name, flags | PM_SPECIAL | PM_HASHED);
663 if (!pm) {
664 return NULL;
665 }
666
667 if (pm->old)
668 pm->level = locallevel;
669
670 /* This creates standard hash. */
671 ht = pm->u.hash = newparamtable(17, name);
672 if (!pm->u.hash) {
673 paramtab->removenode(paramtab, name);
674 paramtab->freenode(&pm->node);
675 zwarnnam(name, "out of memory when allocating hash");
676 return NULL;
677 }
678
679 /* Does free Param (unsetfn is called) */
680 ht->freenode = myfreeparamnode;
681
682 /* These provide special features */
683 ht->getnode = ht->getnode2 = getgdbmnode;
684 ht->scantab = scangdbmkeys;
685
686 return pm;
687 }
688
689 /*
690 * Adds parameter name to `zgdbm_tied`
691 */
692
append_tied_name(const char * name)693 static int append_tied_name( const char *name ) {
694 int old_len = arrlen(zgdbm_tied);
695 char **new_zgdbm_tied = zshcalloc( (old_len+2) * sizeof(char *));
696
697 /* Copy */
698 char **p = zgdbm_tied;
699 char **dst = new_zgdbm_tied;
700 while (*p) {
701 *dst++ = *p++;
702 }
703
704 /* Append new one */
705 *dst = ztrdup(name);
706
707 /* Substitute, free old one */
708 zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
709 zgdbm_tied = new_zgdbm_tied;
710
711 return 0;
712 }
713
714 /*
715 * Removes parameter name from `zgdbm_tied`
716 */
717
remove_tied_name(const char * name)718 static int remove_tied_name( const char *name ) {
719 int old_len = arrlen(zgdbm_tied);
720 int new_len;
721
722 /* Two stage, to always have arrlen() == zfree-size - 1.
723 * Could do allocation and revert when `not found`, but
724 * what would be better about that. */
725
726 /* Find one to remove */
727 char **p = zgdbm_tied;
728 while (*p) {
729 if (0==strcmp(name,*p)) {
730 break;
731 }
732 p++;
733 }
734
735 if (*p)
736 zsfree(*p);
737
738 /* Copy x+1 to x */
739 while (*p) {
740 *p=*(p+1);
741 p++;
742 }
743
744 /* Second stage. Size changed? Only old_size-1
745 * change is possible, but.. paranoia way */
746 new_len = arrlen(zgdbm_tied);
747 if (new_len != old_len) {
748 char **dst;
749 char **new_zgdbm_tied = zshcalloc((new_len+1) * sizeof(char *));
750
751 /* Copy */
752 p = zgdbm_tied;
753 dst = new_zgdbm_tied;
754 while (*p) {
755 *dst++ = *p++;
756 }
757 *dst = NULL;
758
759 /* Substitute, free old one */
760 zfree(zgdbm_tied, sizeof(char *) * (old_len + 1));
761 zgdbm_tied = new_zgdbm_tied;
762 }
763
764 return 0;
765 }
766
767 /*
768 * Unmetafy that:
769 * - duplicates buffer to work on it,
770 * - does zalloc of exact size for the new string,
771 * - restores work buffer to original content, to restore strlen
772 */
773 static char *
unmetafy_zalloc(const char * to_copy,int * new_len)774 unmetafy_zalloc(const char *to_copy, int *new_len) {
775 char *work, *to_return;
776 int my_new_len = 0;
777
778 work = ztrdup(to_copy);
779 work = unmetafy(work,&my_new_len);
780
781 if (new_len)
782 *new_len = my_new_len;
783
784 /* This string can be correctly zsfree()-d */
785 to_return = (char *) zalloc((my_new_len+1)*sizeof(char));
786 memcpy(to_return, work, sizeof(char)*my_new_len); /* memcpy handles $'\0' */
787 to_return[my_new_len]='\0';
788
789 /* Restore original strlen and correctly free */
790 strcpy(work, to_copy);
791 zsfree(work);
792
793 return to_return;
794 }
795
796 static void
myfreeparamnode(HashNode hn)797 myfreeparamnode(HashNode hn)
798 {
799 Param pm = (Param) hn;
800
801 /* Upstream: The second argument of unsetfn() is used by modules to
802 * differentiate "exp"licit unset from implicit unset, as when
803 * a parameter is going out of scope. It's not clear which
804 * of these applies here, but passing 1 has always worked.
805 */
806
807 /* if (delunset) */
808 pm->gsu.s->unsetfn(pm, 1);
809
810 zsfree(pm->node.nam);
811 /* If this variable was tied by the user, ename was ztrdup'd */
812 if (!(pm->node.flags & PM_SPECIAL) && pm->ename) {
813 zsfree(pm->ename);
814 pm->ename = NULL;
815 }
816 zfree(pm, sizeof(struct param));
817 }
818
819 #else
820 # error no gdbm
821 #endif /* have gdbm */
822