/*
* The contents of this file are subject to the AOLserver Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://aolserver.com/.
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
* the License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is AOLserver Code and related documentation
* distributed by AOL.
*
* The Initial Developer of the Original Code is America Online,
* Inc. Portions created by AOL are Copyright (C) 1999 America Online,
* Inc. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License (the "GPL"), in which case the
* provisions of GPL are applicable instead of those above. If you wish
* to allow use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under the
* License, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the License or the GPL.
*/
/*
* adpparse.c --
*
* ADP parser.
*/
static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/adpparse.c,v 1.21 2006/06/19 15:01:41 jgdavidson Exp $, compiled: " __DATE__ " " __TIME__;
#include "nsd.h"
#define SERV_STREAM 1
#define SERV_RUNAT 2
#define SERV_NOTTCL 4
#define TAG_ADP 1
#define TAG_PROC 2
#define TAG_SCRIPT 3
#define TAG_SERVER 4
#define APPEND "ns_adp_append "
#define APPEND_LEN (sizeof(APPEND)-1)
#define LENSZ (sizeof(int))
/*
* The following structure maintains proc and adp registered tags.
* String bytes directly follow the Tag struct in the same allocated
* block.
*/
typedef struct Tag {
int type; /* Type of tag, ADP or proc. */
char *tag; /* The name of the tag (e.g., "mytag") */
char *endtag; /* The closing tag or null (e.g., "/mytag")*/
char *string; /* Proc (e.g., "ns_adp_netscape") or ADP string. */
} Tag;
/*
* The following strucutre maintains state while parsing an ADP block.
*/
typedef struct Parse {
AdpCode *codePtr; /* Pointer to compiled AdpCode struct. */
int line; /* Current line number while parsing. */
Tcl_DString lens; /* Length of text or script block. */
Tcl_DString lines; /* Line number of block for debug messages. */
} Parse;
/*
* Local functions defined in this file
*/
static void AppendBlock(Parse *parsePtr, char *s, char *e, int type);
static void AppendTag(Parse *parsePtr, Tag *tagPtr, char *as, char *ae,
char *se);
static int RegisterObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv, int type);
static void Blocks2Script(AdpCode *codePtr);
static void AppendLengths(AdpCode *codePtr, int *lens, int *lines);
static void GetTag(Tcl_DString *dsPtr, char *s, char *e, char **aPtr);
static char *GetScript(char *tag, char *a, char *e, int *streamPtr);
/*
*----------------------------------------------------------------------
*
* Ns_AdpRegisterParser --
*
* Register an ADP parser (no longer supported).
*
* Results:
* NS_ERROR.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
int
Ns_AdpRegisterParser(char *extension, Ns_AdpParserProc *proc)
{
return NS_ERROR;
}
/*
*----------------------------------------------------------------------
*
* NsTclAdpRegisterAdpObjCmd, NsTclAdpRegisterProcObjCmd,
* NsTclAdpRegisterScriptObjCmd --
*
* Register an proc, script, are ADP string tag.
*
*
* Results:
* Standard Tcl result.
*
* Side effects:
* An ADP tag may be added to the hashtable.
*
*----------------------------------------------------------------------
*/
int
NsTclAdpRegisterAdpObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv)
{
return RegisterObjCmd(arg, interp, objc, objv, TAG_ADP);
}
int
NsTclAdpRegisterProcObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv)
{
return RegisterObjCmd(arg, interp, objc, objv, TAG_PROC);
}
int
NsTclAdpRegisterScriptObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv)
{
return RegisterObjCmd(arg, interp, objc, objv, TAG_SCRIPT);
}
static int
RegisterObjCmd(ClientData arg, Tcl_Interp *interp, int objc,
Tcl_Obj **objv, int type)
{
NsInterp *itPtr = arg;
NsServer *servPtr = itPtr->servPtr;
char *string, *end, *tag;
Tcl_HashEntry *hPtr;
int new, slen, elen, tlen;
Tcl_DString tbuf;
Tag *tagPtr;
if (objc != 4 && objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "tag ?endtag? [adp|proc]");
return TCL_ERROR;
}
string = Tcl_GetStringFromObj(objv[objc-1], &slen);
++slen;
if (objc == 3) {
end = NULL;
elen = 0;
} else {
end = Tcl_GetStringFromObj(objv[2], &elen);
++elen;
}
tagPtr = ns_malloc(sizeof(Tag) + slen + elen);
tagPtr->type = type;
tagPtr->string = (char *) tagPtr + sizeof(Tag);
memcpy(tagPtr->string, string, (size_t) slen);
Tcl_UtfToLower(tagPtr->string);
if (end == NULL) {
tagPtr->endtag = NULL;
} else {
tagPtr->endtag = tagPtr->string + slen;
memcpy(tagPtr->endtag, end, (size_t) elen);
Tcl_UtfToLower(tagPtr->endtag);
}
Tcl_DStringInit(&tbuf);
tag = Tcl_GetStringFromObj(objv[1], &tlen);
Tcl_UtfToLower(Tcl_DStringAppend(&tbuf, tag, tlen));
Ns_RWLockWrLock(&servPtr->adp.taglock);
hPtr = Tcl_CreateHashEntry(&servPtr->adp.tags, tbuf.string, &new);
if (!new) {
ns_free(Tcl_GetHashValue(hPtr));
}
Tcl_SetHashValue(hPtr, tagPtr);
tagPtr->tag = Tcl_GetHashKey(&servPtr->adp.tags, hPtr);
Ns_RWLockUnlock(&servPtr->adp.taglock);
Tcl_DStringFree(&tbuf);
return TCL_OK;
}
/*
*----------------------------------------------------------------------
*
* NsAdpParse --
*
* Parse a string of ADP text/script. Parsing is done in a single,
* top to bottom pass, looking for the following four types of
* embedded script sequences:
*
* 1. <% tcl script %>
* 2.
* 3.
* 4. text
*
* Nested sequences are handled for each case, for example:
*
* Text <% ns_adp_eval {<% ... %>} %> text ...
*
* Results:
* None.
*
* Side effects:
* Given AdpCode structure is initialized and filled in with copy
* of parsed ADP.
*
*----------------------------------------------------------------------
*/
void
NsAdpParse(AdpCode *codePtr, NsServer *servPtr, char *adp, int flags)
{
int level, stream, streamdone;
Tag *tagPtr;
char *script, *s, *e, *n;
char *a, *as, *ae, *text;
Tcl_DString tag;
Tcl_HashEntry *hPtr;
enum {
TagNext,
TagScript,
TagReg
} state;
Parse parse;
/*
* Initialize the code and parse structures.
*/
Tcl_DStringInit(&codePtr->text);
codePtr->nscripts = codePtr->nblocks = 0;
parse.line = 0;
parse.codePtr = codePtr;
Tcl_DStringInit(&parse.lens);
Tcl_DStringInit(&parse.lines);
/*
* Parse ADP one tag at a time.
*/
text = adp;
streamdone = stream = 0;
level = 0;
state = TagNext;
Tcl_DStringInit(&tag);
Ns_RWLockRdLock(&servPtr->adp.taglock);
while ((s = strchr(adp, '<')) && (e = strchr(s, '>'))) {
/*
* Process the tag depending on the current state.
*/
switch (state) {
case TagNext:
/*
* Look for a <% ... %> sequence.
*/
if (s[1] == '%' && s[2] != '>') { /* NB: Avoid <%>. */
/*
* Find the cooresponding %> beyond any additional
* nested <% ... %> sequences.
*/
level = 0;
e = strstr(e - 1, "%>");
n = s + 2;
while (e != NULL && (n = strstr(n, "<%")) != NULL && n < e) {
++level;
n = n + 2;
}
while (e != NULL && level > 0) {
e = strstr(e + 2, "%>");
--level;
}
if (e == NULL) {
/*
* No matching %> found. Append text and invalid
* opening <% before searching for next ADP tag.
*/
AppendBlock(&parse, text, s + 2, 't');
text = s + 2; /* NB: Next text after invalid opening <%. */
} else {
/*
* Append text block followed by script block unless
* in safe mode which suppresses in-line scripts and
* continue looking for next ADP tag.
*/
AppendBlock(&parse, text, s, 't');
if (!(flags & ADP_SAFE)) {
if (s[2] != '=') {
AppendBlock(&parse, s + 2, e, 's');
} else {
AppendBlock(&parse, s + 3, e, 'S');
}
}
text = e + 2; /* NB: Next text after valid closing %>. */
}
s = text - 1; /* NB: Will incr +1, past text, below. */
} else {
/*
* Check for tag.
*/
AppendBlock(&parse, text, s, 't');
state = TagScript;
level = 1;
} else {
hPtr = Tcl_FindHashEntry(&servPtr->adp.tags, tag.string);
if (hPtr != NULL) {
/*
* Append text and the registered tag content
* if the tag does not require a closing tag.
* Otherwise, save the tag attribute offsets
* and begin looking for required closing tag.
*/
AppendBlock(&parse, text, s, 't');
tagPtr = Tcl_GetHashValue(hPtr);
if (tagPtr->endtag == NULL) {
AppendTag(&parse, tagPtr, a, e, NULL);
text = e + 1;
} else {
as = a;
ae = e;
level = 1;
state = TagReg;
}
}
}
}
break;
case TagScript:
/*
* Look for cooresponding closing tag, handling
* possible nesting of other