Note: This documentation is outdated and contains error, yet it gives a good description of HTPL. To INSTALL, see the INSTALL document. 1. What is HTPL? 2. What are the requirements for using HTPL? 3. How do I compile HTPL? 4. How does HTPL work? 5. Tutorial 5.1. Writing simple HTML 5.2. Embedding simple Perl commands 5.3. Interpreting forms 5.4. Simple internet functions 5.5. HTML generation functions 5.6. Reading a comma delimited file 5.7. Integrating an SQL database 6. Reference 6.1. HTPL library functions 6.2. HTPL result set 6.3. HTPL macros 6.4. HTPL directives 6.5. Writing your own macros 6.6. Additional information 1. What is HTPL? HTPL (Hyper Text Programming Language, or just a lame monogram of HTML and PERL) is a Perl based scripting tool for creating web content. It is basically a wrapper to CGI. Anything HTPL can do can be basically done with Perl and CGI, but HTPL provides rapid development tools. An HTPL file is an HTML file with Embedded HTML. Additional commands can be added. (eg, C, SQL), but all in all the web server runs a CGI script created from your document. Note: There are three other tools called HTPL. I have no connection with them. 2. What are the requirements for using HTPL? You must have CGI access on your account, be able to give local directives to your web server (ie, .htaccess files on apache), be able to write temporary files somewhere and preferably be able to install modules from CPAN. (That can be done on your local directory. Modules can also be transferred to the same path the HTPL binary resides in). Perl 5.004 or later is required. 3. How do I compile HTPL? To install type: ./configure make make install make build If you are the superuser, consider importing modules from CPAN by: make CPAN *IMPORTANT* * IMPORTANT* IMPORTANT* *IMPORTANT* If you intend to install htpl as root, run configure as root! *IMPORTANT* * IMPORTANT* IMPORTANT* *IMPORTANT* 4. How does HTPL work? Anytime you access a htpl file the web server activates htpl.cgi as a cgi script. Htpl.cgi reads the htpl file specified and converts it into a perl script with the extension perl. (The file created can be run as a stand alone CGI script, but you will not be able to use the redirect function for document redirection or mime type settings). The perl file is then executed. (Unless you used the XSUB extensions) Once the perl file is created, htpl will not preprocess the source htpl again but immediately execute the perl script; that is, unless the htpl file is newer than the perl file, in which case the perl file will be recreated according to the new version. If you need to force reprocessing, update the htpl file or just “touch” it. When creating an htpl documented with inline C code, htpl.cgi dumps the embedded XSUB functions into a file, creates an installation module for it and activates the usual compilation procedure. This can take a long time, therefore you better add you C code only once fully functional. When the script runs, its output is captured to a file, and only then sent. This allows using the redirect function to redirect the browser even if the script has already yielded output. Same is true for sending HTTP cookies. 5. Tutorial 5.1 Writing simple HTML Our first dcoument will display a very well known string. Create the file hello.htpl in your web directory: Goodbye, Friend. The results will be obvious. 5.2 Embedding simple Perl commands Create the following file: <# @time = localtime; $d = $time[3]; $m = $time[4] + 1; $y = $time[5] + ($time[5] < 70 ? 2000 : 1900); > Today is $d/$m/$y! The contents of the perl script between the <# and > tags will be evaluated, and the variables inside the perl code will be substituted. Remember: If you want to use the @ symbol (eg, for specifying an email) you must escape it with a backslash, for example: Now, look at this: <# @t = localtime; if ($t[6] == 6) { > Today is Shabbath, therefore this site is closed.
Please log out and log on on a working day.
<# } else { print `date`; } > Two reveals: 1) You can escape to HTML in the middle of your code. HTPL will allow it only after a block opening or closing, or an end of statement. Actually, you can escape after the characters }, { and ;. Complicated regular expressions might be a problem, but even Larry doesn’t need that kind of expressions. 2) You can specify output using “print” if you don’t want to escape to HTML and back to PERL. 5.3 Interpreting forms Let’s create the following form:
Name:
Email:
And put the following in the file feedback.htpl: Hello $name <$email> Try it for yourself! For multiple checkboxes or select boxes you can refer to an array. For example, if you had a series of checkboxes called “item” you can refer to the array @item. For single inputs, an array with one element will be created, so you don’t need an additional check if the user checked only one option. You can use the %in hash array if you want to iterate over all the form fields. 5.4 Simple internet functions HTPL comes with lots of prewritten plain perl functions. Sending mail will be as the following: <# &sendmail (“We received your order”, From => ‘bill@microsoft.com’, To => ‘president@whitehouse.gov’, Subject => “Hello!”); > This will send a one line message to bill@microsoft.com, with the appropriate headers. You don’t needto construct the headers. Now, we can’t check for legal mail addresses, but we can check for the domain: <# unless (&validemail($mail)) { > The email $mail seems unreal! <# exit(0); > } &sendmail(< “sales\@microsoft.com” , To => $mail, Subject => “Hey!”); Hello $name, Your order has been received. EOM > Message sent. First, we see we could exit the document when we found out we had to. The validemail function works by validating the MX record and needs the Net::DNS module installed. Now, let’s create a document that gets a URL and redirects the browser, if the URL exists: You will see this only if the URL $url is invalid. <# if &validurl($url) &redirect($url); > The function validurl checks if the URL exists, using the LWP module. The function redirects exits the script, and outputs the redirection location instead of the script output. You can use the functions nslookup and revnslookup for name lookups. To set a cookie, use the setcookie function, for examples: Setcookie(“last-visit”, `date`, “last-ip”, $REMOTE_HOST); The variable REMOTE_HOST is automatically set with the browser’s hostname. Next time the document is loaded, the values of $cookies{‘last-visit’} and $cookies{‘last-ip’} will bset accordingly. Now, suppose we want to read a document from the web: <# unless (&validurl($url)) &redirect(“http://www.disney.com”); opendoc(INP, $url); while () { if (/disney/I) $count++; } > $count line(s) contained Disney. It’s easy as that. 5.5 HTML generation functions Here is something no fun to type:
Hello
And all this for one line! Now…. &html_format(“Hello”, “CENTER| TABLE WIDTH=400|TR|TD|B|CENTER”); Will do the same. The third argument can be set to a true value to have all the tags yielded in one line, and the fourth argument can be set to true value to have the HTML code returned but not printed. Now, arranging a list of elements in a table was never easier: Create a list first. @nums = map {&html_format($_, “I|U”, undef, 1);} (1 .. 100); Now, let’s arrange these in columns: &html_table_cols(items => \@nums, cols => 10, tattr => ‘WIDTH = 100%’); The tags tattr, cattr and rattr contain extra information for TABLE, TR and TD tags. Now, the eval tag causes the table functions to evaluate the strings given for tattr, cattr and rattr instead of plain using them. Try this: <# @nums = map {&html_format($_, "B|I|U", undef, 1);} (1 .. 100); &html_table_cols(items => \@nums, cols => 10, rattr => '($y % 2) ? "BGCOLOR=#C0C000" : "BGCOLOR=#FFFFFF"', cattr => '"ALIGN=" . (($x < 5) ? "RIGHT" : "LEFT")', eval => 1); > 5.6 Reading a comma delimited file All of us CGI programmers have done it…. Let’s use the /etc/passwd as input. (Yes, it is wrong to use it for a web page) We are going to now use one of the HTPL bundled macros: CSV <# #TEXT CSV users /etc/passwd : username password uid gid name #FETCH users print "“$username is $name
\"n”; #LOOP > This doesn’t look like perl! Basically, the first macro, “TEXT CSV” creates a result set called “users” (which will be realized using the variable $users), reading the file /etc/passwd, with the delimiter : while the rest of the parameters are the field names. An HTPL result set is always a table with field names and rows. The retrieval macros always load the result set with all the information and then proceed. Now, the #FETCH macro starts a loop, which ends with the #LOOP macro. Inside the loop, the fields are populated into the main namespace. The HTPL macros get converted by htpl.cgi into plain Perl code. The same page could be written as: “$username is $name > 5.7 Integrating an SQL database HTPL uses DBI to link external databases. You can connect to any dbi data source with the SQL CONNECT macro, for example: #SQL CONNECT dbi:Oracle:test sysdba secret There are simplified statements for connecting to several popular databases. In this example we will use a local mSQL database called mydb, and we will connect by: #SQL MSQL mydb This can be also written inside the HTML code as: Now, let’s do a simple SELECT. We'’ll assume the database mydb contains a table called customers with th fields firstname, lastname and email. <# #SQL MSQL mydb #SQL CURSOR customers SELECT * FROM customers #IFNULL customers print “We are sorry, but no customers were found.
”; #ELSE > <# #FETCH customers print “”; #LOOP print “
First name Last name Email
$firstname$lastname$email
\n”; #ENDIF > What happened here? We connected to the database, and performed a SELECT query, into a result set called customers. We checked if the result set was empty. If it was not, we dumped the results. According to the broad trend, you can access the database without writing SQL code. (Obviously writing SQL code will provide many more options) For example: #SQL QUERY myquery mytable firstname lastname Will provide the same as: #SQL CURSOR myquery SELECT * FROM mytable WHERE firstname = ‘$firstname’ AND lastname = ‘$lastname’ Important: HTPL assumes the new versions of DBI, therefore when using the non script SQL access, you save the need to quote strings and to escape quotes. #SQL INSERT mytable firstname lastname email Will be the same as: #SQL EXEC INSERT INTO mytable (firstname, lastname, email) VALUES (‘$firstname’,‘$lastname’, ‘$email’) 5.8 Some more useful macros Integrating a textual counter to your page as as easy as this: people visited my page. Random quotes were never easier: Man existence must be some form of mistake. Where do you want to go today
One world, no languages Or usual switch statement: #SWITCH CASE $s #CASE 'abc' &proc1(); #CASE 2 + $j &proc2(); #END SWITCH Or a random image: 6. Reference 6.1 HTPL Library functions The source code for these functions is in the file htpl-lib.pl &addheader(HTTP_HEADER) Adds an HTTP header. Unavailable for converted scripts executed outside htpl.cgi. &closedoc(HANDLE) Closes file handle HANDLE and disposes the local copy if needed. To be used with &opendoc. &doconnect(HANDLE, HOST, PORT) Opens socket to HOST:PORT on handle HANDLE. HANDLE should be used with &doread and &dowrite and not standard input/output funcation. &doread(HANDLE) Reads all the incoming data on a socket and returns as a scalar value. &dowrite(HANDLE, BUFFER) Writes the data on the scalar value BUFFER to the socket HANDLE. &expect(HANDLE, REGEXP) &expect(HANDLE, REGEXP, CODE) Reads incoming data and matches against REGEXP. If successful, returns the regular expression results. If not, calls the subroutine referenced by CODE where the input packet is passed to $_ &fileexists(FILENAME) Checks if FILENAME exists. If FILENAME is a valid URL, checks if the URL exists. &forkredirect &forkredirect(LOCATION) Redirects the browser to LOCATION while keeping the script running in background. Useful for batch processing. Redirection unavailable for converted scripts executed outside htpl.cgi. If LCOATION is omitted, exits and returns the script output, while background copy still runs. &getcc Attempts to find the C compiler. Tries in the usual locations, unless the default has been edited in htpl-config.pl. &getmailprog Attempts to find the mail program. Same mechanism as getcc. &html_format(TEXT, TAGS) &html_format(TEXT, TAGS, NONL, NOOUTPUT) TAGS is a cons delmited list of HTML tags to apply, with no < > chars. TEXT is formatted with the TAGS, while closing tags are added automatically. Set NONL to true if you want all the output in one line. Normally html_format yields the HTML code. Setting NOOUTPUT to true will make it only return the code. &html_header(TITLE) Prints , and tags according to the TITLE supplied. &html_hidden(FIELD) &html_hidden(FIELD, VALUE) &html_hidden(FIELD, VALUE, NOOUT) Prints a hidden <INPUT> field. Usually used to transfer parameters between pages. If VALUE is omitted, the value of the scalar variable with the name of FIELD is used. Set NOOUT to true to get the code returned without printing it. &html_table_cols(ATTRIBUTES) &html_table_rows(ATTRIBUTES) Formats a list of values in an HTML table. ATTRIBUTES are given as key => value pairs. Attributes are case insensitive. For columns divided table, attribute cols should contain the column number. For rows divided table, the attribute rows should be supplied. The attributes tattr, rattr and cattr are used to specify the extra code for TABLE, TR and TD tags in the table, to control alignment, etc. Normally, they contain the string to include. If the attribute eval is set to true, the contents of the attributes above are evaluate. The code can use the variables $x, $y, $data to inspect the cell before returning values. Individual cells can be given different values, by using a hash reference instead of a scalar value ad the cell value. The hash should contain: ? Data – the real cell data ? Header – true if the cell should be formatted with TH and not TD ? cattr – alternative attributes for the cell. Ignored if not set or if null. At last, the attribute noout can be set, so the html code is returned without being printed. &include(FILENAME) Dump a text file. FILENAME can be a URL. &inputlist(ARRAYREF, ATTRIBUTES) Returns a reference to an array of HTML code entries rendering a list of checkboxes or radio buttons. ARRAYREF should point to an array of referenced hash tables, for each the following attributes have to appear: ? value – Value for the VALUE attribute of the INPUT tag ? text – text to put near the input element ATTRBIUTES are pairs of key => value that describe the input list. The possible attributes are: ? name – Name of input fields. Mandatory. ? Default – Value or a reference to an array of values of input fields that will be marked as CHECKED ? OnCheck – Javascript code for onCheck event ? Attr – Additional attributes for INPUT tag &isip(IP) Returns true if IP is in IP address format. Doesn’t validate contents. &isurl(STRING) Checks is STRING is composed as a URL. To check validity of the URL use &validurl. &max(LIST) Returns the highest element of LIST, numeric wise. &min(LIST) Returns the lowest element of LIST, numeric wise. &nslookup(HOSTNAME) Uses gethostbyname() to resolve HOSTNAME. Returns undef if lookup fails. &opendoc(HANDLE, FILENAME) Opens HANDLE to read FILENAME. If FILENAME is a URL, the document is fetched and then opened from a local copy. Handles opened with &opendoc should be closed with &closedoc to ensure erasure of temporary files. The filename to use is given by &tempfilename &publish(HASH) For each in HASH, the value is copied to variables in the main namespaces with names identical to the key; both scalar and array values. In htpl.head, this brings the CGI variables to the main namespace. &readfile(FILENAME) Reads the file named FILENAME into a scalar value. FILENAME may be a URL. &redirect(URL) Erases the output of the script and redirects the client. Any data sent to the HTTP headers (cookies etc) won’t be erased except MIME TYPE info. Unavailable for converted scripts executed outside htpl.cgi. &rewind Clear the output of the script. Unavailable when script runs outside of htpl.cgi. &revnslookup(IP) Attempts to find hostname for IP. &selectbox(DEFAULT, PAIRS) &selectbox(HASHREF, PAIRS) Prints <OPTION> tags . If the first argument is a scalar, &selectbox only prints <OPTION> tags and not <SELECT> tags. PAIRS contains VALUE values for the tags and content information in pairs. The option with value equivalent to DEFAULT is marked SELECTED. DEFAULT can be undef if no default is required. (undef and not a blank string) DEFAULT can an array reference, to select several options as default for multiple select boxes. If the first argument is a hash reference, it should contain the following fields: ? Default – default values. Can be ommited. ? Name – Name of the field. <SELECT> tag will be printed ? Attr – Additional attributes for SELECT tag ? Noout – If set to true, HTML code will be returned but not printed &sendmail(TEXT, ATTRIBUTES) Sends a mail message by spawning a sendmail client process. TEXT is the message, without ARPA mail message headers. Headers will be created automatically by the program. ATTRIBUTES is a list of key => value pairs, as follows: ? To: Address of recipent. Can be either user@host, user@host “name” or name user@host ? From: Address of sender. Same format. ? Subject: Subject. ? Reply-To: Reply to address. Optional. Any other attributes will be added as mail message headers. The location of sendmail is taken from the function &getmailprog. &setcookie(PAIRS) Adds persistent cookies. PAIRS is an array of pairs of cookie names and values. Unavailable when script runs outside of htpl.cgi. &setmimetype(STRING) Changes the MIME type of the document. Unavailable when script runs outside of htpl.cgi. To write a script that returns images, use &setmimetype, then &rewind and after that binmode STDOUT and send the image to STDOUT. &takelog(STRING) Write a log entry to the default log file. The default log file for a script is the script name with the extension log, unless the variable $default_log_file points to a lof file name. &tempfilename Creates a temporary filename, unified by the script name, the PID, parent PID, time, index of calls and per session. That should completely suffice. The filename is preceded by ~~ to note a disposable file. If the environment variable TEMP is set, the directory information contained in it is preceded to the filename. &trim(STRING) Cuts leading and trailing spaces, and removes double spaces. Returns the modified string. &txt2html(TEXT) Formats a string with line breaks, ampersands, > and < signs, and double quotes to HTML code. &urldecode(STRING) Decodes a string from an HTTP query. &urlencode(STRING) Encodes a string for HTTP queries. &validemail(ADDRESS) Checks if an email address is formed correctly, and if so, checks for the availability of a mail exchanger for the address. Returns undef if fails, true if succeeds. &validurl(ADDRESS) Checks if a URL is formed correctly, if so, checks for its existence. Returns undef if fails, true if succeeds. 6.2 HTPL result set All the HTPL macros for information retrieval return their values via the HTPL result set class. This class is defined in the file htpl_result.pm The information retrieval macros are implemented with packages creating an instance of htpl_result Interface follows. Class htpl_result { constructor new(String Fields[]); # Gets the list of columns in the result set. # Returns a blessed reference void addrow(String cells[]); # Gets a list of values, ordered according to # the field list. Adds a row to the result set. bool isnone(); # Returns true if there are no rows in the result set bool fetch(); # Populates the main namespace with scalar # variables named like the result set fields # with values from the current row. Then advances 1 row # Returns undef if past end of result set. bool unfetch(); # Simillar to fetch, but moves backwards. bool access(int row); # Accesses a specific row. Returns undef if row does not # exist bool eof(); # Returns true if cursor is past end of result set. bool bof(); # Returns true if cursor is past the beginning of result set. I int index(); # Returns the index of current row int rows(); # Returns the number of rows String cols()[]; # Returns the names of the fields String getcol(int colnumber); # Returns one value of the current row. # The number of the field is zero based String get(String colname); # Returns one value, based on field name. void rewind(); # Sets the cursor on the first row and retrieves it htpl_result filter(bool code()); # Creates a subset of the result set for rows where # the code referenced returns true } HTPL comes with a class called htpl_db.pm used for SQL access. The class is used with the bundled SQL macros. There are also experimental classes for LDAP and XML – htpl_dir.pm and htpl_xml.pm. The classes for text files – htpl_csv.pm for comma delimited files and htpl_flat.pm for text files are also supported. A class for WAIS access is under development. 6.3 HTPL macros Macros are specified in HTPL source as pseudo comments. Lines beginning with a # and no spaces following before alphanumeric content will be matched for macros. If a macro was matched, the perl output file will be commented accordingly and the macro will be resolved. If the macro instance does not contain a > (greater-than) sign or an odd number of double quotes, it can be written inside a tag: <# #MACRO > becomes <HTMACRO> For example: <HTQUERY crs tbl> <HTFETCH crs> $name <HTLOOP> You can define as many macros as you like. See section 6.5. The following macros are bundled with HTPL: 1. SQL 1.1 CONNECT #SQL CONNECT <dbi-dsn> <username> <password> Connects to a database. Specific database: 1.2 MYSQL #SQL MYSQL <database> <username> <password> 1.3 MSQL #SQL MSQL <database> 1.4 XBASE #SQL XBASE <directory> Uses the dbi::xbase module 1.5 POSTGRESQL #SQL POSTGRESQL <database> <username> <password> 1.6 EXEC / EXECUTE #SQL EXECUTE <dml or ddl> Executes the SQL statements. 1.7 CURSOR / SEARCH #SQL CURSOR <result set name> <sql query> Runs a query, creates a result set object. 1.8 INSERT / ADD #SQL INSERT <table name> <list of columns> Inserts a new row. Values are taken from the corresponding scalar variables. Uses type casting of new DBI modules. 1.9 QUERY #SQL QUERY <result set name> <table> <list of constraint columns> Retrieves a table with a simple filter – each column that appears in the constraint list is matched against the corresponding scalar variable in the main namespace. 1.10 UPDATE / MODIFY #SQL UPDATE <table name> <list of columns> WHERE <list of constraint columns> Updates the specified fields, using the specified constraints to match the record. 2. TEXT All text operations can receive a URL for a filename. 2.1 CSV #TEXT CSV <result set name> <source text file> <delimiter> <list of field names> Reads a delimited file, using the module Text::ParseWords. Tokens can be quoted, which escapes the delimiter, and quotes can be escaped. If the list of fields is not supplied, the first line of the file is read and represents the field names. 2.2 FLAT #TEXT FLAT <result set name> <source text file> <list of field names> Reads a flat file in which every record is terminated by a blank line. Each line in each record is a field. 2.3 CUBE #TEXT CUBE <result set name> <source text file> <column delimiter> <row delimiter> <list of field names> Parses a text file with both kinds of delimiters. 2.4 READ #TEXT READ <variable> <filename> Reads the file into the scalar variable. 3. Result set aliases 3.1 FETCH, LOOP #FETCH <result set name> … code … #LOOP Generates a loop over the code block for each row in the result set. The fields are published into the main namespace. 3.2 FETCHIT #FETCHIT <result set name> Publishes the fields without generating a loop. Useful for queries that need to retrieve only one row. 3.3 FETCHCOLS #FETCHCOLS <result set name> <var name> The variable (notated with no $ sign) is iterated via all the column names. 3.4 FETCHCELL #FETCHCELL <result set name> <field name> <var> Fetches one cell. Note, if the field name is stored in a variable, use a $ to evaluate it. 3.5 IFNULL, IFNOTNULL, ENDIF, ELSE #IFNULL <result set name> #ENDIF #IFNOTNULL <result set name> #ELSE #ENDIF Open conditional blocks depending on whether there are any rows on the result set. 3.6 FILTER <source result set> <target result set> <condition source code> Creates a subset of the result set. The condition is supplied as a string and contains a boolean check. 6.4 HTPL directives Several HTPL meta commands are hard coded into the source. The LDAP meta commands are experimental: #LDAP CONNECT SERVER <server> [PORT <port>] [BIND <bind dn>] [PASSWORD <pass>] #LDAP SEARCH [SCOPE <scope>] [FILTER <filter>] [START <start dn>] [SORT <sortkey>] [ATTRIBUTES <attribute list>] [SIZE <sizelimit>] To include HTPL source, (usually with the extension hh): #INCLUDE filename To embed XSUB code: #XSUB … C functions … #ENDXSUB You don’t need to create module definition. If you include XSUB code, you will have to compile the document offline by accessing it and letting the system compile it on the background. Example: <# void hello CODE: printf(“Shalom chaver!\n”); #ENDXSUB &hello; 6.5 Writing your own macros The file htpl.subs contain an XML tree of the macros. The father node is called HTPL. The children are the macro names. Macros that are related are grouped, like the SQL and TEXT macros. Those will have nodes with the group names with children for the macros. Example: <HTPL> <HELLO>$s = “Goodbye, friend”;</HELLO> <WORLD>print “$s\n”;</WORLD> </HTPL> This macro tree will enable us to write the code: #HELLO #WORLD And have it subsituted for: $s = “Goodbye, friend”; print “$s\n”; Example: <HTPL> <HELLO> <WORLD>$s = “Goodbye, friend”;</WORLD> <BAMBA>$s = “Father, mother, bamba”;</BAMBA> </HELLO> </HTPL> Now we have the macros: #HELLO WORLD #HELLO BAMBA Macros with parameters: The tokens that follow the macro unification are used as arguments at run time. Arguemnts are number 1 based. References to arguments are by numbers inside %’s. <PRINT>print “%1%<BR>\n”;</PRINT> Now we can command: #PRINT Hello Which will be substituted for: print “Hello<BR>\n”; We can also capture a row of arguments from a specific one until the last one, for example: <SHOW>print “%1% >> %2*%”;</SHOW> #SHOW 1 2 3 Will be equal to: print “1 >> 2 3”; Some macro groups have prerequisites. For example, the SQL macros must have the htpl_db package loaded. We can define a prerequisite this way: <LOOKUP>($%1%) = gethostbyname(“%2%”); <__PRE>use Socket;</__PRE></LOOKUP> All the tags that begin with two underscores are special. Sometimes we might want to alias a macro: <PRINT>die “%1*%\n” unless ($fork);</PRINT> <HELLO><__ALIAS>PRINT Good bye, friend!</__ALIAS></HELLO> #HELLO will be the same as: die “Good bye, friend!” unless ($fork); We can also define a macro with several steps: <COUNT> <__DO>printf “1”;</__DO> <__DO>printf “2”;</__DO> </COUNT> In such a macro we can include other macros – <COUNT> <__DO>printf “1”;</__DO> <__DO>printf “2”;</__DO> <__INCLUDE>HELLO</__INCLUDE> </COUNT> We might want some macros to be available only inside the aliasing system, so we can define them as private: <PRINT PRIVATE="”"”>die “%1*%”;</PRINT> <HELLO><__ALIAS>PRINT end of all</__ALIAS></HELLO> We might want to define a non operational macro, for example just to hold a prerequisite: <INCLUDEDBI NOOP="”"”><__PRE>use DBI;</__PRE></INCLUDEDBI> <ORA> <__INCLUDE>INCLUDEDBI</__INCLUDE> <__DO>print “D’oh!\n”;</__DO> </ORA> If we want to define variables, we can use the SCOPE attribute to ease adding of brackets before and after the code, for scoping needs: <YEAR SCOPE="1">my @t = localtime; my $y = $t[5]; my $yy = $y + 1900 + ($y < 70 ? 100 : 0); print $yy</YEAR> Using macros as HTML tags: A macro defined as <X>perl commands</X> can be called either by writing #X in the middle of your perl code, or by embedding the psudo HTML tag <HTX> inside your HTML code. The disadvantage of the second option is that the < and > signs may not be used, as they would confuse the parser. Tags beginnig with HTX can be "broken" among lines. Closing tags can also be implementing. </HTX> would be the same as #END X The END macro is bundled with HTPL. If you want to define a tag X with both an opener and a closer, do: <X AREA="1"> <__FWD>print "begin tag\n";</__FWD> <__REV>print "end tag\n";</__REV> </X> In order to process attributes of SGML tags, use the PARAMS attribute. For example: <PRINT PARAMS="1">print $text;</PRINT> And use like: <HTPRINT TEXT="text here"> In order to check for mandatory attributes, use the MANDATORY attribute. For example: <MYIMG PARAMS="1" MANDATORY="SRC, BORDER"> Areas of HTPL code can be scoped with tags. You can capture the HTPL output between tags with the &begintransaction and &gettransaction functions. For example: <BEGIN>&begintransaction();</BEGIN> <STOP>$txt = &endtransaction; print uc($txt);</STOP> And use like: <BEGIN>This is going to be in upper case</BEGIN> In order to make sure tag scopes are not overlapping, you can make a tag demand being in a scope, using a scope stack. For example: <BEGIN PUSH="begin end">print "begin";</BEGIN> <END POP="begin end">print "end";</END> The begin tag would push a "begin end" state to the scope stack. The end tag would make sure we are in that state, and pop it from the stack. It can be easier if we just define a pair of <HTX> - </HTX> or #X - #END X <X BLOCK="x scope" AREA="1"> <__FWD>&beginx;</__FWD> <__REV>&endx;</__REV> </X> Escaping: You should escape ampersands in the htpl.subs as &, < as < and > as > as with any XML file. If you wish the % sign to appear literally in the expanded macro, double it, for example: &myproc() if (power(%%hash) > 0); 6.6 Additional information ? Any script initializes with the form variables brought to the main namespace. ? The variables REMOTE_HOST and REMOTE_USER contain the corresponding CGI variables. ? The file htpl-config.pl resides in the binary directory and contains miscellaneous site oriented configurations. If the functions getcc and getmailprog fail at your site, you can edit defaults at the configuration file. Same for maximum size for HTTP upload file. ? If the directory holding the document has a plain Perl file called htpl-glob.pl, it will be consulted for application based configuration. ? If the directory contains an HTPL file called htpl-glob.hh, it will be included, to supply application based functions. ? Incoming HTTP cookies will be stored in the %cookies hash table. ? Debug information can be displayed by simply accessing the htpl.cgi binary ? A binary called htpldbg is created that only converts the document to a perl script and dumps the scrip to the standard output. It can be useful to convert your script to pure CGI if you need to immigrate, and for debugging your scripts. ? If you can’t write in /tmp but can write somewhere else, edit the TEMP entry in the Makefile. ? When the perl script fails, htpl.cgi captures the error dump, and displays a page with the output dump, temporary filenames, error dump and convert source, to help keep track of line numbers. After you finished working on your application and are not interested in the source to appear with error dumps, create a file called htpl.nodbg with no content in the source dir. Scripts in that dir will not have source attached when failing.