]> cloudbase.mooo.com Git - z180-stamp.git/blob - avr/env.c
f743f417424c3961746f1da207a282ba1f71f964
[z180-stamp.git] / avr / env.c
1 #include "common.h"
2 #include <string.h>
3 #include <stdlib.h>
4
5 #include <avr/eeprom.h>
6
7 #include "config.h"
8 #include "debug.h"
9 #include "xmalloc.h"
10 #include "crc.h"
11 #include "command.h"
12
13 #include "env.h"
14
15
16 #define ENV_SIZE (CONFIG_ENV_SIZE - sizeof(uint16_t) -1)
17 #define ACTIVE_FLAG 1
18 #define OBSOLETE_FLAG 0
19 #define ENV_GET_VAL (1<<0)
20
21 #define ENVLIST_DELETE (1<<0)
22
23
24 /*
25 * Default Environment
26 */
27
28 #define DELIM "\0"
29
30 const FLASH char default_env[] = {
31 "bootdelay=" "3" DELIM
32 "bootcmd=" "reset; loadf; go $(startaddr)" DELIM
33 "baudrate=" "115200" DELIM
34 "startaddr=" "0" DELIM
35 DELIM
36 };
37
38 /* EEPROM storage */
39 typedef struct environment_s {
40 uint16_t crc; /* CRC16 over data bytes */
41 uint8_t flags; /* active/obsolete flags */
42 char data[ENV_SIZE]; /* Environment data */
43 } env_t;
44
45
46 /* */
47 typedef struct env_item_s {
48 #define EF_N_EEP (1<<7) /* Variable name is in EEPROM */
49 #define EF_V_EEP (1<<6) /* Variable value is in EEPROM */
50 #define EF_DIRTY (1<<0) /* Variable is new or value changed */
51 uint8_t flags;
52 union {
53 uint16_t eep;
54 char *ram;
55 } name;
56 union {
57 uint16_t eep;
58 char *ram;
59 } val;
60 } env_item_t;
61
62
63 static uint8_t env_valid;
64 static env_item_t env_list[CONFIG_ENVVAR_MAX];
65 static int entrycount;
66
67
68 static
69 char env_get_char(uint16_t index)
70 {
71 unsigned int off = CONFIG_ENV_OFFSET;
72
73 if (env_valid == 2)
74 off = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
75
76 return (char) eeprom_read_byte((const uint8_t *)off + index +
77 offsetof(env_t, data));
78 }
79
80
81 static
82 uint16_t varname_get(char *buf, env_item_t *ep)
83 {
84 int i = 0;
85 char c;
86
87 if (ep->flags & EF_N_EEP) {
88
89 while ((c = env_get_char(ep->name.eep + i)) != '=' &&
90 c != '\0' && i < CONFIG_SYS_ENV_NAMELEN) {
91
92 buf[i] = c;
93 i++;
94 }
95 if (i > 0 && c != '=') {
96 debug("** ee_name: '%s' not '=' terminated!\n", buf);
97 }
98 } else {
99 strncpy(buf, ep->name.ram, CONFIG_SYS_ENV_NAMELEN);
100 i = strnlen(buf, CONFIG_SYS_ENV_NAMELEN);
101 }
102 buf[i] = '\0';
103 return i;
104 }
105
106
107 static
108 uint16_t varval_getlen(uint16_t index)
109 {
110 int i = 0;
111 char c;
112
113 while ((c = env_get_char (index + i)) != '\0') {
114 i++;
115 };
116
117 return i;
118 }
119
120 static
121 uint16_t varval_get(char *buf, uint16_t index, int len)
122 {
123 int i = 0;
124 char c;
125
126 while ((c = env_get_char (index + i)) != '\0' && i < len) {
127 buf[i] = c;
128 i++;
129 };
130
131 buf[i] = '\0';
132
133 /* TODO: len check */
134
135 return i;
136 }
137
138 static
139 int comp_env_key_item(const void *key, const void *item)
140 {
141 char buf[CONFIG_SYS_ENV_NAMELEN+1];
142 env_item_t *ep = (env_item_t *) item;
143
144 varname_get(buf, ep);
145
146 return strcmp((char *) key, buf);
147 }
148
149 static
150 int comp_env_items(const void *m1, const void *m2)
151 {
152 char b1[CONFIG_SYS_ENV_NAMELEN+1];
153 char b2[CONFIG_SYS_ENV_NAMELEN+1];
154
155 env_item_t *ep1 = (env_item_t *) m1;
156 env_item_t *ep2 = (env_item_t *) m2;
157
158 varname_get(b1, ep1);
159 varname_get(b2, ep2);
160
161 return strcmp(b1, b2);
162 }
163
164
165 env_item_t *envlist_search(const MEMX char *name)
166 {
167 char *p = (char *) name;
168
169 #ifdef __MEMX
170 char buf[CONFIG_SYS_ENV_NAMELEN+1];
171
172 if (__builtin_avr_flash_segment(name) != -1) {
173 p = buf;
174 while ((*p++ = *name++) != '\0')
175 ;
176 p = buf;
177 }
178 #endif /* __MEMX */
179
180 return bsearch(p, env_list, entrycount,
181 sizeof(env_item_t), comp_env_key_item);
182 }
183
184
185 static
186 env_item_t *envlist_enter(env_item_t *e)
187 {
188 char *key = e->name.ram;
189 const size_t size = sizeof(env_item_t);
190 env_item_t *ep;
191
192 ep = bsearch(key, env_list, entrycount,
193 size, comp_env_key_item);
194
195 if (ep == NULL) {
196 if (entrycount < CONFIG_ENVVAR_MAX) {
197
198 env_list[entrycount++] = *e;
199 qsort(env_list, entrycount, size, comp_env_items);
200 ep = bsearch(key, env_list, entrycount,
201 size, comp_env_key_item);
202 }
203 } else {
204 if ((ep->flags & EF_V_EEP) == 0) {
205 free(ep->val.ram);
206 }
207 ep->val.ram = e->val.ram;
208 }
209
210 if (ep != NULL) {
211 ep->flags |= EF_DIRTY;
212 ep->flags &= ~EF_V_EEP;
213
214 if ((ep->flags & EF_N_EEP) == 0) {
215 int nlen = strnlen(key, CONFIG_SYS_ENV_NAMELEN);
216 char *name = xmalloc(nlen + 1);
217 if (name == NULL) {
218 printf_P(PSTR("## Can't malloc %d bytes\n"),
219 nlen + 1);
220 return NULL;
221 }
222 strcpy(name, key);
223 name[nlen] = '\0';
224 ep->name.ram = name;
225 }
226 }
227
228 return ep;
229 }
230
231
232 static
233 int env_item_delete(env_item_t *ep)
234 {
235 if (entrycount == 0)
236 return -1;
237 if ((ep->flags & EF_V_EEP) == 0)
238 free(ep->val.ram);
239 if ((ep->flags & EF_N_EEP) == 0)
240 free(ep->name.ram);
241
242 entrycount--;
243 size_t size = sizeof(env_item_t);
244 memmove(ep, ep + 1, (env_list - ep)*size + entrycount*size);
245
246 return 0;
247 }
248
249 static
250 int envlist_delete(const char *name)
251 {
252 env_item_t *ep = bsearch(name, env_list, entrycount,
253 sizeof(env_item_t), comp_env_key_item);
254
255 if (ep != NULL)
256 return env_item_delete(ep);
257
258 return 1;
259 }
260
261
262 static
263 env_item_t *envlist_get(const MEMX char *name, uint_fast8_t flag)
264 {
265 env_item_t *ep;
266
267 ep = envlist_search(name);
268
269 if (ep != NULL && (flag & ENV_GET_VAL)) {
270 if (ep->flags & EF_V_EEP) {
271 char *vp;
272 int len;
273 len = varval_getlen(ep->val.eep);
274 if (len > CONFIG_SYS_CBSIZE)
275 len = CONFIG_SYS_CBSIZE;
276 vp = xmalloc(len + 1);
277 if (vp) {
278 varval_get(vp, ep->val.eep, len + 1);
279 ep->val.ram = vp;
280 ep->flags &= ~EF_V_EEP;
281 } else
282 printf_P(PSTR("Out of memory!\n"));
283 }
284 }
285
286 return ep;
287 }
288
289
290 static
291 int envlist_import(uint8_t flags)
292 {
293 char name[CONFIG_SYS_ENV_NAMELEN+1];
294 uint16_t idx = 0;
295 int nlen;
296 env_item_t e;
297
298 if ((flags & ENVLIST_DELETE) != 0)
299 while (entrycount != 0)
300 env_item_delete(&env_list[entrycount-1]);
301
302 e.flags = EF_N_EEP | EF_V_EEP;
303 e.name.eep = idx;
304 while (idx < ENV_SIZE && (nlen = varname_get(name, &e)) != 0) {
305
306 if (entrycount <= CONFIG_ENVVAR_MAX) {
307 e.val.eep = idx + nlen + 1;
308
309 env_list[entrycount++] = e;
310
311 idx += nlen + 1;
312 while (idx < ENV_SIZE && env_get_char(idx++) != 0)
313 ;
314 e.name.eep = idx;
315 } else {
316 debug("** Too many environment variables!\n");
317 break;
318 }
319 }
320 qsort(env_list, entrycount, sizeof(env_item_t), comp_env_items);
321
322 return 0;
323 }
324
325
326 static
327 int set_default_env(const FLASH char* msg)
328 {
329 char buf[56];
330 uint16_t eep = CONFIG_ENV_OFFSET + offsetof(env_t, data);
331 unsigned int len = ENV_SIZE;
332 unsigned int i, src = 0;
333 char c = 0xff, c0 = c;
334
335 if (msg)
336 printf_P(msg);
337
338 if (env_valid == 1) {
339 eep = CONFIG_ENV_OFFSET + offsetof(env_t, data) + CONFIG_ENV_SIZE;
340 }
341
342 while (len) {
343 memcpy_P(buf, default_env+src, sizeof(buf));
344 for (i=0; i < (len < sizeof(buf) ? len : sizeof(buf)) &&
345 !(c == 0 && c0 == 0);
346 i++) {
347 c0 = c; c = buf[i];
348 }
349 eeprom_update_block(buf, (char *) eep, i);
350 if (c == 0 && c0 == 0)
351 len = 0;
352 if (len > sizeof(buf))
353 len -= sizeof(buf);
354 src += sizeof(buf);
355 eep += sizeof(buf);
356 }
357
358 return 0;
359 }
360
361
362 static
363 uint16_t env_crc(uint16_t data_offset)
364 {
365 uint16_t crc;
366 uint16_t i;
367 char c, c0;
368
369 crc = 0xffff;
370 c = 0xff;
371 for (i = 0; !(c == 0 && c0 == 0) && i < ENV_SIZE; i++)
372 {
373 c0 = c;
374 c = eeprom_read_byte((const uint8_t *) data_offset + i);
375 crc = crc16(crc, c);
376 }
377 return crc ;
378 }
379
380
381 /**
382 * return valid env
383 */
384 static
385 int env_check_valid(void)
386 {
387 const uint16_t offset[2] = {CONFIG_ENV_OFFSET,
388 CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE};
389 uint_fast8_t flags[2], crc_ok[2];
390 int rc;
391
392 /* read FLAGS */
393 flags[0] = eeprom_read_byte ((uint8_t *) offset[0] +
394 offsetof(env_t, flags));
395 flags[1] = eeprom_read_byte ((uint8_t *) offset[1] +
396 offsetof(env_t, flags));
397
398 /* check CRC */
399 crc_ok[0] = (
400 eeprom_read_word((uint16_t *) offset[0] +
401 offsetof(env_t, crc))
402 == env_crc(offset[0] + offsetof(env_t, data))
403 );
404 crc_ok[1] = (
405 eeprom_read_word((uint16_t *) offset[1] +
406 offsetof(env_t, crc))
407 == env_crc(offset[1] + offsetof(env_t, data))
408 );
409
410 if (!crc_ok[0] && !crc_ok[1]) {
411 rc = 0;
412
413 } else if (crc_ok[0] && !crc_ok[1]) {
414 rc = 1;
415 } else if (!crc_ok[0] && crc_ok[1]) {
416 rc = 2;
417 } else {
418 /* both ok - check serial */
419 #if 1
420 if (flags[1] == ACTIVE_FLAG && flags[0] != ACTIVE_FLAG)
421 rc = 2;
422 else if (flags[1] == OBSOLETE_FLAG && flags[0] == 0xFF)
423 rc = 2;
424 else
425 rc = 1;
426 #else
427 if (flags[0] == ACTIVE_FLAG && flags[1] == OBSOLETE_FLAG)
428 rc = 1;
429 else if (flags[0] == OBSOLETE_FLAG && flags[1] == ACTIVE_FLAG)
430 rc = 2;
431 else if (flags[0] == 0xFF && flags[1] == 0)
432 rc = 2;
433 else if (flags[1] == 0xFF && flags[0] == 0)
434 rc = 1;
435 else /* flags are equal - almost impossible */
436 rc = 1;
437 #endif
438 }
439
440 return rc;
441 }
442
443
444 int env_init(void)
445 {
446 env_valid = env_check_valid();
447 if (env_valid == 0) {
448 set_default_env(PSTR("*** Warning - bad CRC, "
449 "using default environment\n\n"));
450 }
451 envlist_import(ENVLIST_DELETE);
452 return 0;
453 }
454
455
456
457 char *getenv(const MEMX char *name)
458 {
459 env_item_t *ep;
460 char *ret = NULL;
461
462 ep = envlist_get(name, ENV_GET_VAL);
463 if (ep != NULL)
464 ret = ep->val.ram;
465 return ret;
466 }
467
468 static
469 int env_item_save(env_item_t *ep, uint16_t offset, int space_left)
470 {
471 char buf[CONFIG_SYS_ENV_NAMELEN+1];
472 int len;
473 env_item_t e = *ep;
474
475 len = varname_get(buf, ep);
476 if (len == 0)
477 return 0;
478 buf[len++] = '=';
479 space_left -= len;
480
481 if (space_left <= 0)
482 return 0;
483
484 eeprom_update_block(buf, (uint8_t *) offset, len);
485 offset += len;
486
487 if (e.val.ram != NULL) {
488 char c;
489 do {
490 if (e.flags & EF_V_EEP)
491 c = env_get_char(e.val.eep++);
492 else
493 c = *e.val.ram++;
494
495 eeprom_update_byte((uint8_t *) offset, c);
496 offset++;
497 space_left--;
498 len++;
499 } while ((c != '\0') && space_left );
500 }
501 return len;
502 }
503
504
505 static
506 int saveenv(void)
507 {
508 unsigned int off = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
509 unsigned int off_red = CONFIG_ENV_OFFSET;
510 unsigned int pos;
511 int len, left;
512 uint16_t crc;
513 int rc = 0;
514
515 if (env_valid == 2) {
516 off = CONFIG_ENV_OFFSET;
517 off_red = CONFIG_ENV_OFFSET + CONFIG_ENV_SIZE;
518 }
519
520 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags), 0xff);
521
522 pos = off + offsetof(env_t, data);
523 left = ENV_SIZE - 1;
524 for (int i = 0 ; i < entrycount; i++) {
525 len = env_item_save(&env_list[i], pos, left);
526 if (len == 0) {
527 return 1;
528 }
529 pos += len;
530 left -= len;
531 }
532 /* terminate list */
533 eeprom_update_byte((uint8_t *) pos, 0);
534 crc = env_crc(off + offsetof(env_t, data));
535 eeprom_update_word((uint16_t *) off + offsetof(env_t, crc), crc);
536 eeprom_update_byte((uint8_t *) off + offsetof(env_t, flags),
537 ACTIVE_FLAG);
538
539 if (rc == 0) {
540 eeprom_update_byte((uint8_t *) off_red + offsetof(env_t, flags),
541 OBSOLETE_FLAG);
542 env_valid = (env_valid == 2) ? 1 : 2;
543
544 envlist_import(ENVLIST_DELETE);
545 }
546
547 return rc;
548 }
549
550
551 static
552 int env_item_print(env_item_t *ep)
553 {
554 char buf[CONFIG_SYS_ENV_NAMELEN+1];
555 int len;
556 env_item_t e = *ep;
557
558 varname_get(buf, ep);
559 len = printf_P(PSTR("%s="), buf);
560
561 if (e.val.ram != NULL) {
562 while (1) {
563 char c;
564 if (e.flags & EF_V_EEP)
565 c = env_get_char(e.val.eep++);
566 else
567 c = *e.val.ram++;
568
569 if (c != '\0') {
570 putchar(c);
571 len++;
572 } else
573 break;
574 }
575 }
576 putchar('\n');
577 len ++;
578
579 return len;
580 }
581
582
583 /*
584 * Command interface: print one or all environment variables
585 *
586 * Returns -1 in case of error, or length of printed string
587 */
588 static
589 int env_print(const MEMX char *name)
590 {
591 int len = -1;
592
593 if (name) { /* print a single name */
594
595 env_item_t *ep = envlist_search(name);
596 if (ep != NULL)
597 len = env_item_print(ep);
598
599 } else { /* print whole list */
600 len = 0;
601 for (int i = 0 ; i < entrycount; i++) {
602 len += env_item_print(&env_list[i]);
603 }
604 }
605 return len;
606 }
607
608 static
609 int env_print_ramsize(void)
610 {
611 int size = 0;
612 uint8_t name_cnt = 0;
613 uint8_t val_cnt = 0;
614
615 for (int i = 0 ; i < entrycount; i++) {
616 if ((env_list[i].flags & EF_N_EEP) == 0 &&
617 (env_list[i].name.ram != NULL)) {
618 name_cnt++;
619 size += strlen(env_list[i].name.ram) + 3;
620 }
621 if ((env_list[i].flags & EF_V_EEP) == 0 &&
622 (env_list[i].val.ram != NULL)) {
623 val_cnt++;
624 size += strlen(env_list[i].val.ram) + 3;
625 }
626 }
627 printf_P(PSTR("%d bytes RAM used for %u names and %u values\n"),
628 size, name_cnt, val_cnt);
629 return size;
630 }
631
632
633 command_ret_t do_env_print(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
634 {
635 command_ret_t rc = CMD_RET_SUCCESS;
636
637 (void) cmdtp; (void) flag;
638
639 if (argc == 1) {
640 /* print all env vars */
641 int size = env_print(NULL);
642 if (size < 0)
643 return CMD_RET_FAILURE;
644 printf_P(PSTR("\nEnvironment size: %d/%d bytes\n"),
645 size, ENV_SIZE);
646 env_print_ramsize();
647 return CMD_RET_SUCCESS;
648 }
649
650 /* print selected env vars */
651 for (int i = 1; i < argc; ++i) {
652 int rc = env_print(argv[i]);
653 if (rc < 0) {
654 printf_P(PSTR("## Error: \"%s\" not defined\n"), argv[i]);
655 rc = CMD_RET_FAILURE;
656 }
657 }
658
659 return rc;
660 }
661
662
663 /**
664 * Set or delete environment variable
665 *
666 * Set a new environment variable,
667 * or replace or delete an existing one.
668 *
669 * @param flag (not used)
670 * @param argc
671 * @param argv[1] Environment variable to set or delete
672 * if no more args
673 * @param argv[2] ... Value to set it to
674 *
675 * @return 0 if ok, 1 on error
676 */
677 static
678 command_ret_t _do_env_set(int flag, int argc, char * const argv[])
679 {
680 int i, len;
681 char *name, *value, *s;
682 env_item_t e, *ep;
683
684 (void) flag;
685 debug("Initial value for argc=%d\n", argc);
686
687 name = argv[1];
688 value = argv[2];
689
690 if (strchr(name, '=')) {
691 printf_P(PSTR("## Error: illegal character '='"
692 "in variable name \"%s\"\n"), name);
693 return CMD_RET_FAILURE;
694 }
695 if (strlen(name) > CONFIG_SYS_ENV_NAMELEN) {
696 printf_P(PSTR("## Error: Variable name \"%s\" too long. "
697 "(max %d characters)\n"), name, CONFIG_SYS_ENV_NAMELEN);
698 return CMD_RET_FAILURE;
699 }
700 /*
701 env_id++;
702 */
703 /* Delete only ? */
704 if (argc < 3 || argv[2] == NULL) {
705 int rc = envlist_delete(name);
706 return rc ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
707 }
708
709 /*
710 * Insert / replace new value
711 */
712 for (i = 2, len = 0; i < argc; ++i)
713 len += strlen(argv[i]) + 1;
714
715 value = xmalloc(len);
716 if (value == NULL) {
717 printf_P(PSTR("## Can't malloc %d bytes\n"), len);
718 return CMD_RET_FAILURE;
719 }
720 for (i = 2, s = value; i < argc; ++i) {
721 char *v = argv[i];
722
723 while ((*s++ = *v++) != '\0')
724 ;
725 *(s - 1) = ' ';
726 }
727 if (s != value)
728 *--s = '\0';
729
730 e.flags = EF_DIRTY;
731 e.name.ram = name;
732 e.val.ram = value;
733 ep = envlist_enter(&e);
734 if (!ep) {
735 printf_P(PSTR("## Error inserting \"%s\" variable.\n"),
736 name);
737 return CMD_RET_FAILURE;
738 }
739
740 return CMD_RET_SUCCESS;
741 }
742
743 /**
744 * Set an environment variable
745 *
746 * @param varname Environment variable to set
747 * @param varvalue Value to set it to
748 * @return 0 if ok, 1 on error
749 */
750 static
751 int setenv(const char *varname, const char *varvalue)
752 {
753 const char * const argv[3] = { NULL, varname, varvalue };
754 int argc = 3;
755
756 if (varvalue == NULL || varvalue[0] == '\0')
757 --argc;
758
759 return (int) _do_env_set(0, argc, (char * const *)argv);
760 }
761
762 #if 0
763 /**
764 * Set an environment variable to an integer value
765 *
766 * @param varname Environment variable to set
767 * @param value Value to set it to
768 * @return 0 if ok, 1 on error
769 */
770 int setenv_ulong(const char *varname, unsigned long value)
771 {
772 /* TODO: this should be unsigned */
773 char *str = simple_itoa(value);
774
775 return setenv(varname, str);
776 }
777 #endif
778
779
780 /**
781 * Set an environment variable to an value in hex
782 *
783 * @param varname Environment variable to set
784 * @param value Value to set it to
785 * @return 0 if ok, 1 on error
786 */
787 int setenv_hex(const MEMX char *varname, unsigned long value)
788 {
789 char str[sizeof(unsigned long) *2 + 1];
790
791 sprintf_P(str, PSTR("%lx"), value);
792 return setenv(varname, str);
793 }
794
795
796 /**
797 * Decode the integer value of an environment variable and return it.
798 *
799 * @param name Name of environemnt variable
800 * @param base Number base to use (normally 10, or 16 for hex)
801 * @param default_val Default value to return if the variable is not
802 * found
803 * @return the decoded value, or default_val if not found
804 */
805 unsigned long getenv_ulong(const MEMX char *name, int base, unsigned long default_val)
806 {
807 char buf[16];
808 unsigned long value;
809 char *vp, *endp;
810
811 env_item_t *ep = envlist_search(name);
812
813 if (ep != NULL ) {
814 if (ep->flags & EF_V_EEP) {
815 varval_get(buf, ep->val.eep, sizeof(buf));
816 vp = buf;
817 } else
818 vp = ep->val.ram;
819 if (vp != NULL) {
820 value = strtoul(vp, &endp, base);
821 if (endp != vp)
822 return value;
823 }
824 }
825
826 return default_val;
827 }
828
829
830 command_ret_t do_env_set(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
831 {
832 (void) cmdtp;
833
834 if (argc < 2)
835 return CMD_RET_USAGE;
836
837 return _do_env_set(flag, argc, argv);
838 }
839
840
841 command_ret_t do_env_default(cmd_tbl_t *cmdtp, int flag,
842 int argc, char * const argv[])
843 {
844 (void) cmdtp; (void) flag; (void) argc; (void) argv;
845
846 /* Reset the whole environment */
847 set_default_env(PSTR("## Resetting to default environment\n"));
848 envlist_import(ENVLIST_DELETE);
849
850 return CMD_RET_SUCCESS;
851 }
852
853 command_ret_t do_env_save(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
854 {
855 (void) cmdtp; (void) flag; (void) argc; (void) argv;
856
857 printf_P(PSTR("Saving Environment ...\n"));
858 return saveenv() ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
859 }
860
861 #if defined(CONFIG_AUTO_COMPLETE)
862 int env_complete(char *var, int maxv, char *cmdv[], int bufsz, char *buf)
863 {
864 ENTRY *match;
865 int found, idx;
866
867 idx = 0;
868 found = 0;
869 cmdv[0] = NULL;
870
871 while ((idx = hmatch_r(var, idx, &match, &env_htab))) {
872 int vallen = strlen(match->key) + 1;
873
874 if (found >= maxv - 2 || bufsz < vallen)
875 break;
876
877 cmdv[found++] = buf;
878 memcpy(buf, match->key, vallen);
879 buf += vallen;
880 bufsz -= vallen;
881 }
882
883 qsort(cmdv, found, sizeof(cmdv[0]), strcmp_compar);
884
885 if (idx)
886 cmdv[found++] = "...";
887
888 cmdv[found] = NULL;
889 return found;
890 }
891 #endif