]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/io.c
xfs_db: Don't ASSERT on unrecognized metadata
[thirdparty/xfsprogs-dev.git] / db / io.c
CommitLineData
2bd0ea18 1/*
da23017d
NS
2 * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
3 * All Rights Reserved.
dfc130f3 4 *
da23017d
NS
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
2bd0ea18 7 * published by the Free Software Foundation.
dfc130f3 8 *
da23017d
NS
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.
dfc130f3 13 *
da23017d
NS
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
2bd0ea18
NS
17 */
18
6b803e5a 19#include "libxfs.h"
2bd0ea18 20#include "command.h"
2bd0ea18
NS
21#include "type.h"
22#include "faddr.h"
23#include "fprint.h"
24#include "field.h"
1e9c13c5 25#include "dquot.h"
2bd0ea18
NS
26#include "inode.h"
27#include "io.h"
28#include "output.h"
4ca431fc 29#include "init.h"
2bd0ea18 30#include "malloc.h"
b64af2c4 31#include "crc.h"
55f224ba 32#include "bit.h"
2bd0ea18
NS
33
34static int pop_f(int argc, char **argv);
35static void pop_help(void);
36static int push_f(int argc, char **argv);
37static void push_help(void);
38static int stack_f(int argc, char **argv);
39static void stack_help(void);
40static int forward_f(int argc, char **argv);
41static void forward_help(void);
42static int back_f(int argc, char **argv);
43static void back_help(void);
44static int ring_f(int argc, char **argv);
45static void ring_help(void);
46
47static const cmdinfo_t pop_cmd =
48 { "pop", NULL, pop_f, 0, 0, 0, NULL,
9ee7055c 49 N_("pop location from the stack"), pop_help };
2bd0ea18 50static const cmdinfo_t push_cmd =
9ee7055c
AM
51 { "push", NULL, push_f, 0, 2, 0, N_("[command]"),
52 N_("push location to the stack"), push_help };
2bd0ea18
NS
53static const cmdinfo_t stack_cmd =
54 { "stack", NULL, stack_f, 0, 0, 0, NULL,
9ee7055c 55 N_("view the location stack"), stack_help };
dfc130f3 56static const cmdinfo_t forward_cmd =
2bd0ea18 57 { "forward", "f", forward_f, 0, 0, 0, NULL,
9ee7055c 58 N_("move forward to next entry in the position ring"), forward_help };
dfc130f3 59static const cmdinfo_t back_cmd =
2bd0ea18 60 { "back", "b", back_f, 0, 0, 0, NULL,
9ee7055c 61 N_("move to the previous location in the position ring"), back_help };
dfc130f3 62static const cmdinfo_t ring_cmd =
2bd0ea18 63 { "ring", NULL, ring_f, 0, 1, 0, NULL,
9ee7055c 64 N_("show position ring or move to a specific entry"), ring_help };
2bd0ea18
NS
65
66iocur_t *iocur_base;
67iocur_t *iocur_top;
68int iocur_sp = -1;
69int iocur_len;
70
71#define RING_ENTRIES 20
72static iocur_t iocur_ring[RING_ENTRIES];
73static int ring_head = -1;
74static int ring_tail = -1;
75static int ring_current = -1;
76
77void
78io_init(void)
79{
80 add_command(&pop_cmd);
81 add_command(&push_cmd);
82 add_command(&stack_cmd);
83 add_command(&forward_cmd);
84 add_command(&back_cmd);
85 add_command(&ring_cmd);
86}
87
88void
89off_cur(
90 int off,
91 int len)
92{
93 if (iocur_top == NULL || off + len > BBTOB(iocur_top->blen))
9ee7055c 94 dbprintf(_("can't set block offset to %d\n"), off);
2bd0ea18
NS
95 else {
96 iocur_top->boff = off;
97 iocur_top->off = ((xfs_off_t)iocur_top->bb << BBSHIFT) + off;
98 iocur_top->len = len;
99 iocur_top->data = (void *)((char *)iocur_top->buf + off);
100 }
101}
102
103void
104pop_cur(void)
105{
106 if (iocur_sp < 0) {
9ee7055c 107 dbprintf(_("can't pop anything from I/O stack\n"));
2bd0ea18
NS
108 return;
109 }
72298d16
DC
110 if (iocur_top->bp) {
111 libxfs_putbuf(iocur_top->bp);
112 iocur_top->bp = NULL;
113 }
114 if (iocur_top->bbmap) {
115 free(iocur_top->bbmap);
116 iocur_top->bbmap = NULL;
117 }
2bd0ea18
NS
118 if (--iocur_sp >= 0) {
119 iocur_top = iocur_base + iocur_sp;
120 cur_typ = iocur_top->typ;
121 } else {
122 iocur_top = iocur_base;
123 iocur_sp = 0;
124 }
125}
126
127/*ARGSUSED*/
128static int
129pop_f(
130 int argc,
131 char **argv)
132{
133 pop_cur();
134 return 0;
135}
136
137static void
138pop_help(void)
139{
9ee7055c 140 dbprintf(_(
2bd0ea18
NS
141"\n"
142" Changes the address and data type to the first entry on the stack.\n"
143"\n"
9ee7055c 144 ));
2bd0ea18
NS
145}
146
147void
148print_iocur(
149 char *tag,
150 iocur_t *ioc)
151{
152 int i;
153
154 dbprintf("%s\n", tag);
9ee7055c
AM
155 dbprintf(_("\tbyte offset %lld, length %d\n"), ioc->off, ioc->len);
156 dbprintf(_("\tbuffer block %lld (fsbno %lld), %d bb%s\n"), ioc->bb,
5a35bf2c 157 (xfs_fsblock_t)XFS_DADDR_TO_FSB(mp, ioc->bb), ioc->blen,
2bd0ea18 158 ioc->blen == 1 ? "" : "s");
72298d16 159 if (ioc->bbmap) {
9ee7055c 160 dbprintf(_("\tblock map"));
72298d16
DC
161 for (i = 0; i < ioc->bbmap->nmaps; i++)
162 dbprintf(" %lld:%d", ioc->bbmap->b[i].bm_bn,
163 ioc->bbmap->b[i].bm_len);
2bd0ea18
NS
164 dbprintf("\n");
165 }
9ee7055c
AM
166 dbprintf(_("\tinode %lld, dir inode %lld, type %s\n"), ioc->ino,
167 ioc->dirino, ioc->typ == NULL ? _("none") : ioc->typ->name);
2bd0ea18
NS
168}
169
170void
171print_ring(void)
172{
173 int i;
174 iocur_t *ioc;
175
176 if (ring_current == -1) {
9ee7055c 177 dbprintf(_("no entries in location ring.\n"));
2bd0ea18
NS
178 return;
179 }
180
9ee7055c 181 dbprintf(_(" type bblock bblen fsbno inode\n"));
2bd0ea18
NS
182
183 i = ring_head;
184 for (;;) {
185 ioc = &iocur_ring[i];
186 if (i == ring_current)
187 printf("*%2d: ", i);
188 else
189 printf(" %2d: ", i);
190
191 dbprintf("%-7.7s %8lld %5d %8lld %9lld\n",
192 ioc->typ == NULL ? "none" : ioc->typ->name,
193 ioc->bb,
194 ioc->blen,
5a35bf2c 195 (xfs_fsblock_t)XFS_DADDR_TO_FSB(mp, ioc->bb),
2bd0ea18
NS
196 ioc->ino
197 );
198
199 if (i == ring_tail)
200 break;
201
202 i = (i+(RING_ENTRIES-1))%RING_ENTRIES;
203 }
204}
205
206
207void
208push_cur(void)
209{
210 if (iocur_sp + 1 >= iocur_len) {
211 iocur_base = xrealloc(iocur_base,
212 sizeof(*iocur_base) * (iocur_len + 1));
213 iocur_len++;
214 }
215 iocur_sp++;
216 iocur_top = iocur_base + iocur_sp;
217 memset(iocur_top, 0, sizeof(*iocur_base));
218 iocur_top->ino = iocur_sp > 0 ? iocur_top[-1].ino : NULLFSINO;
219 iocur_top->dirino = iocur_sp > 0 ? iocur_top[-1].dirino : NULLFSINO;
220 iocur_top->mode = iocur_sp > 0 ? iocur_top[-1].mode : 0;
221 cur_typ = NULL;
222}
223
3540b418
DW
224void
225push_cur_and_set_type(void)
226{
227 /* save current state */
228 push_cur();
229 if (iocur_top[-1].typ && iocur_top[-1].typ->typnm == TYP_INODE)
230 set_cur_inode(iocur_top[-1].ino);
231 else
232 set_cur(iocur_top[-1].typ, iocur_top[-1].bb,
233 iocur_top[-1].blen, DB_RING_IGN,
234 iocur_top[-1].bbmap);
235}
236
2bd0ea18
NS
237static int
238push_f(
239 int argc,
240 char **argv)
241{
242 const cmdinfo_t *ct;
243
244 if (argc > 1) {
dfc130f3
RC
245 /* check we can execute command */
246 ct = find_command(argv[1]);
247 if (ct == NULL) {
9ee7055c 248 dbprintf(_("no such command %s\n"), argv[1]);
dfc130f3
RC
249 return 0;
250 }
251 if (!ct->canpush) {
9ee7055c 252 dbprintf(_("no push form allowed for %s\n"), argv[1]);
dfc130f3
RC
253 return 0;
254 }
255 }
256
3540b418 257 push_cur_and_set_type();
2bd0ea18 258
dfc130f3
RC
259 /* run requested command */
260 if (argc>1)
261 (void)command(argc-1, argv+1);
2bd0ea18
NS
262 return 0;
263}
264
265static void
266push_help(void)
267{
9ee7055c 268 dbprintf(_(
2bd0ea18
NS
269"\n"
270" Allows you to push the current address and data type on the stack for\n"
271" later return. 'push' also accepts an additional command to execute after\n"
272" storing the current address (ex: 'push a rootino' from the superblock).\n"
273"\n"
9ee7055c 274 ));
2bd0ea18
NS
275}
276
277/* move forward through the ring */
278/* ARGSUSED */
279static int
280forward_f(
281 int argc,
282 char **argv)
283{
284 if (ring_current == -1) {
9ee7055c 285 dbprintf(_("ring is empty\n"));
2bd0ea18
NS
286 return 0;
287 }
288 if (ring_current == ring_head) {
9ee7055c 289 dbprintf(_("no further entries\n"));
2bd0ea18
NS
290 return 0;
291 }
292
293 ring_current = (ring_current+1)%RING_ENTRIES;
294
295 set_cur(iocur_ring[ring_current].typ,
296 iocur_ring[ring_current].bb,
297 iocur_ring[ring_current].blen,
298 DB_RING_IGN,
72298d16 299 iocur_ring[ring_current].bbmap);
2bd0ea18
NS
300
301 return 0;
302}
303
304static void
305forward_help(void)
306{
9ee7055c 307 dbprintf(_(
2bd0ea18
NS
308"\n"
309" The 'forward' ('f') command moves to the next location in the position\n"
310" ring, updating the current position and data type. If the current location\n"
311" is the top entry in the ring, then the 'forward' command will have\n"
312" no effect.\n"
313"\n"
9ee7055c 314 ));
2bd0ea18
NS
315}
316
317/* move backwards through the ring */
318/* ARGSUSED */
319static int
320back_f(
321 int argc,
322 char **argv)
323{
324 if (ring_current == -1) {
9ee7055c 325 dbprintf(_("ring is empty\n"));
2bd0ea18
NS
326 return 0;
327 }
328 if (ring_current == ring_tail) {
9ee7055c 329 dbprintf(_("no previous entries\n"));
2bd0ea18
NS
330 return 0;
331 }
332
333 ring_current = (ring_current+(RING_ENTRIES-1))%RING_ENTRIES;
334
335 set_cur(iocur_ring[ring_current].typ,
336 iocur_ring[ring_current].bb,
337 iocur_ring[ring_current].blen,
338 DB_RING_IGN,
72298d16 339 iocur_ring[ring_current].bbmap);
2bd0ea18
NS
340
341 return 0;
342}
343
344static void
345back_help(void)
346{
9ee7055c 347 dbprintf(_(
2bd0ea18
NS
348"\n"
349" The 'back' ('b') command moves to the previous location in the position\n"
350" ring, updating the current position and data type. If the current location\n"
351" is the last entry in the ring, then the 'back' command will have no effect.\n"
352"\n"
9ee7055c 353 ));
2bd0ea18
NS
354}
355
356/* show or go to specific point in ring */
357static int
358ring_f(
359 int argc,
360 char **argv)
361{
362 int index;
363
364 if (argc == 1) {
365 print_ring();
366 return 0;
367 }
368
b3d6b800 369 index = (int)strtoul(argv[1], NULL, 0);
d15f7cfb 370 if (index < 0 || index >= RING_ENTRIES) {
9ee7055c 371 dbprintf(_("invalid entry: %d\n"), index);
d15f7cfb
ES
372 return 0;
373 }
2bd0ea18
NS
374
375 ring_current = index;
376
377 set_cur(iocur_ring[index].typ,
378 iocur_ring[index].bb,
379 iocur_ring[index].blen,
380 DB_RING_IGN,
72298d16 381 iocur_ring[index].bbmap);
2bd0ea18
NS
382
383 return 0;
384}
385
386static void
387ring_help(void)
388{
9ee7055c 389 dbprintf(_(
2bd0ea18
NS
390"\n"
391" The position ring automatically keeps track of each disk location and\n"
392" structure type for each change of position you make during your xfs_db\n"
393" session. The last %d most recent entries are kept in the ring.\n"
394"\n"
395" To display the current list of ring entries type 'ring' by itself on\n"
396" the command line. The entry highlighted by an asterisk ('*') is the\n"
397" current entry.\n"
398"\n"
399" To move to another entry in the ring type 'ring <num>' where <num> is\n"
400" your desired entry from the ring position list.\n"
401"\n"
402" You may also use the 'forward' ('f') or 'back' ('b') commands to move\n"
403" to the previous or next entry in the ring, respectively.\n"
404"\n"
405" Note: Unlike the 'stack', 'push' and 'pop' commands, the ring tracks your\n"
406" location implicitly. Use the 'push' and 'pop' commands if you wish to\n"
407" store a specific location explicitly for later return.\n"
9ee7055c 408"\n"),
2bd0ea18
NS
409 RING_ENTRIES);
410}
411
412
413void
414ring_add(void)
415{
416 if (ring_head == -1) {
417 /* only get here right after startup */
418 ring_head = 0;
419 ring_tail = 0;
420 ring_current = 0;
421 iocur_ring[0] = *iocur_top;
422 } else {
423 if (ring_current == ring_head) {
424 ring_head = (ring_head+1)%RING_ENTRIES;
425 iocur_ring[ring_head] = *iocur_top;
426 if (ring_head == ring_tail)
427 ring_tail = (ring_tail+1)%RING_ENTRIES;
428 ring_current = ring_head;
429 } else {
430 ring_current = (ring_current+1)%RING_ENTRIES;
431 iocur_ring[ring_current] = *iocur_top;
432 }
433 }
434}
435
2a8b3fdf
DC
436static void
437write_cur_buf(void)
438{
439 int ret;
440
12b53197 441 ret = -libxfs_writebufr(iocur_top->bp);
72298d16 442 if (ret != 0)
2a8b3fdf
DC
443 dbprintf(_("write error: %s\n"), strerror(ret));
444
445 /* re-read buffer from disk */
12b53197 446 ret = -libxfs_readbufr(mp->m_ddev_targp, iocur_top->bb, iocur_top->bp,
72298d16
DC
447 iocur_top->blen, 0);
448 if (ret != 0)
2a8b3fdf
DC
449 dbprintf(_("read error: %s\n"), strerror(ret));
450}
451
2a8b3fdf
DC
452static void
453write_cur_bbs(void)
2bd0ea18
NS
454{
455 int ret;
456
12b53197 457 ret = -libxfs_writebufr(iocur_top->bp);
72298d16 458 if (ret != 0)
9ee7055c 459 dbprintf(_("write error: %s\n"), strerror(ret));
2a8b3fdf 460
72298d16 461
2bd0ea18 462 /* re-read buffer from disk */
12b53197 463 ret = -libxfs_readbufr_map(mp->m_ddev_targp, iocur_top->bp, 0);
72298d16 464 if (ret != 0)
9ee7055c 465 dbprintf(_("read error: %s\n"), strerror(ret));
2bd0ea18
NS
466}
467
c9f5e3db
ES
468void
469xfs_dummy_verify(
470 struct xfs_buf *bp)
471{
472 return;
473}
474
86769b32
DC
475void
476xfs_verify_recalc_crc(
477 struct xfs_buf *bp)
478{
479 xfs_buf_update_cksum(bp, iocur_top->typ->crc_off);
480}
481
2a8b3fdf
DC
482void
483write_cur(void)
484{
b64af2c4
ES
485 bool skip_crc = false;
486
2a8b3fdf
DC
487 if (iocur_sp < 0) {
488 dbprintf(_("nothing to write\n"));
489 return;
490 }
491
b64af2c4
ES
492 if (!xfs_sb_version_hascrc(&mp->m_sb) ||
493 !iocur_top->bp->b_ops ||
494 iocur_top->bp->b_ops->verify_write == xfs_dummy_verify)
495 skip_crc = true;
496
497 if (!skip_crc) {
1e9c13c5
ES
498 if (iocur_top->ino_buf)
499 xfs_inode_set_crc(iocur_top->bp);
500 else if (iocur_top->dquot_buf)
501 xfs_dquot_set_crc(iocur_top->bp);
b335ecb5 502 }
72298d16 503 if (iocur_top->bbmap)
2a8b3fdf
DC
504 write_cur_bbs();
505 else
506 write_cur_buf();
b64af2c4
ES
507
508 /* If we didn't write the crc automatically, re-check inode validity */
509 if (xfs_sb_version_hascrc(&mp->m_sb) &&
510 skip_crc && iocur_top->ino_buf) {
3c73a9a1 511 iocur_top->ino_crc_ok = libxfs_verify_cksum(iocur_top->data,
b64af2c4
ES
512 mp->m_sb.sb_inodesize,
513 XFS_DINODE_CRC_OFF);
514 }
515
2a8b3fdf
DC
516}
517
2bd0ea18
NS
518void
519set_cur(
db23e0f4
BD
520 const typ_t *type,
521 xfs_daddr_t blknum,
522 int len,
b64af2c4 523 int ring_flag,
2bd0ea18
NS
524 bbmap_t *bbmap)
525{
72298d16 526 struct xfs_buf *bp;
2bd0ea18
NS
527 xfs_ino_t dirino;
528 xfs_ino_t ino;
14f8b681 529 uint16_t mode;
db23e0f4 530 const struct xfs_buf_ops *ops = type ? type->bops : NULL;
2bd0ea18
NS
531
532 if (iocur_sp < 0) {
9ee7055c 533 dbprintf(_("set_cur no stack element to set\n"));
2bd0ea18
NS
534 return;
535 }
536
2bd0ea18
NS
537 ino = iocur_top->ino;
538 dirino = iocur_top->dirino;
539 mode = iocur_top->mode;
540 pop_cur();
541 push_cur();
2a8b3fdf
DC
542
543 if (bbmap) {
857592d2 544#ifdef DEBUG_BBMAP
72298d16 545 int i;
db23e0f4 546 printf(_("xfs_db got a bbmap for %lld\n"), (long long)blknum);
72298d16
DC
547 printf(_("\tblock map"));
548 for (i = 0; i < bbmap->nmaps; i++)
549 printf(" %lld:%d", (long long)bbmap->b[i].bm_bn,
550 bbmap->b[i].bm_len);
551 printf("\n");
2a8b3fdf 552#endif
72298d16
DC
553 iocur_top->bbmap = malloc(sizeof(struct bbmap));
554 if (!iocur_top->bbmap)
2a8b3fdf 555 return;
72298d16
DC
556 memcpy(iocur_top->bbmap, bbmap, sizeof(struct bbmap));
557 bp = libxfs_readbuf_map(mp->m_ddev_targp, bbmap->b,
6fea8f83 558 bbmap->nmaps, 0, ops);
2a8b3fdf 559 } else {
db23e0f4 560 bp = libxfs_readbuf(mp->m_ddev_targp, blknum, len, 0, ops);
72298d16 561 iocur_top->bbmap = NULL;
2a8b3fdf 562 }
6fea8f83
DC
563
564 /*
9c0a86ed 565 * Keep the buffer even if the verifier says it is corrupted.
6fea8f83
DC
566 * We're a diagnostic tool, after all.
567 */
66fc04e0
DW
568 if (!bp || (bp->b_error && bp->b_error != -EFSCORRUPTED &&
569 bp->b_error != -EFSBADCRC))
72298d16
DC
570 return;
571 iocur_top->buf = bp->b_addr;
572 iocur_top->bp = bp;
b511ff41
DC
573 if (!ops)
574 bp->b_flags |= LIBXFS_B_UNCHECKED;
2a8b3fdf 575
db23e0f4
BD
576 iocur_top->bb = blknum;
577 iocur_top->blen = len;
2bd0ea18
NS
578 iocur_top->boff = 0;
579 iocur_top->data = iocur_top->buf;
db23e0f4
BD
580 iocur_top->len = BBTOB(len);
581 iocur_top->off = blknum << BBSHIFT;
582 iocur_top->typ = cur_typ = type;
2bd0ea18
NS
583 iocur_top->ino = ino;
584 iocur_top->dirino = dirino;
585 iocur_top->mode = mode;
a73b88f2 586 iocur_top->ino_buf = 0;
66a40d02 587 iocur_top->dquot_buf = 0;
2bd0ea18
NS
588
589 /* store location in ring */
590 if (ring_flag)
591 ring_add();
592}
593
9ba69ce2
DC
594void
595set_iocur_type(
db23e0f4 596 const typ_t *type)
9ba69ce2
DC
597{
598 struct xfs_buf *bp = iocur_top->bp;
599
533d1d22
ES
600 /* Inodes are special; verifier checks all inodes in the chunk */
601 if (type->typnm == TYP_INODE) {
602 xfs_daddr_t b = iocur_top->bb;
603 xfs_ino_t ino;
604
605 /*
606 * Note that this will back up to the beginning of the inode
607 * which contains the current disk location; daddr may change.
608 */
609 ino = XFS_AGINO_TO_INO(mp, xfs_daddr_to_agno(mp, b),
610 ((b << BBSHIFT) >> mp->m_sb.sb_inodelog) %
611 (mp->m_sb.sb_agblocks << mp->m_sb.sb_inopblog));
612 set_cur_inode(ino);
613 return;
614 }
615
55f224ba 616 /* adjust buffer size for types with fields & hence fsize() */
db23e0f4 617 if (type->fields) {
55f224ba
BD
618 int bb_count; /* type's size in basic blocks */
619
db23e0f4
BD
620 bb_count = BTOBB(byteize(fsize(type->fields,
621 iocur_top->data, 0, 0)));
622 set_cur(type, iocur_top->bb, bb_count, DB_RING_IGN, NULL);
55f224ba 623 }
db23e0f4 624 iocur_top->typ = type;
9ba69ce2
DC
625
626 /* verify the buffer if the type has one. */
627 if (!bp)
628 return;
db23e0f4 629 if (!type->bops) {
9ba69ce2
DC
630 bp->b_ops = NULL;
631 bp->b_flags |= LIBXFS_B_UNCHECKED;
632 return;
633 }
634 if (!(bp->b_flags & LIBXFS_B_UPTODATE))
635 return;
636 bp->b_error = 0;
db23e0f4 637 bp->b_ops = type->bops;
9ba69ce2
DC
638 bp->b_ops->verify_read(bp);
639 bp->b_flags &= ~LIBXFS_B_UNCHECKED;
640}
641
2bd0ea18
NS
642static void
643stack_help(void)
644{
9ee7055c 645 dbprintf(_(
2bd0ea18
NS
646"\n"
647" The stack is used to explicitly store your location and data type\n"
648" for later return. The 'push' operation stores the current address\n"
649" and type on the stack, the 'pop' operation returns you to the\n"
650" position and datatype of the top entry on the stack.\n"
651"\n"
652" The 'stack' allows explicit location saves, see 'ring' for implicit\n"
653" position tracking.\n"
654"\n"
9ee7055c 655 ));
2bd0ea18
NS
656}
657
658/*ARGSUSED*/
659static int
660stack_f(
661 int argc,
662 char **argv)
663{
664 int i;
665 char tagbuf[8];
666
2d9475a4
NS
667 for (i = iocur_sp; i > 0; i--) {
668 snprintf(tagbuf, sizeof(tagbuf), "%d: ", i);
2bd0ea18
NS
669 print_iocur(tagbuf, &iocur_base[i]);
670 }
671 return 0;
672}