[exim-cvs] cvs commit: exim/exim-doc/doc-scripts BuildFAQ Bu…

Góra strony
Delete this message
Reply to this message
Autor: Philip Hazel
Data:  
Dla: exim-cvs
Temat: [exim-cvs] cvs commit: exim/exim-doc/doc-scripts BuildFAQ BuildHTML BuildInfo BuildPDF DoConts DoIndex JoinPS Makefile f2h f2txt faqchk fc2k g2h g2man g2t exim/exim-doc/doc-src FAQ.src filter.src m
ph10 2004/10/07 16:04:36 BST

  Added files:
    exim-doc/doc-scripts BuildFAQ BuildHTML BuildInfo BuildPDF 
                         DoConts DoIndex JoinPS Makefile f2h f2txt 
                         faqchk fc2k g2h g2man g2t 
    exim-doc/doc-src     FAQ.src filter.src markup.sg spec.src 
    exim-doc/doc-txt     ChangeLog ChangeLog.0 Exim3.upgrade 
                         Exim4.upgrade NewStuff OptionLists.txt 
                         README README.SIEVE dbm.discuss.txt 
                         pcrepattern.txt pcretest.txt 
  Log:
  Start


  Revision  Changes    Path
  1.1       +59 -0     exim/exim-doc/doc-scripts/BuildFAQ (new)
  1.1       +12 -0     exim/exim-doc/doc-scripts/BuildHTML (new)
  1.1       +32 -0     exim/exim-doc/doc-scripts/BuildInfo (new)
  1.1       +10 -0     exim/exim-doc/doc-scripts/BuildPDF (new)
  1.1       +71 -0     exim/exim-doc/doc-scripts/DoConts (new)
  1.1       +430 -0    exim/exim-doc/doc-scripts/DoIndex (new)
  1.1       +130 -0    exim/exim-doc/doc-scripts/JoinPS (new)
  1.1       +31 -0     exim/exim-doc/doc-scripts/Makefile (new)
  1.1       +338 -0    exim/exim-doc/doc-scripts/f2h (new)
  1.1       +107 -0    exim/exim-doc/doc-scripts/f2txt (new)
  1.1       +102 -0    exim/exim-doc/doc-scripts/faqchk (new)
  1.1       +344 -0    exim/exim-doc/doc-scripts/fc2k (new)
  1.1       +1451 -0   exim/exim-doc/doc-scripts/g2h (new)
  1.1       +251 -0    exim/exim-doc/doc-scripts/g2man (new)
  1.1       +1347 -0   exim/exim-doc/doc-scripts/g2t (new)
  1.1       +7015 -0   exim/exim-doc/doc-src/FAQ.src (new)
  1.1       +1644 -0   exim/exim-doc/doc-src/filter.src (new)
  1.1       +86 -0     exim/exim-doc/doc-src/markup.sg (new)
  1.1       +27935 -0  exim/exim-doc/doc-src/spec.src (new)
  1.1       +2058 -0   exim/exim-doc/doc-txt/ChangeLog (new)
  1.1       +2862 -0   exim/exim-doc/doc-txt/ChangeLog.0 (new)
  1.1       +673 -0    exim/exim-doc/doc-txt/Exim3.upgrade (new)
  1.1       +1732 -0   exim/exim-doc/doc-txt/Exim4.upgrade (new)
  1.1       +605 -0    exim/exim-doc/doc-txt/NewStuff (new)
  1.1       +927 -0    exim/exim-doc/doc-txt/OptionLists.txt (new)
  1.1       +84 -0     exim/exim-doc/doc-txt/README (new)
  1.1       +433 -0    exim/exim-doc/doc-txt/README.SIEVE (new)
  1.1       +322 -0    exim/exim-doc/doc-txt/dbm.discuss.txt (new)
  1.1       +1413 -0   exim/exim-doc/doc-txt/pcrepattern.txt (new)
  1.1       +455 -0    exim/exim-doc/doc-txt/pcretest.txt (new)


Index: BuildFAQ
====================================================================
#! /bin/sh
# $Cambridge: exim/exim-doc/doc-scripts/BuildFAQ,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to build the Exim FAQ in text and HTML formats.

/bin/rm -f FAQ.txt* html/FAQ* FAQ-html/* FAQ-html.tar.*
/bin/rm -f config.samples.tar.gz config.samples.tar.bz2

# The FAQchk Perl script checks for the numbers being in order and for the
# right number of blank lines at various places.

faqchk FAQ.src
if [ $? != 0 ]; then exit 1; fi

# HTML version

f2h FAQ.src html
echo "html/FAQ*.html made"

fc2k
echo "html/FAQ-KWIC*.html made"

cp html/FAQ* html/*.txt FAQ-html
echo "copied to FAQ-html"

tar cf FAQ-html.tar FAQ-html
gzip FAQ-html.tar
echo "FAQ-html.tar.gz made"

tar cf FAQ-html.tar FAQ-html
bzip2 -9 FAQ-html.tar
echo "FAQ-html.tar.gz2 made"

# ASCII version

f2txt FAQ.src FAQ.txt
echo "FAQ.txt made"

cp FAQ.txt FAQ.txt-t
gzip -v --best FAQ.txt-t
mv FAQ.txt-t.gz FAQ.txt.gz
echo "FAQ.txt.gz made"

cp FAQ.txt FAQ.txt-t
bzip2 -v -9 FAQ.txt-t
mv FAQ.txt-t.bz2 FAQ.txt.bz2
echo "FAQ.txt.bz2 made"

# Configuration samples

tar cf config.samples.tar config.samples
gzip config.samples.tar
echo "config.samples.tar.gz made"

tar cf config.samples.tar config.samples
bzip2 -9 config.samples.tar
echo "config.samples.tar.bz2 made"

# End

Index: BuildHTML
====================================================================
#! /bin/sh
# $Cambridge: exim/exim-doc/doc-scripts/BuildHTML,v 1.1 2004/10/07 15:04:35 ph10 Exp $

  if [ $# != 1 ]; then
    echo "*** Usage: BuildHTML <Exim version>"
    exit 1
  fi


g2h -split chapter filter.src "Exim Filter Specification"
g2h -split chapter spec.src "Exim $1 Specification"

# End

Index: BuildInfo
====================================================================
#! /bin/sh
# $Cambridge: exim/exim-doc/doc-scripts/BuildInfo,v 1.1 2004/10/07 15:04:35 ph10 Exp $

if [ "$1" = "filter" ]; then
g2t -filter filter.src >filter.texinfo
if [ $? != 0 ]; then exit 1; fi
cd info
makeinfo filter.texinfo
if [ $? != 0 ]; then exit 1; fi
echo ""
echo info filter.info
echo ""
info -f ./filter.info
exit
fi

if [ "$1" = "" ]; then
g2t spec.src >spec.texinfo
if [ $? != 0 ]; then exit 1; fi
cd info
makeinfo spec.texinfo
if [ $? != 0 ]; then exit 1; fi
echo ""
echo info spec.info
echo ""
info -f ./spec.info
exit
fi

echo "***Usage: null or filter argument required"

####

Index: BuildPDF
====================================================================
#! /bin/sh
# $Cambridge: exim/exim-doc/doc-scripts/BuildPDF,v 1.1 2004/10/07 15:04:35 ph10 Exp $

echo "PDFing the spec"
ps2pdf spec.ps spec.pdf

echo "PDFing the filter spec"
ps2pdf filter.ps filter.pdf

# End

Index: DoConts
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/DoConts,v 1.1 2004/10/07 15:04:35 ph10 Exp $

$style = (@ARGV > 0)? $ARGV[0] : "a4ps";

open(IN, "z-rawindex") || die "Can't open z-rawindex\n";
open(OUT, ">z-contents") || die "Can't open z-contents\n";

print OUT <<'EOF';
.if ~~sys.fancy
.linelength ~~sys.linelength + 0.2in
.pagedepth ~~sys.pagedepth - 0.2in
.linedepth 12.24
.fi
.include "markup.sg"
.set chapter -1
.set p 0
.format p roman
.tabset 2em 2em
.
.foot
.set p ~~sys.pagenumber
$c [~~p]
.endfoot
.
.chapter Contents
.disable filling
.justify left
EOF

  while(<IN>)
    {
    if (/\$e/)
      {
      s/\$e\s*$//;                      # "see also" lines have no line number
      s/--\s*\d+$//;                    # remove "extra" number for index page 


      s/\n$//;                          # trailing newline


      if (!/^\$/)
        {
        print OUT ".blank\n";
        print OUT ".if ~~sys.leftonpage < 2*~~sys.linedepth\n";
        print OUT ".newpage\n";
        print OUT ".fi\n";  
        print OUT "\$shead\{$_\}\n"; 
        print OUT ".blank\n";
        }  
      else
        { 
        print OUT "$_\n";
        } 
      }  
    } 


close(IN);
close(OUT);

  system("sgcal z-contents -to zc-gcode -style $style -index /dev/null");
  if ($style eq "a4ps")
    {
    system("sgtops zc-gcode -to zc-ps");
    print "PostScript in zc-ps\n";
    }
  else
    {
    system("mv -f zc-gcode zc-txt");
    print "Text in zc-txt\n";       
    }


# End

Index: DoIndex
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/DoIndex,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script for producing the Index for the Exim manual from the output of the
# SGCAL run. This is copied from the script for the Exim book.


##############################################################################
# Patterns for matching things to be removed from the sort keys

# This was copied from the Exim book processor, but we have now found a
# better way of doing this. Leave the code until I am quite sure...

  # $pat[0]  = qr/ \(\\\*see also\*\\[^)]+\)/;
  # $pat[1]  = qr/(?<!@)\/\//;                     # //
  # $pat[2]  = qr/(?<!@)\/\\/;                     # /\
  # $pat[3]  = qr/(?<!@)\\\//;                     # \/
  # $pat[4]  = qr/(?<!@) \\                        # non-@ \, followed by one of
  #                             (?:
  #                             [\.\/] |           # dot or slash
  #                             !- |               # !-
  #                             !\+ |              # !+
  #                             !\. |              # !.
  #                             "\+ |              # "+
  #                             \([.\/]? |         # ( and optional . or slash
  #                             [[\$\\%?!-"] |     # [ $ \ % ! " or -
  #                             \*{1,2} |          # * or **
  #                             \^{1,2}\/?         # ^ or ^^ and optional slash
  #                             )/x;
  # $pat[5]  = qr/(?: []\$\\%)?!"] |               # ] $ \ % ) ? " or ! )
  #                   \*{1,2}  |                   # * or **            ) optional
  #                   \^{1,2})?                    # ^ or ^^            )
  #                   \\/x;                        # then \
  # $pat[6]  = qr/(?<!@)::/;
  # $pat[7]  = qr/\sR[FS]\b/;
  # $pat[8]  = qr/``/;
  # $pat[9]  = qr/''/;
  # $pat[10] = qr/`/;
  # $pat[11] = qr/'/;
  # $pat[12] = qr/,/;
  # $pat[13] = qr/\(e?s\)/;



# Other patterns

# $keysplit = qr/^(.*?)(\|\|.*?)?\s(R[AZ])?\s?(\d+)$/;

$keysplit = qr/^(.*?)(\@\|\@\|.*?)?\s(R[AZ])?\s?(\d+)$/;


# The sort function

sub cf {
my($x,$y) = ($a,$b);

  ############old#############
  #foreach $pattern (@pat)    # Remove strings by pattern
  #  {
  #  $x =~ s/$pattern//g;
  #  $y =~ s/$pattern//g; 
  #  } 
  ##########################



# Turn || into @|@|

$x =~ s/\|\|/@|@|/g;
$y =~ s/\|\|/@|@|/g;

# Remove all special characters, except those preceded by @

$x =~ s/(?<!\@)[^\w\@\s]//g;
$y =~ s/(?<!\@)[^\w\@\s]//g;

# Remove the escaping @s

#$x =~ s/\@(.)/$1/g;
#$y =~ s/\@(.)/$1/g;



  ################old ########################
  #$x =~ s/:(\w+):/$1/g;      # :fail: etc => fail
  #$y =~ s/:(\w+):/$1/g;


#$x =~ s/^\@[^a-z]+/\@/i; # Make keys starting with @
#$y =~ s/^\@[^a-z]+/\@/i; # sort on @ followed by the first letter
##############################################3


  $x =~ s/\@_/\x7f/g;        # Make underscore sort late (option names)
  $y =~ s/\@_/\x7f/g; 


# Split up to sort on individual parts

my($xp,$xs,$xr,$xn) = $x =~ /$keysplit/;
my($yp,$ys,$yr,$yn) = $y =~ /$keysplit/;

$xr = "" if !defined $xr;
$yr = "" if !defined $yr;

$xs = "" if !defined $xs;
$ys = "" if !defined $ys;

  if ($show_keys)
    {
    print "a=$a\n  x=$x\n  xp=$xp\n  xs=$xs\n  xr=$xr\n  xn=$xn\n";
    print "b=$b\n  y=$y\n  yp=$yp\n  ys=$ys\n  yr=$yr\n  yn=$yn\n";
    } 


  my ($c) = "\L$xp" cmp "\L$yp";        # Caseless, primary text only
  $c = $xp cmp $yp if $c == 0;          # Caseful, primary text only
  $c = "\L$xs" cmp "\L$ys" if $c == 0;  # Caseless, secondary text only
  $c = $xs cmp $ys if $c == 0;          # Caseful, secondary text only
  $c = $xn <=> $yn if $c == 0;          # Compare the numbers
  $c = $xr cmp $yr if $c == 0;          # Sort RA before RZ
  return $c;
  }




##############################################################################
# Function for getting the next line from the @lines vector, using the global
# index $1. If the next pair of lines specifies a range of pages, combine them.
# That's why $linenumber has to be global - so we can increment it. If there's
# a range error, return "".

sub getnextentry {
my($line) = $lines[$linenumber];
my($aa,$zz,$tline,$nextline,$tnextline);

  if ($line =~ / RA (\d+)/)
    {
    $aa = $1; 
    $nextline = $lines[++$linenumber];
    if ($nextline =~ / RZ (\d+)/) 
      { 
      $zz = $1;
      }
    else    
      {
      print STDERR "** Bad range data (1)\n";
      print STDERR "   $line\n";
      print STDERR "   $nextline\n";
      return "";
      }  


    $tline = $line;
    $tnextline = $nextline; 


    $tline =~ s/ RA \d+//; 
    $tnextline =~ s/ RZ \d+//;


    if ($tline ne $tnextline)
      {
      print STDERR "** Bad range data (2)\n";
      print STDERR "   $line\n";
      print STDERR "   $nextline\n";
      return "";
      }  


    $line = ($aa eq $zz)? "$tline $aa" : "$tline $aa--$zz";
    }   


  elsif ($line =~ / RZ (\d+)/)
    {
    print STDERR "** Bad range data (RZ without RA)\n";
    print STDERR "   $line\n";
    return "";
    } 


return $line
}




##############################################################################
# Function for outputting a line, checking for the current primary
# and indenting a bit for secondaries. We also need a newpar
# before each item, because the main indent is set to a largish indent
# for long reference lists, but the parindent is set to counter this.
# This is where we handle the break between letters. We know that any non-
# alphamerics at the start of lines are markup, except for @. A reference
# value of 99999 is for the "see also" lines. Suppress it.

sub outline {
my($text,$ref) = ($_[0],$_[1]);
my ($letter) = $text =~ /^[^A-Za-z0-9\@]*(.)/;

return if $text =~ /^\s*$/;

  if ($ref eq "99999")    # dummy for see also
    {
    $ref = "" 
    } 
  else
    {
    $ref = "#$ref";       # prepend space
    }    


if ($letter =~ /\d/) { $letter = "0"; } else { $letter = "\U$letter"; }

print OUT ".newpar\n";

  if ($letter ne $currentletter && $letter ge "A")
    {
    print OUT ".newletter\n"; 
    $currentletter = $letter;   
    } 


$text =~ s/\@'/\$'/g; # Turns @' into $' so that it prints a non-curly quote

  if ($text =~ /^(.+)\|\|(.*)$/)
    {
    my($primary,$secondary) = ($1,$2);


    if ($primary ne $lastprimary)
      {
      print OUT ".primary $primary\n"; 
      $lastprimary = $primary;
      }


    $primary =~ s/"/""/g;
    $secondary =~ s/"/""/g;   


    my($contprim) = $primary;
    $contprim =~ s/ \(\\\*see also\*\\[^)]+\)//; 


    print OUT ".secondary \"$primary\" \"$secondary$ref\" \"$contprim\"\n";
    } 


# Not a two-part item; insert @ if the first char is a dot

  else
    {
    print OUT "@" if $text =~ /^\./; 
    print OUT "$text$ref\n";
    $lastprimary = $text; 
    } 
  }






##############################################################################
# The main script

$save_sorted = 0;
$test_index = 0;
$show_keys = 0;

  while (@ARGV > 0)
    {
    my($arg) = shift @ARGV;
    if    ($arg eq "-k") { $show_keys = 1; }
    elsif ($arg eq "-s") { $save_sorted = 1; }
    elsif ($arg eq "-t") { $test_index = $save_sorted = 1; }
    else  { die "Unknown option $arg\n"; }  
    } 


  if ($test_index)
    {
    open(IN, "z-testindex") || die "Can't open z-testindex\n";
    }
  else
    {   
    open(IN, "z-rawindex") || die "Can't open z-rawindex\n";
    }


open(OUT, ">z-index") || die "Can't open z-index\n";

# Extract index lines ($e lines are contents). Until we hit the first
# $e line, we are dealing with "see also" index lines, for which we want
# to turn the line number into 99999.

$#lines = -1;
$prestuff = 1;

  while (<IN>)
    {
    s/\n$//; 
    if (/\$e/)
      {
      $prestuff = 0; 
      }
    else
      {
      s/(\D)$/$1 99999/ if $prestuff;          # No number in "see also"
      push(@lines, $_);
      } 
    $index_pagenumber = $1 if /^Index\$e(\d+)/;
    } 
  close(IN);


# Sort, ignoring markup

print STDERR "Sorting ...\n";
@lines = sort cf @lines;

# Keep a copy of the sorted data, for reference

  if ($save_sorted)
    {
    open(X, ">z-indexsorted") || die "Can't open z-indexsorted\n";
    foreach $line (@lines)
     {
     print X "$line\n";
     }
    close(X);     
    } 


# Heading for the index file

print OUT <<"EOF";
.library "a4ps"
.linelength ~~sys.linelength + 16.0

.include "markup.sg"

.indent 3em
.parspace 0
.parindent -3em
.justify left
.
.foot
\$c [~~sys.pagenumber]
.endfoot
.
.cancelflag #
.flag # "\$S*1"
.set INDEX true
.
.macro primary "text"
.if ~~sys.leftonpage < 2ld
.newcolumn
.fi
~~1
.newpar
.endm
.
.macro secondary "prim" "sec" "contprim"
.if ~~sys.leftonpage < 1ld
.newcolumn
.newpar
~~3 \$it\{(continued)\}
.newpar
.fi
##~~2
.endm
.
.macro newletter
.if ~~sys.leftonpage < 4ld
.newcolumn
.else
.space 1ld
.fi
.newpar
.endm
.
.set chapter -1
.page $index_pagenumber
.chapter Index
.columns 2
.newpar
.
EOF

# Process the lines and output the result.
# Note that $linenumber is global, and is changed by getnextentry() for
# pairs of lines that represent ranges.

$lastprimary = "";
$lastref = "";
$currenttext = $currentref = "";
$currentletter = "";
$badrange = 0;

print STDERR "Processing ...\n";

  for ($linenumber = 0; $linenumber < @lines; $linenumber++) 
    { 
    $line = &getnextentry();


    if ($line eq "")   # Bad range data - but carry on to get all of it
      {
      $badrange = 1;
      next;
      }   


    # Split off the text and reference


    ($text,$ref) = $line =~ /^(.*)\s+([\d-]+)$/;


    # If same as current text, just add the new reference, unless its a duplicate


    if ($text eq $currenttext)
      {
      if ($ref ne $lastref)
        {  
        $currentref .= ", $ref"; 
        $lastref = $ref;
        }  
      next;
      }


    # Not the same as the current text. Output the current text, then 
    # set up a new current. 


    &outline($currenttext, $currentref);


    $currenttext = $text; 
    $currentref = $lastref = $ref; 
    }


# Output the final line and close the file

&outline($currenttext, $currentref);
close(OUT);

die "** Aborted\n" if $badrange;

# Format the index

system("sgcal z-index -to zi-gcode -index /dev/null");
system("sgtops zi-gcode -to zi-ps");
print "PostScript in zi-ps\n";

# End

Index: JoinPS
====================================================================
#!/usr/bin/perl
# $Cambridge: exim/exim-doc/doc-scripts/JoinPS,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Make the basic PostScript file for the Exim spec from the gcode file, then
# join it together with the contents and the index, to make a single
# PostScript file, suitable for double-sided printing.

sub blank_page {
my($title) = shift @_;
printf(OUT "%%%%Page: %s %d\nxpage\n\n", $title, $pagenumber++);
}


  @roman = ("i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix", "x",
            "xi", "xii", "xiii", "xiv", "xv", "xvi", "xvii", "xviii", "xix");


$pagenumber = 1;

system("sgtops z-gcode -to z-ps") && die "sgtops run failed\n";

open(SPEC, "z-ps") || die "Can't open z-ps\n";
open(CONTS, "zc-ps") || die "Can't open zc-ps\n";
open(INDEX, "zi-ps") || die "Can't open zi-ps\n";
open(OUT, ">spec.ps") || die "Can't open spec.ps\n";

# Copy spec headings etc.

  while (<SPEC>)
    {
    last if (/^%%Page:/) ;
    print OUT;
    }


# Copy the first two pages - the title page, and its blank verso

  for ($i = 1; $i < 3; $i++)
    {
    printf(OUT "%%%%Page: title%s %d\n", ($i == 1)? "" : "-verso", $pagenumber++);
    while (<SPEC>)
      {
      last if (/^%%Page:/) ;
      print OUT;
      }
    }   


# Skip the contents heading

  while (<CONTS>)
    {
    last if (/^%%Page:/) ;
    }


# Output the contents pages - fudge the roman numerals as we know there
# won't be a vast number of them.

  $subpagenumber = 0;
  while (!eof CONTS)
    {
    printf(OUT "%%%%Page: %s %d\n", $roman[$subpagenumber++], $pagenumber++);
    while (<CONTS>)
      {
      next if (/^%%Pages:/);
      next if (/^%%Trailer/);
      last if (/^%%Page:/) ;
      print OUT;
      }
    }
  printf(OUT "\n");


# If contents was an odd number of pages, insert a blank page

&blank_page("contents-pad") if ($pagenumber & 1) == 0;

# Copy the rest of the main file

$subpagenumber = 1;

  while (!eof SPEC)
    {
    printf(OUT "%%%%Page: %d %d\n", $subpagenumber++, $pagenumber++);
    while (<SPEC>)
      {
      next if (/^%%Pages:/);
      next if (/^%%Trailer/);
      last if (/^%%Page:/) ;
      print OUT;
      }
    }
  printf(OUT "\n");


# If contents was an odd number of pages, insert a blank page

&blank_page("body-pad") if ($pagenumber & 1) == 0;

# Skip the index heading

  while (<INDEX>)
    {
    last if (/^%%Page:/) ;
    }


# Copy the index pages

$subpagenumber = 1;

  while (!eof INDEX)
    {
    printf(OUT "%%%%Page: I%d %d\n", $subpagenumber++, $pagenumber++);
    while (<INDEX>)
      {
      next if (/^%%Pages:/);
      next if (/^%%Trailer/);
      last if (/^%%Page:/) ;
      print OUT;
      }
    }


# Add final comments

printf(OUT "%%%%Trailer\n");
printf(OUT "%%%%Pages: %d\n", $pagenumber-1);

# That's it

close(SPEC);
close(CONTS);
close(INDEX);
close(OUT);

# End

Index: Makefile
====================================================================
# $Cambridge: exim/exim-doc/doc-scripts/Makefile,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Makefile for Exim documentation

  ps:;        sgcal-fr spec.src -v -to z-gcode -index z-rawindex
          sgtops z-gcode -to z-ps


  txt:;       g2man       
          sgcal-fr spec.src -style online -v -to z-txt -index z-rawindex


contents:; @DoConts

  index:;     @DoIndex


# The file z-rawindex is included by the filter source to create a TOC.
# First empty it, then do a dummy format to create it, then do a second
# pass. This works because the TOC occupies no more than the rest of the
# first page.

  filterps:;  /bin/rm -rf z-rawindex
          touch z-rawindex
          sgcal-fr filter.src -v -to z-gcode -index z-rawindex
          sgcal-fr filter.src -v -to z-gcode -index /dev/null
          sgtops z-gcode -to filter.ps


  filtertxt:; /bin/rm -rf z-rawindex
          touch z-rawindex
          sgcal-fr filter.src -style online -v -to filter.txt -index z-rawindex
          sgcal-fr filter.src -style online -v -to filter.txt -index /dev/null


  clean:;     /bin/rm -f z*


Index: f2h
====================================================================
#!/usr/bin/perl
# $Cambridge: exim/exim-doc/doc-scripts/f2h,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to turn the Exim FAQ into HTML.

use integer;

# Function to do text conversions that apply to both displays and non displays

  sub process_both {
  my($s) = $_[0];
  $s =~ s/</&#60;/g;                                 # Deal with < and >
  $s =~ s/>/&#62;/g;
  return $s;
  }



# Function to do text conversions to display paragraphs

sub process_display {
my($s) = $_[0];
$s =~ s/^==>/ /;
my($indent) = $s =~ /^(\s+)/;
my($remove) = " " x (length($indent) - 3);
$s =~ s/^$remove//mg;
$s = &process_both($s);
return $s;
}


# Function to do text conversions to paragraphs not in displays.

sub process_non_display {
my($s) = &process_both($_[0]);

  $s =~ s/@\\/@@backslash@@/g;                       # @\ temporarily hidden


  $s =~ s/\\#/&nbsp;/g;                              # \# is a hard space


  $s =~ s/\\\*\*([^*]*)\*\*\\/<b>$1<\/b>/g;          # \**...**\   => bold
  $s =~ s/\\\*([^*]*)\*\\/<i>$1<\/i>/g;              # \*.....*\   => italic
  $s =~ s/\\"([^"]*)"\\/<tt>$1<\/tt>/g;              # \"....."\   => fixed pitch
  $s =~ s/\\\$([^\$]*)\$\\/<i>\$$1<\/i>/g;           # \$.....$\   => $italic
  $s =~ s/\\\\([^\\]*)\\\\/<small>$1<\/small>/g;     # \\.....\\   => small
  $s =~ s/\\\(([^)]*)\)\\/<i>$1<\/i>/g;              # \(.....)\   => italic
  $s =~ s/\\-([^\\]*)-\\/<b>-$1<\/b>/g;              # \-.....-\   => -bold
  $s =~ s/\\\[([^]]*)\]\\/&\#60;<i>$1<\/i>&\#62;/gx; # \[.....]\   => <italic>
  $s =~ s/\\\?(.*?)\?\\/<a href="$1">$1<\/a>/g;      # \?.....?\   => URL
  $s =~ s/\\\^\^([^^]*)\^\^\\/<i>$1<\/i>/g;          # \^^...^^\   => italic
  $s =~ s/\\\^([^^]*)\^\\/<i>$1<\/i>/g;              # \^.....^\   => italic
  $s =~ s/\\%([^%]*)%\\/<b>$1<\/b>/g;                # \%.....%\   => bold
  $s =~ s/\\\/([^\/]*)\/\\/<i>$1<\/i>/g;             # \/...../\   => italic
  $s =~ s/\\([^\\]+)\\/<tt>$1<\/tt>/g;               # \.......\   => fixed pitch


  $s =~ s"//([^/\"]*)//"<i>$1</i>"g;                 # //.....//   => italic
  $s =~ s/::([^:]*)::/<i>$1:<\/i>/g;                 # ::.....::   => italic:


  $s =~ s/``(.*?)''/&#147;$1&#148;/g;                # ``.....''   => quoted text


  $s =~ s/\s*\[\[br\]\]\s*/<br>/g;                   # [[br]]      => <br>


  $s =~ s/@@backslash@@/\\/g;                        # Put back single backslash


  $s =~ s/^(\s*\(\d\)\s)/$1&nbsp;/;                  # Extra space after (1), etc.


# Cross references within paragraphs

$s =~ s/Q(\d{4})(?!:)/<a href="$xref{$1}">$&<\/a>/xg;

# References to configuration samples

$s =~ s/\b([CFLS]\d\d\d)\b/<a href="$1.txt">$1<\/a>/g;

# Remove white space preceding a newline in the middle of paragraphs,
# to keep the file smaller (and for human reading when debugging).

$s =~ s/^\s+//mg;

return $s;
}


# Main program

# We want to read the file paragraph by paragraph; Perl only does this if the
# separating lines are truly blank. Having been caught by lines containing
# whitespace before, do a detrailing pass first.

  open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (preliminary)\n";
  open(OUT, ">$ARGV[0]-$$") || die "can't open $ARGV[0]-$$\n";
  while (<IN>)
    {
    s/[ \t]+$//;
    print OUT;
    }
  close(IN);
  close(OUT);
  rename("$ARGV[0]-$$", "$ARGV[0]") ||
    die "can't rename $ARGV[0]-$$ as $ARGV[0]\n";


# The second argument is the name of a directory into which to put multiple
# HTML files. We start off with FAQ.html.

$hdir = $ARGV[1];
open(OUT, ">$hdir/FAQ.html") || die "can't open $hdir/FAQ.html\n";

# Initial output

print OUT <<End ;
<html>
<head>
<title>The Exim FAQ</title>
</head>
<body bgcolor="#F8F8F8" text="#00005A" link="#0066FF" alink="#0066FF" vlink="#000099">
<h1>The Exim FAQ</h1>
End

$/ = "";

# First pass to read the titles and questions and create the table of
# contents. We save it up in a vector so that it can be written after the
# introductory paragraphs.

open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (first time)\n";

$toc = 0;
$sec = -1;
$inul = 0;

  while ($_ = <IN>)
    {
    $count = s/\n/\n/g - 1;          # Number of lines in paragraph


    if ($count == 1 && /^\d+\./)     # Look for headings
      {
      chomp;
      push @toc, "</ul>" if $inul;
      $inul = 0;
      push @toc, "<br>\n\n" if $sec++ >= 0;
      push @toc, "<a name=\"TOC$toc\" href=\"FAQ_$sec.html\">$_</a>\n";
      $toc++;


      ($number,$title) = /^(\d+)\.\s+(.*)$/;
      if ($title ne "UUCP" && $title ne "IRIX" && $title ne "BSDI" &&
          $title ne "HP-UX")
        {
        ($initial,$rest) = $title =~ /^(.)(.*)$/;
        $title = "$initial\L$rest";
        $title =~ s/isdn/ISDN/;
        $title =~ s/\btls\b/TLS/;
        $title =~ s/\bssl\b/SSL/;
        $title =~ s/ os x/ OS X/; 
        }
      push @seclist, "<a href=\"FAQ_$sec.html\">$number. $title</a>";


      next;
      }


    if (/^(Q\d{4})/)                 # Q initial paragraph
      {
      if (!$inul)
        {
        push @toc, "<ul>\n";
        $inul = 1;
        }
      $num = $1;
      $rest = $';
      $xref{substr($num,1)} = "FAQ_$sec.html#TOC$toc";
      $rest =~ s/^: /:&nbsp;&nbsp;/;
      $rest = &process_non_display($rest);
      push @toc, "<li><a name=\"TOC$toc\" href=\"FAQ_$sec.html#TOC$toc\">$num</a>$rest<br><br></li>\n";
      $toc++;
      next;
      }
    }


push @toc, "</ul>\n" if $inul;
close(IN);


# This is the main processing pass. We have to detect the different kinds of
# "paragraph" and do appropriate things.

open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (second time)\n";

# Skip the title line

$_ = <IN>;

# Handle the rest of the file

$toc = 0;
$maxsec = $sec;
$sec = -1;

  while ($_ = <IN>)
    {
    $count = s/\n/\n/g - 1;          # Number of lines in paragraph
    chomp;                           # Trailing newlines


    if (/^The FAQ is divided into/)
      {
      my($count) = scalar(@seclist);
      my($cols) = ($count + 1)/2;


      print OUT "<hr><a name=\"TOC\"><h1>Index</h1></a>\n";
      print OUT "<p>A <i>Keyword-in-context</i> <a href=\"FAQ-KWIC_A.html\">index</a> " .
                "to the questions is available. This is usually the " .
                "quickest way to find information in the FAQ.</p>\n";


      print OUT "<h1>Contents</h1>\n";
      print OUT "<p>The FAQ is divided into the following sections:<br><br></p>\n";


      print OUT "<table>\n";


      for ($i = 0; $i < $cols; $i++)
        {
        print OUT "<tr>\n";
        print OUT "  <td>", "&nbsp;" x 4, "</td>\n";
        print OUT "  <td>&nbsp;$seclist[$i]</td>\n";
        print OUT "  <td>", "&nbsp;" x8, "$seclist[$cols+$i]</td>\n"
          if $cols+$i < $count;
        print OUT "</tr>\n";
        }
      print OUT "</table><br><p>\n<hr><br>\n";
      print OUT "<h1>List of questions</h1>\n";


      $_ = <IN>;                     # Skip section list
      next;
      }


    if ($count == 1 && /^\d+\./)     # Look for headings
      {
      if (@toc != 0)                 # TOC when hit first heading
        {
        while (@toc != 0) { print OUT shift @toc; }
        }


      # Output links at the bottom of this page


      print OUT "<hr><br>\n";
      print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
      if ($sec > 0)
        {
        printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>&nbsp;&nbsp;\n", $sec-1);
        }
      printf OUT ("<a href=\"FAQ_%d.html\">Next</a>\n", $sec+1);


      # New section goes in new file


      print OUT "</body>\n</html>\n";
      close OUT;


      $sec++;
      open(OUT, ">$hdir/FAQ_$sec.html") ||
        die "Can't open $hdir/FAQ_$sec.html\n";


      print OUT "<html>\n<head>\n" .
        "<title>The Exim FAQ Section $sec</title>\n" .
        "</head>\n" .
        "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
        "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";


      printf OUT "<h1>The Exim FAQ</h1>\n";


      print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
      if ($sec > 0)
        {
        printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>&nbsp;&nbsp;\n", $sec-1);
        }
      if ($sec < $maxsec)
        {
        printf OUT ("<a href=\"FAQ_%d.html\">Next</a>\n", $sec+1);
        }


      print OUT "<hr><br>\n";


      print OUT "<h2><a href=\"FAQ.html#TOC$toc\">$_</a></h2>\n";
      $toc++;
      next;
      }


    s/^([QA]\d{4}|[CFLS]\d{3}): /$1:&nbsp;&nbsp;/;


    if (/^(Q\d{4}:)/)               # Q initial paragraph
      {
      print OUT "<p>\n<a name=\"TOC$toc\" href=\"FAQ.html#TOC$toc\">$1</a>";
      $_ = &process_non_display($');
      print OUT "$_\n</p>\n";
      $toc++;
      next;
      }


    if (/^A\d{4}:/)                 # A initial paragraph
      {
      $_ = &process_non_display($_);
      s/^(A\d{4}:)/<font color="#00BB00">$1<\/font>/;
      print OUT "<p>\n$_\n</p>\n";
      next;
      }


    # If a paragraph begins ==> it is a display which must remain verbatin
    # and not be reformatted. The flag gets turned into spaces.


    if ($_ =~ /^==>/)
      {
      $_ = &process_display($_);
      chomp;
      print OUT "<pre>\n$_</pre>\n";
      }


    # Non-display paragraph; massage the final line & my sig.


    elsif (/^\*\*\* End of Exim FAQ \*\*\*/)
      {
      }


    else
      {
      $_ = &process_non_display($_);
      if (/^Philip Hazel/)
        {
        s/\n/<br>\n/g;
        s/<br>$/<hr><br>/;
        }
      print OUT "<p>\n$_\n</p>\n";
      }
    }


close(IN);

print OUT "<hr><br>\n";
print OUT "<a href=\"FAQ.html#TOC\">Contents</a>&nbsp;&nbsp;\n";
printf OUT ("<a href=\"FAQ_%d.html\">Previous</a>\n", $sec-1);

print OUT "</body>\n</html>\n";
close(OUT);
End

Index: f2txt
====================================================================
#!/usr/bin/perl
# $Cambridge: exim/exim-doc/doc-scripts/f2txt,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to turn the Exim FAQ into plain ASCII.

use integer;


# Function to do text conversions to display paragraphs

sub process_display {
my($s) = $_[0];
$s =~ s/^==>/ /;
return $s;
}


# Function to do text conversions to paragraphs not in displays.

sub process_non_display {
my($s) = $_[0];

  $s =~ s/@\\/@@backslash@@/g;                 # @\ temporarily hidden


  $s =~ s/\\#/ /g;                             # \# is a hard space


  $s =~ s/\\\*\*([^*]*)\*\*\\/$1/g;            # \**...**\   => text
  $s =~ s/\\\*([^*]*)\*\\/"$1"/g;              # \*.....*\   => "text"
  $s =~ s/\\"([^"]*)"\\/"$1"/g;                # \"....."\   => "text"
  $s =~ s/\\\$([^\$]*)\$\\/\$$1/g;             # \$.....$\   => $text
  $s =~ s/\\\\([^\\]*)\\\\/$1/g;               # \\.....\\   => text
  $s =~ s/\\\(([^)]*)\)\\/$1/g;                # \(.....)\   => text
  $s =~ s/\\-([^-]*)-\\/-$1/g;                 # \-.....-\   => -text
  $s =~ s/\\\[([^]]*)\]\\/<$1>/gx;             # \[.....]\   => <text>
  $s =~ s/\\\?(.*?)\?\\/$1/g;                  # \?.....?\   => text
  $s =~ s/\\\^\^([^^]*)\^\^\\/$1/g;            # \^^...^^\   => text
  $s =~ s/\\\^([^^]*)\^\\/$1/g;                # \^.....^\   => text
  $s =~ s/\\%([^%]*)%\\/"$1"/g;                # \%.....%\   => "text"
  $s =~ s/\\\/([^\/]*)\/\\/$1/g;               # \/...../\   => text
  $s =~ s/\\([^\\]+)\\/"$1"/g;                 # \.......\   => "text"


  $s =~ s"//([^/\"]*)//"$1"g;                  # //.....//   => text
  $s =~ s/::([^:]*)::/$1:/g;                   # ::.....::   => text:


  $s =~ s/``(.*?)''/"$1"/g;                    # ``.....''   => "text"


  $s =~ s/\s*\[\[br\]\]\s*\n/\n/g;             # Remove [[br]]


  $s =~ s/@@backslash@@/\\/g;                  # Put back single backslash


return $s;
}


# Main program

# We want to read the file paragraph by paragraph; Perl only does this if the
# separating lines are truly blank. Having been caught by lines containing
# whitespace before, do a detrailing pass first.

  open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (preliminary)\n";
  open(OUT, ">$ARGV[0]-$$") || die "can't open $ARGV[0]-$$\n";
  while (<IN>)
    {
    s/[ \t]+$//;
    print OUT;
    }
  close(IN);
  close(OUT);
  rename("$ARGV[0]-$$", "$ARGV[0]") ||
    die "can't rename $ARGV[0]-$$ as $ARGV[0]\n";


# The second argument is the name of the output file.

open(IN, "$ARGV[0]") || die "can't open $ARGV[0] (for real)\n";
open(OUT, ">$ARGV[1]") || die "can't open $ARGV[1]\n";

$/ = "";

  while ($_ = <IN>)
    {
    # Comment lines start with ##


    next if /^\#\#/;


    # If a paragraph begins ==> it is a display which must remain verbatin
    # and not be reformatted. The flag gets turned into spaces.


    if ($_ =~ /^==>/)
      {
      $_ = &process_display($_);
      }


    # Non-display paragraph


    else
      {
      $_ = &process_non_display($_);
      }


    print OUT;
    }


close(IN);
close(OUT);

End

Index: faqchk
====================================================================
#!/usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/faqchk,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to check the FAQ source and make sure I have all the numbers
# in the right order without duplication. Also check the numbers of blank
# lines. It gives up on any error (other than bad blank line counts).

sub oops {
print "\nLine $line: $_[0]\n";
print;
exit 1;
}

sub poops {
print "\nLine $line: $_[0]\n";
print;
$precede_fail = 1;
}


$line = 0;
$section = -1;
$expect_answer = 0;
$precede_fail = 0;

  while (<>)
    {
    $line++;
    if (/^\s*$/)
      {
      $blankcount++;
      next;
      }
    $preceded = $blankcount;
    $blankcount = 0;


    if (/^(\d+)\./)
      {
      &poops("$preceded empty lines precede (3 expected)") if $preceded != 3;
      &oops(sprint("Answer %02d%02d is missing\n", $section, $question))
        if $expect_answer;
      $section = $1;
      $question = ($section == 20)? 0 : 1;
      $expected = 1;
      if ($section == 99)
        {
        $c = 1;
        $f = 1;
        }
      next;
      }


    if ($section != 99)
      {
      if (/^Q(\d\d)(\d\d):/)
        {
        &poops("$preceded empty lines precede ($expected expected)")
          if $preceded != $expected;
        $expected = 2;


        &oops("Answer expected") if $expect_answer;
        &oops("Q$1$2 is in the wrong section") if ($1 != $section);
        &oops("Q$1$2 is out of order") if $2 != $question;


        $expect_answer = 1;
        next;
        }


      if (/^A(\d\d)(\d\d):/)
        {
        &poops("$preceded empty lines precede (1 expected)") if $preceded != 1;


        &oops("Question expected") if !$expect_answer;
        &oops("A$1$2 is in the wrong section") if $1 != $section;
        &oops("A$1$2 is out of order") if $2 != $question++;


        $expect_answer = 0;
        next;
        }
      }


    else
      {
      if (/^C(\d\d\d):/)
        {
        &oops("C$1 is mixed up in the Fs") if $f != 1;
        # Some Cxxx configs are omitted (not translated from Exim 3) so can
        # only check increasing number.
        &oops("C$1 is out of order") if $1 < $c;
        $c = $1;
        }
      elsif (/^F(\d\d\d):/)
        {
        &oops("F$1 is out of order") if $1 != $f++;
        next;
        }
      }
    }


exit 1 if $precede_fail;

# End

Index: fc2k
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/fc2k,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to read the HTML table of contents for the Exim FAQ and create an
# HTML KWIC index out of it.


########################################################################
# List of words to ignore - kept alphabetically for reference, but they
# don't have to be in order.

$ignore_list = "

a ability able about address addresses addressed affect affected
after against all allow allowed allows already also although always am an and
and/or any anybody anyone anything anywhere are aren't arrange arrive as at

back bad based basically be because been behave behaviour being best between
bob both bug build builds built busy but by

call called calls can can't cannot causes causing central certain code comes
coming command commands complain complaining complains configure configured
conjunction contact contain contains contained correct correctly could
currently customer

day days defined deliver delivers delivered delivery deliveries did do does
doesn't doing don't down during

e-mail e-mails each easy else email emails entirely entries entry especially
etc even ever every example exim exim's experiencing

far few file files find fine fly following for form found from fully

get gets getting given gives giving go goes going got

handle handles handled handling happen happens has have haven't having helpful
him host hosts how however

i i'd i'm i've if in indeed instead into is issue issues isn't it it's its

jim just

keep keeps know knows

like line lines look looked looking lot

machine machines machine's mail mails main make me mean means message messages
might more must my myself

near need neither no nor not now

occur of off often ok on one only or other our out over own

part parts particular per place possibility possible present problem problems
put puts

quite

raised rather really reason rid right round run runs

same say saying see seeing seem seems seen sees set setting she should so some
somehow something sometimes stand state statement still strange such supposed
system systems

take takes than that the their them then there these they things think this
those to try though to/for told too tried tries trying

under until up use uses used using usually

valid value values via

want wanted wanting was way we we've well what what's when where whereabouts
whenever whether which while who whose why will with within without wish won't
wondered work worked working works would wrong

xxx

yet yyy

";
########################################################################


# The regular expression fragment that defines the separator between words

$wordgap = "(?:[]().?,;:\"']|(?><[^>]*>))*(?:\\s+|\$)(?:[[(\"'`]|(?><[^>]*>))*";


########################################################################
# Function to add to a length to accommodate HTML stuff

sub setlen{
my($len, $s) = @_;

$len += length($1) while ($s =~ /(<\/?[a-z]+>)/ig);
$len += 1 while ($s =~ /&#\d+;/g);

return $len;
}


########################################################################
# Function to write out the list of initials with references

sub write_initials {
my($this_initial) = "$_[0]";

print OUT "<p>\n&nbsp;&nbsp;";

  foreach $initial (sort keys %initials)
    {
    if ($initial eq $this_initial)
      {
      print OUT "&nbsp;&nbsp;&nbsp;<font size=7 color=\"#FF0A0A\"><b>$initial</b></font>&nbsp;";
      }
    else
      {
      print OUT "<a href=\"FAQ-KWIC_$initial.html\">&nbsp;&nbsp;$initial</a>";
      }
    }


print OUT "&nbsp;"x4 . "<a href=\"FAQ.html#TOC\">FAQ Contents</a>\n</p>\n";
}



########################################################################
# The main program. We can pick out the contents lines because they lie
# between <li> and </li> in the file, sometimes on more than one physical
# line.

# Turn the list of ignorable words into a hash for quick lookup. Add the
# empty word to the list.

@words = split /\s+/, $ignore_list;
foreach $word (@words) { $ignore{$word} = 1; }
$ignore{""} = 1;


# Open the file and do the job

open(IN, "html/FAQ.html") || die "Can't open html/FAQ.html\n";

  while (<IN>)
    {
    next unless /^<li>/;
    $_ .= <IN> while !/<\/li>$/;
    chomp;
    s/\n\s*/ /g;


    # Extract the operative text into $text, with the beginning in $pre.


    my($pre,$text,$post) = /^<li>(.*<\/a>:(?:&nbsp;)*)(.*)<br><br><\/li>$/;


    # Now split into words. As well as punctuation, there may be HTML thingies
    # between words. Absorb them into the separators.


    my(@words) = split /$wordgap/, $text;


    # Lower case all the words, and remove those that we don't want.
    # Then keep a list of all the used initials.


    REMOVE_IGNORE:
    for ($i = 0; $i < scalar @words; $i++)
      {
      my($word) = $words[$i] = "\L$words[$i]\E";


      # Remove certain forms of word and those on the ignore list


      if (defined $ignore{$word} ||  # word on ignore list
          $word =~ /^-+$/        ||  # word consists entirely of hyphens
          $word =~ /^-[^a-z]/    ||  # follows leading hyphen with non-letter
          $word =~ /^[^a-z-]/    ||  # starts with a non-letter or hyphen
          $word =~ /[@^.]/           # contains @ or ^ or .
         )
        {
        splice(@words, $i, 1);
        redo REMOVE_IGNORE if $i < scalar @words;
        }


      # Otherwise, build up a list of initials


      else
        {
        my($inword) = $word; 
        $inword =~ s/^-//; 
        $initial = substr($inword, 0, 1);
        $initials{"\U$initial\E"} = 1;
        }
      }


    # Create the lines for the KWIC index, and store them in associative
    # arrays, with the keyword as the key. That will get them sorted
    # automatically.


    while (scalar @words > 0)
      {
      my($word) = shift @words;
      my($pretext, $casedword, $posttext) =
        $text =~ /(.*?)(?<![a-z])(\Q$word\E)(?![a-z])(.*)/i;


      # Remove a leading hyphen from $word so that it sorts according to
      # the leading letter. What is actually output is $casedword, which
      # retains the hyphen.


      $word =~ s/^-//;   


      my($prelen) = length $pretext;
      my($postlen) = length $posttext;


      # We want to chop excessively long entries on either side. We can't set
      # a fixed length because of the HTML control data. Call a function to
      # add the given length to allow for HTML stuff. This is crude, but it
      # does roughtly the right thing.


      my($leftlen) = &setlen(70, $pretext);
      my($rightlen) = &setlen(70, $posttext);


      if ($prelen > $leftlen)
        {
        my($cutoff) = $leftlen;
        $cutoff++
          while ($cutoff < $prelen && substr($pretext, -$cutoff, 1) ne " ");
        $pretext = "... " . substr($pretext, -$cutoff);
        }


      if ($postlen > $rightlen)
        {
        my($cutoff) = $rightlen;
        $cutoff++
          while ($cutoff < $postlen && substr($posttext, $cutoff, 1) ne " ");
        $posttext = substr($posttext, 0, $cutoff) . "...";
        }


      # If the pre text has a font-ending not preceded by a font beginning
      # (i.e. we've chopped the beginning off), we must insert a beginning.


      while ($pretext =~ /^(.*?)<\/(small|tt|b|i)>/ && $1 !~ /<$2>/)
        {
        $pretext = "<$2>" . $pretext;
        }


      # If the pre text ends in a special font, we have to terminate that,
      # and reset it at the start of the post text.


      my($poststart) = "";


      while ($pretext =~ /<(small|tt|b|i)>(?!.*?<\/\1>)/)
        {
        $pretext .= "</$1>";
        $poststart .= "<$1>";
        }


      # If the post text changes font but doesn't close it, we must add
      # the closure.


      while ($posttext =~ /<(small|tt|b|i)>(?!.*?<\/\1>)/)
        {
        $posttext .= "</$1>";
        }


      # Remove any unnecessary changes in either of them


      $pretext  =~ s/<(small|tt|b|i)>\s*<\/\1>//g;
      $posttext =~ s/<(small|tt|b|i)>\s*<\/\1>//g;


      # Save the texts in associative arrays. Add the question number to
      # the end of the word to make the key.


      $pre =~ /(Q\d\d\d\d)/;
      my($key) = "$word-$1";


      $tableft{$key}  = $pre . $pretext;
      $tabright{$key} = $poststart .
        "<font color=\"#FF0A0A\">$casedword</font>" . $posttext;
      }
    }


close(IN);

# Now write out the files. Each letter in the index goes in a different file

$current_initial = "";

  foreach $key (sort keys %tableft)
    {
    my($initial) = $key =~ /^(.)/;
    $initial = "\U$initial\E";


    if ($initial ne $current_initial)
      {
      if ($current_initial ne "")
        {
        print OUT "</table>\n";
        &write_initials($current_initial);
        print OUT "</body>\n</html>\n";
        close OUT;
        }


      open (OUT, ">html/FAQ-KWIC_$initial.html") ||
        die "Can't open html/FAQ-KWIC_$initial.html\n";
      print OUT
        "<html>\n" .
        "<head>\n" .
        "<title>Exim FAQ: KWIC index section $initial</title>\n" .
        "</head>\n" .
        "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" link=\"#0066FF\" alink=\"#0066FF\" vlink=\"#000099\">\n" .
        "<h1>Exim FAQ: Keyword-in-context index</h1>\n";


      write_initials($initial);


      if ($initial eq "A")
        {
        print OUT <<End ;
  <p>
  This <i>Keyword-in-context</i> index for the Exim FAQ is generated
  automatically from the FAQ source. Browsers may not display the data very
  prettily, but it is hoped that it may provide a useful aid for finding things
  in the FAQ.
  </p>
  End
        }


      print OUT "<table border>\n";
      $current_initial = $initial;
      }


    print OUT "<tr>\n";
    print OUT "<td align=\"right\">$tableft{$key}</td>\n";
    print OUT "<td align=\"left\">$tabright{$key}</td>\n";
    print OUT "</tr>\n";
    }


# Close the final file

  if ($current_initial ne "")
    {
    print OUT "</table>\n";
    &write_initials($current_initial);
    print OUT "</body>\n</html>\n";
    close OUT;
    }


# End

Index: g2h
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/g2h,v 1.1 2004/10/07 15:04:35 ph10 Exp $

  # This is a script that turns the SGCAL source of Exim's documentation into
  # HTML. It can be used for both the filter document and the main Exim
  # specification. The syntax is
  #
  #    g2h [-split no|section|chapter] <source file> <title>
  #
  # Previously, -split section was used for the filter document, and -split
  # chapter for the main specification. However, the filter document has gained
  # some chapters, so they are both split by chapter now. Only one -split can be
  # specified.
  #
  # A number of assumptions about the style of the input markup are made.
  #
  # The HTML is written into the directory html/ using the source file base
  # name as its base.


# Written by Philip Hazel
# Starting 21-Dec-2001
# Last modified 26-Nov-2003

#############################################################################



  ##################################################
  #             Open an output file                #
  ##################################################


sub openout {
open (OUT, ">$_[0]") || die "Can't open $_[0]\n";

# Boilerplate

print OUT "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";

  print OUT "<html>\n<head>\n<title>$doctitle" .
    (($thischapter > 0)? " chapter $thischapter" : "") .
    (($thissection > 0)? " section $thissection" : "") .
    "</title>\n</head>\n" .
    "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
    "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";


# Forward/backward links when chapter splitting

  if ($chapsplit)
    {
    print OUT "<font size=2>\n";
    printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;\n",
      $thischapter - 1) if $thischapter > 1;
    printf OUT ("<a href=\"${file_base}_%s.html\">Next</a>&nbsp;&nbsp;\n",
      $thischapter + 1) if $thischapter < $maxchapter;
    print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
    print OUT "&nbsp;" x 6, "($doctitle)\n</font><hr>\n";
    }


# Forward/backward links when section splitting

  elsif ($sectsplit)
    {
    print OUT "<font size=2>\n";
    printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;\n",
      $thissection - 1) if $thissection > 1;
    printf OUT ("<a href=\"${file_base}_%s.html\">Next</a>&nbsp;&nbsp;\n",
      $thissection + 1) if $thissection < $maxsection;
    print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
    print OUT "&nbsp;" x 6, "($doctitle)\n</font><hr>\n";
    }


# Save the final component of the current file name (for TOC creation)

$_[0] =~ /^(?:.*)\/([^\/]+)$/;
$current_file = $1;
}



  ##################################################
  #              Close an output file              #
  ##################################################


  # The first argument is one of:
  #
  # "CHAP"   a chapter is ending
  # "SECT"   a section is ending
  # ""       the whole thing is ending
  #
  # In the first two cases $thischapter and $thissection contain the new chapter
  # and section numbers, respectively. In the third case, we can deduce what is
  # ending from the flags. The variables contain the current values.


sub closeout {
my($s) = $_[0];

print OUT "<hr>\n" if !$lastwasrule;
&setpar(0);

  if ($s eq "CHAP")
    {
    print OUT "<font size=2>\n";
    printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
      $thischapter - 2) if ($thischapter > 2);
    print OUT "<a href=\"${file_base}_$thischapter.html\">Next</a>&nbsp;&nbsp;";
    print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
    print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
    }


  elsif ($s eq "SECT")
    {
    print OUT "<font size=2>\n";
    printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
      $thissection - 2) if ($thissection > 2);
    print OUT "<a href=\"${file_base}_$thissection.html\">Next</a>&nbsp;&nbsp;";
    print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
    print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
    }


  else
    {
    if ($chapsplit)
      {
      print OUT "<font size=2>\n";
      printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
        $thischapter - 1) if ($thischapter > 1);
      print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
      print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
      }
    elsif ($sectsplit)
      {
      print OUT "<font size=2>\n";
      printf OUT ("<a href=\"${file_base}_%s.html\">Previous</a>&nbsp;&nbsp;",
        $thissection - 1) if ($thissection > 1);
      print OUT "<a href=\"${file_base}_toc.html\">Contents</a>\n";
      print OUT "&nbsp;" x 6, "($doctitle)\n</font>\n";
      }
    }


print OUT "</body>\n</html>\n";
close(OUT);
}



  ##################################################
  #            Handle an index line                #
  ##################################################


# This function returns an empty string so that it can be called as part
# of an s operator when handling index items within paragraphs. The two
# arguments are:
#
# the text to index, already converted to HTML
# 1 for the concept index, 0 for the options index

sub handle_index {
my($text) = $_[0];
my($hash) = $_[1]? \%cindex : \%oindex;
my ($key,$ref);

# Up the index count, and compute the reference to the file and the
# label within it.

  $index_count++;
  $ref = $chapsplit?
        "${file_base}_$thischapter.html#IX$index_count"
    : $sectsplit?
        "${file_base}_$thissection.html#IX$index_count"
    :
        "#IX$index_count";


# Create the index key, which consists of the text with all the HTML
# coding and any leading quotation marks removed. Turn the primary/secondary
# splitting string "||" into ":".

$text =~ s/\|\|/:/g;

$key = "$text";
$key =~ s/<[^>]+>//g;
$key =~ s/&#(\d+);/chr($1)/eg;
$key =~ s/^`+//;

# Turn all spaces in the text into &nbsp; so that they don't ever split.
# However, there may be spaces in the HTML that already exists in the
# text, so we have to avoid changing spaces inside <>.

$text =~ s/ (?=[^<>]*(?:<|$))/&nbsp;/g;

# If this is the first encounter with this index key, we create a
# straightforward reference.

  if (!defined $$hash{$key})
    {
    $$hash{$key} = "<a href=\"$ref\">$text</a>";
    }


# For the second and subsequent encounters, add "[2]" etc. to the
# index text. We find out the number by counting occurrences of "<a"
# in the existing string.

  else
    {
    my($number) = 1;
    $number++ while $$hash{$key} =~ /<a/g;
    $$hash{$key} .= " &nbsp;<a href=\"$ref\">[$number]</a>";
    }


# Place the name in the current output

print OUT "<a name=\"IX$index_count\"></a>\n";
return "";
}



  ##################################################
  #             Handle emphasis bars               #
  ##################################################


# Set colour green for text marked with "emphasis bars", keeping
# track in case the matching isn't perfect.

  sub setinem {
  if ($_[0])
    {
    return "" if $inem;
    $inem = 1;
    return "<font color=green>\n";
    }
  else
    {
    return "" if !$inem;
    $inem = 0;
    return "</font>\n";
    }
  }




  ##################################################
  #          Convert marked-up text                #
  ##################################################


  # This function converts text from SGCAL markup to HTML markup, with a couple
  # of exceptions:
  #
  # 1. We don't touch $t because that is handled by the .display code.
  #
  # 2. The text may contain embedded .index, .em, and .nem directives. We
  #    handle .em and .nem, but leave .index because it must be done during
  #    paragraph outputting.
  #
  # In a non-"rm" display, we turn $rm{ into cancelling of <tt>. Otherwise
  # it is ignored - in practice it is only used in that special case.
  #
  # The order in which things are done in this function is highly sensitive!


sub handle_text {
my($s) = $_[0];
my($rmspecial) = $_[1];

# Escape all & characters (they aren't involved in markup) but for the moment
# use &+ instead of &# so that we can handle # characters in the text.

$s =~ s/&/&+038;/g;

# Turn SGCAL literals into HTML literals that don't look like SGCAL
# markup, so won't be touched by what follows. Again, use + instead of #.

$s =~ s/@@/&+064;/g;
$s =~ s/@([^@])/"&+".sprintf("%0.3d",ord($1)).";"/eg;

# Now turn any #s that are markup into spaces, and convert the previously
# created literals to the correct form.

$s =~ s/#/&nbsp;/g;
$s =~ s/&\+(\d+);/&#$1;/g;

# Some simple markup that doesn't involve argument text.

  $s =~ s/\$~//g;                   # turn $~  into nothing
  $s =~ s/__/_/g;                   # turn __  into _
  $s =~ s/--(?=$|\s|\d)/&#150;/mg;  # turn --  into endash in text or number range
  $s =~ s/\(c\)/&copy;/g;           # turn (c) into copyright symbol


# Use double quotes

# $s =~ s/`([^']+)'/``$1''/g;

$s =~ s/`([^']+)'/&#147;$1&#148;/g;

# This is a fudge for some specific usages of $<; can't just do a global
# is it occurs in things like "$<variable name>" as well.

  $s =~ s/(\d)\$<-/$1-/g;  # turn 0$<- into 0-
  $s =~ s/\$<//g;          # other $< is ignored


# Turn <<...>> into equivalent SGCAL markup that doesn't involve the use of
# < and >, and then escape the remaining < and > characters in the text.

$s =~ s/<<([^>]*?)>>/<\$it{$1}>/g; # turn <<xxx>> into <$it{xxx}>
$s =~ s/</&#060;/g;
$s =~ s/>/&#062;/g;

# Other markup...

  $s =~ s/\$sm\{//g;              # turn $sm{     into nothing
  $s =~ s/\$smc\{//g;             # turn $smc{    into nothing
  $s =~ s/\$smi\{//g;             # turn $smi{    into nothing


  $s =~ s/\$tt\{([^\}]*?)\}/<tt>$1<\/tt>/g;    # turn $tt{xxx} into <tt>xxx</tt>
  $s =~ s/\$it\{([^\}]*?)\}/<em>$1<\/em>/g;    # turn $it{xxx} into <em>xxx</em>
  $s =~ s/\$bf\{([^\}]*?)\}/<b>$1<\/b>/g;      # turn $bf{xxx} into <b>xxx</b>


  $s =~ s/\$cb\{([^\}]*?)\}/<tt><b>$1<\/b><\/tt>/g; # turn $cb{xxx} into
                                                    #   <tt><b>xxx</b></tt>


  $s =~ s/\\\\([^\\]*?)\\\\/<font size=-1>$1<\/font>/g; # turn \\xxx\\ into
                                                        #  small font
  $s =~ s/\\\?([^?]*?)\?\\/<a href="$1">$1<\/a>/g;      # turn \?URL?\ into URL


  $s =~ s/\\\(([^)]*?)\)\\/<i>$1<\/i>/g;       # turn \(xxx)\ into <i>xxx</i>
  $s =~ s/\\\"([^\"]*?)\"\\/<tt>$1<\/tt>/g;    # turn \"xxx"\ into <tt>xxx</tt>



  $s =~ s/\\\$([^\$]*?)\$\\/<tt>\$$1<\/tt>/g;  # turn \$xxx$\   into <tt>$xxx</tt>
  $s =~ s/\\\-([^\\]*?)\-\\/<i>-$1<\/i>/g;     # turn \-xxx-\   into -italic
  $s =~ s/\\\*\*([^*]*?)\*\*\\/<b>$1<\/b>/g;   # turn \**xxx**\ into <b>xxx</b>
  $s =~ s/\\\*([^*]*?)\*\\/<i>$1<\/i>/g;       # turn \*xxx*\   into italic
  $s =~ s/\\%([^*]*?)%\\/<b>$1<\/b>/g;         # turn \%xxx%\   into bold
  $s =~ s/\\([^\\]*?)\\/<tt>$1<\/tt>/g;        # turn \xxx\     into <tt>xxx</tt>
  $s =~ s/::([^\$]*?)::/<i>$1:<\/i>/g;         # turn ::xxx::   into italic:
  $s =~ s/\$\*\$/\*/g;                         # turn $*$       into *


# Handle $rm{...}

  if ($rmspecial)
    {
    $s =~ s/\$rm\{([^\}]*?)\}/<\/tt>$1<tt>/g;  # turn $rm{xxx} into </tt>xxx<tt>
    }
  else
    {
    $s =~ s/\$rm\{([^\}]*?)\}/$1/g;            # turn $rm{xxx} into xxx
    }


# There is one case where the terminating } of an escape sequence is
# in another paragraph - this follows $sm{ - it can be fixed by
# removing any stray } in a paragraph that contains no { chars.

$s =~ s/\}//g if !/\{/;

# Remove any null flags ($$)

$s =~ s/\$\$//g;

# If the paragraph starts with $c\b, remove it.

$s =~ s/^\$c\b//;

# If the paragraph starts with $e\b, indent it slightly.

$s =~ s/^\$e\b/&nbsp;&nbsp;/;

# Handle .em, and .nem directives that occur within the paragraph

$s =~ s/\.em\s*\n/&setinem(1)/eg;
$s =~ s/\.nem\s*\n/&setinem(0)/eg;

# Explicitly included HTML

$s =~ s/\[\(([^)]+)\)\]/<$1>/g; # turn [(...)] into <...>

# Finally, do the substitutions and return the modified text.

$s =~ s/~~(\w+)/$var_value{$1}/eg;

return $s;
}



  ##################################################
  #            Start/end a paragraph               #
  ##################################################


# We want to leave paragraphs unterminated until we know that a horizontal
# rule does not follow, to avoid getting space inserted before the rule,
# which doesn't look good. So we have this function to help control things.
# If the argument is 1 we are starting a new paragraph; if it is 0 we want
# to force the ending of any incomplete paragraph.

  sub setpar {
  if ($inpar)
    {
    print OUT "</p>\n";
    $inpar = 0;
    }
  if ($_[0])
    {
    print OUT "<p>\n";
    $inpar = 1;
    }
  }




  ##################################################
  #            Handle a "paragraph"                #
  ##################################################


# Read a paragraph of text, which may contain many lines and may contain
# .index, .em, and .nem directives within it. We may also encounter
# ".if ~~html" within paragraphs. Process those directives,
# convert the markup, and output the rest as an HTML paragraph.


  sub handle_paragraph{
  my($par) = $_;
  my($htmlcond) = 0;
  while(<IN>)
    {
    if (/^\.if\s+~~html\b/)
      {
      $htmlcond = 1;
      $par =~ s/\s+$//;         # lose unwanted whitespace and newlines
      next;
      }
    elsif ($htmlcond && /^\.else\b/)
      {
      while (<IN>) { last if /^\.fi\b/; }
      $htmlcond = 0;
      next;
      }
    elsif ($htmlcond && /^\.fi\b/)
      {
      $htmlcond = 0;
      next;
      }


    last if /^\s*$/ || (/^\./ && !/^\.index\b/ && !/^\.em\b/ && !/^\.nem\b/);
    $par .= $_;
    }
  $par = &handle_text($par, 0);


# We can't handle .index until this point, when we do it just before
# outputting the paragraph.

  if ($par !~ /^\s*$/)
    {
    &setpar(1);
    $par =~ s/\.index\s+([^\n]+)\n/&handle_index($1, 1)/eg;
    print OUT "$par";
    }
  }




  ##################################################
  #         Handle a non-paragraph directive       #
  ##################################################


# The directives .index, .em, and .nem can also appear within paragraphs,
# and are then handled within the handle_paragraph() code.

sub handle_directive{
my($new_lastwasitem) = 0;

$lastwasrule = 0;

  if (/^\.r?set\b/ || /^\.(?:\s|$)/) {}      # ignore .(r)set and comments


  elsif (/^\.justify\b/) {}                  # and .justify


elsif (/^\.newline\b/) { print OUT "<br>\n"; }

elsif (/^\.blank\b/ || /^\.space\b/) { print OUT "<br>\n"; }

elsif (/^\.rule\b/) { &setpar(0); print OUT "<hr>\n"; $lastwasrule = 1; }

elsif (/^\.index\s+(.*)/) { &handle_index(&handle_text($1), 1); }

# Emphasis is handled by colour

  elsif (/^\.em\b/)
    {
    &setpar(0);
    print OUT "<font color=green>" if ! $inem;
    $inem = 1;
    }


  elsif (/^\.nem\b/)
    {
    &setpar(0);
    print OUT "</font>" if $inem;
    $inem = 0;
    }


# Ignore tab setting stuff - we use tables instead.

elsif (/^\.tabs(?:et)?\b/) {}

# .tempindent is used only to align some of the expansion stuff nicely;
# just ignore it. It is used in conjunction with .push/.pop.

elsif (/^\.(tempindent|push|pop)\b/) {}

# There are some instances of .if ~~sys.fancy in the source. Some of those
# that are not inside displays are two-part things, in which case we just keep
# the non-fancy part. For diagrams, however, they are in three parts:
#
# .if ~~sys.fancy
# <aspic drawing stuff for PostScript and PDF>
# .elif !~~html
# <ascii art for txt and Texinfo>
# .else
# <HTML instructions for including a gif>
# .fi
#
# In this case, we skip to the third part.

  elsif (/^\.if\s+~~sys\.fancy/ || /^\.else\b/)
    {
    while (<IN>)
      { last if /^\.else\b/ || /^\.elif\s+!\s*~~html/ || /^\.fi\b/; }


    if (/^\.elif\b/)
      {
      while (<IN>) { last if /^\.else\b/ || /^\.fi\b/; }
      }
    }


# Similarly, for .if !~~sys.fancy, take the non-fancy part.

elsif (/^\.if\s+!\s*~~sys.fancy/) {}

# There are some explicit tests for ~~html for direct HTML inclusions

elsif (/^\.if\s+~~html\b/) {}

# There are occasional requirements to do things differently for Texinfo/HTML
# and PS/txt versions. The latter are produced by SGCAL, so that's what the
# flag is called.

  elsif (/\.if\s+~~sgcal/)
    {
    while (<IN>) { last if /\.else\b/ || /\.fi\b/; }
    }


# Also there is a texinfo flag

  elsif (/^\.if\s+~~texinfo\b/)
    {
    while (<IN>)
      { last if /^\.else\b/ || /^\.elif\s+!\s*~~html/ || /^\.fi\b/; }
    }


# Ignore any other .if, .else, or .fi directives

elsif (/^\.if\b/ || /^\.fi\b/ || /^\.else\b/) {}

# Ignore .indent

elsif (/^\.indent\b/) {}

# Various flavours of numberpars map to corresponding list types.

  elsif (/^\.numberpars\b/)
    {
    $rest = $';
    &setpar(0);


    if ($rest =~ /(?:\$\.|\" \")/)
      {
      unshift @endlist, "ul";
      unshift @listtype, "";
      print OUT "<ul>\n<li>";
      }
    else
      {
      $nptype = ($rest =~ /roman/)? "a" : "1";
      unshift @endlist, "ol";
      unshift @listtype, " TYPE=\"$nptype\"";
      print OUT "<ol>\n<li$listtype[0]>";
      }
    }


  elsif (/^\.nextp\b/)
    {
    &setpar(0);
    print OUT "</li>\n<li$listtype[0]>";
    }


  elsif (/^\.endp\b/)
    {
    &setpar(0);
    print OUT "</li>\n</$endlist[0]>\n";
    shift @listtype;
    shift @endlist;
    }


# .display asis can use <pre> which uses a typewriter font.
# Otherwise, we have to do our own line breaking. Turn tabbed lines
# into an HTML table. There will always be a .tabs line first.

  elsif (/^\.display\b/)
    {
    my($intable) = 0;
    my($asis) = /asis/;
    my($rm) = /rm/;
    my($eol,$indent);


    # For non asis displays, start a paragraph, and set up to put an
    # explicit break after every line.


    if (!$asis)
      {
      &setpar(1);
      $eol = "<br>";
      $indent = "<tt>&nbsp;&nbsp;</tt>";
      }


    # For asis displays, use <pre> and no explicit breaks


    else
      {
      print OUT "<pre>\n";
      $eol = "";
      $indent = "&nbsp;&nbsp;";
      }


    # Now read through until we hit .endd (or EOF, but that shouldn't happen)
    # and process the lines in the display.


    while (<IN>)
      {
      last if /^\.endd\b/;


      # The presence of .tabs[et] starts a table


      if (/^\.tabs/)
        {
        $intable = 1;
        print OUT "<table cellspacing=0 cellpadding=0>\n";
        }


      # Some displays have an indent setting - ignore


      elsif (/^\.indent\b/) {}


      # Some displays have .blank inside them


      elsif (/^\.blank\b/)
        {
        print OUT "<br>\n";
        }


      # Some displays have emphasis inside them


      elsif (/^\.em\b/)
        {
        print OUT "<font color=green>" if ! $inem;
        $inem = 1;
        }    


      elsif (/^\.nem\b/)
        {
        print OUT "</font>" if $inem;
        $inem = 0;
        }    


      # There are occasional instances of .if [!]~~sys.fancy inside displays.
      # In both cases we want the non-fancy alternative. (The only thing that
      # matters in practice is noticing .tabs[et] actually.) Assume the syntax
      # is valid.


      elsif (/^\.if\s+~~sys.fancy/ || /^\.else\b/)
        {
        while (<IN>)
          {
          last if /^\.fi\b/ || /^\.else/;
          }
        }


      elsif (/^\.if\s+!\s*~~sys.fancy/) {}


      elsif (/^\.fi\b/) {}


      # Ignore .newline and .linelength


      elsif (/^\.newline\b/ || /^\.linelength\b/) {}


      # Ignore comments


      elsif (/^\.(\s|$)/) {}  


      # There shouldn't be any other directives inside displays


      elsif (/^\./)
        {
        print "*** Ignored directive inside .display: $_";
        }


      # Handle a data line within a display. If it's an asis display, the only
      # conversion is to escape the HTML characters. Otherwise, process the
      # SGCAL markup.


      else
        {
        chomp;
        if ($asis)
          {
          s/&/&#038;/g;
          s/</&#060;/g;
          s/>/&#062;/g;
          }
        else
          {
          $_ = &handle_text($_, !$rm);
          $_ = "<tt>$_</tt>" if !$rm && $_ ne "";
          }


        # In a table, break fields at $t. For non-rm we must break the
        # <tt> group as well.


        if ($intable)
          {
          if ($rm)
            {
            s/\s*\$t\s*/&nbsp;&nbsp;<\/td><td>/g;
            }
          else
            {
            s/\s*\$t\s*/&nbsp;&nbsp;<\/tt><\/td><td><tt>/g;
            }
          s/<tt><\/tt>//g;
          print OUT "<tr><td>&nbsp;&nbsp;$_</td></tr>\n";
          }


        # Otherwise, output straight, with <br> for non asis displays


        else
          {
          s/<tt><\/tt>//g;
          print OUT "$indent$_$eol\n";
          }
        }
      }    # Loop for display contents


    # Finish off the table and the <pre> - leave a paragraph open


    print OUT "</table>\n" if $intable;
    print OUT "</pre>\n" if $asis;
    }


# Handle configuration option definitions

elsif (/^\.startconf\b/) {}

  elsif (/^\.conf\b/)
    {
    my($option, $type, $default) =
      /^\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;


    $option =~ s/\@_/_/g;       # Underscore will be quoted in option name


    # If $type ends with $**$, add ",expanded" as there doesn't seem to be
    # a dagger character generally available.


    $type =~ s/^"([^"]+)"/$1/;
    $type =~ s/\$\*\*\$/, expanded/;


    # Default may be quoted, and it may also have quotes that are required,
    # if it is a string.


    $default =~ s/^"(.*)"$/$1/;
    $default =~ s/""/"/g;
    $default = &handle_text($default, 0);


    print OUT "<hr>";
    &setpar(0);
    &handle_index($option, 0);
    print OUT "<h3>$option</h3>\n" .
              "<i>Type:</i>&nbsp; $type<br><i>Default:</i>&nbsp; $default<br>\n";
    }


  elsif (/^\.endconf\b/)
    {
    print OUT "<hr><br>\n";
    }



# Handle "items" - used for expansion items and the like. We force the
# item text into bold, and put a rule between items.

elsif (/^\.startitems\b/) {}

  elsif (/^\.item\s+(.*)/)
    {
    my($arg) = $1;
    chomp($arg);
    $arg =~ s/^"(.*)"$/$1/;
    $arg = &handle_text($arg, 0);


    # If there are two .items in a row, we don't want to put in the
    # separator line or start a new paragraph.


    if ($lastwasitem)
      {
      print OUT "<br>";
      }
    else
      {
      print OUT "<hr>";
      &setpar(1);
      }
    print OUT "<b>$arg</b>\n";
    $new_lastwasitem = 1;
    }


  elsif (/^\.enditems\b/)
    {
    print OUT "<hr><br>\n";
    }



# Handle command line option items

elsif (/^\.startoptions\b/) {}

  elsif (/^\.option\s+(.*)/)
    {
    my($arg) = $1;
    $arg =~ s/^"(.*)"$/$1/;


    print OUT "<hr>";
    &setpar(0);


    # For indexing, we want to take up to the first # or < in the line,
    # before processing.


    my($name) = $arg =~ /^([^#<]+)/;
    $name = &handle_text($name, 0);
    &handle_index("-$name", 0);


    # Output as heading, after the index


    $arg = &handle_text($arg, 0);
    print OUT "<h3>-$arg</h3>\n";
    }


  elsif (/^\.endoptions\b/)
    {
    print OUT "<hr><br>\n";
    }


# Found an SGCAL directive that isn't dealt with. Oh dear.

  else
    {
    print "*** Unexpected SGCAL directive: line $. ignored:\n";
    print "$_\n";
    }


# Remember if last was a .item, and read the next line

$lastwasitem = $new_lastwasitem;
$_ = <IN>;
}



  ##################################################
  #         First Pass - collect references        #
  ##################################################


sub pass_one{
$thischapter = 0;

open (IN, $source_file) || die "Can't open $source_file (first pass)\n";
$_ = <IN>;

# At the start of the specification text, there are some textual replacement
# definitions. They set values, but not cross-references.

  while (/^\.r?set\s+(\S+)\s+"?([^"]+)\"?\s*$/)
    {
    $var_value{$1} = $2;
    $_ = <IN>;
    }


# Now skip on till we hit the start of the first chapter. It will be numbered
# 0 if we hit ".set chapter -1". There is only ever one unnumbered chapter.

  while (!/^\.chapter/)
    {
    $thischapter = -1 if /^\.set\s+chapter\s+-1/;
    $_ = <IN>;
    }


# Loop for handling chapters

  while ($_)
    {
    $thischapter++;
    $thissection = 0;


    # Scan through chapter, setting up cross-references to the chapter
    # and to the sections within it.


    while (<IN>)
      {
      last if /^\.chapter/;
      chomp;


      if (/^\.section/)
        {
        $thissection++;
        next;
        }


      # Handle .(r)set directives.


      if (/^\.r?set\s+(\S+)\s+"?([^"]+)\"?\s*$/ && $1 ne "runningfoot")
        {
        my($key,$value) = ($1,$2);
        $value =~ s/~~chapter/$thischapter/e;
        $value =~ s/~~section/$thissection/e;


        # Only one of $chapsplit or $sectsplit can be set.


        if ($key =~ /^CHAP/)
          {
          $value = $chapsplit?
            "<a href=\"${file_base}_$thischapter.html\">$value</a>"
            :
            "<a href=\"#CHAP$thischapter\">$value</a>";
          }


        elsif ($key =~ /^SECT/)
          {
          $value = $chapsplit?
            "<a href=\"${file_base}_$thischapter.html" .
              "#SECT$thischapter.$thissection\">$value</a>"
            :
            $sectsplit? "<a href=\"${file_base}_$thissection.html\">$value</a>"
            :
            "<a href=\"#SECT$thischapter.$thissection\">$value</a>";
          }


        $var_value{$key} = $value;
        }
      }
    }


close(IN);
}





  ##################################################
  #         Second Pass - generate HTML            #
  ##################################################


sub pass_two{
my($tocn) = 0;
my($inmacro) = 0;
my($insection) = 0;

$inem = 0;
$thischapter = 0;
$thissection = 0;

# Open the source file and get the first line

open (IN, $source_file) || die "Can't open $source_file (2nd pass)\n";
$_ = <IN>;

# Skip on till we hit the start of the first chapter, but note if we
# pass ".set chapter -1", which is used to indicate no chapter numbering for
# the first chapter (we number is 0). Keep track of whether we are in macro
# definitions or not, and when not, notice occurrences of .index, because this
# are the "x see y" type entries.

  while (!/^\.chapter/)
    {
    $thischapter = -1 if /^\.set\s+chapter\s+-1/;
    $inmacro = 1 if /^\.macro/;
    $inmacro = 0 if /^\.endm/;
    if (!$inmacro && /^\.index\s+(.*)/)
      {
      my($key);
      my($s) = $1;
      $s = &handle_text($s, 0);
      $s =~ s/ /&nbsp;/g;            # All spaces unsplittable
      $key = "\L$s";
      $key =~ s/<[^>]+>//g;
      $key =~ s/&#(\d+);/chr($1)/eg;
      $cindex{$key} = $s;
      }
    $_ = <IN>;
    }


# Open the TOC file

  open (TOC, ">$html/${file_base}_toc.html") ||
    die "Can't open $html/${file_base}_toc.html\n";


  print TOC "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
  print TOC "<html>\n<head>\n<title>$doctitle Contents</title>\n</head>\n" .
    "<body bgcolor=\"#F8F8F8\" text=\"#00005A\" " .
    "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";
  print TOC "<h1>$doctitle</h1><hr>\n<ul>\n";


# Open the data file if we are not splitting at chapters

&openout("$html/${file_base}.html") if !$chapsplit;

# Loop for handling chapters. At the start of this loop, $_ is either EOF,
# or contains a .chapter line.

$firstchapter = $thischapter + 1;

  while ($_)
    {
    print TOC "</ul>\n" if $insection;
    $insection = 0;


    $thischapter++;
    $thissection = 0;
    $lastwasrule = 0;


    # Start a new file if required


    if ($chapsplit)
      {
      &closeout("CHAP") if $thischapter != $firstchapter;
      &openout("$html/${file_base}_$thischapter.html");
      }


    # Set up the chapter title. Save it for the TOC. Set up the anchor and
    # link back to the TOC and show the title.


    $_ =~ /^\.chapter\s+(.*)/;


    my($title) = (($thischapter > 0)? "$thischapter. " : "") . &handle_text($1, 0);


    $tocn++;
    print TOC "<li><a " .
      "name=\"TOC$tocn\" " .
      "href=\"$current_file#CHAP$thischapter\">$title</a></li>\n";


    print OUT "<h1>\n";
    print OUT "<a name=\"CHAP$thischapter\" href=\"${file_base}_toc.html#TOC$tocn\">\n";
    print OUT "$title\n</a></h1>\n";


    # Scan the contents of the chapter


    $_ = <IN>;
    while ($_)
      {
      last if /^\.chapter/;


      # Handle the start of a new section, starting a new file if required


      if (/^\.section\s+(.*)/)
        {
        $thissection++;


        print TOC "<ul>\n" if !$insection;
        $insection = 1;


        my($title) = (($thischapter > 0)? "$thischapter." : "") .
          "$thissection. " . &handle_text($1, 0);


        if ($sectsplit)
          {
          &closeout("SECT");
          &openout("$html/${file_base}_$thissection.html");
          }


        $tocn++;
        printf TOC ("<li><a " .
          "name=\"TOC$tocn\" " .
          "href=\"$current_file#SECT%s$thissection\">%s</a></li>\n",
            ($thischapter > 0)? "$thischapter." : "", $title);


        &setpar(0);
        print OUT "<h2>\n";
        printf OUT ("<a name=\"SECT%s$thissection\" ",
          ($thischapter > 0)? "$thischapter." : "");
        print OUT "href=\"${file_base}_toc.html#TOC$tocn\">\n";
        print OUT "$title\n</a></h2>\n";
        $_ = <IN>;
        $lastwasrule = 0;
        }


      # Blank lines at this level are ignored


      elsif (/^\s*$/)
        {
        $_ = <IN>;
        }


      # Directive and non-directive lines are handled independently, though
      # in each case further lines may be read. Afterwards, the next line is
      # in $_. If .em is at the start of a paragraph, treat it with the
      # paragraph, because the matching .nem will be too. Messy!


      elsif (/^\./)
        {
        if (/^\.em\b/)
          {
          $_=<IN>;
          if (/^\./)
            {
            print OUT "<font color=green>" if ! $inem;
            $inem = 1;
            # Used to handle it here - but that fails if it is .section.
            # Just let the next iteration of the loop handle it.  
            # &handle_directive();
            }


          else
            {
            $_ = ".em\n" . $_;
            &handle_paragraph();
            $lastwasrule = 0;
            $lastwasitem = 0;
            }
          }


        # Not .em


        else
          {
          &handle_directive();
          }
        }


      # Not a directive


      else
        {
        &handle_paragraph();
        $lastwasrule = 0;
        $lastwasitem = 0;
        }


      } # Loop for each line in a chapter
    }   # Loop for each chapter


# Close the last file, end off the TOC, and we are done.

&closeout("");

print TOC "</ul>\n" if $insection;

  if (defined %cindex)
    {
    $cindex_tocn = ++$tocn;
    print TOC "<li><a name=\"TOC$tocn\" ".
      "href=\"${file_base}_cindex.html\">Concept Index</a></li>\n";
    }


  if (defined %oindex)
    {
    $oindex_tocn = ++$tocn;
    print TOC "<li><a name=\"TOC$tocn\" ".
      "href=\"${file_base}_oindex.html\">Option Index</a></li>\n";
    }


print TOC "</ul>\n</body>\n</html>\n";
close(TOC);
close(IN);
}




  ##################################################
  #           Adjust index points                  #
  ##################################################


# Because of the way the source is written, there are often index entries
# that immediately follow the start of chapters and sections and the definition
# of "items" like "helo = verify". This gets the correct page numbers for the
# PostScript and PDF formats. However, for HTML we want the index anchor to be
# before the section heading, because browsers tend to put the index point at
# the top of the screen. So we re-read all the files we've just created, and
# move some of the index points about. This is necessary only if indexes exist.
# The files are small enough to be handled entirely in memory.

sub adjust_index_points {
print "Adjusting index points to precede headings\n";

$" = "";

  opendir(DIR, "$html") || die "Failed to opendir $html\n";
  while ($file = readdir(DIR))
    {
    my($i);
    next unless $file =~ /^${file_base}_\d+\.html$/;


    open(IN, "<$html/$file") ||
      die "Failed to open $html/$file (read)\n";
    my(@lines) = <IN>;
    close(IN);


    for ($i = 0; $i < @lines; $i++)
      {
      if ($lines[$i] =~ /^<a name="IX\d+"><\/a>$/)
        {
        # Handle an index line that follows a heading definition. Move it back
        # to just before the <h1> or whatever. This preserves the order of
        # multiple index lines, not that that matters.


        if ($lines[$i-1] =~ /^<\/a><\/h(\d)>/)
          {
          my($j);
          my($found) = 0;
          for ($j = $i-2; $j > 0 && $j > $i - 10; $j--)
            {
            if ($lines[$j] =~ /<h$1>/)
              {
              $found = 1;
              last;
              }
            }
          if ($found)
            {
            splice(@lines, $j, 0, splice(@lines, $i, 1));
            }
          }


        # Handle an index line that follows an "item". Move it back one line.


        elsif ($lines[$i-1] =~ /^<b>.*<\/b>\s*$/)
          {
          splice(@lines, $i-1, 0, splice(@lines, $i, 1));
          }


        # Handle an index line that follows a "conf" definition


        elsif ($lines[$i-1] =~ /^<i>Type:<\/i>/ && $lines[$i-2] =~ /^<h3>/)
          {
          splice(@lines, $i-2, 0, splice(@lines, $i, 1));
          }


        # Handle an index line that follows an "option" definition


        elsif ($lines[$i-1] =~ /^<h3>/)
          {
          splice(@lines, $i-1, 0, splice(@lines, $i, 1));
          }
        }
      }


    open(OUT, ">$html/$file") ||
      die "Failed to open $html/$file (write)\n";


    print OUT "@lines";
    close OUT;
    undef @lines;
    }
  }





  ##################################################
  #               Create Index                     #
  ##################################################


sub create_index{
my($hash) = $_[0];
my($ifname) = $_[1];
my($ititle) = $_[2];
my(%indexindex);

  open(INDEX, ">$html/${file_base}_$_[1].html") ||
    die "Failed to open $html/${file_base}_$ifname\n";


print INDEX "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML//EN\">\n";
print INDEX "<html>\n<head>\n<title>$doctitle $ititle</title>\n";
print INDEX "<base target=\"body\">\n</head>\n";

  print INDEX "<body bgcolor=\"#FFFFDF\" text=\"#00005A\" " .
    "link=\"#FF6600\" alink=\"#FF9933\" vlink=\"#990000\">\n";


print INDEX "<h3>$ititle</h3>\n";

# We have to scan the keys in the hash twice; first to build the list
# of initial letters, and then to do the business. The first time we
# do not need to sort them.

  foreach $key (keys %$hash)
    {
    my($initial) = substr($key,0,1);
    $initial = "\U$initial";
    $indexindex{$initial} = 1 if $initial ge "A";
    }


  print INDEX "<p>\n";
  foreach $key (sort keys %indexindex)
    {
    print INDEX "&nbsp;<a href=\"#$key\" target=\"index\">$key</a>\n";
    }
  print INDEX "<hr></p>\n";


my($letter) = "";
print INDEX "<p>\n";

  foreach $key (sort
        { ("\L$a" eq "\L$b")? ("$a" cmp "$b") : ("\L$a" cmp "\L$b") }
      keys %$hash)
    {
    my($initial) = substr($key,0,1);
    $initial = "\U$initial";
    if ($initial ne $letter)
      {
      if ($initial ge "A")
        {
        print INDEX "<br>\n" if $letter ne "";
        print INDEX "<a name=\"$initial\"></a>\n";
        print INDEX "<font size=\"+1\">\U$initial\E</font><br>\n";
        }
      $letter = $initial;
      }
    print INDEX "$$hash{$key}<br>\n";
    }


print INDEX "</p>\n";

print INDEX "</body>\n</html>\n";
close(INDEX);
}




  ##################################################
  #           Show usage and die                   #
  ##################################################


sub usage {
die "Usage: g2h [-split no|section|chapter] <source> <title>\n";
}



  ##################################################
  #           Entry point and main program         #
  ##################################################



# Directory in which to put the new HTML files

$html = "html";

# Global variables.

%cindex = ();
%oindex = ();

$chapsplit = 0;
$cindex_tocn = 0;
$file_base = "";
$index_count = 0;
$inem = 0;
$inpar = 0;
$lastwasitem = 0;
$lastwasrule = 0;
$oindex_tocn = 0;
$sectsplit = 0;
$source_file = "";
$thischapter = 0;
$thissection = 0;


# Handle options

my($splitset) = 0;

  while (scalar @ARGV > 0 && $ARGV[0] =~ /^-/)
    {
    if ($ARGV[0] eq "-split" && !$splitset)
      {
      $splitset = 1;
      shift @ARGV;
      my($type) = shift @ARGV;
      if    ($type eq "section") { $sectsplit = 1; }
      elsif ($type eq "chapter") { $chapsplit = 1; }
      elsif ($type eq "no"     ) { $sectsplit = $chapsplit = 0; }
      else                       { &usage(); }
      }
    else { &usage(); }
    }


# Get the source file and its base

&usage() if scalar @ARGV <= 0;
$source_file = shift @ARGV;
($file_base) = $source_file =~ /^(.*)\.src$/;

&usage() if scalar @ARGV <= 0;
$doctitle = shift @ARGV;

print "\nCreate HTML for $doctitle from $source_file\n";

# Remove the old HTML files

print "Removing old HTML files\n";
system("/bin/rm -rf $html/${file_base}_*.html");

# First pass identifies all the chapters and sections, and collects the
# values of the cross-referencing variables.

print "Scanning for cross-references\n";
&pass_one();

  $maxchapter = $thischapter;      # Used if chapter splitting
  $maxsection = $thissection;      # Used if section splitting


# Second pass actually creates the HTML files.

print "Creating the HTML files\n";
&pass_two();

# Reprocess for moving some of the index points, if indexes were created

&adjust_index_points() if scalar(keys %cindex) > 0 || scalar(keys %oindex) > 0;

# Finally, we must create the option and concept indexes if any data
# has been collected for them.

  if (scalar(keys %cindex) > 0)
    {
    print "Creating concept index\n";
    &create_index(\%cindex, "cindex", "Concepts");
    }


  if (scalar(keys %oindex) > 0)
    {
    print "Creating option index\n";
    &create_index(\%oindex, "oindex", "Options");
    }


# End of g2h

Index: g2man
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/g2man,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# Script to find the command line options in the Exim spec, and turn them
# into a man page, because people like that.


  ##################################################
  #            De-markup one line                  #
  ##################################################


sub process {
my($x) = $_[0];

# Hide SGCAL escapes

  $x =~ s/\@\@/&&a/g;         # @@
  $x =~ s/\@\\/&&b/g;         # @\
  $x =~ s/\@</&&l/g;          # @<
  $x =~ s/\@>/&&g/g;          # @>
  $x =~ s/\@\{/&&c/g;         # @{
  $x =~ s/\@\}/&&d/g;         # @}
  $x =~ s/\@#/&&s/g;          # @#
  $x =~ s/\@(.)/$1/g;         # all other @s


# Convert SGCAL markup

  $x =~ s/#/ /g;                            # turn #   into a space
  $x =~ s/\$~//g;                           # turn $~  into nothing
  $x =~ s/__/_/g;                           # turn __  into _
  $x =~ s/\$sc\{([^\}]*?)\}/$1/g;           # turn $sc{xxx}   into xxx
  $x =~ s/\$st\{([^\}]*?)\}/$1/g;           # turn $st{xxx}   into xxx
  $x =~ s/\$si\{([^\}]*?)\}/$1/g;           # turn $si{xxx}   into xxx
  $x =~ s/\$tt\{([^\}]*?)\}/$1/g;           # turn $tt{xxx}   into xxx
  $x =~ s/\$it\{([^\}]*?)\}/$1/g;           # turn $it{xxx}   into xxx
  $x =~ s/\$bf\{([^\}]*?)\}/$1/g;           # turn $bf{xxx}   into xxx
  $x =~ s/\$rm\{([^\}]*?)\}/$1/g;           # turn $rm{xxx}   into xxx
  $x =~ s/\$cb\{([^\}]*?)\}/$1/g;           # turn $cb{xxx}   into xxx



  $x =~ s/\\\\([^\\]*?)\\\\/\U$1/g;         # turn \\xxx\\    into XXX
  $x =~ s/\\\(([^)]*?)\)\\/$1/g;            # turn \(xxx)\    into xxx
  $x =~ s/\\\"([^\"]*?)\"\\/$1/g;           # turn \"xxx"\    into xxx
  $x =~ s/\\\%([^\%]*?)\%\\/"$1"/g;         # turn \%xxx%\    into "xxx"


  $x =~   s/\\\?([^?]*?)\?\\/$1/g;          # turn \?URL?\    into URL
  $x =~   s/<<([^>]*?)>>/<$1>/g;            # turn <<xxx>>    into <xxx>
  $x =~   s/\\\$([^\$]*?)\$\\/\$$1/g;       # turn \$xxx$\    into $xxx
  $x =~   s/\\\-([^\\]*?)\-\\/\-$1/g;       # turn \-xxx-\    into -xxx
  $x =~   s/\\\*\*([^*]*?)\*\*\\/$1/g;      # turn \**xxx**\  into xxx
  $x =~   s/\[\(([\w\/]*)\)\]//g;           # remove inline HTML


  $x =~ s/\\\*([^*]*?)\*\\/$1/g;            # turn \*xxx*\    into xxx
  $x =~ s/\\([^\\]*?)\\/"$1"/g;             # turn \xxx\      into "xxx"
  $x =~ s/\$\*\$/\*/g;                      # turn $*$        into *
  $x =~ s/\$t\b//g;                         # turn $t         into nothing


  $x =~ s/::([^:]+)::/$1:/g;                # turn ::xxx::    into xxx:


# Put back escaped SGCAL specials

  $x =~ s/&&a/\@/g;             # @@ => @
  $x =~ s/&&b/\\/g;             # @\ => \          
  $x =~ s/&&l/</g;              # @< => <
  $x =~ s/&&g/>/g;              # @> => >
  $x =~ s/&&c/\@{/g;            # @{ => @{
  # $x =~ s/&&rc/{/g;             # 
  # $x =~ s/&&rd/}/g;             # 
  $x =~ s/&&d/\@}/g;            # @} => @}
  $x =~ s/&&s/#/g;              # @#


# Remove any null flags ($$)

$x =~ s/\$\$//g;

$x;
}


  ##################################################
  #             De-reference a paragraph           #
  ##################################################


# Remove sentences or parenthetical comments that contain references.

sub deref {
my($t) = $_[0];

$t =~ s/^(\n*)[^.()]+~~[^.]+\.\s*/$1/;
$t =~ s/\s?\.[^.()]+~~[^.]+\././g;
$t =~ s/\s?\([^~).]+~~[^)]+\)//g;

$t;
}


  ##################################################
  #            Quote what needs quoting            #
  ##################################################


# This is for anything that must be quoted in the final output, independent
# of whether it is in "asis" text or not.

sub mustquote {
my($t) = $_[0];
$t =~ s/(?<!\\)-/\\-/g;

$t;
}



  ##################################################
  #              Main Program                      #
  ##################################################


open(IN, "spec.src") || die "Can't open spec.src\n";
open(OUT, ">exim.8" ) || die "Can't open exim.8\n";

print OUT <<End;
.TH EXIM 8
.SH NAME
exim \\- a Mail Transfer Agent
.SH SYNOPSIS
.B exim [options] arguments ...
.br
.B mailq [options] arguments ...
.br
.B rsmtp [options] arguments ...
.br
.B rmail [options] arguments ...
.br
.B runq [options] arguments ...
.br
.B newaliases [options] arguments ...

.SH DESCRIPTION
Exim is a mail transfer agent (MTA) developed at the University of Cambridge.
It is a large program with very many facilities. For a full specification, see
the reference manual. This man page contains only a description of the command
line options. It has been automatically generated from the reference manual
source, which is why the formatting is poor in some places.

.SH SETTING OPTIONS BY PROGRAM NAME
.TP 10
\\fBmailq\\fR
Behave as if the option \\-bp were present before any other options. The \\-bp
option requests a listing of the contents of the mail queue on the standard
output.
.TP
\\fBrsmtp\\fR
Behaves as if the option \\-bS were present before any other options, for
compatibility with Smail. The \\-bS option is used for reading in a number of
messages in batched SMTP format.
.TP
\\fBrmail\\fR
Behave as if the \\-i and \\-oee options were present before any other options,
for compatibility with Smail. The name \\fBrmail\\fR is used as an interface by
some UUCP systems. The \\-i option specifies that a dot on a line by itself
does not terminate a non\\-SMTP message; \\-oee requests that errors detected in
non\\-SMTP messages be reported by emailing the sender.
.TP
\\fBrunq\\fR
Behave as if the option \\-q were present before any other options, for
compatibility with Smail. The \\-q option causes a single queue runner process
to be started. It processes the queue once, then exits.
.TP
\\fBnewaliases\\fR
Behave as if the option \\-bi were present before any other options, for
compatibility with Sendmail. This option is used for rebuilding Sendmail's
alias file. Exim does not have the concept of a single alias file, but can be
configured to run a specified command if called with the \\-bi option.


.SH OPTIONS
.TP 10
End

while (<IN>) { last if /^\.startoptions/; }
die "Can't find start of options\n" if ! defined $_;

# Find the start of the first option

while (<IN>) { last if /^\.option/; }
die "Can't find start of first option\n" if ! defined $_;

# Loop for each individual option

  while (/^\.option (.*)/)
    {
    $nlpending = 0;
    $itemtext = "";


    printf OUT ("\\fB\\-%s\\fR\n", &mustquote(&process($1)));


    # Process the data for the option


    while (<IN>)
      {
      last if /^\.(?:option|endoptions)/;
      next if /^\.index/;
      next if /^\.em\s*$/;
      next if /^\.nem\s*$/;


      if (/^\.display(?:\s+flow)?(?:\s+rm)?\s*$/)
        {
        print OUT &mustquote(&deref($itemtext));
        $itemtext = "";
        print OUT "\n";
        while (($_ = <IN>) !~ /^\.endd/)
          {
          print OUT "  ", &mustquote(&deref(&process($_))) if ! /^\./;
          }
        $nlpending = 1;
        }


      elsif (/^\.display asis\s*$/)
        {
        print OUT &mustquote(&deref($itemtext));
        $itemtext = "";
        print OUT "\n";
        while (($_ = <IN>) !~ /^\.endd/)
          {
          print OUT &mustquote("  $_");
          }
        $nlpending = 1;
        }


      elsif (/^\s*$/)
        {
        print OUT &mustquote(&deref($itemtext));
        $itemtext = "";
        $nlpending++;
        }


      else
        {
        while ($nlpending > 0)
          {
          $itemtext .= "\n";
          $nlpending--;
          }
        $itemtext .= &process($_);
        }
      }


    print OUT &mustquote(&deref($itemtext));
    print OUT ".TP\n";
    }


# End of g2man

Index: g2t
====================================================================
#! /usr/bin/perl -w
# $Cambridge: exim/exim-doc/doc-scripts/g2t,v 1.1 2004/10/07 15:04:35 ph10 Exp $

# A Perl script to turn the SGCAL source of the Exim documentation into
# Texinfo input, more or less...

# Supply the source file names as arguments.
# The output goes to the standard output.


  ##################################################
  #         Ensure unique node name                #
  ##################################################


# Node names must be unique. Occasionally in the Exim spec there are duplicate
# section names, and it's become too much of a hassle to keep them distinct
# manually. So it is now automated.

########### Never really got this working. Abandoned ###############

  sub unique {
  my($node) = $_[0];
  if (defined $node_names{$node})
    {
    $node_names{$node} += 1; 
    $node = "$node ($node_names{$node})"; 


print STDERR "+++ $node\n";

    }
  else
    {
    $node_names{$node} = 0;
    }
  $node;
  }  




  ##################################################
  #         De-comma a node name                   #
  ##################################################


# Commas, colons, and apostrophes are not permitted in Texinfo
# node names. I find this incredible, but it is clearly documented.
# The Exim manual has been re-organized not to have colons or
# apostrophes in any chapter or section titles, but I can't manage
# without commas. This function turns "," into " and", which is
# the best that can be done; we can use some clever Perlery to
# just take out commas before "and".

# Sigh. The Sendmail option -p<rval>:<sval> now means that there's a colon
# in the node name for that option. Turn the colon into <colon>. This is also
# done for menus.

# Another thing that causes problems in node names in some versions of
# Texinfo is the use of @sc{xxx} for small caps. Just turn these all into
# real caps. This is also done for menus.

sub decomma {
$_[0] =~ s/,(?!\sand)/ and/g;
$_[0] =~ s/,//g;
$_[0] =~ s/\@sc\{([^}]*)\}/\U$1/g;
$_[0] =~ s/:/<colon>/g;
$_[0];
}



  ##################################################
  #           De-quote a string                    #
  ##################################################


# @x is turned into x, except when x=@, or when asis is set,
# in which case single @ must turn into @@. A single substitute
# doesn't work in the non-asis case, because of the problems of
# handling things like @@@$, so we do it the hard way.

  sub dequote {
  if ($asis) { $_[0] =~ s/@/@@/g; } else
    {
    $_[0] =~ s/@@/&at&/g;
    $_[0] =~ s/@([^@])/$1/g;
    $_[0] =~ s/&at&/@@/g;
    }
  $_[0];
  }



  ##################################################
  #           Get next line                        #
  ##################################################


# Called from handle_directive, to get the next source line
# into $_.

  sub get_next_line {
  if ($processing_subsection)
    { return $_ = shift @SUBBUFFER; }
  else
    { return $_ = <>; }
  }




  ##################################################
  #           Handle text lines                    #
  ##################################################


# This function is handed whole paragraphs, and we assume that
# SGCAL font changing markup is always complete within a paragraph.
# We have to replace escaped versions of significant characters with
# some magic before performing general transformations, and then
# put them back afterwards. The character & is not common in the text,
# and && is unknown, so we use that.

sub handle_text {
$_ = $_[0];

  if ($asis)
    {
    $_ = dequote($_);
    s/(\{|\})/\@$1/g;
    return $_;
    }


  while (/~~/)
    {
    $left = $`;
    ($name) = $' =~ /^(\w+)/;
    $right = $';


    $value = $references{$name};
    $value = "" if !defined($value);


    if ($value =~ /\*\*\*\*/)
      {
      $value = ($` eq $current_chapter)? "\"$'\"" :
        "\"$'\" in chapter \"$`\"";
      $value = "" if $value eq "\"\"";
      }
    elsif ($value !~ /^[0-9]+\.[0-9]+$/)   # quote unless version number
      {                                                
      $value = "\"$value\"";                          
      }


    $_ = "${left}${value}${right}";
    }


  s/\@\@/&&a/g;         # @@
  s/\@\\/&&b/g;         # @\
  s/\@</&&l/g;          # @<
  s/\@>/&&g/g;          # @>
  s/\@\{/&&c/g;         # @{
  s/\@\}/&&d/g;         # @}
  s/\@#/&&s/g;          # @#


# Now remove all other @'s

$_ = dequote($_);

# Convert SGCAL markup

  s/#/ /g;                            # turn #   into a space
  s/\$~//g;                           # turn $~  into nothing
  s/__/_/g;                           # turn __  into _
  s/\$sm\{//g;                        # turn $sm{     into nothing
  s/\$sc\{([^\}]*?)\}/$1/g;           # turn $sc{xxx} into xxx
  s/\$st\{([^\}]*?)\}/$1/g;           # turn $st{xxx} into xxx
  s/\$si\{([^\}]*?)\}/$1/g;           # turn $si{xxx} into xxx
  s/\$tt\{([^\}]*?)\}/$1/g;           # turn $tt{xxx} into xxx


  s/\$it\{([^\}]*?)\}/$1/g;           # turn $it{xxx} into xxx


  s/\$bf\{([^\}]*?)\}/$1/g;           # turn $bf{xxx} into xxx
  s/\$rm\{([^\}]*?)\}/$1/g;           # turn $rm{xxx} into xxx
  s/\$cb\{([^\}]*?)\}/$1/g;           # turn $cb{xxx} into xxx


# This is a fudge for some specific usages of $<; can't just do a global
# is it occurs in things like $<variable name> as well.

  s/\[\$<\]/[]/g;                     # turn [$<]     into []
  s/&&b\$<\./&&b./g;                  # turn \$<.     into \.  (\ == &&b by now)
  s/(\d)\$<-/$1-/g;                   # turn 0$<-     into 0-


# There is one case where the terminating } of an escape sequence is
# in another paragraph - this follows $sm{ - it can be fixed by
# removing any stray } in a paragraph that contains no { chars.

s/\}//g if !/\{/;

# Any remaining {} must be escaped to prevent Texinfo from complaining

s/(\{|\})/\@$1/g;

# Now to conversions that put {} into the file.
# Change <<..>> from @var to just <...> as the caps that Texinfo
# uses look far too shouty.

s/\\\\([^\\]*?)\\\\/\@sc\{\L$1\}/g; # turn \\xxx\\ into @sc{xxx}
s/\\\(([^)]*?)\)\\/\@file\{$1\}/g; # turn \(xxx)\ into @file{xxx}
s/\\\"([^\"]*?)\"\\/\@file\{$1\}/g; # turn \"xxx"\ into @file{xxx}

  s/\\\?([^?]*?)\?\\/$1/g;            # turn \?URL?\    into URL
  s/<<([^>]*?)>>/<$1>/g;              # turn <<xxx>>    into <xxx>
  s/\\\$([^\$]*?)\$\\/\$$1/g;         # turn \$xxx$\    into $xxx
  s/\\\-([^-]*?)\-\\/\-$1/g;          # turn \-xxx-\    into -xxx
  s/\\\*\*([^*]*?)\*\*\\/$1/g;        # turn \**xxx**\  into xxx
  s/\[\(([\w\/]*)\)\]//g;             # remove inline HTML


  s/\\\*([^*]*?)\*\\/\@dfn\{$1\}/g;     # turn \*xxx*\    into @dfn{xxx}
  s/\\%([^*]*?)%\\/\@dfn\{$1\}/g;       # turn \%xxx%\    into @dfn{xxx}
  s/:::([^:]*?)::/\@dfn\{:$1:\}/g;      # turn :::xxx::   into @dfn{:xxx:}
  s/::([^:]*?)::/\@dfn\{$1:\}/g;        # turn ::xxx::    into @dfn{xxx:}
  s/\\([^\\]*?)\\/\@dfn\{$1\}/g;        # turn \xxx\      into @dfn{xxx}
  s/\$\*\$/\*/g;                        # turn $*$        into *


# Put back escaped SGCAL specials

s/&&a/\@\@/g;
s/&&b/\\/g;
s/&&l/</g;
s/&&g/>/g;
s/&&c/\@{/g;
s/&&rc/{/g;
s/&&rd/}/g;
s/&&d/\@}/g;
s/&&s/#/g;

# Remove any null flags ($$)

s/\$\$//g;

# If the paragraph starts with $c\b, change this into @center. Assume
# we don't ever get two of these in a row.

s/^\$c\b/\@center /;

# If the paragraph starts with $e\b, stuff some tabs in there, as
# Texinfo can't do this on its own (as far as I can see). They must
# tabs; Texinfo treats them as different to spaces. Sigh.

s/^\$e\b/\t\t\t\t\t\t\t/;

# Handle $t. The Exim spec only ever has one tab per line. Er, not
# quite true, but a good enough assumption. $t is always followed
# by a non-word character.

# The .tabs directive has stashed the value in the $tab variable.
# Don't count Texinfo font chars.

  while (/(^|.+?\n)(.+?)\$t(\W.*\n)/)
    {
    $before = $` . $1;
    $after = $';
    $left = $2;
    $right = $3;


    $left =~ s/\s$//;
    $right =~ s/^\s+//;


    $plainleft = $left;
    $plainleft =~ s/\@[a-z]+\{([^}]+?)\}/$1/g;
    $plainleft =~ s/\@//g;


    $_ = $before . $left . (" " x ($tab - length($plainleft))) . $right . $after;


    # Fudge for the one case where there are two tabs


    if ($tab2 != 0)
      {
      $temp = $tab;
      $tab = $tab2;
      $tab2 = $temp;
      }
    }


# Return the new line (paragraph)

$_;
}



  ##################################################
  #           Handle directive lines               #
  ##################################################


# Use get_next_line() instead of <> because this is called to process
# stacked up subsection lines

sub handle_directive {

my($new_lastwasitem) = 0;

# Chapter directives just require . => @; however, dequoting the
# line thereafter will remove the first @, so just force it back
# afterwards. If the chapter is is one describing a driver, set
# the driver name.

  if (/\.chapter/)
    {
    tr/./@/;
    push(@ONESECTION, "@" . &dequote("$_\n"));
    $driver_name = (/The\s+(\S+)\s+(director|router|transport|authenticator)/)? $1 :
      (/Generic options common to both directors and routers/)?
        "director or router" :
      (/[Gg]eneric\s+options for (\S+)s/)? $1 : "";
    $driver_name = &dequote($driver_name);
    }


# Section directives just require . => @; however, dequoting the
# line thereafter will remove the first @, so just force it back
# afterwards. Remove any colons in section titles as they cause
# Texinfo trouble. Also remove any \\ (small caps) markup, which
# appears in a couple of cases.

  elsif (/\.section/)
    {
    tr/./@/;
    s/://;
    s"\\\\""g;
    push(@ONESECTION, "@" . &dequote("$_\n"));


    # Horrible magic fudge to cope with the fact that exim_lock has
    # -v and -q options, just like the main program.


    $driver_name = "exim_lock" if /Mailbox maintenance/;


    # Similar magic for exiqgrep, which also duplicates options


    $driver_name = "exiqgrep" if /Selective queue listing/;  
    }


# .newline must put @* on the end of the previous line, if any, except
# inside a display, where it causes trouble.

  elsif (/\.newline/)
    {
    if (@ONESECTION > 0 && ! $indisplay)
      {
      $_ = pop(@ONESECTION);
      s/(\n*)$/\@*$1/;
      push(@ONESECTION, $_);
      }
    }


# .blank turns into @sp, adding 1 if no argument

  elsif (/\.blank/)
    {
    s/\.blank\s+(\d+)/\@sp $1/;
    s/\.blank/\@sp 1/;
    push(@ONESECTION, $_);
    }


# .rule turns into a line of hyphens

  elsif (/\.rule/)
    {
    push(@ONESECTION, ("-" x ($in_itemize? 68 : 73)) . "\@*\n");
    }


# There's one explicit .tabset setting for two tab stops

  elsif (/\.tabset\s*/)
    {
    $rest = $';
    ($first,$second) = $rest =~ /(\d+)em\s+(\d+)em/;
    $tab = ($first * 7)/6;
    $tab2 = $tab + ($second * 7)/6;
    }


# .tabs remembers the first (and only) tab setting

  elsif (/\.tabs\s*/)
    {
    $tab = ($' * 7)/6;
    $tab2 = 0;
    }


# .tempindent is used only to align some of the expansion stuff nicely;
# just ignore it. It is used in conjunction with .push/.pop.

  elsif (/\.(tempindent|push|pop)\s*/)
    {
    }


# There are some instances of .if ~~sys.fancy in the source. Some of these
# are two-part things, in which case we just keep the non-fancy. For diagrams,
# however, they are in three parts:
#
# .if ~~sys.fancy
# <aspic drawing stuff>
# .elif ~~nothtml
# <ascii art for txt and Texinfo>
# .else
# <HTML instructions for including a gif>
# .fi

  elsif (/\.if \~\~sys\.fancy/)
    {
    while (&get_next_line())
      { last if /\.else\b/ || /\.elif\s+\~\~nothtml/ || /\.fi\b/; }


    if (/\.elif/)
      {
      $skip_else = 1;
      }
    }


# There are occasional requirements to do things differently for
# Texinfo/HTML and the PS/txt versions, and there are also some
# HTML-specific things.

  elsif (/\.if\s+~~sgcal/ || /\.if\s+~~html/)
    {
    while (&get_next_line()) { last if /\.else\b/ || /\.fi\b/; }
    }


# We may also have Texinfo-specific bits

  elsif (/^\.if\s+~~texinfo/)
    {
    $skip_else = 1;
    }


# Ignore any other .if directives

elsif (/\.if/) {}

# Skip else part if flag set

  elsif (/\.else/ && $skip_else)
    {
    while (&get_next_line()) { last if /\.fi\b/; }
    $skip_else = 0;
    }


# Ignore other .fi and .else as any .if directives are handled specially

elsif (/\.fi/ || /\.else/) {}

# Ignore .indent

elsif (/\.indent/) {}

# Plain .index goes to @cindex - the "concept" index. Also, there
# are some calls to vindex and findex in the SGCAL source - treated
# as synonymous with .index - which are split into the equivalent
# indexes here.

  elsif (/\.(.?)index/)
    {
    $rest = $';
    $letter = ($1 eq "")? "c" : $1;
    tr/./@/;                           # .index -> @index


    $rest =~ s/\\\(//g;                # Remove markup
    $rest =~ s/\)\\//g; 
    $rest =~ s/\\%//g;
    $rest =~ s/%\\//g;
    $rest =~ s/\\\*//g;
    $rest =~ s/\*\\//g;    
    $rest =~ s/\\"//g;
    $rest =~ s/"\\//g;
    $rest =~ s/:://g;
    $rest =~ s/\\-/-/g;
    $rest =~ s/-\\//g;
    $rest =~ s/~~//g;     


    $rest =~ tr/\\//d;                 # Remove \


    $rest =~ s/\@\$/\$/g;              # @$  -> $
    $rest =~ s/\@_/_/g;                # @_  -> _
    $rest =~ s/\@\+/+/g;               # @+  -> +
    $rest =~ s/\$\*\$/\*/g;            # $*$ -> *
    $rest =~ s/\$([^\$]+)\$/\$$1/g;    # $x$ -> $x


    $rest =~ s/^\s+//;                 # Remove leading spaces
    $rest =~ s/\s+$//;                 # Remove trailing spaces
    $rest =~ s/\|\|/:/;                # || -> : 
    push(@ONESECTION, "\@${letter}index $rest\n");


    # Duplicate entries for things that were listed as "x see y"


    if (defined $indirections{$rest})
      {
      push(@ONESECTION, "\@${letter}index $indirections{$rest}\n");
      }
    }


# Various flavours of numberpars map to itemize and enumerate.
# Haven't found a way of having a blank space 'bullet' yet, so
# currently using minus.

  elsif (/\.numberpars/)
    {
    $rest = $';
    $type = "enumerate";
    $flag = "";


    if    ($rest =~ /\$\./)  { $flag = " \@bullet"; $type = "itemize" }
    elsif ($rest =~ /\" \"/) { $flag = " \@minus";  $type = "itemize"; }
    elsif ($rest =~ /roman/) { $flag = " a"; $type = "enumerate"; }


    push(@ONESECTION, "\n\@$type$flag\n\n\@item\n");
    push(@ENDLIST, $type);
    $in_itemize++;
    }


  elsif (/\.nextp/)
    {
    push(@ONESECTION, "\n\@item\n");
    }


  elsif (/\.endp/)
    {
    $endname = pop(@ENDLIST);
    push(@ONESECTION, "\@end $endname\n\n");
    $in_itemize--;
    }


# The normal .display (typewriter font) => @example, while the rm
# form goes to @display (no change of font). For Texinfo we need a
# blank line after @display.

  elsif (/\.display/)
    {
    $type = /rm/? "display" : "example";
    $asis = 1 if /asis/;
    $indisplay = 1;
    push(@ONESECTION, "\@$type\n\n");
    push(@ENDLIST, $type);
    }


  elsif (/\.endd/)
    {
    $asis = 0;
    $indisplay = 0;
    $endname = pop(@ENDLIST);
    push(@ONESECTION, "\@end $endname\n\n");
    }


  elsif (/\.conf/)
    {
    ($option, $type, $default) =
      /\.conf\s+(\S+)\s+("(?:[^"]|"")+"|\S+)\s+("(?:[^"]|"")+"|.*)/;


    $option = &dequote($option);


    # If $type ends with $**$ (turned into a dagger for PS version),
    # replace with ", expanded". Remove any surrounding quotes.


    $type =~ s/^"([^"]+)"/$1/;
    $type =~ s/\$\*\*\$/, expanded/;


    # Default may be quoted, and it may also have quotes that are required,
    # if it is a string.


    $default =~ s/^"(.*)"$/$1/;
    $default =~ s/""/"/g;
    $default = &handle_text($default);


    push(@ONESECTION, "\nType: $type\@*\nDefault: $default\n\n");
    }


# Handle .startitems, .enditems, and .item

elsif (/\.startitem/ || /\.enditem/) {}

  elsif (/\.item/)
    {
    $arg = $';
    $arg =~ s/^\s*"//;
    $arg =~ s/"\s*$//;
    $arg = &dequote($arg);
    $arg = &handle_text("\\**$arg**\\");


    # If there are two .items in a row, we don't want to put in the
    # separator line.


  #  push(@ONESECTION, "\n\@example\n");
    push(@ONESECTION, "\n");
    if (! $lastwasitem)
      {
      push(@ONESECTION, "_" x 75, "\n\n");
      }
  #  push(@ONESECTION, "$arg\n\@end example\n\n");
    push(@ONESECTION, "$arg\n\n");
    $new_lastwasitem = 1;
    }


  elsif (/\.option/)
    {
    chomp($arg = $');
    $arg =~ s/^\s*//;
    $arg = &dequote("-$arg");
    $arg = &handle_text($arg);
    }


# Texinfo has no facility for emphasis bars.

elsif (/\.em/) {}
elsif (/\.nem/) {}

# Just ignore any .(r)set directives pro tem (or maybe always!)

elsif (/\.r?set/) {}

# Ignore .space, .linelength, and .justify

elsif (/\.space/ || /\.justify/ || /\.linelength/) {}

# Found an SGCAL directive that isn't dealt with. Oh dear.
# Turn the embarrassing characters into question marks and
# output it in an emphasized way.

  else
    {
    tr/@{}/???/;
    push(@ONESECTION, "\n\>>>>>>> $_\n") if ! /^\.( |$)/;
    }


$lastwasitem = $new_lastwasitem;
}



  ##################################################
  #             Flush a section                    #
  ##################################################


# $section_name is the name of the next section.
# $current_section is the name of the one we have buffered up.
# If it is unset, we are at the first section of a chapter.
# $previous_node is the section we last flushed if it was a node.

sub flush_section {

# If there is no text in the section, omit it entirely. However, it
# will have had a pointer set up at the start of the previous section.
# Remember what to replace this with when the chapter gets flushed.

  my($skip) = 1;
  foreach $s (@ONESECTION)
    {
    if ($s !~ /^(\@cindex|\@section|\s*$)/) { $skip = 0; last }
    }


  if ($skip)
    {
    pop @section_list;
    $rewrite{$current_section} = $section_name;
    @ONESECTION = ();
    return;
    }


# There is data in the section: write it out to the chapter file

  if ($current_section)
    {
    printf ONECHAPTER ("\@node %s, %s, %s, %s\n",
      &decomma($current_section), &decomma($section_name),
      &decomma($previous_node), &decomma($current_up));
    $previous_node = $current_section;
    while(scalar(@ONESECTION))
      { print ONECHAPTER shift(@ONESECTION); }
    }
  else
    {
    while(scalar(@ONESECTION))
      { push(@TOPSECTION, shift(@ONESECTION)); }
    }
  }




  ##################################################
  #          Handle a "subsection"                 #
  ##################################################


# A "subsection" is a set of options that must have their own
# local menu. Do two passes; the first just collects the names
# for the menu. This is called for .conf and .option items.

sub handle_subsection{
my($type) = $_[0];
my($save_up) = $current_up;

$current_up = $current_section? $current_section : $current_chapter;

@sublist = ();
@SUBBUFFER = ();

  while (<>)
    {
    last if /^\.end$type/;
    push(@SUBBUFFER, $_);


    # .conf takes the first non-space string as the name, but as there are
    # duplicate confs in various parts of the spec, use the driver name to
    # de-duplicate; .option takes the entire rest of arg as the name, but
    # removes any sequence of ... because this disturbs TexInfo. Also, it
    # turns @- into -.


    if (/^\.$type\s+(\S+)(.*)/)
      {
      if ($type eq "conf")
        {
        $name = &handle_text($1);
        $name .= " ($driver_name)" if ($driver_name ne "");
        }
      else
        {
        chomp($name = &handle_text("-$1$2"));
        $name =~ s/\s*\.\.\.//g;


        $name .= " ($driver_name)" if ($driver_name ne "");


        # There seems to be a major problem in texinfo with the string "--".
        # In the text it gets turned into a single hyphen. This happens if it
        # is used as a menu item, but *not* as a node name. Exim has a command
        # line option "--". With no special action, this appears in the menu
        # as "-", but then the info software complains there is no node called
        # "-". If we triple it in the menu it gets displayed OK, but building
        # software complains about non-existent cross references etc.


        # I have gone for the horrid kludge of turning it into "-<hyhen>"
        # in the menus and nodes.


        # Exim 4 has added --help, which has the same problem.


        $name = "-<hyphen>" if ($name eq "--");
        $name = "-<hyphen>help" if ($name eq "--help");
        }
      push(@sublist, $name);
      }
    }


  push (@ONESECTION, "\n\@sp 2\n\@menu\n");
  for ($i = 0; $i < scalar(@sublist); $i++)
    {
    $mitem = $sublist[$i];
    $mitem =~ s/\@sc\{([^}]*)\}/\U$1/g;       # Get rid of small caps
    $mitem =~ s/:/<colon>/g;                  # Get rid of colons
    push (@ONESECTION, "* ${mitem}::\n");
    }
  push (@ONESECTION, "\@end menu\n\n");


  $prevsub = $current_up;
  $processing_subsection = 1;
  while ($_ = shift(@SUBBUFFER))
    {
    if (/^\.$type\s+(\S+)/)
      {
      $name = shift @sublist;
      $next = (scalar(@sublist))? $sublist[0] : "";
      push @ONESECTION, sprintf("\@node %s, %s, %s, %s\n",
        &decomma($name), &decomma($next), &decomma($prevsub),
        &decomma($current_up));


      if ($name eq "-<hyphen>")    # Fudge for Texinfo
        {
        push(@ONESECTION,
         "\@findex $name\n",
         "\@unnumberedsubsec --- option\n");
        push(@ONESECTION,
             "This option consists of two consecutive hyphens. It appears in\n",
             "the menu as \"-<hyphen>\" because otherwise Texinfo gets\n",
             "confused with its cross-referencing.\n");
        }
      elsif ($name eq "-<hyphen>help")    # Fudge for Texinfo
        {
        push(@ONESECTION,
         "\@findex $name\n",
         "\@unnumberedsubsec ---help option\n");
        push(@ONESECTION,
             "This option consists of \"help\" preceded by two consecutive\n" .
             "hyphens. It appears in the menu as \"-<hyphen>help\" because\n" .
             "otherwise Texinfo gets confused with its cross-referencing.\n");
        }
      else
        {
        push(@ONESECTION,
         "\@findex $name\n",
         "\@unnumberedsubsec $name option\n");
        }


      $prevsub = $name;
      }


    # Then handle as text or directive


    if (substr($_, 0, 1) eq ".")
      { handle_directive(); }
    else
      {
      while($nextline = shift(@SUBBUFFER))
        {
        last if $nextline =~ /^(\.|\s*$)/;
        $_ .= $nextline;
        }
      push(@ONESECTION, handle_text($_));
      $_ = $nextline;
      last if !defined($_);
      redo;
      }
    }


$processing_subsection = 0;
$section_pending = 1;
$current_up = $save_up;
}




  ##################################################
  #            Handle a single chapter             #
  ##################################################


sub handle_chapter{
chop;
($current_chapter) = /^\.chapter (.*)/;
$current_chapter = &dequote($current_chapter);

$current_chapter = $current_chapter;

my($tmp) = $current_chapter;
$tmp =~ s/\[\[\[\]\]\]/./;
print STDERR "processing chapter: $tmp\n";

# Remember the chapter name for the top-level menu

push(@chapter_list, $current_chapter);

# Open a temporary file to hold the chapter's text while collecting
# all its sections for a chapter-level menu.

$ONECHAPTER = "/tmp/ONECHAPTER.$$";
open(ONECHAPTER, ">$ONECHAPTER") || die "Can't open $ONECHAPTER for writing";

# Initialize for handling sections

@section_list = ();
%rewrite = ();
@ONESECTION = ();
@TOPSECTION = ();
undef $current_section;
undef $next_node;

$processing_subsection = 0;

$previous_node = $current_up = $current_chapter;
$section_pending = 0;

# Handle the .chapter directive as the first text of a section without
# a section title.

handle_directive();

# Loop, handling each section. Assume they are sufficiently short that
# we can buffer the text in store, in an array called ONESECTION, instead
# of thrashing yet another file.

  while (<>)
    {
    last if /^\.chapter /;


    # Handle a new section, preserving $_ (handle_text flattens it).
    # It seems we cannot get a fullstop into a Texinfo node name; use a magic
    # character string that gets turned back into a dot by the post-processing.


    if (/^\.section\s+/)
      {
      $save = $_;
      $section_name = $';
      $section_name =~ s/(\s|\n)+$//;
      $section_name =~ s/://;
      $section_name = &handle_text($section_name);
      flush_section();
      push(@section_list, $section_name);
      $current_section = $section_name;
      $next_node = $section_name if !$next_node;
      $section_pending = 0;
      $_ = $save;
      }


    # The .startconf macro introduces a set of .conf's which must have
    # their own local set of menus. Suspend processing the section while
    # we sort out the menu and copy their data. This is all done in a
    # subroutine that is shared with options.


    elsif (/^\.startconf/)
      {
      handle_subsection("conf");
      next;
      }


    elsif (/^\.startoption/)
      {
      handle_subsection("option");
      next;
      }


    # Deal with the actual data lines; if there's a section pending
    # start a new section on hitting some text. We hope this happens
    # only once per chapter...


    if (substr($_, 0, 1) eq ".")
      {
      handle_directive();
      }
    else
      {
      while($nextline = <>)
        {
        last if $nextline =~ /^(\.|\s*$)/;
        $_ .= $nextline;
        }
      if ($section_pending && !/^\s*$/)
        {
        $section_name = (defined $current_section)?
          "$current_section (continued)" :
          "$current_chapter (continued)" ;
        flush_section();
        push(@section_list, $section_name);
        $current_section = $section_name;
        $next_node = $section_name if !$next_node;
        $section_pending = 0;
        }


      push(@ONESECTION, handle_text($_));
      $_ = $nextline;
      last if !defined($_);
      redo;
      }
    }


# Flush any pending text, making its next field null.
# and fudging section_name for the final section of the previous.

$section_name = "";
flush_section();

# Set up section name as the start of the next chapter

$section_name = "Concept Index" if (!$doing_filter);

  if (defined $_ && /^\.chapter (.*)/)
    {
    $section_name = $1;
    $section_name = &dequote($section_name);
    }
  $next_node = $section_name;


# Write out the chapter to the CHAPTERS file, sticking the chapter
# menu after the text that came before the first section heading. This
# will always at least contain the chapter title.

  printf CHAPTERS ("\@node %s, %s, %s, Top\n",
    &decomma($current_chapter), &decomma($next_node),
    &decomma($previous_chapter));


# The pre-section stuff; if we hit an @end menu line, it is the menu of
# a "subsection" before the first section. In that case, we need to put
# the chapter's menu one the end of it, and then resume with the rest of
# the TOPSECTION data afterwards. We also need to thread together this
# "subsection"s nodes because they are all at the same level under the
# chapter.

  $in_menu = 0;
  while(scalar(@TOPSECTION))
    {
    $s = shift(@TOPSECTION);
    if ($s =~ /^\@end menu/)
      {
      $in_menu = 1;
      last;
      }
    print CHAPTERS $s;
    }


# Menu for sections

undef $next_actual_section;
undef $point_back;

  if (scalar(@section_list))
    {
    print CHAPTERS "\n\@sp 2\n\@menu\n" if ! $in_menu;
    $next_actual_section = $section_list[0];
    for ($i = 0; $i < scalar(@section_list); $i++)
      {
      $section_name = $section_list[$i];
      $section_name =~ s/\@sc\{([^}]*)\}/\U$1/g;
      print CHAPTERS "* ${section_name}::\n";
      }
    $in_menu = 1;
    }
  print CHAPTERS "\@end menu\n\n" if $in_menu;


# Remainder of topsection; we must arrange that the final @node in
# it (which will have a blank "next" field) actually points on to
# the next section, if any. If this happens, then the next section
# must point back to the final @node.

  while(scalar(@TOPSECTION))
    {
    $s = shift(@TOPSECTION);
    if ($next_actual_section && $s =~
           /^\@node\s+([^,]+),\s*,\s*([^,]*),\s*(.*)/)
      {
      my($t1, $t2, $t3) = ($1, $2, $3);    # So can be decomma'd
      printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
        &decomma($next_actual_section), &decomma($t2), &decomma($t3));
      $point_back = $1;
      }
    else { print CHAPTERS $s; }
    }


close(ONECHAPTER);
open(ONECHAPTER, "$ONECHAPTER") || die "Can't open $ONECHAPTER for reading";

# While copying the chapter data, check for node references to empty
# sections that got omitted and correct them, and correct the prev pointer
# in the first node if necessary.

  while ($buff = <ONECHAPTER>)
    {
    foreach $key (keys %rewrite)
      {
      $buff =~ s/$key/$rewrite{$key}/;
      }
    if ($point_back && $buff =~ /^\@node\s+([^,]+),\s*([^,]*),\s*([^,]*),\s*(.*)/)
      {
      my($t1, $t2, $t4) = ($1, $2, $4);   # so can be decomma'd
      printf CHAPTERS ("\@node %s, %s, %s, %s\n", &decomma($t1),
        &decomma($t2), &decomma($point_back), &decomma($t4));
      undef $point_back;
      }
    else { print CHAPTERS $buff; }
    }


$previous_chapter = $current_chapter;

close(ONECHAPTER);
unlink($ONECHAPTER);
}



  ##################################################
  #                Main Program                    #
  ##################################################


# This is a two-pass algorithm. The first pass goes through and gets the
# variable names for cross references. The second pass does the real work,
# but we can't just read through doing the translation in one pass. We need
# to know the list of chapters in order to build a top-level menu, and for
# each chapter we need to know the sections in order to build a section
# menu. Consequently, make use of temporary files to buffer things.

# This script is used for the filter document and the overview as well;
# flags tell it if it is doing one of them.

$doing_filter = 0;
$skip_else = 0;
$in_itemize = 0;
$lastwasitem = 0;

$chapter_number = 0;
$section_number = 0;

  if ($#ARGV >= 0 && $ARGV[0] eq "-filter")
    {
    $doing_filter = 1;
    shift @ARGV;
    }


# First pass: Just fish out variable settings. Save the arguments so that
# they can be reinstated for a second pass.

print STDERR "Scanning for references\n";
@save_argv = @ARGV;

# Pick up any .set directives right at the very start

  while (<>)
    {
    last if ! /^\.set\s+(\S+)\s+(.+)$/;
    $name = $1;
    $value = $2;
    $value =~ s/^\"?(.*?)\"?\s*$/$1/;
    $references{$name} = $value;
    }


# Now skip everything before the first .chapter except for
# .index lines that set up indirections. Save these so that
# the relevant index entries can be duplicated.

  while (<>)
    {
    if (/^\.chapter\s+(.+)$/)
      {
      $chapter_number++;
      $section_number = 0;
      $current_chapter = $1;
      $current_chapter = $current_chapter;
      $current_section = "";
      last;
      }


    if (/^\.index\s+([^\$]+)\s+\$it\{see\s+([^}]+)\}\s*$/)
      {
      $indirections{"$2"} = $1;
      }
    }


# Do the business

  while (<>)
    {
    if (/^\.chapter\s+(.+)$/)
      {
      $current_chapter = $1;
      $current_chapter = &dequote($current_chapter);
      $current_section = "";
      }
    elsif (/^\.section\s+(.+)$/)
      {
      $current_section = $1;
      $current_section = &dequote($current_section);
      $current_section =~ s/://;
      }
    elsif (/^\.r?set\s+(\S+)\s+(.+)$/ && $1 ne "runningfoot")
      {
      $name = $1;
      $value = $2;


      # Only set the first time. This handles a few special cases in part2
      # which is included in the filter text as well.


      if (!exists($references{$name}))
        {
        $value =~ s/^\"?(.*?)\"?\s*$/$1/;
        $value =~ s/~~chapter\./~~chapter****/;
        $value =~ s/~~chapter/$current_chapter/;
        $value =~ s/~~section/$current_section/;
        $references{$name} = $value;
        }
      }
    }


$final_chapter = defined($current_chapter)? $current_chapter : "";

# Reinstate ARGV with the list of files and proceed to the main pass

@ARGV = @save_argv;

# $asis is set true when processing .display asis blocks, to stop
# characters getting interpreted.

$asis = 0;

# $indisplay is set true while processing .display blocks, to stop
# .newlines being handled therein (adding @* wrecks alignment)

$indisplay = 0;

# $tab is set to the value of the tab stop - only one stop is ever used
# in the Exim source.

$tab = 0;

# Current driver name, for disambiguating nodes

$driver_name = "";

# $section_pending is set if a new section is to be started on hitting
# any data lines.

$section_pending = 0;

# Open a file to buffer up the entire set of chapters

$CHAPTERS = "/tmp/CHAPTERS.$$";
open(CHAPTERS, ">$CHAPTERS") || die "Can't open $CHAPTERS for writing";

# Skip everything before the first .chapter

while (<>) { last if /^\.chapter /; }

# Loop, handling each chapter

$current_up = "";
$previous_chapter = "Top";
$previous_node = "Top";

$chapter_number = 0;
$section_number = 0;

  while (defined ($_) && /^\.chapter /)
    {
    handle_chapter();
    }


# Output the stuff at the start of the file

print "\\input texinfo\n";

print "\@set{wmYear} 2003\n";
print "\@set{wmAuthor} Philip Hazel\n";
print "\@set{wmAuthor_email} <ph10\@\@cus.cam.ac.uk>\n";
print "\@set{COPYRIGHT1} Copyright \@copyright{} \@value{wmYear} University of Cambridge\n";

print "\@c %**start of header\n";

  if (!$doing_filter)
    {
    print "\@setfilename spec.info\n";
    print "\@settitle Exim Specification\n";
    }
  else
    {
    print "\@setfilename filter.info\n";
    print "\@settitle Exim Filter Specification\n";
    }


print "\@paragraphindent 0\n";
print "\@c %**end of header\n\n";


print "\@titlepage\n";
print "\@title The Exim Mail Transfer Agent\n";
print "\@author \@value{wmAuthor}\n";

print "\@page\n";
print "\@vskip 0pt plus 1filll\n";

print "Permission is granted to make and distribute verbatim copies of this manual provided the\n";
print "copyright notice and this permission notice are preserved on all copies.\n";

print "\@sp2\n";
print "\@value{COPYRIGHT1}\@*\n";

print "\@end titlepage\n\n";

# Output the top-level node and its introductory blurb

  print "\@node       Top,       $chapter_list[0], (dir), (dir)\n";
  print "\@top\n";


if (!$doing_filter)
{
print <<End;
The Exim Mail Transfer Agent\@*
****************************

The specification of the Exim Mail Transfer Agent is converted mechanically
into Texinfo format from its original marked-up source. Some typographic
representations are changed, chapters and sections cannot be numbered, and
Texinfo lacks the ability to mark updated parts of the specification with
change bars.

Because the chapters and sections are unnumbered, cross references are set to
their names. This makes the English a bit odd, with phrases like \`see chapter
\"Retry configuration\"\' but it seemed very cumbersome to change this to \`see
the chapter entitled \"Retry configuration\"\' each time.

Each chapter, section, and configuration option has been placed in a separate
Texinfo node. Texinfo doesn\'t allow commas, colons, or apostrophes in node
names, which is a rather nasty restriction. I have arranged not to use colons
or apostrophes in section titles, but cannot bring myself to omit them from
titles such as \"The foo, bar and baz commands\". For the corresponding node
names I have just used multiple occurrences of \"and\", though it looks very
ugly.

If a chapter or section continues after a list of configuration options that is
not in a new section, a new node is started, using the chapter\'s or section\'s
name plus \`(continued)\'. The \`Up\' operation from a section or configuration
option returns to the start of the current chapter; the \`Up\' operation at a
chapter start returns to the top of the document; the \`Up\' in a list of
configuration options within a section returns to the top of that section.

A number of drivers have options with the same name, so they have been
disambiguated by adding the name of the driver to its option names in order to
create node names. Thus, for example, the specification of the \`command\'
options of the \`lmtp\' and \`pipe\' transports are in nodes called \`command
(lmtp)\' and \`command (pipe)\', respectively.

End
}

else
{
print <<End;
Filtering with the Exim Mail Transfer Agent\@*
*******************************************

The specifications of the Exim Mail Transfer Agent\'s filtering facility is
converted mechanically into Texinfo format from its original marked-up source.
Some typographic representations are changed, chapters and sections cannot be
numbered, and Texinfo lacks the ability to mark updated parts of the
specification with change bars.

Because the chapters and sections are unnumbered, cross references are set to
their names. This makes the English a bit odd, with phrases like \`see section
\"Multiple personal mailboxes\"\' but it seemed very cumbersome to change this to
\`see the section entitled \"Multiple personal mailboxes\"\' each time.

End
}

# Output the top-level menu

print "\@menu\n";

  while (scalar(@chapter_list))
    {
    $name = &decomma(shift(@chapter_list));
    print "* ${name}::\n";
    }
  print "* Concept Index::\n" if (!$doing_filter);
  print "\@end menu\n\n";


# Copy the chapters, then delete the temporary file

close(CHAPTERS);
open(CHAPTERS, "$CHAPTERS") || die "Can't open $CHAPTERS for reading";
print $buff while($buff = <CHAPTERS>);
close(CHAPTERS);
unlink($CHAPTERS);

# Output the finishing off stuff

  if (!$doing_filter)
    {
    print "\@node Concept Index, , $final_chapter, Top\n";
    print "\@chapter Concept Index\n\@printindex cp\n";
    print "\@chapter Function Index\n\@printindex fn\n";
    }
  print "\@contents\n";
  print "\@bye\n";


# End

  Index: FAQ.src
  ====================================================================
  ## $Cambridge: exim/exim-doc/doc-src/FAQ.src,v 1.1 2004/10/07 15:04:35 ph10 Exp $
  ##
  ## This file is processed by Perl scripts to produce an ASCII and an HTML
  ## version. Lines starting with ## are omitted. The markup used with paragraphs
  ## is as follows:
  ##
  ## Markup       User for           HTML            Text
  ## ------------------------------------------------------
  ##  \...\       option          fixed-pitch     "quoted"
  ## \$...$\      variable         $italic         $plain
  ## \*...*\      titles, quotes    italic        "quoted"
  ## \(...)\      file name         italic          plain
  ## \[...]\      replaceable      <italic>        <plain>
  ## \?...?\      URL                 URL           plain
  ## \^...^\      Unix command      italic          plain
  ## \%...%\      Exim driver        bold         "quoted"
  ## \^^.^^\      C function         bold           plain
  ## ::...::      header name       italic:         plain:
  ## //...//      domain            italic          plain
  ## \/.../\      local part        italic          plain
  ## \"..."\      literal         fixed-pitch     "quoted"
  ## \\...\\      SMTP, build     small caps        caps
  ## \**...**\    warn, item        bold            plain
  ## \-...-\      cmd option       -italic         -plain
  ## \#           hard space        &nbsp;          space
  ##
  ## ``...''      quoted string  &#147;...&#148;    "..."
  ##
  ## @\ is used when a real backslash is required
  ##
  ## In addition, sequences of not blank lines that start with ==> are displayed
  ## in fixed-pitch with no further interpretation. A line containing only [[br]]
  ## is removed from the text version, but turned into <br> in the HTML version.
  ##
  ## The starts of sections and of questions and answers are automatically
  ## detected by the scripts.
  ##
  ##
  THE EXIM FAQ
  ------------


This is the FAQ for the Exim Mail Transfer Agent. Many thanks to the many
people who provided the original information. This file would be amazingly
cluttered if I tried to list them all. Suggestions for corrections,
improvements, and additions are always welcome.

This version of the FAQ applies to Exim 4.00 and later releases. It has been
extensively revised, and material that was relevant only to earlier releases
has been removed. As this caused some whole sections to disappear, I've taken
the opportunity to re-arrange the sections and renumber everything except the
configuration samples.

References of the form Cnnn, Fnnn, Lnnn, and Snnn are to the sample
configuration, filter, \^^local_scan()^^\, and ``useful script'' files. These
are hyperlinked from the HTML version of this FAQ. They can also be found in
the separately distributed directory called \(config.samples)\. The primary
location is

\?ftp://ftp.csx.cam.ac.uk/pub/software/email/exim/exim4/config.samples.tar.gz?\
\?ftp://ftp.csx.cam.ac.uk/pub/software/email/exim/exim4/config.samples.tar.bz2?\

There are brief descriptions of these files at the end of this document.

Philip Hazel
Last update: 31-March-2004


The FAQ is divided into the following sections:

    0. General Debugging
    1. Building and Installing
    2. Routing in general
    3. Routing to remote hosts
    4. Routing for local delivery
    5. Filtering
    6. Delivery
    7. Policy controls
    8. Rewriting addresses
    9. Headers
   10. Performance
   11. Majordomo
   12. Fetchmail
   13. Perl
   14. Dial-up and ISDN
   15. UUCP
   16. Modifying message bodies
   17. Encryption (TLS/SSL)
   20. Millennium
   50. Miscellaneous
   91. Mac OS X
   92. FreeBSD
   93. HP-UX
   94. BSDI
   95. IRIX
   96. Linux
   97. Sun sytems
   98. Configuration cookbook
   99. List of sample configurations




0. GENERAL DEBUGGING

Q0001: Exim is crashing. What is wrong?

  A0001: Exim should never crash. The author is always keen to know about
         crashes, so that they can be diagnosed and fixed. However, before you
         start sending me email, please check that you are running the latest
         release of Exim, in case the problem has already been fixed. The
         techniques described below can also be useful in trying to pin down
         exactly which circumstances caused the crash and what Exim was trying to
         do at the time. If the crash is reproducable (by a particular message,
         say) keep a copy of that message.



Q0002: Exim is not working. What is wrong? How can I check what it is doing?

  A0002: Exactly how is it not working? Check the more specific questions in the
         other sections of this FAQ. Some general techniques for debugging are:


         (1) Look for information in Exim's log files. These are in the \(log)\
             directory in Exim's spool directory, unless you have configured a
             different path for them. Serious operational problems are reported
             in paniclog.


         (2) If the problem involves the delivery of one or more messages, try
             forcing a delivery with the \-M-\ option and also set the \-d-\
             option, to cause Exim to output debugging information. For example:


  ==>          exim -d -M 0z6CXU-0005RR-00


             The output is written to the standard error stream. You need to have
             admin privileges to use \-M-\ and \-d-\.


         (3) If the problem involves incoming SMTP mail, try using the \-bh-\
             option to simulate an incoming connection from a specific host,
             for example:


  ==>          exim -bh 10.9.8.7


             This goes through the motions of an SMTP session, without actually
             accepting a message. Information about various policy checks is
             output. You will need to know how to pretend to be an SMTP client.


         (4) If the problem involves lack of recognition or incorrect handling
             of local addresses, try using the \-bt-\ option with debugging turned
             on, to see how Exim is handling the address. For example,


  ==>          exim -d -bt z6abc


             shows you how it would handle the local part \"z6abc"\.



  Q0003: What does the error \*Child process of address_pipe transport returned
         69 from command xxx*\ mean?


  A0003: It means that when a transport called \%address_pipe%\ was run to pass an
         email message by means of a pipe to another process running the command
         xxx, the return code from that command was 69, which indicates some kind
         of error (the success return code is 0).


         The most common meaning of exit code 69 is ``unavailable'', and this often
         means that when Exim tried to run the command \(xxx)\, it failed. One
         cause of this might be incorrect permissions on the file containing the
         command. See also Q0026.



Q0004: My virtual domain setup isn't working. How can I debug it?

  A0004: You can use an exim command with \-d-\ to get it to show you how it is
         processing addresses. You don't actually need to send a message; use the
         \-bt-\ option like this:


  ==>      exim -d -bt localpart@virtualhost


         This will show you which routers it is using. If the problem appears
         to be with the expansion of an option setting, you can use the
         \debug_print\ option on a router to get Exim to output the expanded
         string values as it goes along.



  Q0005: Why is Exim not rejecting incoming messages addressed to non-existent
         users at SMTP time?


  A0005: This is controlled by the ACL that is run for each incoming RCPT
         command. It is defined by the \acl_smtp_rcpt\ option. You can check this
         part of your configuration by using the \-bh-\ option to run a simulated
         SMTP session, during which Exim will tell you what things it is
         checking.



  Q0006: I've put an entry for \"*.my.domain"\ in a DBM lookup file, but it isn't
         getting recognized.


  A0006: You need to request ``partial matching'' by setting the search type to
         \partial-dbm\ in order for this to work.



  Q0007: I've put the entry \"*@domain.com"\ in a lookup database, but it isn't
         working. The expansion I'm using is:


  ==>      ${lookup{${lc:$sender_address}}dbm{/the/file} ...


  A0007: As no sender address will ever be //*@domain.com// this will indeed have
         no effect as it stands. You need to tell Exim that you want it to look
         for defaults after the normal lookup has failed. In this case, change the
         search type from \"dbm"\ to \"dbm*@"\. See the section on \*Default values in
         single-key lookups*\ in the chapter entitled \*File and database
         lookups*\ in the Exim manual.



  Q0008: If I run \"./exim -d -bt user@domain"\ all seems well, but when I send
         a message from my User Agent, it does not arrive at its destination.


A0008: Try sending a message directly to Exim by typing this:

  ==>      exim -v user@domain
           <some message, could be empty>
           .


         If the message gets delivered to a remote host, but never arrives at its
         final destination, then the problem is at the remote host. If, however,
         the message gets through correctly, then the problem may be between your
         User Agent and Exim. Try setting Exim's \log_selector\ option to include
         \"+arguments"\, to see with which arguments the UA is calling Exim.



  Q0009: What does \*no immediate delivery: too many messages received in one SMTP
         connection*\ mean?


  A0009: An SMTP client may send any number of messages down a single SMTP
         connection to a server. Initially, an Exim server starts up a delivery
         process as soon as a message is received. However, in order not to start
         up too many processes when lots of messages are arriving (typically
         after a period of downtime), it stops doing immediate delivery after a
         certain number of messages have arrived down the same connection. The
         threshold is set by \smtp_accept_queue_per_connection\, and the default
         value is 10. On large systems, the value should be increased. If you are
         running a dial-in host and expecting to get all your mail down a single
         SMTP connection, then you can disable the limit altogether by setting
         the value to zero.



  Q0010: Exim puts \*for \[address]\*\ in the ::Received:: headers of some, but not all,
         messages. Is this a bug?


  A0010: No. It is deliberate. Exim inserts a ``for'' phrase only if the incoming
         message has precisely one recipient. If there is more than one
         recipient, nothing is inserted. The reason for this is that not all
         recipients appear in the ::To:: or ::Cc:: headers, and it is considered a
         breach of privacy to expose such recipients to the others. A common
         case is when a message has come from a mailing list.



  Q0011: Instead of \^exim_dbmbuild^\, I'm using a homegrown program to build DBM
         (or cdb) files, but Exim doesn't seem to be able to use them.


  A0011: Exim expects there to be a binary zero value on the end of each key used
         in a DBM file if you use the \"dbm"\ lookup type, but not for the \"dbmnz"\
         lookup type or for the keys of a cdb file. Check that you haven't
         slipped up in this regard.



  Q0012: Exim is unable to route to any remote domains. It doesn't seen to be
         able to access the DNS.


  A0012: Try running \"exim -d+resolver -bt \[remote address]\"\. The \-d-\
         options turns on debugging output, and the addition of \"+resolver"\
         will make it show the resolver queries it is building and the results of
         its DNS queries. If it appears unable to contact any name servers, check
         the contents and permissions of \(/etc/resolv.conf)\.



  Q0013: What does the error message \*transport system_aliases: cannot find
         transport driver "redirect" in line 92*\ mean?


  A0013: \%redirect%\ is a router, not a transport. You have put a configuration
         for a router into the transports section of the configuration file.



  Q0014: Exim is timing out after receiving and responding to the DATA command
         from one particular host, and yet the client host also claims to be
         timing out. This seems to affect only certain messages.


A0014: This kind of problem can have many different causes.

         (1) This problem has been seen with a network that was dropping all
         packets over a certain size, which mean that the first part of the SMTP
         transaction worked, but when the body of a large message started
         flowing, the main data bits never got through the network. See also
         Q0017.


         (2) This can also happen if a host has a broken TCP stack and won't
         reassemble fragmented datagrams.


         (3) A very few ISDN lines have been seen which failed when certain data
         patterns were sent through them, and replacing the routers at both end
         of the link did not fix things. One of them was triggered by more than 4
         X's in a row in the data.



  Q0015: What does the message \*Socket bind() to port 25 for address (any)
         failed: address already in use*\ mean?


  A0015: You are trying to run an Exim daemon when there is one already running -
         or maybe some other MTA is running, or perhaps you have an SMTP line in
         \(/etc/inetd.conf)\ which is causing \(inetd)\ to listen on port 25.



  Q0016: I've set \"verify = header_syntax"\ in my ACL, but this causes Exim to
         complain about header lines like \"To: Work: Jim <jims@email>,
         Home: Bob <bobs@email>"\ which look all right to me. Is this a bug?


  A0016: No. Header lines such as ::From::, ::To::, etc., which contain addresses, are
         structured, and have to be in a specific format which is defined in RFC
         2822. Unquoted colons are not allowed in the ``phrase'' part of an email
         address (they are OK in other headers such as ::Subject::). The correct
         form for that header is


  ==>      To: "Work: Jim" <jims@email>, "Home: Bob" <bobs@email>


         You will sometimes see unquoted colons in ::To:: and ::Cc:: headers, but only
         in connection with name lists (called ``groups''), for example:


  ==>      To: My friends: X <x@???>, Y <y@???>;,
               My enemies: A <a@???>, B <b@???>;


         Each list must be terminated by a semicolon, as shown.



  Q0017: Whenever Exim tries to deliver a specific message to a particular
         server, it fails, giving the error \*Remote end closed connection after
         data*\ or \*Broken pipe*\ or a timeout. What's going on?


  A0017: \*Broken pipe*\ is the error you get on some OS when the remote host just
         drops the connection. The alternative is \*connection reset by peer*\.
         There are many potential causes. Here are some of them (see also Q0068):


         (1) There are some firewalls that fall over on binary zero characters
         in email. Have a look, e.g. with \"hexdump -c mymail | tail"\ to see if
         your mail contains any binary zero characters.


         (2) There are broken SMTP servers around that just drop the connection
         after the data has been sent if they don't like the message for some
         reason (e.g. it is too big) instead of sending a 5xx error code. Have
         you tried sending a small message to the same address?


         It has been reported that some releases of Novell servers running NIMS
         are unable to handle lines longer than 1024 characters, and just close
         the connection. This is an example of this behaviour.


         (3) If the problem occurs right at the start of the mail, then it could
         be a network problem with mishandling of large packets. Many emails are
         small and thus appear to propagate correctly, but big emails will
         generate big IP datagrams.


         There have been problems when something in the middle of the network
         mishandles large packets due to IP tunnelling. In a tunnelled link, your
         IP datagrams gets wrapped in a larger datagram and sent over a network.
         This is how virtual private networks (VPNs), and some ISP transit
         circuits work. Since the datagrams going over the tunnel require a
         larger packet size, the tunnel needs a bigger maximum transfer unit
         (MTU) in the network handling the tunnelled packets. However, MTUs
         are often fixed, so the tunnel will try to fragment the packets.


         If the systems outside the tunnel are using path MTU discovery, (most
         Sun Sparc Solaris machines do by default), and set the DF (don't
         fragment) bit because they don't send packets larger than their \(local)\
         MTU, then ICMP control messages will be sent by the routers at the
         ends of the tunnel to tell them to reduce their MTU, since the tunnel
         can't fragment the data, and has to throw it away. If this mechanism
         stops working, e.g. a firewall blocks ICMP, then your host never
         knows it has hit the maximum path MTU, but it has received no ACK on
         the packet either, so it continues to resend the same packet and the
         connection stalls, eventually timing out.


         You can test the link using pings of large packets and see what works:


  ==>     ping -s host 2048


         Try reducing the MTU on the sending host:


  ==>     ifconfig le0 mtu 1300


         Alternatively, you can reduce the size of the buffer Exim uses for SMTP
         output by putting something like


  ==>      DELIVER_OUT_BUFFER_SIZE=512


         in your \(Local/Makefile)\ and rebuilding Exim (the default is 8192).
         While this should not in principle have any effect on the size of
         packets sent, in practice it does seem to have an effect on some OS.


         You can also try disabling path MTU discovery on the sending host. On
         Linux, try:


  ==>      echo 1 >/proc/sys/net/ipv4/ip_no_pmtu_disc


         For a general discussion and information about other operating systems, see
         \?http://www.netheaven.com/pmtu.html?\. If disabling path MTU discovery
         fixes the problem, try to find the broken or misconfigured
         router/firewall that swallows the ICMP-unreachable packets. Increasing
         timeouts on the receiving host will not work around the problem.



  Q0018: Why do messages not get delivered down the same connection when I do
         something like: \"exim -v -R @aol.com"\? For other domains, I do this and
         I see the appropriate \*waiting for passed connections to get used*\
         messages.


  A0018: Recall that Exim does not keep separate queues for each domain, but
         operates in a distributed fashion. Messages get into its `waiting for
         host x' hints database only when a delivery has been tried, and has had
         a temporary error. Here are some possibilities:


         (1) The messages to \(aol.com)\ got put in your queue, but no previous
         delivery attempt occured before you did the \-R-\. This might have been
         because of your settings of \queue_only_load\, \smtp_accept_queue\, or any
         other option that caused no immediate delivery attempt on arrival. If
         this is the case, you can try using \-qqR-\ instead of \-R-\.


         (2) You have set \connection_max_messages\ on the smtp transport, and
         that limit was reached. This would show as a sequence of messages
         down one connection, then another sequence down a new connection, etc.


         (3) Exim tried to pass on the SMTP connection to another message, but
         that message was in the process of being delivered to \(aol.com)\ by some
         other process (typically, a normal queue runner). This will break the
         sequence, though the other delivery should pass its connection on to
         other messages if there are any.


         (4) The folk at \(aol.com)\ changed the MX records so the host names have
         changed - or a new host has been added. I don't know how likely this is.


         (5) Exim is not performing as it should in this regard, for some reason.
         Next time you have mail queued up for \(aol.com)\, try running


  ==>      exim_dumpdb /var/spool/exim wait-remote_smtp


         to see if those messages are listed among those waiting for the relevant
         \(aol.com)\ hosts.



  Q0019: There seems to be a problem in the string expansion code: it doesn't
         recognize references to headers such as \"${h_to}"\.


  A0019: The only valid syntax for header references is (for example) \"$h_to:"\
         because header names are permitted by RFC 2822 to contain a very wide
         range of characters. A colon (or white space) is required as the
         terminator.



  Q0020: Why do connections to my machine's SMTP port take a long time to respond
         with the banner, when connections to other ports respond instantly? The
         delay is sometimes as long as 30 seconds.


  A0020: These kinds of delay are usually caused by some kind of network problem
         that affects outgoing calls made by Exim at the start of an incoming
         connection. Configuration options that cause outgoing calls are:


         (1) \rfc1413_hosts\ and \rfc1413_query_timeout\ (for \*ident*\ calls).
             Firewalls sometimes block ident connections so that they time out,
             instead of refusing them immediately. This can cause this problem.
             See Q5023 for a discussion of the usefulness of \*ident*\.


         (2) The \host_lookup\ option, the \host_reject_connection\ option, or a
             condition in the ACL that runs at connection time requires the
             remote host's name to be looked up from its IP address. Sometimes
             these DNS lookups time out. You can get this effect with ACL
             statements like this:


  ==>          deny  hosts = *.x.example


             If at all possible, you should use IP addresses instead of host
             names in blocking lists in order to to avoid this problem.


         You can use the \-bh-\ option to get more information about what is
         happening at the start of a connection. However, note that the \-bh-\
         option does not provide a complete simulation. In particular, no
         \*ident*\ checks are done, so it won't show up a delay problem that is
         related to (1) above.



  Q0021: What does \*failed to create child process to send failure message*\ mean?
         This is a busy mail server with \smtp_accept_max\ set to 500, but this
         problem started to occur at about 300 incoming connections.


  A0021: Some message delivery failed, and when Exim wanted to send a bounce
         message, it was unable to create a process in which to do so. Probably
         the limit on the maximum number of simultaneously active processes has
         been reached. Most OS have some means of increasing this limit, and in
         some operating systems there is also a limit per uid which can be
         varied.



Q0022: What does \*No transport set by system filter*\ in a log line mean?

  A0022: Your system filter contains a \"pipe"\ or \"save"\ or \"mail"\ command,
         but you have not set the corresponding option which specifies which
         transport is to be used. You need to set whichever of
         \system_filter_pipe_transport\, \system_filter_file_transport\ or
         \system_filter_reply_transport\ is relevant.



  Q0023: Why is Exim refusing to relay, saying \*failed to find host name from IP
         address*\ when I have the sender's IP address in an ACL condition? My
         configuration contains this ACL statement:


  ==>      accept hosts = lsearch;/etc/mail/relaydomains:192.168.96.0/24


  A0023: When checking a host list, the items are tested in left-to-right
         order. The first item in your list is a lookup on the incoming host's
         name, so Exim has to determine the name from the incoming IP address in
         order to perform the test. If it can't find the host name, it can't do
         the check, so it gives up. You would have discovered what was going
         on if you had run a test such as


  ==>      exim -bh 192.168.96.131


         The solution is to put all explicit IP addresses first in the list.
         Alternatively, you can split the ACL statement into two like this:


  ==>      accept hosts = lsearch;/etc/mail/relaydomains
           accept hosts = 192.168.96.0/24


         If the host lookup fails, the first \"accept"\ fails, but then the
         second one is considered.



Q0024: When I run \"exim -bd -q10m"\ I get \*PANIC LOG: exec of exim -q failed*\.

  A0024: This probably means that Exim doesn't know its own path so it can't
         re-exec itself to do the first queue run. Check the output of


  ==>      exim -bP exim_path



  Q0025: I can't seem to get a pipe command to run when I include a \"${if"\
         expansion in it. This fails:


  ==>      command = perl -T /usr/local/rt/bin/rtmux.pl \
                       rt-mailgate helpdesk \
                       ${if eq {$local_part}{rt} {correspond}{action}}


  A0025: You need some internal quoting in there. Exim expands each individual
         argument separately. Because you have (necessarily) got spaces in your
         \"${if"\ item, you have to quote that argument. Try


  ==>      command = perl -T /usr/local/rt/bin/rtmux.pl \
                       rt-mailgate helpdesk \
                       "${if eq {$local_part}{rt} {correspond}{action}}"


         \**Warning:**\ If command starts with an item that requires quoting,
         you cannot just put it in quotes, because a leading quote means that the
         entire option setting is being quoted. What you have to do is to quote
         the entire value, and use internally escaped quotes for the ones you
         really want. For example:


  ==>      command = "\"${if ....}\" arg1 arg2"


         Any backslashes in the expansion items will have to be doubled to stop
         them being interpreted by the string reader.



  Q0026: I'm trying to get Exim to connect an alias to a pipe, but it always
         gives error code 69, with the comment \*(could mean service or program
         unavailable)*\.


A0026: If your alias entry looks like this:

  ==>      alias:  |"/some/command some parameters"


         change it to look like this:


  ==>      alias:  "|/some/command some parameters"



Q0027: What does the error \*Spool file is locked*\ mean?

  A0027: This is not an error. All it means is that when an Exim delivery
         process (probably started by a queue runner process) looked at a message
         in order to start delivering it, it found that another Exim process was
         already busy delivering it. On a busy system this is quite a common
         occurrence. If you set \"-skip_delivery"\ in the \log_selector\ option,
         these messages are omitted from the log.


         The only time when this message might indicate a problem is if it is
         repeated for the same message for a very long time. That would suggest
         that the process that is delivering the message has somehow got stuck.



  Q0028: Exim is reporting IP addresses as 0.0.0.0 or 255.255.255.255 instead of
         their correct values. What's going on?


  A0028: You are using a version of Exim built with gcc on an IRIX box.
         See Q9502.



Q0029: I can't seem to figure out why PAM support doesn't work correctly.

  A0029: There is a problem using PAM with shadow passwords when the calling
         program is not running as \/root/\. Exim is normally running as the
         Exim user when authenticating a remote host. See this posting for one
         way round the problem:


         \?http://www.exim.org/mailman/htdig/exim-users/Week-of-Mon-20010917/030371.html?\


         Another solution can be found at \?http://www.e-admin.de/pam_exim/?\.


         PAM 0.72 allows authorization as non-\/root/\, using setuid helper programs.
         Furthermore, in \(/etc/pam.d/exim)\ you can explicitelly specify that
         this authorization (using setuid helpers) is only permitted for certain
         users and groups.



  Q0030: I'm trying to use a query-style lookup for hosts that are allowed to
         relay, but it is giving really weird errors.


  A0030: Does your query contain a colon character? Remember that host lists are
         colon-separated, so you need to double any colons in the query. This
         applies even if the query is defined as a macro.



  Q0031: Exim is rejecting connections from hosts that have more than one IP
         address, for no apparent reason.


  A0031: You are using Solaris 7 or earlier, and have \"nis dns files"\ in
         \(/etc/nsswitch.conf)\. Change this to \"dns nis files"\ to avoid hitting Sun
         bug 1154236 (a bad interaction between NIS and the DNS).



  Q0032: Exim is failing to find the MySQL library, even though is it present
         within \\LD_LIBRARY_PATH\\. I'm getting this error:


  ==>      /usr/local/bin/exim: fatal: libmysqlclient.so.6: open failed:
           No such file or directory


  A0032: Exim is suid, and \\LD_LIBRARY_PATH\\ is ignored for suid binaries on a
         Solaris (and other?) systems. What you should be doing is adding
         \"-R/local/lib/mysql"\ to the same place in the compilation that you added
         \"-L/local/lib/mysql"\. This tells the binary where to look without
         needing a path variable.



  Q0033: What does the error \*lookup of host "xx.xx.xx" failed in yyy router*\
         mean?


  A0033: You configured a \%manualroute%\ router to send the message to xx.xx.xx. When
         it tried to look up the IP address for that host, the lookup failed
         with a permanent error. As this is a manual routing, this is a
         considered to be a serious error which the postmaster needs to know
         about (maybe you have a typo in your file), and there is little point
         in keeping on trying. So it freezes the message.


         (1) Don't set up routes to non-existent hosts.


         (2) If you must set up routes to non-existent hosts, and don't want
         freezing, set the \host_find_failed\ option on the router to do something
         other than freeze.



  Q0034: Exim works fine on one host, but when I copied the binary to another
         identical host, it stopped working (it could not resolve DNS names).


  A0034: Is the new host running exactly the same operating system? Most
         importantly, are the versions of the dynamically loaded libraries
         (files with names like \(libsocket.so.1)\) the same on both systems? If not,
         that is probably the cause of the problem. Either arrange for the
         libraries to be the same, or rebuild Exim from source on the new host.



  Q0035: I set a \"hosts"\ condition in an ACL to do a lookup in a file of IP
         addresses, but it doesn't work.


  A0035: Did you remember to put \"net-"\ at the start of the the search type? If
         you set something like this:


  ==>      accept hosts = lsearch;/some/file


         Exim searches the file for the host name, not the IP address. You need
         to set


  ==>      accept hosts = net-lsearch;/some/file


         to make it use the IP address as the key to the lookup.



  Q0036: Why do I get the error \*Permission denied: creating lock file hitching
         post*\ when Exim tries to do a local delivery?


  A0036: Your configuration specifies that local mailboxes are all held in
         single directory, via configuration lines like these (taken from the
         default configuration):


  ==>      local_delivery:
             driver = appendfile
             file = /var/mail/$local_part


         and the permissions on the directory probably look like this:


  ==>      drwxrwxr-x   3 root     mail         512 Jul  9 13:48 /var/mail/


         Using the default configuration, Exim runs as the local user when doing
         a local delivery, and it uses a lock file to prevent any other process
         from updating the mailbox while it is writing to it. With those
         permissions the delivery process, running as the user, is unable to
         create a lock file in the \(/var/mail(\ directory. There are two solutions
         to this problem:


         (1) Set the \"write"\ and \"sticky bit"\ permissions on the directory, so
             that it looks like this:


  ==>          drwxrwxrwt   3 root     mail         512 Jul  9 13:48 /var/mail/


             The \"w"\ allows any user to create new files in the directory, but
             the \"t"\ bit means that only the creator of a file is able to remove
             it. This is the same setting as is normally used with the \(/tmp)\
             directory.


         (2) Arrange to run the local_delivery transport under a specific group
             by changing the configuration to read


  ==>          local_delivery:
                 driver = appendfile
                 file = /var/mail/${local_part}
                 group = mail


             The delivery process still runs under the user's uid, but with the
             group set to \"mail"\. The group permission on the directory allows
             the process to create and remove the lock file.


             The choice between (1) and (2) is up to the administrator. If the
             second solution is used, users can empty their mailboxes by updating
             them, but cannot delete them.


         If your problem involves mail to \/root/\, see also Q0507.



  Q0037: I am experiencing mailbox locking problems with Sun's \"mailtool"\ used
         over a network.


A0037: See Q9705 in the Sun-specific section below.


  Q0038: What does the error message \*error in forward file (filtering not
         enabled): missing or malformed local part*\ mean?


  A0038: If you are trying to use an Exim filter, you have forgotten to enable
         the facility, which is disabled by default. In the \%redirect%\ router
         (in the Exim run time configuration file) you need to set


  ==>      allow_filter = true


         to allow a \(.forward)\ file to be used as an Exim filter. If you are not
         trying to use an Exim filter, then you have put a malformed address in
         the \(.forward)\ file.



  Q0039: I have installed Exim, but now I can't mail to \/root/\ any more. Why is
         this?


  A0039: Most people set up \/root/\ as an alias for the manager of the host. If
         you haven't done this, Exim will attempt to deliver to \/root/\ as if it
         were a normal user. This isn't really a good idea because the delivery
         process would run as \/root/\. Exim has a trigger guard in the option


  ==>      never_users = root


         in the default configuration file. This prevents it from running as \/root/\
         when doing any deliveries. If you really want to run local deliveries as
         \/root/\, remove this line, but it would be better to create an alias for
         \/root/\ instead.



  Q0040: How can I stop undeliverable bounce messages (e.g. to routeable, but
         undeliverable, spammer senders) from clogging up the queue for days?


  A0040: If at all possible, you should try to avoid getting into this situation
         in the first place, for example, by verifying recipients so that you
         do not accept undeliverable messages that lead to these bounces.
         You can, however, configure Exim to discard failing bounce messages
         early. Just set \ignore_bounce_errors_after\ to specify a (short) time
         to keep them for.



  Q0041: What does the message \*unable to set gid=ddd or uid=ddd (euid=ddd):
         local delivery to ... transport=ttt*\ mean?


  A0041: Have you remembered to make Exim setuid \/root/\? It needs root privilege if
         it is to do any local deliveries, because it does them ``as the user''.
         Note also that the partition from which Exim is running (where the
         binary is installed) must not have the \nosuid\ mount option set. You
         can check this by looking at its \(/etc/fstab)\ entry (or \(/etc/vfstab)\,
         depending on your OS).



  Q0042: My ISP's mail server is rejecting bounce messages from Exim, complaining
         that they have no sender. The SMTP trace does indeed show that the
         sender address is \"<>"\. Why is the Sender on the bounce message empty?


  A0042: Because the RFCs say it must be. Your ISP is at fault. Send them this
         extract from RFC 2821 section 6.1 (\*Reliable Delivery and Replies by
         Email*\):


           If there is a delivery failure after acceptance of a message, the
           receiver-SMTP MUST formulate and mail a notification message.  This
           notification MUST be sent using a null (\"<>"\) reverse path in the
           envelope.  The recipient of this notification MUST be the address
           from the envelope return path (or the ::Return-Path:: header line).
           However, if this address is null (\"<>"\), the receiver-SMTP MUST NOT
           send a notification.


         The reason that bounce messages have no sender is so that they
         themselves cannot provoke further bounces, as this could lead to a
         unending exchange of undeliverable messages.



  Q0043: What does the error \*Unable to get interface configuration: 22 Invalid
         argument*\ mean?


  A0043: This is an error that occurs when Exim is trying to find out the all the
         IP addresses on all of the local host's interfaces. If you have lots of
         virtual interfaces, this can occur if there are more than around 250 of
         them. The solution is to set the option \local_interfaces\ to list just
         those IP addresses that you want to use for making and receiving SMTP
         connections.



Q0044: What does the error \*Failed to create spool file*\ mean?

  A0044: Exim has been unable to create a file in its spool area in which to
         store an incoming message. This is most likely to be either a
         permissions problem in the file hierarchy, or a problem with the uid
         under which Exim is running, though it could be something more drastic
         such as your disk being full.


         If you are running Exim with an alternate configuration file using a
         command such as \"exim -C altconfig..."\, remember that the use of -C
         takes away Exim's root privilege.


         Check that you have defined the spool directory correctly by running


  ==>      exim -bP spool_directory


         and examining the output. Check the mode of this directory. It should
         look like this, assuming you are running Exim as user \/exim/\:


  ==>      drwxr-x---   6 exim  exim      512 Jul 16 12:29 /var/spool/exim


         If there are any subdirectories already in existence, they should have
         the same permissions, owner, and group. Check also that you haven't got
         incorrect permissions on superior directories (for example, \(/var/spool)\).
         Check that you have set up the Exim binary to be setuid \/root/\. It should
         look like this:


  ==>      -rwsr-xr-x   1 root     xxx       502780 Jul 16 14:16 exim


         Note that it is not just the owner that must be \/root/\, but also the third
         permission must be \"s"\ rather than \"x"\.



  Q0045: I see entries in the log that mention two different IP addresses for the
         same connection. Why is this? For example:


  ==>      H=tip-mp8-ncs-13.stanford.edu ([36.173.0.189]) [36.173.0.156]


  A0045: The actual IP address from which the call came is the final one.
         Whenever there's something in parentheses in a host name, it is what the
         host quoted as the domain part of an SMTP HELO or EHLO command. So in
         this case, the client, despite being 36.173.0.156, issued the command


  ==>      EHLO [36.173.0.189]


         when it sent your server the message. This is, of course, very
         misleading.



  Q0046: A short time after I start Exim I see a defunct zombie process. What
         is causing this?


  A0046: Your system must be lightly loaded as far as mail is concerned. The
         daemon sets off a queue runner process when it is started, but it only
         tidies up completed child processes when it wakes up for some other
         reason. When there's nothing much going on, you occasionally see
         defunct processes like this waiting to be dealt with. This is
         perfectly normal.



  Q0047: On a reboot, or a restart of the mail system, I see the message \*Mailer
         daemons: exim abandoned: unknown, malformed, or incomplete option
         -bz sendmail*\. What does this mean?


  A0047: \-bz-\ is a Sendmail option requesting it to create a `configuration freeze
         file'. Exim has no such concept and so does not support the option. You
         probably have a line like


  ==>      /usr/lib/sendmail -bz


         in some start-up script (e.g. \(/etc/init.d/mail)\) immedately before


  ==>      /usr/lib/sendmail -bd -q15m


         The first of these lines should be commented out.



  Q0048: Whenever exim restarts it takes up to 3-5 minutes to start responding on
         the SMTP port. Why is this?


  A0048: Something else is hanging onto port 25 and not releasing it. One place
         to look is \(/etc/inetd.conf)\ in case for any reason an SMTP stream is
         configured there.



  Q0049: What does the log message \*no immediate delivery: more than 10 messages
         received in one connection*\ mean?


  A0049: A remote MTA sent a number of messages in a single SMTP session. Exim
         limits the number of immediate delivery processes it creates as a
         result of a single SMTP connection, in order to avoid creating a zillion
         processes on systems that can have many incoming connections. If you are
         dialing in to collect mail from your ISP, you should probably set
         \smtp_accept_queue_per_connection\ to some number larger than 10, or
         arrange to start a queue runner for local delivery (using \-ql-\)
         immediately after collecting the mail.



  Q0050: I am getting complaints from a customer who uses my Exim server for
         relaying that they are being blocked with a \*Too many connections*\
         error.


A0050: See \smtp_accept_max\, \smep_accept_max_per_host\ and \smtp_accept_reserve\.


  Q0051: When I try \"exim -bf"\ to test a system filter, I received the following
         error message: \*Filter error: unavailable filtering command "fail" near
         line 8 of filter file*\.


  A0051: Use the \-bF-\ option to test system filters. This gives you access to the
         freeze and fail actions.



Q0052: What does \*ridiculously long message header*\ in an error report mean?

  A0052: There has to be some limit to the length of a message's header lines,
         because otherwise a malefactor could open an SMTP channel to your host,
         start a message, and then just send characters continuously until your
         host ran out of memory. (Exim stores all the header lines in main
         memory while processing a message). For this reason a limit is imposed
         on the total amount of memory that can be used for header lines. The
         default is 1MB, but this can be changed by setting \\HEADER_MAXSIZE\\ in
         \(Local/Makefile)\ before building Exim. Exceeding the limit provokes
         the ``ridiculous'' error message.



  Q0053: Exim on my host responds to a connection with \"220 *****..."\ and
         won't understand \\EHLO\\ commands.


  A0053: This is the sign of a Cisco Pix ``Mailguard'' sitting in front of your
         MTA. Pix breaks ESMTP and only does SMTP. It is a nuisance when you have
         a secure MTA running on your box. Something like ``no fixup protocol
         smtp 25'' in the Pix configuration is needed. It may be possible to do
         this by logging into the Pix (using \^telnet^\ or \^ssh^\) and typing
         \"no fixup smtp"\ to its console. (You may need to use other commands
         before or after to set up configuration mode and to activate a changed
         configuration. Consult your Pix documentation or expert.) See also
         Q0078.



  Q0054: I'm getting an Exim configuration error \*unknown rewrite flag
         character (m) in line 386*\ but I haven't used any flags on my rewriting
         rules.


  A0054: You have probably forgotten to quote a replacement string that contains
         white space.



  Q0055: What does the error \*Failed to open wait-remote_smtp database: Invalid
         argument*\ mean?


  A0055: This is something that happens if you have existing DBM hints files when
         you install a new version of Exim that is compiled to use a different or
         upgraded DBM library. The simplest thing to try is


  ==>      rm /var/spool/exim/db/*


         This removes all the hints files. Exim will start afresh and build new
         ones. If the symptom recurs, it suggests there is some problem with your
         DBM library.



  Q0056: We are using Exim to send mail from our web server. However, whenever a
         user sends an email it gets sent with the return path (envelope sender)
         //apache@server_name.com// because the PHP script is running as
         \/apache/\.


  A0056: You need to include \/apache/\ in the \trusted_users\ configuration option.
         Only trusted users are permitted to specify senders when mail is passed
         to Exim via the command line.



  Q0057: We've got people complaining about attachments that don't show up
         as attachments, but are included in the body of the message.


  A0057: These symptoms can be seen when some software passes a CRLF line
         terminated message via the command line to an MTA that expects lines to
         be terminated by LF only, and so preserves the CRs as data. If you can
         identify the software that is doing this, try setting the \-dropcr-\
         option on the command it uses to call Exim. Alternatively, you can set
         \drop_cr\ in the configuration file, but then that will apply to all
         input.



  Q0058: What does the error \*failed to open DB file \(/var/spool/exim/db/retry)\:
         File exists*\ mean?


  A0058: This error is most often caused when a hints file that was written with
         one version of the Berkeley DB library is read by another version.
         Sometimes this can happen if you change from a binary version of Exim to
         a locally compiled version. Or it can happen if you compile and install
         a new version of Exim after changing Berkeley DB versions. You can find
         out which version your Exim is using by running:


  ==>      ldd /usr/sbin/exim


         The solution to the problem is to delete all the files in the
         \(/var/spool/exim/db)\ directory, and let Exim recreate them.



  Q0059: When my Outlook Express 6.0 client sends a STARTTLS command to begin a
         TLS session, Exim doesn't seem to receive it. The Outlook log shows
         this:


  ==>      SMTP: 14:19:27 [tx] STARTTLS
           SMTP: 14:19:27 [rx] 500 Unsupported command.


          but the Exim debugging output shows this:


  ==>       SMTP<< EHLO xxxx
            SMTP>> 250-yyyy Hello xxxx [nnn.nnn.nnn.nnn]
            250-SIZE 52428800
            250-PIPELINING
            250-AUTH CRAM-MD5 PLAIN LOGIN
            250-STARTTLS
            250 HELP
            SMTP<< QUIT


  A0059: Turn off scanning of outgoing email in Norton Antivirus. If you aren't
         running Norton Antivirus, see if you are running some other kind of SMTP
         proxying, either on the client or on a firewall between the client and
         server. ``Unsupported command'' is not an Exim message.



  Q0060: Why am I getting the error \*failed to expand \"/data/lists/lists/${lc"\
         for require_files: \"${lc"\ is not a known operator*\ for this setting:


  ==>      require_files = MAILMAN_HOME/lists/${lc:$local_part}/config.db


  A0060: The value of \"require_files"\ is a \*list*\ in which each item is
         separately expanded. You need either to double the colon, or switch to
         a different list separator.



  Q0061: What does the error \*Too many ``Received'' headers - suspected mail
         loop*\ mean?


  A0061: Whenever a message passes through an MTA, a ::Received:: header gets
         added. Exim counts the number of these headers in incoming messages. If
         there are more than the value of \received_headers_max\ (default 30),
         Exim assumes there is some kind of mail routing loop occurring. For
         example, host A passes the message to host B, which immediately passes
         it back to host A. Check the ::Received:: headers and the mail logs to
         determine exactly what is going on.


         One common cause of this problem is users with accounts on both systems
         who set up each one to forward to the other, thinking that will cause
         copies of all messages to be delivered on both of them.



  Q0062: When I try to start an Exim daemon it crashes. I ran a debugger and
         discovered that the crash is happening in the function \^^getservbyname()^^\.
         What's going on?


  A0062: What have you got in the file \(/etc/nsswitch.conf)\? If it contains this
         line:


  ==>      services:       db files


         try removing the \"db"\. (Your system is trying to look in some kind of
         database before searching the file \(/etc/services)\.)



  Q0063: When I try to start an Exim daemon, nothing happens. There is no
         process, and nothing is written to the Exim log.


  A0063: Check to see if anything is written to \(syslog)\. This problem can be
         caused by a permission problem that stops Exim from writing to its log
         files, especially if you've specified that they should be written
         somewhere other than under Exim's spool directory. You could also try
         running the daemon with debugging turned on.



  Q0064: When I run \"exim -d test@domain"\ it delivers fine, but when I send a
         message from the \^mail^\ command, I get \*User unknown*\ and the mail
         is saved in \(dead.letter)\.


  A0064: It looks as if Exim isn't being called by \^mail^\; instead it is
         calling some other program (probably Sendmail). Try running the command


  ==>      /usr/sbin/sendmail -bV


         (If you get \*No such file or directory*\ or \*Command not found*\ you
         are running Solaris or IRIX. Try again with \(/usr/lib/sendmail)\.) The
         output should be something like this:


  ==>      Exim version 4.05 #1 built 13-Jun-2002 10:27:15
           Copyright (c) University of Cambridge 2002


         If you don't see this, your Exim installation isn't fully operational.
         If you are running FreeBSD, see Q9201. For other systems, see Q0114.



  Q0065: When (as \/root/\) I use -C to run Exim with an alternate configuration
         file, it gives an error about being unable to create a spool file when
         trying to run an \%autoreply%\ transport. Why is this?


  A0065: When Exim is called with -C, it passes on -C to any instances of itself
         that it calls (so that the whole sequence uses the same config file). If
         it's running as \/exim/\ when it does this, all is well. However, if it
         happens as a consequence of a non-privileged user running \%autoreply%\,
         the called Exim gives up its root privilege. Then it can't write to the
         spool.


         This means that you can't use -C (even as \/root/\) to run an instance of
         Exim that is going to try to run \%autoreply%\ from a process that is
         neither \/root/\ nor \/exim/\. Because of the architecture of Exim (using
         re-execs to regain privilege), there isn't any way round this
         restriction. Therefore, the only way you can make this scenario work is
         to run the \%autoreply%\ transport as \/exim/\ (that is, the user that
         owns the Exim spool files). This may be satisfactory for autoreplies
         that are essentially system-generated, but of course is no good for
         autoreplies from unprivileged users, where you want the \%autoreply%\
         transport to be run as the user. To get that to work with an alternate
         configuration, you'll have to use two Exim binaries, with different
         configuration file names in each. See S001 for a script that patches
         the configuration name in an Exim binary.



Q0066: What does the message \*unable to set gid=xxx or uid=xxx*\ mean?

  A0066: This message is given when an Exim process is unable to change uid or
         gid when it needs to, because it does not have root privilege. This is a
         serious problem that prevents Exim from carrying on with what it is
         doing. The two most common situations where Exim needs to change uid/gid
         are doing local deliveries and processing users' filter files. There are
         two common causes of this error:


         (1) You have forgotten to make the exim binary setuid to \/root/\. This
             means that it can never change uid/gid in any situation. Also, the
             setuid binary must reside on a disk partition that does not have the
             \"nosuid"\ mount option set.


         (2) The exim binary is setuid, but you have configured Exim so that,
             while trying to verify an address at SMTP time, it runs a router
             that needs to change uid/gid. Because Exim runs as \/exim/\ and not
             \/root/\ while receiving messages, the router is unable to change
             uid and therefore it cannot operate. The usual example of this is a
             \%redirect%\ router for users' filter files.


             Setting the \user\ or \check_local_user\ options on a \redirect\
             router causes this to happen (except in the special case when the
             redirection list is provided by the \data\ option and does not
             contain \":include:"\).


             The solution is to set \no_verify\ on the router that is causing the
             problem. This means that it is skipped when an address is being
             verified. In ``normal'' configurations where the router is indeed
             handling users' filter files, this is quite acceptable, because you
             do not usually need to process a filter file in order to verify that
             the local part is valid. See, for example, the \%userforward%\
             router in the default configuration.



Q0067: What does the error \*too many unrecognized commands*\ mean?

  A0067: There have been instances of network abuse involving mail sent out by
         web servers. In most cases, unrecognizable commands are sent as part of
         the SMTP session. A real MTA never sends out such invalid commands. Exim
         allows a few unrecognized commands in a session to permit humans who are
         testing to make a few typos (it responds with a 5xx error). However, if
         Exim receives too many such commands, it assumes that it is dealing with
         an abuse of some kind, and so it drops the connection.



  Q0068: Exim times out when trying to connect to some hosts, though those hosts
         are known to be up and running. What's the problem?


  A0068: There could be a number of reasons for this (see also Q0017). The
         obvious one is that there is a networking problem between the hosts.
         If you can ping between the hosts or connect in other ways, the problem
         might be caused by ECN (Explicit Congestion Notification) being enabled
         in your kernel. ECN uses TCP flags originally assigned to TOS - it's a
         "new" invention, and some hosts and routers are known to be confused if
         a client uses it. If you are running Linux, you can turn ECN off by
         running this command:


  ==>      /bin/echo "0" > /proc/sys/net/ipv4/tcp_ecn


         This has also been reported to cure web connection problems from Mozilla
         and Netscape browsers in Linux when there were no problems with Windows
         Netscape browsers.



  Q0069: What does the error \*SMTP data timeout (message abandoned) on connection
         from...*\ mean?


  A0069: It means that there was a timeout while Exim was reading the contents of
         a message on an incoming SMTP connection. That is, it had successfully
         accepted a MAIL command, one or more RCPT commands, and a DATA command,
         and was in the process of reading the data itself. The length of timeout
         is controlled by the \smtp_receive_timeout\ option.


         If you get this error regularly, the cause may be incorrect handling of
         large packets by a router or firewall. The maximum size of a packet is
         restricted on some links; routers should split packets that are larger.
         There is a feature called ``path MTU discovery'' that enables a sender
         to discover the maximum packet size over an entire path (multiple
         Internet links). This can be broken by misconfigured firewalls and
         routers. There is a good explanation at \?http://www.netheaven.com/pmtu.html?\.
         Reducing the MTU on your local network can sometimes work round this
         problem. See Q0017 (3) for further discussion.



Q0070: What does the error \*SMTP command timeout on connection from...*\ mean?

  A0070: Exim was expecting to read an SMTP command from the client, but no
         command was read within the \smtp_receive_timeout\ time limit.



  Q0071: What does the error \*failed to open DB file \(/var/spool/exim//db/retry)\:
         Illegal argument*\ mean?


A0071: See Q0058. The cause of this error is usually the same.


  Q0072: Exim will deliver to normal aliases, and aliases that are pipes or
         files, but it objects to aliases that involve \":include:"\ items,
         complaining that it can't change gid or uid. Why is this?


  A0072: See Q0066 for a general answer. The problem happens during verification
         of an incoming SMTP message, not during delivery itself. In this
         particular case, you must have set up your aliasing router with a \user\
         setting. This causes Exim to change uid/gid when reading \":include:"\
         files. If you do not need the detailed verification provided by the
         router, the easy solution is to set \no_verify\ so that the router isn't
         used during verification.


         Otherwise, if you set \user\ on the router in order to provide a user
         for delivery to pipes or files, one solution is to put the \user\
         setting on the transports instead of on the router. You may need to
         create some special transports just for this router. The alternative is
         to supply two different routers, one with \user\ and \no_verify\, and
         the with \verify_only\ but no \user\ setting.



  Q0073: I'm seeing log file corruption, with parts of log lines getting mangled
         by other log entries.


  A0073: The only time this has been seen is when several servers were writing to
         the same log files over NFS. Exim assumes that its log file is on local
         disk, and using NFS, especially for more than one server, will not work.



  Q0074: What does the error message \*remote delivery process count got out of
         step*\ mean?


  A0074: Exim uses subprocesses for remote deliveries; this error means that the
         master process expected to have a child process running, but found there
         were none. Prior to release 4.11, this error could be caused by running
         Exim under \^strace^\ on a Linux system, because stracing causes
         children to be ``stolen'' such that a parent that tries to wait for
         ``any of my children'' is told that it has none. Current releases of
         Exim have code to get round this problem.



  Q0075: I'm using LDAP, and some email addresses that contain special characters
         are causing parsing errors in my LDAP lookups.


  A0075: You should be using \"${quote_ldap:$local_part}"\ instead of just
         \"$local_part"\ in your lookups.



  Q0076: I've configured Exim to use \^syslog^\ for its logs, with the main and
         reject logs sent to different files, but whenever a message is rejected,
         I get one message on the reject log and two messages on the main log.


  A0076: You are probably putting your reject items into the main log as well;
         remember \^syslog^\ levels are inclusive (for example, \"mail.info"\
         includes all higher levels, so a \"mail.notice"\ message will be caught
         by a \"mail.info"\ descriptor).
         Test this by running the command:


  ==>     logger -p mail.notice test


         and seeing which logs it goes into.



  Q0077: I've installed Exim and it is delivering mail just fine. However, when I
         try to read mail from my PC I get \*connection rejected*\ or \*unable to
         connect*\.


A0077: See Q5021.


  Q0078: Exim is logging the unknown SMTP command \"XXXX"\ from my client hosts,
         and they are unable to authenticate.


  A0078: This is a sign of a Cisco PIX firewall getting in the way. It does not
         support ESMTP, and turns EHLO commands into XXXX. You should configure
         the Pix to leave SMTP alone; see Q0053 for how to do this.



  Q0079: Our new PIX firewall is causing problems with incoming mail. How can
         this be fixed?


  A0079: See Q0053 and Q0078. If some messages get through and others do not,
         see also Q0017.



  Q0080: Am I to understand that the database lookups must only return one value?
         They can not return a list of values? The documentation seems to
         indicate that it's possible to return a list.


  A0080: Lookups can be used in two different situations, and what they return is
         different in the two cases. (Be thankful Exim 3 is gone; there was yet
         another case!)


         (1) You can use a lookup in any expanded string. The syntax is


  ==>          ${lookup ..... }


             In this case, whatever is looked up replaces the expansion item. It
             may be one value or a list of values. Whether a single value or a
             list is acceptable or not depends on where you are using the string
             expansion. If it is for an option that expects just one value, then
             only one value is allowed (for example).


         (2) You can make use of the lookup mechanism to test whether something
             (typically a host name or IP address) is in a list. For example,


  ==>          hosts = a : b : c


             in an ACL tests whether the calling host's name matches ``a'', or
             ``b'', or ``c''. Now, suppose you want to keep the list of names in
             a database, or cdb file, or NIS map, or...  By writing


  ==>           hosts = pgsql;select ....


             you are saying to Exim: ``Run this lookup; if it succeeds, behave as
             if the host is in the list; if it fails, the host is not in the
             list.'' You are using the indexing mechanism of the database as a
             fast way of checking a list. A simpler example is


  ==>           hosts = lsearch;/some/file


             where the file contains the list of hosts to be searched.


         The complication happens when a list is first expanded before being
         interpreted as a list. This happens in a lot of cases. You can therefore
         write either of these:


  ==>       hosts = cdb;/some/file
            hosts = ${lookup{something}cdb{/some/file}}


         but they have different meanings. The first means ``see if the host name
         is in the list in this file''. The second means ``run this lookup and
         use the result of the lookup as a list of host items to check''. In the
         second case, the list could contain multiple values (colon separated),
         and one of those values could even be ``cdb;/some/file''.


         Flexibility does lead to complexity, I'm afraid.



  Q0081: What does \*error in redirect data: included file xxxx is too big*\
         mean?


  A0081: You are trying to include a very large file in a redirection list, using
         the \":include:"\ feature. Exim has a built-in limit on the size, as a
         safety precaution. The default is 1 megabyte. If you want to increase
         this, you have to rebuild Exim. In your \(Local/Makefile)\, put


  ==>      MAX_INCLUDE_SIZE = whatever


         and then rebuild Exim. The value is a number of bytes, but you can give
         it as a parenthesized arithmetic expression such as \"(3*1024*1024)"\.
         However, an included file of more than a megabyte is likely to be quite
         inefficient. How many addresses does yours contain? You get the best
         performance out of Exim if you arrange to send mailing list messages
         with no more than about 100 recipients (in order to get parallelism in
         the routing).



  Q0082: What does \*relocation error: /lib/libnss_dns.so.2: symbol
         __libc_res_nquery, version GLIBC_PRIVATE not defined in file
         libresolv.so.2 with link time reference*\ mean?


  A0082: You have updated \^glibc^\ while an Exim daemon is running. Stop and
         restart the daemon.



  Q0083: Netscape on Unix is sending messages containing an unqualified user name
         in the ::Sender:: header line, which Exim is rejecting because I have
         set \"verify = header_syntax"\. How can I fix this?


  A0083: The only thing you can do in Exim is to set the
         \sender_unqualified_hosts\ option to allow unqualified sender addresses
         form the relevant hosts; of course, this applies to all sender
         addresses, not just the ::Sender:: header line.


         Alternatively, you can configure Netscape not to include the header line
         in the first place. Add the following line to the
         \($HOME/.netscape/preferences.js)\ and \($HOME/.netscape/liprefs.js)\
         files:


  ==>      user_pref("mail.suppress_sender_header", true);


         Netscape \*must*\ be shutdown while doing this.



  Q0084: I want to set up an alias that pipes a message to \^gpg^\ and then pipes
         the result to \^mailx^\ to resubmit the message, but when I use my
         tested command in an alias file, I get an error from \^gpg^\.


  A0084: Probably you are using a shell command with two pipe symbols in it. An
         alias like this:


  ==>      gpg-xxx: "|gpg <options> | mailx <options"


         does not work, because Exim does not run pipes under a shell by default.
         You must call a shell explicitly if you want to make use of the shell's
         features for double-piping, either by piping to \"/bin/sh"\ with a
         suitable \"-c"\ option, or by piping to a shell script.



  Q0085: I see a lot of \*rejected EHLO ... syntactically invalid argument(s)*\.
         I know it's because of the underscore in the host name, but is there a
         switch to allow Exim to accept mail from such hosts?


A0085: Yes. Add this to your configuration:

  ==>      helo_allow_chars = _


         For more seriously malformed host names, see \helo_accept_junk_hosts\.
         See also Q0732.



  Q0086: What does \*SMTP protocol violation: synchronization error (next input
         sent too soon)*\ mean?


  A0086: SMTP is a ``lock-step'' protocol, which means that, at certain points in
         the protocol, the client must wait for the server to respond before
         sending more data. Exim checks for correct behaviour, and issues this
         error if the client sends data too soon. This protects against
         malefactious clients who send a bunch of SMTP commands (usually to
         transmit spam) without waiting for any replies.


         This error is also provoked if the client is trying to start up a TLS
         session immediately on connection, without using the STARTTLS command.
         See Q1707 for a discussion of this case.



  Q0087: What does \*rejected after DATA: malformed address: xx@yy may not follow
         <xx@yy> : failing address in "from" header*\ mean? (I've obscured the
         real email addresses.)


A0087: Your DATA ACL contains

  ==>      verify = header_syntax


         and an incoming message contained the line


  ==>      From: xx@yy <xx@yy>


         This is syntactically invalid. The contents of an address in a header
         line are either just the address, or a ``phrase'' followed by an address
         in angle brackets. In the latter case, the ``phrase'' must be quoted if
         it contains special characters such as @. The following are valid
         versions of the bad header:


  ==>      From: xx@yy
           From: "xx@yy" <xx@yy>


         though why on earth anything generates this kind of redundant nonsense I
         can't think.



  Q0088: The Windows mailer SENDFILE.EXE sometimes hangs while trying to send a
         message to Exim 4, and eventually times out. It worked flawlessly with
         Exim 3. What has changed?


  A0088: Exim 4 sets an obscure TCP/IP parameter called TCP_NODELAY. This
         disables the "Nagle algorithm" for the TCP/IP transmission. The Nagle
         algorithm can improve network performance in interactive situations such
         as a human typing at a keyboard, by buffering up outgoing data until the
         previous packet has been acknowledged, and thereby reducing the number
         of packets used. This is not relevant for mail transmission, which
         mostly consists of quite large blocks of data; setting TCP_NODELAY
         should improve performance. However, it seems that some Windows clients
         do not function correctly if the server turns off the Nagle algorithm.
         If you are using Exim 4.23 or later, you can set


  ==>      tcp_nodelay = false


         This stops Exim setting TCP_NODELAY on the sockets created by the
         listening daemon.



  Q0089: What does the error \*kernel: application bug: exim(12099) has SIGCHLD
         set to SIG_IGN but calls wait()*\ mean?


  A0089: This was a bad interaction between a relatively recent change to the
         Linux kernel and some ``belt and braces'' programming in Exim. The
         following explanation is taken from Exim's change log:


         When Exim is receiving multiple messages on a single connection, and
         spinning off delivery processess, it sets the SIGCHLD signal handling to
         SIG_IGN, because it doesn't want to wait for these processes. However,
         because on some OS this didn't work, it also has a paranoid call to
         \^waitpid()^\ in the loop to reap any children that have finished. Some
         versions of Linux now complain (to the system log) about this
         ``illogical'' call to \^waitpid()^\. I have therefore put it inside a
         conditional compilation, and arranged for it to be omitted for Linux.


         I am pretty sure I caught all the places in Exim where this happened.
         However, there are still occasional reports of this error. I have not
         heard of any resolutions, but my current belief is that they are caused
         by something that Exim calls falling foul of the same check. There was
         at one time a suspicion that the IPv6 stack was involved.



  Q0090: I can't seem to get a pipe command to run when I include a \"${lookup"\
         expansion in it.


A0090: See Q0025.


  Q0091: Why is Exim giving the error \*Failed to send message from address_reply
         transport*\ when I run it using -C to specify an alternate
         configuration?


A0091: See Q0065.



1. BUILDING AND INSTALLING

Q0101: I'm having a problem with an Exim RPM.

  A0101: Normally the thing to do if you have a problem with an RPM package is
         to contact the person who built the package first, not the person who
         made the software that's in the package.  You can usually find out who
         made a package using the following command:


  ==>      rpm --query --package --queryformat '%{PACKAGER}\n' <rpm-package-file>


         where \[rpm-package-file]\ is the actual file, e.g. \(exim-3.03-2.i386.rpm)\.
         Or, if the package is installed on your system:


  ==>      rpm --query --queryformat '%{PACKAGER}\n' <package-name>


         where \[package-name]\ is the name component of the package, e.g. \"exim"\.
         If the packager is unable or unwilling to help, only then should you
         contact the actual author or associated mailing list of the software.


         If you discover through the querying process that you can't tell who
         the person (or company or group) is who built the package, or that they
         no longer exist at the given address, then you should reconsider
         whether you want a package from an unknown source on your system.


         If you discover through the querying process that you yourself are the
         person who built the package, then you should either (a) contact the
         author or associated mailing list, or (b) reconsider whether you ought
         to be building and distributing RPM packages of software you don't
         understand.


         Similar rules of thumb govern other binary package formats, including
         debs, tarballs, and POSIX packages.



Q0102: I can't get Exim to compile with Berkeley DB version 2.x or 3.x.

  A0102: Have you set \"USE_DB=yes\" in \(Local/Makefile)\? This causes Exim to use the
         native interface to the DBM library instead of the compatibility
         interface, which needs a header called \(ndbm.h)\ that may not exist on your
         system.



  Q0103: I'm getting an \*undefined symbol*\ error for \"hosts_ctl"\ when I try to
         build Exim. (On some systems this error is \*undefined reference to
         'hosts_ctl'*\.)


  A0103: You should either remove the definition of \\USE_TCP_WRAPPERS\\ or add
         \"-lwrap"\ to your \\EXTRALIBS\\ setting in Local/Makefile.



  Q0104: I'm about to upgrade to a new Exim release. Do I need to ensure the
         spool is empty, or take any other special action?


A0104: It depends on where you are coming from.

         (1) If you are changing to release 4.00 or later from a release prior to
         4.00, you will need to make changes to the run time configuration file.
         See the file \(doc/Exim4.upgrade)\ for details. If you are coming from
         before release 3.00, you should also see \(doc/Exim3.upgrade)\.


         (2) If you are upgrading from an Exim 4 release to a later release, you
         do not need to take special action. New releases are made backwards
         compatible with old spool files and hints databases, so that upgrading
         can be done on a running system. All that should be necessary is to
         install a new binary and then HUP the daemon.



Q0105: What does the error \*install-info: command not found*\ mean?

  A0105: You have set \\INFO_DIRECTORY\\ in your \(Local/Makefile)\, and Exim is trying
         to install the Texinfo documentation, but cannot find the command called
         \(install-info)\. If you have a version of Texinfo prior to 3.9, you
         should upgrade. Otherwise, check your installation of Texinfo to see why
         the \(install-info)\ command is not available.



  Q0106: Exim doesn't seem to be recognizing my operating system type correctly,
         and so is failing to build.


  A0106: Run the command \"scripts/os-type -generic"\. The output should be one of
         the known OS types, and should correspond to your operating system. You
         can see which OS are supported by obeying \"ls OS/Makefile-*"\ and looking
         at the file name suffixes.


         If there is a discrepancy, it means that the script is failing to
         interpret the output from the \"uname"\ command correctly, or that the
         output is wrong. Meanwhile, you can build Exim by obeying


  ==>      EXIM_OSTYPE=xxxx make


         instead of just \"make"\, provided you are running a Bourne-compatible
         shell, or otherwise by setting \\EXIM_OSTYPE\\ correctly in your
         environment. It is probably best to start again from a clean
         distribution, to avoid any wreckage left over from the failed attempt.



  Q0107: Exim fails to build, complaining about the absence of the \"killpg"\
         function.


  A0107: This function should be present in all modern flavours of Unix. If you
         are using an older version, you should be able to get round the problem
         by inserting


  ==>      #define killpg(pgid,sig)   kill(-(pgid),sig)


         into the file called \(OS/os.h-xxx)\, where xxx identifies your operating
         system, and is the output of the command \"scripts/os-type -generic"\.



  Q0108: I'm getting an unresolved symbol \"ldap_is_ldap_url"\ when trying to build
         Exim.


  A0108: You must have specified \"LOOKUP_LDAP=yes"\ in the configuration. Have you
         remembered to set \"-lldap"\ somewhere (e.g. in \\LOOKUP_LIBS\\)? You need that
         in order to get the LDAP library scanned when linking.



Q0109: I'm getting an unresolved symbol \"mysql_close"\ when trying to build Exim.

  A0109: You must have specified \"LOOKUP_MYSQL=yes"\ in the configuration. Have you
         remembered to set \"-lmysqlclient"\ somewhere (e.g. in \\LOOKUP_LIBS\\)? You
         need that in order to get the MySQL library scanned when linking.



  Q0110: I'm trying to build Exim with PAM support. I have included \"-lpam"\ in
         \\EXTRALIBS\\, but I'm still getting a linking error:


  ==>      /lib/libpam.so: undefined reference to `dlerror'
           /lib/libpam.so: undefined reference to `dlclose'
           /lib/libpam.so: undefined reference to `dlopen'
           /lib/libpam.so: undefined reference to `dlsym'


  A0110: Add \"-ldl"\ to \\EXTRALIBS\\. In some systems these dynamic loading functions
         are in their own library.



  Q0111: I'm getting the error \*db.h: No such file or directory*\ when I try to
         build Exim.


  A0111: This problem has been seen with RedHat 7.0, but could also happen in
         other environments. If your system is using the DB library, you
         need to install the DB development package in order to build Exim.
         The package is called something like \"db3-devel-3.1.14-16.i386.rpm"\ for
         Linux systems, but you should check which version of DB you have
         installed (current releases are DB 4).



  Q0112: I'm getting the error \*/usr/bin/ld: cannot find -ldb*\ when I try to
         build Exim.


A0112: This is probably the same problem as Q0111.


  Q0113: I've compiled Exim and I've managed to start it but there was one
         problem - it always complained that \(libmsqlclient.so.10)\ was not found,
         even though this file is in \(/usr/local/lib/mysql/)\.


A0113: Solaris: ensure you have this in your \(Local/Makefile)\:

  ==>      LOOKUP_LIBS=-L/usr/local/lib/mysql -R/usr/local/lib/mysql


         Net/Open/FreeBSD: Run this command (or ensure it gets run automatically
         at boot time):


  ==>      ldconfig -m /usr/local/lib/mysql


         Linux: add \(/usr/local/lib/mysql)\ to \(/etc/ld.so.conf)\ and re-run \(ldconfig)\.
         Alternatively, add


  ==>      -Wl,-rpath -Wl,/usr/local/lib/mysql


         to EXTRA_LIBS and  then re-link (this is similar to the Solaris solution
         above). This will probably also work on other systems that use GNU
         Binutils.



  Q0114: How can I remove Sendmail from my system? I've built Exim and run \"make
         install"\, but it still doesn't seem to be fully operational.


  A0114: If you are running FreeBSD, see Q9201. Otherwise, you need to arrange
         that whichever of the paths \(/usr/sbin/sendmail)\ or \(/usr/lib/sendmail)\
         exists on your system is changed to refer to Exim. For example, you
         could use these commands (as \/root/\):


  ==>      mv /usr/sbin/sendmail /usr/sbin/sendmail.original
           chmod u-s /usr/sbin/sendmail.original
           ln -s /path/to/exim /usr/sbin/sendmail


         The second command removes the setuid privilege from the old MTA, as a
         general safety precaution. In the third command, substitute the actual
         path to the Exim binary for \(/path/to/exim)\.



  Q0115: What does \*Can't open \(../scripts/newer)\: No such file or directory*\
         mean? I got it while trying to build Exim.


  A0115: You are using FreeBSD, or another OS that has a \^make^\ command which
         tries to optimize the running of commands. Exim's \(Makefile)\ contains
         targets with sequential commands like this:


  ==>      buildpcre:
             @cd pcre; $(MAKE) SHELL=$(SHELL) AR="$(AR)" $(MFLAGS) CC="$(CC)" \
               CFLAGS="$(CFLAGS) $(PCRE_CFLAGS)" \
               RANLIB="$(RANLIB)" HDRS="$(PHDRS)" \
               INCLUDE="$(INCLUDE) $(IPV6_INCLUDE) $(TLS_INCLUDE)"
             @if $(SHELL) $(SCRIPTS)/newer pcre/libpcre.a exim; then \
               /bin/rm -f exim eximon.bin; fi


         The second command assumes that the \"cd pcre"\ in the first command is
         no longer in effect. If you have \"-j3"\ in your default set of
         \"MAKEFLAGS"\, FreeBSD \^make^\ tries to optimize, and ends up up with both
         commands in the same shell process. The result is that \"$(SCRIPTS)"\
         (which has a value of \"../scripts"\) is not found.


         The simplest solution is to force \^make^\ to use backwards compatibility
         mode with each command in its own shell, by using the \-B\ flag. To
         ensure that this happens throughout the build, it's best to export it in
         your environment:


  ==>     MAKEFLAGS='-B'
       export MAKEFLAGS
       make



  Q0116: I have tried to build Exim with Berkeley DB 3 and 4, but I always get
         errors.


  A0116: One common problem, especially when you have several different versions
         of BDB installed on the same host, is that the header files and library
         files for BDB are not in a standard place. You therefore need to tell
         Exim where they are, by setting INCLUDE and DBMLIB in your
         \(Local/Makefile)\. For example, I use this on my workstation when
         I want to build with DB 4.1:


  ==>      INCLUDE=-I/opt/local/include/db-4.1
           DBMLIB=/opt/local/lib/db-4.1/libdb.a


         Specifying the complete library file like this will cause it to be
         statically linked with Exim. You'll have to check to see where these
         files are on your system. For example, on FreeBSD 5, the header is in
         \(/usr/local/include/db4)\ and the library is in \(/usr/local/lib)\ and
         called \(libdb4)\. In that environment, you could use:


  ==>      INCLUDE=-I/usr/local/include/db4
           DBMLIB=-L/usr/local/lib -ldb4


         This time, DBMLIB is specifying the library directory (\(/usr/local/lib)\)
         and the name of the library (\(db4)\) separately. The name of the actual
         library file is \(/usr/local/lib/libdb4.something)\. If the library was
         compiled for dynamic linking, that will be used.



Q0117: Is there a quick walk-through of an Exim install from source anywhere?

  A0117: Here! This is a contribution from a RedHat user, somewhat edited. On
         other operating systems things may be slightly different, but the
         general approach is the same.


         (1) Install the db needed for Exim. This needs to be done first if you
         don't have a DBM library installed. Go to \?http://www.sleepycat.com?\
         and download \(db-4.1.25.tar.gz)\, or whatever the current release is.
         Then:


  ==>      gunzip db-4.1.25.tar.gz
           tar -xvf db-4.1.25.tar
           cd db-4.1.25
           cd build_unix
           ../dist/configure
           make
           make install


         (2) Add a user for use by Exim, unless you want to use an existing user
         such as \/mail/\:


  ==>      adduser exim


         (3) Now you can prepare to build Exim. Go to \?http://www.exim.org?\ or
         one of its mirrors, or the master ftp site
         \?ftp://ftp.csx.cam.ac.uk/pub/software/email/exim/exim4?\, and download
         \(exim-4.20.tar.gz)\ or whatever the current release is. Then:


  ==>      gunzip exim-4.20.tar.gz
           tar -xvf exim-4.20.tar
           cd exim-4.20
           cp src/EDITME Local/Makefile
           cp exim_monitor/EDITME Local/eximon.conf


         (4) Edit \(Local/Makefile)\:


         Comment out EXIM_MONITOR= unless you want to install the Exim
           monitor (it requires X-windows).


         Set the user you want Exim to use for itself:


  ==>        EXIM_USER=exim


         If your DBM library is Berkeley DB, set up to use its native interface:


  ==>        USE_DB=yes


         Make sure Exim's build can find the DBM library and its headers. If
           you've installed Berkeley DB 4 you'll need to have settings like this
           in \(Local/Makefile)\:


  ==>        INCLUDE=-I/usr/local/BerkeleyDB.4.1/include
             DBMLIB=/usr/local/BerkeleyDB.4.1/lib/libdb.a


           (Check that the first directory contains the db.h file and that the
           second library exists.)


         You don't need to change anything else, but you might want to review
           the default settings in the ``must specify'' section.


         (4) Build Exim by running the \/make/\ command.


         (5) Install Exim by running, as \/root/\:


  ==>      make install


         You \*must*\ be \/root/\ to do this. You do not have to be root for any of
         the previous building activity.


         (6) Run some tests on Exim; see if it will do local and remote
         deliveries. Change the configuration if necessary (for example,
         uncommenting \group\ on the \%local_delivery%\ transport if you don't
         use a ``sticky bit'' directory).


         (7) Change Sendmail to Exim (of course you need to have had Sendmail
         installed to do this).


  ==>      /etc/init.d/sendmail stop
           mv /usr/sbin/sendmail /usr/sbin/sendmail.org
           ln -s /usr/exim/bin/exim /usr/sbin/sendmail
           /etc/init.d/sendmail start


         (8) Check the Exim log. Either use the Exim monitor, or:


  ==>      tail -f /var/spool/exim/log/mainlog



  Q0118: I've set \"LOOKUP_INCLUDE=-I/client/include"\ in Local/Makefile, but the
         compilation of \^exim_dumpdb^\ is ignoring this option and failing. Why?


  A0118: LOOKUP_INCLUDE is the special include file for lookup modules in Exim
         (e.g. mysql, LDAP). Confusingly, it doesn't apply to basic DBM code
         which is used also for other things. Try setting INCLUDE and DBMLIB
         instead. For example:


  ==>      USE_DB=yes
           INCLUDE=-I/client/include
           DBMLIB=/client/lib/libdb.a



  Q0119: I know there are some 3rd-party patches for Exim, for exiscan and
         other things. Where are they?


  A0119: Exiscan is at \?http://duncanthrax.net/exiscan-acl/?\.
  [[br]]
         Scanexi is at \?http://w1.231.telia.com/~u23107873/scanexi.html?\
  [[br]]
         A sample \^^local_scan()^^\ function for interfacing to \^uvscan^\ is
         at \?http://www.dcs.qmul.ac.uk/~mb/local_scan/?\.
  [[br]]
         An interface to SpamAssassin at SMTP time is at
         \?http://marc.merlins.org/linux/exim/sa.html?\.
  [[br]]
         A mini-HOWTO (PDF file) about scanning and virus scanning, and some RPMs
         can be found at \?http://www.timj.co.uk/linux/exim.php?\.




2. ROUTING IN GENERAL

  Q0201: How can I arrange that messages larger than some limit are handled by
         a special router?


A0201: You can use a \condition\ option on the router line this:

  ==>      condition = ${if >{$message_size}{100K}{yes}{no}}



Q0202: Can I specify a list of domains to explicitly reject?

  A0202: Set up a named domain list containing the domains in the first section
         of the configuration, for example:


  ==>      domainlist reject_domains = list:of:domains:to:reject


         You can use this list in an ACL to reject any SMTP recipients in those
         domains. You can also give a customized error message, like this:


  ==>      deny message = The domain $domain is no longer supported
                domains = +reject_domains


         If you also want to reject these domains in messages that are submitted
         from the command line (not using SMTP), you need to set up a router to
         do it, like this:


  ==>      reject_domains:
             driver = redirect
             domains = +reject_domains
             allow_fail
             data = :fail: The domain $domain is no longer supported



  Q0203: How can I arrange to do my own qualification of non-fully-qualified
         domains, and then pass them on to the next router?


  A0203: If you have some list of domains that you want to qualify, you can do
         this using a redirect router. For example,


  ==>      qualify:
             driver = redirect
             domains = *.a.b
             data = ${quote:$local_part}@$domain.c.com


         This adds \".c.com"\ to any domain that matches \"*.a.b"\.
         If you want to do this in conjunction with a \%dnslookup%\ router, the
         \widen_domains\ option of that router may be another way of achieving
         what you want.



  Q0204: Every system has a \"nobody"\ account under which httpd etc run. I would
         like to know how to restrict mail which comes from that account to users
         on that host only.


A0204: Set up a first router like this:

  ==>      fail_nobody:
              driver = redirect
              senders = nobody@???
              domains = ! +local_domains
              allow_fail
              data = :fail: Nobody may not mail off-site


         This assumes you have defined \+local_domains\ as in the default
         configuration.



  Q0205: How can I get Exim to deliver to me locally and everyone else at the same
         domain via SMTP to the MX record specified host?


  A0205: Create an \%accept%\ router to pick off the one address and pass it to
         an appropriate transport. Put this router before the one that does MX
         routing:


  ==>      me:
             driver = accept
             domains = dom.com
             local_parts = me
             transport = local_delivery


         In the transport you will have to specify the \user\ option. An
         alternative way of doing this is to add a condition to the router that
         does MX lookups to make it skip your address. Subsequent routers can then
         deliver your address locally. You'll need a condition like this:


  ==>      condition = \
             ${if and {{eq{$domain}{dom.com}}{eq{$local_part}{me}}}{no}{yes}}



  Q0206: How can I get Exim to deliver certain domains to a different SMTP port
         on my local host?


  A0206: You must set up a special \%smtp%\ transport, where you can specify the
         \port\ option, and then set up a router to route the domains to that
         transport. There are two possibilities for specifying the host:


         (1) If you use a \%manualroute%\ router, you can specify the local host
             in the router options. You must also set


  ==>          self = send


             so that it does not object to sending to the local host.


         (2) If you use a router that cannot specify hosts (for example, an
             \%accept%\ router with appropriate conditions), you have to specify
             the host using the \hosts\ option of the transport. In this case,
             you must also set \allow_localhost\ on the transport.



  Q0207: Why does Exim lower-case the local-part of a non-local domain when
         routing?


  A0207: Because \caseful_local_part\ is not set (in the default configuration)
         for the \%dnslookup%\ router. This does not matter because the local
         part takes no part in the routing, and the actual local part that is
         sent out in the RCPT command is always the original local part.




3. ROUTING TO REMOTE HOSTS

  Q0301: What do \*lowest numbered MX record points to local host*\ and \*remote
         host address is the local host*\ mean?


  A0301: They mean exactly what they say. Exim expected to route an address to a
         remote host, but the IP address it obtained from a router was for the
         local host. If you really do want to send over TCP/IP to the local host
         (to a different version of Exim or another MTA, for example), see Q0206.


         More commonly, these errors arise when Exim thinks it is routing some
         foreign domain. For example, the router configuration causes Exim to
         look up the domain in the DNS, but when Exim examines the DNS output,
         either the lowest numbered MX record points at the local host, or there
         are no MX records, and the address record for the domain contains an
         IP address that belongs to the local host.


         There has been a rash of instances of domains being deliberately set up
         with MX records pointing to \"localhost"\ (or other names with A records
         that specify 127.0.0.1), which causes this behaviour. You can use the
         \ignore_target_hosts\ option to get Exim to ignore these records. The
         default contiguration does this. For more discussion, see Q0319. For
         other cases:


         (1) If the domain is meant to be handled as a local domain, there
             is a problem with the configuration, because it should not then have
             been looked up in the DNS. Check the \domains\ settings on your
             routers.


         (2) If the domain is one for which the local host is providing a
             relaying service (called ``mail hubbing''), possibly as part of a
             firewall, you need to set up a router to tell Exim where to send
             messages addressed to this domain, because the DNS directs them to
             the local host. You should put a router like this one before the one
             that does DNS lookups:


  ==>          hubbed_hosts:
                 driver = manualroute
                 transport = remote_smtp
                 route_list = see discussion below


             The contents of the \route_list\ option depend on how many hosts you
             are hubbing for, and how their names are related to the domain name.
             Suppose the local host is a firewall, and all the domains in
             \(*.foo.bar)\ have MX records pointing to it, and each domain
             corresponds to a host of the same name. Then the setting could be


  ==>          route_list = *.foo.bar $domain


             If there isn't a convenient relationship between the domain names
             and the host names, you either have to list each domain separately,
             or use a lookup expansion to look up the host from the domain, or
             put the routing information in a file and use the \route_data\
             option with a lookup expansion.


         (3) If neither (1) nor (2) is the case, the lowest numbered MX record or
             the address record for the domain should not be pointing to your
             host. You should arrange to get the DNS mended.



  Q0302: Why does Exim say \*all relevant MX records point to non-existent hosts*\
         when MX records point to IP addresses?


  A0302: MX records cannot point to IP addresses. They are defined to point to
         host names, so Exim always interprets them that way. (An IP address is a
         syntactically valid host name.) The DNS for the domain you are having
         problems with is misconfigured.


         However, it appears that more and more DNS zones are breaking the rules
         and putting IP addresses on the RHS of MX records. Exim follows the
         rules and rejects this, but other MTAs do support it, so the
         \allow_mx_to_ip\ was regretfully added at release 3.14 to permit this
         heinous activity.



  Q0303: How do I configure Exim to send all messages to a central server? I
         don't want to do any local deliveries at all on this host.


A0303: Use this as your first and only router:

  ==>      send_to_gateway:
             driver = manualroute
             transport = remote_smtp
             route_list = * central.server.host



Q0304: How do I configure Exim to send all non-local mail to a gateway host?

  A0304: Replace the \%dnslookup%\ router in the default configuration with the
         following:


  ==>      send_to_gateway:
             driver = manualroute
             domains = !+local_domains
             transport = remote_smtp
             route_list = * gate.way.host


         If there are several hosts you can send to, you can specify them as a
         colon-separated list.



  Q0305: How can I arrange for mail on my local network to be delivered directly
         to the relevant hosts, but all other mail to be sent to my ISP's mail
         server? The local hosts are all DNS-registered and behave like normal
         Internet hosts.


  A0305: Set up a first router to pick off all the domains for your local
         network. There are several ways you might do this. For example


  ==>      local_network:
             driver = dnslookup
             transport = remote_smtp
             domains = *.mydomain.com


         This does a perfectly conventional DNS routing operation, but only for
         the domains that match \(*.mydomain.com)\. Follow this with a `smart
         host' router:


  ==>      internet:
             driver = manualroute
             domains = !+local_domains
             transport = remote_smtp
             route_list = * mail.isp.net


         This routes any other non-local domains to the smart host.



  Q0306: How do I configure Exim to send all non-local mail to a central server
         if it cannot be immediately delivered by my host? I don't want to have
         queued mail waiting on my host.


A0306: Add to the \%remote_smtp%\ transport the following:

  ==>      fallback_hosts = central.server.name(s)


         If there are several names, they must be separated by colons.



  Q0307: The \route_list\ setting \"^foo$:^bar$ $domain"\ in a \%manualroute%\
         router does not work.


  A0307: The first thing in a \route_list\ item is a single pattern, not a list of
         patterns. You need to write that as \"^(foo|bar)$ $domain"\.
         Alternatively, you could use several items and write


  ==>      route_list = foo $domain; bar $domain


         Note the semicolon separator. This is because the second thing in each
         item can itself be a list - of hosts.



  Q0308: I have a domain for which some local parts must be delivered locally,
         but the remainder are to be treated like any other remote addresses.


  A0308: One possible way of doing this is as follows: Assuming you are using a
         configuration that is similar to the default one, first exclude your
         domain from the first router by changing it to look like this:


  ==>      non_special_remote:
             driver = dnslookup
             domains = ! +local_domains : ! special.domain
             transport = remote_smtp
             ignore_target_hosts = 127.0.0.0/8
             no_more


         Then add a second router which handles the local parts that are not to
         be delivered locally:


  ==>      special_remote:
             driver = dnslookup
             domains = special.domain
             local_parts = ! lsearch;/list/of/special/localparts
             transport = remote_smtp
             ignore_target_hosts = 127.0.0.0/8
             no_more


         The remaining local parts will fall through to the remaining routers,
         which can delivery them locally.



  Q0309: How can I configure Exim on a firewall machine so that if mail arrives
         addressed to a domain whose MX points to the firewall, it is forwarded
         to the internal mail server, without having to have a list of all the
         domains involved?


  A0309: As your first router, have the standard \%dnslookup%\ router from the
         default configuration, with the added option


  ==>      self = pass


         This will handle all domains whose lowest numbered MX records do not
         point to your host. Because of the \no_more\ setting, if it encounters
         an unknown domain, routing will fail. However, if it hits a domain whose
         lowest numbered MX points to your host, the \self\ option comes into
         play, and overrides \no_more\. The \"pass"\ setting causes it to pass
         the address on to the next router. (The default causes it to generate an
         error.)


         The only non-local domains that reach the second router are those with
         MX records pointing to the local host. Set it up to send them to the
         internal mail server like this:


  ==>      internal:
             driver = manualroute
             domains = ! +local_domains
             transport = remote_smtp
             route_list = * internal.server



  Q0310: If a DNS lookup returns no MX records why doesn't Exim just bin the
         message?


  A0310: If a DNS lookup returns no MXs, Exim looks for an address record, in
         accordance with the rules that are defined in the RFCs. If you want to
         break the rules, you can set \mx_domains\ in the \%dnslookup%\ router, but
         you will cut yourself off from those sites (and there still seem to be
         plenty) who do not set up MX records.



  Q0311: When a DNS lookup for MX records fails to complete, why doesn't Exim
         send the messsage to the host defined by the A record?


  A0311: The RFCs are quite clear on this. Only if it is known that there are no
         MX records is an MTA allowed to make use of the A record. When an MX
         lookup fails to complete, Exim does not know whether there are any MX
         records or not. There seem to be some name servers (or some
         configurations of some name servers) that give a ``server fail'' error when
         asked for a non-existent MX record. Exim uses standard resolver calls,
         which unfortunately do not distinguish between this case and a timeout,
         so all Exim can do is try again later.



  Q0312: Is it possible to use a conditional expression for the host item in a
         \route_list\ for \%manualroute%\ router? I tried the following, but it
         doesn't work:


  ==>      route_list = * ${if match{$header_from:}{\N.*\.usa\.net$\N} \
                        {<smarthost1>}{<smarthost2>}


  A0312: The problem is that the second item in \route_list\ contains white
         space, which means that it gets terminated prematurely. To avoid this,
         you must put the second item in quotes:


  ==>      route_list = * "${if match{$header_from:}{\N.*\.usa\.net$\N} \
                        {<smarthost1>}{<smarthost2>}}"



  Q0313: I send all external mail to a smart host, but this means that bad
         addresses also get passed to the smart host. Can I avoid this?


  A0313: Assuming you have DNS availability, set up a conventional \%dnslookup%\
         router to do the routing, but in the \%remote_smtp%\ transport set this:


  ==>    hosts = your.smart.host
         hosts_override


         This will override the hosts that the router finds so that everything
         goes to the smart host, but any non-existent domains will be failed by
         the router.



  Q0314: I have a really annoying intermittent problem where attempts to mail to
         valid sites are rejected with \*unknown mail domain*\. This only happens a
         few times a day and there is no particular pattern to the sites it
         rejects. If I try to lookup the same domain a few minutes later then it
         is OK.


  A0314: This is almost certainly a problem with the DNS resolver or the the
         domain's name servers.


         (1) Have you linked Exim against the newest DNS resolver library that
         comes with Bind? If you are using SunOS4 that may be your problem, as
         the resolver that comes with that OS is known to be buggy and to give
         intermittent false negatives.


         (2) Effects like this are sometimes seen if a domain's name servers get
         out of step with each other.



  Q0315: I'd like route all mail with addresses that can't be resolved (the DNS
         lookup times out) to a relay machine.


  A0315: Set \pass_on_timeout\ on your \%dnslookup%\ router, and add below it a
         \%manualroute%\ router that routes all relevant domains to the relay.



  Q0316: I would like to forward all incoming email for a particular domain to
         another host via SMTP. Whereabouts would I configure that?


A0316: Use this as your first router:

  ==>      special:
             driver = manualroute
             transport = remote_smtp
             route_list = the.particular.domain the.other.host


         You will also need to adjust the ACL for incoming SMTP so that this
         domain is accepted for relaying. If you are using the default
         configuration, there is a domain list called \relay_domains\ that is
         set up for this.



  Q0317: What I'd like to do is have alternative smart hosts, where the one to be
         used is determined by which ISP I'm connected to.


  A0317: The simplest way to do this is to arrange for the name of the smart host
         du jour to be placed in a file when you connect, say \(/etc/smarthost)\.
         Then you can read this file from a \%manualroute%\ router like this:


  ==>      smarthost:
             driver = manualroute
             transport = remote_smtp
             route_list = * ${readfile{/etc/smarthost}{}}


         The second argument of the \"readfile"\ item is a string that replaces
         any newline characters in the file (in this case, with nothing).
         By keeping the data out of the main configuration file, you avoid having
         to HUP the daemon when it changes.



Q0318: Exim won't route to a host with no MX record.

A0318: More than one thing may cause this.

         (1) Are you sure there really is no MX record? Sometimes a typo results
         in a malformed MX record in the zone file, in which case some name
         servers give a SERVFAIL error rather than NXDOMAIN. Exim has to treat
         this as a temporary error, so it can't go on to look for address records.
         You can check for this state using one of the DNS interrogation commands,
         such as \(nslookup)\, \(host)\, or \(dig)\.


         (2) Is there a wildcard MX record for \(your)\ domain? Is the
         \search_parents\ option on in your \%dnslookup%\ router? If the answer to
         both these questions is ``yes'', that is the cause of the problem. When
         the DNS resolver fails to find the MX record, it tries adding on your
         domain if \search_parents\ is true, and thereby finds your wildcard MX
         record. For example:


           .  There is a wildcard MX record for \(*.a.b.c)\.


           .  There is a host called \(x.y.z)\ that has an A record and no MX record.


           .  Somebody on the host \(m.a.b.c)\ domain tries to mail to \(user@???)\.


           .  Exim calls the DNS to look for an MX record for \(x.y.z)\.


           .  The DNS doesn't find any MX record. Because \search_parents\ is true,
              it then tries searching the current host's parent domain, so it
              looks for \(x.y.z.a.b.c)\ and picks up the wildcard MX record.


         Setting \search_parents\ false makes this case work while retaining the
         wildcard MX record. However, anybody on the host \(m.a.b.c)\ who mails to
         \(user@???)\ (expecting it to go to \(user@???)\) now has a problem. The
         \widen_domains\ option of the \%dnslookup%\ router may be helpful in this
         circumstance.



  Q0319: I have some mails on my queues that are sticking around longer than
         the retry time indicates they should. They are all getting frozen
         because some remote admin has set their MX record to 127.0.0.1.


  A0319: The admin in question is an idiot. Exim will always freeze such messages
         because they are apparently routed to the local host. To bounce these
         messages immediately, set


  ==>      ignore_target_hosts = 127.0.0.1


         on the \%dnslookup%\ router. This causes Exim to completely ignore any hosts
         with that IP address. In fact, there are quite a number of IP addresses
         that should never be used. Here is a suggested configuration list for
         the IPv4 ones:


  ==>      # Don't allow domains whose single MX (or A) record is a
           # "special-use IPv4 address", as listed in RFC 3330.
           ignore_target_hosts = \
             # Hosts on "this network"; RFC 1700 (page 4) states that these
             # are only allowed as source addresses
             0.0.0.0/8 : \
             # Private networks, RFC 1918
             10.0.0.0/8 : 172.16.0.0/12 : 192.168.0.0/16 : \
             # Internet host loopback address, RFC 1700 (page 5)
             127.0.0.0/8 : \
             # "Link local" block
             169.254.0.0/16 : \
             # "TEST-NET" - should not appear on the public Internet
             192.0.2.0/24 : \
             # 6to4 relay anycast addresses, RFC 3068
             192.88.99.0/24 : \
             # Network interconnect device benchmark testing, RFC 2544
             198.18.0.0/15 : \
             # Multicast addresses, RFC 3171
             224.0.0.0/4 : \
             # Reserved for future use, RFC 1700 (page 4)
             240.0.0.0/4



  Q0320: How can I arrange for all mail to \*user@???*\ to be forwarded
         to \*user@???*\?


A0320: Put this as your first router:

  ==>      forward:
             driver = redirect
             domains = some.domain
             data = ${quote:$local_part}@???



  Q0321: How can I tell an Exim router to use only IPv4 or only IPv6 addresses
         when it finds both types in the DNS?


  A0321: You can do this by making it ignore the addresses you don't want. This
         example ignores all IPv6 addresses and all IPv4 addresses in the 127
         network:


  ==>      ignore_target_hosts = <; 0000::0000/0 ; 127.0.0.0/8


         To ignore all IPv4 addresses, use


  ==>      ignore_target_hosts = 0.0.0.0/0


         See Q0319 for a general discussion of \ignore_target_hosts\.



  Q0322: How can I reroute all messages bound for 192.168.10.0 and 10.0.0.0 to
         a specific mail server?


  A0322: That is an odd requirement. However, there is an obscure feature in
         Exim, originally implemented for packet radio people, that perhaps can
         help. Check out the \translate_ip_address\ generic router option.




4. ROUTING FOR LOCAL DELIVERY

  Q0401: I need to have any mail for \(virt.dom.ain)\ that doesn't match one of the
         aliases in \(/usr/lib/aliases.virt)\ delivered to a particular address, for
         example, \(postmaster@???)\.


  A0401: Adding an asterisk to a search type causes Exim to look up ``*'' when the
         normal lookup fails. So if your aliasing router is something like this:


  ==>      virtual:
             driver = redirect
             domains = virt.dom.ain
             data = ${lookup{$local_part}lsearch{/usr/lib/aliases.virt}}
             no_more


         you should change \"lsearch"\ to \"lsearch*"\, and put this in the alias
         file:


  ==>      *: postmaster@???


         This solution has the feature that if there are several unknown
         addresses in the same message, only one copy gets sent to the
         postmaster, because of Exim's normal de-duplication rules.


         NOTE: This solution works only if there is also an entry for \(postmaster)\
         in the alias file, ultimately resolving to an address that is not in
         \(virt.dom.ain)\. See also Q0434.



  Q0402: How do I arrange for all incoming email for \(*@some.domain)\ to go into one
         pop3 mail account? The customer doesn't want to add a list of specific
         local parts to the system.


A0402: Set up a special transport that writes to the mailbox like this:

  ==>      special_transport:
             driver = appendfile
             file = /pop/mailbox
             envelope_to_add
             return_path_add
             delivery_date_add
             user = exim


         The file will be written as the user \"exim"\. Then arrange to route all
         mail for that domain to that transport, with a router like this:


  ==>      special_router:
             driver = accept
             domains = some.domain
             transport = special_transport



  Q0403: How do I configure Exim to send messages for unknown local users to a
         central server?


  A0403: Assuming you are using something like the default configuration, where
         local users are processed by the later routers, you should add the
         following router at the end:


  ==>      unknown:
             driver = manualroute
             transport = remote_smtp
             route_list = * server.host.name
             no_verify


         However, you should if possible try to verify that the user is known on
         the central server before accepting the message in the first place. This
         can be done by making use of Exim's ``call forward'' facility.



  Q0404: How can I arrange for messages submitted by (for example) Majordomo to
         be handled specially?


A0404: You can use the \condition\ option on a router, with a setting such as

  ==>      condition = ${if and {{eq {$sender_host_address}{}} \
                       {eq {$sender_ident}{majordom}}} {yes}{no}}


         This first tests for a locally-submitted message, by ensuring there is
         no sending host address, and then it checks the identity of the user
         that ran the submitting process.



  Q0405: On a host that accepts mail for several domains, do I have to use fully
         qualified addresses in \(/etc/aliases)\ or do I have to set up an alias
         file for each domain?


A0405: You can do it either way. The default aliasing router contains this line:

  ==>      data = ${lookup{$local_part}lsearch{/etc/aliases}}


         which is what does the actual lookup. To make it look up the complete
         address instead of just the local part, use


  ==>      data = ${lookup{$local_part@$domain}lsearch{/etc/aliases}}


         If you want to use a separate file for each domain, use


  ==>      data = ${lookup{$local_part}lsearch{/etc/aliases/$domain}}



  Q0406: Some of my users are using the \(.forward)\ to pipe to a shell command which
         appends to the user's INBOX. How can I forbid this?


  A0406: If you allow your users to run shells in pipes, you cannot control which
         commands they run or which files they write to. However, you should point
         out to them that writing to an INBOX by arbitrary commands is not
         interlocked with the MTA and MUAs, and is liable to mess up the contents
         of the file.


         If a user simply wants to choose a specific file for the delivery of
         messages, this can be done by putting a file name in a \(.forward)\ file
         rather than using a pipe, or by using the \"save"\ command in an Exim
         filter file.


         You can set \forbid_pipe\ on the router, but that will prevent them from
         running any pipe commands at all. Alternatively, you can restrict which
         commands they may run in their pipes by setting the \allow_commands\
         and/or \restrict_to_path\ options in the \%address_pipe%\ transport.



  Q0407: How can I arrange for a default value when using a query-style lookup
         such as LDAP or NIS+ to handle aliases?


  A0407: Use a second query in the failure part of the original lookup, like
         this:


  ==>      data = ${lookup ldap\
             {ldap://x.y.z/l=yvr?aliasaddress?sub?(&(mail=$local_part@$domain))}\
             {$value}\
             {\
             ${lookup ldap \
               {ldap://x.y.z/l=yvr?aliasaddress?sub?(&(mail=default@$domain))}}\
             }}


          Of course, if the default is a fixed value you can just include it
          directly.



  Q0408: If I don't fully qualify the addresses in a virtual domain's alias file
         then mail to aliases which also match the local domain get delivered to
         the local domain.


A0408: Set the \qualify_preserve_domain\ option on the \%redirect%\ router.


  Q0409: I want mail for any local part at certain virtual domains to go
         to a single address for each domain.


A0409: One way to to this is

  ==>      virtual:
             driver = redirect
             data = ${lookup{$domain}lsearch{/etc/virtual}}


         The \(/etc/virtual)\ file contains a list of domains and the addresses to
         which their mail should be sent. For example:


  ==>       domain1:  postmaster@???
            domain2:  joe@???


         If the number of domains is large, using a DBM or cdb file would be more
         efficient. If the lookup fails to find the domain in the file, the value
         of the \data\ option is empty, causing the router to decline.



Q0410: How can I make Exim look in the alias NIS map instead of \(/etc/aliases)\?

  A0410: The default configuration does not use NIS (many hosts don't run it).
         You need to change this line in the \%system_aliases%\ router:


  ==>      data = ${lookup{$local_part}lsearch{/etc/aliases}}


         Change it to


  ==>      data = ${lookup{$local_part}nis{mail.aliases}}


         If you want to use \(/etc/aliases)\ as well as NIS, put this router (with
         a different name) before or after the default one, depending on which
         data source you want to take precedence.



  Q0411: Why will Exim deliver a message locally to any username that is longer
         than 8 characters as long as the first 8 characters match one of the
         local usernames?


  A0411: The problem is in your operating system. Exim just calls the \^^getpwnam()^^\
         function to test a local part for being a local login name. It does not
         presume to guess the maximum length of user name for the underlying
         operating system. Many operating systems correctly reject names that are
         longer than the maximum length; yours is apparently deficient in this
         regard. To cope with such systems, Exim has an option called
         \max_user_name_length\ which you can set to the maximum allowed length.



  Q0412: Why am I seeing the error \*bad mode (100664) for /home/test/.forward*\?
         I've looked through the documentation but can't see anything to suggest
         that Exim has to do anything other than read the \(.forward)\ file.


  A0412: For security, Exim checks for mode bits that shouldn't be set, by
         default 022. You can change this by setting the \modemask\ option of the
         \%redirect%\ router that is handling \(.forward)\ files.



  Q0413: When a user's \(.forward)\ file is syntactially invalid, Exim defers
         delivery of all messages to that user, which sometimes include the
         user's own test messages. Can it be told to ignore the \(.forward)\ file
         and/or inform the user of the error?


  A0413: Setting \skip_syntax_errors\ on the redirect router causes syntax
         errors to be skipped. When dealing with users' \(.forward)\ files it is best
         to combine this with a setting of \syntax_errors_to\ in order to send
         a message about the error to the user. However, to avoid an infinite
         cascade of messages, you have to be able to send to an address that
         bypasses \(.forward)\ file processing. This can be done by including a
         router like this one


  ==>      real_localuser:
             driver = accept
             check_local_user
             transport = local_delivery
             prefix = real-


         before the \%redirect%\ router that handles \(.forward)\ files. This will
         do an ordinary local delivery without \(.forward)\ processing, if the
         local part is prefixed by \"real-"\. You can then set something like
         the following options on the \%redirect%\ router:


  ==>      skip_syntax_errors
           syntax_errors_to = real-$local_part@$domain
           syntax_errors_text = "\
             This is an automatically generated message. An error has been \
             found\nin your .forward file. Details of the error are reported \
             below. While\nthis error persists, messages addressed to you will \
             get delivered into\nyour normal mailbox and you will receive a \
             copy of this message for\neach one."


         A final tidying setting to go with this is a rewriting rule that changes
         \"real-username"\ into just \"username"\ in the headers of the message:


  ==>      \N^real-([^@]+)@your\.dom\.ain$\N    $1@???   h


         This means that users won't ever see the \"real-"\ prefix, unless they
         look at the ::Envelope-To:: header.



  Q0414: I have set \caseful_local_part\ on the routers that handle my local
         domain because my users have upper case letters in their login names,
         but incoming mail now has to use the correct case. Can I relax this
         somehow?


  A0414: If you really have to live with caseful user names but want incoming
         local parts to be caseless, then you have to maintain a file, indexed by
         the lower case forms, that gives the correct case for each login, like
         this:


  ==>      admin:    Admin
           steven:   Steven
           mcdonald: McDonald
           lamanch:  LaManche
           ...


         and at the start of the routers that handle your local domain, put one
         like this:


  ==>      set_case_router:
             driver = redirect
             data = ${lookup{${lc:$local_part}}lsearch{/the/file}}
             qualify_preserve_domain


         For efficiency, you should also set the \redirect_router\ option to cause
         processing of the changed address to begin at the next router. If you
         are otherwise using the default configuration, the setting would be


  ==>      redirect_router = system_aliases


         If there are lots of users, then a DBM or cdb file would be more
         efficient than a linear search. If you are handling several domains,
         you will have to extend this configuration to cope appropriately.



  Q0415: Can I use my existing alias files and forward files as well as procmail
         and effectively drop in Exim in place of Sendmail ?


  A0415: Yes, as long as your alias and forward files don't assume that pipes are
         going to run under a shell. If they do, you either have to change them,
         or configure Exim to use a shell (which it doesn't by default).



  Q0416: What is quickest way to set up Exim so any message sent to a
         non-existing user would bounce back with a different message, based
         on the name of non-existing user?


  A0416: Place this router last, so that it catches any local addresses that
         are not otherwise handled:


  ==>      non_exist:
             driver = accept
             transport = non_exist_reply
             no_verify


         Then add the following transport to the transports section:


  ==>      non_exist_reply:
             driver = autoreply
             user = exim
             to = $sender_address
             subject = User does not exist
             text = You sent mail to $local_part. That's not a valid user here. \
                    The subject was: $subject.


         If you want to pick up a message from a file, you can use the \file\
         option (use \file_expand\ if you want its contents expanded).



  Q0417: What do I need to do to make Exim handle \(/usr/ucb/vacation)\ processing
         automatically, so that people could just create a \(.vacation.msg)\ file in
         their home directory and not have to edit their \(.forward)\ file?


  A0417: Add a new router like this, immediately before the normal \%localuser%\
         router:


  ==>      vacation:
             driver = accept
             check_local_user
             require_files = $home/.vacation.msg
             transport = vacation_transport
             unseen


         and a matching new transport like this:


  ==>      vacation_transport:
             driver = pipe
             command = /usr/ucb/vacation $local_part


         However, some versions of \(/usr/ucb/vacation)\ do not work properly unless
         the DBM file(s) it uses are created in advance - it won't create them
         itself. You also need a way of removing them when the vacation is over.


         Another possibility is to use a fixed filter file which is run whenever
         \(.vacation.msg)\ exists, for example:


  ==>      vacation:
             driver = redirect
             check_local_user
             require_files = $home/.vacation.msg
             file = /some/central/filter
             allow_filter


         The filter file should use the \"if personal"\ check before sending mail,
         to avoid generating automatic responses to mailing lists. If sending a
         message is all that it does, this doesn't count as a ``significant''
         delivery, so the original message goes on to be delivered as normal.


         Yet another possibility is to make use of Exim's \%autoreply%\ transport,
         and not use \(/usr/ucb/vacation)\ at all.



  Q0418: I want to use a default entry in my alias file to handle unknown local
         parts, but it picks up the local parts that the aliases generate. For
         example, if the alias file is


  ==>      luke.skywalker: luke
           ls: luke
           *: postmaster


         then messages addressed to \/luke.skywalker/\ end up at \/postmaster/\.


  A0418: The default mechanism works best with virtual domains, where the
         generated address is not in the same domain. If you just want to pick up
         all unknown local parts and send them to postmaster, an easier way to do
         it is to put this as your last router:


  ==>      unknown:
             driver = redirect
             data = postmaster
             no_verify


         Another possibility is to put the redirect router for these aliases
         after all the other routers, so that local parts which are user names
         get picked off first. You will need to have two aliasing routers if
         there are some local parts (e.g. \/root/\) which are login names, but which
         you want to handle as aliases.



  Q0419: I have some obsolete domains which people have been warned not to use
         any more. How can I arrange to delete any mail that is sent to them?


  A0419: To reject them at SMTP time, with a customized error message, place
         statments like this in the ACL:


  ==>      deny message = The domain $domain is obsolete
                domains = lsearch;/etc/exim/obsolete.domains


         For messages that don't arrive over SMTP, you can use a router like
         this to bounce them:


  ==>      obsolete:
             driver = redirect
             domains = lsearch;/etc/exim/obsolete.domains
             allow_fail
             data = :fail: the domain $domain is obsolete


         If you just want to throw away mail to those domains, accept them at
         SMTP time, and use a router like this:


  ==>      obsolete:
             domains = lsearch;/etc/exim/obsolete.domains
             data = :blackhole:



  Q0420: How can I arrange that mail addressed to \(anything@???)\
         gets delivered to \(something@???)\?


A0420: Set up a router like this:

  ==>      user_from_domain:
             driver = redirect
             data = ${if match{$domain}{\N^(.+)\.mydomain\.com$\N}\
               {$1@???}}



  Q0421: I can't get a regular expression to work in a \local_parts\ option on
         one of my routers.


  A0421: Have you remembered to protect any backslash and dollar characters in
         your regex from unwanted expansion? The easiest way is to use the
         \"@\N"\ facility, like this:


  ==>      local_parts = \N^0740\d{6}\N



  Q0422: How can I arrange for all addresses in a group of domains \(*.example.com)\
         to share the same alias file? I have a number of such groups.


  A0422: For a single group you could just hard wire the file name into a router
         that had


  ==>      domains = *.example.com


         set, to restrict it to the relevant domains. For a number of such groups
         you can create a file containing the domains, like this:


  ==>      *.example1.com    example1.com
           *.example2.com    example2.com
           ...


         Then create a router like this


  ==>      domain_aliases:
             driver = redirect
             domains = partial-lsearch;/that/file
             data = ${lookup{$local_part}lsearch*{/etc/aliases.d/$domain_data}}


         The variable \$domain_data$\ contains the data that was looked up when the
         \domains\ option was matched, i.e. \"example1.com"\, \"example2.com"\, etc.
         in this case.



  Q0423: Some of our users have no home directories; the field in the password
         file contains \(/no/home/dir)\. This causes the error \*failed to stat
         /no/home/dir (No such file or directory)*\ when Exim tries to look for a
         \(.forward file)\, and the delivery is deferred.


A0423: There are two issues involved here:

         (1) With the default configuration, you are asking Exim to check for a
         \(.forward)\ file in the user's home directory. If no file is found,
         Exim tries to \^^stat()^^\ the home directory. This is so that it will
         notice a missing NFS home directory, and not treat it as if the
         \(.forward)\ file did not exist. This \^^stat()^^\ is failing when the
         home directory really doesn't exist. You should arrange for the
         \%userforward%\ router not to run for these special users, by adding
         this line:


  ==>      condition = ${if eq {$home}{/no/home/dir}{no}{yes}}


         (2) If you use \check_local_user\ on another router to route to a local
         transport (again, this is what is in the default configuration), you
         will also have to specify a current directory for the transport, because
         by default it makes the home directory current. This is easily done by
         adding


  ==>      current_directory = /


         to the transport or


  ==>      transport_current_directory = /


         to the router. Or you can add \home_directory\ to the transport, because
         the current directory defaults to the home directory.



  Q0424: How can I disable Exim's de-duplication features? I want it to do two
         deliveries if two different aliases expand to the same address.


  A0424: This is not possible. Duplication has other ramifications other than
         just (in)convenience. Consider:


           . Message is addressed to A and to B.


           . Both A and B are aliased to C.


           . Without de-duplication, two deliveries to C are scheduled.


           . One delivery happens, Exim records that it has delivered the message
             to C.


           . The next delivery fails (C's mailbox is over quota, say).


         Next time round, Exim wants to know if it has already delivered to C or
         not, before scheduling a new delivery. Has it? Obviously, if duplicate
         deliveries are supported, it has to remember not only that it has
         delivered to C but also the ``history'' of how that delivery happened - in
         effect an ancestry list back to the original envelope address. This it
         does not do, and changing it to work in that way would be a lot of work
         and a big upheaval.


         The best way to get duplicate deliveries if you want them is not to use
         aliases, but to route the addresses directly to a transport, e.g.


  ==>    duplicates:
           driver = accept
           local_parts = lsearch;/etc/list/of/special/local/parts
           transport = local_delivery
           user = exim



  Q0425: My users' mailboxes are distributed between several servers according to
         the first letter of the user name. All the servers receive incoming mail
         at random. I would like to have the same configuration file for all the
         servers, which does local delivery for the mailboxes it holds, and sends
         other addresses to the correct other server. Is this possible?


  A0425: It is easiest if you arrange for all the users to have password entries
         on all the servers. This means that non-existent users can be detected
         at the first server they reach. Set up a file containing a mapping from
         the first letter of the user names to the servers where their mailboxes
         are held. For example:


  ==>      a: server1
           b: server1
           c: server2
           ...


         Before the normal \%localuser%\ router, place the following router:


  ==>      mailbox_host:
             driver = manualroute
             check_local_user
             transport = remote_smtp
             route_list = * ${lookup{${substr_0_1:$local_part}}lsearch{/etc/mapfile}}
             self = pass


         This router checks for a local account, then looks up the host from the
         first character of the local part. If the host is not the local host,
         the address is routed to the \%remote_smtp%\ transport, and sent to the
         correct host. If the host is the local host, the \self\ option causes
         the router to pass the address to the next router, which does a local
         delivery.


         The router is skipped for local parts that are not the names of local
         users, and so these addresses fail.



  Q0426: One of the things I want to set up is for \(anything@onedomain)\ to forward
         to \(anything@anotherdomain)\. I tried adding \($local_part@anotherdomain)\ to
         my aliases but it did not expand - it sent it to that literal address.


  A0426: If you want to do it that way, you can use the \"expand"\ operator on
         the lookup used in the data option of the redirect router. For example:


  ==>      data = ${expand:${lookup{$local_part}lsearch*{/etc/aliases}}}


         Another approach is to use a router like this:


  ==>      forwarddomain:
             driver = redirect
             domains = onedomain
             data = $local_part@anotherdomain


         The value of \data\ can, of course, be more complicated, involving
         lookups etc. if you have lots of different cases.



  Q0427: How can I have an address looked up in two different alias files, and
         delivered to all the addresses that are found?


A0427: Use a router like this:

  ==>      multi_aliases:
             driver = redirect
             data = ${lookup{$local_part}lsearch{/etc/aliases1}\
               {$value${lookup{$local_part}lsearch{/etc/aliases2}{,$value}}}\
               {${lookup{$local_part}lsearch{/etc/aliases2}{$value}fail}}}\


         If the first lookup succeeds, the result is its data, followed by the
         data from the second lookup, if any, separated by a comma. If the first
         lookup fails, the result is the data from the third lookup (which also
         looks in the second file), but if this also fails, the entire expansion
         is forced to fail, thereby causing the router to decline.


         Another approach is to use two routers, with the first re-generating the
         original local part when it succeeds. This won't get processed by the
         same router again. For example:


  ==>      multi_aliases1:
             driver = redirect
             data = ${lookup{$local_part}lsearch{/etc/aliases1}{$value,$local_part}}


  ==>      multi_aliases2:
             data = ${lookup{$local_part}lsearch{/etc/aliases2}}


         This scales more easily to three or more alias files.



  Q0428: I've converted from Sendmail, and I notice that Exim doesn't make use
         of the \"owner-"\ entries in my alias file to change the sender address in
         outgoing messages to a mailing list.


A0428: If you have an alias file with entries like this:

  ==>      somelist:        a@b, c@d, ...
           owner-somelist:  postmaster


         Sendmail assumes that the second entry specifies a new sender address
         for the first. Exim does not make this assumption. However, you can make
         it take the same action, by adding


  ==>      errors_to = owner-$local_part@???


         to the configuration for your aliasing router. This is fail-safe,
         because Exim verifies a new sender address before using it. Thus, the
         change of sender address occurs only when the owner entry exists.



  Q0429: I would like to deliver mail addressed to a given domain to local
         mailboxes, but also to generate messages to the envelope senders.


  A0429: You can do this with an ``unseen'' router and an \%autoreply%\ transport,
         along the following lines:


  ==>      # Router
           auto_warning_r:
             driver = accept
             check_local_user
             domains = <domains you want to do this for>
             condition = ${if eq{$sender_address}{}{no}{yes}}
             transport = warning_t
             no_verify
             unseen


         Place this router immediately before the normal \%localuser%\ router. The
         \unseen\ option means that the address is still passed on to the next
         router. The transport is configured like this:


  ==>      # Transport
           warning_t:
             driver = autoreply
             file = /usr/local/mail/warning.txt
             file_expand
             from = postmaster@???
             to = $sender_address
             user = exim
             subject = Re: Your mail to $local_part@$domain


         Note the use of the \condition\ option to avoid attempting to send a
         message when there is no sender (that is, when the incoming message is a
         bounce message). You can of course extend this to include other
         conditions. If you want to log the sending of messages, you can add


  ==>      log = /some/file


         to the transport and also make use of the \once\ option if you want to
         send only one message to each sender.



  Q0430: Whenever Exim tries to route a local address, it gives a permission
         denied error for the \(.forward)\ file, like this:


  ==>      1998-08-10 16:55:32 0z5y2W-0000B8-00 == xxxx@??? <xxxx@???>
             D=userforward defer (-1): failed to open /home/xxxx/.forward
             (userforward router): Permission denied (euid=1234 egid=101)


A0430: Have you remembered to make Exim setuid \/root/\?


  Q0431: How do I configure Exim to allow arbitrary extensions in local parts, of
         the form \/+extension/\?


A0431: Add this pre-condition to the relevant router:

  ==>      local_part_suffix = +*


         If you want the extensions to be optional, also add the option


  ==>      local_part_suffix_optional


         When the router runs, \$local_part$\ contains the local part with the
         extension removed, and the extension (if any) is in \$local_part_suffix$\.
         If you have set \check_local_user\, the test is carried out after the
         extension is removed.



  Q0432: I use NIS for my user data. How can I stop Exim rejecting mail when my
         NIS servers are being restarted?


  A0432: Exim doesn't know that you are using NIS; it just calls the \^^getpwnam()^^\
         function, which is routed by nsswitch. Unfortunately, \^^getpwnam()^^\
         was never designed to be routed through NIS, and it returns NULL if the
         entry is not found or if the connection to the NIS server fails. This
         means that Exim cannot tell the difference between ``no such user'' and
         ``NIS is down''.


         Crutches to help with this problem are \finduser_retries\ in Exim, and
         \^nscd^\ on the Unix side, but they are not perfect, and mail can still
         be lost. However, Nico Erfurth pointed out that you can create a router
         for Exim that tests for the availability of NIS, and force a defer if
         NIS is not running:


  ==>      check_nis:
              driver = redirect
              data = ${lookup {$local_part} nis {passwd}{}}


         This should be placed before any router that makes any use of NIS,
         typically at the start of your local routers. How does it work? If
         your NIS server is reachable, the lookup will take place, and whether it
         succeeds or fails, the result is an empty strting. This causes the
         router to decline, and the address is passed to the following routers.
         If your NIS server is down, the lookup defers, and this causes the
         router to defer. A verification of an incoming address gets a temporary
         rejection, and a delivery is deferred till later.



  Q0433: How can I arrange for a single address to be processed by \*both*\
         \%redirect%\ \*and*\ \%accept%\?


A0433: Check out the \unseen\ option.


  Q0434: How can I redirect all local parts that are not in my system aliases to
         a single address? I tried using an asterisk in the system alias file
         with an \"lsearch*"\ lookup, but that send \*all*\ messages to the
         default address.


  A0434: If your alias file generates addresses in the local domain, they are
         also processed as a potential aliases. For example, suppose this is your
         alias file:


  ==>      caesar:   jc
           anthony:  ma
           *:        brutus


         The local part \/caesar/\ is aliased to \/jc/\, but that address is then
         reprocessed by the routers. As the address is in the local domain, the
         alias file is again consulted, and this time the default matches. In
         fact after the second aliasing, \/brutus/\ is also processed again from
         the start, and is aliased to itself. However, this happens only once,
         because the next time, Exim notices that the aliasing router has already
         processed \/brutus/\, so the router is skipped in order to avoid
         looping.


         There are several ways of solving this problem; which one you use
         depends on your aliasing data.


         (1) If the result of aliasing is always a local user name, that is,
             aliasing never generates another alias, you can use the
             \redirect_router\ option on the router to specify that processing
             the generated addresses must start at the next router. For example:


  ==>          redirect_router = userforward


             assuming that the next router is called \%userforward%\. This
             ensures that there is at most one pass through the aliasing router.


         (2) If you cannot rely on aliases generating non-aliases, it is often
             easier not to use a default alias, but instead to place a router
             such as the one below after all the other local routers (for the
             relevant domains):


  ==>          catch_unknown:
                 driver = redirect
                 domains = ...
                 data = brutus@$domain


         Note that the default aliasing technique works more successfully for
         virtual domains (see Q0401) because the generated address for the
         default is not usually in the same virtual domain as the incoming
         address.



  Q0435: My alias file contains fully qualified addresses as keys, and some
         wildcard domains in the form @foo.bar. Can Exim handle these?


A0435: You can handle fully qualified addresses with this router:

  ==>      qualified_aliases:
             driver = redirect
             data = ${lookup{$local_part@$domain}lsearch{/etc/aliases}}


         (Add any other options you need for the \%redirect%\ router.) Place this
         router either before or after the default aliases router that looks up
         the local part only. (Or, if you have no unqualified aliases, replace
         the default router.)


         To handle wildcards in the form @foo.bar you will need yet another
         router. (Wildcards of the form *@foo.bar can be handled by an lsearch*@
         lookup.) Something like this:


  ==>      wildcard_aliases:
             driver = redirect
             data = ${lookup{@$domain}lsearch{/etc/aliases}}


         Place this after the routers that handle the more specific aliases.




5. FILTERING

Q0501: My filter isn't working. How can I test it?

  A0501: Use the \-bf-\ option (\-bF-\ for a system filter) to test the basic operation
         of your filter. You can request debugging information for filtering only
         by adding \"-d-all+filter"\ to the command.



  Q0502: What I really need is the ability to obtain the result of a pipe
         command so that I can filter externally and redirect internally. Is
         this possible?


  A0502: The result of a pipe command is not available to a filter, because Exim
         does not run any actual deliveries while filtering. It just sets up
         deliveries at this time. They all actually happen later. If you want to
         run pipes and examine their results, you need to set up a single
         delivery to a delivery agent such as \^procmail^\ which provides this kind
         of facility.


         An possible alternative is to use the \"${run"\ expansion item to run an
         external command while filtering. In this case, you can make use of some
         of the results of the command.



  Q0503: I received a message with a ::Subject:: line that contained a non-printing
         character (a carriage return). This messed up my filter file. Is there a
         way to get round it?


A0503: Instead of \"$h_subject:"\ use \"${escape:$h_subject:}"\


  Q0504: I want to search for \"$"\ in the subject line, but I can't seem to get
         the syntax.


A0504: Try one of these:

  ==>      if $h_subject: contains \$ then ...
           if $h_subject: contains "\\$" then ...



  Q0505: My problem is that Exim replaces \$local_part$\ with an empty string in the
         system filtering. What's wrong or what did I miss?


  A0505: A message may have many recipients. The system filter is run just once
         at the start of a delivery attempt. Consequently, it does not make sense
         to set \$local_part$\. Which recipient should it be set to? However, you
         can access all the recipients from a system filter via the variable
         called \$recipients$\.



  Q0506: Using \$recipients$\ in a system filter gives me another problem: how can
         I do a string lookup if \$recipients$\ is a list of addresses?


  A0506: Check out the section of the filter specification called \*Testing a list of
         addresses*\. If that doesn't help, you may have to resort to calling an
         embedded Perl interpreter - but that is expensive.



  Q0507: What are the main differences between using an Exim filter and using
         \^procmail^\?


  A0507: Exim filters and \^procmail^\ provide different facilities. Exim filters run
         at routing time, before any deliveries are done. A filter is like a
         ``\(.forward)\ file with conditions''. One of the benefits is de-duplication.
         Another is that if you forward, you are forwarding the original message.


         However, this does mean that pipes etc. are not run at filtering time,
         nor can you change the headers, because the message may have other
         recipients and Exim keeps only a single set of headers.


         \^procmail^\ runs at delivery time. This is for one recipient only, and so
         it can change headers, run pipes and check the results, etc. However, if
         it wants to forward, it has to create a new message containing a copy
         of the original message.


         It's your choice as to which of these you use. You can of course use
         both.



  Q0508: How can I allow the use of relative paths in users' filter files when
         the directories concerned are not available from the password data?


  A0508: You need to be running Exim 4.11 or later. You can then specify a value
         for \$home$\ by setting the router_home_directory option on the
         \%redirect%\ router.


         For earlier releases, there is no way to specify the value of \$home$\
         for a \%redirect%\ router; it either comes from the password data as a
         result of \check_local_user\, or is unset.



Q0509: How can I set up a filter file to detect and block virus attachments?

  A0509: Exim's filter facilities aren't powerful enough to do much more than
         very crude testing. Most people that want virus checking are nowadays
         using one of the separate scanning programs such as \^exiscan^\ (see
         \?http://duncanthrax.net/exiscan/?\). There is some further information
         about scanning with Exim via \?http://www.timj.co.uk/linux/exim.php?\.



Q0510: Is it possible to write code for scanning messages in Python?

  A0510: \^elspy^\ is a layer of glue code that enables you to write Python code
         to scan email messages at SMTP time. \^elspy^\ also includes a small
         Python library with common mail-scanning tools, including an interface
         to SpamAssassin and a simple but effective virus detector. You can
         optain \^elspy^\ from \?http://elspy.sourceforge.net/?\.



  Q0511: Whenever my system filter uses a \mail\ command to send a message, I get
         the error \*User 0 set for address_reply transport is on the never_users
         list*\. What does this mean?


  A0511: The system filter runs as \/root/\ in Exim 4, unless you set
         \system_filter_user\ to specify otherwise. When you set up a delivery
         direct from a system filter (an autoreply is a special kind of
         ``delivery'') the transport runs as the same user, unless it has a
         \user\ setting of its own. Normally, deliveries are not allowed to run
         as \/root/\ as a security precaution; this is implemented by the
         \never_users\ option.


         The easiest solution is to add this to your configuration:


  ==>      system_filter_user = exim


         The system filter then runs as \/exim/\ instead of \/root/\.
         Alternatively, you can arrange for autoreplies from the system filter to
         use a special transport of their own, and set the \user\ option on that
         transport.



  Q0512: I'm trying to reference the ::Envelope-To:: header in my filter, but
         \$h_envelope-to:$\ is always empty.


  A0512: ::Envelope-To:: is added at delivery time, by the transport. Therefore,
         the header doesn't exist at filter time. In a user filter, the values
         you probably want are in \$original_local_part$\ and
         \$original_domain$\. In a system filter, the complete list of all
         envelope recipients is in \$recipients$\.



  Q0513: I want my system filter to freeze all mails greater than 500K in size,
         but to exclude those to a specific domain. However, I don't seem to be
         able to use \$domain$\ in a system filter.


  A0513: You cannot do this in a system filter, because a single message may have
         multiple recipients, some in the special domain, and some not. That is
         also the reason why \$domain$\ is not set in a system filter.


         If you want to take actions on a per-recipient basis, you have to do it
         in a router. However, freezing is not appropriate, because freezing
         stops all deliveries. You could, however, delay delivery to all but the
         special domains by using something like this:


  ==>      delay_if_too_big:
             driver = redirect
             domains = !the.special.domain
             condition = ${if >{$message_size}{500K}{yes}{no}}
             allow_defer
             data = :defer: message too big.


         However, there isn't an easy way of ``releasing'' such messages at
         present.



  Q0514: When I try to send to two addresses I get an error in the filter
         file \*malformed address: , e@??? may not follow a@???*\. What
         is going on?


A0514: Have you got

  ==>      deliver "a@???, e@???"


         in your filter? If so, that is your problem. You should have


  ==>      deliver a@???
           deliver e@???


         Each \deliver\ command expects just one address.




6. DELIVERY

  Q0601: What does the error \*Neither the xxx router nor the yyy transport set
         a uid for local delivery of...*\ mean?


  A0601: Whenever Exim does a local delivery, it runs a process under a specific
         user and group id (uid and gid). For deliveries into mailboxes, and to
         pipes and files set up by forwarding, it normally picks up the uid/gid
         of the receiving user. However, if an address is directed to a pipe or a
         file by some other means, such an entry in the system alias file of the
         form


  ==>      majordomo: |/local/mail/majordomo ...


         then Exim has to be told what uid/gid to use for the delivery. This can
         be done either on the routerr that handles the address, or on the
         transport that actually does the delivery. If a pipe is going to run a
         setuid program, then it doesn't matter what uid Exim starts it out with,
         and so the most straightforward thing is to put


  ==>      user = exim


         on either the router or the transport. A setting on the transport
         overrides a setting on the router, so if the same transport is being
         used with several routers, you should set the user on it only if you
         want the same uid to be used in all cases.


         In the default configuration, the transports used for file and pipe
         deliveries are the ones called \address_file\ and \address_pipe\. You
         can specify different transports by setting, for example,


  ==>      pipe_transport = special_pipe_transport


         on the \%system_aliases%\ router. Then you can set up \%special_pipe_transport%\


  ==>      special_pipe_transport:
             driver = pipe
             user = ????


         which will be used only for pipe deliveries from that one router.
         What you put for the ???? is up to you, and depends on the particular
         circumstances.



  Q0602: Exim keeps crashing with segmentation errors (signal 11 or 139) during
         delivery. This seems to happen when it is about to contact a remote
         host or when a delivery is deferred.


  A0602: This could be a problem with Exim's databases. Try running a delivery
         with debugging turned on. If the last line of the debug output is
         something like this:


  ==>      locked /var/spool/exim/db/retry.lockfile


         the crash is happening inside the DBM library. Check that your DBM
         library is correctly installed. In particular, if you have installed a
         second DBM library onto a system that already had one, check that its
         version of \(ndbm.h)\ is being seen first. For example, if the new
         version is in \(/usr/local/include)\, check that there isn't another
         version in \(/usr/include)\. If you are using Berkeley db, you can set


  ==>      USE_DB=yes


         in your \(Local/Makefile)\ to avoid using \(ndbm.h)\ altogether. This is
         particularly relevant for version 2 (or later) of Berkeley db, because
         no \(ndbm.h)\ file is distributed with it. Another thing you can try is
         to run


  ==>      exim_dumpdb /var/spool/exim retry


         to see if it also crashes, or build the \^test_dbfn^\ tool and fiddle
         around with it. If both fail, it is most almost certainly a problem with
         your DBM library. You could try to update it, or force Exim to use
         another library. See the file \(doc/dbm.discuss.txt)\ for hints about
         this.



  Q0603: How can mails that are being routed through routers that do not set
         \check_local_user\ be delivered under the uid of the recipient?


  A0603: Q0601 contains background information on this. If you are using, say, an
         alias file to direct messages to specific mailboxes, you can use
         the \user\ option on either the router or the transport to set the uid.
         What you put in the setting depends on how the required uid is to be
         found. It could be looked up in a file or computed somehow from the
         local part, for example.



  Q0604: I want to use MMDF-style mailboxes. How can I get Exim to append the
         ctrl-A characters that separate indvidual emails?


  A0604: Set the \message_suffix\ option in the \%appendfile%\ transport. In fact,
         for MMDF mailboxes you need a prefix as well as a suffix to get it
         working right, so your transport should contain these settings:


  ==>      message_prefix = "\1\1\1\1\n"
           message_suffix = "\1\1\1\1\n"


         Also, you need to change the \check_string\ and \escape_string\ settings so
         that the escaping happens for lines in the message that happen to begin
         with the MMDF prefix or suffix string, rather than ``From'' (the default):


  ==>      check_string  = "\1\1\1\1\n"
           escape_string = "\1\1\1\1 \n"


         Adding a space to the line is sufficient to prevent it being taken as a
         separator.



  Q0605: If a user's mailbox is over quota, is there a way for me to set it up so
         that the mail bounces to the sender and is not stored in the mail queue?


A0605: In the retry section of the configuration, put

  ==>      *@your.dom.ain        quota


         That is, provide no retry timings for over quota errors. They will then
         bounce immediately. Alternatively, you can set up retries for a short
         time only, or use something like this:


  ==>      *@your.dom.ain        quota_7d
           *@your.dom.ain        quota       F,2h,15m; F,3d,1h


         which bounces immediately if the user's mailbox hasn't been read for 7
         days, but otherwise tries for up to 3 days after the first quota
         failure.



  Q0606: I'm using tmail to do local deliveries, but when I turned on the
         \use_crlf\ option on the \%pipe%\ transport (tmail prefers \"@\r@\n"\
         terminations) message bodies started to vanish.


  A0606: You need to unset the \mesage_prefix\ option, or change it so that its
         default \"@\n"\ terminator becomes \"@\r@\n"\. For example, the
         transport could be:


  ==>      local_delivery_mbx:
         driver = pipe
         command = /usr/local/bin/tmail $local_part
         user = exim
         current_directory = /
             use_crlf
             message_prefix =


         The reason for this is as follows: tmail uses the line terminator on
         the first line it sees to determine whether lines are terminated by
         \"@\r@\n"\ or \"@\n"\. If the latter, it moans to stderr and changes subsequent
         \"@\n"\ terminators to \"@\r@\n"\. The default setting of the \message_prefix\
         option is \"From ...@\n"\, and this is unaffected by the \use_crlf\ option.
         If you don't change this, tmail sees the first line terminated by
         \"@\n"\ and prepends \"@\r"\ to the \"@\n"\ terminator on all subsequent
         lines. However, if \use_crlf\ is set, Exim makes all other lines
         \"@\r@\n"\ terminated, leading to doubled \"@\r@\r@\n"\ lines and
         corrupt mbx mailboxes.



  Q0607: When I activate ``return receipt'' for example in Netscape Mailbox
         sending options, then I get an error message from Exim... something
         like \*not supported*\. Can I activate delivery confirmations?


A0607: Exim does not support any kind of delivery notification.

         (1) You can configure it to recognize headers such as
         \Return-receipt-to:\ if you wish.


         (2) Some people want MSN (message status notification). Such services
         are implemented in MUAs, and don't impact on the MTA at all.


         (3) I investigated the RFCs which describe the DSN (delivery status
         notification) system. However, I was unable to specify any sensible way
         of actually doing anything with the data. There were comments on the
         mailing list at the time; many people, including me, conclude that DSN
         is in practice unworkable. The killer problem is with forwarding and
         aliasing. Do you propagate the DSN data with the generated addresses?
         Do you send back a ``reached end of the DSN world'' or ``expanded'' message?
         Do you do this differently for different kinds of aliasing/forwarding?
         For a user who has a \(.forward)\ file with a single address in, this
         might seem easy - just propagate the data. But what if there are several
         forwardings? If you propagate the DSN data, the sender may get back
         several DSN messages - and should the sender really know about the
         detail of the receiver's forwarding arrangements? There isn't really
         any way to distinguish between a \(.forward)\ file that is forwarding
         and one that is a mini mailing list. And so on, and so on. There are so
         many questions that don't have obvious answers.



  Q0608: What does the message \*retry time not reached [for any host]*\ on the log
         mean? Why won't Exim try to deliver the message?


  A0608: That is not an error. It means exactly what it says. A previous attempt
         to deliver to that address failed with a temporary error, and Exim
         computed the earliest time at which to try again. This can apply to
         local as well as to remote deliveries. For remote deliveries, each host
         (if there are several) has its own retry time.


         If you are running on a dial-up host, the rest of this answer probably
         does not apply to you. Go and read Q1404 instead. If your host is
         permanently online, read on...


         Some MTAs have a retrying schedule for each message. Exim does not work
         like this. Retry timing is normally host-based for remote deliveries and
         address-based for local deliveries. (There are some exceptions for certain
         kinds of remote failure - see \*Errors in outgoing SMTP*\ in the manual.)


         If a new message arrives for a failing address and the retry time has
         not yet arrived, Exim will log \*retry time not reached*\ and leave the
         message on the queue, without attempting delivery. Similarly, if a queue
         runner notices the message before the time to retry has arrived, it
         writes the same log entry. When the retry time has past, Exim attempts
         delivery at the next queue run. If you want to know when that will be,
         run the exinext utility on the address, for example:


  ==>      exinext user@???


         You can suppress these messages on the log by including \"-retry_defer"\
         in the setting of \log_selector\. You can force a delivery attempt on a
         specific message (overriding the retry time) by means of the -M option:


  ==>      exim -M 10hCET-0000Bf-00


         If you want to do this for the entire queue, use the \-qf-\ option.



  Q0609: Exim seems to be sending the same message twice, according to the log,
         although there is a difference in capitalization of the local part of
         the address.


  A0609: That is correct. The RFCs are explicit in stating that capitalization
         matters for local parts. For remote domains, Exim is not entitled to
         assume case independence of local parts. I know, it is utterly silly,
         and it causes a lot of grief, but that's what the rules say. Here is a
         quote from RFC 2821:


           ... a command verb, an argument value other than a mailbox local-part,
           and free form text MAY be encoded in upper case, lower case, or any
           mixture of upper and lower case with no impact on its meaning.  This
           is NOT true of a mailbox local-part.  The local-part of a mailbox
           MUST BE treated as case sensitive.  Therefore, SMTP implementations
           MUST take care to preserve the case of mailbox local-parts.  Mailbox
           domains are not case sensitive.  In particular, for some hosts the
           user "smith" is different from the user "Smith".  However, exploiting
           the case sensitivity of mailbox local-parts impedes interoperability
           and is discouraged.



Q0610: How can I force the next retry time for a host to be now?

  A0610: You can change the retry time with the \^exim_fixdb^\ utility, but its
         interface is very clumsy. If you have a message for the host on the
         queue, the simplest thing to do is to force a delivery with the \-M-\
         command line option. If delivery succeeds, the retry data will get
         cleared. If the host is past the cutoff time, so that messages are
         bouncing immediately without trying a delivery, you can use \-odq-\ to
         put a message on the queue without a delivery attempt, and then use
         \-M-\ on it.



  Q0611: I set up \"|/bin/grep Subject|/usr/bin/smbclient -M <netbiosname>"\ as an
         alias but it doesn't work.


  A0611: That is a shell command line. Exim does not run pipe commands under a
         shell by default (for added security - and it saves a process). You
         need something like


  ==>      "|/bin/sh -c '/bin/grep Subject|/usr/bin/smbclient -M <netbiosname>'"



  Q0612: Why does the \%pipe%\ transport add a line starting with \">From"\ to
         messages?


  A0612: Actually, it adds a line starting with \"From"\ followed by a space.
         This is commonly referred to as the \"From_"\ line, to emphasize the
         fact that \"From"\ is followed by a space and not a colon. This is a
         pseudo-header line that contains the envelope sender address and the
         time of delivery. It originated as a separator line in Berkeley format
         mailboxes, but is also used in other contexts. (And yes, it is often
         confused with the ::From:: header line, and this causes a lot of grief.
         The use of \"From_"\ was one of the really bad email design decisions.)


         Exim's \%pipe%\ transport adds this pseudo-header line by default
         because \(/usr/ucb/vacation)\ needs it, and that is one of the the most
         common uses of piping. The \^procmail^\ local delivery agent also makes
         use of the \"From_"\ line. If you do not want it, change the setting of
         \message_prefix\ on the \%pipe%\ transport. For example, to remove the
         line altogether, use


  ==>      message_prefix =


         If you are not piping to \(/usr/ucb/vacation)\ or \^procmail^\, it is
         likely that you do not need a \"From_"\ line, and indeed it may cause
         problems if it is present.


         One user reported that this line gave trouble when a pipe was used to
         send messages to Courier's \^deliverquota^\ program. The line was
         retained with the message, and caused problems for MS Exchange 2000 when
         retrieving messages with its built-in POP collector. Specifically, it
         caused Exchange to not be able to recognise message attachments.



  Q0613: I have set \fallback_hosts\ on my \%smtp%\ transport, but after the error
         \*sem@??? cannot be resolved at this time*\ Exim isn't using them.


  A0613: \fallback_hosts\ works only if an attempt at delivery to the original
         host(s) fails. In this case, Exim couldn't even resolve the domain
         \(chat.ru)\ to discover what the original hosts were, so it never got as far
         as the transport. However, see Q0315 for a possible solution.



  Q0614: After the holidays my ISP has always hundreds of e-mails waiting for me.
         These are forced down Exim's throat in one go. Exim spawns a lot of
         kids, but is there some limit to the number of processes it creates?


  A0614: Unless you have changed \smtp_accept_queue_per_connection\ it should
         spawn only that many processes per connection (default 10). Your ISP
         may be making many connections, of course. That is limited by
         \smtp_accept_max\.



  Q0615: When a message in the queue got to 12h old, Exim wrote \*retry timeout
         exceeded*\ and removed all messages in the queue to this host - even
         recent messages. How I can avoid this behaviour? I only want to remove
         messages that have exceeded the maximum retry time.


  A0615: Exim's retrying is host-based rather than message-based. The philosophy
         is that if a host has been down for a very long time, there is no point
         in keeping messages hanging around. However, you might like to check
         out \delay_after_cutoff\ in the \%smtp%\ transport. It doesn't do what you
         want, but it might help.



Q0616: Can Exim add a ::Content-Length:: header to messages it delivers?

A0616: You could include something like

  ==>      headers_remove = "content-length"
           headers_add = "Content-Length: $message_body_size"


         to the \%appendfile%\ transport. However, the use of ::Content-Length:: can
         cause several problems, and is not recommended unless you really know
         what you are doing. There is a discussion of the problems in
         \?http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html?\.



  Q0617: Exim seems to be trying to deliver a message every 10 minutes, though
         the retry rules specify longer times after a while, because it is
         writing a log entry every time, like this:


  ==>    1999-08-26 14:51:19 11IVsE-000MuP-00 == example@??? T=smtp defer
         (-34): some host address lookups failed and retry time not reached for
         other hosts or connection limit reached


  A0617: It is looking at the message every 10 minutes, but it isn't actually
         trying to deliver. It's looking up \(example.com)\ in the DNS and finding
         this information:


  ==>      example.com.                MX 10 example-com.isp.example.com.
           example.com.                MX  0 mail.example.com.
           mail.example.com.           A  202.77.183.45
           A lookup for example-com.isp.example.com. yielded NXDOMAIN


         The last line means that there is no address (A) record in the DNS for
         \(example-com.isp.example.com)\. That accounts for \*some host address
         lookups failed*\, but the retry time for \(mail.example.com)\ hasn't been
         reached, which accounts for \*retry time not reached for other hosts*\.



  Q0618: I am trying to set exim up to have a automatic failover if it sees that
         the system that it is sending all mail to is down.


A0618: Add to the \%remote_smtp%\ transport the following:

  ==>      fallback_hosts = failover.server.name(s)


         If there are several names, they must be separated by colons.



  Q0619: I can't get Exim to deliver over NFS. I get the error \*fcntl() failed:
         No locks available*\, though the lock daemon is running on the NFS server
         and other hosts are able to access it.


  A0619: Check that you have \(lockd)\ running on the NFS client. This is not
         always running by default on some systems (Red Hat is believed to be one
         such system).



  Q0620: Why does Exim bounce messages without even attempting delivery, giving
         the error \*retry time not reached for any host after a long failure
         period*\?


  A0620: This message means that all hosts to which the message could be sent
         have been failing for so long that the end of the retry period
         (typically 4 or 5 days) has been reached. In this situation, Exim still
         computes a next time to retry, but any messages that arrive in the
         meantime are bounced straight away. You can alter this behaviour by
         unsetting the \delay_after_cutoff\ option on the smtp transport. Then Exim
         will try most messages for those hosts once before giving up.



  Q0621: My \(.forward)\ file is \"|/usr/bin/procmail -f-"\ and mail gets delivered,
         but there was a bounce to the sender, sending him the output of procmail.
         How can I prevent this?


A0621: Exim's default configuration is set up like this:

  ==>      address_pipe:
             driver = pipe
             return_output


         The \return_output\ option requests that any output that the pipe
         produces be returned to the sender. That is the safest default. If you
         don't want this, you can either remove the option altogether, or change
         it to \return_fail_output\, to return output only if the command fails.
         Note that this will affect all pipes that users run, not just your
         procmail one. It might be better to arrange for procmail not to produce
         any output when it succeeds.



  Q0622: Can I write an ordinary file when I run a perl script as a transport
         filter for the \%remote_smtp%\ and \%address_pipe%\ transports?


  A0622: Yes, provided the file is writeable by the uid under which the transport
         runs (the Exim user in the case of the remote transport). However, if two
         messages are being delivered at once, their data will get mixed up in
         the file unless you implement your own locking scheme. If all you want
         to do is to take a copy of the message, another approach that avoids
         the locking problem is to use a system filter to set up an ``unseen''
         delivery to a file. If you only want the message's headers, you can
         set \message_filter_file_transport\ to point to a special \%appendfile%\
         transport that has \headers_only\ set.



  Q0623: My \(/var/spool/mail)\ has grown drastically. Is there any possibility of
         using two directories?


  A0623: You can use an expansion string to split mailboxes between two
         directories. For example,


  ==>      file = /var/spool/mail${nhash_2:$local_part}/$local_part


         which does a hash on the local part, producing either 0 or 1, thereby
         using \(mail0) or \(mail1)\. But remember, the MUAs that read these mailboxes
         also have to know where they are.



  Q0624: Sendmail has a program called \^smrsh^\ that restricts what binaries
         can be run from sendmail aliases. Is there something like this in Exim ?


A0624: Check out the \allow_commands\ option in the \%pipe%\ transport.


Q0625: I wish to have large emails go out one at a time.

  A0625: One possibility is to set up a router that defers all large messages,
         except in queue runs. Since queue runners deliver just one
         message at a time, if you limited the number of simultaneous queue
         runners to 1, you would get the effect you wanted. A suitable router
         might be


  ==>      defer_if_large_unless_queue_run:
             driver = redirect
             condition = ${if or{{queue_running}{<{$message_size}{200K}}}{no}{yes}}
             allow_defer
             data = :defer: too large for immediate delivery
             no_verify


         Of course, this would always delay any large message until the next
         queue runner, but if you run them fairly regularly, this shouldn't be a
         huge problem, and may even be desirable. Note the use of \no_verify\ to
         ensure that this router is not used when Exim is verifying addresses.



  Q0626: Exim can route local parts independent of their case, but the Cyrus LMTP
         daemon requires the correct case. How can I fix this?


  A0626: You need to rewrite the local part to the correct case before running
         the router that routes to Cyrus. For example, if you require all lower
         case, and your router is called \local_user\, put this router in front
         of it:


  ==>      lowercase_local:
             driver = redirect
             redirect_router = local_user
             domains = +local_domains
             data = ${lc:$local_part}@$domain


         The setting of \redirect_router\ causes processing of the rewritten
         address to start at the next router, instead of the first router. See
         also Q0630, and C045 for a more complete Cyrus configuration.



  Q0627: Is there a command I can send to Exim to retry all queued messages
         regardless of their retry schedule?


  A0627: The \-qff-\ option starts a queue runner that forces a delivery attempt
         for all messages, including frozen ones. If you use \-qf-\, frozen
         messages are skipped.



  Q0628: I have the default retry rule, which I thought meant that Exim should
         keep trying for four days, but it seems to be bouncing some messages
         immediately.


A0628: See Q0615 and Q0620.


  Q0629: I'm having trouble with quotas and Courier, because Exim is not handling
         maildirsize files.


  A0629: You will do better to move the quota handling to Courier. Use \^maildrop^\
         as your MDA rather than direct Exim delivery.  This also has the
         advantage that if you give web access to the mail spool (over \^sqwebmail^\)
         you can then use the web front end to edit \^maildrop^\ filter files.



Q0630: How can I configure Exim to deliver to a Cyrus message store?

A0630: (1) The reference manual contains an example that uses pipe delivery.

         (2) Here is a transport that uses LMTP delivery, assuming that
             \$local_part$\ contains the username:


  ==>      cyrus_inbox:
             driver =lmtp
             user = cyrus
             socket = /var/cyrus/socket/lmtp


         (3) This is a transport that delivers direct to a non-inbox mailbox:


  ==>      cyrus_mailbox:
             driver = pipe
             user = $local_part
             message_prefix =
             message_suffix =
             log_fail_output
             return_output
             command = "/usr/cyrus/bin/deliver -a $local_part \
                        -m <mailbox-name> $local_part"


         This delivers to the Cyrus mailbox \"user.$local_part.<mailbox-name>"\.
         Using \"user = $local_part"\ and \"-a $local_part"\ makes it work
         without needing an explicit `p' ACL set for `anyone' on the mailbox.



  Q0631: I would like to choose a retry rule based on on the sender rather than
         the recipient address. Is this possible?


  A0631: Yes. The address part of a retry rule is matched as a single-item
         address list. Such lists are always expanded, so you can use something
         like this:


  ==>      "${if eq{$sender_address}{xxx}{*@*}{no@no}}" quota F,1h,10m; ...


         If the sender address is ``xxx'', the pattern expands to ``*@*'', which
         matches all recipient addresses; if you want to, you can make this a
         more restrictive pattern. If the sender address is not ``xxx'', the
         pattern expands to ``no@no'', which is assumed to be a recipient address
         that can never match, so the retry rule is skipped.



  Q0632: What does the error \*User 1 set for local_mbx_delivery transport is on
         the never_users list*\ mean?


  A0632: You have configured the \%local_mbx_delivery%\ to run as the user whose
         id (uid) is 1. However, this user is on the list defined by the
         \never_users\ runtime option, or the \\FIXED_NEVER_USERS\\ compile-time
         option. These are ``safety catch'' lists; Exim refuses to deliver to any
         user that is on them. The most common use of \never_users\ is to avoid
         doing any deliveries as \/root/\, but it can contain other uids.



Q0633: Why is \$domain$\ not set in the \%smtp%\ transport?

  A0633: The \%smtp%\ transport can handle several recipient addresses at once.
         This happens by default if the host lists for the addresses are
         identical. A single copy of the message is sent, using multiple \\RCPT\\
         commands to transmit multiple envelope recipients. The \$domain$\
         variable is set in the \%smtp%\ transport only if all the recipient
         addresses have the same domain. You must have a case where several
         addresses with different domains resolve to the same set of hosts.


         If you want to restrict the transport so that it handles only a single
         domain at once (but still possibly with more than one recipient), set


  ==>      multi_domain = false


         If you want to restrict the transport so that it handles only a single
         address at once, set


  ==>      max_rcpt = 1



  Q0634: How can I stop a local transport from trying to access the user's home
         directory, even when the delivery is to a file that is elsewhere?


A0634: See answer (2) for Q0423.


  Q0635: The log message \*error ignored*\ appears after some delivery failures.
         What does it mean?


  A0635: This message is written when Exim fails to deliver a bounce message whose
         age is greater than \ignore_bounce_errors_after\. It indicates that the
         failing bounce message has been discarded.


         The same message is written after failed deliveries when a filter file
         uses the \noerror\ feature when setting up a delivery, or if a router
         has the setting


  ==>      errors_to = <>


         Both of these specify that delivery failures are to be discarded.




7. POLICY CONTROLS

Q0701: How do I block unwanted messages from outside my host?

  A0701: Exim uses Access Control Lists (ACLs) for controlling incoming mail from
         other hosts. A whole chapter in the reference manual is devoted to
         describing how they work. A wide variety of conditions can be imposed on
         incoming messages.


         The default Exim run time configuration contains an example of an ACL
         which blocks all relaying, and messages whose senders cannot be
         verified. This example is heavily commented and worth studying.



  Q0702: I don't want to block spam entirely; how can I inspect each message
         before deciding whether or not to deliver it?


  A0702: Wherever possible, inspection and rejection is best done automatically
         in an ACL, that is, before the message is accepted. If you want to
         verify manually each message that is classified as spam by an automatic
         check, you can arrange for a system filter to freeze such messages after
         they have been accepted.


         If, after inspection, you decide not to deliver the message, it is
         safest to discard it, using the \-Mrm-\ option. Use of the \-Mg-\ option
         to force a bounce carries the risk of ``collateral spam'' if the sender
         address is faked.



Q0703: How can I test that my spam blocks are working?

  A0703: The \-bh-\ option allows you to run a testing SMTP session as if from a
         given IP address. For example,


  ==>      exim -bh 192.168.178.39


         In addition to the normal SMTP replies, it outputs commentary about
         which tests have succeeded or failed. If you are not interested in the
         details, but just want to know if a particular sender at a particular IP
         address is able to mail to a particular recipient, you can use the
         \exim_checkaccess\ utility, which provides a ``packaged'' version of
         \-bh-\. You call it like this:


  ==>      exim_checkaccess 192.168.53.23 recip@??? -f sender@???


         If you don't give a sender, \"<>"\ is used (that it, it acts like a
         bounce message).



  Q0704: How can I test that Exim is correctly configured to use the Realtime
         Blackhole List (RBL)?


  A0704: The \-bh-\ option allows you to run a testing SMTP session as if from a
         given address. The \^exim_checkaccess^\ utility provides a more packaged
         version of this facility. You need to know a blocked IP address with
         which to test. Such a testing address is kindly provided by Russell
         Nelson:


  ==>      linux.crynwr.com [192.203.178.39]


         You can also send mail to \(nelson@???)\ from the server
         whose RBL block you are testing. The robot that receives that email
         will attempt to send a piece of test email in reply. If your RBL block
         didn't work, you get a message to that effect. Regardless of whether the
         RBL block succeeds or not, it emails you the results of the SMTP
         conversation from a host that is not on the RBL, so you can see how your
         server looks from the view of someone on the RBL.



Q0705: How can I use tcpwrappers in conjunction with Exim?

  A0705: Exim's own control facilities can do all that tcpwrappers can do.
         However, if you are already using tcpwrappers for other things it might
         be convenient to include Exim controls in the same place.


         First of all, ensure that Exim is built to call the tcpwrappers library,
         by including \\USE_TCPWRAPPERS=yes\\ in \(Local/Makefile)\. You also need to
         ensure that the header file \(tcpd.h)\ is available at compile time, and the
         \(libwrap.a)\ library is available at link time, typically by including it in
         \\EXTRALIBS\\. You may need to copy these two files from the tcpwrappers
         build directory to, for example, \(/usr/local/include)\ and \(/usr/local/lib)\,
         respectively. Then you could reference them by


  ==>      CFLAGS=-I/usr/local/include
           EXTRALIBS=-L/usr/local/lib -lwrap


         in \(Local/Makefile)\. There are two ways to make use of the functionality,
         depending on how you have tcpwrappers set up. If you have it set up to
         use only one file, you ought to have something like:


  ==>      /etc/hosts.allow:


  ==>          exim : <client_list>  : <allow_or_deny>


           For example:


  ==>          exim : LOCAL  192.168.0.  .friendly.domain  special.host : ALLOW
               exim : ALL                                               : DENY


         This allows connections from local hosts (chiefly //localhost//), from
         the subnet 192.168.0.0/24, from all hosts in \(*.friendly.domain)\, and
         from a specific host called \(special.host)\. All other connections are
         denied. If you have tcpwrappers set up to use two files, use the
         following:


  ==>      /etc/hosts.allow:


  ==>          exim    : <client_list>


  ==>      /etc/hosts.deny:


  ==>          exim    : <client_list>


         Read the \^hosts_access^\ man page for more ways of specifying clients,
         including ports, etc., and on logging connections.



  Q0706: How can I get POP-auth-before-relay (aka POP-before-SMTP) support in
         Exim?


  A0706: Exim 4 supports the ``whoson'' (\?http://whoson.sourceforge.net?\)
         facility for doing this. If you set this up, you can do the check in an
         Exim ACL by a statement like this:


  ==>      require condition = \
             ${lookup whoson {$sender_host_address}{yes}{no}}


         Otherwise you need to arrange for a list of permitted IP addresses to be
         maintained in a file or database, and use this in a \hosts\ condition in
         an ACL statement. An Exim user has published this recipe:


         \#\#\#\#\?http://www.zeiss.cx/memo/computer/linux/email/exim-s-a-p.html?\


         Another Exim user submitted the following idea:


         Use a script to grab authenticated IP addresses from the log files of
         the POP3 and IMAP4 daemons. These are used to create files in the
         directory tree \(/var/db/popb4smtp)\. The existence of a file represents a
         valid ``popped recently token'' for the IP address used as the filename.


         Another script periodically removes stale files from the tree (after two
         hours).  There's a small race condition here; it's possible for a file
         to be deleted just after it has been updated by the script that watches
         the logs. For low-volume servers, the odds of hitting this window are
         low.


         A POPB4SMTP_CLIENT macro in the Exim configure file provides a reusable
         ``has this sender popped recently?'' query:


  ==>    POPB4SMTP_SUBDIR = /var/db/popb4smtp/${substr_-1_1:$sender_host_address}
         POPB4SMTP_CLIENT = ${if exists {POPB4SMTP_SUBDIR/$sender_host_address} \
             {$sender_host_address} {0} }


         Now you can use it just about anywhere, including in your ACLs. Simple
         examples include:


  ==>    hostlist relay_hosts = 127.0.0.1/32 : ... : POPB4SMTP_CLIENT
         host_lookup = !127.0.0.1/32 : ... : !POPB4SMTP_CLIENT
         rfc1413_hosts = !127.0.0.1/32 : ... : !POPB4SMTP_CLIENT


         The two scripts (and a FreeBSD startup script for them) are available
         for download at:


         \#\#\#\#\?http://people.FreeBSD.org/~sheldonh/popb4smtp-nodb.tar.gz?\



  Q0707: I have one or two cases where my host correctly rejects messages, but
         the remote host is quite persistent, and keeps trying over and over.


  A0707: It is an unfortunate fact that a number of SMTP clients, in violation of
         the SMTP RFC, do not treat a permanent error code that is given after
         the DATA portion of the transaction as a permanent error. Consequently
         they keep resending the message, and the worst offenders do so at very
         short intervals.


         The only way to stop such behaviour is to blacklist the IP address, or
         the envelope sender, or both, in such a way that future messages get
         rejected at RCPT time instead of at DATA time. You could also complain
         to the remote host's administrators.



Q0708: How can I run customized verification checks on incoming addresses?

A0708: There are a number of possibilities:

         (1) If you can implement your checks in Perl, you can use Exim's
         facility for running an embedded Perl interpreter. For example, if you
         want to run special checks on local addresses, you could use ACL
         an statement like this:


  ==>      require domains = my.local.domain
                   condition = ${perl{verify}{$local_part}}


         The result of the Perl function should be ``yes'' or ``no''.


         (2) You could also run an external program in a similar way, by a
         statement such as:


  ==>      require domains = my.local.domain
                   condition = ${run{/my/verifier $local_part}}


         This requires the use of another process, so could prove more expensive
         than Perl.


         (3) If you are prepared to write C code, read the chapter in the manual
         entitled \*Adding a local scan function to Exim*\.



  Q0709: Does Exim apply RBL checks to error messages, those with an envelope
         sender of \"<>"\ ?


  A0709: This depends on the ACL configuration. You can test for bounce messages
         (by looking for an empty sender address) and thereby exclude them from
         RBL checking if you want. This ACL statement does that:


  ==>      deny senders = ! :
                dnslist = blackholes.mail-abuse.org


         However, some spam does come with an empty sender address, so this may
         not be a good idea.



  Q0710: I want to reject certain sender-recipient combinations, with a specific
         message for each such combination.


  A0710: Set up a file (or database) containing the messages, keyed by the
         combination, for example:


  ==>      sender1@sdomain1=>recipient1@rdomain1: blocked because...
           sender2@sdomain2=>recipient2@rdomain2: blocked because...


         If you have lots of recipients for the same sender, it might be easier
         to generate this file from more convenient data. In your ACL that is run
         for each RCPT command, you can then put:


  ==>      deny message   = ${lookup{$sender_address=>$local_part@$domain}\
                            lsearch{/that/file}}
                condition = ${lookup{$sender_address=>$local_part@$domain}\
                            lsearch{/that/file}}{yes}{no}}


         The condition is tested first. If the lookup succeeds, the condition
         succeeds so access is denied. The message is then expanded, but the
         lookup won't be repeated, because Exim will have cached the previous
         result.


         This approach blocks only incoming SMTP messages. If you need to do
         similar blocks for messages that do not arrive over SMTP, you have to
         set up a suitable \%redirect%\ router with a \:fail:\ setting.



  Q0711: Will Exim allow me to create a file of regexs and match incoming
         external email to the list - and if a match is found file the offending
         message into a special location? Also is it possible to make Exim only
         filter parts of an incoming email - e.g. ignore large MIME attachments
         for example and only process text/plain?


A0711: You can do some of this in a system filter. For example:

  ==>      if $message_body matches <...some complicated regex...> or
              $message_body matches <...some other regex...> or
              $header_from: matches <...regex...> or
              etc.
           then
             save /some/special/file
           endif


         or instead of \"save"\ you could have \"deliver"\ (to some address) or
         \"pipe"\ (to some script).


         There isn't any mechanism for ignoring attachments, but \$message_body$\
         only looks at the first n bytes of the body, where n defaults to 500 but
         can be changed.


         A more expensive alternative would be to run a Perl subroutine using the
         embedded Perl mechanism. If you passed over the message id, the Perl
         code could read the message files on the spool and implement any
         algorithm it liked for deciding what should be done.



  Q0712: I've hacked sendmail to make an ioctl call at the time of the SMTP RCPT
         command, to check if a user has exceeded their email quota. If they have
         I issue a temporary failure and a message - can I do this with Exim?


  A0712: If you can make this happen in Perl you can use the embedded Perl
         facility, and use it from a \condition\ condition in an ACL statement.
         You can also use the expansion facility to run an external program, but
         this uses more resources because it uses another process.



  Q0713: I'd like to pass all messages through a virus-scanning system before
         delivery. Can Exim do this?


  A0713: One way of achieving this is to deliver all messages via a pipe to a
         checking program that resubmits them for delivery in some private way
         that can be checked (e.g. on a specific SMTP port, or IP address). One
         possibility is to use the `received protocol` field that can be set
         for locally submitted mail via the \-oMr-\ command line option. This
         router sends all messages that are not from the local host and whose
         received protocol is not \"scanned-ok"\ to the \%virus_scan%\ transport:


  ==>      vircheck:
             driver = accept
             transport = virus_scan
             condition = ${if or {{eq {$received_protocol}{scanned-ok}} \
                                  {eq {$sender_host_address}{127.0.0.1}}}\
                                  {0}{1}}


         One problem is that this approach scans the message for each recipient,
         not just once per message.


         The virus_scan transport should be set up to pipe the message to a
         suitable checking program or script which runs as a trusted user. This
         can then re-submit the message to Exim, using \-oMr-\ to set the received
         protocol to \"scanned-ok"\, and the \-f-\ option to set the correct envelope
         sender address. \**Warning:**\ If you forget to make the resubmitting process
         run as a trusted user, the received protocol does not get set, and you
         are likely to generate a loop.



Q0714: Is there a way to configure Exim to reject mail to a certain local host?

  A0714: No, only to certain domains. To reject at SMTP time, you can put a line
         like this in your ACL:


  ==>      deny message = this domain is deliberately rejected
                domains = a.certain.domain


         To fail addresses in messages that do not arrive over SMTP, you can set
         up a router like this:


  ==>      reject_a_certain_domain:
             driver = redirect
             domains = a.certain.domain
             allow_fail
             data = :fail: this domain is deliberately rejected



Q0715: How can I get Exim to remove attachments from messages?

  A0715: Exim does not contain facilities for modifying messages. You must use
         an external program if you want to do this. You can route messages that
         have a ::Content-type:: header line via a pipe to a command that does
         the job and then re-submits the message to Exim. Alternatively, you
         could use a transport filter to do this job.



  Q0716: How can I arrange for each user to have a file listing the only sender
         addresses from which she will accept mail? I want to do this so my
         family members don't get any spam (or other inappropriate mail).


  A0716: Let's assume each user has a file called \(.acceptlist)\ in the home
         directory. You can put in your ACL a line like this:


  ==>      require senders = /home/$local_part/.acceptlist


         This will reject RCPT commands when the sender is not in the accept
         list for the recipient. (Replace \(/home/$local_part)\ with whatever
         the correct path to your user's home directories is.)


         One problem with this is that it will block bounce messages, which have
         empty senders. You can get round this, by changing the line to this:


  ==>      require senders =  : /home/$local_part/.acceptlist


         However, this will, of course, let in spam that has a null sender.



  Q0717: When using Nessus on a system that runs Exim, a number of security
         issues are raised. Nessus complains that Exim answers to EXPN and/or
         VRFY; sometimes it even complains that Exim allows relaying.


  A0717: Exim supports EXPN and VRFY only if you permit it to do so in the ACLs
         defined by \acl_smtp_expn\ and \acl_smtp_vrfy\, respectively. Otherwise,
         its responses are


  ==>      550 Administrative prohibition
           252 Administrative prohibition


         Maybe the use of 252 is the ``problem''. It is recommended that this be
         done (by those that discuss these things) because there are stupid
         clients that attempt VRFY before sending a message.



  Q0718: Could anyone points me to right rules to prevent sending/receiving
         messages to/for domains which have one MX to localhost or only have
         address 127.0.0.1 ?


A0718: See Q0319.


  Q0719: I would like to have a per-user limit for the maximum size of messages
         that can be sent.


  A0719: The simplest way to do this is to put something in a system filter along
         these lines:


  ==>    if $message_size is above
           "${lookup{$sender_address}lsearch{/some/file}{$value}{10M}}"
         then
           fail "Message is larger than $sender_address is allowed to send"
         endif


         In practice, an additional check that the message has arrived from your
         local host or local network is probably wise because sender addresses
         are easily forged.



  Q0720: I set \"accept hosts=192.168.122.96/32"\ in order to accept mail for
         relaying from my local LAN, but it doesn't work. What's wrong?


  A0720: 192.168.122.96/32 is not a network, it is a single host. Exim uses CIDR
         notation for specifying networks, where the number after the slash is
         the number of bits in the IP address that must match. Your setting says
         ``32 bits must match''. If you really mean to specify ``the next 32
         IP addresses'', you need 192.168.122.96/27.



  Q0721: I have POP-before-SMTP set up on my Exim server, but some clients use
         Outlook Express, which sends queued messages before checking the
         mailbox, so it doesn't work.


A0721: Implement SMTP authentication.


Q0722: I installed Amavis and it is working, but bounces are simply vanishing.

A0722: Check that you haven't inadvertently set up the transport like this:

  ==>      amavis:
             driver = pipe
             command = "/usr/sbin/amavis -f ${sender_address} -d ${pipe_addresses}"


         The last line should be:


  ==>        command = /usr/sbin/amavis -f <$sender_address> -d $pipe_addresses


         The important thing is the <> around the sender address; removal of
         the unnecessary "" and {} is just tidying. See the amavis FAQ at
         \?http://www.amavis.org/amavis-faq.php3?\.



  Q0723: I can't get Pine to work with PLAIN authentication; Exim keeps
         responding "535 Incorrect authentication data".


A0723: You need to have this setting in your PLAIN authenticator:

  ==>      server_prompts = :


         This is missing in the examples in all but the most recent Exim
         documentation, because it was not realized that PLAIN authentication
         could be requested by a client without sending the data with the
         request. If the data is not sent, an empty prompt is expected.



  Q0724: I have used \":fail:"\ in some aliases; when one of these addresses is
         refused, I see the message on the log, but the response to the remote
         user is ``unknown user'' instead of the message from the alias file.
         How can I change this?


  A0724: Have you got a \message\ qualifier in the relevant ACL? Exim uses the
         message line in the ACL in preference to the message returned by the
         router. This is so you can restrict the amount of information that
         ``escapes'' from your site via SMTP if you want to. Remove the \message\
         line in the ACL entry that has \"verify = recipient"\ and your message
         will get through.


         Alternatively, if you are running Exim 4.10 or later, you can use the
         \$acl_verify_message$\ variable in your message to include the message
         from the router. See also Q0725.



  Q0725: I've set up some specific rejection messages for certain recipients, but
         when I test them, the SMTP message is always \*550 5.1.1
         <user@???>... User unknown*\.


  A0725: That is not an Exim message (the ``5.1.1'' is a clue; Exim doesn't use
         those extended codes). You are probably being defeated by software that
         sees the 550 error code, and insists on putting in its own text. There
         is stupid software that does this. You can test Exim by using \-bh-\ or
         making a telnet call to the SMTP port. That way, there's no other
         software intervening.



  Q0726: My SMTP authentication can be bypassed by sending an unknown user name
         and an empty password. What is wrong with this condition in a PLAIN
         authenticator?


  ==>     server_condition = ${if eq{$2} {${lookup mysql{SELECT password FROM \
            accounts WHERE username='${local_part:$1}'}}}{1}{0}}


  A0726: Your lookup item returns an empty string when the user does not exist.
         You should instead arrange for the lookup to fail:


  ==>     server_condition = ${if eq{$2} {${lookup mysql{SELECT password FROM \
            accounts WHERE username='${local_part:$1}'}{$value}fail}}{1}{0}}



  Q0727: When a message has many recipients, how can I stop SpamAssassin from
         being called for each of them? I'm running it from a pipe transport.


  A0727: In the transport configuration, set \batch_max\ to a value greater than
         one.



  Q0728: How do I use Exiscan, SA-Exim, SpamAssassin, Clam Antivirus, Sophos
         SAVI, or sophie with Exim?


  A0728: There's a mini-HOWTO about these available via
         \?http://www.timj.co.uk/linux/exim.php?\.
         See also sample configuration C047.



  Q0729: How can I screen out addresses that are neither valid usernames or
         distribution lists on mail being forwarded to an internal Win2K server?


  A0729: A user suggested using a router like this to do the recipient
         verification:


  ==>      verify_user_router:
              driver = accept
              domains = win2kdomain.com
              local_parts=\
                ldap;user="cn=ldap-guest,cn=Users,dc=win2kdomain,dc=com"\
                pass=guest \
                ldap:://win2kpdc/dc=win2kdomain,dc=com?mailNickname?\
                sub?(&(mailNickname=$local_part)\
                (showInAddressBook=*)(sAMAccountName=*))
              verify_only


         Set up ldap-guest as a normal domain user on the Win2K PDC.


         Also, you need to set \no_verify\ on all the other routers that handle
         that domain.



  Q0730: How can I use the same passwords for SMTP authentication as I use for
         Courier IMAP access to my server?


  A0730: You can access the Courier authdaemon from an Exim authenticator. You
         must arrange for the Exim user (often \/exim/\ but sometimes \/mail/\)
         to be able to access \(/var/run/courier/authdaemon/socket)\. The
         configuration is something of a hack, but it is reported to work. Here
         is a LOGIN authenticator:


  ==>      login:
             driver = plaintext
             public_name = LOGIN
             server_prompts = Username:: : Password::
             server_condition = \
               ${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
               {AUTH 76\n${length_76:exim\nlogin\n$1\n$2\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n}}}}{FAIL\n} {no}{yes}}
             server_set_id = $1


         Here is a PLAIN authenticator:


  ==>      plain:
             driver = plaintext
             public_name = PLAIN
             server_prompts = :
             server_condition = \
               ${if eq {${readsocket{/var/run/courier/authdaemon/socket}\
               {AUTH 76\n${length_76:exim\nlogin\n$2\n$3\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\
               \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n}}}}{FAIL\n} {no}{yes}}
             server_set_id = $2



Q0731: Is there any defence I can use against spam sent through an open proxy?

  A0731: The \*ident*\ feature can be used in some cases. See the discussion in
         Q5023.



  Q0732: I would like to either warn or deny when a host uses an underscore in
         the EHLO command.


A0732: First, set

  ==>      helo_allow_chars = _


         This tells Exim not to reject the EHLO or HELO command immediately. Once
         you have done that, you can test for the underscore in an ACL. For
         example, to log a warning for hosts in your LAN, and reject for other
         hosts, you could do something like this:


  ==>      deny  message = Underscores are not valid in host names
                 hosts = ! +lan_hosts
                 condition = ${if match{$sender_helo_name}{_}{yes}{no}}


  ==>      warn  log_message = Accepted underscore from [$sender_host_address]
                 condition = ${if match{$sender_helo_name}{_}{yes}{no}}



  Q0733: Is there any way to tell Exim not to lookup the IP address against any
         DNS black list if the connection is over IPv6?


A0733: Use this condition in your ACL:

  ==>      condition = ${if match{${mask:$sender_host_address/0}}\
                        {${mask:::0/0}}{no}{yes}}


         From Exim 4.23 onwards, this can be simplified to


  ==>      condition = ${if isip6{$sender_host_address}{no}{yes}}



Q0734: How do MailScanner and Exiscan compare? What are the pros and cons?

  A0734: The big advantage of Exiscan is that it can reject messages at SMTP time
         before you have accepted responsibility for them, which means you don't
         have to deal with bouncing messages and thereby becoming a collateral
         spammer.


         The big advantage of MailScanner is that it gives you much greater
         control over the load on your machines. You configure it according to
         the maximum processing capacity of your computer and it will not exceed
         that; in fact because it deals with messages in batches the cost of
         processing a message actually goes down slightly as the load increases,
         because the per-batch costs are shared by more messages.


         With Exiscan, you have to rely on Exim's load protection mechanisms,
         which basically means that you have to stop accepting messages when your
         machine gets too loaded. This is bad if the machine happens to be an
         SMTP smarthost. You therefore need more overcapacity with Exiscan than
         with MailScanner.



Q0735: How can I block non-FQDNs in HELO/EHLOs?

  A0735: Many workstation clients send single-component names; take care that you
         do not block legitimate mail. With that proviso, you can do it using
         something like this in an ACL:


  ==>     drop  message = HELO doesn't look like a hostname
             log_message = Not a hostname
             condition = ${if match{$sender_helo_name} \
                  {\N^[^.].*\.[^.]+$\N}{no}{yes}}


         This means: Drop the HELO unless it contains a dot somewhere in the HELO
         string, but the string may not begin or end with a dot. Thus, the
         imposed minimum length is 3 characters.


         The data for HELO/EHLO doesn't have to be a host name; it may
         legitimately be an IP address literal instead. The above test succeeds
         with an IPv4 address literal, but if you want also to accept IPv6
         address literals, you will have to modify the regular expression.



  Q0736: Is it possible to tell exim to drop the connection after a server
         attempts to send a message to a number of unknown users?


  A0736: Yes. Use \$rcpt_fail_count$\ and the \^drop^\ ACL command, as in this
         example:


  ==>      drop  message = Too many unknown users
                 condition = ${if >{$rcpt_fail_count}{15}{yes}{no}}



Q0737: Is there some way to tell Exim not to consider 127.0.0.1 as a valid MX?

A0737: See Q0319.


  Q0738: How can I configure Exim to delay the SMTP connection if more than 10
         invalid recipients are received in one message?


A0738: Put something like this in your RCPT ACL:

  ==>      deny  message         = Max $rcpt_fail_count failed recipients allowed
                 condition       = ${if >{$rcpt_fail_count}{10} {1}}
                 ! verify        = recipient
                 delay           = ${eval: $rcpt_fail_count * 10}s
                 log_message     = $rcpt_fail_count failed recipient attempts


         This example increases the delay for each failed recipient.



Q0739: Does Exim support SPF?

A0739: An Exim ACL can be used. See \?http://spf.pobox.com/downloads.html?\.



8. REWRITING ADDRESSES

Q0801: How can I get Exim to strip the hostname from the sender's address?

A0801: If you set up a rewriting rule in the following form:

  ==>       *@*.your.domain  $1@???


         then Exim will rewrite all addresses in the envelope and the headers,
         removing anything between \"@"\ and \"your.domain"\. This applies to all
         messages that Exim processes. If you want to rewrite sender addresses
         only, the the rule should be


  ==>       *@*.your.domain  $1@???  Ffrs


         This applies the rule only to the envelope sender address and to the
         ::From::, ::Reply-to::, and ::Sender:: headers.



  Q0802: I have Exim configured to remove the hostname portion of the domain on
         outgoing mail, and yet the hostname is present when the mail gets
         delivered.


  A0802: Check the DNS record for your domain. If the MX record points to a CNAME
         record instead of to an A record, some MTAs (not Exim) are liable to
         rewrite addresses, changing your domain name to its ``canonical'' form,
         as obtained from the CNAME record.



  Q0803: I want to rewrite local addresses in mail that goes to the outside
         world, but not for messages that remain within the local intranet.


  A0803: You can use the \headers_rewrite\ option on a transport to do this.
         The rewriting will then apply to just those copies of a message that
         pass through the transport. The \return_path\ option can similarly be
         used to rewrite the sender address. There is no way of rewriting
         recipient addresses at transport time. However, as these are by
         definition remote addresses, you probably don't want to rewrite them.


         You have to set up the configuration so that it uses different SMTP
         transports for internal and external mail. If you are using a single
         router in both cases, you could configure it like this:


  ==>    dnslookup:
           driver = dnslookup
           transport = ${if match{$domain}{\N\.my\.domain$\N}{int_smtp}{ext_smtp}}


         This example uses the \%int_smtp%\ transport for domains ending in
         \(.my.domain)\, and \%ext_smtp%\ for everything else. The \%ext_smtp%\ transport
         could be something like this:


  ==>    ext_smtp:
           driver = smtp
           headers_rewrite = *@*.my.domain \
                ${lookup{$1}cdb{/etc/$2/mail.handles.cdb}{$value}fail}
           return_path = \
             ${if match{$return_path}{\N^([^@]+)@(.*)\.my\.domain$\N}\
              {\
              ${lookup{$1}cdb{/etc/$2/mail.handles.cdb}{$value}fail}\
              }\
              fail}


         This example uses a separate file of local-to-external address
         translations for each domain. This is not the only possibility, of
         course. The \headers_rewrite\ and \return_path\ options apply the same
         rewriting to the header lines and the envelope sender address,
         respectively.



  Q0804: I'm using this rewriting rule to change login names into ``friendly''
         names, but if mail comes in for an upper case login name, it doesn't
         get rewritten.


  ==>     *@my.domain     ${lookup{$1}dbm{/usr/lib/exim/longforms}\
               {$value}fail}@??? bcfrtFT


         The longforms database has entries of the form:


  ==>      ano23: A.N.Other


  A0804: Replace \"$1"\ in your rule by \"${lc:$1}"\ to force the local part to lower
         case before it is used as a lookup key.



Q0805: Is it possible to completely fail a message if the rewrite rules fail?

  A0805: It depends on what you mean by ``fail a message'' and what addresses you
         are rewriting. If you are rewriting recipient addresses for your local
         domain, you can do:


  ==>     *@dom.ain  ${lookup{$1}dbm{/wher/ever}{$value}{failaddr}}  Ehq


         and in your alias file put something like


  ==>     failaddr:   :fail: Rewriting failed


         This fails a single recipient - others are processed independently.



  Q0806: I'm using \$domain$\ as the key for a lookup in a rewriting rule, but its
         contents are not being lowercased. Aren't domains supposed to be handled
         caselessly?


  A0806: The value of \$domain$\ is the actual domain that appears in the address.
         It could of course be lower cased, but I know that would cause some
         unhappiness, because some people have mixed-case domain names which look
         silly if the case is changed. Thus, one wants to preserve the case in
         rewrites such as


  ==>      *@*.TheRap.com   something@$domain


         because ``therap'' doesn't look like two words. I know it seems trivial,
         but it is important to some people - especially if by some unfortunate
         accident the lowercased word is something indecent.


         You can trivally force lower casing by means of the \"${lc:"\ operator.
         Instead of \"$domain"\ write \"${lc:$domain}"\.



  Q0807: I want to rewrite local sender addresses depending on the domain of the
         recipient.


  A0807: In general, this is not possible, because a message may have more than
         one recipient and Exim keeps just a single copy of each message. It may
         also deliver one copy of a message with several recipient addresses.
         You can do an incomplete job by using a regular expression match in a
         rewrite rule to test, for example, the contents of the ::To:: header. This
         would work except in cases of multiple recipients.




9. HEADERS

  Q0901: I would like add some custom headers to selected outgoing mail based on
         a specific domain and the subject line.


A0901: To the remote_smtp transport, add something like

  ==>      headers_add = ${if and{\
                         {eq{$domain}{spec.dom}}\
                         {matches{$h_subject:}{whatever}}}\
                         {Content-Type: text/html; charset="us-ascii"} fail }


         This example shows a ::Content-Type:: header, but you can have anything you
         like, and multiple headers can be inserted by using \"@\n"\ to separate them.



  Q0902: Is it possible to have Exim add a header to only certain local parts of
         outgoing mail?


  A0902: Only if you arrange for each such local part to receive its own private
         copy of the mail. See \max_rcpt\ in the SMTP transport. If you set this
         to 1, you could use conditions in an expansion string to add or not add
         a header.



Q0903: How can I remove some part of the ::Received:: header?

A0903: Set \received_header_text\.


Q0904: How I can insert the PGP header line using Exim filters?

  A0904: You can't insert headers in a user filter. A system filter can do so,
         but the inserted lines then are included for all recipients.



  Q0905: I know I can use a system filter to replace certain headers in messages,
         but how can I add text to existing headers? I want to add [SPAM] to
         the subject line of messages that appear to be spam.


  A0905: You can only do this in a round about way, using filter commands like
         this:


  ==>      headers add "New-Subject: SPAM: $h_subject:"
           headers remove subject
           neaders add "Subject: $h_new-subject:"
           headers remove new-subject


         This trick works only in system filters, where the commands are obeyed
         in order, and affect the master list of headers that apply to the whole
         message. You cannot do this with the \headers_add\ and \headers_remove\
         options on drivers.




10. PERFORMANCE

  Q1001: I'm running a large mail server. Should I set \split_spool_directory\ to
         improve performance?


  A1001: Splitting the spool directory has most benefit if there are times when
         there are a large number of messages on the queue. If all mail is
         delivered very quickly, and the queue is always less than, say, a few
         hundred messages, there isn't any need to do this. With larger queues,
         there is a definite performance benefit to splitting the spool. It shows
         up earlier on some types of filing system, compared with others.


         Exim was not designed for handling large queues. If you are in an
         enviroment where lots of messages remain on the queue for long periods
         of time, consider implementing a back up host to which you pass these
         messages, so that the main host's queue remains short. You can use
         \fallback_hosts\ to do this, or a router that is conditional on
         \$message_age$\.



Q1002: How well does Exim scale?

  A1002: Although the author did not specifically set out to write a high-
         performance MTA, Exim does seem to be fairly efficient. The biggest
         server at the University of Cambridge (a large Sun box) goes over
         100,000 deliveries per day on busy days (it has over 20,000 users).
         There was a report of a mailing list exploder that sometimes handles
         over 100,000 deliveries a day on a big Linux box, the record being
         177,000 deliveries (791MB in total). Up to 13,000 deliveries an hour
         have been reported.


         These are quotes from some Exim users:


         "... Canada's largest internet provider, uses Exim on all of our mail
         machines, and we're absolutely delighted with it. It brought life back
         into one of our machines plagued with backlogs and high load averages.
         Here's just an example of how much email our largest mail server
         (quad SS1000) is seeing ... "  [230,911 deliveries in a day: 4,475MB]


         "... Exim has to ... do gethostbyname()s and RBL lookups on all of the
         incoming mail servers, and he runs from inetd (TCP Wrappers connected).
         All the same, it seems to me that he runs as fast as lightning on our
         SCO 5.0.4 box (1 Pentium 166) - far faster than MMDF which I (and many
         customers) had before."


         "On a PII 400 with 128M of RAM running Linux 2.2.5, I have achieved
         36656 messages per hour (outgoing unique messages and recipients). For
         about a 5 minute period, I was able to achieve an average of 30 messages
         per second (that would be 108000 m/hour)! We are using: (options that
         make a difference):


  ==>      queue_only
           split_spool_directory
           queue_run_max = 1
           remote_max_parallel = 1


         We have a cron job hat runs every five minutes that spawns 5 \"exim -q"\ if
         there are less that 120 exim processes currently running. We found
         that by manually controlling the concurrency of \"exim -q"\ processes
         contending for the spool for \%remote_smtp%\ delivery that we gained
         considerable performance - 10000 m/hour."



  Q1003: We have a large password file. Can Exim use alternative lookups during
         delivery to speed things up?


  A1003: If you are using FreeBSD, this problem should not arise, because it
         automatically uses an indexed password file. In some other operating
         systems you can arrange for this to happen too. On Linux, for example,
         all you need to do is


  ==>      # cd /var/db
           # make


         and put \"db"\ before \"files"\ in any \(/etc/nsswitch.conf)\ lines you want to
         use db for.


         On systems that do not include support for indexed password files, you
         can build one yourself, and reference it from the Exim configuration.
         For example, for routing to local mailboxes you could use this:


  ==>      localuser:
             driver = accept
             condition = ${lookup{$local_part}cdb{/etc/passwd.cdb}{yes}{no}}
             transport = local_delivery
             user = ${extract{1}{:}{${lookup{$local_part}cdb{/etc/passwd.cdb}}}


         This assumes a cdb version of the password file.



  Q1004: I just wondered if it might be helpful to put the hints database on a
         RAM disk during regular operation. Did anybody try that yet?


  A1004: A user reported thus: ``I have found that this works great under Solaris.
         Make a RAM disk partition and keep everything in the \(db)\ directory on
         it. However, when I try the same thing on Linux, I don't see the same
         boost. I think that Linux's file buffer cache works about the same.
         Plus, this leave more room for processes to run.''


         There have been other reports that Linux's delayed buffer write provides
         better overall performance in general.


         Apparently there is support in the Solaris kernel for a delayed writing,
         as in Linux, but Sun's server policy is to have it disabled so that you
         don't lose so much if the server crashes. There is a program called
         \^fastfs^\ to enable and disable this support. You have to download and
         compile it yourself; find it by looking for \"fastfs.c"\ in a search
         engine. Solaris performance is reported to be much improved, but you
         should take care to understand the potential hazards. In particular,
         \^fsck^\ may be unable to ``fix'' disks automatically after a crash.



  Q1005: A lot of incoming mail is pushing up my system load too much, and there
         are many Exim processes. How can I control this?


  A1005: Have you set any of the Exim configuration options that limit what it
         does under high load? For example, queue_only_load, deliver_queue_load_max?
         See the list in the section entitled \*Resource control*\ in the manual.


         It sounds like a lot of simultaneous incoming mail pushes your system
         into uncontrolled overload. The multiple Exim processes are probably
         just multiple incoming messages. You can use the \^exiwhat^\ utility to
         confirm this.




11. MAJORDOMO

Q1101: How do I set up Majordomo to work with Exim?

  A1101: Users have found several ways of setting up Exim for use with Majordomo.
         One way has been documented at
         \?http://www.averillpark.net/exim/majordomo.html?\.


         Somewhere in the Majordomo docs or FAQ it mentions using batchmail or
         other additional programs to improve the performance of large lists.
         They are not needed with Exim, and their use can actually make things
         worse. However, it's a good idea to set \remote_max_parallel\ to a value
         greater than 1 in the Exim configuration.



  Q1102: I have set \$mailer$\ in \(majordomo.cf)\, but it still isn't setting the
         sender correctly in the messages it sends.


  A1102: Make sure you have got the quoting correct in the \$mailer$\ setting. For
         example,


  ==>      $mailer = "$sendmail_command -oi -oee -f$sender\@lists.mydomain.de";


         is not correct. It needs three backslashes, not one, and the $ at the
         start of \$sender$\ has to be escaped with a backslash.



  Q1103: I'm trying to set up majordomo, but I'm getting a wrong mode error
         when I try to send it mail.


  A1103: Check the mode of \(/var/lib/majordomo/lists/lists.aliases)\ and compare it
         with the setting of the \modemask\ option in the Majordomo aliases
         router. This option specifies bits which must not be set for the alias
         file, and it defaults to 022.



  Q1104: I'm getting return code 9 from \(/home/majordomo/majordomo-1.94.4/wrapper)\
         when it is passed a message from Exim.


  A1104: A problem like this turned out to be the Perl version that came with
         RedHat 5.2. Rebuilding Perl 5.005x solved it.



  Q1105: Exim is complaining about an invalid command line when Majordomo tries
         to send it a message for delivery.


  A1105: Take a look at your \(majordomo.cf)\ file,  It should have something that
         looks like


  ==>      $sendmail_command = "/usr/lib/sendmail";


         and another line like


  ==>      $mailer = "$sendmail_command -oi -oee -f\$sender";


         If you have modified \^resend^\ (one of the majordomo programs) to use
         \$sendmail_command$\ instead of \$mailer$\ you will be calling Exim with no
         command line arguments.




12. FETCHMAIL

  Q1201: When I run fetchmail, I get the error \*SMTP listener doesn't like
         recipient address xxx@localhost*\.


  A1201: Make sure that //localhost// is recognized as a domain that is to be
         delivered locally. If you are using the default Exim run time
         configuration, you'll see a line near the top like this:


  ==>      domainlist local_domains = @


         Change it to


  ==>      domainlist local_domains = @ : localhost



  Q1202: I'm currently using Exim with fetchmail and I'd like to use the RBL on
         Exim, but will it work? Do I need to configure fetchmail any particular
         way? As far as Exim knows, all mail is coming from 127.0.0.1. Will it
         check the source address against RBL? Or will it check the ::From:: header?


  A1202: It will check 127.0.0.1 (not very useful). The point of the RBL is to
         keep messages from black-listed hosts out of your machine. If you are
         using fetchmail, you have got the messages into your machine before you
         approach Exim. That kind of defeats the purpose of the RBL. The right
         way to do this would be for the host from which you fetch your mail to
         do the RBL checking and insert some kind of warning header for you to
         test, as Exim does if you run RBL checks in warning mode.




13. PERL

  Q1301: Exim built with Perl support exits with the error message \*./exim: can't
         load library 'libperl.so'*\.


A1301: If you are using BSDI, see Q9401.


  Q1302: Exim built with Perl support exits with several error messages of the
         form \*undefined reference to `PL_stack_sp'*\.


  A1302: This has been seen on FreeBSD systems that had two different versions of
         Perl installed, the older with an \^a.out^\ library and the newer with an
         ELF library. Ensure that the older package is removed.




14. DIAL-UP AND ISDN

  Q1401: When I'm not connected to the Internet, how can I arrange for mail to
         other hosts on my local network to be delivered, while at the
         same time mail to Internet hosts is queued without any delivery
         attempts?


  A1401: Use the \queue_domains\ option to control which domains are held
         on the queue for later delivery. For example,


  ==>      queue_domains = ! *.localnet


         allows delivery to domains ending in \(.localnet)\, while queueing all the
         others.



  Q1402: I have a dial-up machine, and I use the \queue_smtp_domains\ option so
         that remote mail only goes out when I do a queue run. However, any email
         I send with an address \(anything@???)\ is returned within about 15
         minutes saying \*retry time exceeded*\, and all addresses are affected.


  A1402: You should be using \queue_domains\ rather than \queue_smtp_domains\.
         With the latter, Exim is trying to route the addresses, which involves a
         DNS lookup. This is presumably timing out, causing a retry time to be
         set for the domain, and somehow a valid lookup never happened before the
         maximum retry time (default of 4 days) passed. Hence the bounce. The
         fact that it is \(aol.com)\ is probably not relevant. You should probably
         also be using \-qq-\ to do your queue run rather than \-q-\.



  Q1403: How should Exim be configured when it is acting as a temporary storage
         system for a domain on a dial-up host?


  A1403: Exim isn't really designed for this, but... The lowest-numbered MX
         record for the domain should be pointing to the dial-up host. A higher
         numbered MX record (lower priority) should point to the Exim server that
         is acting as a temporary storage system.


         You should set a large retry time for the domain, so that Exim doesn't


----------------------------------------------
Diff block truncated. (Max lines = 10000)
----------------------------------------------