Add new debugfs commands and arguments which make it easier to 
recover deleted files.  The lsdel command now takes an optional
argument which allows the user to only see the most recently
deleted files.  Also added a new command, undel, which automates
undeleting a deleted inode and linking it back to a directory.
Also added an optional count argument to the testb, freeb, setb,
and find_free_block commands.  The ls command now takes a new
option, -d, which lists deleted directory entries.

Factored out out commonly used code into utility subroutines
for ease of maintenance and to make the executable size smaller.

diff --git a/debugfs/ChangeLog b/debugfs/ChangeLog
index 8cdee82..82149ea 100644
--- a/debugfs/ChangeLog
+++ b/debugfs/ChangeLog
@@ -1,3 +1,29 @@
+2002-01-03  Theodore Tso  <tytso@valinux.com>
+
+	* lsdel.c (do_lsdel): New optional argument which allows the user
+		to only see the most recently deleted files.
+
+	* debugfs.c (do_undel, do_testb, do_freeb, do_setb, do_ffb): Add
+		new command, undelete, which automates undeleting a
+		deleted inode and linking it back to a directory.  Add a
+		count argument to the testb, freeb, setb, and ffb commands.
+
+	* ls.c (list_dir_proc, do_list_dir): Add support for -d option
+		which lists deleted directory entries.
+
+	* debug_cmds.ct: Add new command, undelete.
+
+	* dump.c, icheck.c, logdump.c, ls.c, lsdel.c, setsuper.c,
+		debugfs.c: Use new utility functions which factor out
+		commonly used code.
+
+	* util.c (debugfs_read_inode, debugfs_write_inode,
+		common_block_args_process, common_inode_args_process,
+		common_args_process, strtoblk, parse_ulong): New
+		functions which factor out commonly used code into
+		subroutines for ease of maintenance and to make the
+		executable size smaller.
+
 2001-12-23  Theodore Tso  <tytso@valinux.com>
 
 	* Makefile.in, jfs_user.h: Move linux/jbd.h to
diff --git a/debugfs/debug_cmds.ct b/debugfs/debug_cmds.ct
index 4cd034f..63f9c0a 100644
--- a/debugfs/debug_cmds.ct
+++ b/debugfs/debug_cmds.ct
@@ -103,6 +103,9 @@
 request do_lsdel, "List deleted inodes",
 	list_deleted_inodes, lsdel;
 
+request do_undel, "Undelete file",
+	undelete, undel;
+
 request do_write, "Copy a file from your native filesystem",
 	write;
 
diff --git a/debugfs/debugfs.8.in b/debugfs/debugfs.8.in
index 77a893a..231a3e3 100644
--- a/debugfs/debugfs.8.in
+++ b/debugfs/debugfs.8.in
@@ -185,8 +185,10 @@
 or clearing any filesystem features that were requested, print the current
 state of the filesystem feature set.
 .TP
-.I find_free_block [goal]
-Find the first free block, starting from
+.I find_free_block [count [goal]]
+Find the first 
+.I count
+free blocks, starting from
 .I goal
 and allocate it.
 .TP
@@ -200,10 +202,17 @@
 specifies the permissions of the new inode.  (If the directory bit is set
 on the mode, the allocation routine will function differently.)
 .TP
-.I freeb block
+.I freeb block [count]
 Mark the block number
 .I block
 as not allocated.
+If the optional argument 
+.I count 
+is present, then 
+.I count
+blocks starting at block number
+.I block
+will be marked as not allocated.
 .TP
 .I freei filespec
 Free the inode specified by 
@@ -253,9 +262,15 @@
 .I logdump [-ac] [-b<block>] [-i<inode>] [-f<journal_file>] [output_file]
 Dump the contents of the ext3 journal.
 .TP
-.I ls [-l] filespec
+.I ls [-l] [-d] filespec
 Print a listing of the files in the directory
 .IR filespec .
+The 
+.I \-l
+flag will list files using a more verbose format.
+The
+.I \-d
+flag will list deleted entries in the directory.
 .TP
 .I modify_inode filespec
 Modify the contents of the inode structure in the inode
@@ -318,10 +333,17 @@
 .IR filespec .
 This function is currently not implemented.
 .TP
-.I setb block
+.I setb block [count]
 Mark the block number
 .I block
 as allocated.
+If the optional argument 
+.I count 
+is present, then 
+.I count
+blocks starting at block number
+.I block
+will be marked as allocated.
 .TP
 .I seti filespec
 Mark inode 
@@ -346,10 +368,17 @@
 Display the contents of the inode structure of the inode
 .IR filespec .
 .TP
-.I testb block
+.I testb block [count]
 Test if the block number
 .I block
 is marked as allocated in the block bitmap.
+If the optional argument 
+.I count 
+is present, then 
+.I count
+blocks starting at block number
+.I block
+will be tested.
 .TP
 .I testi filespec
 Test if the inode 
diff --git a/debugfs/debugfs.c b/debugfs/debugfs.c
index ebd02f2..df08610 100644
--- a/debugfs/debugfs.c
+++ b/debugfs/debugfs.c
@@ -95,11 +95,10 @@
 void do_open_filesys(int argc, char **argv)
 {
 	const char *usage = "Usage: open [-s superblock] [-b blocksize] [-c] [-w] <device>";
-	int	c;
+	int	c, err;
 	int	catastrophic = 0;
 	blk_t	superblock = 0;
 	blk_t	blocksize = 0;
-	char	*tmp;
 	int open_flags = 0;
 	
 	optind = 0;
@@ -121,20 +120,16 @@
 			catastrophic = 1;
 			break;
 		case 'b':
-			blocksize = strtoul(optarg, &tmp, 0);
-			if (*tmp) {
-				com_err(argv[0], 0,
-					"Bad block size - %s", optarg);
+			blocksize = parse_ulong(optarg, argv[0],
+						"block size", &err);
+			if (err)
 				return;
-			}
 			break;
 		case 's':
-			superblock = strtoul(optarg, &tmp, 0);
-			if (*tmp) {
-				com_err(argv[0], 0,
-					"Bad superblock number - %s", optarg);
+			superblock = parse_ulong(optarg, argv[0],
+						 "superblock number", &err);
+			if (err)
 				return;
-			}
 			break;
 		default:
 			com_err(argv[0], 0, usage);
@@ -153,12 +148,9 @@
 
 void do_lcd(int argc, char **argv)
 {
-	const char *usage = "Usage: lcd <native dir>";
-
-	if (argc != 2) {
-		com_err(argv[0], 0, usage);
+	if (common_args_process(argc, argv, 2, 2, "lcd",
+				"<native dir>", 0))
 		return;
-	}
 
 	if (chdir(argv[1]) == -1) {
 		com_err(argv[0], errno,
@@ -191,35 +183,26 @@
 
 void do_close_filesys(int argc, char **argv)
 {
-	if (argc > 1) {
-		com_err(argv[0], 0, "Usage: close_filesys");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 1, 1, "close_filesys", "", 0))
 		return;
 	close_filesystem();
 }
 
 void do_init_filesys(int argc, char **argv)
 {
-	const char	*usage = "Usage: initialize <device> <blocksize>";
 	struct ext2_super_block param;
 	errcode_t	retval;
-	char		*tmp;
+	int		err;
 	
-	if (argc != 3) {
-		com_err(argv[0], 0, usage);
-		return;
-	}
-	if (check_fs_not_open(argv[0]))
+	if (common_args_process(argc, argv, 3, 3, "initialize",
+				"<device> <blocksize>", CHECK_FS_NOTOPEN))
 		return;
 
 	memset(&param, 0, sizeof(struct ext2_super_block));
-	param.s_blocks_count = strtoul(argv[2], &tmp, 0);
-	if (*tmp) {
-		com_err(argv[0], 0, "Bad blocks count - %s", argv[2]);
+	param.s_blocks_count = parse_ulong(argv[0], argv[2],
+					   "blocks count", &err);
+	if (err)
 		return;
-	}
 	retval = ext2fs_initialize(argv[1], 0, &param,
 				   unix_io_manager, &current_fs);
 	if (retval) {
@@ -491,29 +474,17 @@
 	close_pager(out);
 }
 
-
 void do_stat(int argc, char *argv[])
 {
 	ext2_ino_t	inode;
 	struct ext2_inode inode_buf;
 	int 		retval;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: stat <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, 0))
 		return;
 
-	retval = ext2fs_read_inode(current_fs, inode, &inode_buf);
-	if (retval) 
-	  {
-	    com_err(argv[0], retval, "while trying to read inode %d", inode);
-	    return;
-	  }
+	if (debugfs_read_inode(inode, &inode_buf, argv[0]))
+		return;
 
 	dump_inode(inode,inode_buf);
 	return;
@@ -524,14 +495,7 @@
 	ext2_ino_t inode;
 	int retval;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: chroot <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, 0))
 		return;
 
 	retval = ext2fs_check_directory(current_fs, inode);
@@ -548,47 +512,22 @@
 	int retval;
 	struct ext2_inode inode_buf;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: clri <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_read_write(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
 		return;
 
-	retval = ext2fs_read_inode(current_fs, inode, &inode_buf);
-	if (retval) {
-		com_err(argv[0], retval, "while trying to read inode %d", 
-			inode);
+	if (debugfs_read_inode(inode, &inode_buf, argv[0]))
 		return;
-	}
 	memset(&inode_buf, 0, sizeof(inode_buf));
-	retval = ext2fs_write_inode(current_fs, inode, &inode_buf);
-	if (retval) {
-		com_err(argv[0], retval, "while trying to write inode %d",
-			inode);
+	if (debugfs_write_inode(inode, &inode_buf, argv[0]))
 		return;
-	}
 }
 
 void do_freei(int argc, char *argv[])
 {
 	ext2_ino_t inode;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: freei <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_read_write(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode,
+				      CHECK_FS_RW | CHECK_FS_BITMAPS))
 		return;
 
 	if (!ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@@ -601,16 +540,8 @@
 {
 	ext2_ino_t inode;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: seti <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_read_write(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode,
+				      CHECK_FS_RW | CHECK_FS_BITMAPS))
 		return;
 
 	if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@@ -623,16 +554,7 @@
 {
 	ext2_ino_t inode;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: testi <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_bitmaps(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, CHECK_FS_BITMAPS))
 		return;
 
 	if (ext2fs_test_inode_bitmap(current_fs->inode_map,inode))
@@ -641,76 +563,58 @@
 		printf("Inode %u is not in use\n", inode);
 }
 
-
 void do_freeb(int argc, char *argv[])
 {
 	blk_t block;
-	char *tmp;
+	int count = 1;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: freeb <block>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_block_args_process(argc, argv, &block, &count))
 		return;
 	if (check_fs_read_write(argv[0]))
 		return;
-	block = strtoul(argv[1], &tmp, 0);
-	if (!block || *tmp) {
-		com_err(argv[0], 0, "No block 0");
-		return;
-	} 
-	if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
-		com_err(argv[0], 0, "Warning: block already clear");
-	ext2fs_unmark_block_bitmap(current_fs->block_map,block);
+	while (count-- > 0) {
+		if (!ext2fs_test_block_bitmap(current_fs->block_map,block))
+			com_err(argv[0], 0, "Warning: block %d already clear",
+				block);
+		ext2fs_unmark_block_bitmap(current_fs->block_map,block);
+		block++;
+	}
 	ext2fs_mark_bb_dirty(current_fs);
 }
 
 void do_setb(int argc, char *argv[])
 {
 	blk_t block;
-	char *tmp;
+	int count = 1;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: setb <block>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_block_args_process(argc, argv, &block, &count))
 		return;
 	if (check_fs_read_write(argv[0]))
 		return;
-	block = strtoul(argv[1], &tmp, 0);
-	if (!block || *tmp) {
-		com_err(argv[0], 0, "No block 0");
-		return;
-	} 
-	if (ext2fs_test_block_bitmap(current_fs->block_map,block))
-		com_err(argv[0], 0, "Warning: block already set");
-	ext2fs_mark_block_bitmap(current_fs->block_map,block);
+	while (count-- > 0) {
+		if (ext2fs_test_block_bitmap(current_fs->block_map,block))
+			com_err(argv[0], 0, "Warning: block %d already set",
+				block);
+		ext2fs_mark_block_bitmap(current_fs->block_map,block);
+		block++;
+	}
 	ext2fs_mark_bb_dirty(current_fs);
 }
 
 void do_testb(int argc, char *argv[])
 {
 	blk_t block;
-	char *tmp;
+	int count = 1;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: testb <block>");
+	if (common_block_args_process(argc, argv, &block, &count))
 		return;
+	while (count-- > 0) {
+		if (ext2fs_test_block_bitmap(current_fs->block_map,block))
+			printf("Block %d marked in use\n", block);
+		else
+			printf("Block %d not in use\n", block);
+		block++;
 	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_bitmaps(argv[0]))
-		return;
-	block = strtoul(argv[1], &tmp, 0);
-	if (!block || *tmp) {
-		com_err(argv[0], 0, "No block 0");
-		return;
-	} 
-	if (ext2fs_test_block_bitmap(current_fs->block_map,block))
-		printf("Block %d marked in use\n", block);
-	else printf("Block %d not in use\n", block);
 }
 
 static void modify_u8(char *com, const char *prompt,
@@ -790,25 +694,11 @@
 	const char	*octal_format = "0%o";
 	const char	*decimal_format = "%d";
 	
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: modify_inode <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	if (check_fs_read_write(argv[0]))
+	if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
 		return;
 
-	inode_num = string_to_inode(argv[1]);
-	if (!inode_num) 
+	if (debugfs_read_inode(inode_num, &inode, argv[1]))
 		return;
-
-	retval = ext2fs_read_inode(current_fs, inode_num, &inode);
-	if (retval) {
-		com_err(argv[1], retval, "while trying to read inode %d",
-			inode_num);
-		return;
-	}
 	
 	modify_u16(argv[0], "Mode", octal_format, &inode.i_mode);
 	modify_u16(argv[0], "User ID", decimal_format, &inode.i_uid);
@@ -867,12 +757,8 @@
 		    &inode.i_block[EXT2_DIND_BLOCK]);
 	modify_u32(argv[0], "Triple Indirect Block", decimal_format,
 		    &inode.i_block[EXT2_TIND_BLOCK]);
-	retval = ext2fs_write_inode(current_fs, inode_num, &inode);
-	if (retval) {
-		com_err(argv[1], retval, "while trying to write inode %d",
-			inode_num);
+	if (debugfs_write_inode(inode_num, &inode, argv[1]))
 		return;
-	}
 }
 
 void do_change_working_dir(int argc, char *argv[])
@@ -880,15 +766,7 @@
 	ext2_ino_t	inode;
 	int		retval;
 	
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: cd <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, 0))
 		return;
 
 	retval = ext2fs_check_directory(current_fs, inode);
@@ -905,11 +783,8 @@
 	int	retval;
 	char	*pathname = NULL;
 	
-	if (argc > 1) {
-		com_err(argv[0], 0, "Usage: print_working_directory");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 1, 1,
+				"print_working_directory", "", 0))
 		return;
 
 	retval = ext2fs_get_pathname(current_fs, cwd, 0, &pathname);
@@ -980,13 +855,63 @@
 
 void do_link(int argc, char *argv[])
 {
-	if (argc != 3) {
-		com_err(argv[0], 0, "Usage: link <source_file> <dest_name>");
+	if (common_args_process(argc, argv, 3, 3, "link",
+				"<source file> <dest_name>", CHECK_FS_RW))
+		return;
+
+	make_link(argv[1], argv[2]);
+}
+
+static int mark_blocks_proc(ext2_filsys fs, blk_t *blocknr,
+			    int blockcnt, void *private)
+{
+	blk_t	block;
+	dgrp_t	group;
+
+	block = *blocknr;
+	ext2fs_block_alloc_stats(fs, block, +1);
+	return 0;
+}
+
+void do_undel(int argc, char *argv[])
+{
+	ext2_ino_t	ino;
+	struct ext2_inode inode;
+	int		retval;
+	dgrp_t group;
+
+	if (common_args_process(argc, argv, 3, 3, "undelete",
+				"<inode_num> <dest_name>",
+				CHECK_FS_RW | CHECK_FS_BITMAPS))
+		return;
+
+	ino = string_to_inode(argv[1]);
+	if (!ino)
+		return;
+
+	if (debugfs_read_inode(ino, &inode, argv[1]))
+		return;
+
+	if (ext2fs_test_inode_bitmap(current_fs->inode_map, ino)) {
+		com_err(argv[1], 0, "Inode is not marked as deleted");
 		return;
 	}
-	if (check_fs_open(argv[0]))
+
+	/*
+	 * XXX this function doesn't handle changing the links count on the
+	 * parent directory when undeleting a directory.  
+	 */
+	inode.i_links_count = LINUX_S_ISDIR(inode.i_mode) ? 2 : 1;
+	inode.i_dtime = 0;
+
+	if (debugfs_write_inode(ino, &inode, argv[0]))
 		return;
 
+	ext2fs_block_iterate(current_fs, ino, 0, NULL,
+			     mark_blocks_proc, NULL);
+
+	ext2fs_inode_alloc_stats(current_fs, ino, +1);
+
 	make_link(argv[1], argv[2]);
 }
 
@@ -1014,11 +939,8 @@
 
 void do_unlink(int argc, char *argv[])
 {
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: unlink <pathname>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 2, 2, "link",
+				"<pathname>", CHECK_FS_RW))
 		return;
 
 	unlink_file_by_name(argv[1]);
@@ -1027,18 +949,28 @@
 void do_find_free_block(int argc, char *argv[])
 {
 	blk_t	free_blk, goal;
+ 	int		count;
 	errcode_t	retval;
 	char		*tmp;
 	
-	if ((argc > 2) || (argc==2 && *argv[1] == '?')) {
-		com_err(argv[0], 0, "Usage: find_free_block [goal]");
+	if ((argc > 3) || (argc==2 && *argv[1] == '?')) {
+		com_err(argv[0], 0, "Usage: find_free_block [count [goal]]");
 		return;
 	}
 	if (check_fs_open(argv[0]))
 		return;
 
 	if (argc > 1) {
-		goal = strtol(argv[1], &tmp, 0);
+		count = strtol(argv[1],&tmp,0);
+		if (*tmp) {
+			com_err(argv[0], 0, "Bad count - %s", argv[1]);
+			return;
+		}
+ 	} else
+		count = 1;
+
+	if (argc > 2) {
+		goal = strtol(argv[2], &tmp, 0);
 		if (*tmp) {
 			com_err(argv[0], 0, "Bad goal - %s", argv[1]);
 			return;
@@ -1047,12 +979,18 @@
 	else
 		goal = current_fs->super->s_first_data_block;
 
-	retval = ext2fs_new_block(current_fs, goal, 0, &free_blk);
-	if (retval)
-		com_err("ext2fs_new_block", retval, "");
-	else
-		printf("Free block found: %d\n", free_blk);
-
+	printf("Free blocks found: ");
+	free_blk = goal - 1;	
+	while (count-- > 0) {
+		retval = ext2fs_new_block(current_fs, free_blk + 1, 0,
+					  &free_blk);
+		if (retval) {
+			com_err("ext2fs_new_block", retval, "");
+			return;
+		} else
+			printf("%d ", free_blk);
+	}
+ 	printf("\n");
 }
 
 void do_find_free_inode(int argc, char *argv[])
@@ -1128,7 +1066,6 @@
 		}
 	}
 	retval = ext2fs_file_close(e2_file);
-
 	return retval;
 
 fail:
@@ -1146,17 +1083,13 @@
 	struct ext2_inode inode;
 	dgrp_t		group;
 
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 3, 3, "write",
+				"<native file> <new file>", CHECK_FS_RW))
 		return;
-	if (argc != 3) {
-		com_err(argv[0], 0, "Usage: write <nativefile> <newfile>");
-		return;
-	}
-	if (check_fs_read_write(argv[0]))
-		return;
+
 	fd = open(argv[1], O_RDONLY);
 	if (fd < 0) {
-		com_err(argv[1], fd, "");
+		com_err(argv[1], errno, "");
 		return;
 	}
 	if (fstat(fd, &statbuf) < 0) {
@@ -1171,10 +1104,6 @@
 		close(fd);
 		return;
 	}
-	group = ext2fs_group_of_ino(current_fs, newfile);
-	current_fs->group_desc[group].bg_free_inodes_count--;
-	current_fs->super->s_free_inodes_count--;
-	ext2fs_mark_super_dirty(current_fs);
 	printf("Allocated inode: %u\n", newfile);
 	retval = ext2fs_link(current_fs, cwd, argv[2], newfile,
 			     EXT2_FT_REG_FILE);
@@ -1185,17 +1114,13 @@
 	}
         if (ext2fs_test_inode_bitmap(current_fs->inode_map,newfile))
 		com_err(argv[0], 0, "Warning: inode already set");
-	ext2fs_mark_inode_bitmap(current_fs->inode_map,newfile);
-	ext2fs_mark_ib_dirty(current_fs);
+	ext2fs_inode_alloc_stats(current_fs, newfile, +1);
 	memset(&inode, 0, sizeof(inode));
 	inode.i_mode = statbuf.st_mode;
 	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
 	inode.i_links_count = 1;
 	inode.i_size = statbuf.st_size;
-	retval = ext2fs_write_inode(current_fs, newfile, &inode);
-	if (retval) {
-		com_err(argv[0], retval, "while trying to write inode %d", 
-			inode);
+	if (debugfs_write_inode(newfile, &inode, argv[0])) {
 		close(fd);
 		return;
 	}
@@ -1218,6 +1143,7 @@
 	if (check_fs_open(argv[0]))
 		return;
 	if (argc < 3 || argv[2][1]) {
+	usage:
 		com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
 		return;
 	}
@@ -1248,10 +1174,8 @@
 		if (major > 255 || minor > 255 || argv[3][0] || argv[4][0])
 			nr = 0;
 	}
-	if (argc != nr) {
-		com_err(argv[0], 0, "Usage: mknod <name> [p| [c|b] <major> <minor>]");
-		return;
-	}
+	if (argc != nr)
+		goto usage;
 	if (check_fs_read_write(argv[0]))
 		return;
 	retval = ext2fs_new_inode(current_fs, cwd, 010755, 0, &newfile);
@@ -1283,11 +1207,8 @@
 	inode.i_atime = inode.i_ctime = inode.i_mtime = time(NULL);
 	inode.i_block[0] = major*256+minor;
 	inode.i_links_count = 1;
-	retval = ext2fs_write_inode(current_fs, newfile, &inode);
-	if (retval) {
-		com_err(argv[0], retval, "while trying to write inode %d", inode);
+	if (debugfs_write_inode(newfile, &inode, argv[0]))
 		return;
-	}
 }
 
 void do_mkdir(int argc, char *argv[])
@@ -1297,15 +1218,8 @@
 	char	*name;
 	errcode_t retval;
 
-	if (check_fs_open(argv[0]))
-		return;
-
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: mkdir <file>");
-		return;
-	}
-
-	if (check_fs_read_write(argv[0]))
+	if (common_args_process(argc, argv, 2, 2, "mkdir",
+				"<filename>", CHECK_FS_RW))
 		return;
 
 	cp = strrchr(argv[1], '/');
@@ -1341,14 +1255,10 @@
 			       int blockcnt, void *private)
 {
 	blk_t	block;
-	int	group;
+	dgrp_t	group;
 
 	block = *blocknr;
-	printf("%d ", block);
-	ext2fs_unmark_block_bitmap(fs->block_map,block);
-	group = ext2fs_group_of_blk(fs, block);
-	fs->group_desc[group].bg_free_blocks_count++;
-	fs->super->s_free_blocks_count++;
+	ext2fs_block_alloc_stats(fs, block, -1);
 	return 0;
 }
 
@@ -1357,22 +1267,17 @@
 	struct ext2_inode inode_buf;
 	int	group;
 
-	ext2fs_read_inode(current_fs, inode, &inode_buf);
+	if (debugfs_read_inode(inode, &inode_buf, 0))
+		return;
 	inode_buf.i_dtime = time(NULL);
-	ext2fs_write_inode(current_fs, inode, &inode_buf);
+	if (debugfs_write_inode(inode, &inode_buf, 0))
+		return;
 
 	printf("Kill file by inode %u\n", inode);
 	ext2fs_block_iterate(current_fs, inode, 0, NULL,
 			     release_blocks_proc, NULL);
 	printf("\n");
-	ext2fs_unmark_inode_bitmap(current_fs->inode_map, inode);
-	group = ext2fs_group_of_ino(current_fs, inode);
-	current_fs->group_desc[group].bg_free_inodes_count++;
-	current_fs->super->s_free_inodes_count++;
-	
-	ext2fs_mark_super_dirty(current_fs);
-	ext2fs_mark_bb_dirty(current_fs);
-	ext2fs_mark_ib_dirty(current_fs);
+	ext2fs_inode_alloc_stats(current_fs, inode, -1);
 }
 
 
@@ -1380,21 +1285,9 @@
 {
 	ext2_ino_t inode_num;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: kill_file <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
+	if (common_inode_args_process(argc, argv, &inode_num, CHECK_FS_RW))
 		return;
 
-	if (check_fs_read_write(argv[0]))
-		return;
-
-	inode_num = string_to_inode(argv[1]);
-	if (!inode_num) {
-		com_err(argv[0], 0, "Cannot find file");
-		return;
-	}
 	kill_file_by_inode(inode_num);
 }
 
@@ -1404,14 +1297,8 @@
 	ext2_ino_t inode_num;
 	struct ext2_inode inode;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: rm <filename>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-
-	if (check_fs_read_write(argv[0]))
+	if (common_args_process(argc, argv, 2, 2, "rm",
+				"<filename>", CHECK_FS_RW))
 		return;
 
 	retval = ext2fs_namei(current_fs, root, cwd, argv[1], &inode_num);
@@ -1420,11 +1307,8 @@
 		return;
 	}
 
-	retval = ext2fs_read_inode(current_fs,inode_num,&inode);
-	if (retval) {
-		com_err(argv[0], retval, "while reading file's inode");
+	if (debugfs_read_inode(inode_num, &inode, argv[0]))
 		return;
-	}
 
 	if (LINUX_S_ISDIR(inode.i_mode)) {
 		com_err(argv[0], 0, "file is a directory");
@@ -1432,11 +1316,8 @@
 	}
 
 	--inode.i_links_count;
-	retval = ext2fs_write_inode(current_fs,inode_num,&inode);
-	if (retval) {
-		com_err(argv[0], retval, "while writing inode");
+	if (debugfs_write_inode(inode_num, &inode, argv[0]))
 		return;
-	}
 
 	unlink_file_by_name(argv[1]);
 	if (inode.i_links_count == 0)
@@ -1459,14 +1340,7 @@
 	ext2_ino_t inode;
 	int retval;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: expand_dir <file>");
-		return;
-	}
-	if (check_fs_open(argv[0]))
-		return;
-	inode = string_to_inode(argv[1]);
-	if (!inode)
+	if (common_inode_args_process(argc, argv, &inode, CHECK_FS_RW))
 		return;
 
 	retval = ext2fs_expand_dir(current_fs, inode);
@@ -1546,7 +1420,6 @@
 	blk_t		superblock = 0;
 	blk_t		blocksize = 0;
 	int		catastrophic = 0;
-	char		*tmp;
 	
 	initialize_ext2_error_table();
 	fprintf (stderr, "debugfs %s (%s)\n", E2FSPROGS_VERSION,
@@ -1567,20 +1440,12 @@
 			open_flags |= EXT2_FLAG_RW;
 			break;
 		case 'b':
-			blocksize = strtoul(optarg, &tmp, 0);
-			if (*tmp) {
-				com_err(argv[0], 0,
-					"Bad block size - %s", optarg);
-				return 1;
-			}
+			blocksize = parse_ulong(argv[0], optarg,
+						"block size", 0);
 			break;
 		case 's':
-			superblock = strtoul(optarg, &tmp, 0);
-			if (*tmp) {
-				com_err(argv[0], 0,
-					"Bad superblock number - %s", optarg);
-				return 1;
-			}
+			superblock = parse_ulong(argv[0], optarg,
+						 "superblock number", 0);
 			break;
 		case 'c':
 			catastrophic = 1;
diff --git a/debugfs/debugfs.h b/debugfs/debugfs.h
index 1cc308a..1634875 100644
--- a/debugfs/debugfs.h
+++ b/debugfs/debugfs.h
@@ -12,6 +12,13 @@
 #define const
 #endif
 
+/*
+ * Flags used by the common argument processing functions
+ */
+#define CHECK_FS_RW		0x0001
+#define CHECK_FS_BITMAPS	0x0002
+#define CHECK_FS_NOTOPEN	0x0004
+
 extern ext2_filsys current_fs;
 extern ext2_ino_t	root, cwd;
 
@@ -23,6 +30,20 @@
 extern int check_fs_bitmaps(char *name);
 extern ext2_ino_t string_to_inode(char *str);
 extern char *time_to_string(__u32);
+extern unsigned long parse_ulong(const char *str, const char *cmd,
+				 const char *descr, int *err);
+extern int strtoblk(const char *cmd, const char *str, blk_t *ret);
+extern int common_args_process(int argc, char *argv[], int min_argc,
+			       int max_argc, const char *cmd,
+			       const char *usage, int flags);
+extern int common_inode_args_process(int argc, char *argv[],
+				     ext2_ino_t *inode, int flags);
+extern int common_block_args_process(int argc, char *argv[],
+				     blk_t *block, int *count);
+extern int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
+			      const char *cmd);
+extern int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
+			       const char *cmd);
 
 /* ss command functions */
 
diff --git a/debugfs/dump.c b/debugfs/dump.c
index e290c14..abe8ea6 100644
--- a/debugfs/dump.c
+++ b/debugfs/dump.c
@@ -104,12 +104,8 @@
 	int		nbytes;
 	unsigned int	got;
 	
-	retval = ext2fs_read_inode(current_fs, ino, &inode);
-	if (retval) {
-		com_err(cmdname, retval,
-			"while reading inode %u in dump_file", ino);
+	if (debugfs_read_inode(ino, &inode, cmdname))
 		return;
-	}
 
 	retval = ext2fs_file_open(current_fs, ino, 0, &e2_file);
 	if (retval) {
@@ -307,11 +303,8 @@
 	strncpy(name, dirent->name, thislen);
 	name[thislen] = 0;
 
-	retval = ext2fs_read_inode(current_fs, dirent->inode, &inode);
-	if (retval) {
-		com_err("rdump", retval, "while dumping %s/%s", dumproot, name);
+	if (debugfs_read_inode(dirent->inode, &inode, name))
 		return 0;
-	}
 
 	rdump_inode(dirent->inode, &inode, name, dumproot);
 
@@ -327,12 +320,8 @@
 	int i;
 	char *p;
 
-	if (argc != 3) {
-		com_err(argv[0], 0, "Usage: rdump <directory> <native directory>");
-		return;
-	}
-
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 3, 3, "rdump",
+				"<directory> <native directory>", 0))
 		return;
 
 	ino = string_to_inode(argv[1]);
@@ -350,11 +339,8 @@
 		return;
 	}
 
-	retval = ext2fs_read_inode(current_fs, ino, &inode);
-	if (retval) {
-		com_err("rdump", retval, "while dumping %s", argv[1]);
+	if (debugfs_read_inode(ino, &inode, argv[1]))
 		return;
-	}
 
 	p = strrchr(argv[1], '/');
 	if (p)
@@ -369,16 +355,7 @@
 {
 	ext2_ino_t	inode;
 
-	if (argc != 2) {
-		com_err(argv[0], 0, "Usage: cat <file>");
-		return;
-	}
-
-	if (check_fs_open(argv[0]))
-		return;
-
-	inode = string_to_inode(argv[1]);
-	if (!inode) 
+	if (common_inode_args_process(argc, argv, &inode, 0))
 		return;
 
 	fflush(stdout);
diff --git a/debugfs/icheck.c b/debugfs/icheck.c
index fa065c6..941c749 100644
--- a/debugfs/icheck.c
+++ b/debugfs/icheck.c
@@ -61,7 +61,6 @@
 	ext2_ino_t		ino;
 	struct ext2_inode	inode;
 	errcode_t		retval;
-	char			*tmp;
 	char			*block_buf;
 	
 	if (argc < 2) {
@@ -86,11 +85,8 @@
 	}
 
 	for (i=1; i < argc; i++) {
-		bw.barray[i-1].blk = strtoul(argv[i], &tmp, 0);
-		if (*tmp) {
-			com_err(argv[0], 0, "Bad block - %s", argv[i]);
+		if (strtoblk(argv[0], argv[i], &bw.barray[i-1].blk))
 			return;
-		}
 	}
 
 	bw.num_blocks = bw.blocks_left = argc-1;
diff --git a/debugfs/logdump.c b/debugfs/logdump.c
index 34552d0..c7a5ca9 100644
--- a/debugfs/logdump.c
+++ b/debugfs/logdump.c
@@ -203,13 +203,8 @@
 		journal_source.where = JOURNAL_IS_EXTERNAL;
 		journal_source.fd = journal_fd;
 	} else if ((journal_inum = current_fs->super->s_journal_inum)) {
-		retval = ext2fs_read_inode(current_fs, journal_inum, 
-					   &journal_inode);
-		if (retval) {
-			com_err(argv[0], retval,
-				"while reading inode %u", journal_inum);
+		if (debugfs_read_inode(journal_inum, &journal_inode, argv[0]))
 			return;
-		}
 
 		retval = ext2fs_file_open(current_fs, journal_inum,
 					  0, &journal_file);
diff --git a/debugfs/ls.c b/debugfs/ls.c
index e24bdb3..4459f30 100644
--- a/debugfs/ls.c
+++ b/debugfs/ls.c
@@ -15,6 +15,15 @@
 #include <errno.h>
 #endif
 #include <sys/types.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#else 
+extern int optind;
+extern char *optarg;
+#endif
+#ifdef HAVE_OPTRESET
+extern int optreset;		/* defined by BSD, but not others */
+#endif
 
 #include "debugfs.h"
 
@@ -23,6 +32,7 @@
  */
 
 #define LONG_OPT	0x0001
+#define DELETED_OPT	0x0002
 
 struct list_dir_struct {
 	FILE	*f;
@@ -33,73 +43,73 @@
 static const char *monstr[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
 				"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
 					
-static void ls_l_file(struct list_dir_struct *ls, char *name, ext2_ino_t ino)
-{
-	struct ext2_inode	inode;
-	errcode_t		retval;
-	struct tm		*tm_p;
-	time_t			modtime;
-	char			datestr[80];
-
-	retval = ext2fs_read_inode(current_fs, ino, &inode);
-	if (retval) {
-		fprintf(ls->f, "%5ld --- error ---  %s\n", retval, name);
-		return;
-	}
-	modtime = inode.i_mtime;
-	tm_p = localtime(&modtime);
-	sprintf(datestr, "%2d-%s-%4d %02d:%02d",
-		tm_p->tm_mday, monstr[tm_p->tm_mon], 1900 + tm_p->tm_year,
-		tm_p->tm_hour, tm_p->tm_min);
- 	fprintf(ls->f, "%6u %6o  %5d  %5d   ", ino, inode.i_mode,
- 	       inode.i_uid, inode.i_gid);
- 	if (LINUX_S_ISDIR(inode.i_mode))
- 		fprintf(ls->f, "%5d", inode.i_size);
- 	else
- 		fprintf(ls->f, "%5lld", inode.i_size |
-			((__u64)inode.i_size_high << 32));
- 	fprintf (ls->f, " %s %s\n", datestr, name);
-}
-
-static void ls_file(struct list_dir_struct *ls, char *name,
-		    ext2_ino_t ino, int rec_len)
-{
-	char	tmp[EXT2_NAME_LEN + 16];
-	int	thislen;
-
-	sprintf(tmp, "%u (%d) %s   ", ino, rec_len, name);
-	thislen = strlen(tmp);
-
-	if (ls->col + thislen > 80) {
-		fprintf(ls->f, "\n");
-		ls->col = 0;
-	}
-	fprintf(ls->f, "%s", tmp);
-	ls->col += thislen;
-}	
-
-
-static int list_dir_proc(struct ext2_dir_entry *dirent,
+static int list_dir_proc(ext2_ino_t dir,
+			 int	entry,
+			 struct ext2_dir_entry *dirent,
 			 int	offset,
 			 int	blocksize,
 			 char	*buf,
 			 void	*private)
 {
-	char	name[EXT2_NAME_LEN];
-
+	struct ext2_inode	inode;
+	ext2_ino_t		ino;
+	errcode_t		retval;
+	struct tm		*tm_p;
+	time_t			modtime;
+	char			name[EXT2_NAME_LEN];
+	char			tmp[EXT2_NAME_LEN + 16];
+	char			datestr[80];
+	char			lbr, rbr;
+	int			thislen;
 	struct list_dir_struct *ls = (struct list_dir_struct *) private;
-	int	thislen;
 
 	thislen = ((dirent->name_len & 0xFF) < EXT2_NAME_LEN) ?
 		(dirent->name_len & 0xFF) : EXT2_NAME_LEN;
 	strncpy(name, dirent->name, thislen);
 	name[thislen] = '\0';
+	ino = dirent->inode;
 
-	if (ls->options & LONG_OPT) 
-		ls_l_file(ls, name, dirent->inode);
-	else
-		ls_file(ls, name, dirent->inode, dirent->rec_len);
-	
+	if (entry == DIRENT_DELETED_FILE) {
+		lbr = '<';
+		rbr = '>';
+		ino = 0;
+	} else {
+		lbr = rbr = ' ';
+	}
+	if (ls->options & LONG_OPT) {
+		if (ino) {
+			if (debugfs_read_inode(ino, &inode, name))
+				return;
+			modtime = inode.i_mtime;
+			tm_p = localtime(&modtime);
+			sprintf(datestr, "%2d-%s-%4d %02d:%02d",
+				tm_p->tm_mday, monstr[tm_p->tm_mon],
+				1900 + tm_p->tm_year, tm_p->tm_hour,
+				tm_p->tm_min);
+		} else {
+			strcpy(datestr, "                 ");
+			memset(&inode, 0, sizeof(struct ext2_inode));
+		}
+		fprintf(ls->f, "%c%6u%c %6o  %5d  %5d   ", lbr, ino, rbr,
+			inode.i_mode, inode.i_uid, inode.i_gid);
+		if (LINUX_S_ISDIR(inode.i_mode))
+			fprintf(ls->f, "%5d", inode.i_size);
+		else
+			fprintf(ls->f, "%5lld", inode.i_size |
+				((__u64)inode.i_size_high << 32));
+		fprintf (ls->f, " %s %s\n", datestr, name);
+	} else {
+		sprintf(tmp, "%c%u%c (%d) %s   ", lbr, dirent->inode, rbr,
+			dirent->rec_len, name);
+		thislen = strlen(tmp);
+
+		if (ls->col + thislen > 80) {
+			fprintf(ls->f, "\n");
+			ls->col = 0;
+		}
+		fprintf(ls->f, "%s", tmp);
+		ls->col += thislen;
+	}
 	return 0;
 }
 
@@ -107,29 +117,48 @@
 {
 	ext2_ino_t	inode;
 	int		retval;
+	int		c;
+	int		flags;
 	struct list_dir_struct ls;
-	int		argptr = 1;
 	
 	ls.options = 0;
 	if (check_fs_open(argv[0]))
 		return;
 
-	if ((argc > argptr) && (argv[argptr][0] == '-')) {
-		argptr++;
-		ls.options = LONG_OPT;
+	optind = 0;
+#ifdef HAVE_OPTRESET
+	optreset = 1;		/* Makes BSD getopt happy */
+#endif
+	while ((c = getopt (argc, argv, "dl")) != EOF) {
+		switch (c) {
+		case 'l':
+			ls.options |= LONG_OPT;
+			break;
+		case 'd':
+			ls.options |= DELETED_OPT;
+			break;
+		}
 	}
 
-	if (argc <= argptr)
+	if (argc > optind+1) {
+		com_err(0, 0, "Usage: ls [-l] [-d] file");
+		return;
+	}
+
+	if (argc == optind)
 		inode = cwd;
 	else
-		inode = string_to_inode(argv[argptr]);
+		inode = string_to_inode(argv[optind]);
 	if (!inode)
 		return;
 
 	ls.f = open_pager();
 	ls.col = 0;
-	retval = ext2fs_dir_iterate(current_fs, inode,
-				    DIRENT_FLAG_INCLUDE_EMPTY,
+	flags = DIRENT_FLAG_INCLUDE_EMPTY;
+	if (ls.options & DELETED_OPT)
+		flags |= DIRENT_FLAG_INCLUDE_REMOVED;
+
+	retval = ext2fs_dir_iterate2(current_fs, inode, flags,
 				    0, list_dir_proc, &ls);
 	fprintf(ls.f, "\n");
 	close_pager(ls.f);
diff --git a/debugfs/lsdel.c b/debugfs/lsdel.c
index 6100434..251e06e 100644
--- a/debugfs/lsdel.c
+++ b/debugfs/lsdel.c
@@ -1,8 +1,9 @@
 /*
- * undelete.c --- routines to try to help a user recover a deleted file.
+ * lsdel.c --- routines to try to help a user recover a deleted file.
  * 
- * Copyright (C) 1994 Theodore Ts'o.  This file may be redistributed
- * under the terms of the GNU Public License.
+ * Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
+ * Theodore Ts'o.  This file may be redistributed under the terms of
+ * the GNU Public License.
  */
 
 #include <stdio.h>
@@ -78,13 +79,20 @@
 	errcode_t		retval;
 	char			*block_buf;
 	int			i;
+ 	long			secs = 0;
+ 	char			*tmp;
+	time_t			now = time(0);
 	
+	if (common_args_process(argc, argv, 1, 2, "ls_deleted_inodes",
+				"[secs]", 0))
+		return;
 	if (argc > 1) {
-		com_err(argv[0], 0, "Usage: ls_deleted_inodes\n");
-		return;
+		secs = strtol(argv[1],&tmp,0);
+		if (*tmp) {
+			com_err(argv[0], 0, "Bad time - %s",argv[1]);
+			return;
+		}
 	}
-	if (check_fs_open(argv[0]))
-		return;
 
 	max_delarray = 100;
 	num_delarray = 0;
@@ -118,7 +126,8 @@
 	}
 	
 	while (ino) {
-		if (inode.i_dtime == 0)
+		if ((inode.i_dtime == 0) ||
+		    (secs && ((now - secs) > inode.i_dtime)))
 			goto next;
 
 		lsd.inode = ino;
@@ -170,7 +179,6 @@
 		}
 	}
 
-	printf("%d deleted inodes found.\n", num_delarray);
 	printf(" Inode  Owner  Mode    Size    Blocks    Time deleted\n");
 	
 	qsort(delarray, num_delarray, sizeof(struct deleted_info),
@@ -182,6 +190,7 @@
 		       delarray[i].free_blocks, delarray[i].num_blocks, 
 		       time_to_string(delarray[i].dtime));
 	}
+	printf("%d deleted inodes found.\n", num_delarray);
 	
 error_out:
 	free(block_buf);
diff --git a/debugfs/setsuper.c b/debugfs/setsuper.c
index d48bd04..ce574d0 100644
--- a/debugfs/setsuper.c
+++ b/debugfs/setsuper.c
@@ -169,7 +169,7 @@
 	return 0;
 }
 
-static void print_possible_fields()
+static void print_possible_fields(void)
 {
 	struct super_set_info *ss;
 	const char	*type;
@@ -190,9 +190,9 @@
 
 void do_set_super(int argc, char *argv[])
 {
-	const char *usage = "Usage: set_super <field> <value>\n"
-		"\t\"set_super -l\" will list the names of superblock fields "
-		"which\n\tcan be set.";
+	const char *usage = "<field> <value>\n"
+		"\t\"set_super_value -l\" will list the names of "
+		"superblock fields\n\twhich can be set.";
 	static struct super_set_info *ss;
 	
 	if ((argc == 2) && !strcmp(argv[1], "-l")) {
@@ -200,17 +200,10 @@
 		return;
 	}
 
-	if (check_fs_open(argv[0]))
+	if (common_args_process(argc, argv, 3, 3, "set_super_value",
+				usage, CHECK_FS_RW))
 		return;
 
-	if (argc != 3) {
-		com_err(argv[0], 0, usage);
-		return;
-	}
-
-	if (check_fs_read_write(argv[0]))
-		return;
-	
 	if ((ss = find_field(argv[1])) == 0) {
 		com_err(argv[0], 0, "invalid field specifier: %s", argv[1]);
 		return;
diff --git a/debugfs/util.c b/debugfs/util.c
index 6fcbac2..183a5f6 100644
--- a/debugfs/util.c
+++ b/debugfs/util.c
@@ -132,6 +132,135 @@
 	return ctime(&t);
 }
 
-
+/*
+ * This function will convert a string to an unsigned long, printing
+ * an error message if it fails, and returning success or failure in err.
+ */
+unsigned long parse_ulong(const char *str, const char *cmd,
+			  const char *descr, int *err)
+{
+	char		*tmp;
+	unsigned long	ret;
 	
+	ret = strtoul(str, &tmp, 0);
+	if (*tmp == 0) {
+		if (*err)
+			*err = 0;
+		return ret;
+	}
+	com_err(cmd, 0, "Bad %s - %s", descr, str);
+	if (*err)
+		*err = 1;
+	else
+		exit(1);
+	return 0;
+}
+
+/*
+ * This function will convert a string to a block number.  It returns
+ * 0 on success, 1 on failure.
+ */
+int strtoblk(const char *cmd, const char *str, blk_t *ret)
+{
+	blk_t	blk;
+	int	err;
+
+	blk = parse_ulong(str, cmd, "block number", &err);
+	*ret = blk;
+	if (err == 0 && blk == 0) {
+		com_err(cmd, 0, "Invalid block number 0");
+		err = 1;
+	}
+	return err;
+}
+
+/*
+ * This is a common helper function used by the command processing
+ * routines
+ */
+int common_args_process(int argc, char *argv[], int min_argc, int max_argc,
+			const char *cmd, const char *usage, int flags)
+{
+	if (argc < min_argc || argc > max_argc) {
+		com_err(argv[0], 0, "Usage: %s %s", cmd, usage);
+		return 1;
+	}
+	if (flags & CHECK_FS_NOTOPEN) {
+		if (check_fs_not_open(argv[0]))
+			return 1;
+	} else {
+		if (check_fs_open(argv[0]))
+			return 1;
+	}
+	if ((flags & CHECK_FS_RW) && check_fs_read_write(argv[0]))
+		return 1;
+	if ((flags & CHECK_FS_BITMAPS) && check_fs_bitmaps(argv[0]))
+		return 1;
+	return 0;
+}
+
+/*
+ * This is a helper function used by do_stat, do_freei, do_seti, and
+ * do_testi, etc.  Basically, any command which takes a single
+ * argument which is a file/inode number specifier.
+ */
+int common_inode_args_process(int argc, char *argv[],
+			      ext2_ino_t *inode, int flags)
+{
+	if (common_args_process(argc, argv, 2, 2, argv[0], "<file>", flags))
+		return 1;
+	
+	*inode = string_to_inode(argv[1]);
+	if (!*inode) 
+		return 1;
+	return 0;
+}
+
+/*
+ * This is a helper function used by do_freeb, do_setb, and do_testb
+ */
+int common_block_args_process(int argc, char *argv[],
+			      blk_t *block, int *count)
+{
+	int	err;
+
+	if (common_args_process(argc, argv, 2, 3, argv[0],
+				"<block> [count]", CHECK_FS_BITMAPS))
+		return 1;
+
+	if (strtoblk(argv[0], argv[1], block))
+		return 1;
+	if (argc > 2) {
+		*count = parse_ulong(argv[0], argv[2], "count", &err);
+		if (err)
+			return 1;
+	}
+	return 0;
+}
+
+int debugfs_read_inode(ext2_ino_t ino, struct ext2_inode * inode,
+			const char *cmd)
+{
+	int retval;
+
+	retval = ext2fs_read_inode(current_fs, ino, inode);
+	if (retval) {
+		com_err(cmd, retval, "while reading inode %u", ino);
+		return 1;
+	}
+	return 0;
+}
+
+int debugfs_write_inode(ext2_ino_t ino, struct ext2_inode * inode,
+			const char *cmd)
+{
+	int retval;
+
+	retval = ext2fs_write_inode(current_fs, ino, inode);
+	if (retval) {
+		com_err(cmd, retval, "while writing inode %u", ino);
+		return 1;
+	}
+	return 0;
+}