| File: | d/adpeval.c |
| Warning: | line 1376, column 13 Access out-of-bound array element (buffer overflow) |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* | |||
| 2 | * The contents of this file are subject to the Mozilla Public License | |||
| 3 | * Version 1.1 (the "License"); you may not use this file except in | |||
| 4 | * compliance with the License. You may obtain a copy of the License at | |||
| 5 | * http://mozilla.org/. | |||
| 6 | * | |||
| 7 | * Software distributed under the License is distributed on an "AS IS" | |||
| 8 | * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See | |||
| 9 | * the License for the specific language governing rights and limitations | |||
| 10 | * under the License. | |||
| 11 | * | |||
| 12 | * The Original Code is AOLserver Code and related documentation | |||
| 13 | * distributed by AOL. | |||
| 14 | * | |||
| 15 | * The Initial Developer of the Original Code is America Online, | |||
| 16 | * Inc. Portions created by AOL are Copyright (C) 1999 America Online, | |||
| 17 | * Inc. All Rights Reserved. | |||
| 18 | * | |||
| 19 | * Alternatively, the contents of this file may be used under the terms | |||
| 20 | * of the GNU General Public License (the "GPL"), in which case the | |||
| 21 | * provisions of GPL are applicable instead of those above. If you wish | |||
| 22 | * to allow use of your version of this file only under the terms of the | |||
| 23 | * GPL and not to allow others to use your version of this file under the | |||
| 24 | * License, indicate your decision by deleting the provisions above and | |||
| 25 | * replace them with the notice and other provisions required by the GPL. | |||
| 26 | * If you do not delete the provisions above, a recipient may use your | |||
| 27 | * version of this file under either the License or the GPL. | |||
| 28 | */ | |||
| 29 | ||||
| 30 | /* | |||
| 31 | * adpeval.c -- | |||
| 32 | * | |||
| 33 | * ADP string and file eval. | |||
| 34 | */ | |||
| 35 | ||||
| 36 | #include "nsd.h" | |||
| 37 | ||||
| 38 | /* | |||
| 39 | * The following structure defines a cached ADP page result. A cached | |||
| 40 | * object is created by executing the non-cached code and saving the | |||
| 41 | * resulting output which may include embedded non-cached components | |||
| 42 | * (see NsTclAdpIncludeObjCmd for details). | |||
| 43 | */ | |||
| 44 | ||||
| 45 | typedef struct AdpCache { | |||
| 46 | int refcnt; /* Current interps using cached results. */ | |||
| 47 | Ns_Time expires; /* Expiration time of cached results. */ | |||
| 48 | AdpCode code; /* ADP code for cached result. */ | |||
| 49 | } AdpCache; | |||
| 50 | ||||
| 51 | /* | |||
| 52 | * The following structure defines a shared page in the ADP cache. The | |||
| 53 | * size of the object is extended to include the filename bytes. | |||
| 54 | */ | |||
| 55 | ||||
| 56 | typedef struct Page { | |||
| 57 | NsServer *servPtr; /* Page server context (reg tags, etc.) */ | |||
| 58 | Tcl_HashEntry *hPtr; /* Entry in shared table of all pages. */ | |||
| 59 | time_t mtime; /* Original modify time of file. */ | |||
| 60 | off_t size; /* Original size of file. */ | |||
| 61 | dev_t dev; /* Device and inode to try to catch modifications ... */ | |||
| 62 | ino_t ino; /* ...below the mtime granularity. */ | |||
| 63 | unsigned int flags; /* Flags used on last compile, e.g., SAFE. */ | |||
| 64 | int refcnt; /* Refcnt of current interps using page. */ | |||
| 65 | int evals; /* Count of page evaluations. */ | |||
| 66 | int cacheGen; /* Cache generation id. */ | |||
| 67 | AdpCache *cachePtr; /* Cached output. */ | |||
| 68 | AdpCode code; /* ADP code blocks. */ | |||
| 69 | bool_Bool locked; /* Page locked for cache update. */ | |||
| 70 | } Page; | |||
| 71 | ||||
| 72 | /* | |||
| 73 | * The following structure holds per-interp script byte codes. The | |||
| 74 | * size of the object is extended based on the number of script objects. | |||
| 75 | */ | |||
| 76 | ||||
| 77 | typedef struct Objs { | |||
| 78 | int nobjs; /* Number of scripts objects. */ | |||
| 79 | Tcl_Obj *objs[1]; /* Scripts to be compiled and reused. */ | |||
| 80 | } Objs; | |||
| 81 | ||||
| 82 | /* | |||
| 83 | * The following structure defines a per-interp page entry with | |||
| 84 | * a pointer to the shared Page and private Objs for cached and | |||
| 85 | * non-cached page results. | |||
| 86 | */ | |||
| 87 | ||||
| 88 | typedef struct InterpPage { | |||
| 89 | Page *pagePtr; /* Pointer to shared page text. */ | |||
| 90 | Objs *objs; /* Non-cache ADP code script. */ | |||
| 91 | int cacheGen; /* Cache generation id. */ | |||
| 92 | Objs *cacheObjs; /* Cache results ADP code scripts. */ | |||
| 93 | } InterpPage; | |||
| 94 | ||||
| 95 | /* | |||
| 96 | * Local functions defined in this file. | |||
| 97 | */ | |||
| 98 | ||||
| 99 | static Page *ParseFile(const NsInterp *itPtr, const char *file, struct stat *stPtr, unsigned int flags) | |||
| 100 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))); | |||
| 101 | ||||
| 102 | static int AdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
| 103 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
| 104 | ||||
| 105 | static int AdpExec(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
| 106 | const AdpCode *codePtr, Objs *objsPtr, Tcl_DString *outputPtr, | |||
| 107 | const struct stat *stPtr) | |||
| 108 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5))) NS_GNUC_NONNULL(7)__attribute__((__nonnull__(7))); | |||
| 109 | ||||
| 110 | static int AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
| 111 | const Ns_Time *expiresPtr, Tcl_DString *outputPtr) | |||
| 112 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(6)__attribute__((__nonnull__(6))); | |||
| 113 | ||||
| 114 | static int AdpDebug(const NsInterp *itPtr, const char *ptr, int len, int nscript) | |||
| 115 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); | |||
| 116 | ||||
| 117 | static void DecrCache(AdpCache *cachePtr) | |||
| 118 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
| 119 | ||||
| 120 | static Objs *AllocObjs(int nobjs); | |||
| 121 | ||||
| 122 | static void FreeObjs(Objs *objsPtr) | |||
| 123 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))); | |||
| 124 | ||||
| 125 | static void AdpTrace(const NsInterp *itPtr, const char *ptr, int len) | |||
| 126 | NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))); | |||
| 127 | ||||
| 128 | static Ns_Callback FreeInterpPage; | |||
| 129 | static Ns_ServerInitProc ConfigServerAdp; | |||
| 130 | ||||
| 131 | ||||
| 132 | /* | |||
| 133 | *---------------------------------------------------------------------- | |||
| 134 | * | |||
| 135 | * NsConfigAdp -- | |||
| 136 | * | |||
| 137 | * Initialize and configure the ADP subsystem. | |||
| 138 | * | |||
| 139 | * Results: | |||
| 140 | * None. | |||
| 141 | * | |||
| 142 | * Side effects: | |||
| 143 | * None. | |||
| 144 | * | |||
| 145 | *---------------------------------------------------------------------- | |||
| 146 | */ | |||
| 147 | ||||
| 148 | void | |||
| 149 | NsConfigAdp(void) | |||
| 150 | { | |||
| 151 | NsRegisterServerInit(ConfigServerAdp); | |||
| 152 | } | |||
| 153 | ||||
| 154 | static Ns_ReturnCode | |||
| 155 | ConfigServerAdp(const char *server) | |||
| 156 | { | |||
| 157 | NsServer *servPtr = NsGetServer(server); | |||
| 158 | const char *path; | |||
| 159 | ||||
| 160 | path = Ns_ConfigSectionPath(NULL((void*)0), server, NULL((void*)0), "adp", (char *)0L); | |||
| 161 | ||||
| 162 | /* | |||
| 163 | * Initialize the page and tag tables and locks. | |||
| 164 | */ | |||
| 165 | ||||
| 166 | Tcl_InitHashTable(&servPtr->adp.pages, TCL_STRING_KEYS(0)); | |||
| 167 | Tcl_InitHashTable(&servPtr->adp.tags, TCL_STRING_KEYS(0)); | |||
| 168 | ||||
| 169 | Ns_CondInit(&servPtr->adp.pagecond); | |||
| 170 | ||||
| 171 | Ns_MutexInit(&servPtr->adp.pagelock); | |||
| 172 | Ns_MutexSetName2(&servPtr->adp.pagelock, "ns:adp:pages", server); | |||
| 173 | ||||
| 174 | Ns_RWLockInit(&servPtr->adp.taglock); | |||
| 175 | Ns_RWLockSetName2(&servPtr->adp.taglock, "rw:adp:tags", server); | |||
| 176 | ||||
| 177 | /* | |||
| 178 | * Initialise various ADP options. | |||
| 179 | */ | |||
| 180 | ||||
| 181 | servPtr->adp.errorpage = Ns_ConfigString(path, "errorpage", NULL((void*)0)); | |||
| 182 | servPtr->adp.startpage = Ns_ConfigString(path, "startpage", NULL((void*)0)); | |||
| 183 | servPtr->adp.debuginit = Ns_ConfigString(path, "debuginit", "ns_adp_debuginit"); | |||
| 184 | servPtr->adp.tracesize = Ns_ConfigInt(path, "tracesize", 40); | |||
| 185 | servPtr->adp.cachesize = (size_t)Ns_ConfigMemUnitRange(path, "cachesize", "5MB", 5000 * 1024, | |||
| 186 | 1000 * 1024, INT_MAX2147483647); | |||
| 187 | servPtr->adp.bufsize = (size_t)Ns_ConfigMemUnitRange(path, "bufsize", "1MB", 1024 * 1000, | |||
| 188 | 100 * 1024, INT_MAX2147483647); | |||
| 189 | servPtr->adp.defaultExtension = Ns_ConfigString(path, "defaultextension", NULL((void*)0)); | |||
| 190 | ||||
| 191 | servPtr->adp.flags = 0u; | |||
| 192 | (void) Ns_ConfigFlag(path, "cache", ADP_CACHE0x10u, 0, &servPtr->adp.flags); | |||
| 193 | (void) Ns_ConfigFlag(path, "stream", ADP_STREAM0x8000u, 0, &servPtr->adp.flags); | |||
| 194 | (void) Ns_ConfigFlag(path, "enableexpire", ADP_EXPIRE0x08u, 0, &servPtr->adp.flags); | |||
| 195 | (void) Ns_ConfigFlag(path, "enabledebug", ADP_DEBUG0x04u, 0, &servPtr->adp.flags); | |||
| 196 | (void) Ns_ConfigFlag(path, "safeeval", ADP_SAFE0x01u, 0, &servPtr->adp.flags); | |||
| 197 | (void) Ns_ConfigFlag(path, "singlescript", ADP_SINGLE0x02u, 0, &servPtr->adp.flags); | |||
| 198 | (void) Ns_ConfigFlag(path, "trace", ADP_TRACE0x20u, 0, &servPtr->adp.flags); | |||
| 199 | (void) Ns_ConfigFlag(path, "detailerror", ADP_DETAIL0x80u, 1, &servPtr->adp.flags); | |||
| 200 | (void) Ns_ConfigFlag(path, "stricterror", ADP_STRICT0x100u, 0, &servPtr->adp.flags); | |||
| 201 | (void) Ns_ConfigFlag(path, "displayerror", ADP_DISPLAY0x200u, 0, &servPtr->adp.flags); | |||
| 202 | (void) Ns_ConfigFlag(path, "trimspace", ADP_TRIM0x400u, 0, &servPtr->adp.flags); | |||
| 203 | (void) Ns_ConfigFlag(path, "autoabort", ADP_AUTOABORT0x2000u, 1, &servPtr->adp.flags); | |||
| 204 | ||||
| 205 | return NS_OK; | |||
| 206 | } | |||
| 207 | ||||
| 208 | ||||
| 209 | /* | |||
| 210 | *---------------------------------------------------------------------- | |||
| 211 | * | |||
| 212 | * NsAdpEval, NsAdpSource -- | |||
| 213 | * | |||
| 214 | * Evaluate an ADP string or file and return the output | |||
| 215 | * as the interp result. | |||
| 216 | * | |||
| 217 | * Results: | |||
| 218 | * A standard Tcl result. | |||
| 219 | * | |||
| 220 | * Side effects: | |||
| 221 | * Variable named by resvar, if any, is updated with results of | |||
| 222 | * Tcl interp before being replaced with ADP output. | |||
| 223 | * | |||
| 224 | *---------------------------------------------------------------------- | |||
| 225 | */ | |||
| 226 | ||||
| 227 | int | |||
| 228 | NsAdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
| 229 | { | |||
| 230 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 231 | ||||
| 232 | return AdpEval(itPtr, objc, objv, resvar); | |||
| 233 | } | |||
| 234 | ||||
| 235 | int | |||
| 236 | NsAdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
| 237 | { | |||
| 238 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 239 | ||||
| 240 | itPtr->adp.flags |= ADP_ADPFILE0x4000u; | |||
| 241 | return AdpEval(itPtr, objc, objv, resvar); | |||
| 242 | } | |||
| 243 | ||||
| 244 | static int | |||
| 245 | AdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar) | |||
| 246 | { | |||
| 247 | Tcl_Interp *interp; | |||
| 248 | AdpCode code; | |||
| 249 | Tcl_DString output; | |||
| 250 | int result; | |||
| 251 | char *obj0; | |||
| 252 | ||||
| 253 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 254 | ||||
| 255 | interp = itPtr->interp; | |||
| 256 | /* | |||
| 257 | * If the ADP object is a file, simply source it. Otherwise, parse | |||
| 258 | * the script as a temporary ADP code object and execute it directly. | |||
| 259 | */ | |||
| 260 | ||||
| 261 | Tcl_DStringInit(&output); | |||
| 262 | obj0 = Tcl_GetString(objv[0]); | |||
| 263 | if ((itPtr->adp.flags & ADP_ADPFILE0x4000u) != 0u) { | |||
| 264 | result = AdpSource(itPtr, objc, objv, obj0, NULL((void*)0), &output); | |||
| 265 | } else { | |||
| 266 | NsAdpParse(&code, itPtr->servPtr, obj0, itPtr->adp.flags, NULL((void*)0)); | |||
| 267 | result = AdpExec(itPtr, objc, objv, NULL((void*)0), &code, NULL((void*)0), &output, NULL((void*)0)); | |||
| 268 | NsAdpFreeCode(&code); | |||
| 269 | } | |||
| 270 | ||||
| 271 | /* | |||
| 272 | * Set the interp result with the ADP output, saving the last interp | |||
| 273 | * result first if requested. | |||
| 274 | */ | |||
| 275 | ||||
| 276 | if (result == TCL_OK0) { | |||
| 277 | Tcl_Obj *objPtr; | |||
| 278 | ||||
| 279 | if (resvar != NULL((void*)0)) { | |||
| 280 | objPtr = Tcl_GetObjResult(interp); | |||
| 281 | if (Tcl_SetVar2Ex(interp, resvar, NULL((void*)0), objPtr, TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0)) { | |||
| 282 | result = TCL_ERROR1; | |||
| 283 | } | |||
| 284 | } | |||
| 285 | if (result == TCL_OK0) { | |||
| 286 | objPtr = Tcl_NewStringObj(output.string, output.length); | |||
| 287 | Tcl_SetObjResult(interp, objPtr); | |||
| 288 | } | |||
| 289 | } | |||
| 290 | Tcl_DStringFree(&output); | |||
| 291 | ||||
| 292 | return result; | |||
| 293 | } | |||
| 294 | ||||
| 295 | ||||
| 296 | /* | |||
| 297 | *---------------------------------------------------------------------- | |||
| 298 | * | |||
| 299 | * NsAdpInclude -- | |||
| 300 | * | |||
| 301 | * Evaluate an ADP file, utilizing per-thread byte-code pages. | |||
| 302 | * | |||
| 303 | * Results: | |||
| 304 | * A standard Tcl result. | |||
| 305 | * | |||
| 306 | * Side effects: | |||
| 307 | * Output is either left in current ADP buffer. | |||
| 308 | * | |||
| 309 | *---------------------------------------------------------------------- | |||
| 310 | */ | |||
| 311 | ||||
| 312 | int | |||
| 313 | NsAdpInclude(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, const Ns_Time *expiresPtr) | |||
| 314 | { | |||
| 315 | Ns_DStringTcl_DString *outputPtr; | |||
| 316 | ||||
| 317 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 318 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
| 319 | ||||
| 320 | /* | |||
| 321 | * If an ADP execution is already active, use the current output | |||
| 322 | * buffer. Otherwise, use the top-level buffer in the ADP struct. | |||
| 323 | */ | |||
| 324 | ||||
| 325 | if (itPtr->adp.framePtr != NULL((void*)0)) { | |||
| 326 | outputPtr = itPtr->adp.framePtr->outputPtr; | |||
| 327 | } else { | |||
| 328 | outputPtr = &itPtr->adp.output; | |||
| 329 | } | |||
| 330 | return AdpSource(itPtr, objc, objv, file, expiresPtr, outputPtr); | |||
| 331 | } | |||
| 332 | ||||
| 333 | ||||
| 334 | /* | |||
| 335 | *---------------------------------------------------------------------- | |||
| 336 | * | |||
| 337 | * NsAdpInit, NsAdpFree -- | |||
| 338 | * | |||
| 339 | * Initialize or free the NsInterp ADP data structures. | |||
| 340 | * | |||
| 341 | * Results: | |||
| 342 | * None. | |||
| 343 | * | |||
| 344 | * Side effects: | |||
| 345 | * None. | |||
| 346 | * | |||
| 347 | *---------------------------------------------------------------------- | |||
| 348 | */ | |||
| 349 | ||||
| 350 | void | |||
| 351 | NsAdpInit(NsInterp *itPtr) | |||
| 352 | { | |||
| 353 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 354 | ||||
| 355 | Tcl_DStringInit(&itPtr->adp.output); | |||
| 356 | NsAdpReset(itPtr); | |||
| 357 | } | |||
| 358 | ||||
| 359 | void | |||
| 360 | NsAdpFree(NsInterp *itPtr) | |||
| 361 | { | |||
| 362 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 363 | ||||
| 364 | if (itPtr->adp.cache != NULL((void*)0)) { | |||
| 365 | Ns_CacheDestroy(itPtr->adp.cache); | |||
| 366 | itPtr->adp.cache = NULL((void*)0); | |||
| 367 | } | |||
| 368 | Tcl_DStringFree(&itPtr->adp.output); | |||
| 369 | } | |||
| 370 | ||||
| 371 | ||||
| 372 | /* | |||
| 373 | *---------------------------------------------------------------------- | |||
| 374 | * | |||
| 375 | * NsAdpReset -- | |||
| 376 | * | |||
| 377 | * Reset the NsInterp ADP data structures for the next | |||
| 378 | * execution request. | |||
| 379 | * | |||
| 380 | * Results: | |||
| 381 | * None. | |||
| 382 | * | |||
| 383 | * Side effects: | |||
| 384 | * None. | |||
| 385 | * | |||
| 386 | *---------------------------------------------------------------------- | |||
| 387 | */ | |||
| 388 | ||||
| 389 | void | |||
| 390 | NsAdpReset(NsInterp *itPtr) | |||
| 391 | { | |||
| 392 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 393 | ||||
| 394 | itPtr->adp.exception = ADP_OK; | |||
| 395 | itPtr->adp.debugLevel = 0; | |||
| 396 | itPtr->adp.debugInit = 0; | |||
| 397 | itPtr->adp.debugFile = NULL((void*)0); | |||
| 398 | itPtr->adp.chan = NULL((void*)0); | |||
| 399 | itPtr->adp.conn = NULL((void*)0); | |||
| 400 | if (itPtr->servPtr != NULL((void*)0)) { | |||
| 401 | itPtr->adp.bufsize = itPtr->servPtr->adp.bufsize; | |||
| 402 | itPtr->adp.flags = itPtr->servPtr->adp.flags; | |||
| 403 | } else { | |||
| 404 | itPtr->adp.bufsize = 1024u * 1000u; | |||
| 405 | itPtr->adp.flags = 0u; | |||
| 406 | } | |||
| 407 | Tcl_DStringSetLength(&itPtr->adp.output, 0); | |||
| 408 | } | |||
| 409 | ||||
| 410 | /* | |||
| 411 | *---------------------------------------------------------------------- | |||
| 412 | * | |||
| 413 | * AdpSource -- | |||
| 414 | * | |||
| 415 | * Execute ADP code in a file with results returned in given | |||
| 416 | * dstring. | |||
| 417 | * | |||
| 418 | * Results: | |||
| 419 | * TCL_ERROR if the file could not be parsed, result of | |||
| 420 | * AdpExec otherwise. | |||
| 421 | * | |||
| 422 | * Side effects: | |||
| 423 | * Page text and ADP code results may be cached up to given time | |||
| 424 | * limit, if any. | |||
| 425 | * | |||
| 426 | *---------------------------------------------------------------------- | |||
| 427 | */ | |||
| 428 | ||||
| 429 | static int | |||
| 430 | AdpSource(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
| 431 | const Ns_Time *expiresPtr, Tcl_DString *outputPtr) | |||
| 432 | { | |||
| 433 | NsServer *servPtr; | |||
| 434 | Tcl_Interp *interp; | |||
| 435 | Tcl_HashEntry *hPtr; | |||
| 436 | struct stat st; | |||
| 437 | Ns_DStringTcl_DString tmp, path; | |||
| 438 | InterpPage *ipagePtr = NULL((void*)0); | |||
| 439 | Page *pagePtr = NULL((void*)0); | |||
| 440 | Ns_Time now; | |||
| 441 | int isNew; | |||
| 442 | const char *p; | |||
| 443 | int result = TCL_ERROR1; /* assume error until accomplished success */ | |||
| 444 | ||||
| 445 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 446 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
| 447 | NS_NONNULL_ASSERT(outputPtr != NULL)((void) (0)); | |||
| 448 | ||||
| 449 | servPtr = itPtr->servPtr; | |||
| 450 | interp = itPtr->interp; | |||
| 451 | ||||
| 452 | Ns_DStringInitTcl_DStringInit(&tmp); | |||
| 453 | Ns_DStringInitTcl_DStringInit(&path); | |||
| 454 | ||||
| 455 | /* | |||
| 456 | * Construct the full, normalized path to the ADP file. | |||
| 457 | */ | |||
| 458 | ||||
| 459 | if (Ns_PathIsAbsolute(file) == NS_FALSE0) { | |||
| 460 | if (itPtr->adp.cwd == NULL((void*)0)) { | |||
| 461 | file = Ns_PagePath(&tmp, servPtr->server, file, (char *)0L); | |||
| 462 | } else { | |||
| 463 | file = Ns_MakePath(&tmp, itPtr->adp.cwd, file, (char *)0L); | |||
| 464 | } | |||
| 465 | } | |||
| 466 | file = Ns_NormalizePath(&path, file); | |||
| 467 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
| 468 | ||||
| 469 | /* | |||
| 470 | * Check for TclPro debugging. | |||
| 471 | */ | |||
| 472 | ||||
| 473 | if (itPtr->adp.debugLevel > 0) { | |||
| 474 | ++itPtr->adp.debugLevel; | |||
| 475 | } else if ((itPtr->conn != NULL((void*)0)) | |||
| 476 | && ((itPtr->adp.flags & ADP_DEBUG0x04u) != 0u) | |||
| 477 | && itPtr->adp.debugFile != NULL((void*)0) | |||
| 478 | && (p = strrchr(file, INTCHAR('/')((int)((unsigned char)(('/')))))) != NULL((void*)0) | |||
| 479 | && Tcl_StringMatch(p+1, itPtr->adp.debugFile) != 0) { | |||
| 480 | const Ns_Set *hdrs; | |||
| 481 | const char *host, *port, *procs; | |||
| 482 | ||||
| 483 | hdrs = Ns_ConnGetQuery(interp, itPtr->conn, NULL((void*)0), NULL((void*)0)); /* currently ignoring encoding errors */ | |||
| 484 | host = Ns_SetIGet(hdrs, "dhost"); | |||
| 485 | port = Ns_SetIGet(hdrs, "dport"); | |||
| 486 | procs = Ns_SetIGet(hdrs, "dprocs"); | |||
| 487 | if (NsAdpDebug(itPtr, host, port, procs) != TCL_OK0) { | |||
| 488 | (void) Ns_ConnReturnNotice(itPtr->conn, 200, "Debug Init Failed", | |||
| 489 | Tcl_GetStringResult(interp)); | |||
| 490 | itPtr->adp.exception = ADP_ABORT; | |||
| 491 | goto done; | |||
| 492 | } | |||
| 493 | } | |||
| 494 | ||||
| 495 | if (itPtr->adp.cache == NULL((void*)0)) { | |||
| 496 | Ns_DStringPrintf(&tmp, "nsadp:%p", (void *)itPtr); | |||
| 497 | itPtr->adp.cache = Ns_CacheCreateSz(tmp.string, TCL_STRING_KEYS(0), | |||
| 498 | itPtr->servPtr->adp.cachesize, FreeInterpPage); | |||
| 499 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
| 500 | } | |||
| 501 | ||||
| 502 | /* | |||
| 503 | * Verify the file is an existing, ordinary file and get page code. | |||
| 504 | */ | |||
| 505 | ||||
| 506 | if (stat(file, &st) != 0) { | |||
| 507 | Ns_TclPrintfResult(interp, "could not stat \"%s\": %s", | |||
| 508 | file, Tcl_PosixError(interp)); | |||
| 509 | } else if (!S_ISREG(st.st_mode)((((st.st_mode)) & 0170000) == (0100000))) { | |||
| 510 | Ns_TclPrintfResult(interp, "not an ordinary file: %s", file); | |||
| 511 | } else { | |||
| 512 | Ns_Entry *ePtr; | |||
| 513 | ||||
| 514 | /* | |||
| 515 | * Check for valid code in interp page cache. | |||
| 516 | */ | |||
| 517 | ||||
| 518 | ePtr = Ns_CacheFindEntry(itPtr->adp.cache, file); | |||
| 519 | if (ePtr != NULL((void*)0)) { | |||
| 520 | ipagePtr = Ns_CacheGetValue(ePtr); | |||
| 521 | if (ipagePtr->pagePtr->mtime != st.st_mtimest_mtim.tv_sec | |||
| 522 | || ipagePtr->pagePtr->size != st.st_size | |||
| 523 | || ipagePtr->pagePtr->dev != st.st_dev | |||
| 524 | || ipagePtr->pagePtr->ino != st.st_ino | |||
| 525 | || ipagePtr->pagePtr->flags != itPtr->adp.flags) { | |||
| 526 | Ns_CacheFlushEntry(ePtr); | |||
| 527 | ipagePtr = NULL((void*)0); | |||
| 528 | } | |||
| 529 | } | |||
| 530 | if (ipagePtr == NULL((void*)0)) { | |||
| 531 | ||||
| 532 | /* | |||
| 533 | * Find or create valid page in server table. | |||
| 534 | */ | |||
| 535 | ||||
| 536 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 537 | hPtr = Tcl_CreateHashEntry(&servPtr->adp.pages, file, &isNew)(*((&servPtr->adp.pages)->createProc))(&servPtr ->adp.pages, (const char *)(file), &isNew); | |||
| 538 | while (isNew == 0 && (pagePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData)) == NULL((void*)0)) { | |||
| 539 | /* NB: Wait for other thread to read/parse page. */ | |||
| 540 | Ns_CondWait(&servPtr->adp.pagecond, &servPtr->adp.pagelock); | |||
| 541 | hPtr = Tcl_CreateHashEntry(&servPtr->adp.pages, file, &isNew)(*((&servPtr->adp.pages)->createProc))(&servPtr ->adp.pages, (const char *)(file), &isNew); | |||
| 542 | } | |||
| 543 | if (isNew == 0 && (pagePtr->mtime != st.st_mtimest_mtim.tv_sec | |||
| 544 | || pagePtr->size != st.st_size | |||
| 545 | || pagePtr->dev != st.st_dev | |||
| 546 | || pagePtr->ino != st.st_ino | |||
| 547 | || pagePtr->flags != itPtr->adp.flags)) { | |||
| 548 | /* NB: Clear entry to indicate read/parse in progress. */ | |||
| 549 | Tcl_SetHashValue(hPtr, NULL)((hPtr)->clientData = (ClientData) (((void*)0))); | |||
| 550 | pagePtr->hPtr = NULL((void*)0); | |||
| 551 | isNew = 1; | |||
| 552 | } | |||
| 553 | if (isNew != 0) { | |||
| 554 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 555 | pagePtr = ParseFile(itPtr, file, &st, itPtr->adp.flags); | |||
| 556 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 557 | if (pagePtr == NULL((void*)0)) { | |||
| 558 | Tcl_DeleteHashEntry(hPtr); | |||
| 559 | } else { | |||
| 560 | pagePtr->hPtr = hPtr; | |||
| 561 | Tcl_SetHashValue(hPtr, pagePtr)((hPtr)->clientData = (ClientData) (pagePtr)); | |||
| 562 | } | |||
| 563 | Ns_CondBroadcast(&servPtr->adp.pagecond); | |||
| 564 | } | |||
| 565 | if (pagePtr != NULL((void*)0)) { | |||
| 566 | ++pagePtr->refcnt; | |||
| 567 | } | |||
| 568 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 569 | if (pagePtr != NULL((void*)0)) { | |||
| 570 | ipagePtr = ns_malloc(sizeof(InterpPage)); | |||
| 571 | ipagePtr->pagePtr = pagePtr; | |||
| 572 | ipagePtr->cacheGen = 0; | |||
| 573 | ipagePtr->objs = AllocObjs(pagePtr->code.nscripts); | |||
| 574 | ipagePtr->cacheObjs = NULL((void*)0); | |||
| 575 | ePtr = Ns_CacheCreateEntry(itPtr->adp.cache, file, &isNew); | |||
| 576 | if (isNew == 0) { | |||
| 577 | Ns_CacheUnsetValue(ePtr); | |||
| 578 | } | |||
| 579 | Ns_CacheSetValueSz(ePtr, ipagePtr, | |||
| 580 | (size_t) ipagePtr->pagePtr->size); | |||
| 581 | } | |||
| 582 | } | |||
| 583 | } | |||
| 584 | ||||
| 585 | /* | |||
| 586 | * If valid page was found, evaluate it in a new call frame. | |||
| 587 | */ | |||
| 588 | ||||
| 589 | if (ipagePtr != NULL((void*)0)) { | |||
| 590 | const AdpCode *codePtr; | |||
| 591 | Objs *objsPtr; | |||
| 592 | int cacheGen = 0; | |||
| 593 | AdpCache *cachePtr; | |||
| 594 | ||||
| 595 | pagePtr = ipagePtr->pagePtr; | |||
| 596 | if (expiresPtr == NULL((void*)0) || (itPtr->adp.flags & ADP_CACHE0x10u) == 0u) { | |||
| 597 | cachePtr = NULL((void*)0); | |||
| 598 | } else { | |||
| 599 | ||||
| 600 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 601 | ||||
| 602 | /* | |||
| 603 | * First, wait for an initial cache if already executing. | |||
| 604 | */ | |||
| 605 | ||||
| 606 | while ((cachePtr = pagePtr->cachePtr) == NULL((void*)0) && pagePtr->locked) { | |||
| 607 | Ns_CondWait(&servPtr->adp.pagecond, &servPtr->adp.pagelock); | |||
| 608 | } | |||
| 609 | ||||
| 610 | /* | |||
| 611 | * Next, if a cache exists and isn't locked, check expiration. | |||
| 612 | */ | |||
| 613 | ||||
| 614 | if (cachePtr != NULL((void*)0) && ! pagePtr->locked) { | |||
| 615 | Ns_GetTime(&now); | |||
| 616 | if (Ns_DiffTime(&cachePtr->expires, &now, NULL((void*)0)) < 0) { | |||
| 617 | pagePtr->locked = NS_TRUE1; | |||
| 618 | cachePtr = NULL((void*)0); | |||
| 619 | } | |||
| 620 | } | |||
| 621 | ||||
| 622 | /* | |||
| 623 | * Create the cached page if necessary. | |||
| 624 | */ | |||
| 625 | ||||
| 626 | if (cachePtr == NULL((void*)0)) { | |||
| 627 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 628 | codePtr = &pagePtr->code; | |||
| 629 | ++itPtr->adp.refresh; | |||
| 630 | result = AdpExec(itPtr, objc, objv, file, codePtr, ipagePtr->objs, | |||
| 631 | &tmp, &st); | |||
| 632 | --itPtr->adp.refresh; | |||
| 633 | ||||
| 634 | /* | |||
| 635 | * Check cache flag here one more time as we might clear it | |||
| 636 | * inside the script | |||
| 637 | */ | |||
| 638 | ||||
| 639 | if (result == TCL_OK0 && (itPtr->adp.flags & ADP_CACHE0x10u) != 0u) { | |||
| 640 | cachePtr = ns_malloc(sizeof(AdpCache)); | |||
| 641 | ||||
| 642 | /* | |||
| 643 | * Turn off Tcl mode after cached result, in caching mode | |||
| 644 | * we wrap Tcl file into proc 'adp:filename' and return | |||
| 645 | * as result only | |||
| 646 | * ns_adp_append {<% adp:filename %>} | |||
| 647 | * | |||
| 648 | * The output will be cached as result and every time we call | |||
| 649 | * that Tcl file, cached command will be executed as long as | |||
| 650 | * file is unchanged, if modified then the file will be reloaded, | |||
| 651 | * recompiled into same Tcl proc and cached | |||
| 652 | */ | |||
| 653 | ||||
| 654 | NsAdpParse(&cachePtr->code, itPtr->servPtr, tmp.string, | |||
| 655 | itPtr->adp.flags & ~ADP_TCLFILE0x10000u, file); | |||
| 656 | Ns_GetTime(&cachePtr->expires); | |||
| 657 | Ns_IncrTime(&cachePtr->expires, expiresPtr->sec, expiresPtr->usec); | |||
| 658 | cachePtr->refcnt = 1; | |||
| 659 | } | |||
| 660 | Ns_DStringSetLengthTcl_DStringSetLength(&tmp, 0); | |||
| 661 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 662 | if (cachePtr != NULL((void*)0)) { | |||
| 663 | if (pagePtr->cachePtr != NULL((void*)0)) { | |||
| 664 | DecrCache(pagePtr->cachePtr); | |||
| 665 | } | |||
| 666 | ++pagePtr->cacheGen; | |||
| 667 | pagePtr->cachePtr = cachePtr; | |||
| 668 | } | |||
| 669 | pagePtr->locked = NS_FALSE0; | |||
| 670 | Ns_CondBroadcast(&servPtr->adp.pagecond); | |||
| 671 | } | |||
| 672 | cacheGen = pagePtr->cacheGen; | |||
| 673 | if (cachePtr != NULL((void*)0)) { | |||
| 674 | ++cachePtr->refcnt; | |||
| 675 | } | |||
| 676 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 677 | } | |||
| 678 | if (cachePtr == NULL((void*)0)) { | |||
| 679 | codePtr = &pagePtr->code; | |||
| 680 | objsPtr = ipagePtr->objs; | |||
| 681 | } else { | |||
| 682 | codePtr = &cachePtr->code; | |||
| 683 | if (ipagePtr->cacheObjs != NULL((void*)0) && cacheGen != ipagePtr->cacheGen) { | |||
| 684 | FreeObjs(ipagePtr->cacheObjs); | |||
| 685 | ipagePtr->cacheObjs = NULL((void*)0); | |||
| 686 | } | |||
| 687 | if (ipagePtr->cacheObjs == NULL((void*)0)) { | |||
| 688 | ipagePtr->cacheObjs = AllocObjs(AdpCodeScripts(codePtr)((codePtr)->nscripts)); | |||
| 689 | ipagePtr->cacheGen = cacheGen; | |||
| 690 | } | |||
| 691 | objsPtr = ipagePtr->cacheObjs; | |||
| 692 | } | |||
| 693 | result = AdpExec(itPtr, objc, objv, file, codePtr, objsPtr, outputPtr, &st); | |||
| 694 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 695 | ++ipagePtr->pagePtr->evals; | |||
| 696 | if (cachePtr != NULL((void*)0)) { | |||
| 697 | DecrCache(cachePtr); | |||
| 698 | } | |||
| 699 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 700 | } | |||
| 701 | if (itPtr->adp.debugLevel > 0) { | |||
| 702 | --itPtr->adp.debugLevel; | |||
| 703 | } | |||
| 704 | ||||
| 705 | done: | |||
| 706 | Ns_DStringFreeTcl_DStringFree(&path); | |||
| 707 | Ns_DStringFreeTcl_DStringFree(&tmp); | |||
| 708 | ||||
| 709 | return result; | |||
| 710 | } | |||
| 711 | ||||
| 712 | ||||
| 713 | /* | |||
| 714 | *---------------------------------------------------------------------- | |||
| 715 | * | |||
| 716 | * NsAdpDebug -- | |||
| 717 | * | |||
| 718 | * Initialize the debugger by calling the debug init proc with | |||
| 719 | * the hostname and port of the debugger and a pattern of procs | |||
| 720 | * to auto-instrument. | |||
| 721 | * | |||
| 722 | * Results: | |||
| 723 | * TCL_OK if debugger initialized, TCL_ERROR otherwise. | |||
| 724 | * | |||
| 725 | * Side effects: | |||
| 726 | * Interp is marked for delete on next deallocation. | |||
| 727 | * | |||
| 728 | *---------------------------------------------------------------------- | |||
| 729 | */ | |||
| 730 | ||||
| 731 | int | |||
| 732 | NsAdpDebug(NsInterp *itPtr, const char *host, const char *port, const char *procs) | |||
| 733 | { | |||
| 734 | Tcl_Interp *interp; | |||
| 735 | Tcl_DString ds; | |||
| 736 | int result; | |||
| 737 | ||||
| 738 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 739 | ||||
| 740 | interp = itPtr->interp; | |||
| 741 | result = TCL_OK0; | |||
| 742 | ||||
| 743 | if (itPtr->adp.debugInit == 0) { | |||
| 744 | itPtr->deleteInterp = NS_TRUE1; | |||
| 745 | Tcl_DStringInit(&ds); | |||
| 746 | Tcl_DStringAppendElement(&ds, itPtr->servPtr->adp.debuginit); | |||
| 747 | Tcl_DStringAppendElement(&ds, (procs != NULL((void*)0)) ? procs : NS_EMPTY_STRING); | |||
| 748 | Tcl_DStringAppendElement(&ds, (host != NULL((void*)0)) ? host : NS_EMPTY_STRING); | |||
| 749 | Tcl_DStringAppendElement(&ds, (port != NULL((void*)0)) ? port : NS_EMPTY_STRING); | |||
| 750 | result = Tcl_EvalEx(interp, ds.string, ds.length, 0); | |||
| 751 | Tcl_DStringFree(&ds); | |||
| 752 | if (result != TCL_OK0) { | |||
| 753 | NsAdpLogError(itPtr); | |||
| 754 | result = TCL_ERROR1; | |||
| 755 | ||||
| 756 | } else { | |||
| 757 | /* | |||
| 758 | * Link the ADP output buffer result to a global variable | |||
| 759 | * which can be monitored with a variable watch. | |||
| 760 | */ | |||
| 761 | ||||
| 762 | if (Tcl_LinkVar(interp, "ns_adp_output", | |||
| 763 | (char *) &itPtr->adp.output.string, | |||
| 764 | TCL_LINK_STRING4 | TCL_LINK_READ_ONLY0x80) != TCL_OK0) { | |||
| 765 | NsAdpLogError(itPtr); | |||
| 766 | } | |||
| 767 | ||||
| 768 | itPtr->adp.debugInit = 1; | |||
| 769 | itPtr->adp.debugLevel = 1; | |||
| 770 | } | |||
| 771 | } | |||
| 772 | return result; | |||
| 773 | } | |||
| 774 | ||||
| 775 | ||||
| 776 | /* | |||
| 777 | *---------------------------------------------------------------------- | |||
| 778 | * | |||
| 779 | * NsTclAdpStatsObjCmd -- | |||
| 780 | * | |||
| 781 | * Implements "ns_adp_stats". This command returns statistics about | |||
| 782 | * cached ADP pages. | |||
| 783 | * | |||
| 784 | * Results: | |||
| 785 | * Standard Tcl result. | |||
| 786 | * | |||
| 787 | * Side effects: | |||
| 788 | * None. | |||
| 789 | * | |||
| 790 | *---------------------------------------------------------------------- | |||
| 791 | */ | |||
| 792 | ||||
| 793 | int | |||
| 794 | NsTclAdpStatsObjCmd(ClientData clientData, Tcl_Interp *interp, | |||
| 795 | int UNUSED(objc)UNUSED_objc __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__))) | |||
| 796 | { | |||
| 797 | const NsInterp *itPtr = clientData; | |||
| 798 | NsServer *servPtr = itPtr->servPtr; | |||
| 799 | Ns_DStringTcl_DString ds; | |||
| 800 | Tcl_HashSearch search; | |||
| 801 | Tcl_HashEntry *hPtr; | |||
| 802 | ||||
| 803 | Ns_DStringInitTcl_DStringInit(&ds); | |||
| 804 | ||||
| 805 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 806 | hPtr = Tcl_FirstHashEntry(&servPtr->adp.pages, &search); | |||
| 807 | while (hPtr != NULL((void*)0)) { | |||
| 808 | const Page *pagePtr = Tcl_GetHashValue(hPtr)((hPtr)->clientData); | |||
| 809 | char *file = Tcl_GetHashKey(&servPtr->adp.pages, hPtr)((void *) (((&servPtr->adp.pages)->keyType == (1) || (&servPtr->adp.pages)->keyType == (-1)) ? (hPtr)-> key.oneWordValue : (hPtr)->key.string)); | |||
| 810 | ||||
| 811 | Ns_DStringPrintf(&ds, "{%s} " | |||
| 812 | "{dev %" PRIu64"l" "u" " ino %" PRIu64"l" "u" " mtime %" PRIu64"l" "u" " " | |||
| 813 | "refcnt %d evals %d size %" PROTd"l" "d"" blocks %d scripts %d} ", | |||
| 814 | file, | |||
| 815 | (uint64_t) pagePtr->dev, (uint64_t) pagePtr->ino, (uint64_t) pagePtr->mtime, | |||
| 816 | pagePtr->refcnt, pagePtr->evals, pagePtr->size, | |||
| 817 | pagePtr->code.nblocks, pagePtr->code.nscripts); | |||
| 818 | hPtr = Tcl_NextHashEntry(&search); | |||
| 819 | } | |||
| 820 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 821 | ||||
| 822 | Tcl_DStringResult(interp, &ds); | |||
| 823 | ||||
| 824 | return TCL_OK0; | |||
| 825 | } | |||
| 826 | ||||
| 827 | ||||
| 828 | /* | |||
| 829 | *---------------------------------------------------------------------- | |||
| 830 | * | |||
| 831 | * ParseFile -- | |||
| 832 | * | |||
| 833 | * Read and parse text from a file. The code is complicated | |||
| 834 | * somewhat to account for changing files. | |||
| 835 | * | |||
| 836 | * Results: | |||
| 837 | * Pointer to new Page structure or NULL on error. | |||
| 838 | * | |||
| 839 | * Side effects: | |||
| 840 | * Error message will be left in interp on failure. | |||
| 841 | * | |||
| 842 | *---------------------------------------------------------------------- | |||
| 843 | */ | |||
| 844 | ||||
| 845 | static Page * | |||
| 846 | ParseFile(const NsInterp *itPtr, const char *file, struct stat *stPtr, unsigned int flags) | |||
| 847 | { | |||
| 848 | Tcl_Interp *interp; | |||
| 849 | Tcl_Encoding encoding; | |||
| 850 | Tcl_DString utf; | |||
| 851 | char *buf = NULL((void*)0); | |||
| 852 | int fd, tries = 0; | |||
| 853 | size_t size = 0u; | |||
| 854 | ssize_t n; | |||
| 855 | Page *pagePtr = NULL((void*)0); | |||
| 856 | ||||
| 857 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 858 | NS_NONNULL_ASSERT(file != NULL)((void) (0)); | |||
| 859 | NS_NONNULL_ASSERT(stPtr != NULL)((void) (0)); | |||
| 860 | ||||
| 861 | interp = itPtr->interp; | |||
| 862 | ||||
| 863 | fd = ns_openopen(file, O_RDONLY00 | O_BINARY(0) | O_CLOEXEC02000000, 0); | |||
| 864 | if (fd < 0) { | |||
| 865 | Ns_TclPrintfResult(interp, "could not open \"%s\": %s", | |||
| 866 | file, Tcl_PosixError(interp)); | |||
| 867 | return NULL((void*)0); | |||
| 868 | } | |||
| 869 | ||||
| 870 | do { | |||
| 871 | /* | |||
| 872 | * fstat() the open file to ensure it has not changed or been replaced | |||
| 873 | * since the original stat(). | |||
| 874 | */ | |||
| 875 | ||||
| 876 | if (fstat(fd, stPtr) != 0) { | |||
| 877 | Ns_TclPrintfResult(interp, "could not fstat \"%s\": %s", | |||
| 878 | file, Tcl_PosixError(interp)); | |||
| 879 | goto done; | |||
| 880 | } | |||
| 881 | size = (size_t)stPtr->st_size; | |||
| 882 | buf = ns_realloc(buf, size + 1u); | |||
| 883 | ||||
| 884 | /* | |||
| 885 | * Attempt to read +1 byte to catch the file growing. | |||
| 886 | */ | |||
| 887 | ||||
| 888 | n = ns_readread(fd, buf, size + 1u); | |||
| 889 | if (n < 0) { | |||
| 890 | Ns_TclPrintfResult(interp, "could not read \"%s\": %s", | |||
| 891 | file, Tcl_PosixError(interp)); | |||
| 892 | goto done; | |||
| 893 | } | |||
| 894 | if ((size_t)n != size) { | |||
| 895 | /* | |||
| 896 | * File is not expected size, rewind and fstat/read again. | |||
| 897 | */ | |||
| 898 | ||||
| 899 | if (ns_lseeklseek(fd, (off_t) 0, SEEK_SET0) != 0) { | |||
| 900 | Ns_TclPrintfResult(interp, "could not lseek \"%s\": %s", | |||
| 901 | file, Tcl_PosixError(interp)); | |||
| 902 | goto done; | |||
| 903 | } | |||
| 904 | Ns_ThreadYield(); | |||
| 905 | } | |||
| 906 | } while ((size_t)n != size && ++tries < 10); | |||
| 907 | ||||
| 908 | if ((size_t)n != size) { | |||
| 909 | Ns_TclPrintfResult(interp, "inconsistent file: %s", file); | |||
| 910 | } else { | |||
| 911 | char *page; | |||
| 912 | ||||
| 913 | buf[n] = '\0'; | |||
| 914 | Tcl_DStringInit(&utf); | |||
| 915 | encoding = Ns_GetFileEncoding(file); | |||
| 916 | if (encoding == NULL((void*)0)) { | |||
| 917 | page = buf; | |||
| 918 | } else { | |||
| 919 | page = Tcl_ExternalToUtfDString(encoding, buf, (int)n, &utf); | |||
| 920 | } | |||
| 921 | pagePtr = ns_malloc(sizeof(Page)); | |||
| 922 | pagePtr->servPtr = itPtr->servPtr; | |||
| 923 | pagePtr->flags = flags; | |||
| 924 | pagePtr->refcnt = 0; | |||
| 925 | pagePtr->evals = 0; | |||
| 926 | pagePtr->locked = NS_FALSE0; | |||
| 927 | pagePtr->cacheGen = 0; | |||
| 928 | pagePtr->cachePtr = NULL((void*)0); | |||
| 929 | pagePtr->mtime = stPtr->st_mtimest_mtim.tv_sec; | |||
| 930 | pagePtr->size = stPtr->st_size; | |||
| 931 | pagePtr->dev = stPtr->st_dev; | |||
| 932 | pagePtr->ino = stPtr->st_ino; | |||
| 933 | NsAdpParse(&pagePtr->code, itPtr->servPtr, page, flags, file); | |||
| 934 | Tcl_DStringFree(&utf); | |||
| 935 | } | |||
| 936 | ||||
| 937 | done: | |||
| 938 | ns_free(buf); | |||
| 939 | (void) ns_closeclose(fd); | |||
| 940 | ||||
| 941 | return pagePtr; | |||
| 942 | } | |||
| 943 | ||||
| 944 | ||||
| 945 | /* | |||
| 946 | *---------------------------------------------------------------------- | |||
| 947 | * | |||
| 948 | * NsAdpLogError -- | |||
| 949 | * | |||
| 950 | * Log an ADP error, possibly invoking the log handling ADP | |||
| 951 | * file if configured. | |||
| 952 | * | |||
| 953 | * Results: | |||
| 954 | * None. | |||
| 955 | * | |||
| 956 | * Side effects: | |||
| 957 | * Depends on log handler. | |||
| 958 | * | |||
| 959 | *---------------------------------------------------------------------- | |||
| 960 | */ | |||
| 961 | ||||
| 962 | void | |||
| 963 | NsAdpLogError(NsInterp *itPtr) | |||
| 964 | { | |||
| 965 | Tcl_Interp *interp; | |||
| 966 | const Ns_Conn *conn; | |||
| 967 | Ns_DStringTcl_DString ds; | |||
| 968 | const AdpFrame *framePtr; | |||
| 969 | int len; | |||
| 970 | const char *err, *adp, *inc, *dot; | |||
| 971 | ||||
| 972 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 973 | ||||
| 974 | interp = itPtr->interp; | |||
| 975 | conn = itPtr->conn; | |||
| 976 | ||||
| 977 | framePtr = itPtr->adp.framePtr; | |||
| 978 | Ns_DStringInitTcl_DStringInit(&ds); | |||
| 979 | ||||
| 980 | if (framePtr != NULL((void*)0)) { | |||
| 981 | Ns_DStringPrintf(&ds, "\n at line %d of ", | |||
| 982 | (int)framePtr->line + Tcl_GetErrorLine(interp)); | |||
| 983 | } | |||
| 984 | inc = NS_EMPTY_STRING; | |||
| 985 | while (framePtr != NULL((void*)0)) { | |||
| 986 | if (framePtr->file != NULL((void*)0)) { | |||
| 987 | Ns_DStringPrintf(&ds, "%sadp file \"%s\"", inc, framePtr->file); | |||
| 988 | if (framePtr->ident != NULL((void*)0)) { | |||
| 989 | Ns_DStringPrintf(&ds, " {%s}", Tcl_GetString(framePtr->ident)); | |||
| 990 | } | |||
| 991 | } else { | |||
| 992 | adp = Tcl_GetStringFromObj(framePtr->objv[0], &len); | |||
| 993 | dot = NS_EMPTY_STRING; | |||
| 994 | if (len > 150) { | |||
| 995 | len = 150; | |||
| 996 | dot = "..."; | |||
| 997 | } | |||
| 998 | while (((unsigned char)adp[len] & 0xC0u) == 0x80u) { | |||
| 999 | /* | |||
| 1000 | * Avoid truncating multi-byte UTF-8 character. | |||
| 1001 | */ | |||
| 1002 | len--; | |||
| 1003 | dot = "..."; | |||
| 1004 | } | |||
| 1005 | Ns_DStringPrintf(&ds, "%sadp script:\n\"%.*s%s\"", | |||
| 1006 | inc, len, adp, dot); | |||
| 1007 | } | |||
| 1008 | framePtr = framePtr->prevPtr; | |||
| 1009 | inc = "\n included from "; | |||
| 1010 | } | |||
| 1011 | if (conn != NULL((void*)0) && (itPtr->adp.flags & ADP_DETAIL0x80u) != 0u) { | |||
| 1012 | size_t i; | |||
| 1013 | ||||
| 1014 | Ns_DStringPrintf(&ds, "\n while processing connection %s:\n%8s%s", | |||
| 1015 | NsConnIdStr(conn), NS_EMPTY_STRING, | |||
| 1016 | conn->request.line); | |||
| 1017 | for (i = 0u; i < Ns_SetSize(conn->headers)((conn->headers)->size); ++i) { | |||
| 1018 | Ns_DStringPrintf(&ds, "\n %s: %s", | |||
| 1019 | Ns_SetKey(conn->headers, i)((conn->headers)->fields[(i)].name), | |||
| 1020 | Ns_SetValue(conn->headers, i)((conn->headers)->fields[(i)].value)); | |||
| 1021 | } | |||
| 1022 | } | |||
| 1023 | err = Ns_TclLogErrorInfo(interp, ds.string); | |||
| 1024 | if ((itPtr->adp.flags & ADP_DISPLAY0x200u) != 0u) { | |||
| 1025 | Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0); | |||
| 1026 | Ns_DStringAppend(&ds, "<br><pre>\n")Tcl_DStringAppend((&ds), ("<br><pre>\n"), -1); | |||
| 1027 | Ns_QuoteHtml(&ds, err); | |||
| 1028 | Ns_DStringAppend(&ds, "\n<br></pre>\n")Tcl_DStringAppend((&ds), ("\n<br></pre>\n"), - 1); | |||
| 1029 | (void)NsAdpAppend(itPtr, ds.string, ds.length); | |||
| 1030 | } | |||
| 1031 | Ns_DStringFreeTcl_DStringFree(&ds); | |||
| 1032 | adp = itPtr->servPtr->adp.errorpage; | |||
| 1033 | if (adp != NULL((void*)0) && itPtr->adp.errorLevel == 0) { | |||
| 1034 | Tcl_Obj *objv[2]; | |||
| 1035 | ||||
| 1036 | ++itPtr->adp.errorLevel; | |||
| 1037 | objv[0] = Tcl_NewStringObj(adp, -1); | |||
| 1038 | Tcl_IncrRefCount(objv[0])++(objv[0])->refCount; | |||
| 1039 | objv[1] = Tcl_GetVar2Ex(interp, "errorInfo", NULL((void*)0), TCL_GLOBAL_ONLY1); | |||
| 1040 | if (objv[1] == NULL((void*)0)) { | |||
| 1041 | objv[1] = Tcl_GetObjResult(interp); | |||
| 1042 | } | |||
| 1043 | (void) NsAdpInclude(itPtr, 2, objv, adp, NULL((void*)0)); | |||
| 1044 | Tcl_DecrRefCount(objv[0])do { Tcl_Obj *_objPtr = (objv[0]); if (_objPtr->refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
| 1045 | --itPtr->adp.errorLevel; | |||
| 1046 | } | |||
| 1047 | } | |||
| 1048 | ||||
| 1049 | ||||
| 1050 | /* | |||
| 1051 | *---------------------------------------------------------------------- | |||
| 1052 | * | |||
| 1053 | * AdpExec -- | |||
| 1054 | * | |||
| 1055 | * Execute ADP code. | |||
| 1056 | * | |||
| 1057 | * Results: | |||
| 1058 | * TCL_OK unless there is an ADP error exception, stack overflow, | |||
| 1059 | * or script error when the ADP_STRICT option is set. | |||
| 1060 | * | |||
| 1061 | * Side Effects: | |||
| 1062 | * Depends on page. | |||
| 1063 | * | |||
| 1064 | *---------------------------------------------------------------------- | |||
| 1065 | */ | |||
| 1066 | ||||
| 1067 | static int | |||
| 1068 | AdpExec(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *file, | |||
| 1069 | const AdpCode *codePtr, Objs *objsPtr, Tcl_DString *outputPtr, | |||
| 1070 | const struct stat *stPtr) | |||
| 1071 | { | |||
| 1072 | Tcl_Interp *interp; | |||
| 1073 | AdpFrame frame; | |||
| 1074 | Ns_DStringTcl_DString cwd; | |||
| 1075 | Tcl_Obj *objPtr; | |||
| 1076 | int nscript, nblocks, result, i; | |||
| 1077 | const char *ptr, *savecwd; | |||
| 1078 | ||||
| 1079 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 1080 | NS_NONNULL_ASSERT(codePtr != NULL)((void) (0)); | |||
| 1081 | NS_NONNULL_ASSERT(outputPtr != NULL)((void) (0)); | |||
| 1082 | ||||
| 1083 | interp = itPtr->interp; | |||
| 1084 | ||||
| 1085 | /* | |||
| 1086 | * Setup the new call frame. | |||
| 1087 | */ | |||
| 1088 | ||||
| 1089 | Ns_DStringInitTcl_DStringInit(&cwd); | |||
| 1090 | frame.file = file; | |||
| 1091 | frame.objc = (unsigned short)objc; | |||
| 1092 | frame.objv = (Tcl_Obj **)objv; | |||
| 1093 | if (stPtr != NULL((void*)0)) { | |||
| 1094 | frame.size = stPtr->st_size; | |||
| 1095 | frame.mtime = stPtr->st_mtimest_mtim.tv_sec; | |||
| 1096 | } else { | |||
| 1097 | frame.size = 0; | |||
| 1098 | frame.mtime = 0; | |||
| 1099 | } | |||
| 1100 | frame.outputPtr = outputPtr; | |||
| 1101 | frame.ident = NULL((void*)0); | |||
| 1102 | savecwd = itPtr->adp.cwd; | |||
| 1103 | if (file != NULL((void*)0)) { | |||
| 1104 | const char *slash = strrchr(file, INTCHAR('/')((int)((unsigned char)(('/'))))); | |||
| 1105 | if (slash != NULL((void*)0)) { | |||
| 1106 | Ns_DStringNAppendTcl_DStringAppend(&cwd, file, (int)(slash - file)); | |||
| 1107 | itPtr->adp.cwd = cwd.string; | |||
| 1108 | } | |||
| 1109 | } | |||
| 1110 | frame.prevPtr = itPtr->adp.framePtr; | |||
| 1111 | itPtr->adp.framePtr = &frame; | |||
| 1112 | itPtr->adp.depth++; | |||
| 1113 | ||||
| 1114 | /* | |||
| 1115 | * Execute the ADP by copying text blocks directly to the output | |||
| 1116 | * stream and evaluating script blocks. | |||
| 1117 | */ | |||
| 1118 | ||||
| 1119 | ptr = AdpCodeText(codePtr)((codePtr)->text.string); | |||
| 1120 | nblocks = AdpCodeBlocks(codePtr)((codePtr)->nblocks); | |||
| 1121 | nscript = 0; | |||
| 1122 | result = TCL_OK0; | |||
| 1123 | for (i = 0; itPtr->adp.exception == ADP_OK && i < nblocks; ++i) { | |||
| 1124 | int len; | |||
| 1125 | ||||
| 1126 | frame.line = (unsigned short)AdpCodeLine(codePtr, i)((codePtr)->line[(i)]); | |||
| 1127 | len = AdpCodeLen(codePtr, i)((codePtr)->len[(i)]); | |||
| 1128 | if ((itPtr->adp.flags & ADP_TRACE0x20u) != 0u) { | |||
| 1129 | AdpTrace(itPtr, ptr, len); | |||
| 1130 | } | |||
| 1131 | if (len > 0) { | |||
| 1132 | result = NsAdpAppend(itPtr, ptr, len); | |||
| 1133 | } else { | |||
| 1134 | len = -len; | |||
| 1135 | if (itPtr->adp.debugLevel > 0) { | |||
| 1136 | result = AdpDebug(itPtr, ptr, len, nscript); | |||
| 1137 | } else if (objsPtr == NULL((void*)0)) { | |||
| 1138 | result = Tcl_EvalEx(interp, ptr, len, 0); | |||
| 1139 | } else { | |||
| 1140 | assert(nscript < objsPtr->nobjs)((void) (0)); | |||
| 1141 | objPtr = objsPtr->objs[nscript]; | |||
| 1142 | if (objPtr == NULL((void*)0)) { | |||
| 1143 | objPtr = Tcl_NewStringObj(ptr, len); | |||
| 1144 | Tcl_IncrRefCount(objPtr)++(objPtr)->refCount; | |||
| 1145 | objsPtr->objs[nscript] = objPtr; | |||
| 1146 | } | |||
| 1147 | result = Tcl_EvalObjEx(interp, objPtr, 0); | |||
| 1148 | } | |||
| 1149 | ++nscript; | |||
| 1150 | ||||
| 1151 | /* | |||
| 1152 | * Propagate NS_TIMEOUT errors from Tcl code. | |||
| 1153 | */ | |||
| 1154 | ||||
| 1155 | if (result == TCL_ERROR1) { | |||
| 1156 | if (NsTclTimeoutException(interp) == NS_TRUE1) { | |||
| 1157 | itPtr->adp.exception = ADP_TIMEOUT; | |||
| 1158 | } | |||
| 1159 | } | |||
| 1160 | } | |||
| 1161 | ||||
| 1162 | /* | |||
| 1163 | * Log an error message and optionally break from this ADP | |||
| 1164 | * call frame unless the error was generated to signal | |||
| 1165 | * and ADP exception. | |||
| 1166 | */ | |||
| 1167 | ||||
| 1168 | if (result != TCL_OK0 && itPtr->adp.exception == ADP_OK) { | |||
| 1169 | if ((itPtr->adp.flags & ADP_ERRLOGGED0x1000u) == 0u) { | |||
| 1170 | NsAdpLogError(itPtr); | |||
| 1171 | } | |||
| 1172 | if ((itPtr->adp.flags & ADP_STRICT0x100u) != 0u) { | |||
| 1173 | itPtr->adp.flags |= ADP_ERRLOGGED0x1000u; | |||
| 1174 | break; | |||
| 1175 | } | |||
| 1176 | } | |||
| 1177 | ptr += len; | |||
| 1178 | } | |||
| 1179 | ||||
| 1180 | /* | |||
| 1181 | * Clear the return exception and reset result. | |||
| 1182 | */ | |||
| 1183 | ||||
| 1184 | switch (itPtr->adp.exception) { | |||
| 1185 | case ADP_OK: | |||
| 1186 | break; | |||
| 1187 | case ADP_RETURN: | |||
| 1188 | itPtr->adp.exception = ADP_OK; | |||
| 1189 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
| 1190 | case ADP_ABORT: | |||
| 1191 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
| 1192 | case ADP_BREAK: | |||
| 1193 | NS_FALL_THROUGH((void)0); /* fall through */ | |||
| 1194 | case ADP_TIMEOUT: | |||
| 1195 | result = TCL_OK0; | |||
| 1196 | break; | |||
| 1197 | } | |||
| 1198 | ||||
| 1199 | /* | |||
| 1200 | * Restore the previous call frame. | |||
| 1201 | */ | |||
| 1202 | ||||
| 1203 | itPtr->adp.framePtr = frame.prevPtr; | |||
| 1204 | itPtr->adp.cwd = savecwd; | |||
| 1205 | if (frame.ident != NULL((void*)0)) { | |||
| 1206 | Tcl_DecrRefCount(frame.ident)do { Tcl_Obj *_objPtr = (frame.ident); if (_objPtr->refCount -- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
| 1207 | } | |||
| 1208 | Ns_DStringFreeTcl_DStringFree(&cwd); | |||
| 1209 | ||||
| 1210 | return result; | |||
| 1211 | } | |||
| 1212 | ||||
| 1213 | ||||
| 1214 | /* | |||
| 1215 | *---------------------------------------------------------------------- | |||
| 1216 | * | |||
| 1217 | * AdpDebug -- | |||
| 1218 | * | |||
| 1219 | * Evaluate an ADP script block with the TclPro debugger. | |||
| 1220 | * | |||
| 1221 | * Results: | |||
| 1222 | * Depends on script. | |||
| 1223 | * | |||
| 1224 | * Side effects: | |||
| 1225 | * A unique temp file with header comments and the script is | |||
| 1226 | * created and sourced, the effect of which is TclPro will | |||
| 1227 | * instrument the code on the fly for single-step debugging. | |||
| 1228 | * | |||
| 1229 | *---------------------------------------------------------------------- | |||
| 1230 | */ | |||
| 1231 | ||||
| 1232 | static int | |||
| 1233 | AdpDebug(const NsInterp *itPtr, const char *ptr, int len, int nscript) | |||
| 1234 | { | |||
| 1235 | Tcl_Interp *interp; | |||
| 1236 | int level; | |||
| 1237 | const char *file; | |||
| 1238 | char debugfile[255]; | |||
| 1239 | Ns_DStringTcl_DString ds; | |||
| 1240 | int result, fd; | |||
| 1241 | ||||
| 1242 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 1243 | NS_NONNULL_ASSERT(ptr != NULL)((void) (0)); | |||
| 1244 | ||||
| 1245 | interp = itPtr->interp; | |||
| 1246 | level = itPtr->adp.debugLevel; | |||
| 1247 | file = Tcl_GetString(itPtr->adp.framePtr->objv[0]); | |||
| 1248 | ||||
| 1249 | Ns_DStringInitTcl_DStringInit(&ds); | |||
| 1250 | Ns_DStringPrintf(&ds, "#\n" | |||
| 1251 | "# level: %d\n" | |||
| 1252 | "# chunk: %d\n" | |||
| 1253 | "# file: %s\n" | |||
| 1254 | "#\n\n", level, nscript, file); | |||
| 1255 | Ns_DStringNAppendTcl_DStringAppend(&ds, ptr, len); | |||
| 1256 | ||||
| 1257 | snprintf(debugfile, sizeof(debugfile),__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript) | |||
| 1258 | P_tmpdir "/adp%d.%d.XXXXXX",__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript) | |||
| 1259 | level, nscript)__builtin___snprintf_chk (debugfile, sizeof(debugfile), 2 - 1 , __builtin_object_size (debugfile, 2 > 1), "/tmp" "/adp%d.%d.XXXXXX" , level, nscript); | |||
| 1260 | fd = ns_mkstempmkstemp(debugfile); | |||
| 1261 | if (fd < 0) { | |||
| 1262 | Ns_TclPrintfResult(interp, "could not create ADP debug file"); | |||
| 1263 | result = TCL_ERROR1; | |||
| 1264 | } else { | |||
| 1265 | if (ns_writewrite(fd, ds.string, (size_t)ds.length) < 0) { | |||
| 1266 | Ns_TclPrintfResult(interp, "write to \"%s\" failed: %s", | |||
| 1267 | debugfile, Tcl_PosixError(interp)); | |||
| 1268 | result = TCL_ERROR1; | |||
| 1269 | } else { | |||
| 1270 | Ns_DStringSetLengthTcl_DStringSetLength(&ds, 0); | |||
| 1271 | Ns_DStringVarAppend(&ds, "source ", debugfile, (char *)0L); | |||
| 1272 | result = Tcl_EvalEx(interp, ds.string, ds.length, 0); | |||
| 1273 | } | |||
| 1274 | (void) ns_closeclose(fd); | |||
| 1275 | unlink(debugfile); | |||
| 1276 | } | |||
| 1277 | Ns_DStringFreeTcl_DStringFree(&ds); | |||
| 1278 | ||||
| 1279 | return result; | |||
| 1280 | } | |||
| 1281 | ||||
| 1282 | ||||
| 1283 | /* | |||
| 1284 | *---------------------------------------------------------------------- | |||
| 1285 | * | |||
| 1286 | * FreeInterpPage -- | |||
| 1287 | * | |||
| 1288 | * Free a per-interp page cache entry. | |||
| 1289 | * | |||
| 1290 | * Results: | |||
| 1291 | * None. | |||
| 1292 | * | |||
| 1293 | * Side Effects: | |||
| 1294 | * None. | |||
| 1295 | * | |||
| 1296 | *---------------------------------------------------------------------- | |||
| 1297 | */ | |||
| 1298 | ||||
| 1299 | static void | |||
| 1300 | FreeInterpPage(void *arg) | |||
| 1301 | { | |||
| 1302 | InterpPage *ipagePtr = arg; | |||
| 1303 | Page *pagePtr = ipagePtr->pagePtr; | |||
| 1304 | NsServer *servPtr = pagePtr->servPtr; | |||
| 1305 | ||||
| 1306 | FreeObjs(ipagePtr->objs); | |||
| ||||
| 1307 | Ns_MutexLock(&servPtr->adp.pagelock); | |||
| 1308 | if (--pagePtr->refcnt == 0) { | |||
| 1309 | if (pagePtr->hPtr != NULL((void*)0)) { | |||
| 1310 | Tcl_DeleteHashEntry(pagePtr->hPtr); | |||
| 1311 | } | |||
| 1312 | if (pagePtr->cachePtr != NULL((void*)0)) { | |||
| 1313 | FreeObjs(ipagePtr->cacheObjs); | |||
| 1314 | DecrCache(pagePtr->cachePtr); | |||
| 1315 | } | |||
| 1316 | NsAdpFreeCode(&pagePtr->code); | |||
| 1317 | ns_free(pagePtr); | |||
| 1318 | } | |||
| 1319 | Ns_MutexUnlock(&servPtr->adp.pagelock); | |||
| 1320 | ns_free(ipagePtr); | |||
| 1321 | } | |||
| 1322 | ||||
| 1323 | ||||
| 1324 | /* | |||
| 1325 | *---------------------------------------------------------------------- | |||
| 1326 | * | |||
| 1327 | * AllocObjs -- | |||
| 1328 | * | |||
| 1329 | * Allocate new page script objects. | |||
| 1330 | * | |||
| 1331 | * Results: | |||
| 1332 | * Pointer to new objects. | |||
| 1333 | * | |||
| 1334 | * Side Effects: | |||
| 1335 | * None. | |||
| 1336 | * | |||
| 1337 | *---------------------------------------------------------------------- | |||
| 1338 | */ | |||
| 1339 | ||||
| 1340 | static Objs * | |||
| 1341 | AllocObjs(int nobjs) | |||
| 1342 | { | |||
| 1343 | Objs *objsPtr; | |||
| 1344 | ||||
| 1345 | objsPtr = ns_calloc(1u, sizeof(Objs) + ((size_t)nobjs * sizeof(Tcl_Obj *))); | |||
| 1346 | objsPtr->nobjs = nobjs; | |||
| 1347 | ||||
| 1348 | return objsPtr; | |||
| 1349 | } | |||
| 1350 | ||||
| 1351 | ||||
| 1352 | /* | |||
| 1353 | *---------------------------------------------------------------------- | |||
| 1354 | * | |||
| 1355 | * FreeObjs -- | |||
| 1356 | * | |||
| 1357 | * Free page objects, decrementing ref counts as needed. | |||
| 1358 | * | |||
| 1359 | * Results: | |||
| 1360 | * None. | |||
| 1361 | * | |||
| 1362 | * Side Effects: | |||
| 1363 | * None. | |||
| 1364 | * | |||
| 1365 | *---------------------------------------------------------------------- | |||
| 1366 | */ | |||
| 1367 | ||||
| 1368 | static void | |||
| 1369 | FreeObjs(Objs *objsPtr) | |||
| 1370 | { | |||
| 1371 | int i; | |||
| 1372 | ||||
| 1373 | NS_NONNULL_ASSERT(objsPtr != NULL)((void) (0)); | |||
| 1374 | ||||
| 1375 | for (i = 0; i < objsPtr->nobjs; ++i) { | |||
| 1376 | if (objsPtr->objs[i] != NULL((void*)0)) { | |||
| ||||
| 1377 | Tcl_DecrRefCount(objsPtr->objs[i])do { Tcl_Obj *_objPtr = (objsPtr->objs[i]); if (_objPtr-> refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0); | |||
| 1378 | } | |||
| 1379 | } | |||
| 1380 | ns_free(objsPtr); | |||
| 1381 | } | |||
| 1382 | ||||
| 1383 | ||||
| 1384 | /* | |||
| 1385 | *---------------------------------------------------------------------- | |||
| 1386 | * | |||
| 1387 | * DecrCache -- | |||
| 1388 | * | |||
| 1389 | * Decrement ref count of a cache entry, potentially freeing | |||
| 1390 | * the cache. | |||
| 1391 | * | |||
| 1392 | * Results: | |||
| 1393 | * None. | |||
| 1394 | * | |||
| 1395 | * Side Effects: | |||
| 1396 | * Will free cache on last reference count. | |||
| 1397 | * | |||
| 1398 | *---------------------------------------------------------------------- | |||
| 1399 | */ | |||
| 1400 | ||||
| 1401 | static void | |||
| 1402 | DecrCache(AdpCache *cachePtr) | |||
| 1403 | { | |||
| 1404 | NS_NONNULL_ASSERT(cachePtr != NULL)((void) (0)); | |||
| 1405 | ||||
| 1406 | if (--cachePtr->refcnt == 0) { | |||
| 1407 | NsAdpFreeCode(&cachePtr->code); | |||
| 1408 | ns_free(cachePtr); | |||
| 1409 | } | |||
| 1410 | } | |||
| 1411 | ||||
| 1412 | ||||
| 1413 | /* | |||
| 1414 | *---------------------------------------------------------------------- | |||
| 1415 | * | |||
| 1416 | * AdpTrace -- | |||
| 1417 | * | |||
| 1418 | * Trace execution of an ADP page. | |||
| 1419 | * | |||
| 1420 | * Results: | |||
| 1421 | * None. | |||
| 1422 | * | |||
| 1423 | * Side effects: | |||
| 1424 | * Dumps tracing info, possibly truncated, via Ns_Log. | |||
| 1425 | * | |||
| 1426 | *---------------------------------------------------------------------- | |||
| 1427 | */ | |||
| 1428 | ||||
| 1429 | static void | |||
| 1430 | AdpTrace(const NsInterp *itPtr, const char *ptr, int len) | |||
| 1431 | { | |||
| 1432 | char type; | |||
| 1433 | ||||
| 1434 | NS_NONNULL_ASSERT(itPtr != NULL)((void) (0)); | |||
| 1435 | NS_NONNULL_ASSERT(ptr != NULL)((void) (0)); | |||
| 1436 | ||||
| 1437 | if (len >= 0) { | |||
| 1438 | type = 'T'; | |||
| 1439 | } else { | |||
| 1440 | type = 'S'; | |||
| 1441 | len = -len; | |||
| 1442 | } | |||
| 1443 | if (len > itPtr->servPtr->adp.tracesize) { | |||
| 1444 | len = itPtr->servPtr->adp.tracesize; | |||
| 1445 | } | |||
| 1446 | Ns_Log(Notice, "adp[%d%c]: %.*s", itPtr->adp.depth, type, len, ptr); | |||
| 1447 | } | |||
| 1448 | ||||
| 1449 | /* | |||
| 1450 | * Local Variables: | |||
| 1451 | * mode: c | |||
| 1452 | * c-basic-offset: 4 | |||
| 1453 | * fill-column: 78 | |||
| 1454 | * indent-tabs-mode: nil | |||
| 1455 | * End: | |||
| 1456 | */ |