File Coverage

/usr/local/bin/optex
Criterion Covered Total %
statement 169 302 55.9
branch 30 112 26.7
condition 15 45 33.3
subroutine 27 36 75.0
pod n/a
total 241 495 48.6


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3 10     10   63551 use App::optex;
  10         1582  
  10         541  
4 10         1947508 my $version = $App::optex::VERSION;
5              
6              
7             =encoding utf8
8              
9             =head1 NAME
10              
11             optex - General purpose command option wrapper
12              
13             =head1 VERSION
14              
15             Version 1.0601
16              
17             =head1 SYNOPSIS
18              
19             B I [ B<-M>I ] ...
20              
21             or I -> B symlink, or
22              
23             B I [ -l | -m ] ...
24              
25             --link, --ln create symlink
26             --unlink, --rm remove symlink
27             --ls list link files
28             --rc list rc files
29             --nop, -x disable option processing
30             --[no]module disable module option on arguments
31              
32             =cut
33              
34              
35 10     10   248 use v5.14;
  10         34  
36 10     10   60 use warnings;
  10         101  
  10         703  
37              
38 10     10   6055 use utf8;
  10         3340  
  10         57  
39 10     10   6638 use Encode;
  10         209887  
  10         1333  
40 10     10   6203 use open IO => ':utf8';
  10         17032  
  10         63  
41              
42 10     10   7830 use Pod::Usage;
  10         716823  
  10         1922  
43 10     10   8507 use Data::Dumper;
  10         101416  
  10         1202  
44 10         35 $Data::Dumper::Sortkeys = 1;
45 10     10   106 use Cwd qw(abs_path);
  10         18  
  10         734  
46 10     10   67 use List::Util qw(uniq max);
  10         7167  
  10         6013  
47 10     10   12763 use Text::ParseWords qw(shellwords);
  10         17754  
  10         708  
48 10     10   5996 use IO::File;
  10         87698  
  10         1369  
49 10     10   6009 use App::optex::Config;
  10         169726  
  10         18766  
50              
51 10     10   514 binmode STDOUT, ":encoding(utf8)";
  10         8890  
  10         176  
  10         60  
52 10         12121 binmode STDERR, ":encoding(utf8)";
53              
54 10         378 our $rcloader;
55 10   33     100 our $debug //= $ENV{DEBUG_OPTEX};
56 10         16 our $no_operation;
57 10         24 our $mod_opt = '-M';
58 10         25 our $mod_arg = 1; # Process -M option in target command
59 10         14 our $exit_code;
60              
61 10   33     82 my $command_path = $ENV{OPTEX_INVOKED_AS} // $0;
62 10 50       108 my($cmd_dir, $cmd_name) = ($command_path =~ m{ (.*) / ([^/]+) $ }x) or die;
63 10 50       560 my($abs_dir, $abs_name) = (abs_path($0) =~ m{ (.*) / ([^/]+) $ }x) or die;
64 10         49 my $env_MODULE_PATH = sprintf '%s_MODULE_PATH', uc($abs_name);
65 10         33 my $env_ROOT = sprintf '%s_ROOT', uc($abs_name);
66 10         23 my $env_BINDIR = sprintf '%s_BINDIR', uc($abs_name);
67 10         27 my $env_CONFIG = sprintf '%s_CONFIG', uc($abs_name);
68              
69 10 50       51 my $HOME = $ENV{HOME} or die "No \$HOME.\n";
70 10   33     72 my $config_dir = $ENV{$env_ROOT} || "${HOME}/.${abs_name}.d";
71 10         27 my $module_dir = $config_dir;
72 10   33     59 my $bin_dir = $ENV{$env_BINDIR} || "$config_dir/bin";
73 10   33     113 my $config_file = $ENV{$env_CONFIG} || "$config_dir/config.toml";
74              
75             ##
76             ## decode @ARGV
77             ##
78 10 50       105 @ARGV = map { utf8::is_utf8($_) ? $_ : decode('utf8', $_) } @ARGV;
  57         1105  
79              
80             ##
81             ## load config file
82             ##
83 10         305 my $config = App::optex::Config::load_config($config_file);
84 10   50     1011 my $alias = $config->{alias} //= {};
85 10   50     72 my $nomodule = $config->{"no-module"} //= [];
86 10         24 my %nomodule = do {
87 10 50       44 if (ref $nomodule eq 'ARRAY') {
    0          
88 10         20 map { $_ => 1 } @{$nomodule};
  0         0  
  10         40  
89             }
90             elsif (ref $nomodule eq 'HASH') {
91 0         0 %{$nomodule};
  0         0  
92             }
93             else {
94 0         0 ($nomodule => 1);
95             }
96             };
97              
98             ##
99             ## setup Getopt::EX
100             ##
101 10         6030 require Getopt::EX::Loader;
102 10         857884 $rcloader = Getopt::EX::Loader->new(
103             BASECLASS => [ '', 'App::optex', 'Getopt::EX' ],
104             IGNORE_NO_MODULE => 1,
105             );
106              
107 10         699 load_rc("$config_dir/default.rc");
108              
109             ##
110             ## setup module search path
111             ##
112             my @private_mod_path = (
113 10         50 do {
114 10 50       53 if (my $mod_path = $ENV{$env_MODULE_PATH}) {
115 0         0 split /:/, $mod_path;
116             } else {
117 10         31 ();
118             }
119             },
120             $module_dir,
121             );
122              
123 10         53 prepend_path(@private_mod_path);
124              
125             ##
126             ## get target command name
127             ##
128 10         14 my $target_name = do {
129 10 50       38 if ($cmd_name ne $abs_name) {
130 0         0 $cmd_name;
131             } else {
132 10         47 self_option(\@ARGV);
133 4 50       1365 if (@ARGV) {
134 4         17 shift @ARGV;
135             } else {
136 0         0 usage();
137 0         0 exit 1;
138             }
139             }
140             };
141              
142             ##
143             ## alias
144             ##
145 4         29 my $aliased_name;
146 4 50       44 if (my $alias = $alias->{$target_name}) {
147 0         0 my($name, @opts) = do {
148 0 0       0 if (ref $alias eq 'ARRAY') {
149 0         0 @{$alias};
  0         0  
150             } else {
151 0         0 shellwords $alias;
152             }
153             };
154 0 0       0 if ($name ne '') {
155 0         0 $aliased_name = $name;
156 0         0 unshift @ARGV, @opts;
157             }
158             }
159              
160 4 50       36 if ($nomodule{$target_name}) {
161 0         0 $mod_arg = 0;
162             }
163              
164             ##
165             ## prepare command specific module path
166             ##
167             my @command_mod_path =
168 4         15 grep { -d $_ } map { "$_/$target_name" } @private_mod_path;
  4         121  
  4         35  
169              
170 4         155 prepend_path(@command_mod_path, @private_mod_path);
171              
172 4 50 33     97 if ($mod_arg and @ARGV > 0 and $ARGV[0] eq $mod_opt) {
      33        
173 0         0 show_modules();
174 0         0 exit;
175             }
176              
177             ##
178             ## load command specific rc file
179             ##
180 4 50       19 unless ($no_operation) {
181 4         90 load_rc("$config_dir/$target_name.rc");
182 4         117 $rcloader->configure(PARSE_MODULE_OPT => $mod_arg);
183 4         453 $rcloader->deal_with(\@ARGV);
184             }
185              
186 4   33     8869 my $exec_name = $aliased_name || search_path($target_name);
187 4 50       17 unless (defined $exec_name) {
188 0         0 warn "$abs_name: $target_name: command not found\n";
189 0         0 exit 127;
190             }
191              
192 4 50       12 warn "$abs_name: exec $exec_name @ARGV\n" if $debug;
193              
194             ##
195             ## execute target command
196             ##
197 4         2420777 my $status = system $exec_name, @ARGV;
198              
199             END {
200 10 50   10   20208 if (defined $exit_code) {
    100          
201 0         0 $? = $exit_code;
202             } elsif (defined $status) {
203 4         172 $? = $status >> 8;
204             }
205 10         810 close STDERR;
206 10         0 close STDOUT;
207             }
208              
209             ######################################################################
210              
211             sub load_rc {
212 14     14   59 my $rc = shift;
213 14         90 $rcloader->load(FILE => $rc);
214 14 50       459 warn "$abs_name: load $rc\n" if $debug;
215             }
216              
217             sub search_path {
218 4     4   13 my $new = shift;
219 4 50 33     88 return $new if $new =~ m{/} && -x $new;
220              
221             # Scan PATH and return the first executable that is not the running
222             # optex binary itself (avoid re-entering the wrapper and looping).
223 4         269 my $self_abs = abs_path("$abs_dir/$abs_name");
224              
225 4   50     171 for my $path (split /:+/, $ENV{PATH} // '') {
226 12         31 my $candidate = "$path/$new";
227 12 100       195 -x $candidate or next;
228 4 50 33     38 next if $path eq $cmd_dir and $new eq $cmd_name;
229              
230 4   33     11 my $cand_abs = eval { abs_path($candidate) } || $candidate;
231 4 50       17 next if $cand_abs eq $self_abs;
232 4         27 return $candidate;
233             }
234              
235 0         0 return;
236             }
237              
238             sub self_option {
239 10     10   21 my $argv = shift;
240              
241 10         61 local $rcloader = Getopt::EX::Loader->new(
242             BASECLASS => [ '', 'App::optex', 'Getopt::EX' ],
243             );
244 10         460 $rcloader->load(FILE => "$config_dir/$abs_name.rc");
245 10         189 $rcloader->deal_with($argv);
246              
247 10     10   8478 use Getopt::Long qw(GetOptionsFromArray);
  10         146735  
  10         48  
248 4         32056 Getopt::Long::Configure(qw"bundling require_order");
249 10     10   9162 use Getopt::EX::Hashed qw(has); {
  10         98256  
  10         73  
250 4         697 Getopt::EX::Hashed->configure(DEFAULT => [ is => 'ro' ]);
  4         537  
251 4     0   1330 has debug => " ! d " , action => sub { $debug = $_[1] };
  0         0  
252 4         1139 has version => " v " ;
253 4         327 has man => " h " ;
254 4         378 has link => " ln " ;
255 4         333 has unlink => " rm " ;
256 4         349 has force => " f " ;
257 4         333 has ls => " " ;
258 4         320 has rc => " " ;
259 4         339 has long => " l " ;
260 4         300 has path => " p " ;
261 4         290 has cat => " m " ;
262 4         337 has M => " " ;
263 4     0   356 has module => " ! " , action => sub { $mod_arg = $_[1] };
  0         0  
264 4     0   386 has nop => " ! x " , action => sub { $no_operation = $_[1] };
  0         0  
265 4     0   417 has exit => " =i " , action => sub { $exit_code = $_[1] } ;
  0         0  
266 10     10   4300 }; no Getopt::EX::Hashed;
  10         17  
  10         47  
267 4         486 my $opt = Getopt::EX::Hashed->new->reset();
268 4 50       5429 GetOptionsFromArray($argv, $opt->optspec, $rcloader->builtins) or usage();
269              
270 4 50 33     18590 if ($opt->man) {
    50          
    50          
    50          
    50          
    50          
271 0         0 exec "perldoc $abs_name";
272 0         0 die "exec: $!";
273             }
274             elsif ($opt->version) {
275 0         0 print $version, "\n";
276 0         0 exit;
277             }
278             elsif ($opt->link || $opt->unlink) {
279 0         0 _symlink($argv, $opt);
280 0         0 exit 0;
281             }
282             elsif ($opt->ls) {
283 0         0 _ls($argv, $opt);
284 0         0 exit 0;
285             }
286             elsif ($opt->rc) {
287 0         0 _rc($argv, $opt);
288 0         0 exit 0;
289             }
290             elsif ($opt->M) {
291 0         0 show_modules();
292 0         0 exit 0;
293             }
294              
295 4         301 return;
296             }
297              
298             sub _symlink {
299 0     0   0 my($argv, $op) = @_;
300 0 0       0 -d $bin_dir or die "Directory $bin_dir does not exist.\n";
301              
302 0   0     0 my $script_path = $ENV{OPTEX_SCRIPT_PATH} // $0;
303 0         0 my @target = @$argv;
304 0         0 for my $target (@target) {
305 0         0 my $link = "$bin_dir/$target";
306 0 0       0 if ($op->{link}) {
    0          
307 0 0       0 -f $link and do { warn "$link already exists.\n"; next };
  0         0  
  0         0  
308 0 0       0 symlink $script_path, $link or die "$link: $!\n";
309 0         0 print "$link -> $script_path\n";
310             }
311             elsif ($op->{unlink}) {
312 0 0       0 -l $link or do { warn "$link is not symlink\n"; next };
  0         0  
  0         0  
313 0 0       0 if ((my $name = readlink $link) ne $script_path ) {
314 0 0       0 if (not $op->{force}) {
315 0         0 warn
316             "$link has unexpected link: -> $name\n" .
317             "Use -f option to force unlink.\n" ;
318 0         0 next;
319             }
320             }
321 0 0       0 unlink $link or die "$link: $!\n";
322 0         0 print "$link removed.\n";
323             }
324             }
325             }
326              
327 10     10   14299 use Text::VisualWidth::PP 'vwidth';
  10         44608  
  10         1204  
328              
329             sub _ls {
330 0     0   0 my($argv, $op) = @_;
331              
332 10     10   5091 use IO::Dir;
  10         125710  
  10         8707  
333 0 0       0 my $dir = IO::Dir->new($bin_dir) or die "$bin_dir: $!\n";
334 0         0 my @dirent = do {
335 0         0 sort { $a cmp $b }
336 0         0 map { decode 'utf8', $_ }
337 0         0 grep { not /^\./ }
  0         0  
338             $dir->read;
339             };
340 0         0 $dir->close;
341              
342 0         0 my @aliases = sort keys %{$alias};
  0         0  
343              
344 0         0 my $max = max map { vwidth $_ } @dirent, @aliases;
  0         0  
345              
346 0         0 print "[link]\n";
347 0         0 for my $ent (@dirent) {
348 0         0 my $path = "$bin_dir/$ent";
349 0 0       0 if (-l $path) {
350 0         0 print "\t";
351 0 0       0 print $op->{path} ? $path : $ent;
352 0 0       0 if ($op->{long}) {
353 0         0 print ' ' x ($max - vwidth $ent);
354 0         0 my $target = do {
355             # if (my $val = $alias->{$ent}) {
356             # ref($val) eq 'ARRAY' ? join(' ', @$val) : $val;
357             # } else {
358 0         0 readlink $path;
359             # }
360             };
361 0         0 print " -> ", $target;
362             }
363 0         0 print "\n";
364             }
365 0 0       0 warn "$ent: not exist\n" unless -f $path;
366             }
367              
368 0         0 print "[alias]\n";
369 0         0 for my $key (@aliases) {
370 0         0 my $val = $alias->{$key};
371 0 0       0 my $command = ref($val) eq 'ARRAY' ? join(' ', @$val) : $val;
372 0         0 print "\t";
373 0         0 print $key;
374 0 0       0 if ($op->{long}) {
375 0         0 print ' ' x ($max - vwidth $key);
376 0         0 printf " => %s", $command;
377             }
378 0         0 print "\n";
379             }
380             }
381              
382             sub _rc {
383 0     0   0 my($argv, $op) = @_;
384 0         0 my @command = @$argv;
385 0         0 my @rc = map { s/(?
  0         0  
386 0         0 my %rc = map { ($_ => 1) } @rc;
  0         0  
387 10     10   109 use IO::Dir;
  10         17  
  10         2513  
388 0 0       0 my $dir = IO::Dir->new($config_dir) or die "$config_dir: $!\n";
389 0         0 my @dirent = do {
390 0 0       0 grep { %rc == 0 or $rc{$_} }
391 0 0       0 grep { /\.rc$/ }
  0         0  
392             @rc ? @rc : sort $dir->read
393             };
394 0         0 $dir->close;
395              
396 0         0 for my $ent (@dirent) {
397 0         0 my $path = "$config_dir/$ent";
398 0 0       0 if ($op->{cat}) {
399 10     10   73 use IO::File;
  10         20  
  10         7840  
400 0 0       0 my $fh = IO::File->new($path) or do {
401 0         0 warn "$path: $!\n";
402 0         0 next;
403             };
404 0         0 while (<$fh>) {
405 0 0       0 print "$ent:" if @dirent > 1;
406 0         0 print;
407             }
408 0         0 $fh->close;
409             } else {
410 0 0       0 print $op->{long} ? $path : $ent;
411 0         0 print "\n";
412             }
413             }
414             }
415              
416             ######################################################################
417              
418             sub usage {
419 0     0   0 pod2usage(-verbose => 0,
420             -message => <<" EOS" =~ s/^\s+//mgr
421             Use `perldoc $abs_name` for document.
422             Use `$abs_name [command] ${mod_opt}help` for available options.
423             EOS
424             );
425             }
426              
427 4     10   268 my @ORIG_INC; BEGIN { @ORIG_INC = @INC }
  10         25916  
428 4         1329 my @mod_path;
429             sub prepend_path {
430 14     14   147 @mod_path = uniq @_;
431 14         378 @INC = (@mod_path, @ORIG_INC);
432             }
433              
434             sub show_modules {
435             my $path = @_ ? shift : [
436             @mod_path,
437 0 0   0     grep { -d } map { "$_/App/optex" } @INC,
  0            
  0            
438             ];
439 0           print "MODULES:\n";
440 0           for my $path (@$path) {
441 0           my($name) = $path =~ m:([^/]+)$:;
442 0           my @module = do {
443             # grep { not /\bdefault\.pm$/ }
444 0           glob "$path/[a-z0-9]*.pm";
445             };
446 0 0         next unless @module;
447 0           print " $path\n";
448 0           for my $mod (@module) {
449 0           printf " ${mod_opt}%s\n", $mod =~ /([^\/]*)\.pm/;
450             }
451 0           print "\n";
452             }
453             }
454              
455             =head1 DESCRIPTION
456              
457             B is a general purpose command option handling wrapper
458             utilizing Perl module L. It enables user to define their
459             own option aliases for any commands on the system, and provide module
460             style extensibility.
461              
462             Target command is given as an argument:
463              
464             % optex command
465              
466             or as a symbolic linked file to B:
467              
468             command -> optex
469              
470             If the configuration file F<~/.optex.d/>IF<.rc> exists, it is
471             evaluated before execution and command arguments are pre-processed
472             using it.
473              
474              
475             =head2 OPTION ALIASES
476              
477             Think of macOS's C command, which does not have C<-I[TIMESPEC]>
478             option. Using B, these can be implemented by preparing
479             following setting in F<~/.optex.d/date.rc> file.
480              
481             option -I -Idate
482             option -Idate +%F
483             option -Iseconds +%FT%T%z
484             option -Iminutes +%FT%H:%M%z
485             option -Ihours +%FT%H%z
486              
487             option --iso-8601 -I
488             option --iso-8601=date -Idate
489             option --iso-8601=seconds -Iseconds
490             option --iso-8601=minutes -Iminutes
491             option --iso-8601=hours -Ihours
492              
493             Then next command will work as expected.
494              
495             % optex date -Iseconds
496              
497             If a symbolic link C<< date -> optex >> is found in command search
498             path, you can use it just same as standard command, but with
499             unsupported options.
500              
501             % date -Iseconds
502              
503             Common configuration is stored in F<~/.optex.d/default.rc> file, and
504             those rules are applied to all commands executed through B.
505              
506             Actually, C<--iso-8601> option can be defined simpler as this:
507              
508             option --iso-8601 -I$
509              
510             This works fine almost always, but fails with sole C<--iso-8601>
511             option preceding other option like this:
512              
513             % date --iso-8601 -u
514              
515             =head2 COMMAND ALIASES
516              
517             B's command alias is no different from the alias function of
518             shell, but it is effective in that it can be executed as a command
519             from a tool or script, and can be managed collectively in a
520             configuration file.
521              
522             Command aliases can be set in the configuration file
523             (F<~/.optex.d/config.toml>) like this:
524              
525             [alias]
526             tc = "optex -Mtextconv"
527              
528             You can make symbolic link from C to C like this:
529              
530             % optex --ln tc
531              
532             And include F<$HOME/.optex.d/bin> in your C evnironment.
533              
534             The C module can be used to convert files given as arguments
535             to plain text. Defined in this way, Word files can be compared as
536             follows.
537              
538             % tc diff A.docx B.docx
539              
540             Alias name is used to find rc file and module directory. In the above
541             example, F<~/.optex.d/tc.rc> and F<~/.optex.d/tc/> will be referred.
542              
543             It is also possible to write shell scripts in the config file. The
544             following example implements the C-shell C command.
545              
546             [alias]
547             repeat = [ 'bash', '-c', '''
548             while getopts 'c:i:' OPT; do
549             case $OPT in
550             c) count=$OPTARG;;
551             i) sleep=$OPTARG;;
552             esac
553             done; shift $((OPTIND - 1))
554             case $1 in
555             [0-9]*) count=$1; shift;;
556             esac
557             while ((count--)); do
558             eval "$*"
559             [ "$sleep" ] && (( count > 0 )) && sleep $sleep
560             done
561             ''', 'repeat' ]
562              
563             Read L section.
564              
565             =head2 MACROS
566              
567             Complex string can be composed using macro C. Next example is
568             an awk script to count vowels in the text, to be declared in file
569             F<~/.optex.d/awk.rc>.
570              
571             define __delete__ /[bcdfgkmnpsrtvwyz]e( |$)/
572             define __match__ /ey|y[aeiou]*|[aeiou]+/
573             define __count_vowels__ <
574             {
575             s = tolower($0);
576             gsub(__delete__, " ", s);
577             for (count=0; match(s, __match__); count++) {
578             s=substr(s, RSTART + RLENGTH);
579             }
580             print count " " $0;
581             }
582             EOS
583             option --vowels __count_vowels__
584              
585             This can be used like this:
586              
587             % awk --vowels /usr/share/dict/words
588              
589             When setting complex option, C directive is useful. C
590             works almost same as C
591             scope, and not available for command line option.
592              
593             expand repository ( -name .git -o -name .svn -o -name RCS )
594             expand no_dots ! -name .*
595             expand no_version ! -name *,v
596             expand no_backup ! -name *~
597             expand no_image ! -iname *.jpg ! -iname *.jpeg \
598             ! -iname *.gif ! -iname *.png
599             expand no_archive ! -iname *.tar ! -iname *.tbz ! -iname *.tgz
600             expand no_pdf ! -iname *.pdf
601              
602             option --clean \
603             repository -prune -o \
604             -type f \
605             no_dots \
606             no_version no_backup \
607             no_image \
608             no_archive \
609             no_pdf
610              
611             % find . --clean -print
612              
613              
614             =head2 MODULES
615              
616             B also supports module extension. In the example of C,
617             module file is found at F<~/.optex.d/date/> directory. If default
618             module, F<~/.optex.d/date/default.pm> exists, it is loaded
619             automatically on every execution.
620              
621             This is a normal Perl module, so package declaration and the final
622             true value is necessary. Between them, you can put any kind of Perl
623             code. For example, next program set environment variable C to
624             C before executing C command.
625              
626             package default;
627             $ENV{LANG} = 'C';
628             1;
629              
630             % /bin/date
631             2017年 10月22日 日曜日 18時00分00秒 JST
632              
633             % date
634             Sun Oct 22 18:00:00 JST 2017
635              
636             Other modules are loaded using C<-M> option. Unlike other options,
637             C<-M> have to be placed at the beginning of argument list. Module
638             files in F<~/.optex.d/date/> directory are used only for C
639             command. If the module is placed on F<~/.optex.d/> directory, it can
640             be used from all commands.
641              
642             If you want use C<-Mes> module, make a file F<~/.optex.d/es.pm> with
643             following content.
644              
645             package es;
646             $ENV{LANG} = 'es_ES';
647             1;
648              
649             % date -Mes
650             domingo, 22 de octubre de 2017, 18:00:00 JST
651              
652             When the specified module was not found in library path, B
653             ignores the option and stops argument processing immediately. Ignored
654             options are passed through to the target command.
655              
656             Module is also used with subroutine call. Suppose
657             F<~/.optex.d/env.pm> module look like:
658              
659             package env;
660             sub setenv {
661             while (($a, $b) = splice @_, 0, 2) {
662             $ENV{$a} = $b;
663             }
664             }
665             1;
666              
667             Then it can be used in more generic fashion. In the next example,
668             first format is easy to read, but second one is more easy to type
669             because it does not have special characters to be escaped.
670              
671             % date -Menv::setenv(LANG=de_DE) # need shell quote
672             % date -Menv::setenv=LANG=de_DE # alternative format
673             So 22 Okt 2017 18:00:00 JST
674              
675             Option aliases can be also declared in the module, at the end of file,
676             following special literal C<__DATA__>. Using this, you can prepare
677             multiple set of options for different purposes. Think about generic
678             B module:
679              
680             package i18n;
681             1;
682             __DATA__
683             option --cn -Menv::setenv(LANG=zh_CN) // 中国語 - 簡体字
684             option --tw -Menv::setenv(LANG=zh_TW) // 中国語 - 繁体字
685             option --us -Menv::setenv(LANG=en_US) // 英語
686             option --fr -Menv::setenv(LANG=fr_FR) // フランス語
687             option --de -Menv::setenv(LANG=de_DE) // ドイツ語
688             option --it -Menv::setenv(LANG=it_IT) // イタリア語
689             option --jp -Menv::setenv(LANG=ja_JP) // 日本語
690             option --kr -Menv::setenv(LANG=ko_KR) // 韓国語
691             option --br -Menv::setenv(LANG=pt_BR) // ポルトガル語 - ブラジル
692             option --es -Menv::setenv(LANG=es_ES) // スペイン語
693             option --ru -Menv::setenv(LANG=ru_RU) // ロシア語
694              
695             This can be used like:
696              
697             % date -Mi18n --tw
698             2017年10月22日 週日 18時00分00秒 JST
699              
700             You can declare autoload module in your F<~/.optex.d/optex.rc> like:
701              
702             autoload -Mi18n --cn --tw --us --fr --de --it --jp --kr --br --es --ru
703              
704             Then you can use them without module option. In this case, option
705             C<--ru> is replaced by C<-Mi18n --ru> automatically.
706              
707             % date --ru
708             воскресенье, 22 октября 2017 г. 18:00:00 (JST)
709              
710             Module C is implemented as L and included in
711             this distribution. So it can be used as above without additional
712             installation.
713              
714             Modules can also define built-in options using C directive in
715             the C<__DATA__> section. Built-in options are processed by
716             L and must be specified before the target command name.
717             For example:
718              
719             optex -Mxform --xform-visible=2 cat file
720              
721             Here C<--xform-visible> is a built-in option defined in the C
722             module.
723              
724             =head1 STANDARD MODULES
725              
726             Standard modules are installed at C, and they can be
727             addressed with and without C prefix.
728              
729             =over 4
730              
731             =item -MB
732              
733             Print available option list. Option name is printed with substitution
734             form, or help message if defined. Use B<-x> option to omit help
735             message.
736              
737             Option B<--man> or B<-h> will print document if available. Option
738             B<-l> will print module path. Option B<-m> will show the module
739             itself. When used after other modules, print information about the
740             last declared module. Next command show the document about B
741             module.
742              
743             % optex -Mfirst -Msecond -Mhelp --man
744              
745             =item -MB
746              
747             Print debug messages.
748              
749             =item -MB
750              
751             Module to manipulate command argument.
752             See L for detail.
753              
754             =item -MB
755              
756             Module to implement command input/output filters.
757             See L for detail.
758              
759             =back
760              
761             =head1 Getopt::EX MODULES
762              
763             In addition to its own modules, B can also use C
764             modules. The standard C modules installed are these.
765              
766             =over 4
767              
768             =item -MB (L)
769              
770             You can display a Greek calendar by doing the following:
771              
772             optex -Mi18n cal --gr
773              
774             =back
775              
776             =head1 OPTIONS
777              
778             These options are not effective when B was executed from
779             symbolic link.
780              
781             =over 4
782              
783              
784             =item B<--link>, B<--ln> [ I ]
785              
786             Create symbolic link in F<~/.optex.d/bin> directory.
787              
788              
789             =item B<--unlink>, B<--rm> [ B<-f> ] [ I ]
790              
791             Remove symbolic link in F<~/.optex.d/bin> directory.
792              
793              
794             =item B<--ls> [ B<-l> ] [ I ]
795              
796             List symbolic link files in F<~/.optex.d/bin> directory.
797              
798              
799             =item B<--rc> [ B<-l> ] [ B<-m> ] [ I ]
800              
801             List rc files in F<~/.optex.d> directory.
802              
803              
804             =item B<--nop>, B<-x> I
805              
806             Stop option manipulation. Use full pathname otherwise.
807              
808              
809             =item B<-->[B]B
810              
811             B deals with module option (-M) on target command by default.
812             However, there is a command which also uses same option for own
813             purpose. Option B<--nomodule> disables that behavior. Other option
814             interpretation is still effective, and there is no problem using
815             module option in rc or module files.
816              
817              
818             =item B<--exit> I
819              
820             Usually B exits with status of executed command. This option
821             override it and force to exit with specified status code.
822              
823              
824             =back
825              
826              
827             =head1 CONFIGURATION FILE
828              
829             When starting up, B reads configuration file
830             F<~/.optex.d/config.toml> which is supposed to be written in TOML
831             format.
832              
833             =head2 PARAMETERS
834              
835             =over 4
836              
837             =item B
838              
839             Set commands for which B does not interpret module option
840             B<-M>. If the target command is found in this list, it is executed as
841             if option B<--no-module> is given to B.
842              
843             no-module = [
844             "greple",
845             "pgrep",
846             ]
847              
848             =item B
849              
850             Set command aliases. Example:
851              
852             [alias]
853             pgrep = [ "greple", "-Mperl", "--code" ]
854             hello = "echo -n 'hello world!'"
855              
856             Command alias can be invoked either from symbolic link and command
857             argument.
858              
859             =item B
860              
861             Load extra TOML fragments before applying the main configuration.
862             Accepts either a string or an array. Each entry may be a literal path
863             or a glob pattern. Tilde is expanded, and relative paths are resolved
864             against the file that declared the include. Includes are processed
865             depth-first; hashes merge recursively, arrays append, and scalars use
866             last-in-wins semantics. The main F is applied last so it
867             can override included values.
868              
869             include = [
870             "~/.optex.d/config.d/*.toml",
871             "local.toml",
872             ]
873              
874             Include directives can nest; cycles (including self-globs) are detected
875             and reported as errors.
876              
877             =back
878              
879              
880             =head1 FILES AND DIRECTORIES
881              
882             =over 4
883              
884              
885             =item F
886              
887             System module directory.
888              
889              
890             =item F<~/.optex.d/>
891              
892             Personal root directory.
893              
894              
895             =item F<~/.optex.d/config.toml>
896              
897             Configuration file.
898              
899              
900             =item F<~/.optex.d/default.rc>
901              
902             Common startup file.
903              
904              
905             =item F<~/.optex.d/>IF<.rc>
906              
907             Startup file for I.
908              
909              
910             =item F<~/.optex.d/>IF
911              
912             Module directory for I.
913              
914              
915             =item F<~/.optex.d/>IF
916              
917             Default module for I.
918              
919              
920             =item F<~/.optex.d/bin>
921              
922             Default directory to store symbolic links.
923              
924             This is not necessary, but it seems a good idea to make special
925             directory to contain symbolic links for B, placing it in your
926             command search path. Then you can easily add/remove it from the path,
927             or create/remove symbolic links.
928              
929             =back
930              
931              
932             =head1 ENVIRONMENT
933              
934             =over 4
935              
936             =item OPTEX_ROOT
937              
938             Override default root directory F<~/.optex.d>.
939              
940             =item OPTEX_CONFIG
941              
942             Override default configuration file F.
943              
944             =item OPTEX_MODULE_PATH
945              
946             Set module paths separated by colon (C<:>). These are inserted before
947             standard path.
948              
949             =item OPTEX_BINDIR
950              
951             Override default symbolic link directory F.
952              
953             =item OPTEX_SCRIPT_PATH
954              
955             Specify the path to use when creating symbolic links with C<--ln>.
956             If not set, C<$0> is used. This is useful when B is invoked
957             through a wrapper script (e.g., Homebrew installation) and you want
958             the symbolic links to point to the wrapper rather than the actual
959             script.
960              
961             =item OPTEX_INVOKED_AS
962              
963             Specify the original command path used to invoke B. This is
964             useful when B is invoked through a wrapper script and the
965             original C<$0> is lost. When a symbolic link to the wrapper is
966             executed, the wrapper should set this variable to its own C<$0> so
967             that B can determine the correct command name.
968              
969             =back
970              
971              
972             =head1 SEE ALSO
973              
974             L, L, L
975              
976             L
977              
978             L
979              
980             =head1 AUTHOR
981              
982             Kazumasa Utashiro
983              
984              
985             =head1 LICENSE
986              
987             You can redistribute it and/or modify it under the same terms
988             as Perl itself.
989              
990             Copyright ©︎ 2017-2026 Kazumasa Utashiro
991              
992              
993             =cut
994              
995              
996             # LocalWords: optex rc iso greple awk pdf LANG ENV Oct JST domingo
997             # LocalWords: setenv autoload PERLLIB BINDIR Utashiro Kazumasa