/*===============================================================
COPYRIGHT NOTICE
-----------------------------------------------------------------
Copyright 2005 Donald MacLean

This file is part of the SpamSpoofer package.

The copyright  holder can be reached at www.macleans.net.

SpamSpoofer  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.

SpamSpoofer 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  SpamSpoofer; if  not,  write  to  the Free  Software
Foundation,  Inc.,  51  Franklin  St,  Fifth  Floor,  Boston,  MA
02110-1301   USA.    An   on-line   version   is   available  at:
http://www.gnu.org/licenses/licenses.html
===============================================================*/

/************************************************
These    global    variables    are    used    by
'registerVers()', which is called by all programs
in  the package--including this  one--to register
their  current  version  numbers.   (We  have  to
declare them  here, instead  of with the  rest of
the global variables,  so that the MANUAL (below)
can register this file's version number.)
************************************************/
var VERSIONS = new Array();
var versLOCK = "free";

/*===============================================================
MANUAL
-----------------------------------------------------------------
NAME
     spoofutils.js - utility functions for SpamSpoofer

SYNOPSIS
     Place this  code in  the HEAD section  of the HTML page from
     which you want to call SpamSpoofer:

          <SCRIPT TYPE="text/javascript"
                   SRC="[path/]spoofutils.js">
          </SCRIPT>

     Invoke the desired function from the HTML page as follows:

          <SCRIPT>function(args)</SCRIPT>

     Any output from  the  function  will  replace  the  function 
     invocation on the HTML page.

DESCRIPTION
     This is one of  the files in the SpamSpoofer HTML/JavaScript
     package, designed  to decode camouflaged  email addresses AT
     RUN TIME so that  no uncamouflaged email addresses need ever
     be  stored on  a site,  where they  would be  easy  prey for
     spammers'   web-crawlers.   Obtaining   a   decoded  address
     requires a copy-and-paste on the part of the user.

     The  file  'spoofmanual.html', which  is  also  part of  the
     package contains documentation in the form of an Interactive
     Manual, at the user and web-author  levels. Documentation in
     the file you are reading is aimed at JavaScript programmers.

     This file consists chiefly  of JavaScript functions, some of
     which are "external", in the sense that they are called from
     HTML  pages that  have "sourced"  SpamSpoofer or  from other
     files  in the  SpamSpoofer  package. But  most  of them  are
     "internal", that  is they are  called by other  functions in
     this file.

     All  knowledge  about  the   method  used  to  encode  email
     addresses  is   contained  in  the   files  'decode.js'  and
     'lookup.js'. This means that if it is ever desired to change
     the encoding  method (and you  are encouraged to do  so, see
     remarks on  this in the  Interactive MANUAL), those  are the
     only  files that  needs  to be  edited.   (I.e.  this  file,
     'spoofutils.js' does NOT need to be edited.)

     An understanding  of the three  different Subject categories
     and how they  are handled in this file,  will, however, also
     be necessary.

     Each  function  in  this  file has  its  own  self-contained
     documentation.

IMPLEMENTATION NOTES
  THE SRC ATTRIBUTE
     When the SRC attribute is  used with the <SCRIPT> tag, it is
     exactly as  if the contents  of the file specified  with the
     SRC  attribute  had been  placed  between  the <SCRIPT>  and
     </SCRIPT>  tags. Any code  actually appearing  between these
     two tags will be ignored.

     This means, specifically, that  it is NOT possible to invoke
     a  function between  these two  tags. 

     If another JavaScript  file is loaded by the  first one (see
     'GETTING  PATH TO SpamSpoofer  DIRECTORY' below)  the second
     file will overwrite  any elements of the first  one with the
     same name EVEN if  the code ("document.write()") to load the
     second file appears at the  beginning of the first file. All
     that  'document.write()'  does  is  write  the  SCRIPT  with
     instructions to load the second  file in the HTML file.  But
     the second file doesn't  get loaded until the browser's next
     pass through  the HTML file,  which happens after  the first
     file has been completely loaded.

  document.write()
     If this  is invoked from  an existing HTML page  (i.e. after
     that page  has already been  loaded), it will  OVERWRITE the
     contents of  that page, not  append to it.  For  example, if
     this function is called

         function hw(){ document.write("Hullo, World!"); }

     It  will replace the  current page  content with  the string
     "Hullo World".

     'document.write()'  can be  used as  a form  of indirection,
     allowing such things  as dynamically creating variable names
     to be accomplished.

   <NOBR>
     does not work in IE. For  example IE will happily put a line
     break before the left parenthesis in 'spoofer()', even if it
     is  enclosed within  <NOBR>...</NOBR>. (Firefox  won't break
     there even if NOBR is NOT used.)

   onUnload()
     is  unpredicable in  IE (It  works,  but other  code can  be
     executed  first.  See  the  documentation  of  the  function
     'resetvars()'.) Works predicatbly in FireFox.

   window.close()
     If used  (immediately) before window.open()  on same window,
     the  'open()' sometimes fails  in IE  (but not  in FireFox).
     See 'getwin()'  in the file 'spamspoofer.html  for code that
     works.
     
AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION */
registerVers(
/*@(#) #(@)*/ "spoofutils.js", "vers 2.4.3 - 2005 12 13");
/*

End MANUAL
===============================================================*/


/*===============================================================
GLOBAL VARIALBLES: 1. USER VARIABLES
-----------------------------------------------------------------
Any of  these varibles that you  want to use,  should be declared
and   set   in  the   file   'spooflocal.js',   where  they   are
explained. (Most  of them are  also explained in  the interactive
MANUAL.)

The declarations  below are just  in case the variables  have not
been  declared in  'spooflocal.js'.   By loading  'spooflocal.js'
AFTER  'spoofutils.js' (which  is  what the  code  in this  file
does) any  variables which have been set  locally will overwrite
the values set here (which is what we want).
---------------------------------------------------------------*/
var DEMODIR;
var DEFSUBJECT    = "SpamSpoofer";
var SPOOFMASTER   = "iam,_at_,home,_dot_,com,_sub_,SpamSpoofer";
var SPOOFMASTERID = "IamZac";
var SPOOFLINKIMG  = "env.png";  
var ME     = "iam";  
var MYID   = "IamZac";
var MYADDR = "iam,_at_,home,_dot_,com,_sub_,SpamSpoofer";    
var MYSUBJ = "SpamSpoofer";
/*---------------------------------------------------------------
GLOBAL VARIALBLES: 2. DEBUGGING VARIALBLES
These are set in the  file 'spooflocal.js', as delivered with the
SpamSpoofer  package.  However,  it doesn't  matter  whether they
have been  set or not, since 'spoofdebug.html'  always checks for
existence  before attempting  to write  out the  values  of these
variables.
-----------------------------------------------------------------
var DEBUG_ON = false;
var DEBUG1   = "unset";
var DEBUG2   = "unset";
var DEBUG3   = "unset"; 
var DEBUG4   = "unset"; 
---------------------------------------------------------------*/
var DEBUG5   = "set in 'spoofutils.js";
/*---------------------------------------------------------------
DEBUG6 is also set below, as an example.
-----------------------------------------------------------------
GLOBAL VARIALBLES: 3. INTERNAL VARIALBLES
-----------------------------------------------------------------
The rest of  the GLOBAL VARIABLES are used  only in communication
between the files  in this package, and should not  be set by the
user (unless you know what you are doing! :-).

The legal values of 'callnum' are:

     callnum   SpamSpoofer window content      set by
     -------   --------------------------      ------
     1         first screen                    callspoofer()
     2         present decoded email address   newname(pname)
     3         no address for pname            newname(pname)
     4         pname not a valid name          newname(pname)
     5         use SpamSpoofer!                newname(pname)
---------------------------------------------------------------*/
var spoofwin;
var callnum      = 0;
var spname       = "";
var pastename    = "";
var plainaddr    = "";
idarr            = new Array();
addrarr          = new Array();
inputarr         = new Array(2);
var spoofDIR;

/*----------------------------------------
These variables, used by 'registerVers()',
are declared at the top of this file:

var VERSIONS = new Array();
var versLOCK = "free";
----------------------------------------*/

/*-----------------------------
This is defined below, with the
rest of the subject  variables:

subjarr          = new Array();
-----------------------------*/

/*----------------------------------------------
These variables, used in version-handling,
are declared in 'spoofversdata.js':

var CURRENTVERS   = "";
var CURRENTSTATUS = ""; e.g. "under development"
var RELEASEVERS   = "";
var RELEASEDATE   = "";
----------------------------------------------*/


/*=============================================================*/


/*===============================================================
STYLE SHEET
-----------------------------------------------------------------
This is a STYLE we use  for some of the hyperlinks that we handle
ourselves with  'onClick'  event-handlers.  Note  that  Exploiter
(incorrectly) does not allow identifiers to begin with '_'.
---------------------------------------------------------------*/
document.write(
    "<STYLE TYPE=\"text/css\">"
  +   "A.spoofer_ { "
  +     "color: blue; "
  +     "text-decoration: underline; "
  +     "cursor: pointer; "
  +   "}"
  + "</STYLE>"
);
/*=============================================================*/


/*===============================================================
GETTING PATH TO SpamSpoofer DIRECTORY
-----------------------------------------------------------------
OVERVIEW
     Since the functions in SpamSpoofer can be called by any HTML
     page  anywhere on  the system,  as  well as  by other  files
     within the  package, dealing with relative paths  would be a
     big nuisance, and  prone to error. So what we  want to do is
     get the  ABSOLUTE path to  the SpamSpoofer directory  into a
     variable. This can  then be used to find  other files in the
     SpamSpoofer  package  regardless  of  where the  calls  come
     from. We COULD ask the user to supply us with this path. But
     that would be even more prone to error. (And unelegant.)
     
     The solution adopted here is  to start with the value of the
     SRC atribute  in the SCRIPT  which loads this file  onto the
     HTML page.  If  this is an absolute path, all  we need to do
     is remove the  file name. If it is a  relative path, we must
     prepend the  absolute path  to the HTML  file, which  we get
     from  'document.URL'.  (It's not  quite  as  simple as  this
     sounds.  There  is a lot of hassle  with Bill's backslashes,
     which can occur both openly and disguised.)
     
HOW BROWSERS HANDLE PATHS
     I have  spent a lot  of time figuring  out how the  two main
     browsers, FireFox  and Internet Exploiter  handle paths, and
     since I  don't want  to have  to do it  again, I'm  going to
     write  down the result  of my  investigations here.   If you
     already know  all about  this, or don't  want to  know, just
     skip the rest of this section.

  document.URL
  ============
     FireFox
     -------
       o) Always returns absolute path with forward slashes
       o) 2 slashes in 'file:///servername'

     Internet Explorer
     -----------------
       o) Returns absolute path with backslashes on PCs.
       o) 3 slashes   in   'file://servername'   (always  forward
          slashes   here,   even  if   rest   of  path   contains
          backslashes)

  SRC attribute 
  =============
     (as obtained with 'document.getElementsByTagName()')
     Fire Fox
     --------
       o) Always returns absolute path, even  if  value  supplied
          in HTML tag is relative address
       o) Cannot  handle backslashes if value supplied  in tag is
          absolute   address  (fails  silently,   blank  document
          results)
       o) Can handle any mixture of forward  and  backslashes  if
          tag supplies relative address
       o) Can  handle "./fn",  "d1/../../d2/fn",  "d1/./d2/fn" in
          relative & absolute paths
       o) Substitutes   the   3-character  string  "%5C"  for all
          backslashes supplied in (relative) tag path

     Internet Explorer
     -----------------
       o) Always returns the value supplied  in  the  SCRIPT  tag
          without any interpretation
       o) Can handle  any  mixture  of  forward  and  backslashes
          in relative or absolute tag values
       o) Can  handle "./fn",  "d1/../../d2/fn",  "d1/./d2/fn" in
          relative & absolute paths
       o) Backslashes are retained as backslashes
 
SLASHES IN PATH
     Note that  the rules for  slashes in URLs is  different from
     the  rules  for  pathnames  in  UNIX, where  any  number  of
     adjacent slashes are treated as  one. In an URL two adjacent
     slashes delimit  the beginning of  the server name.  If they
     occur elsewhere in the path, it's an error.

ONE TRUE PATH FORMAT
     Since  the 2  main browsers  between them  can  supply paths
     containing 3 different path-separators (slash, backslash and
     "%5C"), we  don't waste time  trying to figure out  which of
     theses separators  separates the filename (which  we want to
     remove) from the rest of  the path.  Instead, we convert ALL
     paths to  the One True  Path format (forward  slashes only).
     This we do with calls to the 'deBillify()' function.
===============================================================*/

/********************
Get the SRC attribute
********************/
var scripts = document.getElementsByTagName('script');
var index = scripts.length - 1;
var myScript = scripts[index];
var scriptsrc = myScript.src;
/****************************************
Thanks to Ashley Pond V,
http://feather.elektrum.org/book/src.html
for that  bit  of  magic!  The  important
thing here is that the  script  currently
being loaded is always the  last  one  in
the scripts  array.   Ashley  says   that
this will work "in all current JavaScript
enabled web browsers. Including  but  not
limited to IE for PC  and  Mac,  FireFox,
Safari, and Opera."
****************************************/

var spoofDIR  = "";
var docURL    = "";
var docDIR    = "";
var lastSlash = -1;

spoofDIR = deBillify(scriptsrc);

/****************************************
This is an example of how  the  debugging
variables can be used. See the Inteactive
MANUAL for more information.
****************************************/
var DEBUG6 = "scriptsrc = " + scriptsrc;

if ( 
  (spoofDIR.substring(0,1) == "/") ||
  (spoofDIR.indexOf(":/") >= 0) )
{ 
  /*************
  Absoslute Path
  *************/
} else { 
  /************
  Relative Path
  *************************************************
  Since we only need to do this on Bill's machines,
  there's no sense wasting time to see if  the  URL
  contains  backslashes. It does.
  ************************************************/
  docURL    = deBillify(document.URL);
  lastSlash = docURL.lastIndexOf("/");
  docDIR    = docURL.substring(0,lastSlash + 1);
var DEBUG10 = "docDIR = " + docDIR;
  spoofDIR  = docDIR + spoofDIR;
var DEBUG8 = "spoofDIR before removal of filename = " + spoofDIR;

}

/*****************************
Remove filename at end of path
(but  retain  trailing  slash)
*****************************/
lastSlash = spoofDIR.lastIndexOf("/");
spoofDIR  = spoofDIR.substring(0,lastSlash + 1);

var DEBUG9 = "final spoofDIR = " + spoofDIR;

/*=============================================================*/
function deBillify(path) 
/*---------------------------------------------------------------
NAME
     deBillify() - convert backslashes to forward slashes

SYNOPSIS
     deBillify(path) 
 
DESCRIPTION
     This function converts  either backslashes or occurrences of
     the string  "%5C" (used by  FireFox to encode  backslash) in
     pathnames to forward slashes and returns the result.
---------------------------------------------------------------*/
{
  var tmppath = path;
  var fwdpath = "";

  if ( tmppath.indexOf("%5C") >= 0 ) {
    /***********************************
    Rel path with backslashes on FireFox 
    ***********************************/
    for (i = 0; i < tmppath.length; i++) {
      if (tmppath.substring(i,i + 1) == "%") {
        fwdpath += "/";
        i++;
        i++;
      } else {
        fwdpath += tmppath.substring(i,i + 1);
      }
    }
    tmppath = fwdpath;
    fwdpath = "";
  }
  /**************************************
  This is  mostly  for  paths  on  Bill's
  machines,  but we do  it  for all paths
  passed  to us,  just in case.
  **************************************/
  for (i = 0; i < tmppath.length; i++) {
    if (tmppath.substring(i,i + 1) == "\\") {
      fwdpath += "/";
    } else {
      /*****************************
      This works in FF but not in IE:
         fwdpath[i] = tmppath[i];
      ******************************/
      fwdpath += tmppath.substring(i,i + 1);
    }
  }
  return fwdpath;
}
/* End of function 'deBillify()' */
/*=============================================================*/


/*********************************
Now it's  time   to  use  the  new 
pathname. We want the HTML page to  
load  these  JavaScript  files.
*********************************/
document.write(
  "<SCRIPT TYPE=\"text/javascript\" "
  + "SRC=\"" + spoofDIR + "spooflocal.js\">"
  + "</SCRIPT>"
);
document.write(
  "<SCRIPT TYPE=\"text/javascript\" "
  + "SRC=\"" + spoofDIR + "spoofdecode.js\">"
  + "</SCRIPT>"
);
document.write(
  "<SCRIPT TYPE=\"text/javascript\" "
  + "SRC=\"" + spoofDIR + "spoofversdata.js\">"
  + "</SCRIPT>"
);
/****************************************************************
Note that although the above SCRIPTS get WRITTEN to the HTML page
here, they do not get INTERPRETED until the rest of this file has
been loaded.  This  means, for instance, that if  a variable that
is declared  in 'spooflocal.js'  is asigned to  later on  in this
file, it  will get overwritten when  'spooflocal.js' finally gets
interpreted.

It also  means that  we cannot acces  any variables  or functions
contained in the above '*.js'  files in 'spoofutils.js' unless we
do so with "document.write", as we do here:
****************************************************************/

document.write(
  "<SCRIPT>"
  + "if ( !DEMODIR ) { "
  +   "DEMODIR = spoofDIR;"
  + "} else if (DEMODIR.indexOf('/') < 0) {"
  +   "DEMODIR = spoofDIR;"
  + "}"
  + "</SCRIPT>"
);

/*===============================================================
SUBJECT ROUTINES
===============================================================*/

/*===============================================================
Subject Variable Declarations 
-----------------------------------------------------------------
The subject which ends up on the subject line is whichever of the
following  exists  and has  the  highest  priority. The  function
'setsubject()' handles the selection.

Priority  Origin
========  ======
1         'argsubject' passed as argument to 'callspoofer()'
2         'encsubject' pre-existing in encoded address
3         'DEFSUBJECT' defined in 'spooflocal.js'
4         'DEFSUBJECT' defined above in 'spoofutils.js'
---------------------------------------------------------------*/
var argsubject  = "";
var encsubject  = "";
subjarr         = new Array();

/* End of Subject Variable Declarations */
/*=============================================================*/


/*=============================================================*/
function setsubject()
/*---------------------------------------------------------------
NAME
     setsubject() - set subject in 'subjarr[]' for current name

SYNOPSIS    
     setsubject()

DESCRIPTION
     This is an internal function called by 'decode()'.
---------------------------------------------------------------*/
{
  if (pastename === spname) {
    /*-----------------------------------------------------------
    User has  pasted (or  typed) in the  same name passed  in the
    call to 'callspoofer()'.
    -----------------------------------------------------------*/
    if (argsubject != "") {
      subjarr[pastename] = argsubject;
    } else if (encsubject != 0) {
      subjarr[pastename] = encsubject;
    } else {
      subjarr[pastename] = DEFSUBJECT;
    }
  } else {
    /*-----------------------------------------------------------
    User has typed in a different name from the one passed in the
    call to SpamSpoofer.  This  is O K.--as far as 'setsubject()'
    is  concerned).  It may  be a  name previously  processed, in
    which case  that name's camouflaged email  address will still
    be stored  in 'addrarr[]' and the subject  associated with it
    will  be stored  in  'subjarr[]', until  such  time as  these
    arrays  are cleared.   In any  case,  that is  no concern  of
    'setsubject().
    -----------------------------------------------------------*/
  }
}
/* End of function 'setsubject()' */ 
/*=============================================================*/


/*=============================================================*/
function getsubject()
/*---------------------------------------------------------------
NAME
     getsubject() - return subject in subjarr for current name

SYNOPSIS    
     getsubject()

DESCRIPTION
     This is an external function called from SpamSpoofer.  It is
     also called internally by the function 'havesubject()'.
---------------------------------------------------------------*/
{
    return subjarr[pastename];
}
/* End of function 'getsubject()' */ 
/*=============================================================*/


/*=============================================================*/
function havesubject()
/*---------------------------------------------------------------
NAME
     havesubject() - true if subject set for current name

SYNOPSIS    
     havesubject()

DESCRIPTION
     This is an external function called from SpamSpoofer.  It is
     not used internally.
---------------------------------------------------------------*/
{
  if (getsubject(pastename) != "") {
    return true;
  } else {
    return false;
  }
}
/* End of function 'havesubject()' */ 
/*=============================================================*/


/*=============================================================*/
function resetsubj()
/*---------------------------------------------------------------
NAME
     resetsubj() - reset 'argsubject' and 'encsubject'

SYNOPSIS    
     resetsubj()

DESCRIPTION
     This  is  an  internal  function  called  by  'validname()',
    'resetvars()'.
---------------------------------------------------------------*/
{
  argsubject  = "";
  encsubject  = "";
}
/* End of function 'resetsubj()' */ 
/*=============================================================*/

/*===============================================================
END OF SUBJECT ROUTINES
===============================================================*/


/*=============================================================*/
function newspoofer()
/*---------------------------------------------------------------
NAME
     newspoofer() - create or replace SpamSpoofer window

SYNOPSIS    
     newspoofer()

DESCRIPTION
     This is  an internal function called  by 'callspoofer()' and
     'reloadspoofer()'.

IMPLEMENTATION NOTES
  setting variables in children
     The following works in FireFox, but not in IE (ver 6.0):

          newwin = window.open("spamspoofer.html", "spamspoofer")
          newwin.creator = self;

     As a  kluge, we create a local  variable 

          var creator  = opener;

     in the target file.

  dependent windows
     If a  window is opened  with 'window.open()' and one  of the
     features  in  the feature  list  (3rd  arg  to 'open()')  is
     'dependent' (or 'dependent=yes'),  the window is supposed to
     die if its parent dies.  This works fine in FireFox, but not
     in IE (ver 6.0).
---------------------------------------------------------------*/
{
  var spoofurl = spoofDIR + "spamspoofer.html";

  spoofwin = window.open(spoofurl, "spamspoofer",
    "width=440,height=500"
  );
  spoofwin.focus();
  return true;
}
/* End of function 'newspoofer()' */ 
/*=============================================================*/


/*=============================================================*/
function getGNU(what)
/*---------------------------------------------------------------
NAME
     getGNU() - create or replace GNU window

SYNOPSIS    
     getGNU(what)

     Legal values of 'what':
          "GNU" - Load GNU Project home page
          "GPL" - Load GNU Public Licence

DESCRIPTION
     This  is  an  external  function  called  from  the  on-line
     copyright  notices. It  creates and/or  reloads  a dedicated
     window for GNU pages.

     The  reason  for  having  this function--instead  of  simple
     hyperlinks  with HREF--is  that when  an existing  window is
     reloaded, we want it brought to the front.

     This function is modelled on 'newspoofer()'.
---------------------------------------------------------------*/
{
  var GNUwin;
  var GNUurl;

  if (what == "GNU") {
    GNUurl = "http://www.gnu.org";
  } else if (what == "GPL") {
    GNUurl = spoofDIR + "gnupubliclicense.txt";
  } else {  /* Programming error */
    return false;
  }
  GNUwin = window.open(GNUurl,"GNUwin","");
  GNUwin.focus();
  return true;
}
/* End of function 'getGNU()' */ 
/*=============================================================*/


/*=============================================================*/
function spoofer(ID, sname, saddr, ssubj, sbody)
/*---------------------------------------------------------------
NAME
     spoofer() - create new instance of SpamSpoofer

SYNOPSIS    
     spoofer( ID, sname, saddr [, ssubj [, sbody] ] )

DESCRIPTION
     This  is   the  main  entry  point   to  SpamSpoofer.   What
     'spoofer()' does  is place a call to  'callspoofer()' on the
     HTML page.  See the interactive MANUAL for usage.

     On the relationship between 'spoofer()' and 'callspoofer()',
     including occasions  when only 'callspoofer()'  can be used,
     see the documentation of the latter function.
---------------------------------------------------------------*/
{
  var linkbody;

  if (sbody == null) {
    linkbody = sname;
  } else if (
      (sbody == "#SPOOFLINKIMG#")  && 
      (SPOOFLINKIMG != null)
    ) {
    linkbody = 
      "<IMG SRC='" + spoofDIR + SPOOFLINKIMG + "' BORDER=0>";
  } else {
    linkbody = sbody;
  }

  document.write("<A ONCLICK=\"callspoofer('" 
  + ID    +  "','"
  + sname +  "','" 
  + saddr 
  + ((ssubj != null)?("','" + ssubj):(""))
  + "')\" "
  + "STYLE=\"color: blue; "
  +       "text-decoration: underline; "
  +       "cursor: pointer\">"
  + linkbody + "</A>");
}
/* End of function 'spoofer()' */ 
/*=============================================================*/


/*=============================================================*/
function callspoofer(ID, sname, saddr, ssubj)
/*---------------------------------------------------------------
NAME
     callspoofer() - create new instance of SpamSpoofer

SYNOPSIS    
     callspoofer( sname, saddr [, ssubj] )

DESCRIPTION
     This  is the  function  which starts  the  ball rolling,  by
     calling   'newspoofer()'  to  create   a  new   instance  of
     SpamSpoofer. Or reuse an old one. There should never be more
     than one instance of SpamSpoofer open at the same time.

     From  the   user  and  naive  web-author   points  of  view,
     'spoofer()'   can  be   regarded  as   a   "wrapper"  around
     'callspoofer()'.  But from the JavaScript programmer's point
     of  view, there  is a  world  of differnce  between the  two
     functions:

     spoofer()
       at  LOADTIME  inserts a  call to 'callspoofer()'  onto the
       HTML page

     callspoofer()
       is an  'onClick()' event-handler  which  at  RUNTIME calls
       'newspoofer()'   to   create  a   new   window  and   load
       'spamspoofer.html'

     Because of these  differences, 'callspoofer()' does not have
     the  optional (5th)  link-body argument  of  'spoofer()'. An
     invocation of  'callspoofer()' occurs within  an HTML anchor
     tag, which  means that  the HTML author  can insert  his own
     link body directly.

     Another   Very  Important   Difference  between   these  two
     functions  is  that  from  a  child window  you  cannot  use
     'spoofer()'.   A call  to 'creator.spoofer()'  from  a child
     window  will cause  an anchor  to be  written to  the PARENT
     window, overwriting whatever is in it (and suprising whoever
     is  in  front  of  it).  In  this  case  you  can  only  use
     'callspoofer()'.

     This function is called  internally from 'spoofer()' and may
     also be  called externally from HTML pages.
---------------------------------------------------------------*/
{
  resetvars();
  callnum        = 1;
  spname         = sname;
  idarr[sname]   = ID;
  addrarr[sname] = saddr;

  if ((ssubj != null) && (ssubj != "") && (ssubj != 0)) {
    argsubject = ssubj;
  }
  newspoofer();
  return true;
}
/* End of function 'callspoofer()' */ 
/*=============================================================*/


/*=============================================================*/
function spoofauthor()
/*---------------------------------------------------------------
NAME
     spoofauthor() - call 'callspoofer()' with author's data

SYNOPSIS    
     spoofauthor()

DESCRIPTION
     The  reason for  having  this function,  instead of  calling
     'spoofer()' or 'callspoofer()'  directly to get the author's
     email address (which all the on-line copyright notices carry
     a link  to), is to ensure  that these links  will still work
     even  if the  encoding method  has been  modified  (which is
     encouraged).

     This way modifiers  of the encoding method will  not have to
     worry about changing all the links to the author.

AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION
@(#) - spoofAuthor() - vers 1.0.0 - 2005 12 01
---------------------------------------------------------------*/
{
  callspoofer("author",
    "Donald",
    "tensnaelcamdla",
    "SpamSpoofer"
  );

}
/* End of function 'spoofauthor()' */ 
/*=============================================================*/


/*=============================================================*/
function callDecode(arr)
/*---------------------------------------------------------------
NAME
     callDecode() - call 'decode()' in file 'spoofdecode.js'

SYNOPSIS
     callDecode(arr);

INPUT
     arr[0] - ID
     arr[1] - 2nd argument to 'callspoofer()'
     arr[2] - array containing output from 'callLookup()'

OUTPUT
     retarr[0] - 0 if OK, n != 0 if error
     retarr[1] - plain email address, without subject
     retarr[2] - subject, if encoded in address

DESCRIPTION
     The main reason for having this function, instead of calling
     'decode()' directly from 'newname()', to obtain the author's
     email  address  (which all  the  on-line copyright  notices
     carry a link  to), is to ensure that  these links will still
     work even if the 'decode()' and/or 'lookup()' functions have
     been modified (which is encouraged).

     This way modifiers  of the encoding method will  not have to
     worry about changing all the links to the author.

     Calls  to  'decode()'  are  intercepted  here,  and  if  the
     author's  data  is  recognized,  it  is  processed  in  this
     function   and  returned,   without  calling   the  (perhaps
     modified) function 'decode()'.

AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION
@(#) - callDecode() - vers 1.0.0 - 2005 12 13
---------------------------------------------------------------*/
{
  var lookuparr = new Array();
  var retarr    = new Array();

  lookuparr     = arr[2];

  if ((lookuparr[0] != mirror(spoofwin.window.name)) 
  ||  (lookuparr[0] != mirror("spamspoofer"))) {
    retarr[0] = 1;
    return retarr;
  } else if ((arr[0] == "author") 
    && (arr[1].indexOf("snaelcamdla") > 0)
  ) {
    retarr[0] = 0;
    retarr[1] = disobfuscate(
      lookuparr[2], arr[1] + lookuparr[1]
    );
    retarr[2] = "";
  } else {
    retarr = decode(arr);
  }
  return retarr;
}

/* End function 'callDecode()' */
/*=============================================================*/


/*=============================================================*/
function disobfuscate(a,b)
/*---------------------------------------------------------------
NAME
     disobfuscate() - make sense of obfuscated string

SYNOPSIS    
     disobfuscate(a,b)

DESCRIPTION
     This is an  internal function used by the  slithy toves when
     the mome  raths are  outgrabing.  Especially when  there are
     bandersnatches in the wabe.
AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION
@(#) - disobfuscate() - vers 1.0.0 - 2005 12 13
---------------------------------------------------------------*/
{
  var ka = new Array();
  var kb = new Array();
  var l = a.length;
  var bb = mirror(b);
  var reta = "";
  var retb = "ykcowrebbaJ";
  var c;
  var d;
  var ret = "";

  if (a != "") {
    for (i = 0, j = 0; i < l;) {
      ka[j] = a.substring(i,i + 1);
      kb[j] = "";
      i++;
      c = a.substring(i,i + 1);
      while ((c >= 0) && (c <= 9) && (i < l)) {
        kb[j] += c;
        i++;
        c = a.substring(i,i + 1);
      }
      j++;
    }
    l = bb.length;
    for (i = 0, j= 0, k = 0; (i < l) && (j < ka.length);) {
      k = kb[j];
      while (i < k) {
        ret += bb.substring(i,i + 1);
        i++;
      }
      if (ka[j] == "a") {
        ret += "\u0040";
      } else if (ka[j] == "d") {
        ret += "\u002E";
      }
      d = i;
      j++;
    }
    while (d < l) {
      ret += bb.substring(d,d + 1);
      d++;
    }
    return ret;
  } else {
    var retc = "ku\u002Exo\u0040";
    return mirror(retc + retb);
  }
}
/* End of function 'disobfuscate()' */ 
/*=============================================================*/


/*=============================================================*/
function reloadspoofer(num)
/*---------------------------------------------------------------
NAME
     reloadspoofer(num) - reload SpamSpoofer after decoding

SYNOPSIS    
     reloadspoofer(num)

DESCRIPTION
     This is an internal function called by 'newname(pname)'. The
     argument  'num'  determines  the  content  of  the  reloaded
     window.   The  values  which  'num'  can have  are  2  -  5,
     corresponding to the  values of 'callnum'. See documentation
     of the variable 'callnum' above.
---------------------------------------------------------------*/
{
  callnum = num;
  newspoofer();
  return true;
}
/* End of function 'reloadspoofer()' */ 
/*=============================================================*/


/*=============================================================*/
function resetarr(arr)
/*---------------------------------------------------------------
NAME
     resetarr() - reset array arr

SYNOPSIS    
     resetarr(arr)

DESCRIPTION
     This  is  an internal  function  called  by 'resetvars'.  It
     resets all the elements in 'arr' to null.
---------------------------------------------------------------*/
{
  var len = arr.length;

  for (e in arr) {
    arr[e] = "";
  }

  return true;
}
/* End of function 'resetarr()' */ 
/*=============================================================*/


/*=============================================================*/
function resetvars()
/*---------------------------------------------------------------
NAME
     resetvars() - initialize Spamspoofer variables

SYNOPSIS    
     resetvars()

DESCRIPTION
     This is  an internal function called  by 'callspoofer()'. It
     initializes all  the global variables before  starting a new
     session with SpamSpoofer.

NOTE ON 'onUnload()
     This will work:

          onUnload="creator.cleanupspoofer()" 

     But it works in  an unpredictable way in Internet Exploiter.
     If the  window is closed  by 'window.close()', 'resetvars()'
     may end up being executed AFTER next line of caller code
---------------------------------------------------------------*/
{
  callnum   = 0;
  spname    = "";
  pastename = "";
  plainaddr = "";
  resetsubj();
  resetarr(idarr);
  resetarr(addrarr);
  resetarr(subjarr);
  return true;
}
/* End of function 'resetvars()' */ 
/*=============================================================*/


/*=============================================================*/
function haveaddr(nn)
/*---------------------------------------------------------------
NAME
     haveaddr(nn) - true if addrarr[nn] is non-null

SYNOPSIS
     haveaddr(name)

DESCRIPTION
     This is an internal function called by 'newname()'.

IMPLEMENTATION NOTE
     Don't  make  the  mistake  of comparing  'addrarr[nn]'  with
     'null'. The empty string ("") does NOT evaluate to 'null'!
---------------------------------------------------------------*/
{
  if ((addrarr[nn]) && (addrarr[nn] != "")) {
    return true;
  } else {
    return false;
  }
}
/* End function 'haveaddr()' */
/*=============================================================*/

/*=============================================================*/
function mirror(str)
/*---------------------------------------------------------------
NAME
     mirror() - reverse and return str

SYNOPSIS
     mirror(str)

DESCRIPTION
     This is a simple string-handling function which reverses and
     returns the string it has received a argument. (It is called
     'mirror()' to avoid conflicts with any 'reverse()' functions
     which may be contained in other files.)

AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION
@(#) - mirror() - vers 1.0.0 - 2005 12 01
---------------------------------------------------------------*/
{
  var retstr = "";
  var len    = str.length;

  for (i = len; i > 0; i--) {
    retstr += str.substring(i - 1,i);
  }
  return retstr;
}
/* End function 'mirror()' */
/*=============================================================*/


/*=============================================================*/
function strip(str, ch)
/*---------------------------------------------------------------
NAME
     strip(nn)  - remove trailing chars from str

SYNOPSIS
     strip(str, ch)

DESCRIPTION
     'strip(str, ch)'  returns 'str' after  removing any trailing
     occurences of 'ch'.

     NB! Don't use 'char' here  (or anywhere else). I spent a lot
     of time debugging this program, only to discover that 'char'
     is  a reserverd  word  in Java,  which FireFox's  JavaScript
     interpreter  also reserves  on Java's  behalf,  whereas IE's
     doesn't.  (Howver, it would  have been  nice if  FireFox had
     come out and said so, instead of just failing silently!)

     This is an internal  function called by 'newname()' to strip
     trailing spaces.  It is also suitable  for removing trailing
     slashes from pathnames.
---------------------------------------------------------------*/
{
  var len  = str.length;
  var tstr  = str;
  for (i = len; i > 0, tstr.substring(i - 1, i) == ch; i--) {
    str = tstr.substring(0, i - 1);
  }
  return str;
}
/* End function 'strip()' */
/*=============================================================*/


/*=============================================================*/
function validname(nn)
/*---------------------------------------------------------------
NAME
     validname(nn)  - true if nn contains only valid characters

SYNOPSIS
     validname(nn)

DESCRIPTION
     'validname(nn)'  checks   that  'nn'  contains   only  valid
     characters.

     SpamSpoofer takes a restrictive approach to valid characters
     in a  name. Since  the only names  that can ever  produce an
     email address  are ones that  the web author has  chosen, it
     make  sense to  limit the  allowed characters  in a  name to
     those that a web author is  likely to want to offer. In fact
     it  makes a  lot of  sense.  By being  restrictive, we  foil
     malicious  attempts to crack  the system,  which occur  on a
     daily  basis on  all  internet sites,  usually by  automated
     "bots".

     By "restrictive" we mean that  instead of trying to guess at
     all the characters that could  possibly be used in an attack
     and disallowing them, we take the opposite approach and only
     allow those  characters that  we dictatorily, right  here in
     this  function, have  decided we  are  going to  allow in  a
     name. These are listed below.

     Valid Characters
     ----------------
     A-Z, a-z, 0-9, '_', '-', '.', space

     the characters with diacritical accents in the 
     Swedish, German and French alphabets

     It should be obvious from the code below how new characters
     can be added. The simplest way is to use Unicode character
     encoding, for example "\u00F6" for umlaut "o".

     This is an internal function called by 'newname()'.
---------------------------------------------------------------*/
{
  if (nn == "") {
    return false;
  } else {
    var lnn = nn.toLowerCase(); 
    len = lnn.length;
    var c;
    for (i = 0; i < (len - 1); i++) {
      c = lnn.substring(i, i + 1);
      if  (   !((c >= "a") && (c <= "z"))
           && !((c >= "0") && (c <= "9"))
           &&   (c != "_") && (c != "-") && (c != "." )
           &&   (c != "\040") // space
           &&   (c != "\u00C5") && (c != "\u00E5") // (A,a)ring
           &&   (c != "\u00C4") && (c != "\u00E4") // (A,a)uml
           &&   (c != "\u00D6") && (c != "\u00F6") // (O,o)uml
           &&   (c != "\u00DC") && (c != "\u00FC") // (U,u)uml
           &&   (c != "\u00C8") && (c != "\u00E8") // (E,e)grav
           &&   (c != "\u00C9") && (c != "\u00E9") // (E,e)acute
           &&   (c != "\u00CA") && (c != "\u00EA") // (E,e)circum
           &&   (c != "\u004F") && (c != "\u00F4") // (O,o)circum
           &&   (c != "\u00C7") && (c != "\u00E7") // (C,c)cid
          ) {
        return false;
      } /* End if */
    } /* End for */
  }
  return true;
}
/* End function 'validname()' */
/*=============================================================*/


/*=============================================================*/
function newname(pname)
/*---------------------------------------------------------------
NAME
     newname() - handle name pasted into input box

SYNOPSIS
     newname(pname)

DESCRIPTION
     If 'pname' is  a valid name and there is an  entry for it in
     'addrarr[]', which  there will always be if  the pasted name
     is the same as the one passed to 'callspoofer()', decode the
     camouflaged  address and  present it  in a  new  window.  If
     'pname' is not a valid name,  or no address can be found for
     it, return an error code.

     This is an external function called from 'spamspoofer.html'.
---------------------------------------------------------------*/
{
  retarr = new Array(2);

  /*-------------------------------------------------
  Get rid of any trailing spaces, which may have been 
  picked up during the copy-and-paste of pname.
  -------------------------------------------------*/
  pname = strip(pname, "\040");

  if (validname(pname)) {
    pastename  = pname; 
    if (haveaddr(pname)) {
      inputarr[0] = idarr[pname];
      inputarr[1] = addrarr[pname];
      inputarr[2] = spoofwin.callLookup(idarr[pname]);
      retarr = callDecode(inputarr);
      if (retarr[0] == 0) {
        plainaddr  = retarr[1];
        encsubject = retarr[2];
        setsubject();
        reloadspoofer(2);
      } else { /* retarr[0] != 0 */
        /*********************************
        Someone  seems  to  be  trying  to
        call 'decode()' non-interactively.
        *********************************/
        reloadspoofer(5);
      }
    } else /* ! haveaddr() */{
      return 3;
    }
  } else /* ! validname() */{
    return 4;
  }
}
/* End function 'newname()' */
/*=============================================================*/


/*=============================================================*/
function getwebmaster( subj )
/*---------------------------------------------------------------
NAME
     getwebmaster() - get 'mailto:' link to webmaster

SYNOPSIS
     getwebmaster( [subject] )

DESCRIPTION
     This function  calls SpamSpoofer  with the email  address of
     the current webmaster. 
 
     The idea is  that the email address of  the webmaster should
     only occur  in one place  on the whole site  (in camouflaged
     form,  of course),  whereas calls  to this  function  can be
     liberally dispersed throughout the site.

     That way, when  someone else takes over, there  will be only
     one (1) line  of code that needs to be  updated on the whole
     site.

     This  function  expects  the  variable 'SPOOFMASTER'  to  be
     defined  elsewhere  and available  to  the  HTML page  which
     invokes  SpamSpoofer. The  simplest  way to  do  this is  to
     initialize 'SPOOFMASTER' in  the file 'spooflocal.js', which
     contains instructions.

AUTHOR
     Donald MacLean          IGdonaldNOatREmacCAleansPIdotTAnetLS
                                      (Do what the capitals say.)
VERSION
@(#) - getwebmaster() - vers 2.2.2
---------------------------------------------------------------*/
{
  var spoofname = "webmaster";
  var webmastersubj = "";

  if (getwebmaster.arguments.length > 0) {
    webmastersubj = getwebmaster.arguments[0];
  }
  document.write("<A ONCLICK=\"callspoofer('" 
  + SPOOFMASTERID   +  "','" 
  + spoofname +  "','" 
  + SPOOFMASTER 
  + ((webmastersubj != '')?("','" + webmastersubj +"'"):("'"))
  + ")\" "
  + "STYLE=\"color: blue; "
  +       "text-decoration: underline; "
  +       "cursor: pointer\">"
  + "webmaster" + "</A>");
}
/* End function 'getwebmaster()' */
/*=============================================================*/


/*=============================================================*/
function getme(subject, name)
/*---------------------------------------------------------------
NAME
     getme() - get 'mailto:' link to me

SYNOPSIS
     getme( [subject [, name]] )

DESCRIPTION
     This function calls SpamSpoofer with the values contained in
     the global  variables ME, MYADDR, MYSUBJ, MYNAME.  If one or
     both of the optional arguments are present, they replace the
     values of MYSUBJ and MYNAME as appropriate.

     This  function   is  based  on   'getwebmaster()'.  See  the
     documentation of that function for more details.
---------------------------------------------------------------*/
{
  var spoofname = "";
  var spoofsubj = "";

  if (getme.arguments.length > 0) {
    spoofsubj = getme.arguments[0];
  }
  if (getme.arguments.length > 1) {
    spoofname = getme.arguments[1];
  }
  document.write("<A ONCLICK=\"callspoofer('" 
  + MYID   +  "','" 
  + ME     +  "','" 
  + MYADDR + "','"
  + ((spoofsubj != '')?(spoofsubj):(MYSUBJ))
  + "')\" "
  + "STYLE=\"color: blue; "
  +       "text-decoration: underline; "
  +       "cursor: pointer\">"
  + ((spoofname != '')?(spoofname):('me'))
  + "</A>");
}
/* End function 'getme()' */
/*=============================================================*/


/*=============================================================*/
function set_title(str)
/*---------------------------------------------------------------
NAME
     set_title() - put "(LOCAL)", etc in title of disk file

DESCRIPTION
     When we are viewing a  local (disk) version of an HTML file,
     this function will  nprepend "(C:)", etc, to the  title if it
     can  figure out  the name  of  the disk.  Otherwise it  will
     default to  "(LOCAL)".  Nothing will be prepended  if we are
     viewing the file over the Web.

     This is so that if we have a remote and a local version open
     at  the same time,  we can  distinguish easily  between them
     especially when they are iconized (or "minimized").

     In addition,  the intrusive browser  name will be  pushed to
     the right on the Title Bar.
---------------------------------------------------------------*/
{
  var title = str;

  if ( /http:\/\/.*/.test(window.location.href) ) {
    /*-------------------
    Web file. Do nothing.
    -------------------*/
    ;
  } else if ( /file:\/\/\/.*/.test(window.location.href) ) {
    /* 
     *  Local file. Prepend disk name to Title
     */
    title = "("   + window.location.href.substring(8,10) 
          + ") "  + title;
  } else  {
    /*-------------------------
    Can't figure out disk name.
    -------------------------*/ 
    title = "(LOCAL) " + title;
  }
  /*---------------------------------------
  Push intrusive browser name to the right.
  ---------------------------------------*/
  title +=
    "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
  + "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
  + "(&nbsp;-&nbsp;:&nbsp;)";

  document.write("<TITLE>" + title + "</TITLE>");
}
/* End function 'set_title()' */
/*=============================================================*/


/*=============================================================*/
function spoofdebugger()
/*---------------------------------------------------------------
NAME
     spoofdebugger() - create a hyperlink to 'spoofdebug.html'

SYNOPSIS    
     spoofdebugger()

DESCRIPTION
     This is an external function called from the HTML page which
     invokes SpamSpoofer.  It inserts  a call to newdebugger() on
     the HTML page at the point from which it was called.

     It is  called by placing this  code on the HTML  page at the
     point where you want the link to appear:

       <SCRIPT TYPE="text/javascript">spoofdebugger();</SCRIPT>

     This call can be left in a strategic place on the HTML page.
     A hyperlink will only be generated if the variable DEBUG_ON
     has the Boolean value 'true'. (NOT the stirng "true"!)

     See the file 'spoofdebug.html' for more details.     
---------------------------------------------------------------*/
{
  if ( DEBUG_ON ) {
    document.write(
      "<A CLASS=\"spoofer_\" "
      + "ONCLICK=\"newdebugger()\">"
      + "Print debugging output.</A>"
    );
  }
}
/* End of function 'spoofdebug()' */ 
/*=============================================================*/


/*=============================================================*/
function newdebugger()
/*---------------------------------------------------------------
NAME
     newdebugger() - create or replace debugger window

SYNOPSIS    
     newdebugger()

DESCRIPTION
     This is an external function called from the hyperlink which
     'spoofdebug()' has  inserted on  some HTML page.  It creates
     and/or reloads a dedicated window for the debugger output.

     The  reason for  having this  function  instead of  a simple
     hyperlink  with HREF,  is that  when an  existing  window is
     reloaded,  we  want it  brought  to  the  front. When  we're
     debugging, we  pop this window up  a lot, and  don't want to
     have to go searching for it each time.

     This function is modelled on 'newspoofer()'.
---------------------------------------------------------------*/
{
  var debugwin;
  var debugurl = spoofDIR + "spoofdebug.html";

  debugwin = window.open(debugurl,"debugwin","");
  debugwin.focus();
  return true;
}
/* End of function 'newdebugger()' */ 
/*=============================================================*/


/*=============================================================*/
function getVersLock(caller)
/*---------------------------------------------------------------
NAME
     getVersLock() - get lock on 'VERSIONS[]' array

SYNOPSIS
     getVersLock(caller)

DESCRIPTION
     This function tries to get a lock on the array 'VERSIONS[]'.
     It blocks until it has done so.

AUTHOR
     Donald MacLean      See Interactive MANUAL for contact info.

VERSION
@(#) - getVersLock() - vers 1.0.0 - 2005 11 27
---------------------------------------------------------------*/
{
  for (i = 0; i < 10; i++) {
    if (versLOCK == "free") {
      versLOCK = caller;
      if ( versLOCK == caller ) {
        return true;
      } else {
        alert(
        "'getVersLock()': requested lock for '" + caller + "', "
        + "but '" + versLOCK + "' beat me to it. "
        );
      } /* End else */
    } /* End if */
    self.window.setTimeout("/* Do nothing */",100);
  } /* End for */
  return false;
}
/* End function 'getVersLock()' */
/*=============================================================*/


/*=============================================================*/
function freeVersLock(caller)
/*---------------------------------------------------------------
NAME
     freeVersLock() - release lock on VERSIONS array

SYNOPSIS
     freeVersLock(caller)

DESCRIPTION
     This  function  tries  to  release  its lock  on  the  array
     'VERSIONS[]'.  It complains if it can't do so.

AUTHOR
     Donald MacLean      See Interactive MANUAL for contact info.

VERSION
@(#) - freeVersLock() - vers 1.0.0 - 2005 11 27
---------------------------------------------------------------*/
{
  if ( versLOCK == caller ) {
    versLOCK = "free";
    return true;
  } else {
    alert("Program error: versLOCK == " + versLOCK);
    return false;
  }
}
/* End function 'freeVersLock()' */
/*=============================================================*/


/*=============================================================*/
function registerVers(caller, versinfo)
/*---------------------------------------------------------------
NAME
     registerVers() - add string versinfo to 'VERSIONS[]' array

SYNOPSIS
     registerVers(caller, versinfo)

DESCRIPTION
     This function obtains a  lock on 'VERSIONS[]', adds versinfo
     to   it,   then  releases   lock.   Because   it  waits   on
     'getverlock()', it blocks until successful.

AUTHOR
     Donald MacLean      See Interactive MANUAL for contact info.

VERSION
@(#) - registerVers() - vers 1.0.0 - 2005 11 27
---------------------------------------------------------------*/
{
  if ( getVersLock(caller) ) {
    VERSIONS[caller] = versinfo;
    freeVersLock(caller);
  }
} 
/* End function 'registerVers()' */
/*=============================================================*/


/*==================
Local Variables:
indent-tabs-mode:nil
fill-column:65
fill-prefix:"     "
End:
==================*/
