1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include <errno.h>
16#include <malloc.h>
17
18#ifdef USE_HOSTCC
19# include <string.h>
20# include <assert.h>
21# include <ctype.h>
22
23# ifndef debug
24# ifdef DEBUG
25# define debug(fmt,args...) printf(fmt ,##args)
26# else
27# define debug(fmt,args...)
28# endif
29# endif
30#else
31# include <common.h>
32# include <linux/string.h>
33# include <linux/ctype.h>
34#endif
35
36#ifndef CONFIG_ENV_MIN_ENTRIES
37#define CONFIG_ENV_MIN_ENTRIES 64
38#endif
39#ifndef CONFIG_ENV_MAX_ENTRIES
40#define CONFIG_ENV_MAX_ENTRIES 512
41#endif
42
43#include <env_callback.h>
44#include <env_flags.h>
45#include <search.h>
46#include <slre.h>
47
48
49
50
51
52
53
54
55
56
57
58
59typedef struct _ENTRY {
60 int used;
61 ENTRY entry;
62} _ENTRY;
63
64
65static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
66 int idx);
67
68
69
70
71
72
73
74
75
76
77
78
79static int isprime(unsigned int number)
80{
81
82 unsigned int div = 3;
83
84 while (div * div < number && number % div != 0)
85 div += 2;
86
87 return number % div != 0;
88}
89
90
91
92
93
94
95
96
97
98
99int hcreate_r(size_t nel, struct hsearch_data *htab)
100{
101
102 if (htab == NULL) {
103 __set_errno(EINVAL);
104 return 0;
105 }
106
107
108 if (htab->table != NULL)
109 return 0;
110
111
112 nel |= 1;
113 while (!isprime(nel))
114 nel += 2;
115
116 htab->size = nel;
117 htab->filled = 0;
118
119
120 htab->table = (_ENTRY *) calloc(htab->size + 1, sizeof(_ENTRY));
121 if (htab->table == NULL)
122 return 0;
123
124
125 return 1;
126}
127
128
129
130
131
132
133
134
135
136
137
138void hdestroy_r(struct hsearch_data *htab)
139{
140 int i;
141
142
143 if (htab == NULL) {
144 __set_errno(EINVAL);
145 return;
146 }
147
148
149 for (i = 1; i <= htab->size; ++i) {
150 if (htab->table[i].used > 0) {
151 ENTRY *ep = &htab->table[i].entry;
152
153 free((void *)ep->key);
154 free(ep->data);
155 }
156 }
157 free(htab->table);
158
159
160 htab->table = NULL;
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
200int hmatch_r(const char *match, int last_idx, ENTRY ** retval,
201 struct hsearch_data *htab)
202{
203 unsigned int idx;
204 size_t key_len = strlen(match);
205
206 for (idx = last_idx + 1; idx < htab->size; ++idx) {
207 if (htab->table[idx].used <= 0)
208 continue;
209 if (!strncmp(match, htab->table[idx].entry.key, key_len)) {
210 *retval = &htab->table[idx].entry;
211 return idx;
212 }
213 }
214
215 __set_errno(ESRCH);
216 *retval = NULL;
217 return 0;
218}
219
220
221
222
223
224static inline int _compare_and_overwrite_entry(ENTRY item, ACTION action,
225 ENTRY **retval, struct hsearch_data *htab, int flag,
226 unsigned int hval, unsigned int idx)
227{
228 if (htab->table[idx].used == hval
229 && strcmp(item.key, htab->table[idx].entry.key) == 0) {
230
231 if ((action == ENTER) && (item.data != NULL)) {
232
233 if (htab->change_ok != NULL && htab->change_ok(
234 &htab->table[idx].entry, item.data,
235 env_op_overwrite, flag)) {
236 debug("change_ok() rejected setting variable "
237 "%s, skipping it!\n", item.key);
238 __set_errno(EPERM);
239 *retval = NULL;
240 return 0;
241 }
242
243
244 if (htab->table[idx].entry.callback &&
245 htab->table[idx].entry.callback(item.key,
246 item.data, env_op_overwrite, flag)) {
247 debug("callback() rejected setting variable "
248 "%s, skipping it!\n", item.key);
249 __set_errno(EINVAL);
250 *retval = NULL;
251 return 0;
252 }
253
254 free(htab->table[idx].entry.data);
255 htab->table[idx].entry.data = strdup(item.data);
256 if (!htab->table[idx].entry.data) {
257 __set_errno(ENOMEM);
258 *retval = NULL;
259 return 0;
260 }
261 }
262
263 *retval = &htab->table[idx].entry;
264 return idx;
265 }
266
267 return -1;
268}
269
270int hsearch_r(ENTRY item, ACTION action, ENTRY ** retval,
271 struct hsearch_data *htab, int flag)
272{
273 unsigned int hval;
274 unsigned int count;
275 unsigned int len = strlen(item.key);
276 unsigned int idx;
277 unsigned int first_deleted = 0;
278 int ret;
279
280
281 hval = len;
282 count = len;
283 while (count-- > 0) {
284 hval <<= 4;
285 hval += item.key[count];
286 }
287
288
289
290
291
292 hval %= htab->size;
293 if (hval == 0)
294 ++hval;
295
296
297 idx = hval;
298
299 if (htab->table[idx].used) {
300
301
302
303
304 unsigned hval2;
305
306 if (htab->table[idx].used == -1
307 && !first_deleted)
308 first_deleted = idx;
309
310 ret = _compare_and_overwrite_entry(item, action, retval, htab,
311 flag, hval, idx);
312 if (ret != -1)
313 return ret;
314
315
316
317
318
319 hval2 = 1 + hval % (htab->size - 2);
320
321 do {
322
323
324
325
326 if (idx <= hval2)
327 idx = htab->size + idx - hval2;
328 else
329 idx -= hval2;
330
331
332
333
334
335 if (idx == hval)
336 break;
337
338
339 ret = _compare_and_overwrite_entry(item, action, retval,
340 htab, flag, hval, idx);
341 if (ret != -1)
342 return ret;
343 }
344 while (htab->table[idx].used);
345 }
346
347
348 if (action == ENTER) {
349
350
351
352
353 if (htab->filled == htab->size) {
354 __set_errno(ENOMEM);
355 *retval = NULL;
356 return 0;
357 }
358
359
360
361
362
363 if (first_deleted)
364 idx = first_deleted;
365
366 htab->table[idx].used = hval;
367 htab->table[idx].entry.key = strdup(item.key);
368 htab->table[idx].entry.data = strdup(item.data);
369 if (!htab->table[idx].entry.key ||
370 !htab->table[idx].entry.data) {
371 __set_errno(ENOMEM);
372 *retval = NULL;
373 return 0;
374 }
375
376 ++htab->filled;
377
378
379 env_callback_init(&htab->table[idx].entry);
380
381 env_flags_init(&htab->table[idx].entry);
382
383
384 if (htab->change_ok != NULL && htab->change_ok(
385 &htab->table[idx].entry, item.data, env_op_create, flag)) {
386 debug("change_ok() rejected setting variable "
387 "%s, skipping it!\n", item.key);
388 _hdelete(item.key, htab, &htab->table[idx].entry, idx);
389 __set_errno(EPERM);
390 *retval = NULL;
391 return 0;
392 }
393
394
395 if (htab->table[idx].entry.callback &&
396 htab->table[idx].entry.callback(item.key, item.data,
397 env_op_create, flag)) {
398 debug("callback() rejected setting variable "
399 "%s, skipping it!\n", item.key);
400 _hdelete(item.key, htab, &htab->table[idx].entry, idx);
401 __set_errno(EINVAL);
402 *retval = NULL;
403 return 0;
404 }
405
406
407 *retval = &htab->table[idx].entry;
408 return 1;
409 }
410
411 __set_errno(ESRCH);
412 *retval = NULL;
413 return 0;
414}
415
416
417
418
419
420
421
422
423
424
425
426
427static void _hdelete(const char *key, struct hsearch_data *htab, ENTRY *ep,
428 int idx)
429{
430
431 debug("hdelete: DELETING key \"%s\"\n", key);
432 free((void *)ep->key);
433 free(ep->data);
434 ep->callback = NULL;
435 ep->flags = 0;
436 htab->table[idx].used = -1;
437
438 --htab->filled;
439}
440
441int hdelete_r(const char *key, struct hsearch_data *htab, int flag)
442{
443 ENTRY e, *ep;
444 int idx;
445
446 debug("hdelete: DELETE key \"%s\"\n", key);
447
448 e.key = (char *)key;
449
450 idx = hsearch_r(e, FIND, &ep, htab, 0);
451 if (idx == 0) {
452 __set_errno(ESRCH);
453 return 0;
454 }
455
456
457 if (htab->change_ok != NULL &&
458 htab->change_ok(ep, NULL, env_op_delete, flag)) {
459 debug("change_ok() rejected deleting variable "
460 "%s, skipping it!\n", key);
461 __set_errno(EPERM);
462 return 0;
463 }
464
465
466 if (htab->table[idx].entry.callback &&
467 htab->table[idx].entry.callback(key, NULL, env_op_delete, flag)) {
468 debug("callback() rejected deleting variable "
469 "%s, skipping it!\n", key);
470 __set_errno(EINVAL);
471 return 0;
472 }
473
474 _hdelete(key, htab, ep, idx);
475
476 return 1;
477}
478
479#if !(defined(CONFIG_SPL_BUILD) && !defined(CONFIG_SPL_SAVEENV))
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522static int cmpkey(const void *p1, const void *p2)
523{
524 ENTRY *e1 = *(ENTRY **) p1;
525 ENTRY *e2 = *(ENTRY **) p2;
526
527 return (strcmp(e1->key, e2->key));
528}
529
530static int match_string(int flag, const char *str, const char *pat, void *priv)
531{
532 switch (flag & H_MATCH_METHOD) {
533 case H_MATCH_IDENT:
534 if (strcmp(str, pat) == 0)
535 return 1;
536 break;
537 case H_MATCH_SUBSTR:
538 if (strstr(str, pat))
539 return 1;
540 break;
541#ifdef CONFIG_REGEX
542 case H_MATCH_REGEX:
543 {
544 struct slre *slrep = (struct slre *)priv;
545 struct cap caps[slrep->num_caps + 2];
546
547 if (slre_match(slrep, str, strlen(str), caps))
548 return 1;
549 }
550 break;
551#endif
552 default:
553 printf("## ERROR: unsupported match method: 0x%02x\n",
554 flag & H_MATCH_METHOD);
555 break;
556 }
557 return 0;
558}
559
560static int match_entry(ENTRY *ep, int flag,
561 int argc, char * const argv[])
562{
563 int arg;
564 void *priv = NULL;
565
566 for (arg = 0; arg < argc; ++arg) {
567#ifdef CONFIG_REGEX
568 struct slre slre;
569
570 if (slre_compile(&slre, argv[arg]) == 0) {
571 printf("Error compiling regex: %s\n", slre.err_str);
572 return 0;
573 }
574
575 priv = (void *)&slre;
576#endif
577 if (flag & H_MATCH_KEY) {
578 if (match_string(flag, ep->key, argv[arg], priv))
579 return 1;
580 }
581 if (flag & H_MATCH_DATA) {
582 if (match_string(flag, ep->data, argv[arg], priv))
583 return 1;
584 }
585 }
586 return 0;
587}
588
589ssize_t hexport_r(struct hsearch_data *htab, const char sep, int flag,
590 char **resp, size_t size,
591 int argc, char * const argv[])
592{
593 ENTRY *list[htab->size];
594 char *res, *p;
595 size_t totlen;
596 int i, n;
597
598
599 if ((resp == NULL) || (htab == NULL)) {
600 __set_errno(EINVAL);
601 return (-1);
602 }
603
604 debug("EXPORT table = %p, htab.size = %d, htab.filled = %d, size = %lu\n",
605 htab, htab->size, htab->filled, (ulong)size);
606
607
608
609
610
611 for (i = 1, n = 0, totlen = 0; i <= htab->size; ++i) {
612
613 if (htab->table[i].used > 0) {
614 ENTRY *ep = &htab->table[i].entry;
615 int found = match_entry(ep, flag, argc, argv);
616
617 if ((argc > 0) && (found == 0))
618 continue;
619
620 if ((flag & H_HIDE_DOT) && ep->key[0] == '.')
621 continue;
622
623 list[n++] = ep;
624
625 totlen += strlen(ep->key);
626
627 if (sep == '\0') {
628 totlen += strlen(ep->data);
629 } else {
630 char *s = ep->data;
631
632 while (*s) {
633 ++totlen;
634
635 if ((*s == sep) || (*s == '\\'))
636 ++totlen;
637 ++s;
638 }
639 }
640 totlen += 2;
641 }
642 }
643
644#ifdef DEBUG
645
646 printf("Unsorted: n=%d\n", n);
647 for (i = 0; i < n; ++i) {
648 printf("\t%3d: %p ==> %-10s => %s\n",
649 i, list[i], list[i]->key, list[i]->data);
650 }
651#endif
652
653
654 qsort(list, n, sizeof(ENTRY *), cmpkey);
655
656
657 if (size) {
658 if (size < totlen + 1) {
659 printf("Env export buffer too small: %lu, but need %lu\n",
660 (ulong)size, (ulong)totlen + 1);
661 __set_errno(ENOMEM);
662 return (-1);
663 }
664 } else {
665 size = totlen;
666 }
667
668
669 if (*resp) {
670
671 res = *resp;
672 memset(res, '\0', size);
673 } else {
674
675 *resp = res = calloc(1, size);
676 if (res == NULL) {
677 __set_errno(ENOMEM);
678 return (-1);
679 }
680 }
681
682
683
684
685 for (i = 0, p = res; i < n; ++i) {
686 const char *s;
687
688 s = list[i]->key;
689 while (*s)
690 *p++ = *s++;
691 *p++ = '=';
692
693 s = list[i]->data;
694
695 while (*s) {
696 if ((*s == sep) || (*s == '\\'))
697 *p++ = '\\';
698 *p++ = *s++;
699 }
700 *p++ = sep;
701 }
702 *p = '\0';
703
704 return size;
705}
706#endif
707
708
709
710
711
712
713
714
715
716
717static int drop_var_from_set(const char *name, int nvars, char * vars[])
718{
719 int i = 0;
720 int res = 0;
721
722
723 if (nvars == 0)
724 return 1;
725
726 for (i = 0; i < nvars; i++) {
727 if (vars[i] == NULL)
728 continue;
729
730 if (!strcmp(name, vars[i])) {
731 vars[i] = NULL;
732 res = 1;
733 }
734 }
735 if (!res)
736 debug("Skipping non-listed variable %s\n", name);
737
738 return res;
739}
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779int himport_r(struct hsearch_data *htab,
780 const char *env, size_t size, const char sep, int flag,
781 int crlf_is_lf, int nvars, char * const vars[])
782{
783 char *data, *sp, *dp, *name, *value;
784 char *localvars[nvars];
785 int i;
786
787
788 if (htab == NULL) {
789 __set_errno(EINVAL);
790 return 0;
791 }
792
793
794 if ((data = malloc(size + 1)) == NULL) {
795 debug("himport_r: can't malloc %lu bytes\n", (ulong)size + 1);
796 __set_errno(ENOMEM);
797 return 0;
798 }
799 memcpy(data, env, size);
800 data[size] = '\0';
801 dp = data;
802
803
804 if (nvars)
805 memcpy(localvars, vars, sizeof(vars[0]) * nvars);
806
807 if ((flag & H_NOCLEAR) == 0 && !nvars) {
808
809 debug("Destroy Hash Table: %p table = %p\n", htab,
810 htab->table);
811 if (htab->table)
812 hdestroy_r(htab);
813 }
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833 if (!htab->table) {
834 int nent = CONFIG_ENV_MIN_ENTRIES + size / 8;
835
836 if (nent > CONFIG_ENV_MAX_ENTRIES)
837 nent = CONFIG_ENV_MAX_ENTRIES;
838
839 debug("Create Hash Table: N=%d\n", nent);
840
841 if (hcreate_r(nent, htab) == 0) {
842 free(data);
843 return 0;
844 }
845 }
846
847 if (!size) {
848 free(data);
849 return 1;
850 }
851 if(crlf_is_lf) {
852
853 unsigned ignored_crs = 0;
854 for(;dp < data + size && *dp; ++dp) {
855 if(*dp == '\r' &&
856 dp < data + size - 1 && *(dp+1) == '\n')
857 ++ignored_crs;
858 else
859 *(dp-ignored_crs) = *dp;
860 }
861 size -= ignored_crs;
862 dp = data;
863 }
864
865 do {
866 ENTRY e, *rv;
867
868
869 while (isblank(*dp))
870 ++dp;
871
872
873 if (*dp == '#') {
874 while (*dp && (*dp != sep))
875 ++dp;
876 ++dp;
877 continue;
878 }
879
880
881 for (name = dp; *dp != '=' && *dp && *dp != sep; ++dp)
882 ;
883
884
885 if (*dp == '\0' || *(dp + 1) == '\0' ||
886 *dp == sep || *(dp + 1) == sep) {
887 if (*dp == '=')
888 *dp++ = '\0';
889 *dp++ = '\0';
890
891 debug("DELETE CANDIDATE: \"%s\"\n", name);
892 if (!drop_var_from_set(name, nvars, localvars))
893 continue;
894
895 if (hdelete_r(name, htab, flag) == 0)
896 debug("DELETE ERROR ##############################\n");
897
898 continue;
899 }
900 *dp++ = '\0';
901
902
903 for (value = sp = dp; *dp && (*dp != sep); ++dp) {
904 if ((*dp == '\\') && *(dp + 1))
905 ++dp;
906 *sp++ = *dp;
907 }
908 *sp++ = '\0';
909 ++dp;
910
911 if (*name == 0) {
912 debug("INSERT: unable to use an empty key\n");
913 __set_errno(EINVAL);
914 free(data);
915 return 0;
916 }
917
918
919 if (!drop_var_from_set(name, nvars, localvars))
920 continue;
921
922
923 e.key = name;
924 e.data = value;
925
926 hsearch_r(e, ENTER, &rv, htab, flag);
927 if (rv == NULL)
928 printf("himport_r: can't insert \"%s=%s\" into hash table\n",
929 name, value);
930
931 debug("INSERT: table %p, filled %d/%d rv %p ==> name=\"%s\" value=\"%s\"\n",
932 htab, htab->filled, htab->size,
933 rv, name, value);
934 } while ((dp < data + size) && *dp);
935
936 debug("INSERT: free(data = %p)\n", data);
937 free(data);
938
939 if (flag & H_NOCLEAR)
940 goto end;
941
942
943 for (i = 0; i < nvars; i++) {
944 if (localvars[i] == NULL)
945 continue;
946
947
948
949
950
951
952
953
954 if (hdelete_r(localvars[i], htab, flag) == 0)
955 printf("WARNING: '%s' neither in running nor in imported env!\n", localvars[i]);
956 else
957 printf("WARNING: '%s' not in imported env, deleting it!\n", localvars[i]);
958 }
959
960end:
961 debug("INSERT: done\n");
962 return 1;
963}
964
965
966
967
968
969
970
971
972
973int hwalk_r(struct hsearch_data *htab, int (*callback)(ENTRY *))
974{
975 int i;
976 int retval;
977
978 for (i = 1; i <= htab->size; ++i) {
979 if (htab->table[i].used > 0) {
980 retval = callback(&htab->table[i].entry);
981 if (retval)
982 return retval;
983 }
984 }
985
986 return 0;
987}
988