(* POSIX standards, as enforced by this program: *) (* 0 Option names must be a single alphanumeric character preceded by '-'. *) (* 1 Options without arguments may be combined in a single element. *) (* 2 Options that take arguments must provide exactly one argument. *) (* 3 Options and their arguments may be combined in a single element. *) (* 4 Arguments starting with a '-' must be combined with their option *) (* 5 Options and their arguments must precede all operands. *) (* 6 '--' indicates that future parameters are to be treated as operands. *) (* 7 '--' may be used as an argument or option, but not as an operand. *) (* 8 After '--', any string is considered an operand, including '--' itself.*) (* 9 An operand may not begin with a '-', though '-' is an operand. *) (* 10 "" and '-' are operands, but not arguments or options. *) (* car, cdr: *) (* take a string and return its head char/tail string, respectively *) let car s = s.[0];; let cdr s = String.sub s 1 ((String.length s) - 1);; (* string_for_all: *) (* takes a function of type char -> bool and a string *) (* returns true iff f returns true for every character in the string *) (* this function is modeled after List.for_all *) let rec string_for_all f s = match s with | "" -> true | _ -> f (car s) && string_for_all f (cdr s);; (* is_option, is_operand, is_argument: *) (* take a string *) (* return true iff the string is an option/operand/argument, respectively *) (* (see POSIX standards, above, for a full description of this topic) *) let is_option param = match param with | "" | "-" -> false | "--" -> true | s when (car s) = '-' -> true | _ -> false;; let is_operand param = not (is_option param);; let is_argument param = match param with | "" | "-" -> false | "--" -> true | s when (car s) = '-' -> false | _ -> true;; (* cmdline_valid: *) (* takes in two option-description strings and a command line option list *) (* returns true iff the command line is valid according to the options *) (* (see POSIX standards, above, for a full description of 'valid') *) let rec cmdline_valid sansargs withargs cmdline = match cmdline with (* no more params or rest are operands *) | [] | "--"::_ -> true (* single/multiple options without arguments.. *) | head::tail when is_option head && (string_for_all (String.contains sansargs) (cdr head) (* ..or option with included argument *) || (String.contains withargs (car (cdr head)) && is_argument (cdr (cdr head)))) -> cmdline_valid sansargs withargs tail (* option with separate argument *) | head::tail when is_option head && String.contains withargs (car (cdr head)) -> if tail != [] && is_argument (List.hd tail) then cmdline_valid sansargs withargs (List.tl tail) else false (* operand *) | head::tail when is_operand head -> List.for_all is_operand tail | _ -> false;; (* options_of: *) (* takes in a list of synopses and a command name *) (* returns true and the option-description strings if synopses contains cmd *) (* returns false and empty strings otherwise *) let rec options_of synopses cmd = if synopses = [] then (false, "", "") else match List.hd synopses with | name, sansargs, withargs when name = cmd -> (true, sansargs, withargs) | _, _, _ -> options_of (List.tl synopses) cmd;; (* conforming_options: *) (* takes in a list of synopses and a command to check *) (* returns true iff command meets POSIX standards and matches a synopsis *) let conforming_options synopses command = match command with | [] -> false | cmd::cmdline -> let (cmd_valid, sansargs, withargs) = options_of synopses cmd in if cmd_valid then cmdline_valid sansargs withargs cmdline else false;;