/* Start the request. At this stage, body data may not have been fully received unless the request is a form (POST method and content type is application/x-www-form-urlencoded). Forms are a special case and delay invoking the start callback until all body data is received. WARNING: GC yield */ static void startEsp(HttpQueue *q) { HttpConn *conn; HttpRx *rx; EspReq *req; conn = q->conn; rx = conn->rx; req = conn->reqData; #if ME_WIN_LIKE rx->target = mprGetPortablePath(rx->target); #endif if (req) { mprSetThreadData(req->esp->local, conn); /* WARNING: GC yield */ if (!runAction(conn)) { pruneFlash(conn); } else { if (!conn->error && req->autoFinalize) { if (!conn->tx->responded) { /* WARNING: GC yield */ espRenderDocument(conn, rx->target); } if (req->autoFinalize) { espFinalize(conn); } } pruneFlash(conn); } finalizeFlash(conn); mprSetThreadData(req->esp->local, NULL); } }
/* Path should be an app-relative path to the view file (relative-path.esp) */ void espDefineView(HttpRoute *route, cchar *path, void *view) { Esp *esp; mprAssert(path && *path); mprAssert(view); esp = MPR->espService; path = mprGetPortablePath(mprJoinPath(route->dir, path)); mprAddKey(esp->views, path, view); }
/* Path should be an app-relative path to the view file (relative-path.esp) */ PUBLIC void espDefineView(HttpRoute *route, cchar *path, void *view) { Esp *esp; assert(path && *path); assert(view); if (!route) { mprError("Route not defined for view %s", path); return; } esp = MPR->espService; path = mprGetPortablePath(mprJoinPath(route->documents, path)); mprAddKey(esp->views, path, view); }
/* Path should be a relative path from route->documents to the view file (relative-path.esp) */ PUBLIC void espDefineView(HttpRoute *route, cchar *path, void *view) { EspRoute *eroute; assert(path && *path); assert(view); eroute = ((EspRoute*) route->eroute)->top; if (route) { path = mprGetPortablePath(path); } if (!eroute->views) { eroute->views = mprCreateHash(-1, MPR_HASH_STATIC_VALUES); } mprAddKey(eroute->views, path, view); }
static void testTransform(MprTestGroup *gp) { char *path; path = mprTransformPath("/", MPR_PATH_ABS); assert(mprIsPathAbs(path)); path = mprTransformPath("/", MPR_PATH_REL); assert(mprIsPathRel(path)); assert(path[0] == '.'); path = mprTransformPath("/", 0); assert(strcmp(path, "/") == 0); path = mprTransformPath("/", MPR_PATH_ABS); mprAssert(mprIsPathAbs(path)); #if BIT_WIN_LIKE || CYGWIN /* Test MapSeparators */ path = sclone("\\a\\b\\c\\d"); mprMapSeparators(path, '/'); assert(*path == '/'); assert(strchr(path, '\\') == 0); /* Test PortablePath */ path = sclone("\\a\\b\\c\\d"); path = mprGetPortablePath(path); mprAssert(*path == '/'); assert(strchr(path, '\\') == 0); assert(mprIsPathAbs(path)); #endif #if CYGWIN path = mprGetAbsPath("c:/a/b"); assert(smatch(path, "/cygdrive/c/a/b")); path = mprGetAbsPath("/a/b"); assert(smatch(path, "/a/b")); path = mprGetAbsPath("c:/cygwin/a/b"); assert(smatch(path, "/a/b")); path = mprGetWinPath("c:/a/b"); assert(smatch(path, "C:\\a\\b")); path = mprGetWinPath("/cygdrive/c/a/b"); assert(smatch(path, "C:\\a\\b")); #endif }
/* Tokens: ARCH Build architecture (x86_64) CC Compiler (cc) DEBUG Debug compilation options (-g, -Zi -Od) INC Include directory out/inc LIB Library directory (out/lib, xcode/VS: out/bin) LIBS Libraries required to link with ESP OBJ Name of compiled source (out/lib/view-MD5.o) OUT Output module (view_MD5.dylib) SHLIB Host Shared library (.lib, .so) SHOBJ Host Shared Object (.dll, .so) SRC Source code for view or controller (already templated) TMP Temp directory VS Visual Studio directory WINSDK Windows SDK directory */ char *espExpandCommand(cchar *command, cchar *source, cchar *module) { MprBuf *buf; MaAppweb *appweb; cchar *cp, *out; char *tmp; #if BLD_WIN_LIKE cchar *path; path = 0; #endif if (command == 0) { return 0; } out = mprTrimPathExt(module); buf = mprCreateBuf(-1, -1); appweb = MPR->appwebService; for (cp = command; *cp; ) { if (*cp == '$') { if (matchToken(&cp, "${ARCH}")) { /* Build architecture */ mprPutStringToBuf(buf, appweb->hostArch); #if BLD_WIN_LIKE } else if (matchToken(&cp, "${WINSDK}")) { path = mprReadRegistry("HKLM\\SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows", "CurrentInstallFolder"); path = strim(path, "\\", MPR_TRIM_END); mprPutStringToBuf(buf, path); } else if (matchToken(&cp, "${VS}")) { path = mprGetPathParent(mprGetPathParent(getenv("VS100COMNTOOLS"))); mprPutStringToBuf(buf, mprGetPortablePath(path)); #endif } else if (matchToken(&cp, "${CC}")) { /* Compiler */ #if BLD_WIN_LIKE // MOB - what about cross compilation path = mprJoinPath(mprGetPathParent(mprGetPathParent(getenv("VS100COMNTOOLS"))), "VC/bin/cl.exe"); mprPutStringToBuf(buf, mprGetPortablePath(path)); #else mprPutStringToBuf(buf, BLD_CC); #endif } else if (matchToken(&cp, "${DEBUG}")) { mprPutStringToBuf(buf, ESP_DEBUG); } else if (matchToken(&cp, "${INC}")) { /* Include directory (out/inc) */ mprPutStringToBuf(buf, mprResolvePath(mprGetAppDir(), "inc")); } else if (matchToken(&cp, "${LIB}")) { /* Library directory. IDE's use bin dir */ mprPutStringToBuf(buf, getOutDir(BLD_LIB_NAME)); } else if (matchToken(&cp, "${LIBS}")) { /* Required libraries to link. These may have nested ${TOKENS} */ mprPutStringToBuf(buf, espExpandCommand(ESP_LIBS, source, module)); } else if (matchToken(&cp, "${OBJ}")) { /* Output object with extension (.o) */ mprPutStringToBuf(buf, mprJoinPathExt(out, BLD_OBJ)); } else if (matchToken(&cp, "${OUT}")) { /* Output modules */ mprPutStringToBuf(buf, out); } else if (matchToken(&cp, "${SHLIB}")) { /* .lib */ mprPutStringToBuf(buf, appweb->hostOs); } else if (matchToken(&cp, "${SHOBJ}")) { /* .dll */ #ifdef BLD_HOST_SHOBJ // MOB - need this for bit mprPutStringToBuf(buf, BLD_HOST_SHOBJ); #else mprPutStringToBuf(buf, BLD_SHOBJ); #endif } else if (matchToken(&cp, "${SRC}")) { /* View (already parsed into C code) or controller source */ mprPutStringToBuf(buf, source); } else if (matchToken(&cp, "${TMP}")) { #if BLD_WIN_LIKE if ((tmp = getenv("TMP")) == 0) { tmp = getenv("TEMP"); } #else tmp = getenv("TMPDIR"); #endif mprPutStringToBuf(buf, tmp ? tmp : "."); #ifdef WIND_BASE } else if (matchToken(&cp, "${WIND_BASE}")) { mprPutStringToBuf(buf, WIND_BASE); #endif #ifdef WIND_HOME } else if (matchToken(&cp, "${WIND_HOME}")) { mprPutStringToBuf(buf, WIND_HOME); #endif #ifdef WIND_HOST_TYPE } else if (matchToken(&cp, "${WIND_HOST_TYPE}")) { mprPutStringToBuf(buf, WIND_HOST_TYPE); #endif #ifdef WIND_PLATFORM } else if (matchToken(&cp, "${WIND_PLATFORM}")) { mprPutStringToBuf(buf, WIND_PLATFORM); #endif #ifdef WIND_GNU_PATH } else if (matchToken(&cp, "${WIND_GNU_PATH}")) { mprPutStringToBuf(buf, WIND_GNU_PATH); #endif } else { mprPutCharToBuf(buf, *cp++); } } else { mprPutCharToBuf(buf, *cp++); } } mprAddNullToBuf(buf); return sclone(mprGetBufStart(buf)); }
/* 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; }
/* WARNING: GC yield */ PUBLIC int espLoadModule(HttpRoute *route, MprDispatcher *dispatcher, cchar *kind, cchar *source, cchar **errMsg) { EspRoute *eroute; MprModule *mp; cchar *appName, *cache, *cacheName, *canonical, *entry, *module; int isView, recompile; eroute = route->eroute; *errMsg = ""; #if VXWORKS /* Trim the drive for VxWorks where simulated host drives only exist on the target */ source = mprTrimPathDrive(source); #endif canonical = mprGetPortablePath(mprGetRelPath(source, route->home)); appName = eroute->appName; if (eroute->combine) { cacheName = appName; } else { cacheName = mprGetMD5WithPrefix(sfmt("%s:%s", appName, canonical), -1, sjoin(kind, "_", NULL)); } if ((cache = httpGetDir(route, "CACHE")) == 0) { cache = "cache"; } module = mprJoinPathExt(mprJoinPaths(route->home, cache, cacheName, NULL), ME_SHOBJ); lock(esp); if (route->update) { if (mprPathExists(source, R_OK)) { isView = smatch(kind, "view"); if (espModuleIsStale(source, module, &recompile) || (isView && layoutIsStale(eroute, source, module))) { if (recompile) { mprHoldBlocks(source, module, cacheName, NULL); if (!espCompile(route, dispatcher, source, module, cacheName, isView, (char**) errMsg)) { mprReleaseBlocks(source, module, cacheName, NULL); unlock(esp); return MPR_ERR_CANT_WRITE; } mprReleaseBlocks(source, module, cacheName, NULL); } } } } if (mprLookupModule(source) == 0) { if (!mprPathExists(module, R_OK)) { *errMsg = "Module does not exist"; unlock(esp); return MPR_ERR_CANT_FIND; } entry = getModuleEntry(eroute, kind, source, cacheName); if ((mp = mprCreateModule(source, module, entry, route)) == 0) { *errMsg = "Memory allocation error loading module"; unlock(esp); return MPR_ERR_MEMORY; } if (mprLoadModule(mp) < 0) { *errMsg = "Cannot load compiled esp module"; unlock(esp); return MPR_ERR_CANT_READ; } } unlock(esp); return 0; }