home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2007 September / PCWSEP07.iso / Software / Linux / Linux Mint 3.0 Light / LinuxMint-3.0-Light.iso / casper / filesystem.squashfs / usr / sbin / install-docs < prev    next >
Encoding:
Text File  |  2007-02-22  |  24.2 KB  |  888 lines

  1. #!/usr/bin/perl
  2. # vim:cindent:ts=2:sw=2:et:fdm=marker:cms=\ #\ %s
  3.  
  4. =head1 NAME
  5.  
  6. install-docs - manage online Debian documentation
  7.  
  8. =cut
  9.  
  10. # ---beginning-of-configuration-part---
  11.  
  12. $DATA_DIR = "/var/lib/doc-base/info";
  13. $omf_locations = "/usr/share/omf";
  14.  
  15. $dwww_update = "/usr/bin/update-menus";
  16. $dhelp_parse = "/usr/sbin/dhelp_parse";
  17. $scrollkeeper_update = "/usr/bin/scrollkeeper-update";
  18. $scrollkeeper_gen_seriesid = "/usr/bin/scrollkeeper-gen-seriesid";
  19. $scrollkeeper_map_file = "/usr/share/doc-base/data/scrollkeeper.map";
  20.  
  21. $do_dwww_update = 1;
  22. my $warn_nonexistent_files = 0;
  23.  
  24.  
  25. # All formats handled by the doc-base
  26. my @supported_formats =  (
  27.                             'html',
  28.                             'text',
  29.                             'pdf',
  30.                             'postscript',
  31.                             'info',
  32.                             'dvi',
  33.                             'debiandoc-sgml'
  34.                       );
  35.  
  36. # Formats which need the Index: field
  37. my @need_index_formats = (
  38.                             'html',
  39.                             'info'
  40.                          );
  41.  
  42.  
  43. # ---end-of-configuration-part---
  44.  
  45. # This would normally be just 'use File::Basename;'. However, install-docs
  46. # often gets called opportunistically by packages if it's present, and
  47. # there's no way for those packages to make sure that perl is configured
  48. # when doing so, so it's possible that standard modules will not be usable.
  49.  
  50. sub basename {
  51.   (my $basename = $_[0]) =~ s#.*/##s;
  52.   return $basename;
  53. }
  54.  
  55. sub dirname {
  56.   my ($dirname, $basename) = ($_[0] =~ m#^(.*/)?(.*)#s);
  57.   $dirname = './' if not defined $dirname or $dirname eq '';
  58.   $dirname =~ s#(.)/*\z#$1#s;
  59.   unless (length $basename) {
  60.     ($dirname) = ($dirname =~ m#^(.*/)?#s);
  61.     $dirname = './' if not defined $dirname or $dirname eq '';
  62.     $dirname =~ s#(.)/*\z#$1#s;
  63.   }
  64.   return $dirname;
  65. }
  66.  
  67. # set umask explicitly
  68. umask 022;
  69.  
  70. =head1 SYNOPSIS
  71.  
  72.  install-docs [ -v, --verbose ]
  73.               [ --no-update-menus ]
  74.               -i --install | 
  75.               -r --remove  |
  76.               -s --status  |
  77.               -L --listfiles
  78.                 <doc-id | file>
  79.  
  80. =head1 DESCRIPTION
  81.  
  82. B<install-docs> is a tool allow Debian package maintainers to register
  83. documentation to various documentation systems.  It currently supports
  84. B<dhelp> and B<dwww>.
  85.  
  86. This manual page provides a quick synopsis of B<install-docs> usage.
  87. Full documentation can be found in the documentation, including a
  88. description of the control file syntax and grammar.
  89.  
  90. =head1 OPTIONS
  91.  
  92. =over 4
  93.  
  94. =cut
  95.  
  96. while ($arg = shift) {
  97.  
  98.   if (($arg eq '-v') or ($arg eq '--verbose')) {
  99.  
  100. =item B<-v> | B<--verbose>
  101.  
  102. Operate verbosely.
  103.  
  104. =cut
  105.     $verbose = 1;
  106.     next;
  107.   }
  108.  
  109.   if ($arg eq '--no-update-menus') {
  110.  
  111. =item B<--no-update-menus>
  112.  
  113. Inhibit running L<update-menus(1)>, used for the L<dwww(8)> update program.
  114.  
  115. =cut
  116.     $do_dwww_update = 0;
  117.     next;
  118.   }
  119.  
  120.   if (($arg eq '-i') or ($arg eq '--install')) {
  121.  
  122. =item B<-i> I<file> | B<--install> I<file>
  123.  
  124. Install the documentation described by the control file I<file>.
  125.  
  126. =cut
  127.     # install new docs # {{{
  128.     ($file = shift) or die "argument missing for `install'\n";
  129.     ($#ARGV == -1) or die "too many arguments for `install'\n";
  130.     $warn_nonexistent_files = 1;
  131.  
  132.     read_control_file($file);
  133.     read_status_file(1);
  134.     read_list_file();
  135.  
  136.     # read in doc-base -> scrollkeeper mappings
  137.     %mapping = read_map($scrollkeeper_map_file);
  138.  
  139.     # update status
  140.     $status{'Control-File'} = $file;
  141.     $status_changed = 1;
  142.  
  143.  
  144.     # register documents to subsystems
  145.     register_dhelp();
  146.     register_dwww();
  147.     register_scrollkeeper();
  148.  
  149.     write_list_file();
  150.     write_status_file(); # }}}
  151.  
  152.   } elsif (($arg eq '-r') or ($arg eq '--remove')) {
  153.  
  154. =item B<-r> I<docid> | B<--remove> I<docid>
  155.  
  156. Remove the documentation identified by the document identifier
  157. I<docid>.  Document identifiers are tags which are set in the control
  158. file, and usually correspond to the package name.
  159.  
  160. =cut
  161.     # remove old docs # {{{
  162.     ($docid = shift) or die "argument missing for `remove'\n";
  163.     ($#ARGV == -1) or die "too many arguments for `remove'\n";
  164.  
  165.     if ( ! read_status_file(1) ) {
  166.       warn("Document `$docid' is not installed, cannot remove.\n");
  167.       exit 0;
  168.     }
  169.     read_control_file($status{'Control-File'});
  170.     read_list_file();
  171.  
  172.     # remove newly created files and unregister from menus
  173.     remove_files();
  174.     remove_omf_files() if $status{'Scrollkeeper-omf-file'};
  175.     update_dwww_menus() if $status{'Registered-to-dwww'};
  176.     update_scrollkeeper() if $status{'Registered-to-scrollkeeper'};
  177.  
  178.     # remove data files
  179.     remove_data_files(); # }}}
  180.  
  181.   } elsif (($arg eq '-s') or ($arg eq '--status')) {
  182.  
  183. =item B<-s> I<docid> | B<--status> I<docid>
  184.  
  185. Display the status of the document identifier I<docid>.
  186.  
  187. =cut
  188.     # display status # {{{
  189.     ($docid = shift) or die "argument missing for `status'\n";
  190.     ($#ARGV == -1) or die "too many arguments for `status'\n";
  191.  
  192.     read_status_file();
  193.     read_list_file();
  194.     read_control_file($status{'Control-File'});
  195.  
  196.     display_status_information(); # }}}
  197.  
  198.   } elsif (($arg eq '-L') or ($arg eq '--listfiles')) {
  199.  
  200. =item B<-L> I<docid> | B<--listfiles> I<docid>
  201.  
  202. List the files associated with the document identifier I<docid>.
  203.  
  204. =back
  205.  
  206. =cut
  207.     # display file listning # {{{
  208.     ($docid = shift) or die "argument missing for `listfiles'\n";
  209.     ($#ARGV == -1) or die "too many arguments for `listfiles'\n";
  210.  
  211.     read_status_file();
  212.     read_list_file();
  213.  
  214.     display_listing(); # }}}
  215.  
  216.   } else {
  217.     die "invalid command line argument: $arg\n";
  218.   }
  219. }
  220.  
  221. exit 0;
  222.  
  223. # -------------------------------
  224.  
  225. # Registering to dhelp
  226. sub register_dhelp { # {{{
  227.  
  228.   my $format_data;
  229.   for $format_data (@format_list) {
  230.     next unless $$format_data{'format'} eq 'html'; # dhelp only understand html
  231.     # get directory of index file
  232.     my $file = basename($$format_data{'index'});
  233.     my $dir = dirname($$format_data{'index'});
  234.     $dir =~ m|^/| or 
  235.       die "Index file has to be specified with absolute path: $$format_data{'index'}";
  236.  
  237.     # ensure the documentation is in an area dhelp can deal with
  238.     if ( $dir !~ m|^/usr/share/doc| ) {
  239.       print "register_dhelp: skipping $dir/$file
  240.             because dhelp only knows about /usr/share/doc\n"
  241.       if $verbose;
  242.       last; # we can have only one Format:HTML section
  243.     }
  244.  
  245.     my @dhelp_data;
  246.     my $dhelp_file = "$dir/.dhelp";
  247.     # dhelp file already exists?
  248.     if (-f $dhelp_file) {
  249.       # is this file from us?
  250.       #if (not exists $list{$dhelp_file}) {
  251.         # no, skip action -- actually we could probably tolerate this condition
  252.         #warn "warning: skipping foreign dhelp file $dhelp_file";
  253.         #next;
  254.       #}
  255.  
  256.       # yes, read in the file
  257.       $dhelp_data = read_dhelp_file($dhelp_file);
  258.  
  259.       # take a look at the contents
  260.       my $i;
  261.       for ( $i = 0; $i <= $#$dhelp_data; $i++ ) {
  262.         if ($$dhelp_data[$i]{'filename'} =~ /^\s*\Q$file\E\s*$/o) {
  263.           # remove this entry; we'll add it back below
  264.           print "register_dhelp: found entry for $file in $dhelp_file, replacing\n"
  265.           if $verbose;
  266.           splice(@$dhelp_data, $i, 1);
  267.         }
  268.       }
  269.     } else {
  270.       # no file yet, let's make an empty ref to fill in below
  271.       $dhelp_data = [];
  272.     }
  273.  
  274.  
  275.     # last minute data munging,
  276.     # FIXME when we finally get a real document hierarchy
  277.     my $dhelp_section;
  278.     ( $dhelp_section = $$doc_data{'section'} ) =~ tr/A-Z/a-z/;
  279.     $dhelp_section =~ s|^apps/||;
  280.     $dhelp_section =~ s/^(howto|faq)$/\U$&\E/;
  281.     # now push our data onto the array (undefs are ok)
  282.     push(@$dhelp_data, {
  283.       'filename'    => $file,
  284.       'directory'   => $dhelp_section,
  285.       'linkname'    => $$doc_data{'title'},
  286.       'description' => $$doc_data{'abstract'},
  287.       }
  288.     );
  289.  
  290.     # remove the dhelp_file and any other installed dhelp files 
  291.     # (since the location could change and we can have only one file for document-id)
  292.     # note: remove_files zeroes %list
  293.     $list{$dhelp_file} = 1;  remove_files();
  294.     
  295.     print "Updating $dhelp_file\n" if $verbose;
  296.     add_file($dhelp_file);
  297.     write_dhelp_file($dhelp_file, $dhelp_data);
  298.  
  299.     if (-x $dhelp_parse) {
  300.       print "Executing $dhelp_parse -a $dir\n" if $verbose;
  301.       if (system("$dhelp_parse -a $dir") != 0) {
  302.         warn "warning: error occured during execution of $dhelp_parse -a";
  303.       }
  304.     } else {
  305.       print "Skipping $dhelp_parse, program not found\n" if $verbose;
  306.     }
  307.     # set status
  308.     $status{'Registered-to-dhelp'} = 1;
  309.     $status_changed = 1;
  310.  
  311.     # we can have only one Format:HTML section the document
  312.     last;
  313.   }
  314.  
  315. } # }}}
  316.  
  317. # Registering to dwww:
  318. sub register_dwww { # {{{
  319.   for $format_data (@format_list) {
  320.     $update_dwww = 1;
  321.     # set status
  322.     $status{'Registered-to-dwww'} = 1;
  323.     $status_changed = 1;
  324.   }
  325.  
  326.   if ($update_dwww) {
  327.     update_dwww_menus();
  328.   }
  329. } # }}}
  330.  
  331. sub update_dwww_menus { # {{{
  332.   if ($do_dwww_update && -x $dwww_update) {
  333.     print "Executing $dwww_update\n" if $verbose;
  334.     if (system($dwww_update) != 0) {
  335.       warn "warning: error occured during execution of $dwww_update";
  336.     }
  337.   }
  338. } # }}}
  339.  
  340. sub remove_files { # {{{
  341.   for $file (keys %list) {
  342.     next unless -f $file;
  343.  
  344.     # dhelp file?
  345.     if ($file =~ /\.dhelp$/o) { # yes
  346.  
  347.       my $dir = dirname($file);
  348.  
  349.       if (-x $dhelp_parse) {
  350.         # call dhelp to notice removal of document
  351.         print "Executing $dhelp_parse -d $dir\n" if $verbose;
  352.         if (system("$dhelp_parse -d $dir") != 0) {
  353.           warn "warning: error occured during execution of $dhelp_parse";
  354.         }
  355.       }
  356.  
  357.       print "Removing dhelp file $file\n" if $verbose;
  358.       unlink($file) or die "$file: cannot remove file: $!";
  359.  
  360.       next;
  361.     }
  362.  
  363.     # not a dhelp file
  364.  
  365.     print "Removing file $file\n" if $verbose;
  366.     unlink($file) or die "$file: cannot remove file: $!";
  367.   }
  368.   %list = ();
  369.   $list_changed = 1;
  370. } # }}}
  371.  
  372. sub remove_data_files { # {{{
  373.   my $status_file = "$DATA_DIR/$docid.status";
  374.   if (-f $status_file) {
  375.     print "Removing status file $status_file\n" if $verbose;
  376.     unlink($status_file)
  377.       or die "$status_file: cannot remove status file: $!";
  378.   }
  379.  
  380.   my $list_file = "$DATA_DIR/$docid.list";
  381.   if (-f $list_file) {
  382.     print "Removing list file $list_file\n" if $verbose;
  383.     unlink($list_file)
  384.       or die "$list_file: cannot remove status file: $!";
  385.   }
  386. } # }}}
  387.  
  388. # -------------------------------
  389.  
  390. sub read_status_file { # {{{
  391.   my ($ignore) = @_;
  392.  
  393.   my $status_file = "$DATA_DIR/$docid.status";
  394.   if (not -f $status_file) {
  395.     return(0) if $ignore;
  396.  
  397.     warn "Document `$docid' is not installed.\n";
  398.     exit 1;
  399.   }
  400.  
  401.   open(S,"$status_file")
  402.     or die "$status_file: cannot open status file for reading: $!";
  403.   while (<S>) {
  404.     chomp;
  405.     next if /^\s*$/o;
  406.     /^\s*(\S+):\s*(.*\S)\s*$/
  407.       or die "syntax error in status file: $_";
  408.     $status{$1} = $2;
  409.   }
  410.   close(S)
  411.     or die "$status_file: cannot close status file: $!";
  412. } # }}}
  413.  
  414. sub write_status_file { # {{{
  415.   return unless $status_changed;
  416.  
  417.   my $status_file = "$DATA_DIR/$docid.status";
  418.  
  419.   open(S,">$status_file")
  420.     or die "$status_file: cannot open status file for writing: $!";
  421.   for $k (keys %status) {
  422.     print S "$k: $status{$k}\n";
  423.   }
  424.   close(S) or die "$status_file: cannot close status file: $!";
  425.  
  426.   $status_changed = 0;
  427. } # }}}
  428.  
  429. sub display_status_information { # {{{
  430.   print "---document-information---\n";
  431.   print "Document: $$doc_data{'document'}\n";
  432.   for $k (sort keys %$doc_data) {
  433.     next if $k eq 'document';
  434.     $kk = $k; 
  435.     substr($kk,0,1) =~ tr/a-z/A-Z/;
  436.     print "$kk: $$doc_data{$k}\n";
  437.   }
  438.   for $format_data (@format_list) {
  439.     print "\n";
  440.     print "---format-description---\n";
  441.     print "Format: $$format_data{'format'}\n";
  442.     for $k (sort keys %$format_data) {
  443.       next if $k eq 'format';
  444.       $kk = $k; 
  445.       substr($kk,0,1) =~ tr/a-z/A-Z/;
  446.       print "$kk: $$format_data{$k}\n";
  447.     }
  448.   }
  449.   print "\n";
  450.   print "---status-information---\n";
  451.   for $k (sort keys %status) {
  452.     print "$k: $status{$k}\n";
  453.   }
  454. } # }}}
  455.  
  456. sub display_listing { # {{{
  457.   for $k (sort keys %list) {
  458.     print "$k\n";
  459.   }
  460. } # }}}
  461.  
  462. sub read_list_file { # {{{
  463.   my $list_file = "$DATA_DIR/$docid.list";
  464.   return unless -f $list_file;
  465.  
  466.   open(L,"$list_file") 
  467.     or die "$list_file: cannot open list file for reading: $!";
  468.   while (<L>) {
  469.     chomp;
  470.     next if /^\s*$/o;
  471.     $list{$_} = 1;
  472.   }
  473.   close(L) or die "$list_file: cannot close file: $!";
  474. } # }}}
  475.  
  476. sub write_list_file { # {{{
  477.   return unless $list_changed;
  478.  
  479.   my $list_file = "$DATA_DIR/$docid.list";
  480.  
  481.   open(L,">$list_file")
  482.     or die "$list_file: cannot open list file for writing: $!";
  483.   for $k (keys %list) {
  484.     print L "$k\n";
  485.   }
  486.   close(L) or die "$list_file: cannot close file: $!";
  487.  
  488.   $list_changed = 0;
  489. } # }}}
  490.  
  491. sub add_file { # {{{
  492.   my ($file) = @_;
  493.  
  494.   return if $list{$file};
  495.  
  496.   my $data_file = "$DATA_DIR/$docid.list";
  497.   open(L,">>$data_file")
  498.     or die "$data_file: cannot open for appending";
  499.   print L $file,"\n";
  500.   close(L) or die "$data_file: cannot close file";
  501.  
  502.   $list{$file} = 1;
  503. } # }}}
  504.  
  505. # -------------------------------
  506.  
  507. # read a dhelp file, probably more flexibly than dhelp itself
  508. # input:
  509. #  file name
  510. # output:
  511. #  returns ref to array of hashes containing our data
  512. sub read_dhelp_file { # {{{
  513.   my ($dhelpfile) = @_;
  514.   my ($dhdata);     # array ref, to be returned holding all the dhelp data 
  515.   my (@rets);     # temporary array
  516.  
  517.   open(FH, "<$dhelpfile") or die "open file '$dhelpfile': $!\n";
  518.   $_ = join('', <FH>);    # slurp in the file
  519.  
  520.   while ( m{
  521.       <item>\s*     # item defines a block, required
  522.     (?:     # alternate everything group
  523.        (?:<directory>   # directory is starting, required
  524.    ([^<]+)    #   $1
  525.        )      # ... ending
  526.      |
  527.        (?:<dirtitle>    # dirtitle is starting, optional
  528.          ([^<]+)    #   $2 until next tag start
  529.        )      # ... ending
  530.      |
  531.        (?:<linkname>    # linkname is starting, optional
  532.          ([^<]+)    #   $3
  533.        )      # ... ending
  534.      |
  535.        (?:<filename>    # filename is starting, optional
  536.          ([^<]+)    #   $4
  537.        )      # ... ending
  538.      |
  539.        (?:<description>   # filename is starting, optional
  540.          (.*?)      #  $5, non greedy
  541.        </description>)    # ... ending
  542.      )*     # end alternating
  543.        \s*</item>   # spaces ok, item ends
  544.       }gscx )
  545.     {
  546.       @rets =  ($1, $2, $3, $4, $5);
  547.       @rets = map { chomp; s/^\s+//; s/\s+$//; $_; }  @rets;
  548.       # push a hashref of our dhelp data item onto the $dhdata array
  549.       push(@$dhdata, {
  550.           'directory'   => $rets[0],
  551.           'dirtitle'    => $rets[1],
  552.           'linkname'    => $rets[2],
  553.           'filename'    => $rets[3],
  554.           'description' => $rets[4],
  555.           'converted'   => 1,      # the entry is already HTML-encoded,
  556.                                    # we shouldn'try recode it on writing
  557.          });
  558.     }
  559.  
  560.   close FH;
  561.   return $dhdata;
  562. } # }}}
  563.  
  564.  
  565. sub html_encode { # {{{
  566.   my $text        = shift;
  567.   my $do_convert  = shift;
  568.  
  569.   return $text unless $do_convert;
  570.  
  571.   $text =~ s/&/&/g;
  572.   $text =~ s/</</g;
  573.   $text =~ s/>/>/g;
  574.   $text =~ s/"/"/g;
  575.   no locale; # always use byte semantics for this regex range
  576.   # We take gratuitous advantage of the first 256 Unicode codepoints
  577.   # happening to coincide with ISO-8859-1 so that we can HTML-encode
  578.   # ISO-8859-1 characters without using any non-pragmatic modules.
  579.   $text =~ s/([^\0-\x7f])/sprintf('&#%d;', ord $1)/eg;
  580.   return $text;
  581. } # }}}
  582.  
  583. sub html_encode_description { # {{{
  584.   my $text        = shift;
  585.   my $do_convert  = shift;
  586.  
  587.   return $text unless $do_convert;
  588.  
  589.   $text = &html_encode($text, $do_convert);
  590.   my @lines=split(/\n/, $text);
  591.   $text = "";
  592.   my $in_pre = 0;
  593.   foreach $_  (@lines) {
  594.     s/^\s//;
  595.     if (/^\s/) {
  596.       $_ = "<pre>\n$_" unless $in_pre;
  597.       $in_pre = 1;
  598.     } else {
  599.       $_ = "$_\n<\\pre>" if $in_pre;
  600.       $in_pre = 0;
  601.     }  
  602.     s/^\.\s*$/<br> <br>/;
  603.     s/(http|ftp)s?:\/([\w\/~\.%#-])+[\w\/]/<a href="$&">$&<\/a>/g;
  604.  
  605.     $text .= $_ . "\n";
  606.    }
  607.   $text .= "</pre>\n" if $in_pre;
  608.   return $text;
  609. } # }}}
  610.  
  611.  
  612. sub write_dhelp_file { # {{{
  613.   my ($file, $data) = @_;
  614.  
  615.   open(FH, ">$file") or die "cannot create dhelp file '$file': $!\n";
  616.   foreach $rec (@$data) {
  617.     my $do_html_convert = not (defined $$rec{'converted'} or $$rec{'converted'});
  618.     print FH "<item>\n";
  619.     foreach $field ((
  620.       'directory', 'dtitle', 'linkname', 'filename'
  621.            )) {
  622.       if ($field ne 'linkname') {        
  623.         print FH "<$field>$$rec{$field}\n" if length($$rec{$field});
  624.       } else {   
  625.         print FH "<$field>" . &html_encode($$rec{$field}, $do_html_convert) ."\n" if length($$rec{$field});
  626.      }
  627.     }
  628.     print FH "<description>\n" . &html_encode_description($$rec{description}, $do_html_convert) . "\n</description>\n"
  629.       if length($$rec{'description'});
  630.     print FH "</item>\n\n";
  631.   }
  632.   close FH;
  633. } # }}}
  634.  
  635. # -------------------------------
  636.  
  637. sub remove_omf_files { # {{{
  638.   my $omf_file = $status{'Scrollkeeper-omf-file'};
  639.   my $omf_dir = dirname($omf_file);
  640.   unlink($omf_file) or die "$omf_file: could not delete file: $!";
  641.  
  642.   #check to see if the directory is now empty. if so, kill it.
  643.   opendir(DIR, $omf_dir);
  644.   if (readdir DIR == 0) {
  645.     rmdir($omf_dir) or die "$omf_dir: could not delete directory: $!";
  646.   }
  647.   closedir DIR;
  648. } # }}}
  649.  
  650. sub register_scrollkeeper { # {{{
  651.   my $document = $$doc_data{'document'};
  652.   my $format_data;
  653.   for $format_data (@format_list) {
  654.     next unless $$format_data{'format'} eq 'html'; # scrollkeeper only understands
  655.                                                    # docbook and html.
  656.     my $file = $$format_data{'index'};
  657.     my $format = $$format_data{'format'};
  658.     write_omf_file($file,$format);
  659.     #set status
  660.     $status{'Registered-to-scrollkeeper'} = 1;
  661.     $status_changed = 1;
  662.   }
  663.   update_scrollkeeper();
  664. } # }}}
  665.  
  666. sub update_scrollkeeper { # {{{
  667.   if (-x $scrollkeeper_update) {
  668.     print "Executing $scrollkeeper_update\n" if $verbose;
  669.     if (system("$scrollkeeper_update -q >/dev/null 2>&1") != 0) {
  670.       warn "warning: error occurred during execution of $scrollkeeper_update\n";
  671.     }
  672.   }
  673. } # }}}
  674.  
  675. sub write_omf_file { # {{{
  676.   my ($file, $format) = @_;
  677.   my $document = $$doc_data{'document'};
  678.   my $omf_file = "$omf_locations/$document/$document-C.omf";
  679.   my $date;
  680.   my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
  681.   $year += 1900;
  682.   if ($mday <10) {$mday = "0$mday";}
  683.   if ($mon <10) {$mon = "0$mon";}
  684.   $date = "$year-$mon-$mday";
  685.  
  686.   chomp(my $serial_id = `$scrollkeeper_gen_seriesid`);
  687.  
  688.   if (! -d "$omf_locations/$document") {
  689.     mkdir("$omf_locations/$document");
  690.   }
  691.  
  692.   open(OMF, ">$omf_file")
  693.     or die "$omf_file: cannot open OMF file for writing: $!";
  694.   
  695.   #now for the boiler plate XML stuff
  696.   print OMF "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
  697.   print OMF "<!DOCTYPE omf PUBLIC \"-//OMF//DTD Scrollkeeper OMF Variant V1.0//EN\" \"http://scrollkeeper.sourceforge.net/dtds/scrollkeeper-omf-1.0/scrollkeeper-omf.dtd\">\n";
  698.   print OMF "<omf>\n\t<resource>\n";
  699.  
  700.   #now for the dynamic stuff
  701.   print OMF "\t\t<creator>".&html_encode($$doc_data{'author'}, 1)."</creator>\n";
  702.   print OMF "\t\t<title>".&html_encode($$doc_data{'title'}, 1)."</title>\n";
  703.   print OMF "\t\t<date>$date</date>\n";
  704.   print OMF "\t\t<subject category=\"".map_docbase_to_scrollkeeper($$doc_data{'section'})."\"/>\n";
  705.   print OMF "\t\t<description>".&html_encode($$doc_data{'abstract'}, 1)."</description>\n";
  706.   if ($format eq 'html') {
  707.     print OMF "\t\t<format mime=\"text/html\"/>\n";
  708.   } 
  709.   elsif ($format eq 'docbook') {
  710.     print OMF "\t\t<format mime=\"text/xml\" dtd=\"-//OASIS//DTD DocBook XML V4.1.2//EN\"/>\n";
  711.   }
  712.   print OMF "\t\t<identifier url=\"$file\"/>\n";
  713.   print OMF "\t\t<language code=\"C\"/>\n";
  714.   print OMF "\t\t<relation seriesid=\"$serial_id\"/>\n";
  715.  
  716.   #finish the boiler plate
  717.   print OMF "\t</resource>\n</omf>\n";
  718.   close(OMF) or die "$omf_file: cannot close OMF file: $!";
  719.   $status{'Scrollkeeper-omf-file'} = $omf_file;
  720. } # }}}
  721.  
  722. # -------------------------------
  723.  
  724. ##
  725. ## assuming filehandle IN is the control file, read a section (or
  726. ## "stanza") of the doc-base control file and adds data in that
  727. ## section to the hash reference passed as an argument.  Returns 1 if
  728. ## there is data and 0 if it was empty
  729. ##
  730. sub read_control_file_section { # {{{
  731.   my $fh = shift;
  732.   my ($pfields) = @_;
  733.  
  734.   my $empty = 1;
  735.   my ($cf,$v);
  736.   while (<$fh>) {
  737.     chomp;
  738.     s/\s*$//;                   # trim trailing whitespace
  739.  
  740.     # empty line?
  741.     if (/^\s*$/o) {
  742.       if ($empty) {
  743.         next;
  744.       } else {
  745.         last;
  746.       }
  747.     }
  748.  
  749.     $empty = 0;
  750.  
  751.     # new field?
  752.     if (/^(\S+)\s*:\s*(.*)$/) {
  753.       ($cf,$v) = ($1,$2);
  754.       $cf = lc $cf;
  755.       #print STDERR "$cf -> $v\n";
  756.       if (exists $$pfields{$cf}) {
  757.         warn "warning: $cf: overwriting previous setting of control field";
  758.       }
  759.       $$pfields{$cf} = $v;
  760.     } elsif (/^\s+(\S.*)$/) {
  761.       $v = $&;
  762.       defined($cf) or die "syntax error in control file: no field specified";
  763.       #print STDERR "$cf -> $v (continued)\n";
  764.       $$pfields{$cf} .= "\n$v";
  765.     } else {
  766.       die "syntax error in control file: $_";
  767.     }
  768.   }
  769.  
  770.   return not $empty;
  771. } # }}}
  772.  
  773. # reads control file specified as argument
  774. # output:
  775. #    sets $docid
  776. #    sets $doc_data to point to a hash containing the document data
  777. #    sets @format_list, a list of pointers to hashes containing the format data
  778. sub read_control_file { # {{{
  779.   my ($file) = @_;
  780.  
  781.   my $fh=$file; 
  782.   open($fh, $file) or 
  783.     open($fh, "/usr/share/doc-base/$file") or
  784.     die "$file: cannot open control file for reading: $!\n";
  785.  
  786.   $doc_data = {};
  787.   read_control_file_section($fh, $doc_data) or die "error: empty control file";
  788.   # check for required information
  789.   ($docid = $$doc_data{'document'}) 
  790.     or die "error in control file: `Document' value not specified";
  791.   $$doc_data{'title'}
  792.     or die "error in control file: `Title' value not specified";
  793.   $$doc_data{'section'}
  794.     or die "error in control file: `Section' value not specified";
  795.  
  796.   undef @format_list;
  797.   $format_data = {};
  798.   while (read_control_file_section($fh, $format_data)) {
  799.     # adjust control fields
  800.     $$format_data{'format'} =~ tr/A-Z/a-z/;
  801.     # check for required information
  802.     $$format_data{'format'}
  803.       or die "error in control file: `Format' value not specified";
  804.     $$format_data{'files'}
  805.       or die "error in control file: `Files' value not specified";
  806.       
  807.     if ($verbose) {
  808.       grep { $_ eq $$format_data{'format'} } @supported_formats
  809.         or  warn "warning: ignoring unknown format `$$format_data{'format'}'";
  810.     }
  811.     if (grep { $_ eq $$format_data{'format'}} @need_index_formats) {
  812.       $$format_data{'index'}
  813.         or $$format_data{'format'} eq 'html' and die "error in control file: `Index' value missing for format `" . $$format_data{'format'} . "'"
  814.         # FIXME - temporary
  815.         or warn "error in control file: `Index' value missing for format `" . $$format_data{'format'} . "'";
  816.     } 
  817.  
  818.     my $ok = 1;
  819.     if ($warn_nonexistent_files) {
  820.       if (defined $$format_data{'index'} && ! -e $$format_data{'index'}) {
  821.         warn "warning: file `$$format_data{'index'}' does not exist";
  822.         $ok = 0;
  823.       }
  824.       my @globlist = glob($$format_data{'files'});
  825.       if ($#globlist < 0) {
  826.         warn "warning: file mask `$$format_data{'files'}' does not match any files";
  827.         $ok = 0;
  828.       }    
  829.     }
  830.  
  831.     push(@format_list,$format_data) if $ok;
  832.     $format_data = {};
  833.   }
  834.   close($fh);
  835. } # }}}
  836.  
  837. # arguments: filename
  838. # reads a file that looks like:
  839. # foo: bar
  840. # returns: hash of lv -> rv
  841. sub read_map { # {{{
  842.   my ($file) = @_;
  843.   my $map;
  844.   open (MAP, "<$file") or die "Could not open $file: $!";
  845.   while(<MAP>) {
  846.           chomp;
  847.           my ($lv,$rv) = split(/: /);
  848.           $map{lc($lv)} = $rv;
  849.   }
  850.   close(MAP);
  851.   return %map;
  852. } # }}}
  853.  
  854. # arguments: doc-base section
  855. # returns: scrollkeeper category
  856. sub map_docbase_to_scrollkeeper { # {{{
  857.   return $mapping{lc($_[0])};
  858. } # }}}
  859.   
  860.  
  861. =head1 BUGS
  862.  
  863. None known, but a much more robust system is being planned as the next
  864. generation of doc-base.
  865.  
  866. =head1 SEE ALSO
  867.  
  868. dwww(8), Debian doc-base Manual
  869. F</usr/share/doc/doc-base/doc-base.html/index.html>, dhelp Manual
  870. F</usr/share/doc/dhelp/dhelp.html>
  871.  
  872. =head1 AUTHOR
  873.  
  874. This program was originally written by Christian Schwarz
  875. <schwarz@debian.org>, for the Debian GNU/Linux system.  Adam Di Carlo
  876. <aph@debian.org> is currently maintaining and extending it.
  877.  
  878. This software was meant to be for the benefit of the entire Debian
  879. user and developer community.  If you are interested in being involved
  880. with this software, please join the mailing list
  881. <debian-doc@lists.debian.org>.
  882.  
  883. =cut
  884.  
  885. #Local Variables:
  886. #perl-indent-level:2
  887. #End:
  888.