The correct generation and typesetting of an index for a Greek document prepared with LaTeX is not a trivial task at all. First of all, the program makeindex cannot handle Greek letters, but even if the .idx is writen using the babel latintranscription, the generated .ind file does not follow the order of the Greek alphabet. On the other hand, makeindex is doing a fine job for English. This means that if we let makeindex think that the Greek words are actually strange English words, we can use it to generate correct Greek indices. Let’s be more concrete.

Every line of an .idx file has the following format: \indexnetry{word}{Page number}, with the obvious meaning. If word is some Greek word, then in order to allow correct sorting we must sort index entries by using a transliteration that will correspond to a, to b, to c, etc. Now, if we transform the above

line into \indexentry{trans. word@word}{Page number}, it is possible to get

a correct index. So, the first thing we need is a routine that will perform this transliteration.

The following routine, g2e, accepts one argument, a word, and returns a transliteration of it. It can handle properly words writen with the Greek alphabet and word writen using the babel Greek transliteration. The routine is very simple: it splits the word into an array of characters, and creates a new word by transliterating each character of the original word.

1 ⟨Routine that performs transliteration 1⟩≡

  sub g2e
  {
     $word = $_[0];
     $tword = "";
     foreach $_(split(//, $word))
     {
       if    (/^a|^A|^|^|^|^/)    { $tword .= "a";}
       elsif (/^b|^B|^|^/)                { $tword .= "b"; }
       elsif (/^d|^D|^|^/)                { $tword .= "d"; }
       elsif (/^e|^E|^|^|^|^/)    { $tword .= "e"; }
       elsif (/^i|^I|^|^|^|^|^/) { $tword .= "i"; }
       elsif (/^o|^O|^|^|^|^/)    { $tword .= "o"; }
       elsif (/^p|^P|^|^/)                { $tword .= "p"; }
       elsif (/^g|^G|^|^/)                { $tword .= "c"; }
       elsif (/^z|^Z|^|^/)                { $tword .= "f"; }
       elsif (/^h|^H|^|^|^|^/)    { $tword .= "g"; }
       elsif (/^j|^J|^|^/)                { $tword .= "h"; }
       elsif (/^k|^K|^|^/)                { $tword .= "j"; }
       elsif (/^l|^L|^|^/)                { $tword .= "k"; }
       elsif (/^m|^M|^|^/)                { $tword .= "l"; }
       elsif (/^n|^N|^|^/)                { $tword .= "m"; }
       elsif (/^x|^X|^|^/)                { $tword .= "n"; }
       elsif (/^r|^R|^|^/)                { $tword .= "q"; }
       elsif (/^s|^c|^S|^|^|^/)       { $tword .= "r"; }

       elsif (/^t|^T|^|^/)                { $tword .= "s"; }
       elsif (/^u|^U|^|^|^|^|^/)  { $tword .= "t"; }
       elsif (/^f|^F|^|^/)                { $tword .= "u"; }
       elsif (/^q|^Q|^|^/)                { $tword .= "v"; }
       elsif (/^y|^Y|^|^/)                { $tword .= "w"; }
       elsif (/^w|^W|^|^|^|^/)    { $tword .= "x"; }
       elsif (/^'|^`|^~/)                         {                }
     }
     return $tword;
  }

Now, that we have solved one problem we must face the others, namely the modification of the .idx file and the subsequent modification of the .ind file. (This of course means that we use makeindex to actually generate the index.) Before we do anything we must get the various command line arguments. Our program accepts at most three command line arguments: mkindex.pl [-s A|a] [-l] index. With the -s switch one can specify which index style he wants to be used: A stands for an index where each group of entries that start with the same letter has as group-header this letter in uppercase form; and a means that the group-headers are lowercase letter. The absence of this switch denotes that the index will be just an ordinary LaTeX index, i.e., without a group-header. The -l specifies that this is an index that has only Latin words. The absence of this switch means that the index is a Greek one. Finally, index denotes the name of the .idx file; users may omit the file extension.

The first think we do is to check whether there any command line arguments. In case there aren’t or there more than three, we just print a usage message and exit. Otherwise, we process the command line arguments.

2 ⟨Check for command line arguments 2⟩≡

  $argc = @ARGV;
  if ($argc == 0 || $argc > 3 ) # no command line arguments or more than 3
  {                             # arguments
     die "Usage: mkindex.pl [-s A|a] [-l] index\n";
  }
  else
  {

⟨Process command line arguments 3⟩

  }

Since we don’t know apriori the number of arguments we set the following global variables: $style and $is_latin. For variable $style a negative value means that a lowercase letter will precede each group of words beginning with the same letter, a positive value means that the letter will be uppercase and the value 0 means that there will be no letter preceding each group of words. While for variable $is_latin a positive value means that the index is non-Greek script index and a negative or zero means it is a Greek script index. The last thing we must do is to check whether the index file exists.

3 ⟨Process command line arguments 3⟩≡

     $style = 0;
     $is_latin = 0;

⟨Get command line arguments 4⟩ ⟨Check if .idx file exists 5⟩ In order to get the various command line arguments we use a simple while loop that check each element of the array @ARGV. We check for both switches and we get the name of the file that contains the index.

4 ⟨Get command line arguments 4⟩≡

SWITCHES:
while($_= $ARGV[0], /^-/)
{
shift;
if (/^-s/)
{
if ($ARGV[0] eq "a")
{
   $style = -1;
}
elsif ($ARGV[0] eq "A")
{
   $style = 1;
}
else
{
   die "$ARGV[0]: Illegal argument for switch -s\n";
}
shift;
}
elsif (/^-l/)
{
$is_latin = 1;
}
elsif (!@ARGV)
{
last SWITCHES;
}
}
die "No index file name specified!\n" if(!@ARGV);
$file = $ARGV[0];

In order to check whether the index file exists, we simply use the -e operator. First we check if $file exits, if doesn’t then probably file $file.idx exists, i.e., the user hasn’t typed the filename extension. In case this doesn’t exist, then we are sure there is no index file. After these checks, variable $file contains the file name without extension.

5 ⟨Check if .idx file exists 5⟩≡

     if (! (-e $file))
     {
         die "$file.idx: no such file!\n" if (! (-e "$file.idx"));
         $index_file = "$file.idx";
     }
     else
     {
         $index_file = $file;
         $file = $1 if $index_file =~ /(.+)\..+/;
     }

We have now all the information we need in order to modify the index file. First we must rename the index file, since we are going to modify it. Next, we open the renamed file for reading and we open a fresh file into which we will write the modified index. These changes must be done only if this is a Greek index, i.e., if the value of $is_latin is equal to zero. The next think we do is to modify the index entries. If an index entry appears in the \frontmatter of a LaTeX document, then its page number comes out as a Latin number. Since we are preparing a document with the greek option of the babel package this number comes out as an argument of the command \textlatin, e.g., \textlatin{ix}. This means that we must modify the page information too. However, this change requires to rescan the whole index file. So, we delete the original index file, rename the modified one and then correct the page numbers, by using the same method.

6 ⟨Modify LaTeX index file 6⟩≡

  $old_file="$index_file.old";
  if ($is_latin == 0)
  {
     rename $index_file, $old_file;
     open(OLD, "$old_file") || die "Can't open file $old_file\n";
     open(NEW, ">$index_file") || die "Can't open file $index_file\n";

⟨Modify index entries 7⟩

     close OLD;
     close NEW;
     unlink $old_file;
  }
  rename $index_file, $old_file;
  open(OLD, "$old_file") || die "Can't open file $old_file\n";
  open(NEW, ">$index_file") || die "Can't open file $index_file\n";

⟨Correct page numbers 8a⟩

  close OLD;
  close NEW;
  unlink $old_file;

If the value of variable $is_latin is positive there is nothing to do. But, if it is zero, then we change each index line. In general each each index

line has one of the following forms: \indexentry{w@w'}, \indexentry{w|w'},

\indexentry{w!w'}, and \indexnetry{w} (for reason of brevity we have omitted the page information). In order to ensure proper sorting we must transliterate each index entry. In all case we must transliterate the word w. So, the for each case we must generate the following index lines: \indexnetry{tw@w'},

\indexentry{tw@w|w'}, \indexentry{tw@w!w'}, and \indexnetry{tw@w}, where

tw is the transliterated word.

7 ⟨Modify index entries 7⟩≡

while (<OLD>)
{
chomp($_);
#case \indexentry{w@w'}{p}
if (/^\\indexentry\x7B(.+)([^"])\x40(.+)\x7D(.+)/) #\x7B = {, \x7D = },
{                                                  #\x40 = @, \x7C = |,
                                                   #\x21 = !
   $tw = &g2e($1.$2);
   print NEW "\\indexentry{$tw\@$3}$4\n";
}
#case \indexnetry{w|w'}{p}
elsif (/^\\indexentry\x7B(.+)([^"])\x7C(.+)\x7D(.+)/)
{
   $w = "$1$2";
   $tw = &g2e($w);
   print NEW "\\indexentry{$tw\@$w|$3}$4\n";
}
#case \indexnetry{w!w'}{p}
elsif (/^\\indexentry\x7B(.+)([^"])\x21(.+)\x7D(.+)/)
{
   $w = "$1$2";
   $tw = &g2e($w);
   print NEW "\\indexnetry{$tw\@$w!$3}$4\n";
}
#case \indexentry{w}{p}
elsif (/^\\indexentry\x7B(.+)\x7D(.+)/)
{
   $tw = &g2e($1);
   print NEW "\\indexentry{$tw\@$1}$2\n";
}
}

Correcting the page numbers is very easy. We check each line and if one happens to have the form \indexentry{w}{\textlatin{p}}, then we replace it with \indexentry{w|textlatin}{p}. Of course this does not take care of cases where the | operator is already in use, but then we make the assumption that definitions and such stuff do not appear in the foreword of a document.

8a ⟨Correct page numbers 8a⟩≡

  while(<OLD>)
  {
      chomp($_);
      if (/\\indexentry\x7B(.+)\x7D\x7B\\textlatin\s*?\x7B(\w+)\x7D\x7D/)
      {
         print NEW "\\indexentry{$1|textlatin}{$2}\n";
      }
      else
      {
         print NEW "$_\n";
      }
  }

The .ind file will be generated by invoking program makeindex. In case the name of the program is different in the user’s installation, he/she must change the assignment at the beginning of the program. As we said, the user can choose between three different types of indices. Indices of type ”A” and ”a”, assume that LaTeX and makeindex have access to files Alpha.ist and alpha.ist. So, make sure these files are properly installed. Depending on the value of variable $style, we generate the appropriate index.

8b ⟨Generate .ind file 8b⟩≡

  if ($style < 0)
  {
     system("$makeindex -s alpha.ist $file");
  }
  elsif ($style > 0)
  {
     system("$makeindex -s Alpha.ist $file");
  }
  else
  {
     system("$makeindex $file");
  }

Now the we have created the .ind file, if the user has chosen either the A or a option we must modify the file so that the English letters become Greek ones. The mechanism is very simple: we employ the inverse of the mapping we employed in subroutine g2e. In order to do this we use a hash table. In case the $is_latin == 1, then the only thing we need to do is to translate the word “symbol” into Greek. Before, we do anything further we must open the file and to rename the old index file.

9 ⟨Modify the .ind file 9⟩≡

⟨Declare hash table 10⟩

$ind_file = "$file.ind";
$old_file="$ind_file.old";
if ($is_latin == 0)
{
   rename $ind_file, $old_file;
   open(OLD, "$old_file") || die "Can't open file $old_file\n";
   open(NEW, ">$ind_file") || die "Can't open file $ind_file\n";

⟨Correct header letters 12⟩

   close OLD;
   close NEW;
   unlink $old_file;
}
rename $ind_file, $old_file;
open(OLD, "$old_file") || die "Can't open file $old_file\n";
open(NEW, ">$ind_file") || die "Can't open file $ind_file\n";

⟨Translate the word symbol 11⟩

close OLD;
close NEW;
unlink $old_file;

We declare a hash table in which we will store the inverse transliteration table.

10 ⟨Declare hash table 10⟩≡

%e2g = (
'a' => '', 'A' => '',
'b' => '', 'B' => '',
'c' => '', 'C' => '',
'd' => '', 'D' => '',
'e' => '', 'E' => '',
'f' => '', 'F' => '',
'g' => '', 'G' => '',
'h' => '', 'H' => '',
'i' => '', 'I' => '',
'j' => '', 'J' => '',
'k' => '', 'K' => '',
'l' => '', 'L' => '',
'm' => '', 'M' => '',
'n' => '', 'N' => '',
'o' => '', 'O' => '',
'p' => '', 'P' => '',
'q' => '', 'Q' => '',
'r' => 'v', 'R' => '',
's' => '', 'S' => '',
't' => '', 'T' => '',
'v' => '', 'V' => '',
'u' => '', 'U' => '',
'w' => '', 'W' => '',
'x' => '', 'X' => ''
);

In order to translate the word symbol we must just scan the entire .ind file and to find the word symbols or Symbols. This isn’t difficult since each header line is of the form {\hfil word \hfil}, where word is either the a letter, the word symbols or the word Symbols. Once we find the word we replace it with its translation.

11 ⟨Translate the word symbol 11⟩≡

while (<OLD>)
{
if (/^\x7B\\hfil (\w)ymbols \\hfil\x7D/)
{
if ($1 eq "s")
{
       print NEW "{\\hfil \\textgreek{\\textbf{s'umbola}}";
       print NEW "\\hfil}\\nopagebreak\n";
}
elsif ($1 eq "S")
{
       print NEW "{\\hfil \\textgreek{\\textbf{S'umbola}}";
       print NEW "\\hfil}\\nopagebreak\n";
}
else
{
   die "Illegal header $1 in .ind file\n";
}
}
else
{
print NEW;
}
}

Correcting the header letters means to scan the whole index file and replace the English letters with Greek ones. We find lines of the form {\hfil a \hfil}\nopagebreak and we replace the English letter with the corresponding Greek by using the hash %e2g.

12 ⟨Correct header letters 12⟩≡

while (<OLD>)
{
if (/^{\\hfil (\w?) \\hfil}/)
{
   $lettergr = $e2g{$1};
   print NEW "{\\hfil $lettergr \\hfil}\\nopagebreak\n";
}
else
{
   print NEW ;
}
}

Let’s summarize. This program is makes it possible to use program makeindex as if this program was writen for Greek people only! The program works as follows: (a) it check the command line arguments, (b) it modifies the LaTeX generated index, (c) generates the .ind according to the user’s wishes, and (d) corrects the .ind file if necessary, i.e., if the user has asked for some alphabetic headers in his/her index.

13 ⟨* 13⟩≡

#!/usr/bin/perl
#
#(c) Copyright 1998 Apostolos Syropoulos
#                   apostolo@obelix.ee.duth.gr
#
$makeindex = "makeindex"; #name of the index generation utility

⟨Routine that performs transliteration 1⟩ ⟨Check for command line arguments 2⟩ ⟨Modify LaTeX index file 6⟩ ⟨Generate .ind file 8b⟩

if ($style != 0)
{

⟨Modify the .ind file 9⟩

}
__END__