| 62431 |      1 | /*  Title:      Pure/System/getopts.scala
 | 
|  |      2 |     Author:     Makarius
 | 
|  |      3 | 
 | 
|  |      4 | Support for command-line options as in GNU bash.
 | 
|  |      5 | */
 | 
|  |      6 | 
 | 
|  |      7 | package isabelle
 | 
|  |      8 | 
 | 
|  |      9 | 
 | 
| 71601 |     10 | import scala.annotation.tailrec
 | 
|  |     11 | 
 | 
|  |     12 | 
 | 
| 75393 |     13 | object Getopts {
 | 
|  |     14 |   def apply(usage_text: String, option_specs: (String, String => Unit)*): Getopts = {
 | 
| 62431 |     15 |     val options =
 | 
| 73359 |     16 |       option_specs.foldLeft(Map.empty[Char, (Boolean, String => Unit)]) {
 | 
| 62431 |     17 |         case (m, (s, f)) =>
 | 
|  |     18 |           val (a, entry) =
 | 
| 74037 |     19 |             if (s.length == 1) (s(0), (false, f))
 | 
|  |     20 |             else if (s.length == 2 && s.endsWith(":")) (s(0), (true, f))
 | 
| 62431 |     21 |             else error("Bad option specification: " + quote(s))
 | 
|  |     22 |           if (m.isDefinedAt(a)) error("Duplicate option specification: " + quote(a.toString))
 | 
|  |     23 |           else m + (a -> entry)
 | 
|  |     24 |       }
 | 
|  |     25 |     new Getopts(usage_text, options)
 | 
|  |     26 |   }
 | 
|  |     27 | }
 | 
|  |     28 | 
 | 
| 75393 |     29 | class Getopts private(usage_text: String, options: Map[Char, (Boolean, String => Unit)]) {
 | 
|  |     30 |   def usage(): Nothing = {
 | 
| 67178 |     31 |     Output.writeln(usage_text, stdout = true)
 | 
| 74306 |     32 |     sys.exit(Process_Result.RC.error)
 | 
| 62431 |     33 |   }
 | 
|  |     34 | 
 | 
|  |     35 |   private def is_option(opt: Char): Boolean = options.isDefinedAt(opt)
 | 
|  |     36 |   private def is_option0(opt: Char): Boolean = is_option(opt) && !options(opt)._1
 | 
|  |     37 |   private def is_option1(opt: Char): Boolean = is_option(opt) && options(opt)._1
 | 
| 62432 |     38 |   private def print_option(opt: Char): String = quote("-" + opt.toString)
 | 
| 62434 |     39 |   private def option(opt: Char, opt_arg: List[Char]): Unit =
 | 
|  |     40 |     try { options(opt)._2.apply(opt_arg.mkString) }
 | 
|  |     41 |     catch {
 | 
|  |     42 |       case ERROR(msg) =>
 | 
|  |     43 |         cat_error(msg, "The error(s) above occurred in command-line option " + print_option(opt))
 | 
|  |     44 |     }
 | 
| 62431 |     45 | 
 | 
| 71601 |     46 |   @tailrec private def getopts(args: List[List[Char]]): List[List[Char]] =
 | 
| 62431 |     47 |     args match {
 | 
|  |     48 |       case List('-', '-') :: rest_args => rest_args
 | 
| 71383 |     49 |       case ('-' :: opt :: rest_opts) :: rest_args if is_option0(opt) && rest_opts.nonEmpty =>
 | 
| 62431 |     50 |         option(opt, Nil); getopts(('-' :: rest_opts) :: rest_args)
 | 
|  |     51 |       case List('-', opt) :: rest_args if is_option0(opt) =>
 | 
|  |     52 |         option(opt, Nil); getopts(rest_args)
 | 
| 71383 |     53 |       case ('-' :: opt :: opt_arg) :: rest_args if is_option1(opt) && opt_arg.nonEmpty =>
 | 
| 62431 |     54 |         option(opt, opt_arg); getopts(rest_args)
 | 
|  |     55 |       case List('-', opt) :: opt_arg :: rest_args if is_option1(opt) =>
 | 
|  |     56 |         option(opt, opt_arg); getopts(rest_args)
 | 
|  |     57 |       case List(List('-', opt)) if is_option1(opt) =>
 | 
| 62432 |     58 |         Output.error_message("Command-line option " + print_option(opt) + " requires an argument")
 | 
| 62431 |     59 |         usage()
 | 
|  |     60 |       case ('-' :: opt :: _) :: _ if !is_option(opt) =>
 | 
| 62432 |     61 |         if (opt != '?') Output.error_message("Illegal command-line option " + print_option(opt))
 | 
| 62431 |     62 |         usage()
 | 
|  |     63 |       case _ => args
 | 
|  |     64 |   }
 | 
|  |     65 | 
 | 
|  |     66 |   def apply(args: List[String]): List[String] = getopts(args.map(_.toList)).map(_.mkString)
 | 
|  |     67 |   def apply(args: Array[String]): List[String] = apply(args.toList)
 | 
|  |     68 | }
 |