#!/usr/bin/perl -w

#############################################################################
#
#  MAKE-LSM - Linux Software Map (LSM) description file creator
#
#
#  Author:   Joshua Swink  <yathster@yahoo.com>
#
#  make-lsm is released under the GNU General Public License.
#  See the file COPYING. If that file is missing, see
#  http://www.gnu.org/copyleft/gpl.html
#
#  Get a blank LSM file from http://lsm.execpc.com/lsm/LSM.README
#
#  Usage:
#
#    *  Create the lsm.in file (it's a regular LSM file)
#    *  Run make-lsm <version>
#
#    You may omit <version> by creating a file named VERSION that
#    contains the version.
#
#    -  The following fields will be filled in by this program:
#       Version, Entered-date, and the <size>-<filename> part of Primary-site
#
#############################################################################

use POSIX;
use Getopt::Long;

Getopt::Long::Configure('bundling');

$version = '0.3';
%options = ();
@suffixes = qw(tar.gz tar.bz2);

if (!GetOptions(
   'version|V',     \$options{version},
   'help|h',        \$options{help}
   )) {
   exit 1;
}

if ($options{version}) {
   print "make-lsm $version\n";
   exit;
}

if ($options{help}) {
   print
"Usage:  make-lsm [version]

Creates a Linux Software Map (LSM) description file.  The file lsm.in must
be created first; you can get a blank at http://lsm.execpc.com/lsm/LSM.README

You may omit [version] when the version is kept in a file named VERSION.

  -h, --help     Print this help text
  -V, --version  Print program version

";
   exit;
}

#----------------------------------------------------------------------------
# Set up some lsm-describing data
#----------------------------------------------------------------------------

# Fields with multiple lines, whose form will be preserved:
$multiline = join('|', qw(
   Author
   Maintained-by));

# Fields with multiple lines, which will be formatted to the
# maximum line length:
$freeform = join('|', qw(
   Description
   Keywords
   Platforms
   Copying-policy));

$margin = 17;
$maxline = 78;

@order = qw(
  Title          
  Version        
  Entered-date   
  Description    
  Keywords       
  Author         
  Maintained-by  
  Primary-site   
  Alternate-site 
  Original-site  
  Platforms      
  Copying-policy 
  );

%required = (
  'Title'        => 1,
  'Version'      => 1,
  'Entered-date' => 1,
  'Description'  => 1,
  'Primary-site' => 1
  );

#----------------------------------------------------------------------------
#  Read lsm.in.  It is in the form of a regular LSM file.
#----------------------------------------------------------------------------

%fields = ();

read_lsm_in();


#----------------------------------------------------------------------------
# Fill in the fields that a program can
#----------------------------------------------------------------------------

unless ($version = shift @ARGV) {
  chomp($version = `cat VERSION`);
  $version or die "make-lsm: can't get version from file 'VERSION'\n";
}

# The LSM format (v4) only allows kilobytes or megabytes for the size.

$prefix = "$fields{Title}-$version";
$size = undef;

foreach $suffix (@suffixes) {
   if (-f "$prefix.$suffix") {
      $tarball = "$prefix.$suffix";
      $bytes = (stat $tarball)[7];
      if ($bytes < 1048576) {
         $size = sprintf('%0.1fkb', $bytes / 1024);
      } else {
         $size = sprintf('%0.1fMB', $bytes / 1048576);
      }
      $size =~ s/\.0//;
      last;
   }
}

unless (defined $size) {
   $tarball = "$prefix.tar.gz";
   $size = '1kb';
}

$lsmfile = "$fields{Title}-$version.lsm";

$fields{Version} = $version;
$fields{'Entered-date'} = strftime("%Y-%m-%d", localtime);

#----------------------------------------------------------------------------
# Make sure required fields are present
#----------------------------------------------------------------------------

foreach $field (@order) {
  if (!$fields{$field} and $required{$field}) {
    die "Missing required field $field\n";
  }
}

#----------------------------------------------------------------------------
#  Write the file
#----------------------------------------------------------------------------

open F, ">$lsmfile" or die "Can't write $lsmfile: $!\n";

select F;

print "Begin4\n";

print itemhead('Title'), "$fields{Title}\n";
print itemhead('Version'), "$fields{Version}\n";
print itemhead('Entered-date'), "$fields{'Entered-date'}\n";
lsmfmt('Description', $fields{Description});
lsmfmt('Keywords', $fields{Keywords}) if $fields{Keywords};

printmultiline('Author');
printmultiline('Maintained-by');

print itemhead('Primary-site'), "$fields{'Primary-site'}\n";
print ' ' x $margin, "$size $tarball\n";

print itemhead('Alternate-site'), "$fields{'Alternate-site'}\n";

print itemhead('Original-site'), "$fields{'Original-site'}\n"
  if $fields{'Original-site'};

lsmfmt('Platforms', $fields{Platforms}) if $fields{Platforms};
lsmfmt('Copying-policy', $fields{'Copying-policy'})
   if $fields{'Copying-policy'};

print "End\n";

select STDOUT;

close F;

print "make-lsm: wrote $lsmfile\n";

#============================================================================
#  FIN
#============================================================================

sub itemhead {
  "$_[0]:" . ' ' x ($margin - (length($_[0]) + 1));
}

sub printmultiline {
   my ($field) = @_;
   my $i;

   if (@{$fields{$field}}) {
     print itemhead($field), "$fields{$field}->[0]\n";
     for ($i = 1; $i < @{$fields{$field}}; $i++) {
       print ' ' x $margin, "$fields{$field}->[$i]\n";
     }
   }
}

sub lsmfmt {
  my ($field, $text) = @_;
  my (@words, $i);

  my $line = itemhead($field);

  @words = split(/ +/, $text);

  for ($i = 0; $i < @words; $i++) {
    if (length($line) eq $margin) {
      $line .= $words[$i];
    } elsif (length($line) + 1 + length($words[$i]) > $maxline) {
      print "$line\n";
      $line = ' ' x $margin . $words[$i];
    } else {
      $line .= " $words[$i]";
    }
  }

  print "$line\n" if length($line) > $margin;
}


sub read_lsm_in {
   # Reading the lsm.in file

   my ($line, $text);
   my $lastfield = '';

   open F, 'lsm.in' or die "Please create lsm.in first.  Try make-lsm -h for usage instructions.\n";

   # 1. Look for 'Begin4'

   while ($line = <F>) {
      chomp $line;
      if ($line =~ /^Begin4$/) {
         last;
      } elsif ($line !~ /^\s*$/) {
         die "Invalid lsm.in file; begins with '$line'\n"
      }
   }

   while ($line = <F>) {
      chomp $line;
      if ($line =~ /^(\S[^:\s]+):\s*(\S.*?)\s*$/) {
         ($lastfield, $text) = ($1, $2);
         if ($lastfield =~ /^(?:$multiline)$/) {
            $fields{$lastfield} = [$text];
         } else {
            $fields{$lastfield} = $text;
         }
      } elsif ($line =~ /^\s\s*(\S.*?)\s*$/) {
         $text = $1;

         if ($lastfield eq 'Primary-site') {

            # Primary-site is two lines long, and here is the second line
            # from lsm.in.  Ignore it, as this program will be generating
            # that information.
            $lastfield = '';

         } elsif ($lastfield =~ /^(?:$multiline)$/) {
            push @{$fields{$lastfield}}, $text;
         } elsif ($lastfield =~ /^(?:$freeform)$/) {
            $fields{$lastfield} .= ' ' . $text;
         } elsif ($lastfield eq '') {
            die "make-lsm: lsm.in contains invalid line '$line'\n";
         } else {
            die "make-lsm: lsm.in contains multiline '$lastfield' field, which is not permitted\n";
         }
      }
   }

   close F;
}

