static int buildScript(EspRequest *ep, char **jsBuf, char *input, char **errMsg) { EspParse parse; char path[MPR_MAX_FNAME], dir[MPR_MAX_FNAME], incPath[MPR_MAX_FNAME]; char *incBuf, *incText; int state, tid, len, rc, maxScriptSize, incSize; mprAssert(ep); mprAssert(jsBuf); mprAssert(input); rc = 0; len = 0; state = ESP_STATE_BEGIN; if (errMsg) { *errMsg = 0; } memset(&parse, 0, sizeof(parse)); parse.token = (char*) mprMalloc(ESP_TOK_INCR); if (parse.token == 0) { return MPR_ERR_CANT_ALLOCATE; } parse.token[0] = '\0'; parse.tokLen = ESP_TOK_INCR; parse.endp = &parse.token[parse.tokLen - 1]; parse.tokp = parse.token; parse.inBuf = input; parse.inp = parse.inBuf; maxScriptSize = esp->maxScriptSize; tid = getEspToken(state, &parse); while (tid != ESP_TOK_EOF && len >= 0) { switch (tid) { default: case ESP_TOK_ERR: mprFree(parse.token); return MPR_ERR_BAD_SYNTAX; case ESP_TOK_LITERAL: len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, "write(\"", parse.token, "\");\n", (void*) 0); break; case ESP_TOK_ATAT: /* * Trick to get undefined variables to evaluate to "". * Catenate with "" to cause toString to run. */ len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, "write(\"\" + ", parse.token, ");\n", (void*) 0); break; case ESP_TOK_EQUALS: len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, "write(\"\" + ", parse.token, ");\n", (void*) 0); state = ESP_STATE_IN_ESP_TAG; break; case ESP_TOK_START_ESP: state = ESP_STATE_IN_ESP_TAG; tid = getEspToken(state, &parse); while (tid != ESP_TOK_EOF && tid != ESP_TOK_EOF && tid != ESP_TOK_END_ESP && len >= 0) { len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, parse.token, (void*) 0); tid = getEspToken(state, &parse); } state = ESP_STATE_BEGIN; break; case ESP_TOK_END_ESP: state = ESP_STATE_BEGIN; break; case ESP_TOK_INCLUDE: if (parse.token[0] == '/') { mprStrcpy(incPath, sizeof(incPath), parse.token); } else { mprGetDirName(dir, sizeof(dir), ep->uri); mprSprintf(incPath, sizeof(incPath), "%s/%s", dir, parse.token); } if (esp->mapToStorage(ep->requestHandle, path, sizeof(path), incPath, 0) < 0) { mprAllocSprintf(errMsg, MPR_MAX_STRING, "Can't find include file: %s", path); rc = MPR_ERR_CANT_OPEN; break; } if (esp->readFile(ep->requestHandle, &incText, &incSize, path) < 0) { mprAllocSprintf(errMsg, MPR_MAX_STRING, "Can't read include file: %s", path); rc = MPR_ERR_CANT_READ; break; } incText[incSize] = '\0'; /* * Recurse and process the include script */ incBuf = 0; if ((rc = buildScript(ep, &incBuf, incText, errMsg)) < 0) { mprFree(incText); mprFree(parse.token); return rc; } len = mprReallocStrcat(jsBuf, maxScriptSize, len, 0, incBuf, (void*) 0); mprFree(incText); mprFree(incBuf); state = ESP_STATE_IN_ESP_TAG; break; } tid = getEspToken(state, &parse); } mprFree(parse.token); if (len < 0) { mprAllocSprintf(errMsg, MPR_MAX_STRING, "Script token is too big in %s.\nConfigured maximum is %d.", path, maxScriptSize); return MPR_ERR_WONT_FIT; } return rc; }
/* Convert an ESP web page into C code Directives: <%@ include "file" Include an esp file <%@ layout "file" Specify a layout page to use. Use layout "" to disable layout management <%@ content Mark the location to substitute content in a layout pag <% Begin esp section containing C code <%^ global Put esp code at the global level <%^ start Put esp code at the start of the function <%^ end Put esp code at the end of the function <%= Begin esp section containing an expression to evaluate and substitute <%= [%fmt] Begin a formatted expression to evaluate and substitute. Format is normal printf format. Use %S for safe HTML escaped output. %> End esp section -%> End esp section and trim trailing newline @@var Substitue the value of a variable. Var can also be simple expressions (without spaces) @#field Lookup the current record for the value of the field. */ char *espBuildScript(HttpRoute *route, cchar *page, cchar *path, cchar *cacheName, cchar *layout, char **err) { EspParse parse; char *control, *incBuf, *incText, *global, *token, *body, *where; char *rest, *start, *end, *include, *line, *fmt, *layoutPage, *layoutBuf; ssize len; int tid; mprAssert(page); body = start = end = global = ""; *err = 0; memset(&parse, 0, sizeof(parse)); parse.data = (char*) page; parse.next = parse.data; if ((parse.token = mprCreateBuf(-1, -1)) == 0) { return 0; } tid = getEspToken(&parse); while (tid != ESP_TOK_EOF) { token = mprGetBufStart(parse.token); #if FUTURE if (parse.lineNumber != lastLine) { body = sfmt("\n# %d \"%s\"\n", parse.lineNumber, path); } #endif switch (tid) { case ESP_TOK_CODE: if (*token == '^') { for (token++; *token && isspace((int) *token); token++) ; where = stok(token, " \t\r\n", &rest); if (rest == 0) { ; } else if (scmp(where, "global") == 0) { global = sjoin(global, rest, NULL); } else if (scmp(where, "start") == 0) { if (*start == '\0') { start = " "; } start = sjoin(start, rest, NULL); } else if (scmp(where, "end") == 0) { if (*end == '\0') { end = " "; } end = sjoin(end, rest, NULL); } } else { body = sjoin(body, token, NULL); } break; case ESP_TOK_CONTROL: /* NOTE: layout parsing not supported */ control = stok(token, " \t\r\n", &token); if (scmp(control, "content") == 0) { body = sjoin(body, CONTENT_MARKER, NULL); } else if (scmp(control, "include") == 0) { if (token == 0) { token = ""; } token = strim(token, " \t\r\n\"", MPR_TRIM_BOTH); token = mprNormalizePath(token); if (token[0] == '/') { include = sclone(token); } else { include = mprJoinPath(mprGetPathDir(path), token); } if ((incText = mprReadPathContents(include, &len)) == 0) { *err = sfmt("Can't read include file: %s", include); return 0; } /* Recurse and process the include script */ incBuf = 0; if ((incBuf = espBuildScript(route, incText, include, NULL, NULL, err)) == 0) { return 0; } body = sjoin(body, incBuf, NULL); } else if (scmp(control, "layout") == 0) { token = strim(token, " \t\r\n\"", MPR_TRIM_BOTH); if (*token == '\0') { layout = 0; } else { token = mprNormalizePath(token); if (token[0] == '/') { layout = sclone(token); } else { layout = mprJoinPath(mprGetPathDir(path), token); } if (!mprPathExists(layout, F_OK)) { *err = sfmt("Can't access layout page %s", layout); return 0; } } } else { *err = sfmt("Unknown control %s at line %d", control, parse.lineNumber); return 0; } break; case ESP_TOK_ERR: return 0; case ESP_TOK_EXPR: /* <%= expr %> */ if (*token == '%') { fmt = stok(token, ": \t\r\n", &token); if (token == 0) { token = ""; } } else { fmt = "%s"; } token = strim(token, " \t\r\n;", MPR_TRIM_BOTH); body = sjoin(body, " espRender(conn, \"", fmt, "\", ", token, ");\n", NULL); break; case ESP_TOK_FIELD: /* @#field */ token = strim(token, " \t\r\n;", MPR_TRIM_BOTH); body = sjoin(body, " espRender(conn, getField(\"", token, "\"));\n", NULL); break; case ESP_TOK_LITERAL: line = joinLine(token, &len); body = sfmt("%s espRenderBlock(conn, \"%s\", %d);\n", body, line, len); break; case ESP_TOK_VAR: /* @@var */ token = strim(token, " \t\r\n;", MPR_TRIM_BOTH); /* espRenderParam uses espRenderSafeString */ body = sjoin(body, " espRenderParam(conn, \"", token, "\");\n", NULL); break; default: return 0; } tid = getEspToken(&parse); } if (cacheName) { /* CacheName will only be set for the outermost invocation */ if (layout && mprPathExists(layout, R_OK)) { if ((layoutPage = mprReadPathContents(layout, &len)) == 0) { *err = sfmt("Can't read layout page: %s", layout); return 0; } layoutBuf = 0; if ((layoutBuf = espBuildScript(route, layoutPage, layout, NULL, NULL, err)) == 0) { return 0; } body = sreplace(layoutBuf, CONTENT_MARKER, body); } if (start && start[slen(start) - 1] != '\n') { start = sjoin(start, "\n", NULL); } if (end && end[slen(end) - 1] != '\n') { end = sjoin(end, "\n", NULL); } mprAssert(slen(path) > slen(route->dir)); mprAssert(sncmp(path, route->dir, slen(route->dir)) == 0); if (sncmp(path, route->dir, slen(route->dir)) == 0) { path = &path[slen(route->dir) + 1]; } body = sfmt(\ "/*\n Generated from %s\n */\n"\ "#include \"esp-app.h\"\n"\ "%s\n"\ "static void %s(HttpConn *conn) {\n"\ "%s%s%s"\ "}\n\n"\ "%s int esp_%s(HttpRoute *route, MprModule *module) {\n"\ " espDefineView(route, \"%s\", %s);\n"\ " return 0;\n"\ "}\n", path, global, cacheName, start, body, end, ESP_EXPORT_STRING, cacheName, mprGetPortablePath(path), cacheName); mprLog(6, "Create ESP script: \n%s\n", body); } return body; }