#!/usr/bin/env perl
=comment
------------------------------------------------------------------------------
  
SWL - Short Web Language
by Kris Kowal
Version 2.9
2004-10-02-21-15 PDT

Requirements:

  Perl 5

    Perl is free software.  Most flavors of Unix, including Mac OS X, come
     with Perl already installed.  If you find that your system does not have
     Perl already, consult these links:

    for MS Windows: http://www.activestate.com/Products/ActivePerl/
    other systems: http://www.perl.com/download.csp

  A compatible operating system.  SWL is known to run with the following
   operating systems, but should work anywhere there is Perl:
   
    Microsoft Windows 2000 or newer
    Solaris
    Linux
    Mac OS X

Installation Instructions:

  Run this installation program:
    perl swl-2-9-install.pl [switches]
    
      --prefix=[prefix]
          selects an alternate install location.  defaults are
            /usr/local for UNIX
            C:\Program Files for Microsoft Windows
      --swl=[swl]
          selects an alternate swl location.  defaults are
            share/swl for UNIX
            SWL for Microsoft Windows
      --bin=[bin]
          selects an alternate bin location (pertains to UNIX only)
      --version
          shows version information
      --help
          shows this message

------------------------------------------------------------------------------
=cut

use strict;

sub croak {
  print shift;
  if ( $::os eq 'windows' ) {
    system "pause";
  }
  exit shift || -1;
}

sub mkpath {
  my $path = shift;
  my $mode = shift || 0777;
  my @paths = split /[\\\/]/, $path;
  for my $path ( map { join '/', @paths[0..$_] } 0..$#paths ) {
    if ( not -e $path and $path ne '' ) {
      my $code = mkdir $path, $mode;
      if ( not $code ) {
        if ( $::os eq 'windows' ) {
          croak << "END";
Unable to create path '$path'.  This may be because you lack
administrative priviledges.  If this is the case, log in as an
administrator.
END
	} else {
          croak << "END";
Unable to create path '$path'.  This may be because you are not
logged in as root on this system.  If this is the case, use 'su' or
'sudo' to gain adminstrator priviledges and try again.  If you aren't
familiar with either of these commands, consult 'man su' and
'man sudo'.
END
	}
      }
    }
  }
  return 1;
}

sub version {
  print << "END";
SWL (Short Web Language) 2.9 Installer
(C) 2001-2004 by Kris Kowal
END
}

sub help {
  print << "END";
Usage: perl swl-2-9-install.pl [switches]

  --prefix=[prefix]
      selects an alternate install location.  defaults are
        /usr/local for UNIX
        C:\Program Files for Microsoft Windows
  --swl=[swl]
      selects an alternate swl location.  defaults are
        share/swl for UNIX
        SWL for Microsoft Windows
  --bin=[bin]
      selects an alternate bin location (pertains to UNIX only)
  --version
      shows version information
  --help
      shows this message

END
}

sub path {
  my $path = shift;
  1 while $path =~ s/^(.*)\/\//\//;
  return $path;
}

# review arguments
my $prefix;
my $swl;
my $bin;
my $os;
my $show_version;
while ( $_ = shift ) {
  if ( /^--(.+)/ ) {
    $_ = $1;
    if ( 0 ) {
    } elsif ( /^help$/ ) {
      version;
      help;
      exit 0;
    } elsif ( /^version$/ ) {
      version;
      exit 0;
    } elsif ( /^os=(.*)$/ ) {
      $os = $1;
    } elsif ( /^prefix=(.*)$/ ) {
      $prefix = glob $1;
    } elsif ( /^bin=(.*)$/ ) {
      $bin = $1;
    } elsif ( /^swl=(.*)$/ ) {
      $swl = $1;
    } else {
      print STDERR "ERROR: invalid switch '$_'\n";
    }
  } elsif ( /^-(.+)$/ ) {
    for ( split '', $1 ) {
      if ( 0 ) {
      } else {
        print STDERR "ERROR: invalid switch '-$_'\n";
      }
    }
  } else {
    $prefix = $_;
  }
}

my @oss = ( 'unix', 'windows' );
if ( grep { $os eq $_ } @oss ) {
} elsif ( -e '/usr' ) {
  $os = 'unix';
} elsif ( -e 'C:/Program Files' ) {
  $os = 'windows';
  $::os = 'windows'; # changes croak behaviour
} else {
  croak << "END";
Unable to install because the installer is unable to determine which
operating system to configure SWL for.  Unix varieties, including Mac OS X,
typically contain a '/usr' directory.  Microsoft Windows machines typically
contain a 'C:/Program Files' directory.  Aparantly your system has neither.
To specify what system you are running, use the '--os' switch at the command
line, for example:

  perl swl-2-9-install.pl --os=windows
  perl swl-2-9-install.pl --os=unix

END
}

if ( $prefix ) {
} elsif ( $os eq 'unix' ) {
  $prefix = '/usr/local';
} elsif ( $os eq 'windows' ) {
  $prefix = 'C:/Program Files';
} else {
  croak << "END";
Unable to install because the system was unable to determine the installation
path prefix.  The installer can infer this location from what operating system
you appear to be using.  You may explicitly specify your os with the --os=[os]
switch.  For example:

  perl swl-2-9-install.pl --os=windows
  perl swl-2-9-install.pl --os=unix

Additionally, you can specify an installation prefix with the --prefix=[prefix]
switch.  For example:

  perl swl-2-9-install.pl --prefix=\$HOME
  perl swl-2-9-install.pl --prefix=/usr
  perl swl-2-9-install.pl "--prefix=%USERPROFILE%"

END
}

# setup bin location
if ( $bin ) {
  $bin = path "$prefix/$bin";
  mkpath $bin, 0755;
} elsif ( $os eq 'unix' ) {
  $bin = "$prefix/bin";
}

# verify bin location
if ( $os eq 'unix' ) {
  if ( not -e $bin or not -W $bin ) {
    croak << "END";
Unable to install.  The installer will not be able to write to
'$bin'.  This may be because you are not logged in as 'root'.

If you are the system administrator and attempting to install
SWL for all users, log in as 'root' using 'su' or 'sudo'.  For
more information on 'su' and 'sudo', refer to 'man su' and
'man sudo'.

If you are using a Macintosh, '$bin' may not exist.  If that is
the case, try overriding the default bin location:

  perl swl-2-9-install.pl --bin=/usr/bin

If you just want to install SWL for your personal use in your
own home directory, try overriding the installation prefix:

  perl swl-2-9-install.pl --prefix=\$HOME

END
  }
}

# setup swl location
if ( $swl ) {
} elsif ( $os eq 'unix' ) {
  $swl = 'share/swl';
} elsif ( $os eq 'windows' ) {
  $swl = 'SWL';
}
$swl = path "$prefix/$swl";

# check for previous installation
if ( -e $swl ) {
  croak << "END";
Unable to install.  There seems to be an existing installation in the way.
If you intent to proceed, remove the '$swl' installation
and try again.
END
}

mkpath $prefix, 0755;

chdir $prefix or croak << "END";
Installation failed because this installer was unable to enter the
directory '$prefix'
END

mkpath $swl, 0755;

chdir $swl or croak << "END";
Installation failed because this installer was unable to enter the
directory '$swl'
END

my $file_length = 0;
my $file = '';
my $content_length = 0;
my $content = '';

binmode DATA;
while ( read DATA, $file_length, 4 ) {
  read DATA, $file, unpack 'N', $file_length;
  read DATA, my $type, 1;

  if ( $type eq 'd' ) {

    mkpath $file, 0755 or croak << "END";
Installation failed because this installer was unable to create the
directory '$file'
END

  } elsif ( $type eq 'f' ) {

    read DATA, $content_length, 4;
    read DATA, $content, unpack 'N', $content_length;
    
    $content =~ s{#PATH#([.\n]*)#/PATH#}{#PATH#\n'$swl',\n#/PATH#};

    open FILE, ">$file" or croak << "END";
Installation failed because this installer was unable to write the
file '$file'
END

    binmode FILE;
    syswrite FILE, $content, -1  + length $content; 
    close FILE;

  } else {

    last;

  }
}

if ( $os eq 'unix' ) {

  chmod 0755, "$swl/swl.pl" or croak << "END";
Installation failed because this installer was unable to set execute
permission to 'swl.pl'
END

  mkpath $bin, 0755;
  system "ln -s $swl/swl.pl $bin/swl";

  croak << "END", 0;
Installation complete.  The 'swl' command should now be available.  For
information on how to use Short Web Language, use the 'swl --help' command.
Documentation in HTML form resides in '$swl/doc'.
END

} elsif ( $os eq 'windows' ) {
  system "install.reg";

  croak << "END", 0;
Installation complete.  You should be able to compile swl source code by
double-clicking on a .swl file.  Documentation in HTML form resides in
'$swl\\doc'.
END
}

__DATA__
   readme.html f  <HTML>

<!-- by SWL 2.8 Sat Oct  2 21:12:07 2004 -->

<head>
<title>SWL Document</title>
<meta http-equiv=Expires content=0>
<style>

	body
	{
			font-family: Verdana,Helvetica,Arial,Sans Serif;
			font-size: 15px;
			color: #000000;
	}

	p, tr, thred, tfoot, th, td, dl, ol, ul, blockquote, span, br
	{
			font-size: 15px;
	}
	
</style>
</head>

<BODY>

<table border=0 cellspacing=0 cellpadding=0 width=700></tr><td>

<p>


		<h1>
			SWL - Short Web Language
		</h1>
	and Associated Interpreter
	<br>Distribution 9
	<br>Copyright 2001-2004 by Kris Kowal
</p>
<p>SWL may be distributed under the terms of this <a href="license.txt">GNU General Public License</a>.</p>
<p><a href="doc/index.html">Documentation</a></p>

</td></tr></table>


</BODY>

</HTML>    swl.pl f  #!/usr/bin/env perl

################################################################################
# 
# 
# SWL - Short Web Language
# by Kris Kowal
# Version 2.9
# 2004-10-02-21-15 PDT
# 
# SWL may be distributed under the terms of this General Public License.
# 
# converts text/swl to text/html using swl.pm
#
################################################################################

sub version {
print << "END";
SWL (Short Web Language) Version $SWL::VERSION
(C) Copyright 2001-2004 by Kris Kowal
END
}
sub usage {
version;
print << "END";
Usage: swl [[-fFr] input [-o output] ...] ...]

    input is the name of an input file like moo.swl
    output is the name of an output file like moo.html

    -f forces all following files to be written even if the input is
        older than the output
    -F stops forcing (default)
    -r same as "-f ."
    -o overrides the default output file name

    -q quiet
    -v verbose (default)

    input and output can be '-' to use standard io instead of files
END
}

require lib;
for my $path (
#PATH#
'/usr/local/share/swl',
#/PATH#
) {
  if ( -e $path ) {
    import lib $path;
    last;
  }
}
require swl;

my @args = @ARGV;
if ( @args == 0 ) {
	@args = ( '.' );
}

my $force = 0;
my $recur = 0;
my $verbose = 1;
while ( my $arg = shift @args ) {

	if ( $arg =~ /^--(.*)/ ) {
		$1 eq 'version' and do {
			$verbose and version();
			exit;
		};
		$1 eq 'help' and do {
			$verbose and usage();
			exit;
		};
		$verbose and print STDERR "Invalid switch: $1\n";
	} elsif ( $arg =~ /^-(.+)/ ) {
		for $arg ( split '', $1 ) {
			$arg eq 'f' and do {
				$force = 1;
				next;
			};
			$arg eq 'F' and do {
				$force = 0;
				next;
			};
			$arg eq 'r' and do {
				unshift @args, '.';
				next;
			};
			$arg eq 'h' and do {
				$verbose and usage();
				next;
			};
			$arg eq 'q' and do {
				$verbose = 0;
				next;
			};
			$arg eq 'Q' and do {
				$verbose = 1;
				next;
			};
			$arg eq 'v' and do {
				$verbose = 1;
				next;
			};
			$arg eq 'V' and do {
				$verbose = 0;
				next;
			};
			$verbose and print STDERR "Invalid switch: $arg\n";
		}
		next;
	}

	# todo: use glob instead
	my @files = ( $arg );
	while ( my $in = shift @files ) {

		next if $in eq '';

		# construct the default output
		my $out;
		if ( $in ne '-' ) {
			$in = SWL::Grok( $in );
			$out = $in;
			$out =~ s/\.[^\.]*$//;
			$out .= ".html" if $out !~ /\./;
		} else {
			$out = '-';
		}

		# check whether an output override follows
		my $next = shift @args;
		if ( $next eq '-o' ) {
			$out = shift @args;
		} else {
			unshift @args, $next;
		}
		
		if ( $in ne '-' and -d $in ) {
			push @files, grep { -d $_; } glob("$in/*");
			push @files, glob("$in/*.swl");
		} elsif (
			$force or
			$out eq '-' or
			not -e $out or
			( stat "$in" )[9] > ( stat "$out" )[9]
		) {
			if ( $verbose and $out ne '-' ) {
				print "$out\n";
			}
			SWL::File( "$in", "$out" ) or
				$verbose and print STDERR "Error: Could not compile '$in'\n";
		}

	}

}

exit;

    doc d   doc/tag_paragraphs.swl f   1>Multiple Paragraphs Tag

">p&gt;

This is the default tag.  It interprets each line as a paragraph.

->>

< tag_paragraphs.swl >
    doc/tag_include.swl f  1>Include File Tag

">+&gt;

The include tag compiles a <i>.swlt</i> SWL template or <i>.swl</i> SWL document into the current file.
The file extention need not be specified.  Swl will search for the file name first in the local folder, then the SWL library folder, then the absolute directory.  It will try to match your file name with the <i>.swlt</i>, <i>.swl</i>, or <i>.txt</i> in respective order if not specified.

3>Note:
Text files are compiled just like SWL files are.  This allows for some nifty tricks:
">".+&gt;<i>file</i>
...will put the text file into your document under the < tag_pre.html preformatted > tag.
">t.+&gt;<i>file</i>
...will put a tab-delimitted text file into a table.

3>Note:
The nearest <i>local.swlt</i> file up the directory tree is automatically included in all SWL files.


->>

2>Include Other Formats

You can include lines of plain text without SWL compilation or even tables of delimited data using '+' sub-tags.  SWL currently will import the following data formats other than swl:

t>
	h>sub-tag	description	assumed file extentions (in respective order)
	txt	plain text files	txt
	html	html files	html, htm, txt
	csv	comma separated values	csv, txt
	tab	tab delimited values	tab, txt
/>

3>Example:
">
	t.+.csv&gt;<i>filename</i>
/>


->>

< tag_include.swl >
    doc/tag_variable.swl f   1>Variable Define Tag
">@&gt;

Example:
">
	>@>
		>&nbsp;table>
			>bgcolor>000000
		>/>
	>/>
/>

< tag_variable.swl > | < local.swlt >
    doc/tag_pre.swl f   1>Pre-formatted Text Tag

">"&gt;

Preserves plain text formatting.

3>Example:
">
	"&gt;
		How Now Brown Cow?
		Green Eggs and Ham.
	/&gt;
/>
yields:
">
	How Now Brown Cow?
	Green Eggs and Ham.
/>

->>

< tag_pre.swl >
    doc/introduction.swl f  _
1>Introduction

When writing web-sites, you typically either write the HTML code or use a 'WYSIWYG' (What You See Is What You Get) editor to make it for you.  Some sites use dynamic content languages, e.g., ASP or PHP, to generate their code for them on the fly.  SWL is an efficient alternative to each of these.

2>What is SWL?

SWL is, simply put, abbreviated HTML.  In fact, with a 2 character header, most HTML files can be put directly into a SWL file and compile back to the same HTML they started out as.  <b>But</b>, SWL offers an alternative to writing 'bulk HTML', through the use of 'line-tags'.  A line-tag is a brief ID (usually a single character) followed by a closed angle bracket, '&gt;'.
Using line-tags, you can reduce:
">
	&lt;table&gt;
		&lt;tr&gt;
			&lt;td&gt;
				Cell 1
			&lt;/td&gt;
			&lt;td&gt;
				Cell 2
			&lt;/td&gt;
		&lt;/tr&gt;
		&lt;tr&gt;
			&lt;td&gt;
				Cell 3
			&lt;/td&gt;
			&lt;td&gt;
				Cell 4
			&lt;/td&gt;
		&lt;/tr&gt;
	&lt;/table&gt;
/>
To:
">
	t&gt;
		Cell 1	Cell 2
		Cell 3	Cell 4
	/&gt;
/>
And that is a mere scratch of its <b>power</b>.

j>benefits
2>Benefits

*>
	<b>Easy</b> to learn.  <b>Easy</b> to write.  <b>Easy</b> to use.
	<b>Brevity.</b>  SWL code is as short as it gets.  SWL source files (<u><i>without</i></u> file includes) tend to be one half to one third the size of the HTML they generate.
	<b>Brevity.</b>  The HTML that SWL outputs doesn't include unnecessary information.  WYSIWYG editors tend to put lots of junk line breaks and font information.
	<b>Brevity.</b>  Copy a document you want to publish into a text 'SWL' file.  It's almost ready for publication.  Your paragraphs are already set.  To format your headings will take 2 characters each.  To format your tables usually takes the addition of 4 characters.
	<b>No loss in developing potential.</b>  Anything that SWL doesn't recognize won't be changed in your object HTML.
	<b>Plays well with others.</b>  SWL is perfect for use with CSS (Cascading Style Sheets).  A lot of the junk that WYSIWYG editors pump out is for formatting each individual section.  Using a style sheet in your SWL template will automatically make every similar tag look the same, without junk code.  Most document editors (e.g., MS Word, Adobe Pagemaker) already include 'style' information which can be directly interpreted into SWL.  You could even write your PHP in SWL with just a flip of the template.
	<b>Plays well with others.</b>  SWL can import data from plain text, HTML, tab delimited text, and comma separated values (CSV).
	<b>Clean HTML.</b>  The ugliest SWL code outputs to easy-to-read HTML.  SWL indents nested tags and puts white space between major sections.
	<b>Efficiency</b>.  Once you compile your SWL into HTML, it's done.  When you use PHP and ASP to generate your pages, your web server has to start a program to compile it <b>every</b> time someone looks at it on the web.  You only have to compile SWL once.
	<b>Extensibility</b>.  You can make your own 'SWL tags' on the fly in your document.  You can use SWL's internal variables to change the ones it already has.  You can create templates and template modifications on the fly in your SWL documents, or include them from an external SWL library.
	<b>You don't have to be a programmer.</b>  You could teach a monkey to write 'SWL' and they would like it so much that they would forget to eat.
	Etc.
/>
	

< introduction.swl >
    doc/tag_table.swl f  	1>Table Tags

">t&gt;


The table tag allows you to create concise tables with any degree of particularity.
Following are samples of table code an the results they yield.  You will probably only find use for the first two though the remaining are available just in case you need more functionality.


->>


2>t

The simplest table consists of rows and columns where each row is represented by a line and the columns are separated with tabs.

3>Example:
">
	t&gt;
		1,1	1,2	1,3
		2,1,	2,2	2,3
	/&gt;
/>
yields:
t>
	1,1	1,2	1,3
	2,1,	2,2	2,3
/>


2>Automatic Column Sizing

When swl encounters blank cells, it will automatically increase the 'span' of the cell to their immediate left.

3>Example:
">
	t&gt;
		1,1	1,2	1,3
		2,1,	   	2,3
	/&gt;
/>
yields:
t>
	1,1	1,2	1,3
	2,1,		2,3
/>

To get around this feature, place periods in blank cells:

3>Example:
">
	t&gt;
		1,1	1,2	1,3
		2,1,	.	2,3
	/&gt;
/>
yields:
t>
	1,1	1,2	1,3
	2,1,	.	2,3
/>


->>


2>t.h


3>Example:
">
	t&gt;
		h&gt;1,1	1,2	1,3
		2,1,	2,2	2,3
	/&gt;
/>
yields:
t>
	h>1,1	1,2	1,3
	2,1,	2,2	2,3
/>


->>


2>t.r


3>Example:
">
	t&gt;
		r&gt;
			1
			2
			3
		/&gt;
	/&gt;
/>
yields:
t>
	r>
		1
		2
		3
	/>
/>


->>


2>t.rh


3>Example:
">
	t&gt;
		rh&gt;
			1
			2
			3
		/&gt;
	/&gt;
/>
yields:
t>
	rh>
		1
		2
		3
	/>
/>


->>


2>t.r.c


3>Example:
">
	t.r&gt;
		1
		2
		c&gt;
			3
			...
		/&gt;
	/./&gt;
/>
yields:
t.r>
	1
	2
	c>
		3
		...
	/>
/./>


->>


2>t.r.ch


3>Example:
">
	t.r&gt;
		1
		2
		ch&gt;
			3
			...
		/&gt;
	/./&gt;
/>
yields:
t.r>
	1
	2
	ch>
		3
		...
	/>
/./>


->>


2>t.r.h


3>Example:
">
	t.r&gt;
		1
		h&gt;
			2
			3
		/&gt;
		4
	/./&gt;
/>
yields:
t.r>
	1
	h>
		2
		3
	/>
	4
/./>


->>


2>t.rh.c


3>Example:
">
	t.rh&gt;
		1
		c&gt;
			2
			...
		/&gt;
		3
	/./&gt;
/>
yields:
t.rh>
	1
	c>
		2
		...
	/>
	3
/./>


->>


2>t.rh.ch


3>Example:
">
	t.rh&gt;
		1
		ch&gt;
			2
			...
		/&gt;
		3
	/./&gt;
/>
yields:
t.rh>
	1
	ch>
		2
		...
	/>
	3
/./>


->>


2>Variables

For information on how to use variables, see the < tag_variable.html "variable tag" > page.

o>
	table
		bgcolor
		background
		cellpadding
		cellspacing
		bordercolor
		border
		width
		height
		etc.
	tr/treven/trodd
		width
		height
		etc.
	td/th
		bgcolor
		background
		align
		valign
		width
		height
		etc.
/>

->>

< tag_table.swl >
    doc/tag_heading.swl f  i
1>Heading Tags

"><i>heading number</i>&gt;

There are 6 heading levels.  Just plunk the heading number on a line and put a hat on it.  The tag works pretty well in the multi-line format as well.

1>Heading 1
is the largest, whereas
6>Heading 6
is the smallest.


3>Note:
If you want a numbered < tag_list.html list >, use the '#'.


->>

< tag_heading.swl >
    doc/syntax.swl f  
1>SWL Syntax

SWL is like HTML in that HTML tags can be put into a SWL document and will be preserved.  Unline HTML, SWL is in-line.  That is, in HTML, you can have a paragraph spanning multiple lines in your code.  In SWL, each line of code is assumed to be a unique, self-contained paragraph.  Using 'tags' at the beginning of a line, you can specify different formatting behavior for an individual line of code or a range of lines, using the following syntacies.

3>Single Line of 'Tag' Formatting

">tag&gt;single line of content

3>Multiple Lines on which 'Tag' will operate.
">
	tag&gt;
		content lines
		content lines
	/&gt;
/>

To see the various ways to you can format your code, refer to the < tags.html "tag reference" >.


->>


1>Nested Tags

By nesting tags, you can create outlines, break out rows in tables, and much more.  Here's the general idea:

Through SWL's system of tag nesting, all of the following syntacies are functionally the same:

">
	a&gt;
		b&gt;
			content
		/&gt;
	/&gt;
/>

">
	a&gt;
		b&gt;content
	/&gt;
/>

">
	a.b&gt;content
/>

Note that there are 2 closing tags nested in this example.

">
	a.b&gt;
		content
	/./&gt;
/>

So, this also works.

">
	a.b&gt;
			content
		/&gt;
	/&gt;
/>


->>


1>Links and Images

By simply enclosing links or image file URL's in spaced angle-brackets, you can automatically create full image or anchor tags without the syntax hastle.  These tags can be placed anywhere in your code, not constrained to the beginning of the line.
<b>Note:</b> the space after the opening &lt; is required.  SWL will assume that any URL's containing the <i>@</i> (at) symbol are email links.  Images must end with <i>.gif</i>, <i>.jpg</i>, <i>.jpeg</i>, <i>.svg</i> or <i>.png</i>.  I have ambitions of adding support for other media extentions.

">
	&lt; link link_text &gt;
	&lt; image alternate_text properties &gt;
	&lt; "link with spaces" "link text with spaces" &gt;
	&lt; "image file name" "alternate text" "properties with spaces" &gt;
/>


2>Examples

3>Links:
">
	&lt; foo.html Foo &gt;
	&lt; bar.html "The Bar" &gt;
	&lt; "foo bar.html" foobar &gt;
	
	&lt; foo@bar.com &gt;
	&lt; foo@bar.com Foo &gt;
	&lt; foo@bar.com "Email Foo" &gt;
/>


3>Images:
">
	&lt; bob.gif &gt;
	&lt; bob.jpg &gt;
	&lt; bob.jpeg &gt;
	
	&lt; bob.jpg "A picture of Bob" &gt;
	&lt; bob.jpg Bob &gt;
	&lt; bob.jpg "A picture of Bob" align=center &gt;
	&lt; bob.jpg "A picture of Bob" "align=center hspace=20 vspace=20" &gt;
	
/>


3> Your Web Root

SWL makes your website portable.  You can specify in a single location where your entire document tree lies on the web.  This allows SWL to manage all of your links when you move your site.  All you have to do is change the <tt>root</tt> variable and forcibly recompile your entire site (which is only one command).

To use this feature, specify your web root in your template (usually <tt>local.swlt</tt> or <tt>@.swlt</tt> in your document root) and use <tt>~/</tt> in all of your links to specify that your link is relative to your web root.  For example:

">
	=.root&gt;//cixar.com/~swilly
	&lt; ~/index.html "Swilly's Homepage" &gt;
/>


3> Mangle Your Email Addresses
SWL can mangle your site's email addresses to reduce the risk of web crawlers harvesting your email for unsolicited email, SPAM.  To activate this feature, specify the <tt>email-mangle</tt> variable in your site template and forcibly recompile your site with the command "<tt>swl -f .</tt>".

">
	=.email-mangle&gt;yes
/>

< syntax.swl >
    doc/synopsis.swl f  
1>SWL Synopsis

<b>SWL</b> stands for <u>S</u>hort <u>W</u>eb <u>L</u>anguage.

SWL is an abbreviated subset of HTML designed to expediate document construction.  SWL helps a web designer construct sets of documents with a consistent look and feel, quickly.  Take a moment to read about the < introduction.html#benefits benefits > of SWL if you haven't already.

'SWL' refers to the language itself.  'SWL document' and 'SWL template' refer to source code written in SWL.  'Swl', 'swl', 'SWL compiler', and 'SWL interpreter' refer to any program that converts SWL source into HTML object code, albeit 'swl.pm' and 'swl.pl'.

< synopsis.swl >    doc/tag_paragraph.swl f  E1>Single Paragraph Tag

">P&gt;

This tag is handy if you don't want double spacing (poetry).  It creates a single paragraph and puts line-breaks between your lines.

3>Example:
">
	P&gt;
		How Now Brown Cow?
		Green Eggs and Ham.
	/&gt;
/>
yields:
P>
	How Now Brown Cow?
	Green Eggs and Ham.
/>

->>

< tag_paragraph.swl >
    doc/tag_macro.swl f  1>Macro Define Tag

">^&gt;

2>Macro Properties
*>
	top
	left
	tab
	colon
	right
	bottom

	single-top
	single-left
	single-tab
	single-colon
	single-right
	single-bottom

	multi-top
	multi-left
	multi-tab
	multi-colon
	multi-right
	multi-bottom

	multi-first-left
	multi-first-tab
	multi-first-colon
	multi-first-right
	multi-left-first
	multi-tab-first
	multi-colon-first
	multi-right-first
	first-left
	first-tab
	first-colon
	first-right

	noindent
	nocontent
/>

< tag_macro.swl > | < local.swlt >
    doc/tag_comment.swl f  1>Comment Tags

">!&gt;

Use the comment tag and comment sub-tags to make invisible notes in your code.

3>Single line comment
">!&gt;<i>comment</i>

3>Multi-line comment
">
	!&gt;
		<i>comment</i>
		<i>comment</i>
		<i>comment</i>
	/&gt;
/>

->>

2>Sub-comment Tags

You can use the following list tags in your comments and swl will format them so that they look good when you're spying the innards of your HTML.  See the < tag_list.html "list tags" > page for more information on their coresponding formats.

*>
	#
	*
	A
	a
/>

3>Example:

">
	!&gt;
		<i>comment</i>
		a&gt;
			<i>comment</i>
			<i>comment</i>
		/&gt;
	/&gt;
/>


will appear in your HTML as such:

">
	&lt;!--
		<i>comment</i>
		a.)	<i>comment</i>
		b.)	<i>comment</i>
	--&gt;
/>


->>


2>Mnemonic
SWL uses '!' to represent comments simply because it's the first character of a comment tag in HTML.  If you need aditional help remembering, '!' is the character you might write at the end of a 'comment'!


->>

< tag_comment.swl >
    doc/templates.swl f  
1>Templates


This is where most of the power of SWL is.  There will be a quiz.
Templates...  Templates have 3 aspects:
*>
	the template, the framework for you HTML document, defined using '<i>=.template></i>'
	template tag, for placement of values defined in your document, using '&lt;swl <i>your variable</i>&gt;'
	template definition, using '<i>=.your variable</i>' in your document
/>

Look at the < local.swlt > and figure it out.  It's 4 a.m. and Kris needs to sleep.


->>

2>&lt;swl&gt; Tag Syntax

3>&lt;swl value&gt; Notation
3>&lt;swl /value&gt; Notation

3>
	&lt;swl value = new value&gt; and
	&lt;swl /value = new value&gt; Notation
/>
    doc/tags.swl f  	
1>SWL Builtin Tag Reference

t>

	@.table.width>100%
	
	h>tag	html	description
	
	:	html	just plain html	< tag_revert.html goto >
	p	p	multiple paragraphs	< tag_paragraphs.html goto >
	P	p br	single paragraph, delimiting each space with a br	< tag_paragraph.html goto >
	j	a name	anchor	< tag_anchor.html goto >
	"	pre	pre-formatted text	< tag_pre.html goto >
	-	hr	horizontal rule	< tag_rule.html goto >
	
	h>Headings
	
	1	h1	heading level 1	< tag_heading.html goto >
	2	h2	heading level 2
	3	h3	heading level 3
	4	h4	heading level 4
	5	h5	heading level 5
	6	h6	heading level 6
	
	h>Lists
	
	*	ul	bulleted list	< tag_list.html goto >
	#	ol type=1	numbered list
	a	ol type=a	ordered list starting with 'a'
	A	ol type=A	ordered list starting with 'A'
	i	ol type=i	ordered list starting with 'i'
	I	ol type=I	ordered list starting with 'I'
	o	ol ul	ouline	< tag_outline.html goto >
	
	h>Link Lists
	
	l	ul a	bulleted list of links	< tag_list.html goto >
	*l	ul a	bulleted list of links
	#l	ol type=1 a	numbered list of links
	al	ol type=a a	ordered list of links starting with 'a'
	Al	ol type=A a	ordered list of links starting with 'A'
	il	ol type=i a	ordered list of links starting with 'i'
	Il	ol type=I a	ordered list of links starting with 'I'
	ol	ol ul a	outline of links	< tag_outline.html goto >
	
	h>Tables
	
	t	table	table (lines=rows) (columns=tab delimited)	< tag_table.html goto >
	t.h	tr th	rows of header cells in a table (lines=row) (columns=tab delimited)
	t.r	tr td	row of normal cells in a table (lines=cells)
	t.rh	tr th	row of header cells (line delimited)
	t.r.c	td	lines of a normal cell in a row of a table
	t.r.ch	th	lines of a header cell in a normal row of a table
	t.r.h	tr th	lines of a header cell in a normal row of a table
	t.rh.c	td	lines of a normal cell in a header row of a table
	t.rh.ch	th	lines of a header cell in a header row of a table
	
	h>Calendar
	C	table	creates a calendar in table form for the specified dates	< tag_calendar.html goto >

	h>Comments
	
	!	!--	comment	< tag_comment.html goto >
	!.#	!--	numbered list in a comment
	!.*	!--	bulleted list in a comment
	!.A	!--	ordered list in a comment starting with 'A'
	!.a	!--	ordered list in a comment starting with 'a'

	h>Manipulators

	@	.	variables	< tag_variable.html goto >
	^	.	macro definition	< tag_macro.html goto >
	=	swl	template replacement	< tag_template.html goto >
	+	.	file include	< tag_include.html goto >

	
/>

< tags.swl >
    doc/tag_revert.swl f  ]1>Exact HTML (Revert) Tag

">:&gt;

Allows you to revert to normal HTML formating (crossing over lines and all).

3>Example:
">
	:&gt;
		&lt;b&gt;this is really
		interesting because
		it's plain HTML.
		joy.&lt;/b&gt;
	/&gt;
/>
yields:
t>
	:>
		<b>this is really
		interesting because
		it's plain HTML.
		joy.</b>
	/>
/>

->>

< tag_revert.swl >
    doc/tag_rule.swl f  1>Horizontal Rule Tag

">-&gt;

The rule tag simply places a horizontal rule on your page.  Any information that happens to be on the same line goes the the bit-bucket.  If you use the multi-line form, all the lines go to the bit-bucket.

3>Clue:
">-&gt;&gt;

If the remainder of your document disappears after this tag, it's because you forgot the second bracket.  I probably don't need to say that to you wise and learnd folk, but in the previous version of SWL (for which there were a total of 3 users: me, my friend, and my imaginary goldfish), the second bracket wasn't necessary so this notation has been an uphill battle for some of us (mostly the goldfish).
You can really help the aesthetic value of your document by overriding this tag with a graphic that fits in your document's theme.  See < tag_macro.html "macro tags" > for information on overriding.


->>


2>Mnemonic:
I realize that it doesn't look <i>that</i> much like a horizontal bar anymore.  It kinduh got burried in more and more brackets as the language generalized.  In fact, it officially looks more like a christmas tree than a divider now.  If that bothers you, just remember <i>National Lampoons' Christmas Vacation</i>.


->>

< tag_rule.swl >
    doc/tag_calendar.swl f  a
1>Calendar Tag

">
	calendar&gt;
	C&gt;
/>

The calendar tag allows you to arbitrarily place a calendar in your document.

3>Example:
">
	calendar.2002.2&gt;&gt;
/>
yields:
calendar.2002.2>>

3>Example:
">
	calendar&gt;
		@.calendar.date&gt;
			height&gt;60px
			valign&gt;top
		/././&gt;
		2002.2.11&gt;&gt;
		2000.7.28&gt; SWL's Birthday
		2002.2.13&gt; &lt; # "Some Event" &gt;
	/&gt;
/>
yields:
calendar>
	@.calendar.date>
		height>60px
		valign>top
	/././>
	2002.2.11>>
	2000.7.28> SWL's Birthday
	2002.2.13> < # "Some Event" >
/>

->>

2>Variables

For information on how to use variables, see the < tag_variable.html "variable tag" > page.

t>
	h>Tag	Description
	h>Basic Formating
	calendar.table	the encompasing table
	calendar.row	defaults for all tr's in calendars
	calendar.cell	defaults for all td's in calendars
	calendar.cellh	defaults for all th's in calendars
	h>Specific Cells and Rows
	calendar.month	month and year cell
	calendar.month.row	month and year row
	calendar.day	day of the week cells
	calendar.day.row	day of the week rows
	calendar.date	date cells
	calendar.event	date cells that contain an event
	calendar.date.row	date rows
/>

Note that each basic formatting tag inherits the values of the same variable less its calendar prefix.

There are many acceptable, undocumented permutations for each of these variables.

->>

< tag_calendar.swl >
    doc/swl.gif f  6GIF89a)     - W]~ltǥ !   ]Q/333| ֣f388
=]]PEJbSY fffv3!  ,    ) 'dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.4UDB | zGil :		b1m~\|w)}ҷ}V~#~	n{nʺy`hཱ	D	jtp<p-PoV$Qn2kFr')f	\0&Ļ2xnN"E:"NĊ#2Me@cM<ŶnztJ+ZDuK⿴jZhiEa ge0	G9 %pb7	nVUZ0Ӽ?ͻollyۊl O	@ Ȃjc|`;{$؛*埀Zv[|o@ПA7~rևFL5NgnTqↄ (Fe2U cAA0J3]-3h `@{"'$;X#Y@ߒG`}e u7f.%IN.P˙Y䜺T$ԛ=|ǝx^!t<jCฦx&^?'6K&IC-9 Oi\zsv|fjmznXr^gJ¾!js޺Pu
lzM@)6SxVXfm<SBZZhj	+ ( J$t䛥驕BIRR	X(5<bX,jL#4˥NJ|Bت*8S20`ٲG9"-P9̥@cB` Fh]uA )OTe"HhxuVBLhe-kBtyMϵ7xuo`]Eyo\ ByH7ԉd5^=)b$c-o"4^?O@!<	v<AG#]`E[^|!_N6eHo/F;6?uW? <-^@<&~rLPoPIJ/
NP7pۊ2E(T5,EHLR
97e(9ݔ7!N=ԁD$"@h7,qW+'$@lx`@eE5nw@"	P =PÛJƘDh;H'؈^$!JD[~c YI$ʈFenXqq!ee@ ܍2k<8Z^D괕@ -xrDYهc\v.XV$d45$ɚȍ9(Sԕ܊QS`RrHGPj`C	MVEҁV4RtQ bH6
 &EKOA,8R$eo
Du"K֢0SL+|C)oʿ:[LIٙ4nb\<U%	5MUkPpa)>7䔀e4֏H*rj<O_]"q`5U
!wŠ2sh'@Z;ԛf PY꬘XG4@Pr sfǷ3s߈ށDp@]O0]#Ikl .X'yp5||
zX=˂*s	on!WD:"*uu(ہYU I1g? dsRv=#6);ICTL;E` )]"k8kc#zLE8̭lKPENWr@BS&l5,yz'(9w6sIs:n.+皁dk5ہQYO4~X/Z52Ze~f:cM`:إuYV=u)쬰eQ9nJO.h֎&ܑO ʼ,}l/\h?Ikc'$&/'EhӚtL*
a}G`mAН){d+$EEb^DI>]y[eDbv4j<;2-s+,@øp⽩7⛉$
Jl. 1I#!*FHFm#VcCkG~s47KG	fև{\>Coul}_MviI:.dn̕vǉD-9pTԙ~fHz'PG'ys7HDo@IUd!EĬk|uJ߹oVa|j2H~ǅ!{#*0^ڎpL1pk@:WiƠ+%R'vp7U'k|b# R@qU{P4/#pZ}n`;ˡ6ṛ2sw:(l[a~$BR~}0j6xDVNVDq2wx-`P.v(@~rRϕR&I5zz$D҂B0Xjcr	L("8+r*cR{(1\*pem5X(BB*@tщ[h20I)0-ACv 42n!%%#oxC3?0Rux#@+@KrX@B. U[Eff0Ћ4t8V"7я,px&ѸJsp׏
W`J/쓐
Q  	Jld+4(Ψ(vȐ+) 
HYD#	9X.raޠq4['V
+ :g>j$@NpZTGqB T	bՑdcy3g3+ELi>yT76d?bL:ُcI-	=lI6VJ%* vy?pBfmە!( (PH n`^T0@IiPa9n@  F5WZ ȉPb&yoٜ
 j`W]`i@ډ}p!b pE )(ɜY-f5ځٞeH%!:s]BdɩUxڝ?ɡ
HIbh		P i
@j	.@ FVG<:Z)UtKYn	a \j@'$Gz_:c
a; vm*yhʒ٧vڤ9`֩lg'9-zj٢j*0٩/
$r
 uwqJhjs
)(ʞopZ}`CkHI͊oꢼ 𸥇
ުɮsib]
)əo9h  :T(jzګ	ۜ9~Eڰ:{1XE{4+0+2[&hB;j%01N2۴A>K䪉w곛z\볜bzE:>Ъ[IwI
Ti:[Y Pw	#Kړhڶ0h>ۧ@򱫀Sc+tj 룡j?ʡQazs nۭz[;`ʤ	5KnвK[kEٞ
h66-|{j u ,a
`1,kyPk=:kAٺ_j[k#ۓJkhLYPQ{wVYv_:rhߋn0lڨ/lzӚ4U0SjkKĀVv_WùEN (9٣zU+Q@Š<*`L0-kj9{G|Z:	 <Ȍ| P0JP|ܱ{)sVpȰ#;Y?9,OTYaʲͰ& ǻlx{`Ǹg;c ]!.͜ Ͱќ(@rC|M[;+YǋX[q p Ͳ@ в,#s.|,}Z;Yd mЋgV[,8+Ҟ^ FmpOJ Ӎ?+@ENϣJE,;  hJpj} K+,mSZ&:j|n<+*jCܝlE֐'b	N΋|S	2m@i`əi7j GccS٠A =l(غ])i˪m x% mԁܙD# VʭFm}jʒor.~qRދ\j9׉Y
:IAwi}ڨO1pK pYj mNt-≠rJ׾)^n<A  4@㱜Kl G}{CvK0VӛУUƆک}`҂AXˈi y٦Aj}TV	y*Z˺@R͢LVjZubNIBͺquS\:Nݹ+n~ Uv@lw(<6@=_FMcݴj?13+ ~P p>|(8@$V}PȎޔ=Ą[ U,ЪٚMn ˽ ^	 ^䭞Ua~<~2	8[w=ٴZ\!EZeפ5~O~g$/&	+ ŋc-{?NZQ1lC7P@9VU2cn	0Ԉ9vhIʨh),;x/,hLr_LPٽ;qݩ.p7>)h=sz]ͯ,qa #⥟mӰM
uտNѿ$o)  P*+
YrB5O2' ƣ'M>'4*u23r,b*C!Kq\n7qUC)D?`_LB\&ցƞDceMcEEme	 obˢ,@Bk2lq&TGA.Z֖\ܱ2t19]pLZx}jR(-ȕO[r1tA7yQsm
6XKެ֣@PGmX  R:,<̀$wyd*	L<e@	SV6L+6%J4PY< [3E%BSu@u[%$:YW`p r L]xH;}Z;c"Z@茤BШ)lչCARgbuEeڒd[-y)Ç/R4zfs>UC0wL\U@0NLCliH5lpͰ|E}kaK/UcH@@ɲ%83(vFqh2`abҕc~4'^8f5. k"ZC|X@$d6PN[
btWz5וyBQnn0x	܄hDdB4\%fZKg '\\Q3胡&|QrvPtm.~*()>L(e
X?m$>-MT5jARdj|f,@2QT0T1/[;:DK:[;)k ے/~1GV"Sl.[N|vq%||A/*SJE#̠IA͒B-3.+MTDFF[FMgmne!@]-&nPf0p߅":xUoC7$X);S{X%n6g4:XƐ֖z>_ 0Ѱ/~@!C8K_{'X+q#\:}i2t=q<7VȾYBp\~Tg)~z#,Q
r6A_(tS8oZ{[
WH`9G!rA7<[uxF@,Hé$[&ڮ	xI89YE0Ë0aˌHg3
"\\,(F)#5FP}sq'!tcwX+ .YG1\6-0]ZBFS*2bmLS* qDafH0MgԕX/( @*kCo{4,܊%Yi,G-*#+u'睯?<YDi,0q Bd/9Q2ѣAiOHӡ"FIъv/=)ҩc֫@RJ(f1g¹Tgy%rU-7=QIȈCwCQHgj(Zj^9E83ō֐*(L mI
ÜsJI
4^X݊j%^+*UVXse	`5*6YͣiQlL;S-p WeO1>#_q7Q֖]D+[8s)$܉ցgڽ]7z6@qTqH Pz 屪ݷZ-W&M	: ^RD 0H0 b	4. 3 ;>1,!F>2%3N~2<  ;    doc/tag_anchor.swl f  1>Anchor Tag

">j</i>&gt;<i>anchor_name

An anchor in HTML is what you would call a bookmark anywhere else.  To create a link to an anchor in SWL, simply place this tag anywhere in your code:

">&lt; #<i>anchor_name</i> "<i>label</i>" &gt;

3>Mnemonic
'j' was chosen to represent the anchor tag because the letter somewhat resembles a boat anchor.  Bookmarks passing in the night, per se.

->>

< tag_anchor.swl >
    doc/tag_list.swl f  V1>List Tags

t>
	*&gt;	bulleted list
	#&gt;	numbered list
	a&gt;	ordered lists
	A&gt;	.
	i&gt;	.
	I&gt;	.
/>

These tags produce the various ordered and unordered lists.

3>Example:
">
	#&gt;
		<i>item</i>
		<i>item</i>
		<i>item</i>
	/&gt;
/>
yields:
#>
	<i>item</i>
	<i>item</i>
	<i>item</i>
/>

2>Mnemonics

You'll notice that most of these lists start with their respective starting character.  Asterisk (<i>*</i>) is close enough to a bullet if you ask me.  Pound (<i>#</i>) marks numbered lists intuitively, but it isn't the first character of the list, like '<i>A</i>' and '<i>i</i>' are.  This is because '<i>1</i>' is used with '<i>1-6</i>' for headings.

<i>see also < tag_outline.html outlines >.</i>


->>


1>Link List Tags


*>
	l <i>(same as next)</i>
	*l
	#l
	al
	Al
	il
	Il
/>

If you tack an '<i>l</i>' onto the end of the list tag (or just '<i>l</i>' for bulleted lists), you can create lists of links.  Lines with just a link on them serve as both the reference and the label.  If you want a label that's different than your reference, put a tab after the reference and write the name.  No quotes are necessary.

3>Example:
">
	*l&gt;
		tag_list.html#here
		tag_list.html#here	Right Here
	/&gt;
/>
j>here
yields:
*l>
	tag_list.html#here
	tag_list.html#here	Right Here
/>


<i>see also < tag_outline.html outlines >.</i>



->>

< tag_list.swl >
    doc/tag_template.swl f  -1>Template Variable Define Tag
">=&gt;

The template variable define tag allows you to set 'variables' whose values will be pasted into your docuement's template.  <i>(More on creating your own template later)</i>

3>Example:
">
	=.author&gt;<i>your_name</i>
/>

To place a template value in your document, you use the pseudo-HTML tag, '<i>&lt;swl&gt;</i>'

3>Example:
">
	&lt;swl author&gt;
/>
yields: <i>your_name</i>.


2>Builtin Templates
The SWL compiler automatically sets these templates so that you can insert them into your document:
t>
	date	date of compilation
	author	the default author is "SWL <i>version</i>"
	body	if you define a 'template' variable, the content of your document will be dumped into the 'body' template so you can place it wherever you want it.
/>


->>


2><i>about.swlt</i> Templates
If you put the line '<i>+&gt;about</i>' on the top of your document, these template variables are set:
*>
	swl.author
	swl.version
	swl.release date
	swl.copyright
	swl.license
/>


Note that when you compile a SWL document, SWL tries to find <i>local.swlt</i> in the same directory as your source code or one of the parent directories.  Thusly, you can port generic SWL files from one site to another, recompile, and completely change the look of your pages.


->>


< tag_template.swl > | < local.swlt >
    doc/index.swl f   
:><image src=swl.gif align=right>

1>SWL - Short Web Language

*l>
	introduction.html	Introduction
	syntax.html	Syntax
	tags.html	Builtin Tags
	!>templates.html	Templates (Advanced Topic)
/>

< index.swl >
    doc/tag_outline.swl f  v1>Outline Tag

">o&gt;

The outline tag allows you to create large quatities of nested items simply by using tabs to differentiate the different heading levels.

3>Example:
">
	o&gt;
		item
			sub-item
			sub-item
			sub-item
		another item
			sub-item
			sub-item
				another child
					way out here
			sub-item
			sub-item
	/&gt;
/>
yields:
o>
	item
		sub-item
		sub-item
		sub-item
	another item
		sub-item
		sub-item
			another child
				way out here
		sub-item
		sub-item
/>

->>

1>Outline of Links Tag

">ol&gt;

Much like a list of links, you can have an outline of links.  This is useful when you want to create a Table of Contents.

3>Example:
j>here
">
	ol&gt;
		tag_outline.html#here
			tag_outline.html#here	You can put a label after a tab.
	/&gt;
/>
yields:
ol>
	tag_outline.html#here
		tag_outline.html#here	You can put a label after a tab.
/>

->>

< tag_outline.swl >
    lib d   lib/swl_about.swlt f   
=>

	swl>
		version>2.9
		author>Kris Kowal
		copyright>(C) Copyright 2002  Kris Kowal
		license>SWL may be distributed under the terms of this General Public License.
	/>
	
/>
    lib/swl_doc.swlt f  
+>swl_about

@>

	=>

		title>SWL Documentation

		author>Kris Kowal
		
		navigator>
			>->>
			< index.html "Index" > |
			< synopsis.html "Synopsis" > |
			< syntax.html "Syntax Reference" > |
			< tags.html "Builtin Tag Reference" >
		/>
		
		template>
		
			<html>
				<!-- by <swl author> with SWL <swl swl.version> <swl date> -->
				<head>
					<title><swl title></title>
					<style>
						<!--
					
						body
						{
							font-family: Verdana,Helvetica,Arial,Sans Serif;
							background-color: #ffffff;
							scrollbar-base-color: #EF7094;
						}
					
						p, tr, thred, tfoot, th, td, dl, ol, ul, blockquote, span, br
						{
							font-size: 15px;
							color: #000000;
						}

						a
						{
							font-weight: bold;
							color: #000077;
						}
						a:hover
						{
							color: #EF7094;
						}
						a:active
						{
							color: #EF7094;
						}
						a:vlink
						{
							color:  #000000;
						}
						
			>>			// -->
					</style>
				</head>
				<body>
					<swl /body>
					<swl /navigator>
				</body>
			</html>
		/>
	/>
	
	table>
		border		>1
		cellspacing	>>0
		cellpadding	>2
		bgcolor		>eeeeee
	/>
	th>
		bgcolor		>aaeeff
	/>
	calendar>
		week.bgcolor>bbddee
		blank.bgcolor>cccccc
		date.bgcolor>ffcccc
		event.bgcolor>bbddee
	/>
	
/>

    lib/std.swlt f  
@>
	=>
	
		author>SWL <swl version>
		title>SWL Document

		template>
		
			<html>
				<!-- by <swl author> <swl date> -->
				<head>
					<title><swl title></title>
					<meta http-equiv=Expires content=0>
					<style>
						body
						{
							font-family: Verdana,Helvetica,Arial,Sans Serif;
							font-size: 15px;
							color: #000000;
						}
						p, tr, thred, tfoot, th, td, dl, ol, ul, blockquote, span, br
						{
							font-size: 15px;
						}
					</style>
				</head>
				<body>
					<table border=0 cellspacing=0 cellpadding=0 width=700></tr><td>
						<swl /body>
					</td></tr></table>
				</body>
			</html>
			
		/>
	/>
	table>
		border		>1
		cellspacing	>>0
		cellpadding	>4
	/>
	
/>
    license.txt f  ^
You are reading: license.txt
----------------------------


		    GNU GENERAL PUBLIC LICENSE
		       Version 2, June 1991

 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
                          675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The licenses for most software are designed to take away your
freedom to share and change it.  By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it.  (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.)  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must show them these terms so they know their
rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  Finally, any free program is threatened constantly by software
patents.  We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary.  To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License.  The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language.  (Hereinafter, translation is included without limitation in
the term "modification".)  Each licensee is addressed as "you".

Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope.  The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.

  1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.

You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.

  2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:

    a) You must cause the modified files to carry prominent notices
    stating that you changed the files and the date of any change.

    b) You must cause any work that you distribute or publish, that in
    whole or in part contains or is derived from the Program or any
    part thereof, to be licensed as a whole at no charge to all third
    parties under the terms of this License.

    c) If the modified program normally reads commands interactively
    when run, you must cause it, when started running for such
    interactive use in the most ordinary way, to print or display an
    announcement including an appropriate copyright notice and a
    notice that there is no warranty (or else, saying that you provide
    a warranty) and that users may redistribute the program under
    these conditions, and telling the user how to view a copy of this
    License.  (Exception: if the Program itself is interactive but
    does not normally print such an announcement, your work based on
    the Program is not required to print an announcement.)

These requirements apply to the modified work as a whole.  If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works.  But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.

Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.

In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.

  3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:

    a) Accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of Sections
    1 and 2 above on a medium customarily used for software interchange; or,

    b) Accompany it with a written offer, valid for at least three
    years, to give any third party, for a charge no more than your
    cost of physically performing source distribution, a complete
    machine-readable copy of the corresponding source code, to be
    distributed under the terms of Sections 1 and 2 above on a medium
    customarily used for software interchange; or,

    c) Accompany it with the information you received as to the offer
    to distribute corresponding source code.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form with such
    an offer, in accord with Subsection b above.)

The source code for a work means the preferred form of the work for
making modifications to it.  For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable.  However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.

If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.

  4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License.  Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.

  5. You are not required to accept this License, since you have not
signed it.  However, nothing else grants you permission to modify or
distribute the Program or its derivative works.  These actions are
prohibited by law if you do not accept this License.  Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions.  You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.

  7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all.  For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.

If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.

It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices.  Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.

This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.

  8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded.  In such case, this License incorporates
the limitation as if written in the body of this License.

  9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.

  10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19yy name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here is a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
  `Gnomovision' (which makes passes at compilers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

This General Public License does not permit incorporating your program into
proprietary programs.  If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library.  If this is what you want to do, use the GNU Library General
Public License instead of this License.


 
			 The "Artistic License" 
 
				Preamble 
 
The intent of this document is to state the conditions under which a 
Package may be copied, such that the Copyright Holder maintains some 
semblance of artistic control over the development of the package, 
while giving the users of the package the right to use and distribute 
the Package in a more-or-less customary fashion, plus the right to make 
reasonable modifications. 
 
Definitions: 
 
	"Package" refers to the collection of files distributed by the 
	Copyright Holder, and derivatives of that collection of files 
	created through textual modification. 
 
	"Standard Version" refers to such a Package if it has not been 
	modified, or has been modified in accordance with the wishes 
	of the Copyright Holder as specified below. 
 
	"Copyright Holder" is whoever is named in the copyright or 
	copyrights for the package. 
 
	"You" is you, if you're thinking about copying or distributing 
	this Package. 
 
	"Reasonable copying fee" is whatever you can justify on the 
	basis of media cost, duplication charges, time of people involved, 
	and so on.  (You will not be required to justify it to the 
	Copyright Holder, but only to the computing community at large 
	as a market that must bear the fee.) 
 
	"Freely Available" means that no fee is charged for the item 
	itself, though there may be fees involved in handling the item. 
	It also means that recipients of the item may redistribute it 
	under the same conditions they received it. 
 
1. You may make and give away verbatim copies of the source form of the 
Standard Version of this Package without restriction, provided that you 
duplicate all of the original copyright notices and associated disclaimers. 
 
2. You may apply bug fixes, portability fixes and other modifications 
derived from the Public Domain or from the Copyright Holder.  A Package 
modified in such a way shall still be considered the Standard Version. 
 
3. You may otherwise modify your copy of this Package in any way, provided 
that you insert a prominent notice in each changed file stating how and 
when you changed that file, and provided that you do at least ONE of the 
following: 
 
    a) place your modifications in the Public Domain or otherwise make them 
    Freely Available, such as by posting said modifications to Usenet or 
    an equivalent medium, or placing the modifications on a major archive 
    site such as uunet.uu.net, or by allowing the Copyright Holder to include 
    your modifications in the Standard Version of the Package. 
 
    b) use the modified Package only within your corporation or organization. 
 
    c) rename any non-standard executables so the names do not conflict 
    with standard executables, which must also be provided, and provide 
    a separate manual page for each non-standard executable that clearly 
    documents how it differs from the Standard Version. 
 
    d) make other distribution arrangements with the Copyright Holder. 
 
4. You may distribute the programs of this Package in object code or 
executable form, provided that you do at least ONE of the following: 
 
    a) distribute a Standard Version of the executables and library files, 
    together with instructions (in the manual page or equivalent) on where 
    to get the Standard Version. 
 
    b) accompany the distribution with the machine-readable source of 
    the Package with your modifications. 
 
    c) give non-standard executables non-standard names, and clearly 
    document the differences in manual pages (or equivalent), together 
    with instructions on where to get the Standard Version. 
 
    d) make other distribution arrangements with the Copyright Holder. 
 
5. You may charge a reasonable copying fee for any distribution of this 
Package.  You may charge any fee you choose for support of this 
Package.  You may not charge a fee for this Package itself.  However, 
you may distribute this Package in aggregate with other (possibly 
commercial) programs as part of a larger (possibly commercial) software 
distribution provided that you do not advertise this Package as a 
product of your own.  You may embed this Package's interpreter within 
an executable of yours (by linking); this shall be construed as a mere 
form of aggregation, provided that the complete Standard Version of the 
interpreter is so embedded. 
 
6. The scripts and library files supplied as input to or produced as 
output from the programs of this Package do not automatically fall 
under the copyright of this Package, but belong to whomever generated 
them, and may be sold commercially, and may be aggregated with this 
Package.  If such scripts or library files are aggregated with this 
Package via the so-called "undump" or "unexec" methods of producing a 
binary executable image, then distribution of such an image shall 
neither be construed as a distribution of this Package nor shall it 
fall under the restrictions of Paragraphs 3 and 4, provided that you do 
not represent such an executable image as a Standard Version of this 
Package. 
 
7. C subroutines (or comparably compiled subroutines in other 
languages) supplied by you and linked into this Package in order to 
emulate subroutines and variables of the language defined by this 
Package shall not be considered part of this Package, but are the 
equivalent of input as in Paragraph 6, provided these subroutines do 
not change the language in any way that would cause it to fail the 
regression tests for the language. 
 
8. Aggregation of this Package with a commercial distribution is always 
permitted provided that the use of this Package is embedded; that is, 
when no overt attempt is made to make this Package's interfaces visible 
to the end user of the commercial distribution.  Such use shall not be 
construed as a distribution of this Package. 
 
9. The name of the Copyright Holder may not be used to endorse or promote 
products derived from this software without specific prior written permission. 
 
10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR 
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 
WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
 
				The End 


    swl.pm f f################################################################################
# 
# SWL - Short Web Language
# by Kris Kowal
#
# SWL may be distributed under the terms of this General Public License.
# converts text/swl to text/html
# 
#
# Revision History
# (most recent first)
#
# 2004-10-02-2115 PDT distribution 9 of SWL 2 attempted to fix newline problem
#                                             added email mangling
#                                             made ~/ expand to <swl /root>/
#                                              in SWL URL's
#                                             added C tag for calendars with
#                                              event descriptions in the date
#                                              cells
#                                             renamed swl swl.pl and rewrote
#                                              based on common usage including
#                                              standard io streams and "-f ."
#                                              default behavior
#                                             fixed bug whith the file include
#                                              tag.
#                                             guaranteed spec compliance for
#                                              some tags by binding properties
#                                              in double quotes.
#                                             fixed bug: failure to recognize
#                                              tags with following white space
#                                             fixed many bugs by moving toward
#                                              use strict
# ????-??-??-???? ??? distribution 8 of SWL 2 packaged for solaris 9
# 2003-05-09-1953 PST distribution 7 of SWL 2
#                                             improved command line interface
#                                             used environment variables to find
#                                              swl.pm
#                                             changed the $SWL::dir to @SWLPATH
#                                              for searching for locations with
#                                              swl files
#                                             added alternating table row
#                                              properties syntax
#                                             conformed to no news is good news
#                                              unix ideology
# 2002-10-25-0007 PDT distribution 6 of SWL 2 fixed a mis-spell in documentation
#                                             extended interpretation of month
#                                              names in calendars
#                                             <swl date> tag now reports local
#                                              time
# 2002-02-03-0151 PST distribution 5 of SWL 2 documentation fleshed out
#                                             added support for new file format
#                                              includes (text, html, csv, tab)
#                                             added support for .png graphics
#                                             fixed 'pre' bug
#                                             calendar tag
# ????-??-??-???? ??? distribution 4 of SWL 2 new logo and coresponding icons
# 2002-01-25-1111 PST distribution 3 of SWL 2 some bug fixes
#                                              added local.swlt search
# 2001-11-06-2327 PST distribution 2 of SWL 2 major release
# 2001-09-16-2122 PDT distribution 1 of SWL 2
# 2001-05-02-1429 PDT rebuilt
# 2000-08-26-1713 PDT multiple file support
# 2000-07-28          born as HT.pl
# 
################################################################################

$SWL::VERSION = "2.9";

use Cwd;

for my $path (
#PATH#
'/usr/local/share/swl',
#/PATH#
) {
  if ( -e $path ) {
    @::SWLPATH = ( "$path/lib" );
    last;
  }
}

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

# SWL
# returns a single string of swl code, without outlining and such

sub SWL
{
  return

    &SWL::CompilePost
    (
      &SWL::Compile
      (
        &SWL::CompilePre
        (
          &SWL::Structure( shift )
        )
      )
    );
    
}

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

package SWL;

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

sub Glob {
  my $file = shift;

  # prepend the current working directory if necessary
  if (
    $file !~ m/^[\\\/]/ and
    $file !~ m/^[a-z]\:[\\\/]/i
  ) {
    $file = ::cwd() . '/' . $file;
  }
  
  my @leftss = ([]);
  my @rights = split /\//, $file;

  while ( @rights ) {
    my $right = shift @rights;
    if ( $right eq '.' ) {
      # do Nothing
    } elsif ( $right eq '..' ) {
      # back up one
      @leftss = map {
        pop @$_;
        $_;
      } @leftss;
    } elsif ( $right eq '...' ) {
      @leftss = map {
        my @parts = @$_;
        map {
          [ @parts[0..$_] ];
        } reverse 0..$#parts;
      } @leftss;
    } else {
      @leftss = map {
      	my @paths = @$_;
      	my $path = join '/', @paths;
       	[ @paths, $right ];
      } @leftss;
    }
  }

  my @files;
  for $lefts ( @leftss ) {
    my $file = join '/', @$lefts;
    push @files, $file if -e $file;
  }
  return @files;
}

sub Grok {
  my $pattern = shift;
  my @files = Glob $pattern;
  return shift @files;
}

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

sub File
{
  my $FileIn = shift;
  my $FileOut = shift;

  # Store the old current directory (Restore at the end of this function)
  my $Cwd = ::cwd();

  # Move into the file's directory
  my @Path = split /[\\\/]/, $FileIn;
  $FileIn = pop @Path;
  chdir join '/', @Path;
  
  # get this file
  ( open FILE, "$FileIn" ) or ( open FILE, "$FileIn.swl" ) or return undef;
  my $In = join "\n", map {chomp; $_} <FILE>;
  close FILE;

  my @LocalIns = split /\./, $FileIn;
  shift @LocalIns;
  pop @LocalIns;

  # include whatever template is relevant
  for
  (
    map ".../$_",
    map {($_, ".$_");}
    map { join '.', $_, @LocalIns, 'swlt' }
    '@', 'local'
  )
  {
    my $FileLocal = Grok($_);
    if ( $FileLocal ne '' ) 
    {
      $In = "+>$FileLocal\n$In";
      last;
    }
  }

  # compile
  my $Out = &main::SWL( $In );
  
  # put the file
  if ( $FileOut eq '' )
  {
    $FileOut = $FileIn;
    $FileOut =~ s/\.swl$//i;
    $FileOut = "$FileOut.html";
  }
  
  ( open FILE, ">$FileOut" ) or return undef;
  print FILE $Out;
  close FILE;
  
  chdir $Cwd;
  return 1;
}

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

# takes SWL code and converts it into a SWL node tree

sub Structure
{
  my @Lines = split /\n/, shift;

  # map the lines and assign their line number
  my @Lines = map
  {
    $Lines[$_] =~ s/^(\s*)//;
    {
      'string' => $Lines[$_],
      'space' => length $1,
      'number' => $_,
    };
  } 0..$#Lines;

  # create a structure root  
  my $Root = 
  {
    'mark' => 'p',
    'nodes' => [],
    'vars' => {},
  };
  
  my @NodeStack = ( $Root );
  my $Mark;
  my $Line;
  my $Level;

  # simplify lines
  while ( $Line = shift @Lines )
  {
  
    my $Number = $Line->{'number'};
    my $Space = $Line->{'space'};
    my $String = $Line->{'string'};
    
    my $StringLeft;
    my $StringRight;
    
    # if there's an open bracket (a disallowed tag character)
    if ( $String =~ '<' )
    {
      my $Pos = index $String, '<';
      $StringRight = substr $String, $Pos;
      $String = substr $String, 0, $Pos;
    }
    
    # if there is a tag bracket
    if ( $String =~ '>' )
    {
      my $Pos = index $String, '>';
      $StringLeft = substr $String, 0, $Pos;
      $StringRight = ( substr $String, $Pos + 1 ) . $StringRight;
      $StringLeft =~ s/\s*$//g; # clean trailing space off
      $StringRight =~ s/\s*$//g; # clean trailing space off
    }
    # if there isn't a tag bracket
    else
    {
      $StringLeft = '';
      $StringRight = $String . $StringRight;
    }
    
    # if there was any mark information
    if ( $StringLeft ne '' )
    {
    
      # clean off trailing space
      $StringLeft =~ s/\s+$//;

      # get the first mark
      my @Marks = split '\.', $StringLeft;
      my $Mark = shift @Marks;
      
      # put the content back on
      if ( $StringRight ne '' )
      {
      
        # push the content onto a new line
        unshift @Lines,
        (
          map
          {
            {
              'string' => '/>',
            };
          } -1..$#Marks,
        );
        unshift @Lines,
        (
          {
            'space' => 0,
            'number' => $Number,
            'string' => $StringRight,
          }
        );

      }
    
      # push the rest of the marks back on the stack
      unshift @Lines, map
      {
        {
          'space' => $Space,
          'number' => $Number,
          'string' => "$_>",
        };
      } @Marks;
      
      
      # interpret the relevent mark
      
      # if it's a stack up
      if ( $Mark ne '/' )
      {

        # add the node to the tree      
        my %Node =
        (
          'mark' => $Mark,
          'linenumber' => $Number,
          'space' => $Space, # this value is useless beyond the scope of this function.
          'nodes' => [],
        );

        push @{ $NodeStack[$#NodeStack]{'nodes'} }, \%Node;
        push @NodeStack, \%Node;
          
      }
      
      # if it's a stack down
      else
      {
        # pop the node stack
        pop @NodeStack if $#NodeStack > 0;
      }
      
    }
    # if there wasn't any mark information on this line
    else
    {
    
      $Space -= $NodeStack[$#NodeStack]{'space'} + 1;
    
      # add the line to the node stack
      my %Node =
      (
        'mark' => '',
        'linenumber' => $Number,
        'nodes' => [ ( "\t" x $Space ) . $StringRight ],
      );

      push @{ $NodeStack[$#NodeStack]{'nodes'} }, \%Node;
      
    }

  }
  
  return $Root;
}

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

sub Structure::TXT
{
  my $In = shift;
  return map
  {
    {
      'mark' => '',
      'nodes' =>
      [
        $_,
      ],
    };
  } split "\n", $In;
}

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

sub Structure::HTML
{
  my $In = shift;
  return
  {
    'mark' => ':',
    'nodes' =>
    [
      {
        'mark' => '',
        'nodes' =>
        [
          shift,
        ],
      },
    ],
  };
}

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

sub Structure::CSV
{
  return map
  {

    # spearate the line into tab delimited cells with '.' in blank ones
    # account for quotes

    my $Line = $_;
    chomp $Line;

    my @Cells;

    while ( $Line ne '' )
    {
      if ( $Line =~ '^"' )
      {

        my $Cell;
        $Line = substr $Line, 1; # bump the inital quote

        while (1)
        {
          my $QuotePos = index $Line, '"';
          my $QuoteQuotePos = index $Line, '""';
          my $QuoteCommaPos = index $Line, '",';
          if ( $QuotePos == $QuoteQuotePos ) # search for real quotes
          {
            $Cell .= substr $Line, 0, $QuotePos;
            $Cell .= '"';
            $Line = substr $Line, $QuotePos + 2;
          }
          elsif ( $QuotePos == $QuoteCommaPos ) # search for end of cell quotes
          {
            $Cell .= substr $Line, 0, $QuotePos;
            $Line = substr $Line, $QuotePos + 2;
            last;
          }
          else
          {
            $Cell .= $Line;
            $Line = '';
            last;
          }
        }
        push @Cells, "$Cell";
      }
      elsif ( $Line =~ '^,' )
      {
        push @Cells, '.';
        $Line = substr $Line, 1;
      }
      else
      {
        if ( $Line =~ ',' )
        {
          my $CommaPos = index $Line, ",";
          my $Cell = substr $Line, 0, $CommaPos;
          $Cell = '.' if $Cell eq '';
          push @Cells, $Cell;
          $Line = substr $Line, $CommaPos + 1;
        }
        else
        {
          push @Cells, $Line;
          $Line = '';
        }
      }
    }

    {
      'mark' => '',
      'nodes' =>
      [
        join "\t", @Cells
      ],
    };

  } split "\n", shift;
}

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

sub Structure::TAB
{
  return map
  {

    # spearate the line into tab delimited cells with '.' in blank ones
    # account for quotes

    my $Line = $_;
    chomp $Line;

    my @Cells;

    while ( $Line ne '' )
    {
      if ( $Line =~ '^"' )
      {

        my $Cell;
        $Line = substr $Line, 1; # bump the inital quote

        while (1)
        {
          my $QuotePos = index $Line, '"';
          my $QuoteQuotePos = index $Line, '""';
          my $QuoteTabPos = index $Line, "\"\t";
          if ( $QuotePos == $QuoteQuotePos ) # search for real quotes
          {
            $Cell .= substr $Line, 0, $QuotePos;
            $Cell .= '"';
            $Line = substr $Line, $QuotePos + 2;
          }
          elsif ( $QuotePos == $QuoteTabPos ) # search for end of cell quotes
          {
            $Cell .= substr $Line, 0, $QuotePos;
            $Line = substr $Line, $QuotePos + 2;
            last;
          }
          else
          {
            $Cell .= $Line;
            $Line = '';
            last;
          }
        }
        push @Cells, "$Cell";
      }
      elsif ( $Line =~ "^\t" )
      {
        push @Cells, '.';
        $Line = substr $Line, 1;
      }
      else
      {
        if ( $Line =~ "\t" )
        {
          my $CommaPos = index $Line, "\t";
          my $Cell = substr $Line, 0, $CommaPos;
          $Cell = '.' if $Cell eq '';
          push @Cells, $Cell;
          $Line = substr $Line, $CommaPos + 1;
        }
        else
        {
          push @Cells, $Line;
          $Line = '';
        }
      }
    }

    {
      'mark' => '',
      'nodes' =>
      [
        join "\t", @Cells
      ],
    };

  } split "\n", shift;
}


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

sub StructureInclude
{
	my $Node = shift;
	my $File = shift;
	my $Extention = shift;
	
	my $Grok = Grok( $File );

	if (
		(
			$File !~ /^[\\\/]/ and
			$File !~ /^[a-z]\:[\\\/]/i
		) and
		$Grok eq ''
	)
	{
		for my $Path ( @::SWLPATH )
		{
			$Grok = Grok( "$Path/$File" );
			last if $Grok ne '';
		}
	}

	my $Path = $Grok;
	$Path =~ s/[^\\\/]*$//;

	my %Types = (
		'swl' => 'SWL',
		'swlt' => 'SWL',
		'text' => 'TXT',
		'csv' => 'CSV',
		'tab' => 'TAB',
		'html' => 'HTML',
		'htm' => 'HTML',
		'txt' => 'TXT',
	);
	$Extention = $1 if not $Extention and $File =~ m/\.([^\.]+)$/i;
	$Type = $Types{$Extention};

	(open FILE, "$Grok") or print STDERR "Unable to open '$Grok'\n";
	my $In = join "\n", map {chomp; $_} <FILE>;
	close FILE;

	my $Cwd = ::cwd();
	chdir $Path;

	if ( $Type eq 'SWL' )
	{

		# make a structure (multi-tree of nodes)
		my $Structure = &Structure( $In );
		$Structure = &CompilePre( $Structure );

		# add those nodes
		push @{ $Node->{'nodes'} }, @{ $Structure->{'nodes'} };

	}
	elsif ( $Type eq 'HTML' )
	{
		push @{ $NodeOut->{'nodes'} }, &Structure::HTML( $In );
	}
	elsif ( $Type eq 'TXT' )
	{
		push @{ $NodeOut->{'nodes'} }, &Structure::TXT( $In );
	  }
	elsif ( $Type eq 'CSV' )
	{
		push @{ $NodeOut->{'nodes'} }, &Structure::CSV( $In );
	  }
	elsif ( $Type eq 'TAB' )
	{
		push @{ $NodeOut->{'nodes'} }, &Structure::TAB( $In );
	  }
	else
	{

		my $NodeNew =
		{
			'mark' => '',
			'nodes' =>
			[
				$In,
			],
		};
		push @{ $Node->{'nodes'} }, $NodeNew;

	}

	chdir $Cwd;

}

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

#  reads high level tags, like '+' to add nodes from other files

#  File

#    Title (from highest header level or from the 'title' directive, depending)

#    NodeRoot
#    NodeParent

#    FileNext
#    FilePrev
#    FileParent

#    TOC

sub CompilePre
{

  my $RootIn = shift;
  my $RootOut = {};
  
  my @StackIn = ( $RootIn );
  my @StackOut = ( $RootOut );
  
  RECUR: while ( @StackIn != 0 )
  {
  
    my $NodeIn = $StackIn[$#StackIn];
    my $NodeOut = $StackOut[$#StackOut];
    
    # copy each key, recur for node lists
    while ( my @Keys = keys %$NodeIn )
    {
    
      my $Key = shift @Keys;
      
      # if it's a node list, copy its members
      if ( $Key eq 'nodes' )
      {
      
        # copy each node      
        while ( my $Node = shift @{ $NodeIn->{'nodes'} } )
        {
        
          # if the node is just data, don't recur, just copy
          if ( $Node->{'mark'} eq '' )
          {
            push @{ $NodeOut->{'nodes'} }, $Node;
          }
          
          # handle includes
          elsif ( $Node->{'mark'} eq '+' )
          {

            # read in each file
            foreach my $NodeSub ( @{ $Node->{'nodes'} } )
            {
            
              if ( $NodeSub->{'mark'} eq '' )
              {
              	StructureInclude( $NodeOut, $NodeSub->{'nodes'}[0] );
              }
              elsif
              (
                grep { $NodeSub->{'mark'} eq $_ }
                'swl', 'swlt', 'html', 'text', 'txt', 'csv', 'tab'
              )
              {
                foreach my $NodeSubSub ( @{ $NodeSub->{'nodes'} } )
                {
                  if ( $NodeSubSub->{'mark'} eq '' )
                  {
										StructureInclude( $NodeOut, $NodeSubSub->{'nodes'}[0],  $NodeSub->{'mark'} );
                  }
                }
              }
              else
              {
              	StructureInclude( $NodeOut, $NodeSub->{'nodes'}[0], 'text' );
              }
              
            }
            
          }
          
          # if it's not just data, there might be sub-nodes, so recur
          else
          {
          
            my $NodeNew = {};
            push @{ $NodeOut->{'nodes'} }, $NodeNew;
          
            push @StackIn, $Node;
            push @StackOut, $NodeNew;
            next RECUR;
          
          }
          
        }
      
      }
      
      # otherwise, just copy the key over
      else
      {
        $NodeOut->{$Key} = $NodeIn->{$Key};
      }
      
      delete $NodeIn->{$Key};
      
    }
    
    pop @StackIn;
    pop @StackOut;
    
  }
  
  return $RootOut;

}

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

# The template function is a fallback, in case any of the template '<swl>' tags
# in the datafile weren't replaced.  this either deletes them or replaces them
# with defaults.

sub CompilePost
{

  my $String = shift;
  my $Templatez = shift;
  
  my %TemplateChangerz;
  
  my $Body;
  if ( exists $Templatez->{'.template'} )
  {
    $Body = $String;
    $String = '<swl template>';
  }
  
  # replace all templates
  while ( $String =~ '<swl ' )
  {
  
    my $Pos = index $String, '<swl ';
    
    my $StringLeft = substr $String, 0, $Pos;
    my $StringRight = substr $String, $Pos + 5;
    my $Tag;
    
    # if the tag ends (as it is supposed to)    
    if ( $StringRight =~ '>' )
    {
      
      my $Pos = index $StringRight, '>';
      
      $Tag = substr $StringRight, 0, $Pos;
      $StringRight = substr $StringRight, $Pos + 1;
      
      # get rid of trailing spaces
      $Tag =~ s/\s+$//;
      
      # get rid of global requirement '/'s
      $Tag =~ s/\///g;
      
      # if it is a member of a template changer
      if ( exists $TemplateChangerz{$Tag} )
      {
        $Tag = $TemplateChangerz{$Tag};
      }      

      # find a replacement
      if ( $Tag eq 'body' )
      {
        $Tag = $Body;
      }
      elsif ( $Tag eq 'date' )
      {
        $Tag = localtime;
      }
      elsif ( $Tag eq 'author' )
      {
        $Tag = "SWL $SWL::VERSION";
      }
      # if it's a template changer
      elsif ( $Tag =~ /^(.*) \= (.*)$/ )
      {
        $TemplateChangerz{$1} = $2;
        $Tag = '';
      }
      # find a substitude for the mid-string
      elsif ( exists $Templatez->{ ".$Tag" } )
      {
      
        my @Lines = @{ $Templatez->{ ".$Tag" } };
        
        $Tag = join "\n", @Lines;

        # recompile the contents of the tag        
        my $node = &Structure( ":>\n$Tag" );
        $Tag = &Compile( $node );
        
      }
      else
      {
        $Tag = '';
      }
      
    }
    
    $Tag =~ s/\s+$//;
    $Tag =~ s/^\s+//;

    $String = $StringLeft . $Tag . $StringRight;
    
  }
  
  # find and replace link bits
  while ( $String =~ '< ' )
  {
  
    my $Pos = index $String, '< ';
    
    my $StringLeft = substr $String, 0, $Pos;
    my $StringRight = substr $String, $Pos + 2;
    my $Tag;
    
    # if the tag ends (as it is supposed to
    if ( $StringRight =~ ' >' )
    {
      
      my $Pos = index $StringRight, ' >';
      
      $Tag = substr $StringRight, 0, $Pos;
      $StringRight = substr $StringRight, $Pos + 2;
      
      # collect the parts from the tag's content
      my @Parts = ();
      while ( $Tag ne '' )
      {
      
        my $Part;
      
        # get rid of leading and trailing spaces
        $Tag =~ s/^\s*(.*)\s*$/$1/;
        
        # if the part is in quotes
        if ( '"' eq substr $Tag, 0, 1 )
        {
          $Tag = substr $Tag, 1;
          if ( $Tag =~ '"' )
          {
            my $Pos = index $Tag, '"';
            $Part = substr $Tag, 0, $Pos;
            $Tag = substr $Tag, $Pos + 2;
          }
          else
          {
            $Part = $Tag;
          }
        }
        # if it ends with a space
        elsif ( $Tag =~ ' ' )
        {
          my $Pos = index $Tag, ' ';
          $Part = substr $Tag, 0, $Pos;
          $Tag = substr $Tag, $Pos + 1;
        }
        # otherwise, just use whatever's left
        else
        {
          $Part = $Tag;
          $Tag = '';
        }
        
        push @Parts, $Part;
        
      }
      
      # determine what type of output this warrants
      my $Part = shift @Parts;
      $Part =~ s/^\~\//$Templatez->{'.root'}[0]\//; # grok
      #  if it's an image
      if
      (
        $Part =~ /\.gif/
        or $Part =~ /\.jpg/
        or $Part =~ /\.jpeg/
        or $Part =~ /\.svg/
        or $Part =~ /\.png/
      )
      {
        my $File = $Part;
        my $Name = shift @Parts;
        my $Propertys = join '', map " $_", @Parts;

        $File =~ s/ /%20/g;
        $Name = qq("$Name") if $Name =~ ' ';

        $Tag = qq(<img src="$File" alt="$Name$Propertys">);
      }
      # if it's an email address
      elsif ( $Part =~ /\@/ )
      {
        my $Link = $Part;
        my $Name = shift @Parts;
      	if ( $Templatez->{'.email-mangle'}[0] )
	{
          ( my $Left, my $Right ) = split '@', $Link;
          $Left = join ', ', reverse map "'&#$_'", map ord, split '', $Left;
          $Right = join ', ', reverse map "'&#$_'", map ord, split '', $Right;
	  if ( not $Name ) {
	    $Name = "left + '&#64' + right";
	  } else {
	    $Name = '"' . ( join '', map "&#$_", map ord, split '', $Name ) . '"';
	  }
          $Tag = qq{
            <script language=javascript>
	      var left = [ $Left ].reverse().join("");
	      var right = [ $Right ].reverse().join("");
              document.write( "<a href='mailto:" + left + '&#64' + right + "'>" );
              document.write( $Name );
              document.write( "</a>" );
            </script>
          };
	}
	else
	{
          $Name = $Link if $Name eq '';
          $Link =~ s/ /%20/g;
          $Tag = qq(<a href="mailto:$Link">$Name</a>);
	}
      }
      # otherwise, treat it as a link
      else
      {
        my $Link = $Part;
        my $Name = shift @Parts;
        $Name = $Link if $Name eq '';
        $Link =~ s/ /%20/g;
        $Tag = qq(<a href="$Link">$Name</a>);
      }
      
    }
    
    $String = $StringLeft . $Tag . $StringRight;
    
  }
  
  return $String;
  
}

################################################################################
#
#  COMPILATION FUNCTIONS
# all of the following functions and variables are used by the recursive
# compile function.
#

# changes SWL nodes into a string of HTML recursively

sub Compile
{
  my $Node = shift;
  my $Parent = shift;
  my $Level = shift;
  my $Out;
  
  $Level = 0 if not defined $Level;
  
  # find the list of marks and templates
  if ( defined $Parent )
  {

    # construct the current node's mark by concatenating the local mark onto
    #   the local mark
    $Node->{'mark'} = "$Parent->{'mark'}.$Node->{'mark'}";

    # construct a mark list from the local marks and the parent's marks
    $Node->{'vars'} =
    {
      %{ &CopyMarkNest( $Parent->{'vars'} ) },
      %{ &CopyMarkNest( $Node->{'vars'} ) },
    };
    
  }
  else
  {
  
    # construct the current node's mark from itself
    $Node->{'mark'} = ".$Node->{'mark'}";
    
  }


  # MARKS
  
  # find the longest mark in the mark list that the node's mark fits in
  #   and call the appropriate build function
  FIND_MARK:
  {
  
    # search through user defined marks first
    
    my $Markz = &FlatenMarkNest( $Node->{'vars'}{'^'}[1] );
    
    # sort keys
    my @Marks = sort
    {
      my $aba = $a;
      my $bab = $b;
      $aba =~ s/\.//g;
      $bab =~ s/\.//g;
      ( length( $b ) - length( $bab ) )
      <=>
      ( length( $a ) - length( $aba ) )
      ;
    } keys %$Markz;
    
    # find the first mark on the list that fits
    foreach my $Mark ( @Marks )
    {
      if ( $Node->{'mark'} =~ /$Mark$/ )
      {
        $Out = &BuildMacro( $Markz->{$Mark}, $Node, $Level );
        last FIND_MARK;
      }
    }
    
    # search builtin marks
    # sort keys
    my @Marks = sort
    {
      my $aba = $a;
      my $bab = $b;
      $aba =~ s/\.//g;
      $bab =~ s/\.//g;
      ( length( $b ) - length( $bab ) )
      <=>
      ( length( $a ) - length( $aba ) )
      ;
    } keys %SWL::Builds;
    # find the first mark on the list that fits
    foreach my $Mark ( @Marks )
    {
      if ( $Node->{'mark'} =~ /\.$Mark$/ )
      {
        $Out = &{ $SWL::Builds{$Mark} }( $Node, $Parent, $Level );
        last FIND_MARK;
      }
    }
    
  }
  
  
  # TEMPLATE REPLACEMENTS (impossible infinite recursion)
  
  my $Templatez = &FlatenMarkNest2( $Node->{'vars'}{'='}[1] );

  my $In = $Out;
  my $Out;

  # replace all templates
  while ( $In =~ '<swl ' )
  {
  
    my $Pos = index $In, '<swl ';
    
    $Out .= substr $In, 0, $Pos;
    $In = substr $In, $Pos + 5;
    
    # if the tag ends (as it is supposed to)    
    if ( $In =~ '>' )
    {
      
      my $Pos = index $In, '>';
      
      my $Tag = substr $In, 0, $Pos;
      $In = substr $In, $Pos + 1;
      
      # get rid of trailing spaces
      $Tag =~ s/\s+$//;
      
      # find a substitude for the mid-string
      if ( exists $Templatez->{ ".$Tag" } )
      {

        my @Lines = @{ $Templatez->{ ".$Tag" } };
        
        $Tag = join "\n", @Lines;

        # recompile the contents of the tag
        my $Node = &Structure( ":>\n$Tag" );
        $Tag = &Compile( $Node );
        
      }
      else
      {
        $Tag = "<swl $Tag>";
      }
      
      $Tag =~ s/\s+$//;
      $Tag =~ s/^\s+//;
      
      $Out .= $Tag;
      
    }
    
    
  }
  
  $Out .= $In;

  if ( wantarray )
  {
    return
    (
      $Out,
      $Templatez,
    );
  }
  else
  {
    return $Out;
  }
  
}

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

# develops a node based on a macro definition hash

sub BuildMacro
{
  my $Macro = shift;
  my $Node = shift;
  my $Level = shift;
  my $Out;
  
  &ClearBlankNodes( $Node );
    
  # if there's only one node (single)
  if ( @{ $Node->{'nodes'} } == 1 )
  {
  
    if ( $Node->{'nodes'}[0]{'mark'} eq '' )
    {
      
      # top
      if ( @{ $Macro->{'single-top'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'single-top'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
      elsif ( @{ $Macro->{'top'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'top'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
    
      my $Line = $Node->{'nodes'}[0]{'nodes'}[0];
      
      # tab
      if ( $Macro->{'single-tab'}[0][0] ne '' )
      {
        $Line =~ s/\t/$Macro->{'single-tab'}[0]/ge;
      }
      elsif ( $Macro->{'tab'}[0][0] ne '' )
      {
        $Line =~ s/\t/$Macro->{'tab'}[0][0]/ge;
      }
      
      # colon
      if ( $Macro->{'single-colon'}[0][0] ne '' )
      {
        if ( $Line !~ s/:/$Macro->{'single-colon'}[0][0]/e )
        {
          $Line = $Macro->{'single-colon'}[0][0] . $Line;
        }
      }
      elsif ( $Macro->{'colon'}[0][0] ne '' )
      {
        if ( $Line !~ s/:/$Macro->{'colon'}[0][0]/e )
        {
          $Line = $Macro->{'colon'}[0][0] . $Line;
        }
      }

      
      if ( $Line ne '' )
      {
      
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
          if
          (
            @{ $Macro->{'top'}[0] }
            or @{ $Macro->{'bottom'}[0] }
            or @{ $Macro->{'single-bottom'}[0] }
            or @{ $Macro->{'single-top'}[0] }
          )
          {
            $Out .= "\t";
          }
        }
        
        # left
        if ( $Macro->{'single-left'}[0][0] ne '' )
        {
          $Out .= $Macro->{'single-left'}[0][0];
        }
        elsif ( $Macro->{'left'}[0][0] ne '' )
        {
          $Out .= $Macro->{'left'}[0][0];
        }

        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$Line";
        }
        
        # right
        if ( $Macro->{'single-right'}[0][0] ne '' )
        {
          $Out .= $Macro->{'single-right'}[0][0];
        }
        elsif ( $Macro->{'right'}[0][0] ne '' )
        {
          $Out .= $Macro->{'right'}[0][0];
        }

        $Out .= "\n";

      }
      
      # bottom
      if ( @{ $Macro->{'single-bottom'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'single-bottom'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
      if ( @{ $Macro->{'bottom'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'bottom'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
    
    }
    # if it's a single, blank section, use the multi-top and multi-bottom
    else
    {

      # top
      if ( @{ $Macro->{'multi-top'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'multi-top'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
      elsif ( @{ $Macro->{'top'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'top'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
    
      if
      (
        @{ $Macro->{'multi-top'}[0] }
        or @{ $Macro->{'top'}[0] }
        or @{ $Macro->{'multi-bottom'}[0] }
        or @{ $Macro->{'bottom'}[0] }
      )
      {
        $Out .= &Compile( $Node->{'nodes'}[0], $Node, $Level + 1);
      }
      else
      {
        $Out .= &Compile( $Node->{'nodes'}[0], $Node, $Level );
      }

      # bottom
      if ( @{ $Macro->{'multi-bottom'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'multi-bottom'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }
      elsif ( @{ $Macro->{'bottom'}[0] } )
      {
        foreach my $Line ( @{ $Macro->{'bottom'}[0] } )
        {
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
          }
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }
          $Out .= "\n";
        }
      }

    }
    
  }
  # if there are multiple or no nodes (multi)
  else
  {
  
    my $First = 1;

    # top
    if ( @{ $Macro->{'multi-top'}[0] } )
    {
      foreach my $Line ( @{ $Macro->{'multi-top'}[0] } )
      {
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$Line";
        }
        $Out .= "\n";
      }
    }
    elsif ( @{ $Macro->{'top'}[0] } )
    {
      foreach my $Line ( @{ $Macro->{'top'}[0] } )
      {
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$Line";
        }
        $Out .= "\n";
      }
    }
    
    my @Outs;

    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {
    
      my $Out;

      if ( $NodeSub->{'mark'} eq '' )
      {
      
        my $Line = $NodeSub->{'nodes'}[0];
        $Line =~ s/^\s*//;
        $Line =~ s/\s*$//;
        
        # tab
        if ( $Macro->{'multi-first-tab'}[0][0] ne '' and $First  )
        {
          $Line =~ s/\t/$Macro->{'multi-first-tab'}[0][0]/ge;
        }
        elsif ( $Macro->{'multi-tab-first'}[0][0] ne '' and $First  )
        {
          $Line =~ s/\t/$Macro->{'multi-tab-first'}[0][0]/ge;
        }
        elsif ( $Macro->{'first-tab'}[0][0] ne '' and $First  )
        {
          $Line =~ s/\t/$Macro->{'first-tab'}[0][0]/ge;
        }
        elsif ( $Macro->{'multi-tab'}[0][0] ne '' )
        {
          $Line =~ s/\t/$Macro->{'multi-tab'}[0][0]/ge;
        }
        elsif ( $Macro->{'tab'}[0][0] ne '' )
        {
          $Line =~ s/\t/$Macro->{'tab'}[0][0]/ge;
        }
        
        # colon
        if ( $Macro->{'multi-first-colon'}[0][0] ne '' )
        {
          if ( $Line !~ s/:/$Macro->{'multi-first-colon'}[0][0]/e )
          {
            $Line = $Macro->{'multi-first-colon'}[0][0] . $Line;
          }
        }
        elsif ( $Macro->{'multi-colon-first'}[0][0] ne '' )
        {
          if ( $Line !~ s/:/$Macro->{'multi-colon-first'}[0][0]/e )
          {
            $Line = $Macro->{'multi-colon-first'}[0][0] . $Line;
          }
        }
        elsif ( $Macro->{'first-colon'}[0][0] ne '' )
        {
          if ( $Line !~ s/:/$Macro->{'first-colon'}[0][0]/e )
          {
            $Line = $Macro->{'first-colon'}[0][0] . $Line;
          }
        }
        elsif ( $Macro->{'multi-colon'}[0][0] ne '' )
        {
          if ( $Line !~ s/:/$Macro->{'multi-colon'}[0][0]/e )
          {
            $Line = $Macro->{'multi-colon'}[0][0] . $Line;
          }
        }
        elsif ( $Macro->{'colon'}[0][0] ne '' )
        {
          if ( $Line !~ s/:/$Macro->{'colon'}[0][0]/e )
          {
            $Line = $Macro->{'colon'}[0][0] . $Line;
          }
        }
        
        if ( $Line ne '' )
        {
        
          # indent
          if
          (
            $Macro->{'noindent'}[0][0] eq ''
            or lc $Macro->{'noindent'}[0][0] eq 'no'
            or lc $Macro->{'noindent'}[0][0] eq 'false'
          )
          {
            $Out .= "\t" x $Level;
            if
            (
              @{ $Macro->{'multi-top'}[0] }
              or @{ $Macro->{'top'}[0] }
              or @{ $Macro->{'multi-bottom'}[0] }
              or @{ $Macro->{'bottom'}[0] }
            )
            {
              $Out .= "\t";
            }
          }

          # left
          if ( $Macro->{'multi-first-left'}[0][0] ne '' and $First )
          {
            $Out .= $Macro->{'multi-first-left'}[0][0];
          }
          elsif ( $Macro->{'multi-left-first'}[0][0] ne '' and $First )
          {
            $Out .= $Macro->{'multi-left-first'}[0][0];
          }
          elsif ( $Macro->{'first-left'}[0][0] ne '' and $First  )
          {
            $Out .= $Macro->{'first-left'}[0][0];
          }
          elsif ( $Macro->{'multi-left'}[0][0] ne '' )
          {
            $Out .= $Macro->{'multi-left'}[0][0];
          }
          elsif ( $Macro->{'left'}[0][0] ne '' )
          {
            $Out .= $Macro->{'left'}[0][0];
          }
        
          # content
          if
          (
            $Macro->{'nocontent'}[0][0] eq ''
            or lc $Macro->{'nocontent'}[0][0] eq 'no'
            or lc $Macro->{'nocontent'}[0][0] eq 'false'
          )
          {
            $Out .= "$Line";
          }

          # right
          if ( $Macro->{'multi-first-right'}[0][0] ne '' and $First  )
          {
            $Out .= $Macro->{'multi-first-right'}[0][0];
          }
          elsif ( $Macro->{'multi-right-first'}[0][0] ne '' and $First  )
          {
            $Out .= $Macro->{'multi-right-first'}[0][0];
          }
          elsif ( $Macro->{'first-right'}[0][0] ne '' and $First  )
          {
            $Out .= $Macro->{'first-right'}[0][0];
          }
          elsif ( $Macro->{'multi-right'}[0][0] ne '' )
          {
            $Out .= $Macro->{'multi-right'}[0][0];
          }
          elsif ( $Macro->{'right'}[0][0] ne '' )
          {
            $Out .= $Macro->{'right'}[0][0];
          }

          $Out .= "\n";

          $First = 0;
          
        }
        
      }
      else
      {
      
        if
        (
          @{ $Macro->{'multi-top'}[0] }
          or @{ $Macro->{'top'}[0] }
          or @{ $Macro->{'multi-bottom'}[0] }
          or @{ $Macro->{'bottom'}[0] }
        )
        {
          $Out .= &Compile( $NodeSub, $Node, $Level + 1 );
        }
        else
        {
          $Out .= &Compile( $NodeSub, $Node, $Level );
        }
        
      }
      
      push @Outs, $Out;
      
    }
    
    # between
    if ( @{ $Macro->{'multi-between'}[0] } )
    {
      my $Between = join '', map
      {
        my $Out;
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$_";
        }
        $Out .= "\n";
        $Out;
      } @{ $Macro->{'multi-between'}[0] };
      $Out .= join $Between, @Outs;
    }
    elsif ( @{ $Macro->{'between'}[0] } )
    {
      my $Between = join '', map
      {
        my $Out;
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$_";
        }
        $Out .= "\n";
        $Out;
      } @{ $Macro->{'between'}[0] };
      $Out .= join $Between, @Outs;
    }
    else
    {
      $Out .= join '', @Outs;
    }

    # bottom
    if ( @{ $Macro->{'multi-bottom'}[0] } )
    {
      foreach my $Line ( @{ $Macro->{'multi-bottom'}[0] } )
      {
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$Line";
        }
        $Out .= "\n";
      }
    }
    elsif ( @{ $Macro->{'bottom'}[0] } )
    {
      foreach my $Line ( @{ $Macro->{'bottom'}[0] } )
      {
        # indent
        if
        (
          $Macro->{'noindent'}[0][0] eq ''
          or lc $Macro->{'noindent'}[0][0] eq 'no'
          or lc $Macro->{'noindent'}[0][0] eq 'false'
        )
        {
          $Out .= "\t" x $Level;
        }
        # content
        if
        (
          $Macro->{'nocontent'}[0][0] eq ''
          or lc $Macro->{'nocontent'}[0][0] eq 'no'
          or lc $Macro->{'nocontent'}[0][0] eq 'false'
        )
        {
          $Out .= "$Line";
        }
        $Out .= "\n";
      }
    }
    
  }

  return $Out;
    
}

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

sub BuildVariable
{

  # adds plain lines to the definition
  # adds calls recursively for sub-definitions
  
  my $Hash = shift; # the hash that contains the definition (it's an array)
  my $Key = shift; # the key that refers to that definition
  my $Node = shift; # the node with nodes that need to be processed into the def
  
  # my $Array = $Hash->{"$Key"}; # the array is the actual object
    # being affecteded here.  it's just referenced so that we can
    # delete it from its parent hash

  if ( @{ $Node->{'nodes'} } )
  {
    # if there are nodes to read
  
    # create the appropriate sub values
    $Hash->{"$Key"}[0] = []; # for line definitions
    $Hash->{"$Key"}[1] = {} if not defined $Hash->{"$Key"}[1]; # for nested definitions
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {
    
      # if it's a line to add to the definition
      if ( $NodeSub->{'mark'} eq '' )
      {
        push @{ $Hash->{"$Key"}[0] }, $NodeSub->{'nodes'}[0];
      }
      # if it's a sub-definition
      else
      {
        &BuildVariable( $Hash->{"$Key"}[1], $NodeSub->{'mark'}, $NodeSub );
      }
      
    }

  }
  else
  {
    #  if there are no nodes, delete any previously existing value
    delete $Hash->{"$Key"};
  }

}

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

sub BuildList
{

  my $Node = shift;
  my $Parent = shift;
  my $Level = shift;
  my $Top = shift;
  my $Bottom = shift;

  &ClearBlankNodes( $Node );
    
  my $Out;
  $Out .= "\n";

  $Out .= "\t" x $Level;
  $Out .= "$Top";
  $Out .= "\n";

  foreach my $NodeSub ( @{ $Node->{'nodes'} } )
  {

    if ( $NodeSub->{'mark'} eq '' )
    {

      my $line = $NodeSub->{'nodes'}[0];
      $line =~ s/^\s*//;
      $line =~ s/\s*$//;

      if ( $line ne '' )
      {
        $Out .= "\t" x ( $Level + 1 );
        $Out .= "<li>$line</li>";
        $Out .= "\n";
      }
      else
      {
        $Out .= "\n";
      }

    }
    else
    {
      $Out .= &Compile( $NodeSub, $Node, $Level + 1 );
    }
    
  }

  $Out .= "\t" x $Level;
  $Out .= "$Bottom";
  $Out .= "\n";

}

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

sub BuildListLink
{

  my $Node = shift;
  my $Parent = shift;
  my $Level = shift;
  my $Top = shift;
  my $Bottom = shift;

  &ClearBlankNodes( $Node );
    
  my $Out;
  $Out .= "\n";

  $Out .= "\t" x $Level;
  $Out .= "$Top";
  $Out .= "\n";

  foreach my $NodeSub ( @{ $Node->{'nodes'} } )
  {

    if ( $NodeSub->{'mark'} eq '' )
    {

      my $line = $NodeSub->{'nodes'}[0];
      $line =~ s/^\s*//;
      $line =~ s/\s*$//;
      
      ( my $link, my $title ) = split "\t", $line;
      $title = $link if $title eq '';
      $link =~ s/ /%20/g;
      $link =~ s/\~\//<swl root>\//; # grok

      if ( $line ne '' )
      {
        $Out .= "\t" x ( $Level + 1 );
        $Out .= qq(<li><a href="$link">$title</a></li>);
        $Out .= "\n";
      }
      else
      {
        $Out .= "\n";
      }

    }
    else
    {
      $Out .= &Compile( $NodeSub, $Node, $Level + 1 );
    }
    
  }

  $Out .= "\t" x $Level;
  $Out .= "$Bottom";
  $Out .= "\n";

}

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

sub BuildHeading
{

  my $Node = shift;
  my $Parent = shift;
  my $Level = shift;
  my $Heading = shift;

  &ClearBlankNodes( $Node );
    
  my $Out;
  $Out .= "\n";
  $Out .= "\n";

  my @NodeSubs = @{ $Node->{'nodes'} };

  if ( @NodeSubs )
  {

    my $Line = $Node->{'nodes'}[0]{'nodes'}[0];
    $Line =~ s/^\s*//;
    $Line =~ s/\s*$//;
    
    $Out .= "\t" x $Level;
    $Out .= "<h$Heading>";
    $Out .= "\n";
    
    $Out .= "\t" x ( $Level + 1 );
    $Out .= "$Line";
    $Out .= "\n";
    
    shift @NodeSubs;
    foreach my $NodeSub ( @NodeSubs )
    {
    
      if ( $NodeSub->{'mark'} eq '' )
      {

        my $Line = $NodeSub->{'nodes'}[0];
        $Line =~ s/^\s*//;
        $Line =~ s/\s*$//;

        $Out .= "\t" x ( $Level + 1 );
        $Out .= "<br>$Line\n";
      }
      
    }

    $Out .= "\t" x $Level;
    $Out .= "</h$Heading>";

  }
  else
  {

    my $Line = $Node->{'nodes'}[0];
    $Line =~ s/^\s*//;
    $Line =~ s/\s*$//;

    if ( $Node->{'mark'} eq '' )
    {
      $Out .= "\t" x $Level;
      $Out .= "<h$Heading>$Line</h$Heading>";
    }

  }

  $Out .= "\n";

  return $Out;
  
}

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

sub BuildCommentList
{

  my $Node = shift;
  my $Parent = shift;
  my $Level = shift;
  my $Method = shift;

  &ClearBlankNodes( $Node );
    
  my $Out;

  my $Number = $Method;

  foreach my $NodeSub ( @{ $Node->{'nodes'} } )
  {

    if ( $NodeSub->{'mark'} eq '' )
    {

      my $Line = $NodeSub->{'nodes'}[0];
      $Line =~ s/^\s*//;
      $Line =~ s/\s*$//;

      $Out .= "\t" x $Level;
      $Out .= $Number++;
      $Out .= ".)\t";
      $Out .= "$Line";
      $Out .= "\n";

    }

  }

  return $Out;

}

################################################################################
################################################################################
#
# BUILDS
#  %Builds contains the default definitions for all high level 'tags',
# associating them with appropriate function references.

%SWL::Builds =
(

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

  '@' => sub
  {
  
    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {
      if ( $NodeSub->{'mark'} ne '' )
      {
        $Parent->{'vars'} = {} if not exists $Parent->{'vars'};
        &BuildVariable( $Parent->{'vars'}, $NodeSub->{'mark'}, $NodeSub );
      }
    }
    
  },
  
################################################################################

  '\^' => sub
  {
  
    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {
      if ( $NodeSub->{'mark'} ne '' )
      {
        $Parent->{'vars'}{'^'}[1] = {} if not defined $Parent->{'vars'}{'^'}[1];
        &BuildVariable( $Parent->{'vars'}{'^'}[1], $NodeSub->{'mark'}, $NodeSub );
      }
    }
    
  },
  
################################################################################

  '=' => sub
  {
  
    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {
      if ( $NodeSub->{'mark'} ne '' )
      {
        $Parent->{'vars'}{'='}[1] = {} if not defined $Parent->{'vars'}{'='}[1];
        &BuildVariable( $Parent->{'vars'}{'='}[1], $NodeSub->{'mark'}, $NodeSub );
      }
    }
    
  },
  
################################################################################

  ':' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    my $Out;
    $Out .= "\n";

    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {

      if ( $NodeSub->{'mark'} eq '' )
      {
        $Out .= "$NodeSub->{'nodes'}[0]\n";
      }
      else
      {
        $Out .= &Compile( $NodeSub, $Node, $Level + 1 );
      }

    }

    return $Out;

  },

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

  '-' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    my $Out;
    $Out .= "\n";
    $Out .= "\n";

    $Out .= "\t" x $Level;
    if ( $Node->{'vars'}{'hr'}[1]{'clear'}[0][0] )
    {
      $Node->{'vars'}{'hr'}[1]{'clear'}[0][0] = 0;
      $Out .= "<br clear=all>";
      $Out .= "\n";
    }
    $Out .= "<hr>";
    $Out .= "\n";

    return $Out;

  },

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

  'p' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    my $Out;

    &ClearBlankNodes( $Node );
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {

      if ( $NodeSub->{'mark'} eq '' )
      {
        my $line = $NodeSub->{'nodes'}[0];
        $line =~ s/^\s*//;
        $line =~ s/\s*$//;
        if ( $line ne '' )
        {
          $Out .= "\t" x $Level;
          $Out .= "<p>$line</p>";
          $Out .= "\n";
        }
      }
      else
      {
        $Out .= &Compile( $NodeSub, $Node, $Level );
      }
    }

    return $Out;

  },

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

  'P' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    my $Out;

    my $DoBr;

    if ( @{ $Node->{'nodes'} } == 1 )
    {
      if ( $Node->{'nodes'}[0]{'mark'} eq '' )
      {
        my $line = $Node->{'nodes'}[0]{'nodes'}[0];
        $line =~ s/^\s*//;
        $line =~ s/\s*$//;
        if ( $line ne '' )
        {
          $Out .= "\t" x $Level;
          $Out .= "<p>$line</p>";
          $Out .= "\n";
        }
      }
      else
      {
        $Out .= &Compile( $Node->{'nodes'}[0], $Node, $Level + 1 );
      }
    }
    else
    {

      $Out .= "\t" x $Level;
      $Out .= '<p>';
      $Out .= "\n";

      foreach my $NodeSub ( @{ $Node->{'nodes'} } )
      {

        if ( $NodeSub->{'mark'} eq '' )
        {
          my $line = $NodeSub->{'nodes'}[0];
          $line =~ s/^\s*//;
          $line =~ s/\s*$//;
          if ( $line ne '' )
          {
            $Out .= "\t" x ( $Level + 1 );
            $Out .= '<br>' if $DoBr;
            $Out .= "$line";
            $Out .= "\n";
            $DoBr = 1;
          }
        }
        else
        {
          $Out .= &Compile( $NodeSub, $Node, $Level + 2 );
        }
      }

      $Out .= "\t" x $Level;
      $Out .= '</p>';
      $Out .= "\n";

    }

    return $Out;

  },

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

  'j' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    my $Out;
    $Out .= "\n";

    &ClearBlankNodes( $Node );
    
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {

      if ( $NodeSub->{'mark'} eq '' )
      {
      
        my $line = $NodeSub->{'nodes'}[0];
        $line =~ s/^\s*//;
        $line =~ s/\s*$//;
        $line = qq("$line") if $line =~ /\s/; # enclose in quotes if contains spaces
        
        $Out .= "\t" x $Level;
        $Out .= "<a name=$line></a>";
        $Out .= "\n";
        
      }
      else
      {
        $Out .= &Compile( $NodeSub, $Node, $Level );
      }
    }

    return $Out;

  },

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

  '\*' => sub
  {
    return BuildList( shift, shift, shift, '<ul>', '</ul>' );
  },

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

  '#' => sub
  {
    return BuildList( shift, shift, shift, '<ol type=1>', '</ol>' );
  },

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

  'a' => sub
  {
    return BuildList( shift, shift, shift, '<ol type=a>', '</ol>' );
  },

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

  'A' => sub
  {
    return BuildList( shift, shift, shift, '<ol type=A>', '</ol>' );
  },

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

  'i' => sub
  {
    return BuildList( shift, shift, shift, '<ol type=i>', '</ol>' );
  },

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

  'I' => sub
  {
    return BuildList( shift, shift, shift, '<ol type=I>', '</ol>' );
  },

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

  'l' => sub
  {
    return BuildListLink( shift, shift, shift, '<ul>', '</ul>' );
  },

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

  '\*l' => sub
  {
    return BuildListLink( shift, shift, shift, '<ul>', '</ul>' );
  },

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

  '#l' => sub
  {
    return BuildListLink( shift, shift, shift, '<ol type=1>', '</ol>' );
  },

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

  'al' => sub
  {
    return BuildListLink( shift, shift, shift, '<ol type=a>', '</ol>' );
  },

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

  'Al' => sub
  {
    return BuildListLink( shift, shift, shift, '<ol type=A>', '</ol>' );
  },

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

  'il' => sub
  {
    return BuildListLink( shift, shift, shift, '<ol type=i>', '</ol>' );
  },

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

  'Il' => sub
  {
    return BuildListLink( shift, shift, shift, '<ol type=I>', '</ol>' );
  },

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

  '1' => sub
  {
    return BuildHeading( shift, shift, shift, 1 );
  },
  
################################################################################

  '2' => sub
  {
    return BuildHeading( shift, shift, shift, 2 );
  },
  
################################################################################

  '3' => sub
  {
    return BuildHeading( shift, shift, shift, 3 );
  },
  
################################################################################

  '4' => sub
  {
    return BuildHeading( shift, shift, shift, 4 );
  },
  
################################################################################

  '5' => sub
  {
    return BuildHeading( shift, shift, shift, 5 );
  },
  
################################################################################

  '6' => sub
  {
    return BuildHeading( shift, shift, shift, 6 );
  },
  
################################################################################

  't' => sub
  {

    my $Node = shift;
    my $Parent = shift;
    my $Level = shift;
    
    my $Table; # ->[ X ][ Y ][ Z: line number ] = Cell
    my $Y = 0;
    my $X = 0;
    
    &ClearBlankNodes( $Node );
    foreach my $NodeSub ( @{ $Node->{'nodes'} } )
    {

      # if it's just a line of text, interpret it as a row of cells, tab delimited
      if ( $NodeSub->{'mark'} eq '' )
      {
      
        my @Cells = split 