]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blob - db/write.c
xfs_db: write values into dir/attr blocks and recalculate CRCs
[thirdparty/xfsprogs-dev.git] / db / write.c
1 /*
2 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19 #include "libxfs.h"
20 #include <ctype.h>
21 #include <time.h>
22 #include "bit.h"
23 #include "block.h"
24 #include "command.h"
25 #include "type.h"
26 #include "faddr.h"
27 #include "fprint.h"
28 #include "field.h"
29 #include "flist.h"
30 #include "io.h"
31 #include "init.h"
32 #include "output.h"
33 #include "print.h"
34 #include "write.h"
35 #include "malloc.h"
36
37 static int write_f(int argc, char **argv);
38 static void write_help(void);
39
40 static const cmdinfo_t write_cmd =
41 { "write", NULL, write_f, 0, -1, 0, N_("[-c] [field or value]..."),
42 N_("write value to disk"), write_help };
43
44 void
45 write_init(void)
46 {
47 if (!expert_mode)
48 return;
49
50 add_command(&write_cmd);
51 srand48(clock());
52 }
53
54 static void
55 write_help(void)
56 {
57 dbprintf(_(
58 "\n"
59 " The 'write' command takes on different personalities depending on the\n"
60 " type of object being worked with.\n\n"
61 " Write has 3 modes:\n"
62 " 'struct mode' - is active anytime you're looking at a filesystem object\n"
63 " which contains individual fields (ex: an inode).\n"
64 " 'data mode' - is active anytime you set a disk address directly or set\n"
65 " the type to 'data'.\n"
66 " 'string mode' - only used for writing symlink blocks.\n"
67 "\n"
68 " Examples:\n"
69 " Struct mode: 'write core.uid 23' - set an inode uid field to 23.\n"
70 " 'write fname \"hello\\000\"' - write superblock fname.\n"
71 " (note: in struct mode strings are not null terminated)\n"
72 " 'write fname #6669736800' - write superblock fname with hex.\n"
73 " 'write uuid 00112233-4455-6677-8899-aabbccddeeff'\n"
74 " - write superblock uuid.\n"
75 " Data mode: 'write fill 0xff' - fill the entire block with 0xff's\n"
76 " 'write lshift 3' - shift the block 3 bytes to the left\n"
77 " 'write sequence 1 5' - write a cycle of number [1-5] through\n"
78 " the entire block.\n"
79 " String mode: 'write \"This_is_a_filename\" - write null terminated string.\n"
80 "\n"
81 " In data mode type 'write' by itself for a list of specific commands.\n\n"
82 " Specifying the -c option will allow writes of invalid (corrupt) data with\n"
83 " an invalid CRC. Specifying the -d option will allow writes of invalid data,\n"
84 " but still recalculate the CRC so we are forced to check and detect the\n"
85 " invalid data appropriately.\n\n"
86 ));
87
88 }
89
90 static int
91 write_f(
92 int argc,
93 char **argv)
94 {
95 pfunc_t pf;
96 extern char *progname;
97 int c;
98 bool corrupt = false; /* Allow write of bad data w/ invalid CRC */
99 bool invalid_data = false; /* Allow write of bad data w/ valid CRC */
100 struct xfs_buf_ops local_ops;
101 const struct xfs_buf_ops *stashed_ops = NULL;
102
103 if (x.isreadonly & LIBXFS_ISREADONLY) {
104 dbprintf(_("%s started in read only mode, writing disabled\n"),
105 progname);
106 return 0;
107 }
108
109 if (cur_typ == NULL) {
110 dbprintf(_("no current type\n"));
111 return 0;
112 }
113
114 pf = cur_typ->pfunc;
115 if (pf == NULL) {
116 dbprintf(_("no handler function for type %s, write unsupported.\n"),
117 cur_typ->name);
118 return 0;
119 }
120
121 while ((c = getopt(argc, argv, "cd")) != EOF) {
122 switch (c) {
123 case 'c':
124 corrupt = true;
125 break;
126 case 'd':
127 invalid_data = true;
128 break;
129 default:
130 dbprintf(_("bad option for write command\n"));
131 return 0;
132 }
133 }
134
135 if (corrupt && invalid_data) {
136 dbprintf(_("Cannot specify both -c and -d options\n"));
137 return 0;
138 }
139
140 if (invalid_data &&
141 iocur_top->typ->crc_off == TYP_F_NO_CRC_OFF &&
142 !iocur_top->ino_buf &&
143 !iocur_top->dquot_buf) {
144 dbprintf(_("Cannot recalculate CRCs on this type of object\n"));
145 return 0;
146 }
147
148 argc -= optind;
149 argv += optind;
150
151 /*
152 * If the buffer has no verifier or we are using standard verifier
153 * paths, then just write it out and return
154 */
155 if (!iocur_top->bp->b_ops ||
156 !(corrupt || invalid_data)) {
157 (*pf)(DB_WRITE, cur_typ->fields, argc, argv);
158 return 0;
159 }
160
161
162 /* Temporarily remove write verifier to write bad data */
163 stashed_ops = iocur_top->bp->b_ops;
164 local_ops.verify_read = stashed_ops->verify_read;
165 iocur_top->bp->b_ops = &local_ops;
166
167 if (corrupt) {
168 local_ops.verify_write = xfs_dummy_verify;
169 dbprintf(_("Allowing write of corrupted data and bad CRC\n"));
170 } else if (iocur_top->ino_buf) {
171 local_ops.verify_write = xfs_verify_recalc_inode_crc;
172 dbprintf(_("Allowing write of corrupted inode with good CRC\n"));
173 } else if (iocur_top->dquot_buf) {
174 local_ops.verify_write = xfs_verify_recalc_dquot_crc;
175 dbprintf(_("Allowing write of corrupted dquot with good CRC\n"));
176 } else if (iocur_top->typ->crc_off == TYP_F_CRC_FUNC) {
177 local_ops.verify_write = iocur_top->typ->set_crc;
178 dbprintf(_("Allowing write of corrupted data with good CRC\n"));
179 } else { /* invalid data */
180 local_ops.verify_write = xfs_verify_recalc_crc;
181 dbprintf(_("Allowing write of corrupted data with good CRC\n"));
182 }
183
184 (*pf)(DB_WRITE, cur_typ->fields, argc, argv);
185
186 iocur_top->bp->b_ops = stashed_ops;
187
188 return 0;
189 }
190
191 /* compare significant portions of commands */
192
193 static int
194 sigcmp(
195 char *s1,
196 char *s2,
197 int sig)
198 {
199 int sigcnt;
200
201 if (!s1 || !s2)
202 return 0;
203
204 for (sigcnt = 0; *s1 == *s2; s1++, s2++) {
205 sigcnt++;
206 if (*s1 == '\0')
207 return 1;
208 }
209 if (*s1 && *s2)
210 return 0;
211
212 if (sig && (sigcnt >= sig))
213 return 1;
214
215 return 0;
216 }
217
218 /* ARGSUSED */
219 static void
220 bwrite_lshift(
221 int start,
222 int len,
223 int shift,
224 int from,
225 int to)
226 {
227 char *base;
228
229 if (shift == -1)
230 shift = 1;
231 if (start == -1)
232 start = 0;
233 if (len == -1)
234 len = iocur_top->len - start;
235
236 if (len+start > iocur_top->len) {
237 dbprintf(_("length (%d) too large for data block size (%d)"),
238 len, iocur_top->len);
239 }
240
241 base = (char *)iocur_top->data + start;
242
243 memcpy(base, base+shift, len-shift);
244 memset(base+(len-shift), 0, shift);
245 }
246
247 /* ARGSUSED */
248 static void
249 bwrite_rshift(
250 int start,
251 int len,
252 int shift,
253 int from,
254 int to)
255 {
256 char *base;
257
258 if (shift == -1)
259 shift = 1;
260 if (start == -1)
261 start = 0;
262 if (len == -1)
263 len = iocur_top->len - start;
264
265 if (len+start > iocur_top->len) {
266 dbprintf(_("length (%d) too large for data block size (%d)"),
267 len, iocur_top->len);
268 }
269
270 base = (char *)iocur_top->data + start;
271
272 memcpy(base+shift, base, len-shift);
273 memset(base, 0, shift);
274 }
275
276 /* ARGSUSED */
277 static void
278 bwrite_lrot(
279 int start,
280 int len,
281 int shift,
282 int from,
283 int to)
284 {
285 char *base;
286 char *hold_region;
287
288 if (shift == -1)
289 shift = 1;
290 if (start == -1)
291 start = 0;
292 if (len == -1)
293 len = iocur_top->len - start;
294
295 if (len+start > iocur_top->len) {
296 dbprintf(_("length (%d) too large for data block size (%d)"),
297 len, iocur_top->len);
298 }
299
300 base = (char *)iocur_top->data + start;
301
302 hold_region = xmalloc(shift);
303 memcpy(hold_region, base, shift);
304 memcpy(base, base+shift, len-shift);
305 memcpy(base+(len-shift), hold_region, shift);
306 free(hold_region);
307 }
308
309 /* ARGSUSED */
310 static void
311 bwrite_rrot(
312 int start,
313 int len,
314 int shift,
315 int from,
316 int to)
317 {
318 char *base;
319 char *hold_region;
320
321 if (shift == -1)
322 shift = 1;
323 if (start == -1)
324 start = 0;
325 if (len == -1)
326 len = iocur_top->len - start;
327
328 if (len+start > iocur_top->len) {
329 dbprintf(_("length (%d) too large for data block size (%d)"),
330 len, iocur_top->len);
331 }
332
333 base = (char *)iocur_top->data + start;
334
335 hold_region = xmalloc(shift);
336 memcpy(hold_region, base+(len-shift), shift);
337 memmove(base+shift, base, len-shift);
338 memcpy(base, hold_region, shift);
339 free(hold_region);
340 }
341
342 /* ARGSUSED */
343 static void
344 bwrite_seq(
345 int start,
346 int len,
347 int step,
348 int from,
349 int to)
350 {
351 int i;
352 int tmp;
353 int base;
354 int range;
355 int top;
356 char *buf;
357
358 if (start == -1)
359 start = 0;
360
361 if (len == -1)
362 len = iocur_top->len - start;
363
364 if (len+start > iocur_top->len) {
365 dbprintf(_("length (%d) too large for data block size (%d)"),
366 len, iocur_top->len);
367 }
368
369 if (from == -1 || from > 255)
370 from = 0;
371 if (to == -1 || to > 255)
372 to = 255;
373 if (step == -1)
374 step = 1;
375
376 base = from;
377 top = to;
378 if (from > to) {
379 base = to;
380 top = from;
381 if (step > 0)
382 step = -step;
383 }
384
385 range = top - base;
386 buf = (char *)iocur_top->data + start;
387
388 tmp = 0;
389 for (i = start; i < start+len; i++) {
390 *buf++ = tmp + base;
391 tmp = (tmp + step)%(range+1);
392 }
393 }
394
395 /* ARGSUSED */
396 static void
397 bwrite_random(
398 int start,
399 int len,
400 int shift,
401 int from,
402 int to)
403 {
404 int i;
405 char *buf;
406
407 if (start == -1)
408 start = 0;
409
410 if (len == -1)
411 len = iocur_top->len - start;
412
413 if (len+start > iocur_top->len) {
414 dbprintf(_("length (%d) too large for data block size (%d)"),
415 len, iocur_top->len);
416 }
417
418 buf = (char *)iocur_top->data + start;
419
420 for (i = start; i < start+len; i++)
421 *buf++ = (char)lrand48();
422 }
423
424 /* ARGSUSED */
425 static void
426 bwrite_fill(
427 int start,
428 int len,
429 int value,
430 int from,
431 int to)
432 {
433 char *base;
434
435 if (value == -1)
436 value = 0;
437 if (start == -1)
438 start = 0;
439 if (len == -1)
440 len = iocur_top->len - start;
441
442 if (len+start > iocur_top->len) {
443 dbprintf(_("length (%d) too large for data block size (%d)"),
444 len, iocur_top->len);
445 }
446
447 base = (char *)iocur_top->data + start;
448
449 memset(base, value, len);
450 }
451
452 static struct bw_cmd {
453 void (*cmdfunc)(int,int,int,int,int);
454 char *cmdstr;
455 int sig_chars;
456 int argmin;
457 int argmax;
458 int shiftcount_arg;
459 int from_arg;
460 int to_arg;
461 int start_arg;
462 int len_arg;
463 char *usage;
464 } bw_cmdtab[] = {
465 /* cmd sig min max sh frm to start len */
466 { bwrite_lshift, "lshift", 2, 0, 3, 1, 0, 0, 2, 3,
467 "[shiftcount] [start] [len]", },
468 { bwrite_rshift, "rshift", 2, 0, 3, 1, 0, 0, 2, 3,
469 "[shiftcount] [start] [len]", },
470 { bwrite_lrot, "lrot", 2, 0, 3, 1, 0, 0, 2, 3,
471 "[shiftcount] [start] [len]", },
472 { bwrite_rrot, "rrot", 2, 0, 3, 1, 0, 0, 2, 3,
473 "[shiftcount] [start] [len]", },
474 { bwrite_seq, "sequence", 3, 0, 4, 0, 1, 2, 3, 4,
475 "[from] [to] [start] [len]", },
476 { bwrite_random, "random", 3, 0, 2, 0, 0, 0, 1, 2,
477 "[start] [len]", },
478 { bwrite_fill, "fill", 1, 1, 3, 1, 0, 0, 2, 3,
479 "num [start] [len]" }
480 };
481
482 #define BWRITE_CMD_MAX (sizeof(bw_cmdtab)/sizeof(bw_cmdtab[0]))
483
484 static int
485 convert_oct(
486 char *arg,
487 int *ret)
488 {
489 int count;
490 int i;
491 int val = 0;
492
493 /* only allow 1 case, '\' and 3 octal digits (or less) */
494
495 for (count = 0; count < 3; count++) {
496 if (arg[count] == '\0')
497 break;
498
499 if ((arg[count] < '0') && (arg[count] > '7'))
500 break;
501 }
502
503 for (i = 0; i < count; i++) {
504 val |= ((arg[(count-1)-i]-'0')&0x07)<<(i*3);
505 }
506
507 *ret = val&0xff;
508
509 return(count);
510 }
511
512 #define NYBBLE(x) (isdigit(x)?(x-'0'):(tolower(x)-'a'+0xa))
513
514 /*
515 * convert_arg allows input in the following forms:
516 *
517 * - A string ("ABTB") whose ASCII value is placed in an array in the order
518 * matching the input.
519 *
520 * - An even number of hex numbers. If the length is greater than 64 bits,
521 * then the output is an array of bytes whose top nibble is the first hex
522 * digit in the input, the lower nibble is the second hex digit in the
523 * input. UUID entries are entered in this manner.
524 *
525 * - A decimal or hexadecimal integer to be used with setbitval().
526 *
527 * Numbers that are passed to setbitval() need to be in big endian format and
528 * are adjusted in the buffer so that the first input bit is to be be written to
529 * the first bit in the output.
530 */
531 static char *
532 convert_arg(
533 char *arg,
534 int bit_length)
535 {
536 int i;
537 int alloc_size;
538 int octval;
539 int offset;
540 int ret;
541 static char *buf = NULL;
542 char *endp;
543 char *rbuf;
544 char *ostr;
545 __u64 *value;
546 __u64 val = 0;
547
548 if (bit_length <= 64)
549 alloc_size = 8;
550 else
551 alloc_size = (bit_length + 7) / 8;
552
553 buf = xrealloc(buf, alloc_size);
554 memset(buf, 0, alloc_size);
555 value = (__u64 *)buf;
556 rbuf = buf;
557
558 if (*arg == '\"') {
559 /* input a string and output ASCII array of characters */
560
561 /* zap closing quote if there is one */
562 ostr = strrchr(arg + 1, '\"');
563 if (ostr)
564 *ostr = '\0';
565
566 ostr = arg + 1;
567 for (i = 0; i < alloc_size; i++) {
568 if (!*ostr)
569 break;
570
571 /* do octal conversion */
572 if (*ostr == '\\') {
573 if (*(ostr + 1) >= '0' || *(ostr + 1) <= '7') {
574 ret = convert_oct(ostr + 1, &octval);
575 *rbuf++ = octval;
576 ostr += ret + 1;
577 continue;
578 }
579 }
580 *rbuf++ = *ostr++;
581 }
582 return buf;
583 }
584
585 if (arg[0] == '#' || ((arg[0] != '-') && strchr(arg,'-'))) {
586 /*
587 * handle hex blocks ie
588 * #00112233445566778899aabbccddeeff
589 * and uuids ie
590 * 1122334455667788-99aa-bbcc-ddee-ff00112233445566778899
591 *
592 * (but if it starts with "-" assume it's just an integer)
593 */
594 int bytes = bit_length / NBBY;
595
596 /* is this an array of hec numbers? */
597 if (bit_length % NBBY)
598 return NULL;
599
600 /* skip leading hash */
601 if (*arg == '#')
602 arg++;
603
604 while (*arg && bytes--) {
605 /* skip hypens */
606 while (*arg == '-')
607 arg++;
608
609 /* get first nybble */
610 if (!isxdigit((int)*arg))
611 return NULL;
612 *rbuf = NYBBLE((int)*arg) << 4;
613 arg++;
614
615 /* skip more hyphens */
616 while (*arg == '-')
617 arg++;
618
619 /* get second nybble */
620 if (!isxdigit((int)*arg))
621 return NULL;
622 *rbuf++ |= NYBBLE((int)*arg);
623 arg++;
624 }
625 if (bytes < 0 && *arg)
626 return NULL;
627
628 return buf;
629 }
630
631 /* handle decimal / hexadecimal integers */
632 val = strtoll(arg, &endp, 0);
633 /* return if not a clean number */
634 if (*endp != '\0')
635 return NULL;
636
637 /* Does the value fit into the range of the destination bitfield? */
638 if (bit_length < 64 && (val >> bit_length) > 0)
639 return NULL;
640 /*
641 * If the length of the field is not a multiple of a byte, push
642 * the bits up in the field, so the most signicant field bit is
643 * the most significant bit in the byte:
644 *
645 * before:
646 * val |----|----|----|----|----|--MM|mmmm|llll|
647 * after
648 * val |----|----|----|----|----|MMmm|mmll|ll00|
649 */
650 offset = bit_length % NBBY;
651 if (offset)
652 val <<= (NBBY - offset);
653
654 /*
655 * convert to big endian and copy into the array
656 * rbuf |----|----|----|----|----|MMmm|mmll|ll00|
657 */
658 *value = cpu_to_be64(val);
659
660 /*
661 * Align the array to point to the field in the array.
662 * rbuf = |MMmm|mmll|ll00|
663 */
664 offset = sizeof(__be64) - 1 - ((bit_length - 1) / sizeof(__be64));
665 rbuf += offset;
666 return rbuf;
667 }
668
669
670 /* ARGSUSED */
671 void
672 write_struct(
673 const field_t *fields,
674 int argc,
675 char **argv)
676 {
677 const ftattr_t *fa;
678 flist_t *fl;
679 flist_t *sfl;
680 int bit_length;
681 char *buf;
682 int parentoffset;
683
684 if (argc != 2) {
685 dbprintf(_("usage: write fieldname value\n"));
686 return;
687 }
688
689 fl = flist_scan(argv[0]);
690 if (!fl) {
691 dbprintf(_("unable to parse '%s'.\n"), argv[0]);
692 return;
693 }
694
695 /* if we're a root field type, go down 1 layer to get field list */
696 if (fields->name[0] == '\0') {
697 fa = &ftattrtab[fields->ftyp];
698 ASSERT(fa->ftyp == fields->ftyp);
699 fields = fa->subfld;
700 }
701
702 /* run down the field list and set offsets into the data */
703 if (!flist_parse(fields, fl, iocur_top->data, 0)) {
704 flist_free(fl);
705 dbprintf(_("parsing error\n"));
706 return;
707 }
708
709 sfl = fl;
710 parentoffset = 0;
711 while (sfl->child) {
712 parentoffset = sfl->offset;
713 sfl = sfl->child;
714 }
715
716 /*
717 * For structures, fsize * fcount tells us the size of the region we are
718 * modifying, which is usually a single structure member and is pointed
719 * to by the last child in the list.
720 *
721 * However, if the base structure is an array and we have a direct index
722 * into the array (e.g. write bno[5]) then we are returned a single
723 * flist object with the offset pointing directly at the location we
724 * need to modify. The length of the object we are modifying is then
725 * determined by the size of the individual array entry (fsize) and the
726 * indexes defined in the object, not the overall size of the array
727 * (which is what fcount returns).
728 */
729 bit_length = fsize(sfl->fld, iocur_top->data, parentoffset, 0);
730 if (sfl->fld->flags & FLD_ARRAY)
731 bit_length *= sfl->high - sfl->low + 1;
732 else
733 bit_length *= fcount(sfl->fld, iocur_top->data, parentoffset);
734
735 /* convert this to a generic conversion routine */
736 /* should be able to handle str, num, or even labels */
737
738 buf = convert_arg(argv[1], bit_length);
739 if (!buf) {
740 dbprintf(_("unable to convert value '%s'.\n"), argv[1]);
741 flist_free(fl);
742 return;
743 }
744
745 setbitval(iocur_top->data, sfl->offset, bit_length, buf);
746 write_cur();
747
748 flist_print(fl);
749 print_flist(fl);
750 flist_free(fl);
751 }
752
753 /* ARGSUSED */
754 void
755 write_string(
756 const field_t *fields,
757 int argc,
758 char **argv)
759 {
760 char *buf;
761 int i;
762
763 if (argc != 1) {
764 dbprintf(_("usage (in string mode): write \"string...\"\n"));
765 return;
766 }
767
768 buf = convert_arg(argv[0], (int)((strlen(argv[0])+1)*8));
769 for (i = 0; i < iocur_top->len; i++) {
770 ((char *)iocur_top->data)[i] = *buf;
771 if (*buf++ == '\0')
772 break;
773 }
774
775 /* write back to disk */
776 write_cur();
777 }
778
779 /* ARGSUSED */
780 void
781 write_block(
782 const field_t *fields,
783 int argc,
784 char **argv)
785 {
786 int i;
787 int shiftcount = -1;
788 int start = -1;
789 int len = -1;
790 int from = -1;
791 int to = -1;
792 struct bw_cmd *cmd = NULL;
793
794 if (argc <= 1 || argc > 5)
795 goto block_usage;
796
797 for (i = 0; i < BWRITE_CMD_MAX; i++) {
798 if (sigcmp(argv[0], bw_cmdtab[i].cmdstr,
799 bw_cmdtab[i].sig_chars)) {
800 cmd = &bw_cmdtab[i];
801 break;
802 }
803 }
804
805 if (!cmd) {
806 dbprintf(_("write: invalid subcommand\n"));
807 goto block_usage;
808 }
809
810 if ((argc < cmd->argmin + 1) || (argc > cmd->argmax + 1)) {
811 dbprintf(_("write %s: invalid number of arguments\n"),
812 cmd->cmdstr);
813 goto block_usage;
814 }
815
816 if (cmd->shiftcount_arg && (cmd->shiftcount_arg < argc))
817 shiftcount = (int)strtoul(argv[cmd->shiftcount_arg], NULL, 0);
818 if (cmd->start_arg && (cmd->start_arg < argc))
819 start = (int)strtoul(argv[cmd->start_arg], NULL, 0);
820 if (cmd->len_arg && (cmd->len_arg < argc))
821 len = (int)strtoul(argv[cmd->len_arg], NULL, 0);
822 if (cmd->from_arg && (cmd->len_arg < argc))
823 from = (int)strtoul(argv[cmd->from_arg], NULL, 0);
824 if (cmd->to_arg && (cmd->len_arg < argc))
825 to = (int)strtoul(argv[cmd->to_arg], NULL, 0);
826
827 cmd->cmdfunc(start, len, shiftcount, from, to);
828
829 /* write back to disk */
830 write_cur();
831 return;
832
833 block_usage:
834
835 dbprintf(_("usage: write (in data mode)\n"));
836 for (i = 0; i < BWRITE_CMD_MAX; i++) {
837 dbprintf(" %-9.9s %s\n",
838 bw_cmdtab[i].cmdstr, bw_cmdtab[i].usage);
839 }
840 dbprintf("\n");
841 return;
842 }