<MIVA standardoutputlevel=""></MIVA>
<MvASSIGN NAME="OFF" VALUE = "{'<MIVA standardoutputlevel=\"\">'}">
<MvASSIGN NAME="ON" VALUE = "{'<MIVA standardoutputlevel=\"text,html\">'}">
<MvCOMMENT>###################################################################
# Message Board Version 1.1 #
# Developed by Michael Sussna Yahoo! #
# Created 12/11/97 Last Modified 9/28/99 #
# Based on WWWBoard #
# Copyright 1996 Matt Wright mattw@worldwidemart.com #
# Scripts Archive at: http://www.worldwidemart.com/scripts/ #
##############################################################################
# WWWBoard COPYRIGHT NOTICE #
# Copyright 1996 Matthew M. Wright All Rights Reserved. #
# #
# WWWBoard may be used and modified free of charge by anyone so long as #
# this copyright notice and the comments above remain intact. By using this #
# code you agree to indemnify Matthew M. Wright from any liability that #
# might arise from it's use. #
# #
# Selling the code for this program without prior written consent is #
# expressly forbidden. In other words, please ask first before you try and #
# make money off of my program. #
# #
# Obtain permission before redistributing this software over the Internet or #
# in any other medium. In all cases copyright and header must remain intact.#
##############################################################################
# Yahoo!, Inc. COPYRIGHT NOTICE #
# Copyright 1998-1999 Simple Network Communications, Inc. #
# Copyright 1999-2000 Yahoo!, Inc. #
# All rights reserved. #
##############################################################################
# #
# CHANGE LOG #
# #
# Version 1.16, 8/01/01: #
# #
# Added additional checks for executable code being submitted in form #
# fields. #
# #
# Version 1.15, 8/16/00: #
# #
# Added additional checks for executable code being submitted in form #
# fields. #
# #
# Version 1.14, 6/7/00: #
# #
# Added additional file locking logic to Remove and Compress Database #
# functions. #
# #
# Version 1.13, 9/28/99: #
# #
# Added MvLOCKFILE to Compress Database function. This should prevent #
# potential conflicting write accesses to the database during a compress. #
# #
# Version 1.12, 2/11/99: #
# #
# Changed default year in search to be current year. #
# #
# Version 1.11, 12/24/98: #
# #
# Added URL information in post and followup preview. #
# #
# Version 1.10, 12/18/98: #
# #
# Added "In reply to" feature when viewing a message. #
# Changed access of messages to use MvFIND, significantly speeding access. #
# Added wildcard to IP blocking in Admin. #
# #
##################################################################</MvCOMMENT>
<MvCOMMENT> Define Variables </MvCOMMENT>
<MvASSIGN NAME = "mesgdir" VALUE = "{'messages'}">
<MvASSIGN NAME = "passwd_file" VALUE = "{'passwd.txt'}">
<MvASSIGN NAME = "config_file" VALUE = "{'config.txt'}">
<MvASSIGN NAME = "unwelcome_list" VALUE = "{'unwelcome.txt'}">
<MvASSIGN NAME = "unwelcome_list2" VALUE = "{'unwelcome2.txt'}">
<MvASSIGN NAME = "ext" VALUE = "{'txt'}">
<MvCOMMENT> This is a special self number in the database: </MvCOMMENT>
<MvASSIGN NAME = "super_root" VALUE = "{'9999999'}">
<MvCOMMENT>
The message board is displayed in a specific sequence. The most recent
main postings (or roots) are displayed first. Within each main posting
any followup postings are then displayed, most recent first. Followups
to followups are displayed most recent first, and so on. This produces
a hierarchy of postings.
To build this display as fast as possible from the database, each posting
is assigned a sequence number. The sequence numbers are designed so that
the hierarchy follows from simply listing the postings in sequence. For
example, suppose that there are 10 postings that should display in the
following hierarchy:
4
10
5
8
9
6
7
3
1
2
The main postings are 1, 3, and 4. 4 is the most recent main posting.
Posting 4 has 2 followups, 5 and 10. 10 is more recent than 5 and has
no followups. 5 has two followups, 6 and 8, each of which has a followup.
The numbering of the postings reflects the order in which they were
created, 10 being the most recently created posting.
To simply list the postings so that they would display in the proper
order above, we would need the following relationships between any
sequence numbers assigned: 4's sequence number should be first in the
list, followed by 10's, 5's, 8's, 9's, 6's, 7's, 3's, 1's, and finally
2's.
We could assign ascending sequence numbers, but we actually assign
descending numbers. So, in our example, we could think of the sequence
number assignments as 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 for postings
4, 10, 5, 8, 9, 6, 7, 3, 1, and 2 respectively.
There are several constraints which influence our scheme for sequence
numbering:
Each posting should have a unique sequence number --- we want to avoid
duplicates so that there is no ambiguity when listing postings.
Each sequence number should correctly reflect the relative position of its
posting in the hierarchy --- thus it should fall between the sequence
numbers of the postings between which the new posting is being inserted.
Insertion can occur at any arbitrary point in the hierarchy. We cannot
know ahead of time how many postings will be placed at any given location.
Finally, we need to allow for a reasonably large number of levels of
followup. In actual practice it is not unusual to see 20 levels of
followup. That is, one main posting could have a followup which could
have a followup which could have a followup, and so on, to 20 levels of
followup.
To accommodate these constraints, we have devised the following scheme
for sequence numbering. Unique numbering is supported by careful planning
for handling the other constraints. Reflecting relative position in the
hierarchy is provided by selecting a sequence number for a new posting
that lies between the sequence numbers of the postings before and after
the new posting.
By assigning each root a comfortable range of numbers for its followups,
we lay the foundation for satisfying the final two constraints. Suppose
that we allow 1000 followups per root. So the roots are numbered 1000,
2000, and so on as they are created. Followups are numbered sequentially
from 1 within that range. So root 2000's followups fall in the range
1001 - 1999, since posting 2000 must display before any of its followups.
The first followup gets 1001. If a second followup to posting 2000 is
created, it gets sequence number 1002. But what if a followup to posting
1001 is created? It needs a sequence number lower than 1001, since it
must display after 1001, right? What if we renumber so that what was 1001
becomes 1002, and the new posting gets 1001? This is the scheme that we use.
As a followup is inserted, it causes a renumbering of all followups between
it and the root. So suppose we have 1001 - 1005, and need to insert a new
followup to 1003? 1001 and 1002 can stay unchanged. 1003 - 1005 need to
be incremented, and the new posting becomes 1003. The root stays at 2000.
This scheme satisfies both of the final constraints because arbitrary
insertion is accommodated and because any number of levels of followup is
allowed as well. At least, any number of followups that fit within the
range of 1000 numbers. Nested followups share the range of numbers with
any other followups.
But how does this method affect performance? Since the renumbering is
localized to just the region of numbers assigned to a single root, this
minimizes the impact. Since just the followups between the new posting and
the root must be visited, this reduces the impact further.
All ancestors of a new posting must be visited, to update their count of
descendents. We do this by moving back from the parent to the root. All
of the postings between the root and the new posting must have their
sequence numbers incremented. We do this by moving forward from the root
to the parent.
Unfortunately these two movements oppose each other. But, each requires
its specific direction of motion. The chain of ancestors flows bottom up
in the hierarchy, that is, from leaf to root. We learn the parent of a
posting from the posting's database record. This means traveling from
parent to grandparent etc. as we move back to the root via index1
(sequence number). Not all of the postings encountered are necessarily
in the lineage of the new posting.
For updating the sequence numbers, we move forward from the root because
otherwise we would be assigning duplicate numbers temporarily, which is
enough time to do damage. For example, say we have root 2000 and followups
1002 and 1001 under it. We want to insert a followup to 1001. Note
that it doesn't matter whether 1001 and 1002 are both followups to 2000,
or 1001 is a followup to 1002. We want the end result of the insertion
to be the sequence 2000, 1003, 1002, 1001 with the new posting being 1001.
If we start the renumbering with the old 1001 and move back towards the
root, when we make 1001 into 1002 we now temporarily have two 1002's.
This throws off the index and plays havoc. Skipping back from this finds
2000, not the original 1002. So, we must start from the root and move
forward, making 1002 into 1003, then 1001 into 1002. Then we can safely
create a new sequence number of 1001 for the new posting.
One final note on performance considerations is the following. The current
maximum sequence number is stored in the super root as its sequence number.
This means that we have to retrieve that sequence number before inserting
a main posting, and update that number with the new value. But we have to
access the super root's database record anyway when we create a posting,
because we need to update its number of descendents, which generates the
message number for the new posting.
By using a 10-digit number for sequencing, and allowing 1000 followups per
main posting, we are allowing for 10,000,000 main postings. We can change
these values, but doing so means that existing postings would be obsolete.
</MvCOMMENT>
<MvASSIGN NAME = "max_seqnum_increment" VALUE = "{'0000001000'}">
<MvASSIGN NAME = "seqnum_size" VALUE = "{'10'}">
<MvCOMMENT> Increment for creating self and parent numbers: </MvCOMMENT>
<MvASSIGN NAME = "big_bump" VALUE = "{'1000000'}">
<MvASSIGN NAME = "db_just_created" VALUE = "{'0'}">
<MvASSIGN NAME = "textwidth" VALUE = "{'60'}">
<MvASSIGN NAME = "urlwidth" VALUE = "{'80'}">
<MvASSIGN NAME = "admin_banner_ad" VALUE = "{''
}">
<MvASSIGN NAME = "banner_ad" VALUE = "{''
}">
<MvASSIGN NAME = "newline" VALUE = "{asciichar(10)}">
<MvASSIGN NAME = "carriage" VALUE = "{asciichar(13)}">
<MvASSIGN NAME = "crlf" VALUE = "{carriage $ newline}">
<MvASSIGN NAME = "amp" VALUE = "{asciichar(38)}">
<MvASSIGN NAME = "quote" VALUE = "{asciichar(39)}">
<MvASSIGN NAME = "longquote" VALUE = "{'''}">
<MvASSIGN NAME = "period" VALUE = "{asciichar(46)}">
<MvASSIGN NAME = "slash" VALUE = "{asciichar(47)}">
<MvASSIGN NAME = "less" VALUE = "{asciichar(60)}">
<MvASSIGN NAME = "greater" VALUE = "{asciichar(62)}">
<MvASSIGN NAME = "qmark" VALUE = "{asciichar(63)}">
<MvASSIGN NAME = "atsign" VALUE = "{asciichar(64)}">
<MvASSIGN NAME = "backslash" VALUE = "{asciichar(92)}">
<MvASSIGN NAME = "newlines" VALUE = "{crlf $ crlf}">
<MvASSIGN NAME = "longless" VALUE = "{'<'}">
<MvASSIGN NAME = "longgreater" VALUE = "{'>'}">
<MvASSIGN NAME = "dquote" VALUE = "{'\"'}">
<MvASSIGN NAME = "longdquote" VALUE = "{'"'}">
<MvASSIGN NAME = "bullet" VALUE = "{asciichar(149)}">
<MvASSIGN NAME = "nbsp" VALUE = "{asciichar(174)}">
<MvASSIGN NAME = "longnbsp" VALUE = "{' '}">
<MvASSIGN NAME = "indent" VALUE = "{' '}">
<MvASSIGN NAME = "delim" VALUE = "{'!!##!!'}">
<MvASSIGN NAME = "yuml" VALUE = "{asciichar(255)}">
<MvCOMMENT>###########################################################</MvCOMMENT>
<MvCOMMENT>###########################################################</MvCOMMENT>
<MvCOMMENT> MAIN PROCESSING </MvCOMMENT>
<MvEVAL EXPR = Initialization()>
<MvIF EXPR = "{parm_func EQ 'showmain' OR parm_func EQ ''}">
<MvEVAL EXPR = ShowMain()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'showmsg'}">
<MvEVAL EXPR = ShowMessage()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'showpostform'}">
<MvEVAL EXPR = ShowPostForm()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'showsearch'}">
<MvEVAL EXPR = ShowSearchForm()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'post'}">
<MvEVAL EXPR = PostControl()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'postpreview'}">
<MvEVAL EXPR = ShowPostPreview()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'followuppreview'}">
<MvEVAL EXPR = ShowFollowupPreview()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'showfaq'}">
<MvEVAL EXPR = ShowFAQ()>
<MvEXIT>
<MvELSE>
<MvIF EXPR = "{parm_func EQ 'admin'}">
<MvEVAL EXPR = Admin()>
<MvEXIT>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
<MvEXIT>
<MvCOMMENT> END OF MAIN PROCESSING </MvCOMMENT>
<MvFUNCTION NAME = "Initialization">
<MvIF EXPR = "{nargs GT 1 AND arg2 EQ 'admin'}">
<MvASSIGN NAME = "parm_func" VALUE="{'admin'}">
<MvELSE>
<MvEVAL EXPR = GetFormInput()>
</MvIF>
<MvEVAL EXPR = FindMboardName()>
<MvIF EXPR = "{NOT fexists(dirprefix)}">
<MvASSIGN NAME = "x" VALUE = "{fmkdir(dirprefix)}">
</MvIF>
<MvASSIGN NAME = "msgfiledir" VALUE = "{dirprefix $ '/' $ mesgdir}">
<MvIF EXPR = "{NOT fexists(msgfiledir)}">
<MvASSIGN NAME = "x" VALUE = "{fmkdir(msgfiledir)}">
</MvIF>
<MvIF EXPR = "{NOT fexists(database)}">
<MvEVAL EXPR = CreateDatabase()>
</MvIF>
<MvASSIGN NAME = "passwdfile" VALUE = "{dirprefix $ '/' $ passwd_file}">
<MvIF EXPR = "{NOT fexists(passwdfile)}">
<MvIF EXPR = "{action EQ 'provide_identity'}"> <MvCOMMENT> Done til input errors fixed </MvCOMMENT>
<MvEVAL EXPR = ProvideIdentity("ShowProvideIdentity")>
<MvELSE>
<MvIF EXPR = "{parm_func EQ ''}"> <MvCOMMENT> 1st time in </MvCOMMENT>
<MvEVAL EXPR = ShowWelcome()>
<MvELSE>
<MvEVAL EXPR = ShowProvideIdentity()>
</MvIF>
</MvIF>
</MvIF>
<MvASSIGN NAME = "configfile" VALUE = "{dirprefix $ '/' $ config_file}">
<MvIF EXPR = "{NOT fexists(configfile)}">
<MvEVAL EXPR = SetupLinks()> <MvCOMMENT> No title yet, but don't care. </MvCOMMENT>
<MvEVAL EXPR = AssignDefaultSettings()>
<MvEVAL EXPR = ExportSettings()>
<MvIF EXPR = "{p_user NE ''}"> <MvCOMMENT> If we just came from ShowProvideIdentity </MvCOMMENT>
<MvEVAL EXPR = ChooseAdminFunction(p_user,p_pass1)>
<MvELSE>
<MvEVAL EXPR = ChooseAdminFunction(parm_user,parm_pass)>
</MvIF>
<MvEXIT>
</MvIF>
<MvASSIGN NAME = "unwelcomelist" VALUE = "{dirprefix $ '/' $ unwelcome_list}">
<MvASSIGN NAME = "unwelcomelist2" VALUE = "{dirprefix $ '/' $ unwelcome_list2}">
<MvEVAL EXPR = GetSettings()>
<MvEVAL EXPR = DateAndTime()>
<MvEVAL EXPR = SetupLinks()>
</MvFUNCTION>
<MvFUNCTION NAME = "GetFormInput">
<MvASSIGN NAME = "lenny" VALUE = "{len(QUERY_STRING)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{1}">
<MvWHILE EXPR = "{curr_pos LE lenny}">
<MvASSIGN NAME = "a1" VALUE = "{'=' CIN substring(QUERY_STRING,curr_pos,lenny-curr_pos+1)}">
<MvIF EXPR = "{a1 GT 0}">
<MvASSIGN NAME = "a2" VALUE = "{'+' CIN substring(QUERY_STRING,curr_pos+a1,lenny-curr_pos-a1+1)}">
<MvIF EXPR = "{a2 EQ 0}">
<MvASSIGN NAME = "a2" VALUE = "{lenny-curr_pos-a1+2}">
</MvIF>
<MvASSIGN NAME = "x" VALUE = "{substring(QUERY_STRING,curr_pos,a1-1)}">
<MvASSIGN NAME = "&[x]"
VALUE = "{substring(QUERY_STRING,curr_pos+a1,a2-1)}">
</MvIF>
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1 + a2}">
</MvWHILE>
</MvFUNCTION>
<MvFUNCTION NAME = "FindMboardName">
<MvCOMMENT>
The name of the msgboard is what immediately precedes ".mv" in the
document URL, but parsing depends on whether the URL is an NSAPI or CGI URL.
The user must give each message board Miva doc a unique name, e.g. msgboard1.mv,
msgboard2.mv, etc. Each message board will have a separate subdirectory
under mivadata. The subdirectory will be named with the unique message board
name, e.g. msgboard1. The path to the database files, etc. will be based on the
message board name, e.g. mivadata/msgboard1/msgboard.dbf.
If CGI, URL looks like either
.../cgi-bin/miva?xxx.mv+
or
.../cgi-bin/miva?zzz/xxx.mv+
where zzz is the path within the Documents directory (so could have >1 slash).
If NSAPI, URL looks like either
...xxx.mv?
or
...zzz/xxx.mv?
where zzz is the path within the Documents directory (so could have >1 slash).
</MvCOMMENT>
<MvASSIGN NAME = "a1" VALUE = "{'.mv' CIN documenturl}">
<MvASSIGN NAME = "curr_pos" VALUE = "{a1 - 1}">
<MvIF EXPR = "{substring(documenturl,curr_pos+4,1) EQ qmark}">
<MvWHILE EXPR = "{substring(documenturl,curr_pos,1) NE slash}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos - 1}">
</MvWHILE>
<MvELSE>
<MvWHILE EXPR = "{(substring(documenturl,curr_pos,1) NE slash)
AND (substring(documenturl,curr_pos,1) NE qmark)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos - 1}">
</MvWHILE>
</MvIF>
<MvASSIGN NAME = "dirprefix" VALUE = "{substring(documenturl,curr_pos+1,a1-curr_pos-1)}">
<MvASSIGN NAME = "database" VALUE = "{'&[dirprefix];/msgboard.dbf'}"><MvCOMMENT> path name </MvCOMMENT>
<MvASSIGN NAME = "index_file1" VALUE = "{'&[dirprefix];/msgboard1.mvx'}"><MvCOMMENT> path name </MvCOMMENT>
<MvASSIGN NAME = "index_file2" VALUE = "{'&[dirprefix];/msgboard2.mvx'}"><MvCOMMENT> path name </MvCOMMENT>
<MvASSIGN NAME = "index_file3" VALUE = "{'&[dirprefix];/msgboard3.mvx'}"><MvCOMMENT> path name </MvCOMMENT>
<MvASSIGN NAME = "dbname" VALUE = "{'&[dirprefix];'}"><MvCOMMENT> short name </MvCOMMENT>
</MvFUNCTION>
<MvFUNCTION NAME = "SetupLinks">
<MvASSIGN NAME = "link_main_short"
VALUE = "{'[ <a href="&[documenturl];parm_func=showmain+parm_starting_root=1">&[title];</a> ]'}">
<MvASSIGN NAME = "link_main_short_dflt"
VALUE = "{'[ <a href="&[documenturl];parm_func=showmain+parm_starting_root=1">Message Board</a> ]'}">
<MvASSIGN NAME = "link_post_short"
VALUE = "{'[ <a href="&[documenturl];parm_func=showpostform">Post Message</a> ]'}">
<MvASSIGN NAME = "link_search_short"
VALUE = "{'[ <a href="&[documenturl];parm_func=showsearch+parm_starting_root=1">Search</a> ]'}">
<MvASSIGN NAME = "link_faq_short"
VALUE = "{'[ <a href="&[documenturl];parm_func=showfaq">FAQ</a> ]'}">
</MvFUNCTION>
<MvFUNCTION NAME = "GetSettings">
<MvCOMMENT>
Importing a single big field and parsing it into data fields takes
one second. Importing the fields individually takes several seconds.
</MvCOMMENT>
<MvIMPORT FILE = "&[configfile];" DELIMITER = "%%%%%%" FIELDS = "glob">
</MvIMPORT>
<MvASSIGN NAME = "curr_pos" VALUE = "{1}">
<MvEVAL EXPR = ExtractSetting("roots_to_view")>
<MvEVAL EXPR = ExtractSetting("entries_thread")>
<MvEVAL EXPR = ExtractSetting("entries_msgnum")>
<MvEVAL EXPR = ExtractSetting("entries_date")>
<MvEVAL EXPR = ExtractSetting("entries_author")>
<MvEVAL EXPR = ExtractSetting("time_zone")>
<MvEVAL EXPR = ExtractSetting("show_faq")>
<MvEVAL EXPR = ExtractSetting("quote_text")>
<MvEVAL EXPR = ExtractSetting("use_time")>
<MvEVAL EXPR = ExtractSetting("show_preview")>
<MvEVAL EXPR = ExtractSetting("allow_gif")>
<MvEVAL EXPR = ExtractSetting("allow_html_body")>
<MvEVAL EXPR = ExtractSetting("allow_html_subj")>
<MvEVAL EXPR = ExtractSetting("subject_line")>
<MvEVAL EXPR = ExtractSetting("title")>
<MvEVAL EXPR = ExtractSetting("header")>
<MvEVAL EXPR = ExtractSetting("footer")>
<MvASSIGN NAME = "linkline" VALUE = "{substring(glob,curr_pos,len(glob)-curr_pos+1)}">
<MvIF EXPR = "{roots_to_view EQ '0'}">
<MvASSIGN NAME = "roots_to_view" VALUE = "{'1'}">
</MvIF>
<MvASSIGN NAME = "admin_lit" VALUE = "{' Admin'}">
<MvASSIGN NAME = "title2" VALUE = "{title $ admin_lit}">
</MvFUNCTION>
<MvFUNCTION NAME = "ExtractSetting" PARAMETERS = "glob_field">
<MvASSIGN NAME = "a1" VALUE = "{delim CIN substring(glob,curr_pos,len(glob)-curr_pos+1)}">
<MvASSIGN NAME = "&[glob_field];" VALUE = "{substring(glob,curr_pos,a1-1)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1 - 1 + len(delim)}">
</MvFUNCTION>
<MvFUNCTION NAME = "DateAndTime">
<MvCOMMENT> Figure out the date and time of the entry, accounting for the different time zones.
The "time_zone" field is set in admin.</MvCOMMENT>
<MvIF EXPR = "{tm_isdst}"> <MvCOMMENT> If daylight savings </MvCOMMENT>
<MvASSIGN NAME = "time_t" VALUE = "{time_t + 3600}"> <MvCOMMENT> add one hour </MvCOMMENT>
</MvIF>
<MvASSIGN NAME = "month" VALUE = "{time_t_month(time_t,time_zone)}">
<MvASSIGN NAME = "day" VALUE = "{time_t_dayofmonth(time_t,time_zone)}">
<MvASSIGN NAME = "year" VALUE = "{time_t_year(time_t,time_zone)}">
<MvASSIGN NAME = "hour" VALUE = "{time_t_hour(time_t,time_zone)}">
<MvASSIGN NAME = "minute" VALUE = "{time_t_min(time_t,time_zone)}">
<MvASSIGN NAME = "second" VALUE = "{time_t_sec(time_t,time_zone)}">
<MvEVAL EXPR = DateAndTimeMonth()>
<MvIF EXPR = "{minute LT '10'}">
<MvASSIGN NAME = "minute" VALUE = "{'0' $ minute}">
</MvIF>
<MvIF EXPR = "{second LT '10'}">
<MvASSIGN NAME = "second" VALUE = "{'0' $ second}">
</MvIF>
<MvASSIGN NAME = "time" VALUE = "{hour $ ':' $ minute $ ':' $ second}">
<MvASSIGN NAME = "date" VALUE = "{month $ '/' $ day $ '/' $ year}">
<MvCOMMENT> Get rid of any newlines </MvCOMMENT>
<MvASSIGN NAME = "date" VALUE = "{glosub(date,newline,'')}">
<MvASSIGN NAME = "long_date" VALUE = "{monthname $ ' ' $ day $ ', ' $ year $ ' at ' $ hour $ ':' $ minute $ ':' $ second}">
</MvFUNCTION>
<MvFUNCTION NAME = "DateAndTimeMonth">
<MvIF EXPR = "{month EQ '1'}"> <MvASSIGN NAME = "monthname" VALUE = "{'January'}">
<MvELSE>
<MvIF EXPR = "{month EQ '2'}"> <MvASSIGN NAME = "monthname" VALUE = "{'February'}">
<MvELSE>
<MvIF EXPR = "{month EQ '3'}"> <MvASSIGN NAME = "monthname" VALUE = "{'March'}">
<MvELSE>
<MvIF EXPR = "{month EQ '4'}"> <MvASSIGN NAME = "monthname" VALUE = "{'April'}">
<MvELSE>
<MvIF EXPR = "{month EQ '5'}"> <MvASSIGN NAME = "monthname" VALUE = "{'May'}">
<MvELSE>
<MvIF EXPR = "{month EQ '6'}"> <MvASSIGN NAME = "monthname" VALUE = "{'June'}">
<MvELSE>
<MvIF EXPR = "{month EQ '7'}"> <MvASSIGN NAME = "monthname" VALUE = "{'July'}">
<MvELSE>
<MvIF EXPR = "{month EQ '8'}"> <MvASSIGN NAME = "monthname" VALUE = "{'August'}">
<MvELSE>
<MvIF EXPR = "{month EQ '9'}"> <MvASSIGN NAME = "monthname" VALUE = "{'September'}">
<MvELSE>
<MvIF EXPR = "{month EQ '10'}"> <MvASSIGN NAME = "monthname" VALUE = "{'October'}">
<MvELSE>
<MvIF EXPR = "{month EQ '11'}"> <MvASSIGN NAME = "monthname" VALUE = "{'November'}">
<MvELSE> <MvASSIGN NAME = "monthname" VALUE = "{'December'}">
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "RemoveYumlaut" PARAMETERS="infield">
<MvCOMMENT> This subroutine should be physically before any calls to it.
That's because of the following scenario.
Yumlaut, which is ascii 255, means end of file to Miva. If it is
encountered at any point by a Miva script, the script terminates
immediately, whether that is the desired action or not.
Suppose there *is* a yumlaut in an input field. Suppose also that
we think we're prepared for this and we call this Remove function
before we try to use the field for anything. If the code for the
function is physically after the call and the usage of the input
field, the function is not found and the script crashes.
Because the function is not found, the yumlaut in the input field
is not removed. The usage of the field thus still has the yumlaut.
This causes immediate termination of the script at that point.
This in turn causes the code following that point to not be seen,
including this Remove function. Thus the function is not found.
The script crashes because it has a function that doesn't have a
</MvFUNCTION>. This is due to the false end of file, which causes
the function within which the input field is used to end right away,
before any more code is seen, including its </MvFUNCTION>.
</MvCOMMENT>
<MvASSIGN NAME = "curr_pos" VALUE = "{1}">
<MvASSIGN NAME = "lenny" VALUE = "{len(infield)}">
<MvASSIGN NAME = "outfield" VALUE = "{''}">
<MvWHILE EXPR = "{curr_pos LE lenny}">
<MvASSIGN NAME = "a1" VALUE = "{asciichar(255) CIN
substring(infield,curr_pos,lenny-curr_pos+1)}">
<MvIF EXPR = "{a1 GT 0}">
<MvASSIGN NAME = "outfield" VALUE = "{outfield $
substring(infield,curr_pos,a1 - 1)}">
<MvELSE>
<MvASSIGN NAME = "outfield" VALUE = "{outfield $
substring(infield,curr_pos,lenny-curr_pos+1)}">
<MvWHILESTOP>
</MvIF>
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1}">
</MvWHILE>
<MvFUNCRETURN VALUE = "{outfield}"></MvFUNCRETURN>
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMain">
<MvIF EXPR = "{dosearch NE ''}">
<MvASSIGN NAME = "search_string" VALUE = "{RemoveYumlaut(search_string)}">
<MvEVAL EXPR = CheckSearchCriteria()>
<MvIF EXPR = "{search_input_error GE '1'}">
<MvEVAL EXPR = ShowSearchForm()>
<MvFUNCRETURN></MvFUNCRETURN>
</MvIF>
</MvIF>
<MvEVAL EXPR = FindStart(roots_to_view,parm_starting_root)>
<MvEVAL EXPR = MainPageHeader()>
<MvEVAL EXPR = MainPageBody()>
<MvEVAL EXPR = MainPageFooter()> <MvCOMMENT> Doesn't include post form </MvCOMMENT>
<MvEXIT>
</MvFUNCTION>
<MvFUNCTION NAME = "CheckSearchCriteria">
<MvIF EXPR = "{(use_searchstring EQ '' AND use_startdate EQ '' AND use_enddate EQ '')}">
<MvASSIGN NAME = "search_input_error" VALUE = "{'1'}">
<MvELSE>
<MvIF EXPR = "{(use_searchstring NE '' AND search_string EQ '')
OR (use_startdate NE '' AND start_month EQ '' AND start_day EQ '' AND start_year EQ '')
OR (use_enddate NE '' AND end_month EQ '' AND end_day EQ '' AND end_year EQ '')}">
<MvASSIGN NAME = "search_input_error" VALUE = "{'2'}">
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "FindStart" PARAMETERS = "rootstoview,startingroot">
<MvIF EXPR = "{db_just_created EQ '1'}"> <MvCOMMENT> 1st record, super-root not visible yet. </MvCOMMENT>
<MvASSIGN NAME = "is_last_page" VALUE = "{'1'}">
<MvASSIGN NAME = "starting_recno" VALUE = "{'0'}">
<MvASSIGN NAME = "last_rootnum" VALUE = "{'0'}">
<MvFUNCRETURN></MvFUNCRETURN>
</MvIF>
<MvASSIGN NAME = "starting_root" VALUE = "{startingroot}">
<MvASSIGN NAME = "starting_root_save" VALUE = "{startingroot}">
<MvIF EXPR = "{starting_root EQ ''}">
<MvASSIGN NAME = "starting_root" VALUE = "{'1'}">
</MvIF>
<MvCOMMENT> Find main posting to start page with. </MvCOMMENT>
<MvEVAL EXPR = FindDirection()>
<MvIF EXPR = "{dosearch EQ '' AND nextmatch EQ '' AND prevmatch EQ ''}">
<MvEVAL EXPR = FindStartingRoot(rootstoview)>
<MvEVAL EXPR = FindStartingRec(rootstoview)>
<MvELSE>
<MvEVAL EXPR = FindStartingRootSearch(rootstoview)>
<MvEVAL EXPR = FindStartingRecSearch()>
</MvIF>
<MvEVAL EXPR = FindScrolling(rootstoview)>
</MvFUNCTION>
<MvFUNCTION NAME = "FindDirection">
<MvIF EXPR = "{parm_starting_root NE ''}">
<MvIF EXPR = "{backintime NE '' OR nextmatch NE ''}">
<MvASSIGN NAME = "direction" VALUE = "{'back'}">
<MvELSE>
<MvIF EXPR = "{forwardintime NE '' OR prevmatch NE ''}">
<MvASSIGN NAME = "direction" VALUE = "{'forward'}">
</MvIF>
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "FindStartingRoot" PARAMETERS = "rootstoview">
<MvIF EXPR = "{direction EQ 'back'}">
<MvIF EXPR = "{form_num_displayed EQ ''}">
<MvASSIGN NAME = "starting_root" VALUE = "{starting_root + rootstoview}">
<MvELSE>
<MvASSIGN NAME = "starting_root" VALUE = "{starting_root + form_num_displayed}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{parm_starting_root EQ ''}"> <MvCOMMENT> First time in, no direction specified. </MvCOMMENT>
<MvASSIGN NAME = "starting_root" VALUE = "{'1'}">
<MvELSE>
<MvIF EXPR = "{direction EQ 'forward'}">
<MvIF EXPR = "{starting_root GT rootstoview}">
<MvASSIGN NAME = "starting_root" VALUE = "{starting_root - rootstoview}">
<MvELSE>
<MvASSIGN NAME = "partial_page" VALUE = "{'1'}">
<MvASSIGN NAME = "starting_root" VALUE = "{'1'}">
</MvIF>
</MvIF>
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "FindStartingRootSearch" PARAMETERS = "rootstoview">
<MvCOMMENT> First time in, start at top of database, else at current pagetop. </MvCOMMENT>
<MvIF EXPR = "{parm_starting_root EQ '' OR dosearch NE ''}">
<MvASSIGN NAME = "starting_root" VALUE = "{'0'}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "FindStartingRec" PARAMETERS = "rootstoview">
<MvCOMMENT> Find record number of root that will start page. </MvCOMMENT>
<MvEVAL EXPR = GetSuperRoot()>
<MvSKIP ROWS = starting_root - 1>
<MvASSIGN NAME = "starting_recno" VALUE = "{&[dbname];.d.recno}">
<MvASSIGN NAME = "starting_seqnum" VALUE = "{dbseqnum}">
<MvCLOSE NAME = "&[dbname];">
</MvFUNCTION>
<MvFUNCTION NAME = "GetSuperRoot">
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file1];,&[index_file2];,&[index_file3];">
<MvGO ROW = 1>
<MvASSIGN NAME = "last_rootnum" VALUE = "{dbparentno}">
<MvIF EXPR = "{last_rootnum GT 0}">
<MvASSIGN NAME = "roots_exist" VALUE = "{'1'}">
<MvELSE>
<MvASSIGN NAME = "roots_exist" VALUE = "{'0'}">
</MvIF>
<MvFILTER NAME = "&[dbname];"
FILTER = "{dbparentno EQ super_root AND NOT &[dbname];.d.deleted}">
</MvFUNCTION>
<MvFUNCTION NAME = "FindScrolling" PARAMETERS = "rootstoview">
<MvCOMMENT> Determine which scrolling options make sense, if any. </MvCOMMENT>
<MvIF EXPR = "{direction EQ 'forward' AND partial_page NE ''}">
<MvCOMMENT> Starting value of roots_displayed_count > 0 since displaying partial page. </MvCOMMENT>
<MvASSIGN NAME = "roots_displayed_count" VALUE = "{rootstoview - starting_root_save + 1}">
<MvASSIGN NAME = "num_displayed" VALUE = "{starting_root_save - 1}">
<MvELSE>
<MvASSIGN NAME = "roots_displayed_count" VALUE = "{'0'}">
<MvIF EXPR = "{last_rootnum - starting_root GE rootstoview}">
<MvASSIGN NAME = "num_displayed" VALUE = "{rootstoview}">
<MvELSE>
<MvASSIGN NAME = "num_displayed" VALUE = "{last_rootnum - starting_root + 1}">
</MvIF>
</MvIF>
<MvASSIGN NAME = "ending_root" VALUE = "{starting_root + num_displayed - 1}">
<MvIF EXPR = "{roots_exist EQ '1'}">
<MvIF EXPR = "{ending_root EQ last_rootnum}">
<MvASSIGN NAME = "is_last_page" VALUE = "{'1'}">
<MvELSE>
<MvASSIGN NAME = "is_last_page" VALUE = "{'0'}">
</MvIF>
<MvELSE>
<MvCOMMENT> Almost empty database </MvCOMMENT>
<MvASSIGN NAME = "starting_recno" VALUE = "{'1'}">
<MvASSIGN NAME = "is_last_page" VALUE = "{'1'}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "FindStartingRecSearch">
<MvCOMMENT> Find record number of root that will start page, whether 1st search or
search for next match. For 1st search, starting_root s/b zero.
Get root count from super_root's parent field.
</MvCOMMENT>
<MvEVAL EXPR = GetSuperRoot()>
<MvASSIGN NAME = "root_count" VALUE = "{'0'}">
<MvASSIGN NAME = "match_found" VALUE = "{'0'}">
<MvIF EXPR = "{prevmatch EQ ''}">
<MvWHILE EXPR = "{NOT &[dbname];.d.eof}">
<MvIF EXPR = "{match_found EQ '0'}">
<MvASSIGN NAME = "root_count" VALUE = "{root_count + 1}">
<MvIF EXPR = "{root_count GT starting_root}">
<MvIF EXPR = "{use_startdate NE '' OR use_enddate NE ''}">
<MvEVAL EXPR = ExtractDateFields()>
</MvIF>
<MvEVAL EXPR = SeeIfMatch()>
<MvIF EXPR = "{match_found EQ '1'}">
<MvASSIGN NAME = "starting_recno" VALUE = "{&[dbname];.d.recno}">
<MvASSIGN NAME = "starting_root" VALUE = "{root_count}">
<MvWHILESTOP></MvWHILESTOP>
</MvIF>
</MvIF>
</MvIF>
<MvSKIP>
</MvWHILE>
<MvELSE>
<MvEVAL EXPR = DoPreviousMatch()>
</MvIF>
<MvIF EXPR = "{match_found EQ '0'}">
<MvIF EXPR = "{starting_recno_save NE ''}">
<MvASSIGN NAME = "starting_recno" VALUE = "{starting_recno_save}">
<MvELSE>
<MvASSIGN NAME = "starting_recno" VALUE = "{'1'}">
</MvIF>
</MvIF>
<MvCLOSE NAME = "&[dbname];">
</MvFUNCTION>
<MvFUNCTION NAME = "DoPreviousMatch">
<MvCOMMENT> First go forward to find starting point. Start backward from
there and look for match. </MvCOMMENT>
<MvWHILE EXPR = "{NOT &[dbname];.d.eof}">
<MvASSIGN NAME = "root_count" VALUE = "{root_count + 1}">
<MvIF EXPR = "{root_count EQ starting_root}">
<MvASSIGN NAME = "starting_recno" VALUE = "{&[dbname];.d.recno}">
<MvWHILESTOP></MvWHILESTOP>
</MvIF>
<MvSKIP>
</MvWHILE>
<MvSKIP ROWS = -1> <MvCOMMENT> Back up 1 from curr rec so don't match on it again.</MvCOMMENT>
<MvWHILE EXPR = "{NOT &[dbname];.d.eof}">
<MvIF EXPR = "{match_found EQ '0'}">
<MvASSIGN NAME = "root_count" VALUE = "{root_count - 1}">
<MvIF EXPR = "{use_startdate NE '' OR use_enddate NE ''}">
<MvEVAL EXPR = ExtractDateFields()>
</MvIF>
<MvEVAL EXPR = SeeIfMatch()>
<MvIF EXPR = "{match_found EQ '1'}">
<MvASSIGN NAME = "starting_recno" VALUE = "{&[dbname];.d.recno}">
<MvASSIGN NAME = "starting_root" VALUE = "{root_count}">
<MvWHILESTOP></MvWHILESTOP>
</MvIF>
</MvIF>
<MvCOMMENT> If we're at beginning of index, i.e. recno 1, stop. </MvCOMMENT>
<MvSKIP ROWS = -1>
</MvWHILE>
</MvFUNCTION>
<MvFUNCTION NAME = "ExtractDateFields">
<MvASSIGN NAME = "a1" VALUE = "{'/' CIN dbdate}">
<MvIF EXPR = "{a1 EQ '2'}">
<MvASSIGN NAME = "db_month" VALUE = "{'0' $ substring(dbdate,1,1)}">
<MvELSE>
<MvASSIGN NAME = "db_month" VALUE = "{substring(dbdate,1,2)}">
</MvIF>
<MvASSIGN NAME = "a2" VALUE = "{'/' CIN substring(dbdate,a1+1,10-a1)}">
<MvIF EXPR = "{a2 EQ '2'}">
<MvASSIGN NAME = "db_day" VALUE = "{'0' $ substring(dbdate,a1+1,1)}">
<MvELSE>
<MvASSIGN NAME = "db_day" VALUE = "{substring(dbdate,a1+1,2)}">
</MvIF>
<MvASSIGN NAME = "db_year" VALUE = "{substring(dbdate,a1+a2+1,4)}">
</MvFUNCTION>
<MvFUNCTION NAME = "SeeIfMatch">
<MvIF EXPR = "{ (use_searchstring EQ '' OR search_string CIN dbsubject)
AND (use_startdate EQ ''
OR start_year LT db_year
OR (start_year EQ db_year
AND (start_month LT db_month)
OR (start_month EQ db_month
AND start_day LE db_day)))
AND (use_enddate EQ ''
OR db_year LT end_year
OR (db_year EQ end_year
AND (db_month LT end_month)
OR (db_month EQ end_month
AND db_day LE end_day)))
}">
<MvASSIGN NAME = "match_found" VALUE = "{'1'}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "MainPageHeader">
&[ON];&[header];<h1 align="center">&[title];</h1>&[crlf];&[OFF];
<MvEVAL EXPR = LinkLine(parm_admin_flag,parm_user,parm_pass)>
<MvIF EXPR = "{dosearch NE '' OR nextmatch NE '' OR prevmatch NE ''}">
<MvIF EXPR = "{match_found EQ '0'}">
&[ON];<center><font color="#ff0000" size = "+1">Match not found.</font></center><br>&[crlf];&[OFF];
<MvELSE>
&[ON];<center><font color="#ff0000" size = "+1">Match found:</font></center><br>&[crlf];&[OFF];
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "MainPageBody">
<MvCOMMENT>
Each posting which is not a followup is the root of a tree of postings. To tie all
of these roots together, we use an artificial posting called the super-root. It
forms the root of a tree or hierarchy. Its children are the original roots. Their
children, if any, are their followups. To display the overall hierarchy of postings
in most-recent-first sequence, we traverse the overall tree recursively. To do so, we
start at the super-root and print its "followups" (postings which are *not* followups).
While processing a posting, we print *its* followups, etc., recursively traversing
the hierarchy of postings and followups in most-recent-first sequence.
Additionally, we don't just print the whole database. We only print a limited number
of postings at a time. Other postings can be viewed by "scrolling" forward or
backward in time. A practical way to limit the amount of material to display
at a time is to pick a fixed number of roots to display postings for. This
prevents us from cutting off a set of related postings under one root. Rather,
we finish the set of postings related to a root. So, the exact number of
postings per page will fluctuate, though the number of *roots* is fixed. The
scrolling amount can be set in the administration function of the message board.
The amount is called "roots_to_view".
</MvCOMMENT>
<MvASSIGN NAME = "traverse_function" VALUE = "{'AddLine'}">
<MvASSIGN NAME = "starting_nestlvl" VALUE = "{'0'}">
<MvASSIGN NAME = "roots_per_page" VALUE = "{roots_to_view}">
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file1];,&[index_file2];,&[index_file3];">
<MvFILTER NAME = "&[dbname];" FILTER = "{NOT &[dbname];.d.deleted}">
&[ON]<BLOCKQUOTE>&[OFF]
<MvEVAL EXPR = TraverseTree(parm_admin_flag,parm_user,parm_pass)> <MvCOMMENT> Start recursion </MvCOMMENT>
<MvEVAL EXPR = Unindent()>
<MvCLOSE NAME = "&[dbname];">
</MvFUNCTION>
<MvFUNCTION NAME = "TraverseTree" PARAMETERS = "admin,uname,pass">
<MvCOMMENT>
The following example should help one visualize this processing. Suppose the
messsage board has 10 postings so far, numbered 1 to 10, the oldest being 1.
There would 11 records in the database, the eleventh being the super-root.
Suppose the posting structure should print out like this:
4
10
5
8
9
6
7
3
1
2
The records are in exactly this hierarchical and most-recent-first sequence
within the database because the index used here is decreasing sequence number.
Sequence number for a posting is midway between the preceding and following
postings' sequence numbers. This speeds up displaying the list as it looks above.
</MvCOMMENT>
<MvCOMMENT> Start at beginning of database, or at starting point if scrolling,
according to index 1 (sequence number). </MvCOMMENT>
<MvIF EXPR = "{dosearch NE '' AND match_found EQ '0'}">
<MvGO ROW = "top">
<MvSKIP>
<MvELSE>
<MvGO ROW = starting_recno>
<MvIF EXPR = "{dbselfnum EQ super_root}">
<MvSKIP>
</MvIF>
</MvIF>
<MvIF EXPR = "{traverse_reason EQ 'followups'}">
<MvSKIP>
<MvIF EXPR = "{dbnestlvl LE starting_nestlvl}">
<MvFUNCRETURN></MvFUNCRETURN>
</MvIF>
</MvIF>
<MvASSIGN NAME = "save_nestlvl" VALUE = "{starting_nestlvl}">
<MvIF EXPR = "{currself EQ (1 + big_bump)}"> <MvCOMMENT> 1st record, super-root not visible yet. </MvCOMMENT>
<MvEVAL EXPR = TravTreeFirstrec(admin,uname,pass)>
<MvELSE>
<MvWHILE EXPR = "{NOT &[dbname];.d.eof
AND (roots_displayed_count LT roots_per_page
OR (roots_displayed_count EQ roots_per_page
AND dbparentno NE super_root))}">
<MvEVAL EXPR = &[traverse_function];(admin,uname,pass)>
<MvIF EXPR = "{dbparentno EQ super_root}">
<MvASSIGN NAME = "roots_displayed_count" VALUE = "{roots_displayed_count + 1}">
</MvIF>
<MvSKIP>
<MvIF EXPR = "{dbnestlvl LE starting_nestlvl}">
<MvWHILESTOP></MvWHILESTOP>
</MvIF>
</MvWHILE>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "TravTreeFirstrec" PARAMETERS = "admin,uname,pass">
<MvASSIGN NAME = "dbselfnum" VALUE = "{currself}">
<MvASSIGN NAME = "dbnumdesc" VALUE = "{'0'}">
<MvASSIGN NAME = "dbdate" VALUE = "{date}">
<MvASSIGN NAME = "dbtime" VALUE = "{time}">
<MvASSIGN NAME = "dbname1" VALUE = "{name}">
<MvASSIGN NAME = "dbsubject" VALUE = "{subject}">
<MvEVAL EXPR = &[traverse_function];(admin,uname,pass)>
</MvFUNCTION>
<MvFUNCTION NAME = "AddLine" PARAMETERS = "admin,uname,pass">
<MvIF EXPR = "{use_time EQ 'on'}">
<MvASSIGN NAME = "print_date" VALUE = "{dbtime $ ' ' $ dbdate}">
<MvELSE>
<MvASSIGN NAME = "print_date" VALUE = "{dbdate}">
</MvIF>
<MvASSIGN NAME = "indent_string" VALUE = "{''}">
<MvASSIGN NAME = "indent_count" VALUE = "{dbnestlvl - save_nestlvl}">
<MvIF EXPR = "{indent_count GT 0}">
<MvASSIGN NAME = "indent_string" VALUE = "{'<UL>'}">
<MvELSE>
<MvIF EXPR = "{indent_count LT 0}">
<MvWHILE EXPR = "{indent_count LT 0}">
<MvASSIGN NAME = "indent_string" VALUE = "{indent_string $ '</UL>'}">
<MvASSIGN NAME = "indent_count" VALUE = "{indent_count + 1}">
</MvWHILE>
<MvELSE>
<MvASSIGN NAME = "indent_string" VALUE = "{''}">
</MvIF>
</MvIF>
<MvASSIGN NAME = "save_nestlvl" VALUE = "{dbnestlvl}">
&[ON]&[indent_string];<LI>&[OFF]
<MvIF EXPR = "{admin EQ 'y'}">
&[ON];<a href = "&[documenturl];parm_func=showmsg+parm_msgnum=&[dbselfnum];+parm_admin_flag=&[admin];+parm_user=&[uname];+parm_pass=&[pass];">&[OFF];
<MvELSE>
&[ON];<a href = "&[documenturl];parm_func=showmsg+parm_msgnum=&[dbselfnum];">&[OFF];
</MvIF>
&[ON];&[dbsubject];</a> - <b>&[dbname1]; &[crlf];&[OFF];
&[ON];</b><i>&[print_date];</i> (&[dbnumdesc];)&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "Unindent">
<MvASSIGN NAME = "unindent_string" VALUE = "{''}">
<MvWHILE EXPR = "{save_nestlvl GT 0}">
<MvASSIGN NAME = "unindent_string" VALUE = "{unindent_string $ '</UL>'}">
<MvASSIGN NAME = "save_nestlvl" VALUE = "{save_nestlvl - 1}">
</MvWHILE>
&[ON]&[unindent_string];</BLOCKQUOTE>&[OFF]
</MvFUNCTION>
<MvFUNCTION NAME = "MainPageFooter">
<MvCOMMENT> If first page, no previous button. If last page, no next button.
Except when we're first coming in to message board, and it's page 1,
starting root in url reflects page we're coming *from*, not page we're on.
</MvCOMMENT>
<MvIF EXPR = "{starting_root EQ '0'}">
<MvASSIGN NAME = "starting_root" VALUE = "{'1'}">
</MvIF>
&[ON];<CENTER>&[crlf];&[OFF];
<MvIF EXPR = "{parm_admin_flag EQ 'y'}">
&[ON];<form method="post" action="&[documenturl];parm_func=showmain+parm_starting_root=&[starting_root];+parm_admin_flag=y+parm_user=&[parm_user];+parm_pass=&[parm_pass];">&[crlf];&[OFF];
<MvELSE>
&[ON];<form method="post" action="&[documenturl];parm_func=showmain+parm_starting_root=&[starting_root];">&[crlf];&[OFF];
</MvIF>
&[ON];<input type = "hidden" name = "form_num_displayed" value = &[num_displayed];></INPUT>&[crlf];&[OFF];
&[ON];<input type = "hidden" name = "starting_recno_save" value = &[starting_recno];></INPUT>&[crlf];&[OFF];
<MvIF EXPR = "{(is_last_page NE '1') AND (starting_root LT last_rootnum)}">
&[ON];<input name="backintime" type="submit" value="Next Page"></INPUT>&[crlf];&[OFF];
</MvIF>
<MvIF EXPR = "{starting_root GT '1'}">
&[ON];<input name="forwardintime" type="submit" value="Previous Page"></INPUT>&[crlf];&[OFF];
</MvIF>
<MvEVAL EXPR = SearchFields()>
&[ON];</form></CENTER>&[OFF];
<MvEVAL EXPR = LinkLine(parm_admin_flag,parm_user,parm_pass)>
&[ON];&[banner_ad];&[footer];&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "SearchFields">
<MvIF EXPR = "{(dosearch NE '' AND match_found EQ '1') OR search_started NE ''}">
<MvIF EXPR = "{nextmatch NE '' AND (match_found EQ '0' OR starting_root GE last_rootnum)}">
<MvELSE>
&[ON];<input name="nextmatch" type="submit" value="Next Match"></INPUT>&[crlf];&[OFF];
<MvASSIGN NAME = "button_out" VALUE = "{'1'}">
</MvIF>
<MvIF EXPR = "{(dosearch NE '') OR (prevmatch NE '' AND match_found EQ '0') OR (starting_root LE '1')}">
<MvELSE>
&[ON];<input name="prevmatch" type="submit" value="Previous Match"></INPUT>&[crlf];&[OFF];
<MvASSIGN NAME = "button_out" VALUE = "{'1'}">
</MvIF>
<MvIF EXPR = "{button_out EQ '1'}">
&[ON];<input name="search_started" type="hidden" value="'1'"></INPUT>&[crlf];&[OFF];
&[ON];<input name="use_searchstring" type="hidden" value="&[use_searchstring];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="use_startdate" type="hidden" value="&[use_startdate];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="use_enddate" type="hidden" value="&[use_enddate];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="search_string" type="hidden" value="&[search_string];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="start_month" type="hidden" value="&[start_month];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="start_day" type="hidden" value="&[start_day];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="start_year" type="hidden" value="&[start_year];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="end_month" type="hidden" value="&[end_month];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="end_day" type="hidden" value="&[end_day];"></INPUT>&[crlf];&[OFF];
&[ON];<input name="end_year" type="hidden" value="&[end_year];"></INPUT>&[crlf];&[OFF];
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "LinkLine" PARAMETERS = "admin,uname,pass">
<MvEVAL EXPR = GeneralLinkLineParms(admin,uname,pass)>
&[ON];&[general_data_area];&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "GeneralLinkLineParms" PARAMETERS = "admin,uname,pass">
<MvEVAL EXPR = GenLinkLineIntro(admin,uname,pass)>
<MvEVAL EXPR = GenLinkLineParmsFaqShort()>
<MvEVAL EXPR = GenLinkLineParmsShowpostform(admin)>
<MvEVAL EXPR = GenLinkLineParmsShowmain(admin)>
<MvEVAL EXPR = GenLinkLineParmsShowsearch(admin)>
<MvEVAL EXPR = GenLinkLineParmsShowFaq(admin)>
<MvEVAL EXPR = GenLinkLineParmsFinishLinks()>
<MvEVAL EXPR = GenLinkLineParmsWrapup(admin)>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineIntro" PARAMETERS = "admin,uname,pass">
<MvASSIGN NAME = "data_area" VALUE = "{linkline}">
<MvASSIGN NAME = "link_admin"
VALUE = "{'[ <a href="&[documenturl];parm_func=admin+parm_admin_func=null'
$ '+parm_user=&[uname];+parm_pass=&[pass];'
$ '">&[title2];</a> ]'}">
<MvASSIGN NAME = "extra_parms" VALUE = "{'+parm_admin_flag=&[admin];+parm_user=&[uname];+parm_pass=&[pass];'}">
<MvASSIGN NAME = "curr_pos" VALUE = "{'1'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{''}">
<MvASSIGN NAME = "a1" VALUE = "{'[' CIN data_area}">
<MvIF EXPR = "{(a1 GT '0')}">
<MvASSIGN NAME = "general_data_area" VALUE = "{substring(data_area,1,a1-1)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{a1}">
</MvIF>
<MvIF EXPR = "{showing_message EQ 1}">
<MvASSIGN NAME = "general_data_area"
VALUE = "{general_data_area $ '[ <a href="#followups">Followups</a> ] '
$ '[ <a href="#postfollowup">Post Followup</a> ] '}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsFaqShort">
<MvCOMMENT> Omit faq link if not to be shown </MvCOMMENT>
<MvASSIGN NAME = "a1" VALUE = "{link_faq_short CIN data_area}">
<MvIF EXPR = "{(a1 GT '0') AND (show_faq NE 'on')}">
<MvASSIGN NAME = "data_area" VALUE = "{substring(data_area,1,a1-1)
$ substring(data_area,a1+len(link_faq_short),len(data_area)-(a1+len(link_faq_short)-1))}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsShowpostform" PARAMETERS = "admin">
<MvASSIGN NAME = "a1" VALUE = "{'parm_func=showpostform' CIN substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ substring(data_area,curr_pos,a1+21)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1+21}">
<MvIF EXPR = "{admin EQ 'y'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ extra_parms}">
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsShowmain" PARAMETERS = "admin">
<MvASSIGN NAME = "a1" VALUE = "{'parm_func=showmain+parm_starting_root=1' CIN substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ substring(data_area,curr_pos,a1+38)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1+38}">
<MvIF EXPR = "{admin EQ 'y'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ extra_parms}">
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsShowsearch" PARAMETERS = "admin">
<MvASSIGN NAME = "a1" VALUE = "{'parm_func=showsearch+parm_starting_root=1' CIN substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ substring(data_area,curr_pos,a1+40)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1+40}">
<MvIF EXPR = "{admin EQ 'y'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ extra_parms}">
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsShowfaq" PARAMETERS = "admin">
<MvASSIGN NAME = "a1" VALUE = "{'parm_func=showfaq' CIN substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ substring(data_area,curr_pos,a1+16)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1+16}">
<MvIF EXPR = "{admin EQ 'y'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ extra_parms}">
</MvIF>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsFinishLinks">
<MvASSIGN NAME = "a1" VALUE = "{']' CIN substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ substring(data_area,curr_pos,a1)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "GenLinkLineParmsWrapup" PARAMETERS = "admin">
<MvIF EXPR = "{admin EQ 'y' AND data_area NE ''}">
<MvASSIGN NAME = "general_data_area" VALUE = "{general_data_area $ ' ' $ link_admin}">
</MvIF>
<MvIF EXPR = "{curr_pos LE len(data_area)}">
<MvASSIGN NAME = "general_data_area"
VALUE = "{general_data_area $ substring(data_area,curr_pos,len(data_area)-curr_pos+1)}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "PostControl">
<MvEVAL EXPR = SeeIfBlocked()>
<MvIF EXPR = "{blocked_ip EQ '1'}">
<MvEVAL EXPR = Error("blocked_ip")>
<MvFUNCRETURN></MvFUNCRETURN>
</MvIF>
<MvEVAL EXPR = EditInput()>
<MvEVAL EXPR = UpdateDatabase()>
<MvEVAL EXPR = BuildMessageFile()>
<MvEVAL EXPR = ReturnHtml()>
<MvEXIT>
</MvFUNCTION>
<MvFUNCTION NAME = "SeeIfBlocked">
<MvCOMMENT>
If visitor's IP in unwelcome list, go no further.
Use &[REMOTE_ADDR];. Import unwelcome list file, check for match.
</MvCOMMENT>
<MvASSIGN NAME = "blocked_ip" VALUE = "{0}">
<MvIF EXPR = "{fexists(unwelcomelist)}">
<MvIMPORT FILE = "&[unwelcomelist];" FIELDS = "ip" DELIMITER = "">
<MvIF EXPR = "{'*' CIN ip}">
<MvIF EXPR = "{gettoken(ip,'*',1) CIN &[REMOTE_ADDR];}">
<MvASSIGN NAME = "blocked_ip" VALUE = "{1}">
<MvIMPORTSTOP></MvIMPORTSTOP>
</MvIF>
<MvELSE>
<MvIF EXPR = "{&[REMOTE_ADDR]; EQ ip}">
<MvASSIGN NAME = "blocked_ip" VALUE = "{1}">
<MvIMPORTSTOP></MvIMPORTSTOP>
</MvIF>
</MvIF>
</MvIMPORT>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "EditInput">
<MvASSIGN NAME = "form_name" VALUE = "{RemoveYumlaut(form_name)}">
<MvASSIGN NAME = "form_email" VALUE = "{RemoveYumlaut(form_email)}">
<MvASSIGN NAME = "form_subject" VALUE = "{RemoveYumlaut(form_subject)}">
<MvASSIGN NAME = "form_body" VALUE = "{RemoveYumlaut(form_body)}">
<MvASSIGN NAME = "form_url" VALUE = "{RemoveYumlaut(form_url)}">
<MvASSIGN NAME = "form_url_title" VALUE = "{RemoveYumlaut(form_url_title)}">
<MvASSIGN NAME = "form_img" VALUE = "{RemoveYumlaut(form_img)}">
<MvIF EXPR = "{parm_followup_to NE ''}">
<MvASSIGN NAME = "is_followup" VALUE = "{'1'}">
<MvELSE>
<MvASSIGN NAME = "is_followup" VALUE = "{'0'}">
</MvIF>
<MvIF EXPR = "{form_name NE ''}">
<MvEVAL EXPR = EditInputName()>
<MvELSE>
<MvEVAL EXPR = Error("no_name")>
</MvIF>
<MvASSIGN NAME = "a1" VALUE = "{atsign IN form_email}">
<MvIF EXPR = "{a1 GT 0}">
<MvIF EXPR = "{period IN substring(form_email,a1+1,len(form_email)-a1)}">
<MvASSIGN NAME = "email" VALUE = "{ConvertTags(form_email)}">
</MvIF>
</MvIF>
<MvEVAL EXPR = EditInputSubject()>
<MvEVAL EXPR = EditInputBody()>
<MvASSIGN NAME = "a1" VALUE = "{':' IN form_url}">
<MvASSIGN NAME = "a2" VALUE = "{period IN form_url}">
<MvIF EXPR = "{a1 GT 0 AND a2 GT 0 AND a1 LT a2 AND form_url_title NE ''}">
<MvASSIGN NAME = "message_url" VALUE = "{ConvertTags(form_url)}">
<MvASSIGN NAME = "message_url_title" VALUE = "{ConvertTags(form_url_title)}">
</MvIF>
<MvASSIGN NAME = "a1" VALUE = "{'tp://' IN form_img}">
<MvASSIGN NAME = "a2" VALUE = "{period IN form_img}">
<MvIF EXPR = "{a1 GT 0 AND a2 GT 0 AND a1 LT a2}">
<MvASSIGN NAME = "message_img" VALUE = "{ConvertTags(form_img)}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "EditInputName">
<MvASSIGN NAME = "curr_pos" VALUE = "{1}">
<MvASSIGN NAME = "len_limit" VALUE = "{len(form_name)}">
<MvWHILE EXPR = "{curr_pos LE len_limit}">
<MvASSIGN NAME = "curr_char" VALUE = "{substring(form_name,curr_pos,1)}">
<MvIF EXPR = "{NOT isalpha(curr_char)
AND curr_char NE '0'
AND curr_char NE '1'
AND curr_char NE '2'
AND curr_char NE '3'
AND curr_char NE '4'
AND curr_char NE '5'
AND curr_char NE '6'
AND curr_char NE '7'
AND curr_char NE '8'
AND curr_char NE '9'
AND curr_char NE ' '
AND curr_char NE '_'}">
<MvASSIGN NAME = "form_name" VALUE = "{ConvertTags(form_name)}">
<MvEVAL EXPR = Error("bad_char_in_name")>
</MvIF>
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + 1}">
</MvWHILE>
<MvASSIGN NAME = "name" VALUE = "{form_name}">
</MvFUNCTION>
<MvFUNCTION NAME = "EditInputSubject">
<MvIF EXPR = "{form_subject NE ''}">
<MvASSIGN NAME = "subject" VALUE = "{glosub(form_subject,'\cM','')}">
<MvCOMMENT>Remove any anchors from subject, since it goes in an anchor on main page.</MvCOMMENT>
<MvASSIGN NAME = "work_subject" VALUE = "{''}">
<MvASSIGN NAME = "a1" VALUE = "{'<a href' CIN subject}">
<MvASSIGN NAME = "a1_abs" VALUE = "{a1}">
<MvASSIGN NAME = "a2_abs" VALUE = "{'1'}">
<MvASSIGN NAME = "curr_pos" VALUE = "{'1'}">
<MvASSIGN NAME = "lenny" VALUE = "{len(subject)}">
<MvWHILE EXPR = "{a1 GT 0}">
<MvASSIGN NAME = "a2" VALUE = "{'</a>' CIN
substring(subject,a1_abs,lenny - a1_abs + 1)}">
<MvASSIGN NAME = "a2_abs" VALUE = "{a2 + a1_abs - 1}">
<MvASSIGN NAME = "work_subject" VALUE = "{work_subject $
substring(subject,curr_pos,a1_abs - curr_pos)}">
<MvASSIGN NAME = "curr_pos" VALUE = "{a2_abs + 4}">
<MvASSIGN NAME = "a1" VALUE = "{'<a href' CIN
substring(subject,a2_abs,lenny - a2_abs + 1)}">
<MvASSIGN NAME = "a1_abs" VALUE = "{a1 + a2_abs - 1}">
</MvWHILE>
<MvCOMMENT>Copy part of subject following last anchor, if any.
Replace subject with anchor-free version.</MvCOMMENT>
<MvIF EXPR = "{curr_pos GT 1}">
<MvIF EXPR = "{curr_pos GT lenny}">
<MvEVAL EXPR = Error("link_subject")>
<MvELSE>
<MvASSIGN NAME = "work_subject" VALUE = "{work_subject $
substring(subject,curr_pos,lenny-curr_pos+1)}">
<MvASSIGN NAME = "subject" VALUE = "{work_subject}">
</MvIF>
</MvIF>
<MvIF EXPR = "{'<font' CIN subject}">
<MvIF EXPR = "{'</font' CIN subject}">
<MvELSE>
<MvASSIGN NAME = "subject" VALUE = "{subject $ '</font>'}">
</MvIF>
</MvIF>
<MvIF EXPR = "{'SCRIPT LANGUAGE' CIN subject}">
<MvASSIGN NAME = "foo" VALUE = "{'SCRIPT LANGUAGE' CIN subject}">
<MvASSIGN NAME = "subject" VALUE = "{substring(subject,0,foo-1) $ substring(subject,foo+16,len(subject)-foo)}">
</MvIF>
<MvIF EXPR = "{allow_html_subj NE 'on' OR 'MvEVAL' CIN subject}">
<MvASSIGN NAME = "subject" VALUE = "{ConvertTags(subject)}">
</MvIF>
<MvELSE>
<MvEVAL EXPR = Error("no_subject")>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "EditInputBody">
<MvIF EXPR = "{form_body NE ''}">
<MvASSIGN NAME = "body" VALUE = "{glosub(form_body,'\cM','')}">
<MvIF EXPR = "{allow_html_body NE 'on' OR 'MvEVAL' CIN body}">
<MvASSIGN NAME = "body" VALUE = "{ConvertTags(body)}">
</MvIF>
<MvIF EXPR = "{allow_html_body EQ 'on'}">
<MvIF EXPR = "{'<font' CIN body}">
<MvIF EXPR = "{'</font' CIN body}">
<MvELSE>
<MvASSIGN NAME = "body" VALUE = "{body $ '</font>'}">
</MvIF>
</MvIF>
<MvIF EXPR = "{'SCRIPT LANGUAGE' CIN body}">
<MvASSIGN NAME = "foo" VALUE = "{'SCRIPT LANGUAGE' CIN body}">
<MvASSIGN NAME = "body" VALUE = "{substring(body,0,foo-1) $ substring(body,foo+16,len(body)-foo)}">
</MvIF>
</MvIF>
<MvASSIGN NAME = "body" VALUE = "{glosub(body,crlf,'<br>')}">
<MvASSIGN NAME = "body" VALUE = "{glosub(body,'<p>','<br>')}">
<MvELSE>
<MvEVAL EXPR = Error("no_body")>
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "UpdateDatabase">
<MvWHILE EXPR = "{fexists('&[dirprefix];/lockfile')}"></MvWHILE>
<MvEXPORT FILE = "&[dirprefix];/lockfile" FIELDS = "" DELIMITER = "">
<MvLOCKFILE FILE = "&[database];">
<MvLOCKFILE FILE = "&[index_file2];">
<MvLOCKFILE FILE = "&[index_file1];">
<MvLOCKFILE FILE = "&[index_file3];">
<MvIF EXPR = "{is_followup EQ '1'}">
<MvEVAL EXPR = ProcessFollowup()>
<MvELSE>
<MvASSIGN NAME = "parentnum" VALUE = "{super_root}">
<MvASSIGN NAME = "nest_level" VALUE = "{'1'}">
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file1];,&[index_file2];,&[index_file3];">
</MvIF>
<MvEVAL EXPR = UpdateSuperRoot()>
<MvEVAL EXPR = CreateDatabaseRecord()>
</MvLOCKFILE>
</MvLOCKFILE>
</MvLOCKFILE>
</MvLOCKFILE>
<MvASSIGN NAME = "foo" VALUE = "{fdelete('&[dirprefix];/lockfile')}">
</MvFUNCTION>
<MvFUNCTION NAME = "ProcessFollowup">
<MvASSIGN NAME = "parentnum" VALUE = "{parm_followup_to}">
<MvCOMMENT> The following will change as we ascend to super_root. </MvCOMMENT>
<MvASSIGN NAME = "temp_parentnum" VALUE = "{parentnum}">
<MvCOMMENT> Find parent quickly via selfnum index. </MvCOMMENT>
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file2];">
<MvFIND NAME = "&[dbname];" VALUE = "{temp_parentnum}" EXACT = "EXACT"></MvFIND>
<MvASSIGN NAME = "nest_level" VALUE = "{dbnestlvl + 1}">
<MvASSIGN NAME = "parent_recno" VALUE = "{&[dbname];.d.recno}">
<MvCLOSE NAME = "&[dbname];">
<MvEVAL EXPR = UpdatePredecessors()>
</MvFUNCTION>
<MvFUNCTION NAME = "UpdatePredecessors">
<MvCOMMENT> Visit the posting immediately after the new posting, and set up
the new posting's sequence number. Then visit each posting from the
new posting's parent back up to the root which is the ancestor of
the new posting. Increment each *ancestor*'s number of descendents,
including the root's. Then travel *forward* from the root to the
parent again, incrementing each posting's sequence number except
for the root's. We have to do the sequence number incrementing in
the forward direction to avoid generating temporarily duplicate
sequence numbers, which would throw off the index.
</MvCOMMENT>
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file1];,&[index_file2];,&[index_file3];">
<MvFILTER NAME = "&[dbname];" FILTER = "{NOT &[dbname];.d.deleted}">
<MvEVAL EXPR = AccessFollowingPosting()>
<MvEVAL EXPR = UpdateNumDescendents()>
<MvEVAL EXPR = UpdateSequenceNumbers()>
</MvFUNCTION>
<MvFUNCTION NAME = "AccessFollowingPosting">
<MvGO ROW = parent_recno>
<MvCOMMENT> Read record after insert point. </MvCOMMENT>
<MvSKIP>
<MvCOMMENT> Set up sequence number for new posting. </MvCOMMENT>
<MvIF EXPR = "{NOT &[dbname];.d.eof}">
<MvASSIGN NAME = "seqnum" VALUE = "{dbseqnum + 1}">
<MvELSE>
<MvASSIGN NAME = "seqnum" VALUE = "{'1'}">
</MvIF>
<MvASSIGN NAME = "seqnum" VALUE = "{padl(seqnum,seqnum_size,'0')}">
</MvFUNCTION>
<MvFUNCTION NAME = "UpdateNumDescendents">
<MvGO ROW = parent_recno>
<MvWHILE EXPR = "{dbparentno NE super_root}">
<MvIF EXPR = "{dbselfnum EQ temp_parentnum}">
<MvASSIGN NAME = "dbnumdesc" VALUE = "{dbnumdesc + 1}">
<MvASSIGN NAME = "temp_parentnum" VALUE = "{dbparentno}">
<MvUPDATE NAME = "&[dbname];"></MvUPDATE>
</MvIF>
<MvSKIP ROWS = -1>
</MvWHILE>
<MvCOMMENT> Update root. </MvCOMMENT>
<MvASSIGN NAME = "dbnumdesc" VALUE = "{dbnumdesc + 1}">
<MvUPDATE NAME = "&[dbname];"></MvUPDATE>
</MvFUNCTION>
<MvFUNCTION NAME = "UpdateSequenceNumbers">
<MvCOMMENT> We're at root. </MvCOMMENT>
<MvIF EXPR = "{dbselfnum EQ parentnum}">
<MvFUNCRETURN></MvFUNCRETURN>
</MvIF>
<MvWHILE EXPR = "{dbselfnum NE parentnum}">
<MvSKIP>
<MvASSIGN NAME = "dbseqnum" VALUE = "{dbseqnum + 1}">
<MvASSIGN NAME = "dbseqnum" VALUE = "{padl(dbseqnum,seqnum_size,'0')}">
<MvUPDATE NAME = "&[dbname];"></MvUPDATE>
</MvWHILE>
</MvFUNCTION>
<MvFUNCTION NAME = "UpdateSuperRoot">
<MvGO ROW = 1>
<MvASSIGN NAME = "dbnumdesc" VALUE = "{dbnumdesc + 1}">
<MvASSIGN NAME = "currself" VALUE = "{dbnumdesc + big_bump}">
<MvCOMMENT> If adding a root, assign new root's sequence number, and
update super root's root count and sequence number. </MvCOMMENT>
<MvIF EXPR = "{is_followup NE '1'}">
<MvASSIGN NAME = "seqnum" VALUE = "{dbseqnum}">
<MvASSIGN NAME = "dbparentno" VALUE = "{dbparentno + 1}">
<MvASSIGN NAME = "dbseqnum" VALUE = "{dbseqnum + max_seqnum_increment}">
<MvASSIGN NAME = "dbseqnum" VALUE = "{padl(dbseqnum,seqnum_size,'0')}">
</MvIF>
<MvASSIGN NAME = "last_rootnum" VALUE = "{dbparentno}">
<MvUPDATE NAME = "&[dbname];"></MvUPDATE>
</MvFUNCTION>
<MvFUNCTION NAME = "CreateDatabaseRecord">
<MvCOMMENT> Create database record for the new posting. </MvCOMMENT>
<MvASSIGN NAME = "dbselfnum" VALUE = "{currself}">
<MvASSIGN NAME = "dbparentno" VALUE = "{parentnum}">
<MvASSIGN NAME = "dbnumdesc" VALUE = "{'0'}">
<MvASSIGN NAME = "dbseqnum" VALUE = "{seqnum}">
<MvASSIGN NAME = "dbnestlvl" VALUE = "{nest_level}">
<MvASSIGN NAME = "dbdate" VALUE = "{date}">
<MvASSIGN NAME = "dbtime" VALUE = "{time}">
<MvASSIGN NAME = "dblongdate" VALUE = "{long_date}">
<MvASSIGN NAME = "dbipaddr" VALUE = "{&[REMOTE_ADDR];}">
<MvASSIGN NAME = "dbname1" VALUE = "{name}">
<MvASSIGN NAME = "dbemail" VALUE = "{email}">
<MvASSIGN NAME = "dbsubject" VALUE = "{subject}">
<MvASSIGN NAME = "dblinkurl" VALUE = "{message_url}">
<MvASSIGN NAME = "dblinktitl" VALUE = "{message_url_title}">
<MvASSIGN NAME = "dbimageurl" VALUE = "{message_img}">
<MvADD NAME = "&[dbname];"></MvADD>
<MvCLOSE NAME = "&[dbname];">
</MvFUNCTION>
<MvFUNCTION NAME = "BuildMessageFile">
<MvCOMMENT> Create a file for the message text </MvCOMMENT>
<MvASSIGN NAME = "filename" VALUE = "{dirprefix $ '/' $ mesgdir $ '/' $ currself $ '.' $ ext}">
<MvEXPORT FILE = filename FIELDS = "body" DELIMITER = ""></MvEXPORT>
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessage">
<MvEVAL EXPR = ShowMessageInit()>
<MvEVAL EXPR = ShowMessageHTML1()>
<MvEVAL EXPR = ShowMessageHTML2()>
<MvEVAL EXPR = ShowMessageHTML3()>
<MvEVAL EXPR = ShowMessageFormIntro()>
<MvEVAL EXPR = ShowMessageFormSubject()>
<MvEVAL EXPR = ShowMessageFormComments()>
<MvEVAL EXPR = ShowMessageWrapup()>
<MvEXIT>
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageInit">
<MvASSIGN NAME = "filename" VALUE = "{dirprefix $ '/' $ mesgdir $ '/' $ parm_msgnum $ '.' $ ext}">
<MvIMPORT FILE = filename FIELDS = "body" DELIMITER = "">
</MvIMPORT>
<MvCOMMENT> Get hidden field data from database. </MvCOMMENT>
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file2];,&[index_file1];,&[index_file3];">
<MvFILTER NAME = "&[dbname];" FILTER = "{NOT &[dbname];.d.deleted}">
<MvFIND NAME = "&[dbname];" VALUE = "{parm_msgnum}" EXACT = "EXACT"></MvFIND>
<MvASSIGN NAME = "parent" VALUE = "{dbparentno}">
<MvASSIGN NAME = "name" VALUE = "{dbname1}">
<MvASSIGN NAME = "email" VALUE = "{dbemail}">
<MvASSIGN NAME = "subject" VALUE = "{dbsubject}">
<MvASSIGN NAME = "date" VALUE = "{dbdate}">
<MvASSIGN NAME = "time" VALUE = "{dbtime}">
<MvASSIGN NAME = "longdate" VALUE = "{dblongdate}">
<MvASSIGN NAME = "ipaddr" VALUE = "{dbipaddr}">
<MvASSIGN NAME = "url" VALUE = "{dblinkurl}">
<MvASSIGN NAME = "url_title" VALUE = "{dblinktitl}">
<MvASSIGN NAME = "img" VALUE = "{dbimageurl}">
<MvASSIGN NAME = "starting_recno" VALUE = "{&[dbname];.d.recno}">
<MvASSIGN NAME = "starting_nestlvl" VALUE = "{dbnestlvl}">
<MvIF EXPR = "{parent NE super_root}">
<MvFIND NAME = "&[dbname];" VALUE = "{parent}" EXACT = "EXACT"></MvFIND>
<MvASSIGN NAME = "parent_name" VALUE = "{dbname1}">
<MvASSIGN NAME = "parent_subject" VALUE = "{dbsubject}">
<MvASSIGN NAME = "parent_date" VALUE = "{dbdate}">
<MvASSIGN NAME = "parent_time" VALUE = "{dbtime}">
<MvASSIGN NAME = "parent_ipaddr" VALUE = "{dbipaddr}">
</MvIF>
<MvCLOSE NAME = "&[dbname];">
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageHTML1">
&[ON];&[header];<center><h1>&[subject];</h1></center>&[crlf];&[OFF];
<MvASSIGN NAME = "showing_message" VALUE = "{1}">
<MvEVAL EXPR = LinkLine(parm_admin_flag,parm_user,parm_pass)>
<MvASSIGN NAME = "showing_message" VALUE = "{0}">
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageHTML2">
&[ON];<BLOCKQUOTE>Posted by&[crlf];&[OFF];
<MvIF EXPR = "{use_time EQ 'on'}">
<MvASSIGN NAME = "print_date" VALUE = "{time $ ' ' $ date}">
<MvASSIGN NAME = "parent_print_date" VALUE = "{parent_time $ ' ' $ parent_date}">
<MvELSE>
<MvASSIGN NAME = "print_date" VALUE = "{date}">
<MvASSIGN NAME = "parent_print_date" VALUE = "{parent_date}">
</MvIF>
<MvIF EXPR = "{email NE ''}">
&[ON];<a href="mailto:&[email];">&[name];</a> on &[print_date]; from &[ipaddr];:<p>&[crlf];&[OFF];
<MvELSE>
&[ON];&[name]; on &[print_date]; from &[ipaddr];:<p>&[crlf];&[OFF];
</MvIF>
<MvIF EXPR = "{parent NE super_root}">
<MvIF EXPR = "{parm_admin_flag EQ 'y'}">
<MvASSIGN NAME = "parent_msg" VALUE = "{'&[documenturl];parm_func=showmsg+parm_msgnum=&[parent];+parm_admin_flag=y+parm_user=&[parm_user];+parm_pass=&[parm_pass];'}">
<MvELSE>
<MvASSIGN NAME = "parent_msg" VALUE = "{'&[documenturl];parm_func=showmsg+parm_msgnum=&[parent];'}">
</MvIF>
&[ON];In reply to: <a href="&[parent_msg];">&[parent_subject];</a> posted by &[crlf];&[OFF];
<MvIF EXPR = "{parent_email NE ''}">
&[ON];<a href="&[parent_email];">&[parent_name];</a> on &[parent_print_date];:<p>&[crlf];&[OFF];
<MvELSE>
&[ON];&[parent_name]; on &[parent_print_date]; from &[parent_ipaddr];:<p>&[crlf];&[OFF];
</MvIF>
</MvIF>
<MvIF EXPR = "{allow_gif EQ 'on' AND img NE ''}">
&[ON];<center><img src="&[img];"></center><p>&[crlf];&[OFF];
</MvIF>
&[ON];&[body];&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageHTML3">
<MvIF EXPR = "{url NE ''}">
&[ON];<ul><li><a href="&[url];">&[url_title];</a></ul>&[crlf];&[OFF];
</MvIF>
&[ON];</BLOCKQUOTE><hr width=90%><p>&[crlf];&[OFF];
&[ON];<a NAME = followups><UL><H3>Follow Ups:</H3></UL>&[crlf];&[OFF];
<MvASSIGN NAME = "roots_per_page" VALUE = "{'1'}">
<MvASSIGN NAME = "roots_displayed_count" VALUE = "{'0'}">
<MvASSIGN NAME = "traverse_function" VALUE = "{'AddLine'}">
<MvASSIGN NAME = "traverse_reason" VALUE = "{'followups'}">
<MvOPEN NAME = "&[dbname];" DATABASE = "&[database];"
INDEXES = "&[index_file1];,&[index_file2];,&[index_file3];">
<MvFILTER NAME = "&[dbname];" FILTER = "{NOT &[dbname];.d.deleted}">
&[ON]<BLOCKQUOTE>&[OFF]
<MvEVAL EXPR = TraverseTree(parm_admin_flag,parm_user,parm_pass)>
<MvEVAL EXPR = Unindent()>
<MvCLOSE NAME = "&[dbname];">
&[ON];<hr width=90%><p>&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageFormIntro">
&[ON];<a NAME = postfollowup><UL><center><H1>Post a Followup</H1></center></UL>&[crlf];&[OFF];
<MvIF EXPR = "{show_preview EQ 'on'}">
<MvASSIGN NAME = "postvar" VALUE = "{'parm_func=followuppreview'}">
<MvELSE>
<MvASSIGN NAME = "postvar" VALUE = "{'parm_func=post'}">
</MvIF>
<MvIF EXPR = "{parm_admin_flag EQ 'y'}">
&[ON];<form method=POST action="&[documenturl];&[postvar];+parm_admin_flag=y+parm_user=&[parm_user];+parm_pass=&[parm_pass];">&[crlf];&[OFF];
<MvELSE>
&[ON];<form method=POST action="&[documenturl];&[postvar];">&[crlf];&[OFF];
</MvIF>
<MvIF EXPR = "{allow_html_subj EQ 'on'}">
<MvASSIGN NAME = "subject" VALUE = "{ConvertTags(subject)}">
</MvIF>
&[ON];<input type=hidden name="parm_followup_to" value="&[parm_msgnum];"></INPUT>&[crlf];&[OFF];
&[ON];<input type=hidden name="form_origname" value="&[name];"></INPUT>&[crlf];&[OFF];
&[ON];<input type=hidden name="form_origemail" value="&[email];"></INPUT>&[crlf];&[OFF];
&[ON];<input type=hidden name="form_origsubject" value="&[subject];"></INPUT>&[crlf];&[OFF];
&[ON];<input type=hidden name="form_origdate" value="&[longdate];"></INPUT>&[crlf];&[OFF];
&[ON];<CENTER><TABLE>&[crlf];&[OFF];
&[ON];<TR><TH align="left">Name:</TH><TD><input type=text name="form_name" size=&[textwidth]; maxlength=&[textwidth];></INPUT></TD></TR>&[crlf];&[OFF];
&[ON];<TR><TH align="left">E-Mail:</TH><TD><input type=text name="form_email" size=&[textwidth]; maxlength=&[textwidth];></INPUT></TD></TR>&[crlf];&[OFF];
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageFormSubject">
<MvIF EXPR = "{subject_line EQ '0'}">
<MvASSIGN NAME = "a1" VALUE = "{'Re:' IN subject}">
<MvIF EXPR = "{a1 EQ '1'}">
&[ON];<TR><TH align="left">Subject:</TH><TD><b>&[crlf];&[OFF];
&[ON];<input type=text name="form_subject" size=&[textwidth]; maxlength=&[textwidth]; value="&[subject];"></INPUT></b></TD></TR>&[crlf];&[OFF];
<MvELSE>
&[ON];<TR><TH align="left">Subject:</TH><TD><b>&[crlf];&[OFF];
&[ON];<input type=text name="form_subject" size=&[textwidth]; maxlength=&[textwidth]; value="Re: &[subject];"></INPUT></b></TD></TR>&[crlf];&[OFF];
</MvIF>
<MvELSE>
<MvIF EXPR = "{subject_line EQ '1'}">
<MvASSIGN NAME = "a1" VALUE = "{'Re:' IN subject}">
<MvIF EXPR = "{a1 EQ '1'}">
&[ON];<input type=hidden name="form_subject" size=&[textwidth]; maxlength=&[textwidth]; value="&[subject];"></INPUT>&[crlf];&[OFF];
&[ON];<TR><TH align="left">Subject:</TH><TD><b>&[subject];</b></TD></TR>&[crlf];&[OFF];
<MvELSE>
&[ON];<input type=hidden name="form_subject" size=&[textwidth]; maxlength=&[textwidth]; value="Re: &[subject];"></INPUT>&[crlf];&[OFF];
&[ON];<TR><TH align="left">Subject:</TH><TD><b>Re: &[subject];</b></TD></TR>&[crlf];&[OFF];
</MvIF>
<MvELSE>
<MvIF EXPR = "{subject_line EQ '2'}">
&[ON];<TR><TH align="left">Subject:</TH><TD><input type=text name="form_subject" size=&[textwidth]; maxlength=&[textwidth];></INPUT></TD></TR>&[crlf];&[OFF];
<MvELSE>
<MvASSIGN NAME = "a1" VALUE = "{'Re:' IN subject}">
<MvIF EXPR = "{a1 EQ '1'}">
&[ON];<TR><TH align="left">Subject:</TH><TD><input type=text name="form_subject" value="&[subject];" size=&[textwidth]; maxlength=&[textwidth];></INPUT></TD></TR>&[crlf];&[OFF];
<MvELSE>
&[ON];<TR><TH align="left">Subject:</TH><TD><input type=text name="form_subject" value="Re: &[subject];" size=&[textwidth]; maxlength=&[textwidth];></INPUT></TD></TR>&[crlf];&[OFF];
</MvIF>
</MvIF>
</MvIF>
</MvIF>
<MvIF EXPR = "{allow_html_subj EQ 'on'}">
<MvASSIGN NAME = "subject" VALUE = "{ReconvertTags(subject)}">
</MvIF>
</MvFUNCTION>
<MvFUNCTION NAME = "ShowMessageFormComments">
&[ON];<TR><TH align="left">Comments:</TH><TD>&[crlf];&[OFF];
<MvCOMMENT> If requested, add lines of the message to the reply comment area, preceded by colons.
Produce one line until a <br>, newline, or <p> tag is seen. If it's a <p>, also produce
a blank line.
Before processing body text to be quoted, convert any longforms of characters (i.e. < for
<, > for >, and " for ") to shortform, then reconvert when ready to display.
</MvCOMMENT>
<MvIF EXPR = "{quote_text EQ 'on'}">
<MvIF EXPR = "{allow_html_body NE 'on'}">
<MvASSIGN NAME = "body" VALUE = "{ReconvertTags(body)}">
</MvIF>
<MvASSIGN NAME = "curr_pos" VALUE = "{'1'}">
<MvASSIGN NAME = "lenny" VALUE = "{len(body)}">
<MvASSIGN NAME = "a3" VALUE = "{crlf CIN body}">
<MvIF EXPR = "{lenny GT 1 AND a3 EQ lenny - 1}">
<MvASSIGN NAME = "lenny" VALUE = "{lenny - 2}">
</MvIF>
<MvASSIGN NAME = "out_body" VALUE = "{''}">
<MvWHILE EXPR = "{curr_pos LE lenny}">
<MvASSIGN NAME = "a1" VALUE = "{'<p>' CIN substring(body,curr_pos,lenny-curr_pos+1)}">
<MvASSIGN NAME = "a2" VALUE = "{'<br>' CIN substring(body,curr_pos,lenny-curr_pos+1)}">
<MvASSIGN NAME = "a3" VALUE = "{crlf CIN substring(body,curr_pos,lenny-curr_pos+1)}">
<MvIF EXPR = "{a1 GT '0'}">
<MvIF EXPR = "{((a2 EQ 0) OR (a2 GT a1)) AND ((a3 EQ 0) OR (a3 GT a1))}">
<MvIF EXPR = "{a1 GT textwidth - 2}"> <MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + textwidth - 2}">
<MvELSE>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,a1-1)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a1-1 + 3}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{((a2 NE 0) AND (a2 LT a1)) AND ((a3 EQ 0) OR (a3 GT a2))}">
<MvIF EXPR = "{a2 GT textwidth - 2}"> <MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + textwidth - 2}">
<MvELSE>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,a2-1)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a2-1 + 4}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{(a3 GT 0) AND (a3 LT a1)}">
<MvIF EXPR = "{a3 GT textwidth - 2}"> <MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + textwidth - 2}">
<MvELSE>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,a3-1)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a3-1 + 2}">
</MvIF>
</MvIF>
</MvIF>
</MvIF>
<MvELSE>
<MvIF EXPR = "{(a2 GT 0) AND ((a3 EQ 0) OR (a3 GT a2))}">
<MvIF EXPR = "{a2 GT textwidth - 2}"> <MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + textwidth - 2}">
<MvELSE>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,a2-1)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a2-1 + 4}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{a3 GT '0'}">
<MvIF EXPR = "{a3 GT textwidth - 2}"> <MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + textwidth - 2}">
<MvELSE>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,a3-1)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">
<MvASSIGN NAME = "curr_pos" VALUE = "{curr_pos + a3-1 + 2}">
</MvIF>
<MvELSE>
<MvIF EXPR = "{(lenny - curr_pos + 1) GT textwidth - 2}"><MvCOMMENT> Too big to fit with : </MvCOMMENT>
<MvASSIGN NAME = "a4" VALUE = "{substring(body,curr_pos,textwidth - 2)}">
<MvASSIGN NAME = "out_body" VALUE = "{out_body $ ': ' $ a4 $ crlf}">