2 // vim: foldmethod=marker
6 * @author Yoshinari Takaoka <takaoka@beatcraft.com>
7 * @license http://www.opensource.org/licenses/bsd-license.php The BSD License
12 if (!defined('ETHNA_OPTVALUE_IS_DISABLED')) {
13 define('ETHNA_OPTVALUE_IS_DISABLED', 1);
15 if (!defined('ETHNA_OPTVALUE_IS_REQUIRED')) {
16 define('ETHNA_OPTVALUE_IS_REQUIRED', 2);
18 if (!defined('ETHNA_OPTVALUE_IS_OPTIONAL')) {
19 define('ETHNA_OPTVALUE_IS_OPTIONAL', 3);
25 * PEAR への依存を排除するため、 Console_Getopt クラスを最実装したもの
27 * @author Yoshinari Takaoka <takaoka@beatcraft.com>
30 * @see http://pear.php.net/manual/en/package.console.console-getopt.php
35 * PHP 設定を考慮して、$argv 配列を読みます。
36 * ini ディレクティブ中の register_argc_argv を考慮します。
38 * 注意: PHP 4.2.0 以前では、$argv を読むためには
39 * register_globals が ON になっている必要が
40 * ありました。Ethna は この設定がoffであるこ
41 * とを前提にして書かれているため、ここでは考
44 * @return array - オプションとパラメータを含む配列、
47 function readPHPArgv()
51 if (ini_get('register_argc_argv') == false) {
52 return Ethna::raiseError(
53 'Could not read cmd args (register_argc_argv=Off?'
60 * コマンドラインオプションをパースし、結果を返します。
62 * @param array $args - コマンドライン引数の配列
63 * @param string $shortoptions - 使用できる短いオプション目録を指定します。
64 * @param array $longoptions - 使用できる長いオプション目録を指定します。
66 * @return array - パースされたオプションと非オプションのコマンドライン引数
67 * の 2つの要素からなる配列、もしくは Ethna_Error 。
69 function getopt($args, $shortoptions, $longoptions = NULL)
71 $shortopts = $this->_parseShortOption($shortoptions);
72 if (Ethna::isError($shortopts)) {
75 $longopts = $this->_parseLongOption($longoptions);
76 if (Ethna::isError($longopts)) {
80 $parsed_arguments = array();
81 $nonparsed_arguments = array();
83 for ($pos = 0; $pos < count($args); $pos++) {
86 $next_arg = isset($args[$pos + 1]) ? $args[$pos + 1] : NULL;
87 $is_nextarg_is_value = false;
90 if (strpos($arg, '--') === 0) { // long option
93 // GNU getopt(3) の場合は、長いオプションは他と重なら
94 // ない限りにおいて短縮できる。たとえば --foo, --fuji
95 // というオプションが定義された場合、 --fo や --fu と
98 // PEAR の Console_Getopt はこの短縮指定に対応していな
99 // い。よって、それを使用してきた Ethna でもそこまでは
104 $lopt = str_replace('--', '', $arg);
105 $opt_and_value = explode('=', $lopt);
106 $opt = $opt_and_value[0];
107 if (!array_key_exists($opt, $longopts)) {
108 return Ethna::raiseError("unrecognized option --$opt");
112 $required = $longopts[$opt];
114 if (count($opt_and_value) == 2) {
115 $value = $opt_and_value[1]; // --foo=bar
116 } elseif (strpos('-', $next_arg) !== 0
117 && $required == ETHNA_OPTVALUE_IS_REQUIRED) {
118 if (!empty($next_arg)) { // --foo bar
120 // == が設定されていた場合は値として解釈「しない」
128 case ETHNA_OPTVALUE_IS_REQUIRED:
129 if ($value === NULL) {
130 return Ethna::raiseError(
131 "option --$opt requires an argument"
135 case ETHNA_OPTVALUE_IS_DISABLED:
136 if ($value !== NULL) {
137 return Ethna::raiseError(
138 "option --$opt doesn't allow an argument"
144 // 長いオプションの場合は、-- 付きでオプション名を記録する
145 // Console_Getopt 互換にするため。
146 $parsed_arguments[] = array("--$opt", $value);
148 } elseif (strpos($arg, '-') === 0) { // short option
151 // -abcd のように、オプションと値が続けて
152 // 入力される場合がある。この場合どうオプションを解釈
153 // するかの仕様は、GNU getopt(3) の仕様に従う
155 // 1. abcd を1文字ずつに分解し、a, b, c, d にする
157 // 2. ':' (値必須) として設定されていた場合は、次の文字以降は
158 // 全て値として解釈する。この場合は次のargvは値として解釈し
159 // ない。また、次の文字がなく、次の argv が値だった場合は、
161 // 3. '::'(値が任意) として設定されていた場合も次の文字以降を
162 // 全て値として解釈するが、次の文字がない場合でも次のargvは
165 // 4. 無設定(値設定禁止)の場合は、次の文字もオプションとして解
166 // 釈する。また、次のargvは値として解釈しない
168 // @see LANG=C; man 3 getopt (日本語マニュアルは見ない方がいいかも)
169 // @see http://www.gnu.org/software/libtool/manual/libc/Using-Getopt.html
171 // TODO: ambiguous なオプションを検出できるようにする
174 $sopt = str_replace('-', '', $arg);
175 $sopt_len = strlen($sopt);
177 for ($sopt_pos = 0; $sopt_pos < $sopt_len; $sopt_pos++) {
180 $opt = $sopt[$sopt_pos];
183 $do_next_arg = false;
184 $required = isset($shortopts[$opt]) ? $shortopts[$opt] : NULL;
186 case ETHNA_OPTVALUE_IS_REQUIRED:
187 case ETHNA_OPTVALUE_IS_OPTIONAL:
189 && $required == ETHNA_OPTVALUE_IS_REQUIRED) {
190 if ($next_arg[0] != '-') { // -a hoge
198 $value = substr($sopt, $sopt_pos + 1);
199 $value = (empty($value)) ? NULL : $value;
201 if ($required == ETHNA_OPTVALUE_IS_REQUIRED
203 return Ethna::raiseError(
204 "option -$opt requires an argument"
207 // ':' または '::' が設定された場合は、次の文字
208 // 以降を全て値として解釈するため、次のargv要素に
212 case ETHNA_OPTVALUE_IS_DISABLED:
213 // 値を設定禁止にした場合は、値が解釈されなく
217 return Ethna::raiseError("unrecognized option -$opt");
221 // 短いオプションの場合は、- を付けないでオプション名を記録する
222 // Console_Getopt 互換にするため。
223 $parsed_arguments[] = array($opt, $value);
225 if ($do_next_arg === true) {
230 } else { // オプションとして解釈されない
231 $nonparsed_arguments[] = $arg;
235 return array($parsed_arguments, $nonparsed_arguments);
241 * @param string $sopts 短いオプション目録
242 * @return array オプションと引数指定種別の配列
243 * エラーの場合は Ethna_Error
246 function _parseShortOption($sopts)
252 if (!preg_match('/^[A-Za-z:]+$/', $sopts)) {
253 return Ethna::raiseError('invalid short options.');
256 $analyze_result = array();
258 for ($pos = 0; $pos < strlen($sopts); $pos++) {
259 $char = $sopts[$pos];
260 $next_char = (isset($sopts[$pos + 1]))
263 $next2_char = (isset($sopts[$pos + 2]))
271 // $sopts[$pos] is character.
272 if ($next_char == ':' && $next2_char == ':') {
273 $analyze_result[$char] = ETHNA_OPTVALUE_IS_OPTIONAL; // 値は任意
274 } elseif ($next_char == ':' && $next2_char != ':') {
275 $analyze_result[$char] = ETHNA_OPTVALUE_IS_REQUIRED; // 値は必須
277 $analyze_result[$char] = ETHNA_OPTVALUE_IS_DISABLED; // 値は不要
281 return $analyze_result;
287 * @param array $lopts 長いオプション目録
288 * @return array オプションと引数指定種別の配列
289 * エラーの場合は Ethna_Error
292 function _parseLongOption($lopts)
298 if (!is_array($lopts)) {
299 return Ethna::raiseError('invalid long options.');
302 $analyze_result = array();
304 foreach ($lopts as $opt) {
305 if (preg_match('/==$/', $opt) > 0) {
306 $opt = substr($opt, 0, -2);
307 $analyze_result[$opt] = ETHNA_OPTVALUE_IS_OPTIONAL; // 値は任意
308 } elseif (preg_match('/=$/', $opt) > 0) {
309 $opt = substr($opt, 0, -1);
310 $analyze_result[$opt] = ETHNA_OPTVALUE_IS_REQUIRED; // 値は必須
312 $analyze_result[$opt] = ETHNA_OPTVALUE_IS_DISABLED; // 値は不要
316 return $analyze_result;