[LWN Logo]
[LWN.net]
From:	 Alexander Viro <viro@math.psu.edu>
To:	 Linus Torvalds <torvalds@transmeta.com>
Subject: [RFC][PATCH] parser for mount options
Date:	 Tue, 7 Aug 2001 13:02:05 -0400 (EDT)
Cc:	 linux-kernel@vger.kernel.org, linux-fsdevel@vger.kernel.org

	OK, folks - here's an implementation of parser for mount options.
Patch contains parser itself (lib/parser.c, include/linux/parser.h) and
switches parse_options() in several filesystems to using it instead of
the current ad-hackery.

	Patch works and AFAICS it shouldn't be hard to convert the rest.

What I wanted to get:
	* set of options accepted by fs and syntax of their arguments
should be visible in source. Explicitly.
	* no cascades of strcmp/strncmp/peeking at individual characters
by hands.
	* simple and regular parse_options() implementations.
	* being able to use that mechanism for procfs ->write() and its ilk.
	* parser itself should be small.

What had been done:
	* match_table_t is an array of pairs (number [== enum member], pattern)
It describes the acceptable options. Patterns are small subset of scanf
formats - the only things recognized by now are %d, %u, %o, %x, %s and
%<num>s. E.g. "uid=%u" or "iocharset=%20s". NULL is a catch-all - everything
matches it.
	* function match_token() looks for the first pattern in the table
that matches given string _and_ fills the array of arguments. Said array
consists of struct { char *from; char *to;} - no conversion, we just find
the substrings.
	* match_int(), match_octal(), etc. take a pointer to such structure
and do the conversion. IOW, usually code will look like
	token = match_token(s, table, argv);
	switch (token) {
		case Opt_uid:
			uid = match_int(&argv[0]);
			break;
		...
	}
	* for %s we have two kinds of conversion - match_strcpy() and
match_strdup(). Meaning should be obvious (allocation is done with
kmalloc(..., GFP_KERNEL);


	It works surprisingly well - syntax is immediately visible in
the table, code is not crapped with tons of global variables and parser
itself is not large.

	Patch applies clean at least to -pre4 and -pre5.  Comments,
suggestions and flames are welcome.  I hope that it got enough
filesystems converted to be representative - adfs, autofs, devpts,
ext2, fat and isofs.
								Al

Patch follows:

diff -urN S8-pre5/fs/adfs/super.c S8-pre5-opt/fs/adfs/super.c
--- S8-pre5/fs/adfs/super.c	Thu Apr 19 23:46:42 2001
+++ S8-pre5-opt/fs/adfs/super.c	Tue Aug  7 11:25:29 2001
@@ -18,6 +18,7 @@
 #include <linux/string.h>
 #include <linux/locks.h>
 #include <linux/init.h>
+#include <linux/parser.h>
 
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
@@ -164,48 +165,43 @@
 	kfree(sb->u.adfs_sb.s_map);
 }
 
+enum {Opt_uid, Opt_gid, Opt_ownmask, Opt_othmask, Opt_err};
+
+static match_table_t tokens = {
+	{Opt_uid, "uid=%u"},
+	{Opt_gid, "gid=%u"},
+	{Opt_ownmask, "ownmask=%o"},
+	{Opt_othmask, "othmask=%o"},
+	{Opt_err, NULL}
+};
+	
 static int parse_options(struct super_block *sb, char *options)
 {
-	char *value, *opt;
+	char *p;
 
 	if (!options)
 		return 0;
 
-	for (opt = strtok(options, ","); opt != NULL; opt = strtok(NULL, ",")) {
-		value = strchr(opt, '=');
-		if (value)
-			*value++ = '\0';
-
-		if (!strcmp(opt, "uid")) {	/* owner of all files */
-			if (!value || !*value)
-				return -EINVAL;
-			sb->u.adfs_sb.s_uid = simple_strtoul(value, &value, 0);
-			if (*value)
-				return -EINVAL;
-		} else
-		if (!strcmp(opt, "gid")) {	/* group owner of all files */
-			if (!value || !*value)
-				return -EINVAL;
-			sb->u.adfs_sb.s_gid = simple_strtoul(value, &value, 0);
-			if (*value)
-				return -EINVAL;
-		} else
-		if (!strcmp(opt, "ownmask")) {	/* owner permission mask */
-			if (!value || !*value)
-				return -EINVAL;
-			sb->u.adfs_sb.s_owner_mask = simple_strtoul(value, &value, 8);
-			if (*value)
-				return -EINVAL;
-		} else
-		if (!strcmp(opt, "othmask")) {	/* others permission mask */
-			if (!value || !*value)
-				return -EINVAL;
-			sb->u.adfs_sb.s_other_mask = simple_strtoul(value, &value, 8);
-			if (*value)
+	for (p = strtok(options, ","); p != NULL; p = strtok(NULL, ",")) {
+		substring_t args[MAX_OPT_ARGS];
+		int token = match_token(p, tokens, args);
+
+		switch (token) {
+			case Opt_uid:
+				sb->u.adfs_sb.s_uid = match_int(args);
+				break;
+			case Opt_gid:
+				sb->u.adfs_sb.s_gid = match_int(args);
+				break;
+			case Opt_ownmask:
+				sb->u.adfs_sb.s_owner_mask = match_octal(args);
+				break;
+			case Opt_othmask:
+				sb->u.adfs_sb.s_other_mask = match_octal(args);
+				break;
+			default:
+				printk("ADFS-fs: unrecognised mount option %s\n", p);
 				return -EINVAL;
-		} else {			/* eh? say again. */
-			printk("ADFS-fs: unrecognised mount option %s\n", opt);
-			return -EINVAL;
 		}
 	}
 	return 0;
diff -urN S8-pre5/fs/autofs/inode.c S8-pre5-opt/fs/autofs/inode.c
--- S8-pre5/fs/autofs/inode.c	Tue Jul  3 21:09:13 2001
+++ S8-pre5-opt/fs/autofs/inode.c	Tue Aug  7 11:25:29 2001
@@ -14,6 +14,7 @@
 #include <linux/slab.h>
 #include <linux/file.h>
 #include <linux/locks.h>
+#include <linux/parser.h>
 #include <asm/bitops.h>
 #include "autofs_i.h"
 #define __NO_VERSION__
@@ -47,9 +48,21 @@
 	statfs:		autofs_statfs,
 };
 
+enum {Opt_err, Opt_fd, Opt_uid, Opt_gid, Opt_pgrp, Opt_minproto, Opt_maxproto};
+static match_table_t autofs_tokens = {
+	{Opt_fd, "fd=%d"},
+	{Opt_uid, "uid=%d"},
+	{Opt_gid, "gid=%d"},
+	{Opt_pgrp, "pgrp=%d"},
+	{Opt_minproto, "minproto=%d"},
+	{Opt_maxproto, "maxproto=%d"},
+	{Opt_err, NULL}
+};
+
 static int parse_options(char *options, int *pipefd, uid_t *uid, gid_t *gid, pid_t *pgrp, int *minproto, int *maxproto)
 {
-	char *this_char, *value;
+	char *p;
+	substring_t args[MAX_OPT_ARGS];
 	
 	*uid = current->uid;
 	*gid = current->gid;
@@ -59,53 +72,32 @@
 
 	*pipefd = -1;
 
-	if ( !options ) return 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
-		if (!strcmp(this_char,"fd")) {
-			if (!value || !*value)
-				return 1;
-			*pipefd = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-		}
-		else if (!strcmp(this_char,"uid")) {
-			if (!value || !*value)
-				return 1;
-			*uid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-		}
-		else if (!strcmp(this_char,"gid")) {
-			if (!value || !*value)
-				return 1;
-			*gid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-		}
-		else if (!strcmp(this_char,"pgrp")) {
-			if (!value || !*value)
-				return 1;
-			*pgrp = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-		}
-		else if (!strcmp(this_char,"minproto")) {
-			if (!value || !*value)
-				return 1;
-			*minproto = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-		}
-		else if (!strcmp(this_char,"maxproto")) {
-			if (!value || !*value)
-				return 1;
-			*maxproto = simple_strtoul(value,&value,0);
-			if (*value)
+	if ( !options )
+		return 1;
+	for (p = strtok(options,","); p; p = strtok(NULL,",")) {
+		int token = match_token(p, autofs_tokens, args);
+		switch (token) {
+			case Opt_fd:
+				*pipefd = match_int(&args[0]);
+				break;
+			case Opt_uid:
+				*uid = match_int(&args[0]);
+				break;
+			case Opt_gid:
+				*gid = match_int(&args[0]);
+				break;
+			case Opt_pgrp:
+				*pgrp = match_int(&args[0]);
+				break;
+			case Opt_minproto:
+				*minproto = match_int(&args[0]);
+				break;
+			case Opt_maxproto:
+				*maxproto = match_int(&args[0]);
+				break;
+			default:
 				return 1;
 		}
-		else break;
 	}
 	return (*pipefd < 0);
 }
diff -urN S8-pre5/fs/devpts/inode.c S8-pre5-opt/fs/devpts/inode.c
--- S8-pre5/fs/devpts/inode.c	Wed Apr 18 00:35:58 2001
+++ S8-pre5-opt/fs/devpts/inode.c	Tue Aug  7 11:25:29 2001
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 #include <linux/stat.h>
 #include <linux/tty.h>
+#include <linux/parser.h>
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
 
@@ -57,6 +58,15 @@
 	remount_fs:	devpts_remount,
 };
 
+enum {Opt_uid, Opt_gid, Opt_mode, Opt_err};
+
+static match_table_t tokens = {
+	{Opt_uid, "uid=%u"},
+	{Opt_gid, "gid=%u"},
+	{Opt_mode, "mode=%o"},
+	{Opt_err, NULL}
+};
+
 static int devpts_parse_options(char *options, struct devpts_sb_info *sbi)
 {
 	int setuid = 0;
@@ -64,39 +74,28 @@
 	uid_t uid = 0;
 	gid_t gid = 0;
 	umode_t mode = 0600;
-	char *this_char, *value;
+	char *p = NULL;
 
-	this_char = NULL;
 	if ( options )
-		this_char = strtok(options,",");
-	for ( ; this_char; this_char = strtok(NULL,",")) {
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
-		if (!strcmp(this_char,"uid")) {
-			if (!value || !*value)
-				return 1;
-			uid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-			setuid = 1;
-		}
-		else if (!strcmp(this_char,"gid")) {
-			if (!value || !*value)
-				return 1;
-			gid = simple_strtoul(value,&value,0);
-			if (*value)
-				return 1;
-			setgid = 1;
-		}
-		else if (!strcmp(this_char,"mode")) {
-			if (!value || !*value)
-				return 1;
-			mode = simple_strtoul(value,&value,8);
-			if (*value)
+		p = strtok(options,",");
+	for ( ; p; p = strtok(NULL,",")) {
+		substring_t args[MAX_OPT_ARGS];
+		int token = match_token(p, tokens, args);
+		switch(token) {
+			case Opt_uid:
+				uid = match_int(args);
+				setuid = 1;
+				break;
+			case Opt_gid:
+				gid = match_int(args);
+				setgid = 1;
+				break;
+			case Opt_mode:
+				mode = match_octal(args);
+				break;
+			default:
 				return 1;
 		}
-		else
-			return 1;
 	}
 	sbi->setuid  = setuid;
 	sbi->setgid  = setgid;
diff -urN S8-pre5/fs/ext2/super.c S8-pre5-opt/fs/ext2/super.c
--- S8-pre5/fs/ext2/super.c	Sun Jul 29 01:54:47 2001
+++ S8-pre5-opt/fs/ext2/super.c	Tue Aug  7 11:25:29 2001
@@ -25,6 +25,7 @@
 #include <linux/init.h>
 #include <linux/locks.h>
 #include <linux/blkdev.h>
+#include <linux/parser.h>
 #include <asm/uaccess.h>
 
 
@@ -154,127 +155,102 @@
 	remount_fs:	ext2_remount,
 };
 
-/*
- * This function has been shamelessly adapted from the msdos fs
- */
+enum {
+	Opt_bsd_df, Opt_minix_df, Opt_grpid, Opt_nogrpid,
+	Opt_resgid, Opt_resuid, Opt_sb, Opt_err_cont, Opt_err_panic, Opt_err_ro,
+	Opt_nouid32, Opt_check, Opt_nocheck, Opt_debug, Opt_ignore, Opt_err,
+};
+static match_table_t tokens = {
+	{Opt_bsd_df, "bsddf"},
+	{Opt_minix_df, "minixdf"},
+	{Opt_grpid, "grpid"},
+	{Opt_grpid, "bsdgroups"},
+	{Opt_nogrpid, "nogrpid"},
+	{Opt_nogrpid, "sysvgroups"},
+	{Opt_resgid, "resgid=%d"},
+	{Opt_resuid, "resuid=%d"},
+	{Opt_sb, "sb=%d"},
+	{Opt_err_cont, "errors=continue"},
+	{Opt_err_panic, "errors=panic"},
+	{Opt_err_ro, "errors=remount-ro"},
+	{Opt_nouid32, "nouid32"},
+	{Opt_check, "check"},
+	{Opt_nocheck, "nocheck"},
+	{Opt_nocheck, "check=none"},
+	{Opt_debug, "debug"},
+	{Opt_ignore, "grpquota"},
+	{Opt_ignore, "noquota"},
+	{Opt_ignore, "quota"},
+	{Opt_ignore, "usrquota"},
+	{Opt_err, NULL}
+};
+
 static int parse_options (char * options, unsigned long * sb_block,
 			  unsigned short *resuid, unsigned short * resgid,
 			  unsigned long * mount_options)
 {
-	char * this_char;
-	char * value;
+	char * p;
+	substring_t args[MAX_OPT_ARGS];
+	int kind = EXT2_MOUNT_ERRORS_CONT;
 
 	if (!options)
 		return 1;
-	for (this_char = strtok (options, ",");
-	     this_char != NULL;
-	     this_char = strtok (NULL, ",")) {
-		if ((value = strchr (this_char, '=')) != NULL)
-			*value++ = 0;
-		if (!strcmp (this_char, "bsddf"))
-			clear_opt (*mount_options, MINIX_DF);
-		else if (!strcmp (this_char, "nouid32")) {
-			set_opt (*mount_options, NO_UID32);
-		}
-		else if (!strcmp (this_char, "check")) {
-			if (!value || !*value || !strcmp (value, "none"))
-				clear_opt (*mount_options, CHECK);
-			else
+	for (p = strtok (options, ","); p; p = strtok (NULL, ",")) {
+		int token = match_token(p, tokens, args);
+		switch (token) {
+			case Opt_bsd_df:
+				clear_opt (*mount_options, MINIX_DF);
+				break;
+			case Opt_minix_df:
+				set_opt (*mount_options, MINIX_DF);
+				break;
+			case Opt_grpid:
+				set_opt (*mount_options, GRPID);
+				break;
+			case Opt_nogrpid:
+				clear_opt (*mount_options, GRPID);
+				break;
+			case Opt_resuid:
+				*resuid = match_int(&args[0]);
+				break;
+			case Opt_resgid:
+				*resgid = match_int(&args[0]);
+				break;
+			case Opt_sb:
+				*sb_block = match_int(&args[0]);
+				break;
+			case Opt_err_panic:
+				kind = EXT2_MOUNT_ERRORS_PANIC;
+				break;
+			case Opt_err_ro:
+				kind = EXT2_MOUNT_ERRORS_RO;
+				break;
+			case Opt_err_cont:
+				kind = EXT2_MOUNT_ERRORS_CONT;
+				break;
+			case Opt_nouid32:
+				set_opt (*mount_options, NO_UID32);
+				break;
+			case Opt_check:
 #ifdef CONFIG_EXT2_CHECK
 				set_opt (*mount_options, CHECK);
 #else
 				printk("EXT2 Check option not supported\n");
 #endif
-		}
-		else if (!strcmp (this_char, "debug"))
-			set_opt (*mount_options, DEBUG);
-		else if (!strcmp (this_char, "errors")) {
-			if (!value || !*value) {
-				printk ("EXT2-fs: the errors option requires "
-					"an argument\n");
-				return 0;
-			}
-			if (!strcmp (value, "continue")) {
-				clear_opt (*mount_options, ERRORS_RO);
-				clear_opt (*mount_options, ERRORS_PANIC);
-				set_opt (*mount_options, ERRORS_CONT);
-			}
-			else if (!strcmp (value, "remount-ro")) {
-				clear_opt (*mount_options, ERRORS_CONT);
-				clear_opt (*mount_options, ERRORS_PANIC);
-				set_opt (*mount_options, ERRORS_RO);
-			}
-			else if (!strcmp (value, "panic")) {
-				clear_opt (*mount_options, ERRORS_CONT);
-				clear_opt (*mount_options, ERRORS_RO);
-				set_opt (*mount_options, ERRORS_PANIC);
-			}
-			else {
-				printk ("EXT2-fs: Invalid errors option: %s\n",
-					value);
-				return 0;
-			}
-		}
-		else if (!strcmp (this_char, "grpid") ||
-			 !strcmp (this_char, "bsdgroups"))
-			set_opt (*mount_options, GRPID);
-		else if (!strcmp (this_char, "minixdf"))
-			set_opt (*mount_options, MINIX_DF);
-		else if (!strcmp (this_char, "nocheck"))
-			clear_opt (*mount_options, CHECK);
-		else if (!strcmp (this_char, "nogrpid") ||
-			 !strcmp (this_char, "sysvgroups"))
-			clear_opt (*mount_options, GRPID);
-		else if (!strcmp (this_char, "resgid")) {
-			if (!value || !*value) {
-				printk ("EXT2-fs: the resgid option requires "
-					"an argument\n");
-				return 0;
-			}
-			*resgid = simple_strtoul (value, &value, 0);
-			if (*value) {
-				printk ("EXT2-fs: Invalid resgid option: %s\n",
-					value);
-				return 0;
-			}
-		}
-		else if (!strcmp (this_char, "resuid")) {
-			if (!value || !*value) {
-				printk ("EXT2-fs: the resuid option requires "
-					"an argument");
-				return 0;
-			}
-			*resuid = simple_strtoul (value, &value, 0);
-			if (*value) {
-				printk ("EXT2-fs: Invalid resuid option: %s\n",
-					value);
-				return 0;
-			}
-		}
-		else if (!strcmp (this_char, "sb")) {
-			if (!value || !*value) {
-				printk ("EXT2-fs: the sb option requires "
-					"an argument");
-				return 0;
-			}
-			*sb_block = simple_strtoul (value, &value, 0);
-			if (*value) {
-				printk ("EXT2-fs: Invalid sb option: %s\n",
-					value);
+				break;
+			case Opt_nocheck:
+				clear_opt (*mount_options, CHECK);
+				break;
+			case Opt_debug:
+				set_opt (*mount_options, DEBUG);
+				break;
+			case Opt_ignore:
+				break;
+			default:
 				return 0;
-			}
-		}
-		/* Silently ignore the quota options */
-		else if (!strcmp (this_char, "grpquota")
-		         || !strcmp (this_char, "noquota")
-		         || !strcmp (this_char, "quota")
-		         || !strcmp (this_char, "usrquota"))
-			/* Don't do anything ;-) */ ;
-		else {
-			printk ("EXT2-fs: Unrecognized mount option %s\n", this_char);
-			return 0;
 		}
 	}
+	*mount_options |= kind;
 	return 1;
 }
 
diff -urN S8-pre5/fs/fat/inode.c S8-pre5-opt/fs/fat/inode.c
--- S8-pre5/fs/fat/inode.c	Tue Aug  7 05:57:47 2001
+++ S8-pre5-opt/fs/fat/inode.c	Tue Aug  7 11:25:29 2001
@@ -30,6 +30,7 @@
 #include <linux/slab.h>
 #include <linux/bitops.h>
 #include <linux/smp_lock.h>
+#include <linux/parser.h>
 
 #include "msbuffer.h"
 
@@ -201,14 +202,53 @@
 	}
 }
 
+enum {
+Opt_bits, Opt_blocksize, Opt_charset, Opt_check_n, Opt_check_r, Opt_check_s,
+Opt_codepage, Opt_conv_a, Opt_conv_b, Opt_conv_t, Opt_cvf_format,
+Opt_cvf_options, Opt_debug, Opt_dots, Opt_err, Opt_gid, Opt_immutable,
+Opt_nocase, Opt_nodots, Opt_quiet, Opt_showexec, Opt_uid, Opt_umask,
+};
+
+static match_table_t FAT_tokens = {
+	{Opt_check_r, "check=relaxed"},
+	{Opt_check_s, "check=strict"},
+	{Opt_check_n, "check=normal"},
+	{Opt_check_r, "check=r"},
+	{Opt_check_s, "check=s"},
+	{Opt_check_n, "check=n"},
+	{Opt_conv_b, "conv=binary"},
+	{Opt_conv_t, "conv=text"},
+	{Opt_conv_a, "conv=auto"},
+	{Opt_conv_b, "conv=b"},
+	{Opt_conv_t, "conv=t"},
+	{Opt_conv_a, "conv=a"},
+	{Opt_dots, "dots"},
+	{Opt_dots, "dotsOK=yes"},
+	{Opt_nodots, "nodots"},
+	{Opt_dots, "dotsOK=no"},
+	{Opt_uid, "uid=%d"},
+	{Opt_gid, "gid=%d"},
+	{Opt_umask, "umask=%o"},
+	{Opt_bits, "fat=%d"},
+	{Opt_codepage, "codepage=%d"},
+	{Opt_charset, "iocharset=%s"},
+	{Opt_cvf_format, "cvf_format=%20s"},
+	{Opt_cvf_options, "cvf_options=%100s"},
+	{Opt_blocksize, "blocksize=%d"},
+	{Opt_nocase, "nocase"},
+	{Opt_quiet, "quiet"},
+	{Opt_showexec, "showexec"},
+	{Opt_debug, "debug"},
+	{Opt_immutable, "sys_immutable"},
+	{Opt_err, NULL}
+};
 
 static int parse_options(char *options,int *fat, int *debug,
 			 struct fat_mount_options *opts,
 			 char *cvf_format, char *cvf_options)
 {
-	char *this_char,*value,save,*savep;
+	substring_t args[MAX_OPT_ARGS];
 	char *p;
-	int ret = 1, len;
 
 	opts->name_check = 'n';
 	opts->conversion = 'b';
@@ -223,141 +263,89 @@
 	*debug = *fat = 0;
 
 	if (!options)
-		goto out;
-	save = 0;
-	savep = NULL;
-	for (this_char = strtok(options,","); this_char;
-	     this_char = strtok(NULL,",")) {
-		if ((value = strchr(this_char,'=')) != NULL) {
-			save = *value;
-			savep = value;
-			*value++ = 0;
-		}
-		if (!strcmp(this_char,"check") && value) {
-			if (value[0] && !value[1] && strchr("rns",*value))
-				opts->name_check = *value;
-			else if (!strcmp(value,"relaxed"))
+		return 1;
+
+	for (p = strtok(options,","); p; p = strtok(NULL,",")) {
+		int token = match_token(p, FAT_tokens, args);
+		switch (token) {
+			case Opt_check_s:
+				opts->name_check = 's';
+				break;
+			case Opt_check_r:
 				opts->name_check = 'r';
-			else if (!strcmp(value,"normal"))
+				break;
+			case Opt_check_n:
 				opts->name_check = 'n';
-			else if (!strcmp(value,"strict"))
-				opts->name_check = 's';
-			else ret = 0;
-		}
-		else if (!strcmp(this_char,"conv") && value) {
-			if (value[0] && !value[1] && strchr("bta",*value))
-				opts->conversion = *value;
-			else if (!strcmp(value,"binary"))
-				opts->conversion = 'b';
-			else if (!strcmp(value,"text"))
-				opts->conversion = 't';
-			else if (!strcmp(value,"auto"))
-				opts->conversion = 'a';
-			else ret = 0;
-		}
-		else if (!strcmp(this_char,"dots")) {
-			opts->dotsOK = 1;
-		}
-		else if (!strcmp(this_char,"nocase")) {
-			opts->nocase = 1;
-		}
-		else if (!strcmp(this_char,"nodots")) {
-			opts->dotsOK = 0;
-		}
-		else if (!strcmp(this_char,"showexec")) {
-			opts->showexec = 1;
-		}
-		else if (!strcmp(this_char,"dotsOK") && value) {
-			if (!strcmp(value,"yes")) opts->dotsOK = 1;
-			else if (!strcmp(value,"no")) opts->dotsOK = 0;
-			else ret = 0;
-		}
-		else if (!strcmp(this_char,"uid")) {
-			if (!value || !*value) ret = 0;
-			else {
-				opts->fs_uid = simple_strtoul(value,&value,0);
-				if (*value) ret = 0;
-			}
-		}
-		else if (!strcmp(this_char,"gid")) {
-			if (!value || !*value) ret= 0;
-			else {
-				opts->fs_gid = simple_strtoul(value,&value,0);
-				if (*value) ret = 0;
-			}
-		}
-		else if (!strcmp(this_char,"umask")) {
-			if (!value || !*value) ret = 0;
-			else {
-				opts->fs_umask = simple_strtoul(value,&value,8);
-				if (*value) ret = 0;
-			}
-		}
-		else if (!strcmp(this_char,"debug")) {
-			if (value) ret = 0;
-			else *debug = 1;
-		}
-		else if (!strcmp(this_char,"fat")) {
-			if (!value || !*value) ret = 0;
-			else {
-				*fat = simple_strtoul(value,&value,0);
-				if (*value || (*fat != 12 && *fat != 16 &&
-					       *fat != 32)) 
-					ret = 0;
-			}
-		}
-		else if (!strcmp(this_char,"quiet")) {
-			if (value) ret = 0;
-			else opts->quiet = 1;
-		}
-		else if (!strcmp(this_char,"blocksize")) {
-			printk("FAT: blocksize option is obsolete, "
-			       "not supported now\n");
-		}
-		else if (!strcmp(this_char,"sys_immutable")) {
-			if (value) ret = 0;
-			else opts->sys_immutable = 1;
-		}
-		else if (!strcmp(this_char,"codepage") && value) {
-			opts->codepage = simple_strtoul(value,&value,0);
-			if (*value) ret = 0;
-			else printk ("MSDOS FS: Using codepage %d\n",
+				break;
+			case Opt_conv_b:
+				opts->name_check = 'b';
+				break;
+			case Opt_conv_t:
+				opts->name_check = 't';
+				break;
+			case Opt_conv_a:
+				opts->name_check = 'a';
+				break;
+			case Opt_dots:
+				opts->dotsOK = 1;
+				break;
+			case Opt_nodots:
+				opts->dotsOK = 0;
+				break;
+			case Opt_uid:
+				opts->fs_uid = match_int(&args[0]);
+				break;
+			case Opt_gid:
+				opts->fs_gid = match_int(&args[0]);
+				break;
+			case Opt_umask:
+				opts->fs_umask = match_octal(&args[0]);
+				break;
+			case Opt_bits:
+				*fat = match_int(&args[0]);
+				if (*fat != 12 && *fat != 16 && *fat != 32)
+					return 0;
+				break;
+			case Opt_codepage:
+				opts->codepage = match_int(&args[0]);
+				printk("MSDOS FS: Using codepage %d\n",
 					opts->codepage);
+				break;
+			case Opt_charset:
+				opts->iocharset = match_strdup(&args[0]);
+				if (!opts->iocharset)
+					return 0;
+				printk("MSDOS FS: IO charset %s\n",
+					opts->iocharset);
+				break;
+			case Opt_cvf_format:
+				match_strcpy(cvf_format, &args[0]);
+				break;
+			case Opt_cvf_options:
+				match_strcpy(cvf_options, &args[0]);
+				break;
+			case Opt_blocksize:
+				printk("FAT: blocksize option is obsolete, "
+				       "not supported now\n");
+				break;
+			case Opt_nocase:
+				opts->nocase = 1;
+				break;
+			case Opt_quiet:
+				opts->quiet = 1;
+				break;
+			case Opt_showexec:
+				opts->showexec = 1;
+				break;
+			case Opt_debug:
+				*debug = 1;
+				break;
+			case Opt_immutable:
+				opts->sys_immutable = 1;
+				break;
 		}
-		else if (!strcmp(this_char,"iocharset") && value) {
-			p = value;
-			while (*value && *value != ',') value++;
-			len = value - p;
-			if (len) { 
-				char * buffer = kmalloc(len+1, GFP_KERNEL);
-				if (buffer) {
-					opts->iocharset = buffer;
-					memcpy(buffer, p, len);
-					buffer[len] = 0;
-					printk("MSDOS FS: IO charset %s\n",
-						buffer);
-				} else
-					ret = 0;
-			}
-		}
-		else if (!strcmp(this_char,"cvf_format")) {
-			if (!value)
-				return 0;
-			strncpy(cvf_format,value,20);
-		}
-		else if (!strcmp(this_char,"cvf_options")) {
-			if (!value)
-				return 0;
-			strncpy(cvf_options,value,100);
-		}
-
-		if (this_char != options) *(this_char-1) = ',';
-		if (value) *savep = save;
-		if (ret == 0)
-			break;
 	}
-out:
-	return ret;
+	return 1;
 }
 
 static void fat_read_root(struct inode *inode)
diff -urN S8-pre5/fs/isofs/inode.c S8-pre5-opt/fs/isofs/inode.c
--- S8-pre5/fs/isofs/inode.c	Thu Apr 19 23:46:45 2001
+++ S8-pre5-opt/fs/isofs/inode.c	Tue Aug  7 11:25:29 2001
@@ -28,6 +28,7 @@
 #include <linux/ctype.h>
 #include <linux/smp_lock.h>
 #include <linux/blkdev.h>
+#include <linux/parser.h>
 
 #include <asm/system.h>
 #include <asm/uaccess.h>
@@ -268,9 +269,48 @@
 }
 #endif
 
+enum {
+Opt_block, Opt_check_r, Opt_check_s, Opt_cruft, Opt_gid, Opt_ignore,
+Opt_iocharset, Opt_map_a, Opt_map_n, Opt_map_o, Opt_mode, Opt_nojoliet,
+Opt_norock, Opt_sb, Opt_session, Opt_uid, Opt_unhide, Opt_utf8, Opt_err
+};
+static match_table_t tokens = {
+	{Opt_norock, "norock"},
+	{Opt_nojoliet, "nojoliet"},
+	{Opt_unhide, "unhide"},
+	{Opt_cruft, "cruft"},
+	{Opt_utf8, "utf8"},
+	{Opt_iocharset, "iocharset=%s"},
+	{Opt_map_a, "map=a"},
+	{Opt_map_a, "map=acorn"},
+	{Opt_map_n, "map=n"},
+	{Opt_map_n, "map=normal"},
+	{Opt_map_o, "map=o"},
+	{Opt_map_o, "map=off"},
+	{Opt_session, "session=%u"},
+	{Opt_sb, "sbsector=%u"},
+	{Opt_check_r, "check=r"},
+	{Opt_check_r, "check=relaxed"},
+	{Opt_check_s, "check=s"},
+	{Opt_check_s, "check=strict"},
+	{Opt_uid, "uid=%u"},
+	{Opt_gid, "gid=%u"},
+	{Opt_mode, "mode=%u"},
+	{Opt_block, "block=%u"},
+	{Opt_ignore, "conv=b"},
+	{Opt_ignore, "conv=t"},
+	{Opt_ignore, "conv=m"},
+	{Opt_ignore, "conv=a"},
+	{Opt_ignore, "conv=binary"},
+	{Opt_ignore, "conv=text"},
+	{Opt_ignore, "conv=mtext"},
+	{Opt_ignore, "conv=auto"},
+	{Opt_err, NULL}
+};
+
 static int parse_options(char *options, struct iso9660_options * popt)
 {
-	char *this_char,*value;
+	char *p;
 
 	popt->map = 'n';
 	popt->rock = 'y';
@@ -289,106 +329,82 @@
 	popt->utf8 = 0;
 	popt->session=-1;
 	popt->sbsector=-1;
-	if (!options) return 1;
-	for (this_char = strtok(options,","); this_char; this_char = strtok(NULL,",")) {
-	        if (strncmp(this_char,"norock",6) == 0) {
-		  popt->rock = 'n';
-		  continue;
-		}
-	        if (strncmp(this_char,"nojoliet",8) == 0) {
-		  popt->joliet = 'n';
-		  continue;
-		}
-	        if (strncmp(this_char,"unhide",6) == 0) {
-		  popt->unhide = 'y';
-		  continue;
-		}
-	        if (strncmp(this_char,"cruft",5) == 0) {
-		  popt->cruft = 'y';
-		  continue;
-		}
-	        if (strncmp(this_char,"utf8",4) == 0) {
-		  popt->utf8 = 1;
-		  continue;
-		}
-		if ((value = strchr(this_char,'=')) != NULL)
-			*value++ = 0;
+	if (!options)
+		return 1;
 
+	for (p = strtok(options,","); p; p = strtok(NULL,",")) {
+		substring_t args[MAX_OPT_ARGS];
+		int token = match_token(p, tokens, args);
+		unsigned n;
+
+		switch (token) {
+			case Opt_norock:
+				popt->rock = 'n';
+				break;
+			case Opt_nojoliet:
+				popt->joliet = 'n';
+				break;
+			case Opt_unhide:
+				popt->unhide = 'y';
+				break;
+			case Opt_cruft:
+				popt->cruft = 'y';
+				break;
+			case Opt_utf8:
+				popt->cruft = 1;
+				break;
 #ifdef CONFIG_JOLIET
-		if (!strcmp(this_char,"iocharset") && value) {
-			popt->iocharset = value;
-			while (*value && *value != ',')
-				value++;
-			if (value == popt->iocharset)
+			case Opt_iocharset:
+				popt->iocharset = match_strdup(&args[0]);
+				break;
+#endif
+			case Opt_map_a:
+				popt->map = 'a';
+				break;
+			case Opt_map_o:
+				popt->map = 'o';
+				break;
+			case Opt_map_n:
+				popt->map = 'n';
+				break;
+			case Opt_session:
+				n = match_int(&args[0]);
+				if (n > 99)
+					return 0;
+				popt->session = n + 1;
+				break;
+			case Opt_sb:
+				n = match_int(&args[0]);
+				if (n > 660 * 512)
+					return 0;
+				popt->sbsector = n;
+				break;
+			case Opt_check_r:
+				popt->check = 'r';
+				break;
+			case Opt_check_s:
+				popt->check = 's';
+				break;
+			case Opt_ignore:
+				break;
+			case Opt_uid:
+				popt->uid = match_int(&args[0]);
+				break;
+			case Opt_gid:
+				popt->gid = match_int(&args[0]);
+				break;
+			case Opt_mode:
+				popt->mode = match_int(&args[0]);
+				break;
+			case Opt_block:
+				n = match_int(&args[0]);
+				if (n != 512 && n != 1024 && n != 2048)
+					return 0;
+				popt->blocksize = n;
+				break;
+			default:
 				return 0;
-			*value = 0;
-		} else
-#endif
-		if (!strcmp(this_char,"map") && value) {
-			if (value[0] && !value[1] && strchr("ano",*value))
-				popt->map = *value;
-			else if (!strcmp(value,"off")) popt->map = 'o';
-			else if (!strcmp(value,"normal")) popt->map = 'n';
-			else if (!strcmp(value,"acorn")) popt->map = 'a';
-			else return 0;
-		}
-		if (!strcmp(this_char,"session") && value) {
-			char * vpnt = value;
-			unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
-			if(ivalue < 0 || ivalue >99) return 0;
-			popt->session=ivalue+1;
 		}
-		if (!strcmp(this_char,"sbsector") && value) {
-			char * vpnt = value;
-			unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
-			if(ivalue < 0 || ivalue >660*512) return 0;
-			popt->sbsector=ivalue;
-		}
-		else if (!strcmp(this_char,"check") && value) {
-			if (value[0] && !value[1] && strchr("rs",*value))
-				popt->check = *value;
-			else if (!strcmp(value,"relaxed")) popt->check = 'r';
-			else if (!strcmp(value,"strict")) popt->check = 's';
-			else return 0;
-		}
-		else if (!strcmp(this_char,"conv") && value) {
-			/* no conversion is done anymore;
-			   we still accept the same mount options,
-			   but ignore them */
-			if (value[0] && !value[1] && strchr("btma",*value)) ;
-			else if (!strcmp(value,"binary")) ;
-			else if (!strcmp(value,"text")) ;
-			else if (!strcmp(value,"mtext")) ;
-			else if (!strcmp(value,"auto")) ;
-			else return 0;
-		}
-		else if (value &&
-			 (!strcmp(this_char,"block") ||
-			  !strcmp(this_char,"mode") ||
-			  !strcmp(this_char,"uid") ||
-			  !strcmp(this_char,"gid"))) {
-		  char * vpnt = value;
-		  unsigned int ivalue = simple_strtoul(vpnt, &vpnt, 0);
-		  if (*vpnt) return 0;
-		  switch(*this_char) {
-		  case 'b':
-		    if (   ivalue != 512
-			&& ivalue != 1024
-			&& ivalue != 2048) return 0;
-		    popt->blocksize = ivalue;
-		    break;
-		  case 'u':
-		    popt->uid = ivalue;
-		    break;
-		  case 'g':
-		    popt->gid = ivalue;
-		    break;
-		  case 'm':
-		    popt->mode = ivalue;
-		    break;
-		  }
-		}
-		else return 1;
 	}
 	return 1;
 }
@@ -814,6 +830,9 @@
 	if (opt.check == 'r') table++;
 	s->s_root->d_op = &isofs_dentry_ops[table];
 
+	if (opt.iocharset)
+		kfree(opt.iocharset);
+
 	return s;
 
 	/*
@@ -856,6 +875,8 @@
 out_freebh:
 	brelse(bh);
 out_unlock:
+	if (opt.iocharset)
+		kfree(opt.iocharset);
 	return NULL;
 }
 
diff -urN S8-pre5/include/linux/parser.h S8-pre5-opt/include/linux/parser.h
--- S8-pre5/include/linux/parser.h	Wed Dec 31 19:00:00 1969
+++ S8-pre5-opt/include/linux/parser.h	Tue Aug  7 11:25:29 2001
@@ -0,0 +1,21 @@
+struct match_token {
+	int token;
+	char *pattern;
+};
+
+typedef struct match_token match_table_t[];
+
+enum { MAX_OPT_ARGS=3 };
+
+typedef struct {
+	char *from;
+	char *to;
+} substring_t;
+
+int match_token(char *s, match_table_t table, substring_t args[]);
+
+int match_int(substring_t *);
+int match_octal(substring_t *);
+int match_hex(substring_t *);
+void match_strcpy(char *, substring_t *);
+char *match_strdup(substring_t *);
diff -urN S8-pre5/lib/Makefile S8-pre5-opt/lib/Makefile
--- S8-pre5/lib/Makefile	Fri Apr 27 06:30:47 2001
+++ S8-pre5-opt/lib/Makefile	Tue Aug  7 11:25:29 2001
@@ -8,9 +8,9 @@
 
 L_TARGET := lib.a
 
-export-objs := cmdline.o rwsem-spinlock.o rwsem.o
+export-objs := cmdline.o rwsem-spinlock.o rwsem.o parser.o
 
-obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o
+obj-y := errno.o ctype.o string.o vsprintf.o brlock.o cmdline.o parser.o
 
 obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o
 obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem.o
diff -urN S8-pre5/lib/parser.c S8-pre5-opt/lib/parser.c
--- S8-pre5/lib/parser.c	Wed Dec 31 19:00:00 1969
+++ S8-pre5-opt/lib/parser.c	Tue Aug  7 11:26:50 2001
@@ -0,0 +1,131 @@
+/*
+ * lib/parser.c - simple parser for mount, etc. options.
+ */
+
+#include <linux/ctype.h>
+#include <linux/string.h>
+#include <linux/parser.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+static int match_one(char *s, char *p, substring_t args[])
+{
+	char *meta;
+	int argc = 0;
+
+	if (!p)
+		return 1;
+
+	if (*p != *s && *p != '%')
+		return 0;
+
+	while(1) {
+		int len = -1;
+		meta = strchr(p, '%');
+		if (!meta)
+			return strcmp(p, s) == 0;
+
+		if (strncmp(p, s, meta-p))
+			return 0;
+
+		s += meta - p;
+		p = meta + 1;
+
+		if (isdigit(*p))
+			len = simple_strtoul(p, &p, 10);
+		else if (*p == '%') {
+			if (*s++ != '%')
+				return 0;
+			continue;
+		}
+
+		if (argc >= MAX_OPT_ARGS)
+			return 0;
+
+		args[argc].from = s;
+		switch (*p++) {
+			case 's':
+				if (len == -1 || len > strlen(s))
+					len = strlen(s);
+				args[argc].to = s + len;
+				break;
+			case 'd':
+				simple_strtol(s, &args[argc].to, 0);
+				goto num;
+			case 'u':
+				simple_strtoul(s, &args[argc].to, 0);
+				goto num;
+			case 'o':
+				simple_strtoul(s, &args[argc].to, 8);
+				goto num;
+			case 'x':
+				simple_strtoul(s, &args[argc].to, 16);
+			num:
+				if (args[argc].to == args[argc].from)
+					return 0;
+				break;
+			default:
+				return 0;
+		}
+		s = args[argc].to;
+		argc++;
+	}
+}
+
+int match_token(char *s, match_table_t table, substring_t args[])
+{
+	struct match_token *p;
+
+	for (p = table; !match_one(s, p->pattern, args) ; p++)
+		;
+
+	return p->token;
+}
+
+int match_int(substring_t *s)
+{
+	char buf[s->to - s->from + 1];
+
+	memcpy(buf, s->from, s->to - s->from);
+	buf[s->to - s->from] = '\0';
+	return simple_strtol(buf, NULL, 0);
+}
+
+int match_octal(substring_t *s)
+{
+	char buf[s->to - s->from + 1];
+
+	memcpy(buf, s->from, s->to - s->from);
+	buf[s->to - s->from] = '\0';
+	return simple_strtoul(buf, NULL, 8);
+}
+
+int match_hex(substring_t *s)
+{
+	char buf[s->to - s->from + 1];
+
+	memcpy(buf, s->from, s->to - s->from);
+	buf[s->to - s->from] = '\0';
+	return simple_strtoul(buf, NULL, 16);
+}
+
+void match_strcpy(char *to, substring_t *s)
+{
+	memcpy(to, s->from, s->to - s->from);
+	to[s->to - s->from] = '\0';
+}
+
+char *match_strdup(substring_t *s)
+{
+	char *p = kmalloc(s->to - s->from + 1, GFP_KERNEL);
+	if (p)
+		match_strcpy(p, s);
+	return p;
+}
+
+EXPORT_SYMBOL(match_token);
+EXPORT_SYMBOL(match_int);
+EXPORT_SYMBOL(match_octal);
+EXPORT_SYMBOL(match_hex);
+EXPORT_SYMBOL(match_strcpy);
+EXPORT_SYMBOL(match_strdup);

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/