install-info.pl 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. #!/usr/bin/perl --
  2. BEGIN { # Work-around for bug #479711 in perl
  3. $ENV{PERL_DL_NONLAZY} = 1;
  4. }
  5. use Text::Wrap;
  6. use Dpkg;
  7. use Dpkg::Gettext;
  8. textdomain("dpkg");
  9. # FIXME: sort entries
  10. # FIXME: send to FSF ?
  11. sub version {
  12. printf _g("Debian %s version %s.\n"), $progname, $version;
  13. printf _g("
  14. Copyright (C) 1994,1995 Ian Jackson.");
  15. printf _g("
  16. This is free software; see the GNU General Public Licence version 2 or
  17. later for copying conditions. There is NO warranty.
  18. ");
  19. }
  20. sub usage {
  21. $file = $_[0];
  22. printf $file _g(
  23. "Usage: %s [<options> ...] [--] <filename>
  24. Options:
  25. --section <regexp> <title>
  26. put the new entry in the <regex> matched section
  27. or create a new one with <title> if non-existent.
  28. --menuentry=<text> set the menu entry.
  29. --description=<text> set the description to be used in the menu entry.
  30. --info-file=<path> specify info file to install in the directory.
  31. --dir-file=<path> specify file name of info directory file.
  32. --infodir=<directory> same as '--dir-file=<directory>/dir'.
  33. --info-dir=<directory> likewise.
  34. --keep-old do not replace entries nor remove empty ones.
  35. --remove remove the entry specified by <filename> basename.
  36. --remove-exactly remove the exact <filename> entry.
  37. --test enables test mode (no actions taken).
  38. --debug enables debug mode (show more information).
  39. --quiet do not show output messages.
  40. --help show this help message.
  41. --version show the version.
  42. "), $progname;
  43. }
  44. $dirfile = '/usr/share/info/dir';
  45. $maxwidth=79;
  46. $Text::Wrap::columns=$maxwidth;
  47. $backup='/var/backups/infodir.bak';
  48. $default='/usr/share/base-files/info.dir';
  49. $menuentry="";
  50. $description="";
  51. $sectionre="";
  52. $sectiontitle="";
  53. $infoentry="";
  54. $quiet=0;
  55. $nowrite=0;
  56. $keepold=0;
  57. $debug=0;
  58. $remove=0;
  59. my $remove_exactly;
  60. $0 =~ m|[^/]+$|; $name= $&;
  61. while ($ARGV[0] =~ m/^--/) {
  62. $_= shift(@ARGV);
  63. last if $_ eq '--';
  64. if ($_ eq '--version') {
  65. &version(STDOUT); exit 0;
  66. } elsif ($_ eq '--quiet') {
  67. $quiet=1;
  68. } elsif ($_ eq '--test') {
  69. $nowrite=1;
  70. } elsif ($_ eq '--keep-old') {
  71. $keepold=1;
  72. } elsif ($_ eq '--remove') {
  73. $remove=1;
  74. } elsif ($_ eq '--remove-exactly') {
  75. $remove=1;
  76. $remove_exactly=1;
  77. } elsif ($_ eq '--help') {
  78. &usage(STDOUT); exit 0;
  79. } elsif ($_ eq '--version') {
  80. &version; exit 0;
  81. } elsif ($_ eq '--debug') {
  82. open(DEBUG,">&STDERR")
  83. || &quit(sprintf(_g("could not open stderr for output! %s"), $!));
  84. $debug=1;
  85. } elsif ($_ eq '--section') {
  86. if (@ARGV < 2) {
  87. printf STDERR _g("%s: --section needs two more args")."\n", $name;
  88. &usage(STDERR); exit 1;
  89. }
  90. $sectionre= shift(@ARGV);
  91. $sectiontitle= shift(@ARGV);
  92. } elsif (m/^--(c?align|maxwidth)=([0-9]+)$/) {
  93. warn(sprintf(_g("%s: option --%s is deprecated (ignored)"), $name, $1)."\n");
  94. } elsif (m/^--info-?dir=/) {
  95. $dirfile = $' . '/dir';
  96. } elsif (m/^--info-file=/) {
  97. $filename = $';
  98. } elsif (m/^--menuentry=/) {
  99. $menuentry = $';
  100. } elsif (m/^--description=/) {
  101. $description = $';
  102. } elsif (m/^--dir-file=/) { # for compatibility with GNU install-info
  103. $dirfile = $';
  104. } else {
  105. printf STDERR _g("%s: unknown option \`%s'")."\n", $name, $_;
  106. &usage(STDERR); exit 1;
  107. }
  108. }
  109. if (!@ARGV) { &usage(STDERR); exit 1; }
  110. if ( !$filename ) {
  111. $filename= shift(@ARGV);
  112. $name = "$name($filename)";
  113. }
  114. if (@ARGV) { printf STDERR _g("%s: too many arguments")."\n", $name; &usage(STDERR); exit 1; }
  115. if ($remove) {
  116. printf(STDERR _g("%s: --section ignored with --remove")."\n", $name) if length($sectiontitle);
  117. printf(STDERR _g("%s: --description ignored with --remove")."\n", $name) if length($description);
  118. }
  119. printf(STDERR _g("%s: test mode - dir file will not be updated")."\n", $name)
  120. if $nowrite && !$quiet;
  121. umask(umask(0777) & ~0444);
  122. if($remove_exactly) {
  123. $remove_exactly = $filename;
  124. }
  125. $filename =~ m|[^/]+$|; $basename= $&; $basename =~ s/(\.info)?(\.gz)?$//;
  126. # The location of the info files from the dir entry, i.e. (emacs-20/emacs).
  127. my $fileinentry;
  128. &dprint("dirfile='$dirfile' filename='$filename' maxwidth='$maxwidth'");
  129. &dprint("menuentry='$menuentry' basename='$basename'");
  130. &dprint("description='$description' remove=$remove");
  131. if (!$remove) {
  132. if (!-f $filename && -f "$filename.gz" || $filename =~ s/\.gz$//) {
  133. $filename= "gzip -cd <$filename.gz |"; $pipeit= 1;
  134. } elsif (-f $filename) {
  135. $filename= "< $filename";
  136. } else {
  137. die "$progname: File $filename not found\n";
  138. }
  139. if (!length($description)) {
  140. open(IF,"$filename") || &quit(sprintf(_g("unable to read %s: %s"), $filename, $!));
  141. $asread='';
  142. while(<IF>) {
  143. m/^START-INFO-DIR-ENTRY$/ && last;
  144. m/^INFO-DIR-SECTION (.+)$/ && do {
  145. $sectiontitle = $1 unless ($sectiontitle);
  146. $sectionre = '^'.quotemeta($1) unless ($sectionre);
  147. }
  148. }
  149. while(<IF>) { last if m/^END-INFO-DIR-ENTRY$/; $asread.= $_; }
  150. if ($pipeit) {
  151. while (<IF>) {};
  152. }
  153. close(IF); &checkpipe;
  154. if ($asread =~ m/(\*\s*[^:]+:\s*\(([^\)]+)\).*\. *.*\n){2}/) {
  155. $infoentry= $asread;
  156. $multiline= 1;
  157. $fileinentry = $2;
  158. &dprint("multiline '$asread'");
  159. } elsif ($asread =~ m/^\*\s*([^:]+):(\s*\(([^\)]+)\)\.|:)\s*/) {
  160. $menuentry= $1;
  161. $description = $';
  162. $fileinentry = $3;
  163. &dprint("infile menuentry '$menuentry' description '$description'");
  164. } elsif (length($asread)) {
  165. printf STDERR _g("%s: warning, ignoring confusing INFO-DIR-ENTRY in file.")."\n", $name;
  166. }
  167. }
  168. if (length($infoentry)) {
  169. $infoentry =~ m/\n/;
  170. print "$`\n" unless $quiet;
  171. $infoentry =~ m/^\*\s*([^:]+):\s*\(([^\)]+)\)/ ||
  172. &quit(_g("invalid info entry")); # internal error
  173. $sortby= $1;
  174. $fileinentry= $2;
  175. } else {
  176. if (!length($description)) {
  177. open(IF,"$filename") || &quit(_g("unable to read %s: %s"), $filename, $!);
  178. $asread='';
  179. while(<IF>) {
  180. if (m/^\s*[Tt]his file documents/) {
  181. $asread = $';
  182. last;
  183. }
  184. }
  185. if (length($asread)) {
  186. while(<IF>) { last if m/^\s*$/; $asread.= $_; }
  187. $description= $asread;
  188. }
  189. if ($pipeit) {
  190. while (<IF>) {};
  191. }
  192. close(IF); &checkpipe;
  193. }
  194. if (!length($description)) {
  195. printf STDERR _g("
  196. No \`START-INFO-DIR-ENTRY' and no \`This file documents'.
  197. %s: unable to determine description for \`dir' entry - giving up
  198. "), $name;
  199. exit 1;
  200. }
  201. $description =~ s/^\s*(.)//; $_=$1; y/a-z/A-Z/;
  202. $description= $_ . $description;
  203. if (!length($menuentry)) {
  204. $menuentry= $basename; $menuentry =~ s/\Winfo$//;
  205. $menuentry =~ s/^.//; $_=$&; y/a-z/A-Z/;
  206. $menuentry= $_ . $menuentry;
  207. }
  208. &dprint("menuentry='$menuentry' description='$description'");
  209. if($fileinentry) {
  210. $cprefix= sprintf("* %s: (%s).", $menuentry, $fileinentry);
  211. } else {
  212. $cprefix= sprintf("* %s: (%s).", $menuentry, $basename);
  213. }
  214. $align--; $calign--;
  215. $lprefix= length($cprefix);
  216. if ($lprefix < $align) {
  217. $cprefix .= ' ' x ($align - $lprefix);
  218. $lprefix= $align;
  219. }
  220. $prefix= "\n". (' 'x $calign);
  221. $cwidth= $maxwidth+1;
  222. for $_ (split(/\s+/,$description)) {
  223. $l= length($_);
  224. $cwidth++; $cwidth += $l;
  225. if ($cwidth > $maxwidth) {
  226. $infoentry .= $cprefix;
  227. $cwidth= $lprefix+1+$l;
  228. $cprefix= $prefix; $lprefix= $calign;
  229. }
  230. $infoentry.= ' '; $infoentry .= $_;
  231. }
  232. $infoentry.= "\n";
  233. print $infoentry unless $quiet;
  234. $sortby= $menuentry; $sortby =~ y/A-Z/a-z/;
  235. }
  236. }
  237. if (!$nowrite && ( ! -e $dirfile || ! -s _ )) {
  238. if (-r $backup) {
  239. printf( STDERR _g("%s: no file %s, retrieving backup file %s.")."\n",
  240. $name, $dirfile, "$backup" );
  241. if (system ('cp', $backup, $dirfile)) {
  242. printf( STDERR _g("%s: copying %s to %s failed, giving up: %s")."\n",
  243. $name, $backup, $dirfile, $! );
  244. exit 1;
  245. }
  246. } else {
  247. if (-r $default) {
  248. printf( STDERR _g("%s: no backup file %s available, retrieving default file.")."\n",
  249. $name, $backup );
  250. if (system('cp', $default, $dirfile)) {
  251. printf( STDERR _g("%s: copying %s to %s failed, giving up: %s")."\n",
  252. $name, $default, $dirfile, $! );
  253. exit 1;
  254. }
  255. } else {
  256. printf STDERR _g("%s: no backup file %s available.")."\n", $name, $backup;
  257. printf STDERR _g("%s: no default file %s available, giving up.")."\n", $name, $default;
  258. exit 1;
  259. }
  260. }
  261. }
  262. if (!$nowrite && !link($dirfile, "$dirfile.lock")) {
  263. printf( STDERR _g("%s: failed to lock dir for editing! %s")."\n",
  264. $name, $! );
  265. printf( STDERR _g("try deleting %s?")."\n", "$dirfile.lock")
  266. if $!{EEXIST};
  267. exit 1;
  268. }
  269. open(OLD, $dirfile) || &ulquit(sprintf(_g("unable to open %s: %s"), $dirfile, $!));
  270. @work= <OLD>;
  271. eof(OLD) || &ulquit(sprintf(_g("unable to read %s: %s"), $dirfile, $!));
  272. close(OLD) || &ulquit(sprintf(_g("unable to close %s after read: %s"),
  273. $dirfile, $!));
  274. while (($#work >= 0) && ($work[$#work] !~ m/\S/)) { $#work--; }
  275. while (@work) {
  276. $_= shift(@work);
  277. push(@head,$_);
  278. last if (m/^\*\s*Menu:/i);
  279. }
  280. if (!$remove) {
  281. my $target_entry;
  282. if($fileinentry) {
  283. $target_entry = $fileinentry;
  284. } else {
  285. $target_entry = $basename;
  286. }
  287. for ($i=0; $i<=$#work; $i++) {
  288. next unless $work[$i] =~ m/^\*\s*[^:]+:\s*\(([^\)]+)\).*\.\s/;
  289. last if $1 eq $target_entry || $1 eq "$target_entry.info";
  290. }
  291. for ($j=$i; $j<=$#work+1; $j++) {
  292. next if $work[$j] =~ m/^\s+\S/;
  293. last unless $work[$j] =~ m/^\* *[^:]+: *\(([^\)]+)\).*\.\s/;
  294. last unless $1 eq $target_entry || $1 eq "$target_entry.info";
  295. }
  296. if ($i < $j) {
  297. if ($keepold) {
  298. printf(_g("%s: existing entry for \`%s' not replaced")."\n", $name, $target_entry) unless $quiet;
  299. $nowrite=1;
  300. } else {
  301. printf(_g("%s: replacing existing dir entry for \`%s'")."\n", $name, $target_entry) unless $quiet;
  302. }
  303. $mss= $i;
  304. @work= (@work[0..$i-1], @work[$j..$#work]);
  305. } elsif (length($sectionre)) {
  306. $mss= -1;
  307. for ($i=0; $i<=$#work; $i++) {
  308. $_= $work[$i];
  309. next if m/^(\*|\s)/;
  310. next unless m/$sectionre/io;
  311. $mss= $i+1; last;
  312. }
  313. if ($mss < 0) {
  314. printf(_g("%s: creating new section \`%s'")."\n", $name, $sectiontitle) unless $quiet;
  315. for ($i= $#work; $i>=0 && $work[$i] =~ m/\S/; $i--) { }
  316. if ($i <= 0) { # We ran off the top, make this section and Misc.
  317. printf(_g("%s: no sections yet, creating Miscellaneous section too.")."\n", $name)
  318. unless $quiet;
  319. @work= ("\n", "$sectiontitle\n", "\n", "Miscellaneous:\n", @work);
  320. $mss= 1;
  321. } else {
  322. @work= (@work[0..$i], "$sectiontitle\n", "\n", @work[$i+1..$#work]);
  323. $mss= $i+1;
  324. }
  325. }
  326. while ($mss <= $#work) {
  327. $work[$mss] =~ m/\S/ || last;
  328. $work[$mss] =~ m/^\* *([^:]+):/ || ($mss++, next);
  329. last if $multiline;
  330. $_=$1; y/A-Z/a-z/;
  331. last if $_ gt $sortby;
  332. $mss++;
  333. }
  334. } else {
  335. printf(_g("%s: no section specified for new entry, placing at end")."\n", $name)
  336. unless $quiet;
  337. $mss= $#work+1;
  338. }
  339. @work= (@work[0..$mss-1], map("$_\n",split(/\n/,$infoentry)), @work[$mss..$#work]);
  340. } else {
  341. my $target_entry;
  342. if($remove_exactly) {
  343. $target_entry = $remove_exactly;
  344. } else {
  345. $target_entry = $basename;
  346. }
  347. for ($i=0; $i<=$#work; $i++) {
  348. next unless $work[$i] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/;
  349. $tme= $1; $tfile= $2; $match= $&;
  350. next unless $tfile eq $target_entry;
  351. last if !length($menuentry);
  352. $tme =~ y/A-Z/a-z/;
  353. last if $tme eq $menuentry;
  354. }
  355. for ($j=$i; $j<=$#work+1; $j++) {
  356. next if $work[$j] =~ m/^\s+\S/;
  357. last unless $work[$j] =~ m/^\* *([^:]+): *\((\w[^\)]*)\)/;
  358. $tme= $1; $tfile= $2;
  359. last unless $tfile eq $target_entry;
  360. next if !length($menuentry);
  361. $tme =~ y/A-Z/a-z/;
  362. last unless $tme eq $menuentry;
  363. }
  364. if ($i < $j) {
  365. &dprint("i=$i \$work[\$i]='$work[$i]' j=$j \$work[\$j]='$work[$j]'");
  366. printf(_g("%s: deleting entry \`%s ...'")."\n", $name, $match) unless $quiet;
  367. $_= $work[$i-1];
  368. unless (m/^\s/ || m/^\*/ || m/^$/ ||
  369. $j > $#work || $work[$j] !~ m/^\s*$/) {
  370. s/:?\s+$//;
  371. if ($keepold) {
  372. printf(_g("%s: empty section \`%s' not removed")."\n", $name, $_) unless $quiet;
  373. } else {
  374. $i--; $j++;
  375. printf(_g("%s: deleting empty section \`%s'")."\n", $name, $_) unless $quiet;
  376. }
  377. }
  378. @work= (@work[0..$i-1], @work[$j..$#work]);
  379. } else {
  380. unless ($quiet) {
  381. if (length($menuentry)) {
  382. printf _g("%s: no entry for file \`%s' and menu entry \`%s'")."\n", $name, $target_entry, $menuentry;
  383. } else {
  384. printf _g("%s: no entry for file \`%s'")."\n", $name, $target_entry;
  385. }
  386. }
  387. }
  388. }
  389. $length = 0;
  390. $j = -1;
  391. for ($i=0; $i<=$#work; $i++) {
  392. $_ = $work[$i];
  393. chomp;
  394. if ( m/^(\* *[^:]+: *\(\w[^\)]*\)[^.]*\.)[ \t]*(.*)/ ) {
  395. $length = length($1) if ( length($1) > $length );
  396. $work[++$j] = $_;
  397. } elsif ( m/^[ \t]+(.*)/ ) {
  398. $work[$j] = "$work[$j] $1";
  399. } else {
  400. $work[++$j] = $_;
  401. }
  402. }
  403. @work = @work[0..$j];
  404. my $descalign=40;
  405. @newwork = ();
  406. foreach ( @work ) {
  407. if ( m/^(\* *[^:]+: *\(\w[^\)]*\)[^.]*\.)[ \t]*(.*)/ ||
  408. m/^([ \t]+)(.*)/ ) {
  409. if (length $1 >= $descalign) {
  410. push @newwork, $1;
  411. $_=(" " x $descalign) . $2;
  412. }
  413. else {
  414. $_ = $1 . (" " x ($descalign - length $1)) . $2;
  415. }
  416. push @newwork, split( "\n", wrap('', " " x $descalign, $_ ) );
  417. } else {
  418. push @newwork, $_;
  419. }
  420. }
  421. if (!$nowrite) {
  422. open(NEW,"> $dirfile.new") || &ulquit(sprintf(_g("unable to create %s: %s"),
  423. "$dirfile.new", $!));
  424. print(NEW @head,join("\n",@newwork)) ||
  425. &ulquit(sprintf(_g("unable to write %s: %s"), "$dirfile.new", $!));
  426. close(NEW) || &ulquit(sprintf(_g("unable to close %s: %s"), "$dirfile.new", $!));
  427. unlink("$dirfile.old");
  428. link($dirfile, "$dirfile.old") ||
  429. &ulquit(sprintf(_g("unable to backup old %s, giving up: %s"),
  430. $dirfile, $!));
  431. rename("$dirfile.new", $dirfile) ||
  432. &ulquit(sprintf(_g("unable to install new %s: %s"), $dirfile, $!));
  433. unlink("$dirfile.lock") ||
  434. &quit(sprintf(_g("unable to unlock %s: %s"), $dirfile, $!));
  435. system ('cp', $dirfile, $backup) &&
  436. warn sprintf(_g("%s: could not backup %s in %s: %s"), $name, $dirfile, $backup, $!)."\n";
  437. }
  438. sub quit
  439. {
  440. die "$name: $@\n";
  441. }
  442. sub ulquit {
  443. unlink("$dirfile.lock") ||
  444. warn sprintf(_g("%s: warning - unable to unlock %s: %s"),
  445. $name, $dirfile, $!)."\n";
  446. &quit($_[0]);
  447. }
  448. sub checkpipe {
  449. return if !$pipeit || !$? || $?==0x8D00 || $?==0x0D;
  450. &quit(sprintf(_g("unable to read %s: %d"), $filename, $?));
  451. }
  452. sub dprint {
  453. printf(DEBUG _g("dbg: %s")."\n", $_[0]) if ($debug);
  454. }
  455. exit 0;