github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/namespace/parser.go (about)

     1  // Copyright 2020 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Package namespace parses name space description files
     6  // https://plan9.io/magic/man2html/6/namespace
     7  package namespace
     8  
     9  import (
    10  	"bufio"
    11  	"fmt"
    12  	"io"
    13  	"os"
    14  	"strings"
    15  )
    16  
    17  // Parse takes a namespace file and returns a collection
    18  // of operations that build a name space in Plan 9.
    19  //
    20  // Think oci runtime spec, but don't think too much cause the JSON
    21  // will make your head hurt.
    22  //
    23  // http://man.cat-v.org/plan_9/1/ns
    24  func Parse(r io.Reader) (File, error) {
    25  	scanner := bufio.NewScanner(r)
    26  
    27  	cmds := []Modifier{}
    28  	for scanner.Scan() {
    29  		buf := scanner.Bytes()
    30  		if len(buf) <= 0 {
    31  			continue
    32  		}
    33  		r := buf[0]
    34  		// Blank lines and lines with # as the first non–space character are ignored.
    35  		if r == '#' || r == ' ' {
    36  			continue
    37  		}
    38  		cmd, err := ParseLine(scanner.Text())
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		cmds = append(cmds, cmd)
    43  	}
    44  	if err := scanner.Err(); err != nil {
    45  		fmt.Fprintln(os.Stderr, "reading standard input:", err)
    46  	}
    47  	return cmds, nil
    48  }
    49  
    50  // ParseFlags parses flags as mount, bind would. Supports -a, -b, -c,
    51  // -q, and -C, although the -q flag is not yet implemented. Other flags
    52  // are silently ignored.
    53  // Not using the os.Flag package here as that would break the
    54  // name space description files.
    55  // https://9p.io/magic/man2html/6/namespace
    56  func ParseFlags(args []string) (mountflag, []string) {
    57  	flag := REPL
    58  	for i, arg := range args {
    59  		// these args are passed trough strings.Fields which doesn't return empty strings
    60  		// so this is ok.
    61  		if arg[0] == '-' {
    62  			args = append(args[:i], args[i+1:]...)
    63  			for _, r := range arg {
    64  				switch r {
    65  				case 'a':
    66  					flag |= AFTER
    67  				case 'b':
    68  					flag |= BEFORE
    69  				case 'c':
    70  					flag |= CREATE
    71  				case 'q':
    72  				// todo(sevki): support quiet flag
    73  				case 'C':
    74  					flag |= CACHE
    75  				default:
    76  				}
    77  			}
    78  		}
    79  	}
    80  	return flag, args
    81  }
    82  
    83  // ParseArgs parses os.Args (not flag.Args()!) to return a
    84  // namespace.Modifier which is appropriate for the given
    85  // command. The command is taken from args[0]; the following
    86  // commands are supported: bind, mount, unmount, clear, cd, ., import.
    87  //
    88  // After extracting the command, ParseArgs will call ParseFlags to extract
    89  // appropriate bind/mount flags, if any.
    90  func ParseArgs(args []string) (Modifier, error) {
    91  	arg := args[0]
    92  	args = args[1:]
    93  	trap := syzcall(0)
    94  
    95  	c := cmd{
    96  		syscall: trap,
    97  		flag:    REPL,
    98  		args:    args,
    99  	}
   100  	switch arg {
   101  	case "bind":
   102  		c.syscall = BIND
   103  	case "mount":
   104  		c.syscall = MOUNT
   105  	case "unmount":
   106  		c.syscall = UNMOUNT
   107  	case "clear":
   108  		c.syscall = RFORK
   109  	case "cd":
   110  		c.syscall = CHDIR
   111  	case ".":
   112  		c.syscall = INCLUDE
   113  	case "import":
   114  		c.syscall = IMPORT
   115  	default:
   116  		return nil, fmt.Errorf("%q is an unknown operation", arg)
   117  	}
   118  
   119  	c.flag, c.args = ParseFlags(args)
   120  
   121  	return c, nil
   122  }
   123  
   124  // ParseLine breaks a string into arguments and passes them to ParseArgs.
   125  func ParseLine(line string) (Modifier, error) {
   126  	return ParseArgs(strings.Fields(line))
   127  }