install-info.pl 14 KB

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