123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. #!/usr/bin/perl
  2. # make an executable file
  3. use strict;
  4. use warnings;
  5. sub usage; sub mkexec; sub guesstype; sub guesspurpose;
  6. sub getcmd; sub launch_editor;
  7. my $DEFAULT_TYPE = 'sh';
  8. my $DEFAULT_MODE = 0755;
  9. my $DEFAULT_FORCE = 0;
  10. my $DEFAULT_EDIT = 0;
  11. ## .... ## . . . . . . . . . . .
  12. ## INIT ## -------------------------------------------------------------------
  13. ## '''' ## ' ' ' ' ' ' ' ' ' ' '
  14. my $force = $DEFAULT_FORCE;
  15. my $edit = $DEFAULT_EDIT;
  16. my $name; my $type; my $purpose;
  17. foreach (@ARGV) {
  18. if (m/\b-f\b|\b--force\b/) {
  19. $force++;
  20. } elsif (m/\b-e\b|\b--edit\b/) {
  21. $edit++;
  22. } elsif (defined $type) {
  23. $purpose = $_;
  24. } elsif (defined $name) {
  25. $type = $_;
  26. } else {
  27. $name = $_;
  28. }
  29. }
  30. usage unless defined $name;
  31. $type = guesstype $name unless defined $type;
  32. $purpose = guesspurpose $name unless defined $purpose;
  33. my $bangs = {
  34. pl => `which perl`,
  35. sh => '/bin/sh',
  36. py => `which python`,
  37. bash => '/bin/bash',
  38. sed => `which sed`,
  39. bc => `which bc`,
  40. exp => `which expect` . " -f",
  41. };
  42. my $templates = {
  43. py => {
  44. '' => "if __name__ == '__main__':\n",
  45. test => "import unittest\n\n\n"
  46. . "class TestCase(unittest.TestCase):\n"
  47. . " pass\n\n\n"
  48. . "if __name__ == '__main__':\n"
  49. ." unittest.main()\n",
  50. },
  51. pl => {
  52. '' => "use strict;\nuse warnings;\n",
  53. test => "use strict;\nuse warnings;\nuse Test::More;\n\n",
  54. },
  55. bash => {
  56. 'ffoo' => "\n. \"\$(ffoom path)\" || exit 3\n\n"
  57. . "ffoo import pretty\n\n"
  58. . "FFOO_DEBUG=true",
  59. 'shellfu' => "\n. \"\$(shellfu-get path)\" || exit 3\n\n"
  60. . "shellfu import pretty\n\n"
  61. . "SHELLFU_DEBUG=\${SHELLFU_DEBUG:-true}\n\n"
  62. . "usage() {\n"
  63. . " mkusage \"arg...\"\n"
  64. . "}\n\n"
  65. . "main() {\n"
  66. . " while true; do case \$1 in\n"
  67. . " -*) usage ;;\n"
  68. . " *) break ;;\n"
  69. . " esac done\n"
  70. . "}\n\n"
  71. . "main \"@\"",
  72. },
  73. };
  74. my $editors = [ qw/ vim editor / ];
  75. my $cmds;
  76. $cmds->{vim}->{test} = "vim --version 2>/dev/null";
  77. $cmds->{vim}->{run} = "vim +\"normal G\$\" '%s'";
  78. $cmds->{editor}->{test} = "editor --version 2>/dev/null";
  79. $cmds->{editor}->{run} = "editor '%s'";
  80. unless ($purpose eq ""
  81. or exists $templates->{$type}->{$purpose}) {
  82. warn "undefined purpose '$purpose' for type '$type'\n";
  83. $purpose = "";
  84. }
  85. ## ... ## # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  86. ## RUN ## ----------------------------------------------------------------- #
  87. ## ''' ## # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
  88. if (exists $bangs->{$type}) {
  89. $force and -e $name
  90. and (defined `cp "$name" "$name~"` or die $!);
  91. (not -e $name or $force)
  92. and mkexec $name, mkbody($type, $purpose);
  93. chmod $DEFAULT_MODE, $name;
  94. launch_editor $name if $edit;
  95. } else {
  96. die "unknown type: $type\n";
  97. }
  98. ## .... ## ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''-.
  99. ## SUBZ ## ----------------------------------------------------------------- :
  100. ## '''' ## ..............................................................-'
  101. sub usage {
  102. print STDERR "usage: $0 [-f|-e] filename [type] [purpose]\n";
  103. exit 0;
  104. }
  105. sub guesstype {
  106. my $name = shift;
  107. my ($ext) = $name =~ m|\.(\w+)$|;
  108. return ( $ext ? $ext : $DEFAULT_TYPE);
  109. }
  110. sub guesspurpose {
  111. my $name = shift;
  112. my $testword = '[tT][eE][sS][tT]';
  113. return 'test' if $name =~ m|^test_|i;
  114. return 'test' if $name =~ m|_test.py$|i;
  115. return 'test' if $name =~ m|_test.pl$|i;
  116. return 'test' if $name =~ m|\btest\w*/|i;
  117. return 'test' if $name =~ m|.*Test/|i;
  118. return 'test' if $name =~ m|\bt/|;
  119. return ''
  120. }
  121. sub mkbody {
  122. my $type = shift;
  123. my $purpose = shift;
  124. my $tmpl = "";
  125. $tmpl .= $templates->{$type}->{$purpose}
  126. if exists $templates->{$type}
  127. and exists $templates->{$type}->{$purpose};
  128. return sprintf "#!%s\n%s\n", $bangs->{$type}, $tmpl;
  129. }
  130. sub mkexec {
  131. my ($name, $body) = @_;
  132. open EXE, ">", $name or die "cannot open $name for writing: $!\n";
  133. -W EXE or die "file $name is not writable\n";
  134. print EXE $body;
  135. close EXE or die "cannot close $name: $!\n";
  136. }
  137. sub get_cmd {
  138. foreach (@$editors) {
  139. return $cmds->{$_}->{run} if `$cmds->{$_}->{test}`
  140. }
  141. warn "no supported editor available\n";
  142. return;
  143. }
  144. sub launch_editor {
  145. my $name = shift;
  146. my $form = get_cmd;
  147. if ($form) {
  148. my $command = sprintf get_cmd, $name;
  149. exec "$command";
  150. } else { return }
  151. warn "failed to launch editor: $form\n";
  152. }