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