Bug Summary

File:d/adpeval.c
Warning:line 1376, column 13
Access out-of-bound array element (buffer overflow)

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name adpeval.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/home/isvv/naviserver/nsd -resource-dir /usr/local/lib/clang/15.0.0 -D _FORTIFY_SOURCE=2 -D NDEBUG -D SYSTEM_MALLOC -I ../include -I /usr/include/tcl8.6 -D HAVE_CONFIG_H -internal-isystem /usr/local/lib/clang/15.0.0/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/11/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -std=c99 -fdebug-compilation-dir=/home/isvv/naviserver/nsd -ferror-limit 19 -stack-protector 2 -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -analyzer-checker alpha -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2022-07-23-130959-11103-1 -x c adpeval.c
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
45typedef 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
56typedef 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
77typedef 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
88typedef 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
99static 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
102static int AdpEval(NsInterp *itPtr, int objc, Tcl_Obj *const* objv, const char *resvar)
103 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
104
105static 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
110static 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
114static 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
117static void DecrCache(AdpCache *cachePtr)
118 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
119
120static Objs *AllocObjs(int nobjs);
121
122static void FreeObjs(Objs *objsPtr)
123 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
124
125static 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
128static Ns_Callback FreeInterpPage;
129static 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
148void
149NsConfigAdp(void)
150{
151 NsRegisterServerInit(ConfigServerAdp);
152}
153
154static Ns_ReturnCode
155ConfigServerAdp(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
227int
228NsAdpEval(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
235int
236NsAdpSource(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
244static int
245AdpEval(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
312int
313NsAdpInclude(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
350void
351NsAdpInit(NsInterp *itPtr)
352{
353 NS_NONNULL_ASSERT(itPtr != NULL)((void) (0));
354
355 Tcl_DStringInit(&itPtr->adp.output);
356 NsAdpReset(itPtr);
357}
358
359void
360NsAdpFree(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
389void
390NsAdpReset(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
429static int
430AdpSource(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
705done:
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
731int
732NsAdpDebug(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
793int
794NsTclAdpStatsObjCmd(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
845static Page *
846ParseFile(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
937done:
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
962void
963NsAdpLogError(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
1067static int
1068AdpExec(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
1232static int
1233AdpDebug(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
1299static void
1300FreeInterpPage(void *arg)
1301{
1302 InterpPage *ipagePtr = arg;
1303 Page *pagePtr = ipagePtr->pagePtr;
1304 NsServer *servPtr = pagePtr->servPtr;
1305
1306 FreeObjs(ipagePtr->objs);
1
Calling 'FreeObjs'
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
1340static Objs *
1341AllocObjs(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
1368static void
1369FreeObjs(Objs *objsPtr)
1370{
1371 int i;
1372
1373 NS_NONNULL_ASSERT(objsPtr != NULL)((void) (0));
1374
1375 for (i = 0; i < objsPtr->nobjs; ++i) {
2
Assuming 'i' is < field 'nobjs'
3
Loop condition is true. Entering loop body
6
Assuming 'i' is < field 'nobjs'
7
Loop condition is true. Entering loop body
1376 if (objsPtr->objs[i] != NULL((void*)0)) {
4
Assuming the condition is false
5
Taking false branch
8
Access out-of-bound array element (buffer overflow)
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
1401static void
1402DecrCache(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
1429static void
1430AdpTrace(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 */