[arch-projects] [mkinitcpio][PATCH 2/2] refactor parse_cmdline into something more readable

Dave Reisner dreisner at archlinux.org
Thu Jul 7 12:31:40 UTC 2016


This refactors parse_cmdline into a few chunks of code, mainly to
separate the work of parsing from the handling of the actual parsed
parameters. By default, parse_cmdline_item is used as the callback for
handling the parameters, but this could be overriden by other code
sourcing init_functions if desirable.

Our test harness passes more tests, but we leave behind some expected
failures to describe the cases where it still fails. Fortunately I've
not yet been able to find any cases which warrant --expect-parse-fail.
---
 init_functions          | 175 ++++++++++++++++++++++++++++++++----------------
 test/test_parse_cmdline |  16 ++---
 2 files changed, 125 insertions(+), 66 deletions(-)

diff --git a/init_functions b/init_functions
index 05f4ab0..d5a584e 100644
--- a/init_functions
+++ b/init_functions
@@ -114,74 +114,133 @@ set_log_option() {
     done
 }
 
-parse_cmdline() {
-    local _w _quoted _lhs _rhs _cmdline
-    read -r _cmdline
-    for _w in $_cmdline; do
-        if [ -z "$_quoted" ]; then
-            case $_w in
-                # ignore everything after a # in the commandline
-                \#*) break ;;
-                # special cases
-                rw|ro) rwopt=$_w ;;
-                fstype) rootfstype=$_w ;;
-                fsck.mode=*)
-                    case ${_w#*=} in
-                        force)
-                            forcefsck=y
-                            ;;
-                        skip)
-                            fastboot=y
-                            ;;
-                        *)
-                            err "unknown fsck.mode parameter: '${_w#*=}'"
-                            ;;
-                    esac
+startswith() {
+    local word=$1 prefix=$2
+
+    case $word in
+        $prefix*)
+            return 0
+            ;;
+    esac
+
+    return 1
+}
+
+endswith() {
+    local word=$1 suffix=$2
+
+    case $word in
+        *$suffix)
+            return 0
+            ;;
+    esac
+
+    return 1
+}
+
+parse_cmdline_item() {
+    local key=$1 value=$2
+
+    case $key in
+        rw|ro)
+            rwopt=$key
+            ;;
+        fstype)
+            # The kernel understands 'rootfstype', but mkinitcpio has (without
+            # documentation) supported 'fstype' instead. Ensure we support both
+            # for backwards compat, but make fstype legacy.
+            rootfstype=$value
+            ;;
+        fsck.mode)
+            case $value in
+                force)
+                    forcefsck=y
                     ;;
-                rd.*)
-                    case ${_w#rd.} in
-                        debug)
-                            rd_debug=y
-                            ;;
-                        log)
-                            rd_logmask=$(( _rdlog_kmsg | _rdlog_cons ))
-                            ;;
-                        log=*)
-                            set_log_option "${_w#rd.log=}"
-                            ;;
-                    esac
+                skip)
+                    fastboot=y
                     ;;
-                # abide by shell variable naming rules
-                [[:alpha:]_]*=*)
-                    _rhs=${_w#*=}
-                    _lhs=${_w%%=*}
-                    _lhs=${_lhs//[-.]/_}
-                    if [ '"' = "${_rhs:0:1}" ]; then
-                        if [ '"' = "${_rhs:$((${#_rhs}-1))}" ]; then
-                            _rhs="${_rhs:1:$((${#_rhs}-2))}"
-                        else
-                            _rhs=${_rhs:1}
-                            _quoted=1
-                            continue
-                        fi
+                *)
+                    err "unknown fsck.mode parameter: '$value'"
+                    ;;
+            esac
+            ;;
+        rd.debug)
+            rd_debug=y
+            ;;
+        rd.log)
+            if [ -n "$value" ]; then
+                set_log_option "$value"
+            else
+                rd_logmask=$(( _rdlog_kmsg | _rdlog_cons ))
+            fi
+            ;;
+        [![:alpha:]_]*|[[:alpha:]_]*[![:alnum:]_]*)
+            # invalid shell variable, ignore it
+            ;;
+        *)
+            # valid shell variable
+            eval "$key"='${value:-y}'
+            ;;
+    esac
+}
+
+process_cmdline_param() {
+    local item_callback=$1 key=$2 value=$3
+
+    # maybe unquote the value
+    if startswith "$value" "[\"']" && endswith "$value" "${value:0:1}"; then
+        value=${value#?} value=${value%?}
+    fi
+
+    "$item_callback" "$key" "$value"
+}
+
+parse_cmdline() {
+    local item_callback=${1:-parse_cmdline_item}
+    local cmdline word quoted key value
+
+    set -f
+    read -r cmdline
+    set -- $cmdline
+    set +f
+
+    for word; do
+        if [ -n "$quoted" ]; then
+            value="$value $word"
+        else
+            case $word in
+                *=*)
+                    key=${word%%=*}
+                    value=${word#*=}
+
+                    if startswith "$value" "[\"']"; then
+                        quoted=${value:0:1}
                     fi
-                    eval $_lhs=\$_rhs
                     ;;
-                [[:alpha:]_]*)
-                    _lhs=${_w//[-.]/_}
-                    eval $_lhs=y
+                '#'*)
+                    break
+                    ;;
+                *)
+                    key=$word
                     ;;
             esac
-        else
-            if [ '"' = "${_w:$((${#_w}-1))}" ]; then
-                _rhs="$_rhs ${_w%\"}"
-                unset _quoted
-                eval $_lhs=\$_rhs
+        fi
+
+        if [ -n "$quoted" ]; then
+            if endswith "$value" "$quoted"; then
+                unset quoted
             else
-                _rhs="$_rhs $_w"
+                continue
             fi
         fi
+
+        process_cmdline_param "$item_callback" "$key" "$value"
+        unset key value
     done
+
+    if [ -n "$key" ]; then
+        process_cmdline_param "$item_callback" "$key" "$value"
+    fi
 }
 
 fsck_device() {
diff --git a/test/test_parse_cmdline b/test/test_parse_cmdline
index 9afd9cb..ff855e4 100755
--- a/test/test_parse_cmdline
+++ b/test/test_parse_cmdline
@@ -130,19 +130,19 @@ test_parse 'foo="bar baz"' \
   'foo' 'bar baz'
 
 # single quoting
-test_parse --expect-fail "foo='bar'" \
+test_parse "foo='bar'" \
   'foo' 'bar'
-test_parse --expect-parse-fail "foo='bar baz'" \
+test_parse "foo='bar baz'" \
   'foo' 'bar baz'
 
 # dangling quotes
-test_parse --expect-fail 'foo="bar' \
+test_parse 'foo="bar' \
   'foo' '"bar'
 test_parse 'foo=bar"' \
   'foo' 'bar"'
 
 # nested quotes
-test_parse --expect-parse-fail "foo='\"bar baz\"' herp='\"de\"rp'" \
+test_parse "foo='\"bar baz\"' herp='\"de\"rp'" \
   'foo' '"bar baz"' \
   'herp' '"de"rp'
 
@@ -174,9 +174,9 @@ test_parse 'foo="bar #baz" parse=this' \
 # shell metachars
 test_parse 'foo=*' \
   'foo' '\*'
-test_parse --expect-fail 'Make*' \
+test_parse 'Make*' \
   'Makefile' ''
-test_parse --expect-fail '[Makefile]*' \
+test_parse '[Makefile]*' \
   'Makefile' '' \
   'init' '' \
   'functions' ''
@@ -184,7 +184,7 @@ test_parse --expect-fail '[Makefile]*' \
 # invalid names
 test_parse 'in-valid=name'
 test_parse '6foo=bar'
-test_parse --expect-parse-fail '"gar bage"' \
+test_parse '"gar bage"' \
   'gar' '' \
   'bage' ''
 
@@ -197,7 +197,7 @@ test_parse 'ro' \
   'ro' '' \
   'rw' '' \
   'rwopt' 'ro'
-test_parse --expect-fail 'fstype=btrfs' \
+test_parse 'fstype=btrfs' \
   'rootfstype' 'btrfs'
 test_parse 'fsck.mode=force' \
   'forcefsck' 'y' \
-- 
2.8.3


More information about the arch-projects mailing list