Bug Summary

File:d/tclobjv.c
Warning:line 1714, column 24
Using a fixed address is not portable because that address will probably not be valid in all environments or platforms

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 tclobjv.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 tclobjv.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 * Alternatively, the contents of this file may be used under the terms
13 * of the GNU General Public License (the "GPL"), in which case the
14 * provisions of GPL are applicable instead of those above. If you wish
15 * to allow use of your version of this file only under the terms of the
16 * GPL and not to allow others to use your version of this file under the
17 * License, indicate your decision by deleting the provisions above and
18 * replace them with the notice and other provisions required by the GPL.
19 * If you do not delete the provisions above, a recipient may use your
20 * version of this file under either the License or the GPL.
21 */
22
23
24/*
25 * tclobjv.c --
26 *
27 * Routines for parsing the options and arguments passed to Tcl commands.
28 *
29 */
30
31#include "nsd.h"
32
33#define VALUE_SUPPLIED(((void *)(intptr_t)(1))) (INT2PTR(NS_TRUE)((void *)(intptr_t)(1)))
34
35
36/*
37 * Static functions defined in this file.
38 */
39
40static Tcl_FreeInternalRepProc FreeSpecObj;
41static Tcl_DupInternalRepProc DupSpec;
42static Tcl_UpdateStringProc UpdateStringOfSpec;
43static Tcl_SetFromAnyProc SetSpecFromAny;
44
45static Ns_ObjvProc ObjvTcl;
46static Ns_ObjvProc ObjvTclArgs;
47
48static void FreeSpecs(Ns_ObjvSpec *specPtr)
49 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
50
51static int SetValue(Tcl_Interp *interp, const char *key, Tcl_Obj *valueObj)
52 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
53
54static void WrongNumArgs(const Ns_ObjvSpec *optSpec, Ns_ObjvSpec *argSpec,
55 Tcl_Interp *interp, int objc, Tcl_Obj *const* objv);
56
57static int GetOptIndexObjvSpec(Tcl_Obj *obj, const Ns_ObjvSpec *tablePtr, int *idxPtr)
58 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3)));
59static int GetOptIndexSubcmdSpec(Tcl_Interp *interp, Tcl_Obj *obj, const char *msg, const Ns_SubCmdSpec *tablePtr, int *idxPtr)
60 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2))) NS_GNUC_NONNULL(3)__attribute__((__nonnull__(3))) NS_GNUC_NONNULL(4)__attribute__((__nonnull__(4))) NS_GNUC_NONNULL(5)__attribute__((__nonnull__(5)));
61
62static void UpdateStringOfMemUnit(Tcl_Obj *objPtr)
63 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1)));
64
65static int SetMemUnitFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
66 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
67
68static void AppendRange(Ns_DStringTcl_DString *dsPtr, const Ns_ObjvValueRange *r)
69 NS_GNUC_NONNULL(1)__attribute__((__nonnull__(1))) NS_GNUC_NONNULL(2)__attribute__((__nonnull__(2)));
70
71/*
72 * Static variables defined in this file.
73 */
74static const Tcl_ObjType *intTypePtr;
75
76static const Tcl_ObjType specType = {
77 "ns:spec",
78 FreeSpecObj,
79 DupSpec,
80 UpdateStringOfSpec,
81 SetSpecFromAny
82};
83
84static const Tcl_ObjType memUnitType = {
85 "ns:mem_unit",
86 NULL((void*)0),
87 NULL((void*)0),
88 UpdateStringOfMemUnit,
89 SetMemUnitFromAny
90};
91
92
93/*
94 *----------------------------------------------------------------------
95 *
96 * NsTclInitSpecType --
97 *
98 * Initialize ns:spec Tcl_Obj type.
99 *
100 * Results:
101 * None.
102 *
103 * Side effects:
104 * None.
105 *
106 *----------------------------------------------------------------------
107 */
108
109void
110NsTclInitSpecType(void)
111{
112 Tcl_RegisterObjType(&specType);
113}
114
115
116/*
117 *----------------------------------------------------------------------
118 *
119 * GetOptIndexObjvSpec --
120 *
121 * Process options similar to Tcl_GetIndexFromObj() but allow
122 * only options (starting with a "-"), allow only exact matches
123 * and don't cache results as internal reps.
124 *
125 * Background: Tcl_GetIndexFromObj() validates internal reps
126 * based on the pointer of the base string table, which works
127 * only reliably with *static* string tables. Since NaviServer
128 * can't use static string tables, these tables are allocated on
129 * the stack. This can lead to mix-ups for shared objects with
130 * the consequence the resulting indices might be incorrect,
131 * leading to potential crashes. In order to allow caching, it
132 * should be possible to validate the entries based on other
133 * means, but this requires a different interface.
134 *
135 * Results:
136 * Tcl result.
137 *
138 * Side effects:
139 * None.
140 *
141 *----------------------------------------------------------------------
142 */
143static int
144GetOptIndexObjvSpec(Tcl_Obj *obj, const Ns_ObjvSpec *tablePtr, int *idxPtr)
145{
146 const char *key;
147 int result = TCL_ERROR1;
148
149 NS_NONNULL_ASSERT(obj != NULL)((void) (0));
150 NS_NONNULL_ASSERT(tablePtr != NULL)((void) (0));
151 NS_NONNULL_ASSERT(idxPtr != NULL)((void) (0));
152
153 key = Tcl_GetString(obj);
154 if (*key == '-') {
155 const Ns_ObjvSpec *entryPtr;
156 int idx;
157
158 for (entryPtr = tablePtr, idx = 0; entryPtr->key != NULL((void*)0); entryPtr++, idx++) {
159 const char *p1, *p2;
160
161 for (p1 = key, p2 = entryPtr->key; *p1 == *p2; p1++, p2++) {
162 if (*p1 == '\0') {
163 /*
164 * Both words are at their ends. Match is successful.
165 */
166 *idxPtr = idx;
167 result = TCL_OK0;
168 break;
169 }
170 }
171 if (*p1 == '\0') {
172 /*
173 * The value is an abbreviation for this entry or was
174 * matched in the inner loop.
175 */
176 break;
177 }
178 }
179 }
180
181 return result;
182}
183
184
185/*
186 *----------------------------------------------------------------------
187 *
188 * Ns_ParseObjv --
189 *
190 * Process objv according to given option and arg specs.
191 *
192 * Results:
193 * NS_OK or NS_ERROR.
194 *
195 * Side effects:
196 * Depends on the Ns_ObjvTypeProcs which run.
197 *
198 *----------------------------------------------------------------------
199 */
200Ns_ReturnCode
201Ns_ParseObjv(Ns_ObjvSpec *optSpec, Ns_ObjvSpec *argSpec, Tcl_Interp *interp,
202 int offset, int objc, Tcl_Obj *const* objv)
203{
204 Ns_ObjvSpec *specPtr;
205 int optIndex, requiredArgs = 0, remain = (objc - offset);
206
207 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
208
209 /*
210 * In case, the number of actual arguments is equal to the number
211 * of required arguments, skip option processing and use the
212 * provided argument for the required arguments. This way,
213 * e.g. "ns_md5 --" will compute the checksum of "--" instead of
214 * spitting out an error message about a missing input string.
215 */
216 if (likely(argSpec != NULL)(__builtin_expect((argSpec != ((void*)0)), 1)) && likely(optSpec != NULL)(__builtin_expect((optSpec != ((void*)0)), 1))) {
217 /*
218 * Count required args.
219 */
220 for (specPtr = argSpec; specPtr != NULL((void*)0) && specPtr->key != NULL((void*)0); specPtr++) {
221 if (unlikely(specPtr->key[0] == '?')(__builtin_expect((specPtr->key[0] == '?'), 0))) {
222 break;
223 }
224 requiredArgs++;
225 }
226 if (requiredArgs+offset == objc) {
227 /*
228 * No need to process optional parameters.
229 */
230 optSpec = NULL((void*)0);
231 }
232 }
233
234 if (likely(optSpec != NULL)(__builtin_expect((optSpec != ((void*)0)), 1)) && likely(optSpec->key != NULL)(__builtin_expect((optSpec->key != ((void*)0)), 1))) {
235
236 while (remain > 0) {
237 Tcl_Obj *obj = objv[objc - remain];
238 int result;
239
240#ifdef NS_TCL_PRE87
241 /*
242 * In case a Tcl_Obj has no stringrep (e.g. a pure/proper
243 * byte array), it is assumed that this cannot be an
244 * option flag (starting with a '-'). Since
245 * GetOptIndexObjvSpec() and Tcl_GetIndexFromObjStruct()
246 * create on demand string representations, the "pure"
247 * property will be lost and Tcl cannot distinguish later
248 * whether it can use the string representation as byte
249 * array or not. Fortunately, this dangerous fragility is
250 * gone in Tcl 8.7.
251 */
252 if (obj->bytes == NULL((void*)0)) {
253 break;
254 }
255#endif
256 result = Tcl_IsShared(obj)((obj)->refCount > 1) ?
257 GetOptIndexObjvSpec(obj, optSpec, &optIndex) :
258 Tcl_GetIndexFromObjStruct(NULL((void*)0), obj, optSpec,
259 sizeof(Ns_ObjvSpec), "option",
260 TCL_EXACT1, &optIndex);
261 if (result != TCL_OK0) {
262 break;
263 }
264
265 --remain;
266 specPtr = optSpec + optIndex;
267 result = specPtr->proc(specPtr, interp, &remain, objv + (objc - remain));
268
269 if (result == TCL_BREAK3) {
270 break;
271 } else if (result != TCL_OK0) {
272 return NS_ERROR;
273 }
274 }
275 }
276 if (unlikely(argSpec == NULL)(__builtin_expect((argSpec == ((void*)0)), 0))) {
277 if (remain > 0) {
278 badargs:
279 WrongNumArgs(optSpec, argSpec, interp, offset, objv);
280 return NS_ERROR;
281 }
282 return NS_OK;
283 }
284
285 for (specPtr = argSpec; specPtr != NULL((void*)0) && specPtr->key != NULL((void*)0); specPtr++) {
286 if (unlikely(remain == 0)(__builtin_expect((remain == 0), 0))) {
287 if (unlikely(specPtr->key[0] != '?')(__builtin_expect((specPtr->key[0] != '?'), 0))) {
288 goto badargs; /* Too few args. */
289 }
290 return NS_OK;
291 }
292 if (unlikely(specPtr->proc(specPtr, interp, &remain, objv + (objc - remain)))(__builtin_expect((specPtr->proc(specPtr, interp, &remain
, objv + (objc - remain))), 0))
293 != TCL_OK0) {
294 return NS_ERROR;
295 }
296 }
297 if (unlikely(remain > 0)(__builtin_expect((remain > 0), 0))) {
298 goto badargs; /* Too many args. */
299 }
300
301 return NS_OK;
302}
303
304
305/*
306 *----------------------------------------------------------------------
307 *
308 * Ns_CheckWideRange --
309 *
310 * Helper function for range checking based on Tcl_WideInt specification.
311 * In error cases, the function leaves an error message in the interpreter.
312 *
313 * Results:
314 * TCL_OK or TCL_ERROR;
315 *
316 * Side effects:
317 * None.
318 *
319 *----------------------------------------------------------------------
320 */
321int
322Ns_CheckWideRange(Tcl_Interp *interp, const char *name, const Ns_ObjvValueRange *r, Tcl_WideInt value)
323{
324 int result;
325
326 if (r == NULL((void*)0) || (value >= r->minValue && value <= r->maxValue)) {
327 /*
328 * No range or valid range.
329 */
330 result = TCL_OK0;
331 } else {
332 /*
333 * Invalid range.
334 */
335 Tcl_DString ds, *dsPtr = &ds;
336
337 Tcl_DStringInit(dsPtr);
338 Tcl_DStringAppend(dsPtr, "expected integer in range ", 26);
339 AppendRange(dsPtr, r);
340 Ns_DStringPrintf(dsPtr, " for '%s', but got %" TCL_LL_MODIFIER"l" "d", name, value);
341 Tcl_DStringResult(interp, dsPtr);
342
343 result = TCL_ERROR1;
344 }
345 return result;
346}
347
348
349/*
350 *----------------------------------------------------------------------
351 *
352 * Ns_CheckTimeRange --
353 *
354 * Helper function for time range checking based on Ns_Times.
355 * In error cases, the function leaves an error message in the interpreter.
356 *
357 * Results:
358 * TCL_OK or TCL_ERROR;
359 *
360 * Side effects:
361 * None.
362 *
363 *----------------------------------------------------------------------
364 */
365int
366Ns_CheckTimeRange(Tcl_Interp *interp, const char *name, const Ns_ObjvTimeRange *r, Ns_Time *value)
367{
368 int result;
369 Tcl_DString ds0;
370
371 Tcl_DStringInit(&ds0);
372 //fprintf(stderr, "check time range '%s'\n", Ns_DStringAppendTime(&ds0, value));
373
374 if (r == NULL((void*)0)
375 || (Ns_DiffTime(value, &r->minValue, NULL((void*)0)) >= 0
376 && Ns_DiffTime(value, &r->maxValue, NULL((void*)0)) <= 0) ) {
377 /*
378 * No range or valid range.
379 */
380 result = TCL_OK0;
381 } else {
382 /*
383 * Invalid range.
384 */
385 Tcl_DString ds, *dsPtr = &ds;
386
387 Tcl_DStringInit(dsPtr);
388 Tcl_DStringAppend(dsPtr, "expected time value in range [", -1);
389 if (r->maxValue.sec == LONG_MAX9223372036854775807L) {
390 Ns_DStringAppendTime(dsPtr, &r->minValue);
391 Tcl_DStringAppend(dsPtr, "s, MAX],", 8);
392 } else {
393 Ns_DStringAppendTime(dsPtr, &r->minValue);
394 Tcl_DStringAppend(dsPtr, "s , ", 5);
395 Ns_DStringAppendTime(dsPtr, &r->maxValue);
396 Tcl_DStringAppend(dsPtr, "],", 2);
397 }
398 Ns_DStringPrintf(dsPtr, " for '%s', but got ", name);
399 (void)Ns_DStringAppendTime(dsPtr, value);
400
401 Tcl_DStringResult(interp, dsPtr);
402
403 result = TCL_ERROR1;
404 }
405 return result;
406}
407
408/*
409 *----------------------------------------------------------------------
410 *
411 * Ns_ObjvInt,
412 * Ns_ObjvUShort,
413 * Ns_ObjvLong,
414 * Ns_ObjvWideInt,
415 * Ns_ObjvDouble
416 *
417 * Consume exactly one argument, returning its value into dest.
418 * A typical use case for Ns_ObjvUShort is for ports.
419 *
420 * Results:
421 * TCL_OK or TCL_ERROR;
422 *
423 * Side effects:
424 * Argument maybe converted to type specific internal rep.
425 *
426 *----------------------------------------------------------------------
427 */
428int
429Ns_ObjvInt(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
430 Tcl_Obj *const* objv)
431{
432 int result;
433
434 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
435
436 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
437 int *dest = spec->dest;
438
439 result = Tcl_GetIntFromObj(interp, objv[0], dest);
440 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
441 if (Ns_CheckWideRange(interp, spec->key, spec->arg, (Tcl_WideInt)*dest) == TCL_OK0) {
442 *objcPtr -= 1;
443 } else {
444 result = TCL_ERROR1;
445 }
446 }
447 } else {
448 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
449 result = TCL_ERROR1;
450 }
451
452 return result;
453}
454
455int
456Ns_ObjvUShort(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
457 Tcl_Obj *const* objv)
458{
459 int result;
460
461 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
462
463 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
464 int intValue;
465
466 result = Tcl_GetIntFromObj(interp, objv[0], &intValue);
467 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
468 /*
469 * Check permissible values (USHRT_MAX)
470 */
471 if (intValue > 65535 || intValue < 0) {
472 Ns_TclPrintfResult(interp, "value %d out of range (0..65535)", intValue);
473 result = TCL_ERROR1;
474 } else {
475 unsigned short *dest = spec->dest;
476
477 *dest = (unsigned short)intValue;
478 *objcPtr -= 1;
479 }
480 }
481 } else {
482 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
483 result = TCL_ERROR1;
484 }
485
486 return result;
487}
488
489int
490Ns_ObjvLong(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
491 Tcl_Obj *const* objv)
492{
493 int result;
494
495 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
496
497 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
498 long *dest = spec->dest;
499
500 result = Tcl_GetLongFromObj(interp, objv[0], dest);
501 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
502 if (Ns_CheckWideRange(interp, spec->key, spec->arg, (Tcl_WideInt)*dest) == TCL_OK0) {
503 *objcPtr -= 1;
504 } else {
505 result = TCL_ERROR1;
506 }
507 }
508 } else {
509 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
510 result = TCL_ERROR1;
511 }
512
513 return result;
514}
515
516int
517Ns_ObjvWideInt(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
518 Tcl_Obj *const* objv)
519{
520 int result;
521
522 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
523
524 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
525 Tcl_WideInt *dest = spec->dest;
526
527 result = Tcl_GetWideIntFromObj(interp, objv[0], dest);
528 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
529 if (Ns_CheckWideRange(interp, spec->key, spec->arg, *dest) == TCL_OK0) {
530 *objcPtr -= 1;
531 } else {
532 result = TCL_ERROR1;
533 }
534 }
535 } else {
536 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
537 result = TCL_ERROR1;
538 }
539
540 return result;
541}
542
543int
544Ns_ObjvDouble(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
545 Tcl_Obj *const* objv)
546{
547 int result;
548
549 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
550
551 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
552 double *dest = spec->dest;
553
554 result = Tcl_GetDoubleFromObj(interp, objv[0], dest);
555 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
556 *objcPtr -= 1;
557 }
558 }
559 else {
560 result = TCL_ERROR1;
561 }
562
563 return result;
564}
565
566
567/*
568 *----------------------------------------------------------------------
569 *
570 * Ns_ObjvBool --
571 *
572 * If spec->arg is NULL consume exactly one argument and attempt
573 * conversion to a boolean value. Otherwise, spec->arg is
574 * treated as an int and placed into spec->dest with zero args
575 * consumed.
576 *
577 * Results:
578 * TCL_OK or TCL_ERROR.
579 *
580 * Side effects:
581 * Tcl_Obj maybe converted to boolean type.
582 *
583 *----------------------------------------------------------------------
584 */
585
586int
587Ns_ObjvBool(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, Tcl_Obj *const* objv)
588{
589 int *dest, result;
590
591 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
592
593 dest = spec->dest;
594
595 if (spec->arg != NULL((void*)0)) {
596 *dest = PTR2INT(spec->arg)((int)(intptr_t)(spec->arg));
597 result = TCL_OK0;
598 } else {
599 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
600 result = Tcl_GetBooleanFromObj(interp, objv[0], dest);
601 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
602 *objcPtr -= 1;
603 }
604 } else {
605 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
606 result = TCL_ERROR1;
607 }
608 }
609 return result;
610}
611
612
613/*
614 *----------------------------------------------------------------------
615 *
616 * Ns_ObjvString --
617 *
618 * Consume exactly one argument, returning a pointer to its
619 * string representation into *spec->dest.
620 *
621 * If spec->arg is != NULL it is assumed to be a pointer to an
622 * int and the returned string length will be left in it.
623 *
624 * Results:
625 * TCL_OK or TCL_ERROR.
626 *
627 * Side effects:
628 * None.
629 *
630 *----------------------------------------------------------------------
631 */
632
633int
634Ns_ObjvString(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
635 Tcl_Obj *const* objv)
636{
637 int result;
638
639 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
640
641 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
642 const char **dest = spec->dest;
643
644 *dest = Tcl_GetStringFromObj(objv[0], (int *) spec->arg);
645 *objcPtr -= 1;
646 result = TCL_OK0;
647 } else {
648 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
649 result = TCL_ERROR1;
650 }
651
652 return result;
653}
654
655/*
656 *----------------------------------------------------------------------
657 *
658 * Ns_ObjvEval --
659 *
660 * Consume exactly one argument, returning a pointer to the
661 * result of eval into *spec->dest.
662 *
663 * If spec->arg is != NULL it is assumed to be a pointer to an
664 * int and the returned string length will be left in it.
665 *
666 * Results:
667 * TCL_OK or TCL_ERROR.
668 *
669 * Side effects:
670 * None.
671 *
672 *----------------------------------------------------------------------
673 */
674
675int
676Ns_ObjvEval(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
677 Tcl_Obj *const* objv)
678{
679 int result;
680
681 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
682
683 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
684 const char **dest = spec->dest;
685
686 result = Tcl_EvalObjEx(interp, objv[0], 0);
687 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
688 *dest = Tcl_GetStringFromObj(Tcl_GetObjResult(interp), (int *) spec->arg);
689 *objcPtr -= 1;
690 }
691 } else {
692 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
693 result = TCL_ERROR1;
694 }
695 return result;
696}
697
698
699/*
700 *----------------------------------------------------------------------
701 *
702 * Ns_ObjvByteArray --
703 *
704 * Consume exactly one argument, returning a pointer to the
705 * raw bytes into *spec->dest.
706 *
707 * If spec->arg is != NULL it is assumed to be a pointer to an
708 * int and the number of bytes will be left in it.
709 *
710 * Results:
711 * TCL_OK or TCL_ERROR.
712 *
713 * Side effects:
714 * None.
715 *
716 *----------------------------------------------------------------------
717 */
718
719int
720Ns_ObjvByteArray(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
721 Tcl_Obj *const* objv)
722{
723 int result;
724
725 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
726
727 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
728 const unsigned char **dest = spec->dest;
729
730 *dest = Tcl_GetByteArrayFromObj(objv[0], (int *) spec->arg);
731 *objcPtr -= 1;
732 result = TCL_OK0;
733 } else {
734 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
735 result = TCL_ERROR1;
736 }
737 return result;
738}
739
740
741/*
742 *----------------------------------------------------------------------
743 *
744 * Ns_ObjvObj --
745 *
746 * Consume exactly one argument, returning its pointer into dest
747 * with no conversion.
748 *
749 * Results:
750 * TCL_OK or TCL_ERROR.
751 *
752 * Side effects:
753 * None.
754 *
755 *----------------------------------------------------------------------
756 */
757
758int
759Ns_ObjvObj(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
760 Tcl_Obj *const* objv)
761{
762 int result;
763
764 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
765
766 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
767 Tcl_Obj **dest = spec->dest;
768
769 *dest = objv[0];
770 *objcPtr -= 1;
771 result = TCL_OK0;
772 } else {
773 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
774 result = TCL_ERROR1;
775 }
776
777 return result;
778}
779
780
781/*
782 *----------------------------------------------------------------------
783 *
784 * Ns_ObjvTime --
785 *
786 * Consume exactly one argument, returning a pointer to the
787 * Ns_Time into *spec->dest.
788 *
789 * Results:
790 * TCL_OK or TCL_ERROR.
791 *
792 * Side effects:
793 * None.
794 *
795 *----------------------------------------------------------------------
796 */
797
798int
799Ns_ObjvTime(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
800 Tcl_Obj *const* objv)
801{
802 int result;
803
804 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
805
806 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
807 Ns_Time **dest = spec->dest;
808
809 result = Ns_TclGetTimePtrFromObj(interp, objv[0], dest);
810 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
811 result = Ns_CheckTimeRange(interp, spec->key, spec->arg, *dest);
812 }
813
814 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
815 *objcPtr -= 1;
816 }
817 } else {
818 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
819 result = TCL_ERROR1;
820 }
821
822 return result;
823}
824
825
826/*
827 *----------------------------------------------------------------------
828 *
829 * Ns_TclGetTimeFromObj --
830 *
831 * Return the internal value of an Ns_Time Tcl_Obj. If the value is
832 * specified as integer, the value is interpreted as seconds.
833 *
834 * Results:
835 * TCL_OK or TCL_ERROR if not a valid Ns_Time.
836 *
837 * Side effects:
838 * Object is converted to Ns_Time type if necessary.
839 *
840 *----------------------------------------------------------------------
841 */
842void
843NsTclInitMemUnitType(void)
844{
845 intTypePtr = Tcl_GetObjType("int");
846 if (intTypePtr == NULL((void*)0)) {
847 Tcl_Panic("NsTclInitObjs: no int type");
848 }
849 Tcl_RegisterObjType(&memUnitType);
850}
851
852static void
853UpdateStringOfMemUnit(Tcl_Obj *objPtr)
854{
855 long memUnit;
856 int len;
857 char buf[(TCL_INTEGER_SPACE24) + 1];
858
859 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
860
861 memUnit = PTR2INT((void *) &objPtr->internalRep)((int)(intptr_t)((void *) &objPtr->internalRep));
862 len = ns_uint64toa(buf, (uint64_t)memUnit);
863 Ns_TclSetStringRep(objPtr, buf, len);
864}
865
866static int
867SetMemUnitFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
868{
869 Tcl_WideInt memUnit = 0;
870 int result = TCL_OK0;
871
872 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
873 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
874
875 if (objPtr->typePtr == intTypePtr) {
876 long longValue;
877 /*
878 * When the type is "int", the memory unit is in bytes.
879 */
880 if (Tcl_GetLongFromObj(interp, objPtr, &longValue) != TCL_OK0) {
881 result = TCL_ERROR1;
882 } else {
883 memUnit = longValue;
884 }
885 } else {
886 Ns_ReturnCode status;
887
888 status = Ns_StrToMemUnit(Tcl_GetString(objPtr), &memUnit);
889 if (status == NS_ERROR) {
890 result = TCL_ERROR1;
891 }
892 }
893
894 if (result == TCL_OK0) {
895 Ns_TclSetTwoPtrValue(objPtr, &memUnitType, INT2PTR(memUnit)((void *)(intptr_t)(memUnit)), NULL((void*)0));
896 }
897
898 return result;
899}
900
901/*
902 *----------------------------------------------------------------------
903 *
904 * Ns_TclGetMemUnitFromObj --
905 *
906 * Convert a Tcl_Obj with a string value of a memory unit into a Tcl_WideInt.
907 * It has the same interface as e.g. Tcl_GetWideIntFromObj().
908 *
909 * Results:
910 * TCL_OK or TCL_ERROR if not a valid memory unit string.
911 *
912 * Side effects:
913 * None.
914 *
915 *----------------------------------------------------------------------
916 */
917
918int
919Ns_TclGetMemUnitFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, Tcl_WideInt *memUnitPtr)
920{
921 int result = TCL_OK0;
922
923 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
924 NS_NONNULL_ASSERT(objPtr != NULL)((void) (0));
925 NS_NONNULL_ASSERT(memUnitPtr != NULL)((void) (0));
926
927 /*
928 * Many values come in already as int values. No need to convert
929 * these to memUnitType.
930 */
931 if (objPtr->typePtr == intTypePtr) {
932 int intValue;
933
934 if (likely(Tcl_GetIntFromObj(interp, objPtr, &intValue) != TCL_OK)(__builtin_expect((Tcl_GetIntFromObj(interp, objPtr, &intValue
) != 0), 1))
) {
935 result = TCL_ERROR1;
936 } else {
937 *memUnitPtr = (Tcl_WideInt)intValue;
938 }
939 } else {
940 /*
941 * When the values are already in memUnitType, get the value
942 * directly, otherwise convert.
943 */
944 if (objPtr->typePtr != &memUnitType) {
945
946 if (unlikely(Tcl_ConvertToType(interp, objPtr, &memUnitType) != TCL_OK)(__builtin_expect((Tcl_ConvertToType(interp, objPtr, &memUnitType
) != 0), 0))
) {
947 Ns_TclPrintfResult(interp, "invalid memory unit '%s'; "
948 "valid units kB, MB, GB, KiB, MiB, and GiB",
949 Tcl_GetString(objPtr));
950 result = TCL_ERROR1;
951 }
952 }
953 if (likely(objPtr->typePtr == &memUnitType)(__builtin_expect((objPtr->typePtr == &memUnitType), 1
))
) {
954 *memUnitPtr = (Tcl_WideInt)objPtr->internalRep.twoPtrValue.ptr1;
955 }
956 }
957
958 return result;
959}
960
961
962/*
963 *----------------------------------------------------------------------
964 *
965 * Ns_ObjvMemUnit --
966 *
967 * Consume exactly one argument, returning a pointer to the
968 * memUnit (Tcl_WideInt) into *spec->dest.
969 *
970 * Results:
971 * TCL_OK or TCL_ERROR.
972 *
973 * Side effects:
974 * None.
975 *
976 *----------------------------------------------------------------------
977 */
978
979int
980Ns_ObjvMemUnit(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
981 Tcl_Obj *const* objv)
982{
983 int result;
984
985 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
986
987 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
988 Tcl_WideInt *dest = spec->dest;
989
990 result = Ns_TclGetMemUnitFromObj(interp, objv[0], dest);
991
992 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
993 if (Ns_CheckWideRange(interp, spec->key, spec->arg, (Tcl_WideInt)*dest) == TCL_OK0) {
994 *objcPtr -= 1;
995 } else {
996 result = TCL_ERROR1;
997 }
998 }
999 } else {
1000 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
1001 result = TCL_ERROR1;
1002 }
1003
1004 return result;
1005}
1006
1007
1008/*
1009 *----------------------------------------------------------------------
1010 *
1011 * Ns_ObjvSet --
1012 *
1013 * Consume exactly one argument, returning a pointer to the
1014 * Ns_Set into *spec->dest.
1015 *
1016 * Results:
1017 * TCL_OK or TCL_ERROR.
1018 *
1019 * Side effects:
1020 * None.
1021 *
1022 *----------------------------------------------------------------------
1023 */
1024
1025int
1026Ns_ObjvSet(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
1027 Tcl_Obj *const* objv)
1028{
1029 int result;
1030
1031 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
1032
1033 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
1034 Ns_Set **dest = spec->dest;
1035
1036 result = Ns_TclGetSet2(interp, Tcl_GetString(objv[0]), dest);
1037 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1038 *objcPtr -= 1;
1039 }
1040 } else {
1041 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
1042 result = TCL_ERROR1;
1043 }
1044
1045 return result;
1046}
1047
1048
1049/*
1050 *----------------------------------------------------------------------
1051 *
1052 * Ns_ObjvIndex --
1053 *
1054 * Match the next argument against the keys in the specified
1055 * table, returning the value at the index of the first match
1056 * into dest. It is an error for the argument to contain anything
1057 * but one of the table keys.
1058 *
1059 * Results:
1060 * TCL_OK or TCL_ERROR.
1061 *
1062 * Side effects:
1063 * None.
1064 *
1065 *----------------------------------------------------------------------
1066 */
1067
1068int
1069Ns_ObjvIndex(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
1070 Tcl_Obj *const* objv)
1071{
1072 const Ns_ObjvTable *tablePtr;
1073 int *dest, tableIdx, result;
1074
1075 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
1076
1077 tablePtr = spec->arg;
1078 dest = spec->dest;
1079
1080 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
1081 result = Tcl_GetIndexFromObjStruct(interp, objv[0], tablePtr,
1082 sizeof(Ns_ObjvTable), "option",
1083 TCL_EXACT1, &tableIdx);
1084 if (result == TCL_OK0) {
1085 *dest = (int)tablePtr[tableIdx].value;
1086 *objcPtr -= 1;
1087 }
1088 } else {
1089 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
1090 result = TCL_ERROR1;
1091 }
1092
1093 return result;
1094}
1095
1096
1097/*
1098 *----------------------------------------------------------------------
1099 *
1100 * Ns_ObjvFlags --
1101 *
1102 * Treat the next argument as a list of flags and compare them
1103 * all with the keys in the specified table. As flags are
1104 * matched the values are ORed togethter and the result is
1105 * returned in dest. An unknown flag causes an error.
1106 *
1107 * Results:
1108 * TCL_OK or TCL_ERROR.
1109 *
1110 * Side effects:
1111 * Arg may be converted to list representation.
1112 *
1113 *----------------------------------------------------------------------
1114 */
1115
1116int
1117Ns_ObjvFlags(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr,
1118 Tcl_Obj *const* objv)
1119{
1120 unsigned int *dest;
1121 const Ns_ObjvTable *tablePtr;
1122 int result, tableIdx = 0;
1123 Tcl_Obj **flagv;
1124
1125 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
1126 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1127
1128 dest = spec->dest;
1129 tablePtr = spec->arg;
1130
1131 if (*objcPtr < 1) {
1132 result = TCL_ERROR1;
1133 } else {
1134 int flagc;
1135
1136 result = Tcl_ListObjGetElements(interp, objv[0], &flagc, &flagv);
1137 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1138 if (likely(flagc > 0)(__builtin_expect((flagc > 0), 1))) {
1139 int i;
1140
1141 for (i = 0; i < flagc; ++i) {
1142 result = Tcl_GetIndexFromObjStruct(interp, flagv[i], tablePtr,
1143 sizeof(Ns_ObjvTable), "flag",
1144 TCL_EXACT1, &tableIdx);
1145 if (unlikely(result != TCL_OK)(__builtin_expect((result != 0), 0))) {
1146 break;
1147 }
1148 }
1149 } else {
1150 Ns_TclPrintfResult(interp, "blank flag specification");
1151 result = TCL_ERROR1;
1152 }
1153 }
1154 }
1155
1156 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1157 *dest |= tablePtr[tableIdx].value;
1158 *objcPtr -= 1;
1159 }
1160
1161 return result;
1162}
1163
1164
1165/*
1166 *----------------------------------------------------------------------
1167 *
1168 * Ns_ObjvBreak --
1169 *
1170 * Handle '--' option/argument separator.
1171 *
1172 * Results:
1173 * Always TCL_BREAK.
1174 *
1175 * Side effects:
1176 * Option processing will end successfully, argument processing
1177 * will begin.
1178 *
1179 *----------------------------------------------------------------------
1180 */
1181
1182int
1183Ns_ObjvBreak(Ns_ObjvSpec *UNUSED(spec)UNUSED_spec __attribute__((__unused__)), Tcl_Interp *UNUSED(interp)UNUSED_interp __attribute__((__unused__)),
1184 int *UNUSED(objcPtr)UNUSED_objcPtr __attribute__((__unused__)), Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
1185{
1186 return TCL_BREAK3;
1187}
1188
1189
1190/*
1191 *----------------------------------------------------------------------
1192 *
1193 * Ns_ObjvArgs --
1194 *
1195 * Count all remaining arguments, leaving zero left
1196 * unprocessed.
1197 *
1198 * Results:
1199 * Always TCL_OK.
1200 *
1201 * Side effects:
1202 * Argument processing will end successfully.
1203 *
1204 *----------------------------------------------------------------------
1205 */
1206
1207int
1208Ns_ObjvArgs(Ns_ObjvSpec *spec, Tcl_Interp *UNUSED(interp)UNUSED_interp __attribute__((__unused__)),
1209 int *objcPtr, Tcl_Obj *const* UNUSED(objv)UNUSED_objv __attribute__((__unused__)))
1210{
1211 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
1212
1213 *((int *) spec->dest) = *objcPtr;
1214 *objcPtr = 0;
1215
1216 return TCL_OK0;
1217}
1218
1219
1220/*
1221 *----------------------------------------------------------------------
1222 *
1223 * Ns_ObjvServer --
1224 *
1225 * Get server from argument, consume it, put result into "dest".
1226 *
1227 * Results:
1228 * TCL_OK or TCL_ERROR.
1229 *
1230 * Side effects:
1231 * None.
1232 *
1233 *----------------------------------------------------------------------
1234 */
1235
1236int
1237Ns_ObjvServer(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, Tcl_Obj *const* objv)
1238{
1239 NsServer **dest;
1240 int result = TCL_OK0;
1241
1242 NS_NONNULL_ASSERT(spec != NULL)((void) (0));
1243 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1244
1245 dest = spec->dest;
1246
1247 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1)) && likely(dest != NULL)(__builtin_expect((dest != ((void*)0)), 1))) {
1248 NsServer *servPtr = NsGetServer(Tcl_GetString(objv[0]));
1249
1250 if (likely(servPtr != NULL)(__builtin_expect((servPtr != ((void*)0)), 1))) {
1251 *dest = servPtr;
1252 *objcPtr -= 1;
1253 } else {
1254 Ns_TclPrintfResult(interp, "invalid server: '%s'", Tcl_GetString(objv[0]));
1255 result = TCL_ERROR1;
1256 }
1257 } else {
1258 result = TCL_ERROR1;
1259 }
1260
1261 return result;
1262}
1263
1264
1265/*
1266 *----------------------------------------------------------------------
1267 *
1268 * NsTclParseArgsObjCmd --
1269 *
1270 * Implements "ns_parseargs".
1271 *
1272 * Results:
1273 * Tcl result.
1274 *
1275 * Side effects:
1276 * Specification may be converted to ns:spec obj type.
1277 *
1278 *----------------------------------------------------------------------
1279 */
1280
1281int
1282NsTclParseArgsObjCmd(ClientData UNUSED(clientData)UNUSED_clientData __attribute__((__unused__)), Tcl_Interp *interp, int objc, Tcl_Obj *const* objv)
1283{
1284 Tcl_Obj **argv, *argsObj;
1285 int argc, status = TCL_OK0;
1286
1287 if (objc != 3) {
1288 Tcl_WrongNumArgs(interp, 1, objv, "specification args");
1289 return TCL_ERROR1;
1290 }
1291 /*
1292 * In case both arguments are shared (objv[1] == objv[2]), make
1293 * sure to decouple these, since Tcl_ListObjGetElements() and
1294 * Tcl_ConvertToType() would shimmer on the shared object.
1295 */
1296 if (objv[2] == objv[1]) {
1297 argsObj = Tcl_DuplicateObj(objv[2]);
1298 Tcl_IncrRefCount(argsObj)++(argsObj)->refCount;
1299 } else {
1300 argsObj = objv[2];
1301 }
1302
1303 if (Tcl_ListObjGetElements(interp, argsObj, &argc, &argv) != TCL_OK0
1304 || Tcl_ConvertToType(interp, objv[1], &specType) != TCL_OK0) {
1305 status = TCL_ERROR1;
1306
1307 } else {
1308 Ns_ObjvSpec *opts, *args;
1309
1310 opts = objv[1]->internalRep.twoPtrValue.ptr1;
1311 args = objv[1]->internalRep.twoPtrValue.ptr2;
1312 if (Ns_ParseObjv(opts, args, interp, 0, argc, argv) != NS_OK) {
1313 status = TCL_ERROR1;
1314
1315 } else {
1316 bool_Bool doneOpts = NS_FALSE0;
1317 Ns_ObjvSpec *specPtr = opts;
1318
1319 /*
1320 * Set defaults for args which were passed no values and
1321 * reset the dest pointer for subsequent calls to this
1322 * command.
1323 */
1324
1325 for (;;) {
1326 if (specPtr->key == NULL((void*)0)) {
1327 if (doneOpts) {
1328 break;
1329 }
1330 doneOpts = NS_TRUE1;
1331 specPtr++;
1332 continue;
1333 }
1334 if (status == TCL_OK0 && specPtr->dest == NULL((void*)0) && specPtr->arg != NULL((void*)0)) {
1335 status = SetValue(interp, specPtr->key, specPtr->arg);
1336 }
1337 specPtr->dest = NULL((void*)0);
1338 specPtr++;
1339 }
1340
1341 }
1342 }
1343 if (argsObj != objv[2]) {
1344 Tcl_DecrRefCount(argsObj)do { Tcl_Obj *_objPtr = (argsObj); if (_objPtr->refCount--
<= 1) { TclFreeObj(_objPtr); } } while(0)
;
1345 }
1346 return status;
1347}
1348
1349
1350/*
1351 *----------------------------------------------------------------------
1352 * SetSpecFromAny --
1353 *
1354 * Attempt to convert a Tcl_Obj to ns:spec type.
1355 *
1356 * Results:
1357 * TCL_OK or TCL_ERROR.
1358 *
1359 * Side effects:
1360 * None.
1361 *
1362 *----------------------------------------------------------------------
1363 */
1364
1365static int
1366SetSpecFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr)
1367{
1368 Ns_ObjvSpec *specPtr, *optSpec, *argSpec = NULL((void*)0);
1369 Tcl_Obj **specv, **specPair, *defObjPtr;
1370 int numSpecs, specLen, keyLen, i;
1371
1372 if (Tcl_ListObjGetElements(interp, objPtr, &numSpecs, &specv) != TCL_OK0) {
1373 return TCL_ERROR1;
1374 }
1375 optSpec = ns_calloc((size_t) numSpecs + 2u, sizeof(Ns_ObjvSpec));
1376 specPtr = optSpec;
1377
1378 for (i = 0; i < numSpecs; ++i) {
1379 const char *key;
1380
1381 /*
1382 * Check for a default and extract the key.
1383 */
1384
1385 if (Tcl_ListObjGetElements(interp, specv[i],
1386 &specLen, &specPair) != TCL_OK0) {
1387 FreeSpecs(optSpec);
1388 return TCL_ERROR1;
1389 }
1390 if (specLen == 0 || specLen > 2) {
1391 Ns_TclPrintfResult(interp, "wrong # fields in argument specifier \"%s\"",
1392 Tcl_GetString(specv[i]));
1393 FreeSpecs(optSpec);
1394 return TCL_ERROR1;
1395 }
1396 key = Tcl_GetStringFromObj(specPair[0], &keyLen);
1397 if (specLen == 2) {
1398 defObjPtr = specPair[1];
1399 } else if (i + 1 == numSpecs && STREQ(key, "args")(((*(key)) == (*("args"))) && (strcmp((key),("args"))
== 0))
) {
1400 defObjPtr = Tcl_NewListObj(0, NULL((void*)0));
1401 } else {
1402 defObjPtr = NULL((void*)0);
1403 }
1404
1405 /*
1406 * Examine the key: is this an option or an argument?
1407 */
1408
1409 if (key[0] == '\0' || (key[0] == '-' && key[1] == '\0')) {
1410 Ns_TclPrintfResult(interp,
1411 "argument or option in position %d has no name", i);
1412 FreeSpecs(optSpec);
1413 return TCL_ERROR1;
1414 }
1415 if (key[0] == '-' && argSpec != NULL((void*)0)) {
1416 Ns_TclPrintfResult(interp, "expected argument \"%s\"", key);
1417 FreeSpecs(optSpec);
1418 return TCL_ERROR1;
1419 }
1420 if (key[0] != '-' && argSpec == NULL((void*)0)) {
1421 /*
1422 * Found the first non-option.
1423 */
1424 argSpec = ++specPtr;
1425 }
1426
1427 /*
1428 * Arguments with default values must have their keys
1429 * prepended with '?' for the run time parser. Tcl 'args' are
1430 * always optional.
1431 */
1432
1433 if ((key[0] != '-' && defObjPtr != NULL((void*)0))
1434 || (i + 1 == numSpecs && STREQ(key, "args")(((*(key)) == (*("args"))) && (strcmp((key),("args"))
== 0))
)) {
1435 char *rewrittenKey = ns_malloc((size_t)keyLen + 2u);
1436
1437 *rewrittenKey = '?';
1438 memcpy(rewrittenKey + 1, key, (size_t)keyLen + 1u);
1439 specPtr->key = rewrittenKey;
1440 } else {
1441 specPtr->key = ns_strdup(key);
1442 }
1443
1444 if (defObjPtr != NULL((void*)0)) {
1445 Tcl_IncrRefCount(defObjPtr)++(defObjPtr)->refCount;
1446 specPtr->arg = defObjPtr;
1447 }
1448 if (STREQ(key, "--")(((*(key)) == (*("--"))) && (strcmp((key),("--")) == 0
))
) {
1449 specPtr->proc = Ns_ObjvBreak;
1450 } else if (i + 1 == numSpecs && STREQ(key, "args")(((*(key)) == (*("args"))) && (strcmp((key),("args"))
== 0))
) {
1451 specPtr->proc = ObjvTclArgs;
1452 } else {
1453 specPtr->proc = ObjvTcl;
1454 }
1455 specPtr++;
1456 }
1457 if (argSpec == NULL((void*)0)) {
1458 argSpec = specPtr;
1459 }
1460 Ns_TclSetTwoPtrValue(objPtr, &specType, optSpec, argSpec);
1461
1462 return TCL_OK0;
1463}
1464
1465
1466/*
1467 *----------------------------------------------------------------------
1468 * FreeSpecs --
1469 *
1470 * Free array of opt and arg specs.
1471 *
1472 * Results:
1473 * None.
1474 *
1475 * Side effects:
1476 * None.
1477 *
1478 *----------------------------------------------------------------------
1479 */
1480
1481static void
1482FreeSpecs(Ns_ObjvSpec *specPtr)
1483{
1484 Ns_ObjvSpec *saveSpec = specPtr;
1485 bool_Bool doneOpts = NS_FALSE0;
1486
1487 NS_NONNULL_ASSERT(specPtr != NULL)((void) (0));
1488
1489 for (;;) {
1490 if (specPtr->key == NULL((void*)0)) {
1491 if (doneOpts) {
1492 break;
1493 }
1494 doneOpts = NS_TRUE1;
1495 specPtr++;
1496 continue;
1497 }
1498 ns_free((char *)specPtr->key);
1499 if (specPtr->arg != NULL((void*)0)) {
1500 Tcl_DecrRefCount((Tcl_Obj *) specPtr->arg)do { Tcl_Obj *_objPtr = ((Tcl_Obj *) specPtr->arg); if (_objPtr
->refCount-- <= 1) { TclFreeObj(_objPtr); } } while(0)
;
1501 }
1502 specPtr++;
1503 }
1504 ns_free(saveSpec);
1505}
1506
1507
1508/*
1509 *----------------------------------------------------------------------
1510 * FreeSpecObj --
1511 *
1512 * This procedure is called to delete the internal rep of a
1513 * ns:spec Tcl_Obj.
1514 *
1515 * Results:
1516 * None.
1517 *
1518 * Side effects:
1519 * The internal representation of the given object is deleted.
1520 *
1521 *----------------------------------------------------------------------
1522 */
1523
1524static void
1525FreeSpecObj(Tcl_Obj *objPtr)
1526{
1527 Ns_ObjvSpec *optSpec;
1528
1529 optSpec = objPtr->internalRep.twoPtrValue.ptr1;
1530 FreeSpecs(optSpec);
1531
1532 objPtr->internalRep.twoPtrValue.ptr1 = NULL((void*)0);
1533 objPtr->internalRep.twoPtrValue.ptr2 = NULL((void*)0);
1534}
1535
1536
1537/*
1538 *----------------------------------------------------------------------
1539 * UpdateStringOfSpec --
1540 *
1541 * This procedure is called to convert a Tcl_Obj from
1542 * ns:spec internal form to its string form.
1543 *
1544 * Results:
1545 * None.
1546 *
1547 * Side effects:
1548 * The string representation of the object is updated.
1549 *
1550 *----------------------------------------------------------------------
1551 */
1552
1553static void
1554UpdateStringOfSpec(Tcl_Obj *objPtr)
1555{
1556 const Ns_ObjvSpec *specPtr;
1557 Tcl_Obj *defaultObj;
1558 Tcl_DString ds;
1559 bool_Bool doneOpts = NS_FALSE0;
1560
1561 Tcl_DStringInit(&ds);
1562 Tcl_DStringStartSublist(&ds);
1563 specPtr = (Ns_ObjvSpec *) objPtr->internalRep.twoPtrValue.ptr1;
1564 for (;;) {
1565 if (specPtr->key == NULL((void*)0)) {
1566 if (doneOpts) {
1567 break;
1568 }
1569 doneOpts = NS_TRUE1;
1570 specPtr++;
1571 continue;
1572 }
1573 if (specPtr->arg != NULL((void*)0)) {
1574 defaultObj = (Tcl_Obj *) specPtr->arg;
1575 Tcl_DStringStartSublist(&ds);
1576 Tcl_DStringAppendElement(&ds, specPtr->key);
1577 Tcl_DStringAppendElement(&ds, Tcl_GetString(defaultObj));
1578 Tcl_DStringEndSublist(&ds);
1579 } else {
1580 Tcl_DStringAppendElement(&ds, specPtr->key);
1581 }
1582 specPtr++;
1583 }
1584 Tcl_DStringEndSublist(&ds);
1585 Ns_TclSetStringRep(objPtr, ds.string, ds.length);
1586 Tcl_DStringFree(&ds);
1587}
1588
1589
1590/*
1591 *----------------------------------------------------------------------
1592 * DupSpec --
1593 *
1594 * This procedure is called to copy the internal rep of a
1595 * ns:spec Tcl_Obj to another object.
1596 *
1597 * Results:
1598 * None.
1599 *
1600 * Side effects:
1601 * The internal representation of the target object is updated
1602 * and the type is set.
1603 *
1604 *----------------------------------------------------------------------
1605 */
1606
1607static void
1608DupSpec(Tcl_Obj *srcObj, Tcl_Obj *dupObj)
1609{
1610 Ns_ObjvSpec *oldOptSpec = srcObj->internalRep.twoPtrValue.ptr1;
1611 Ns_ObjvSpec *oldArgSpec = srcObj->internalRep.twoPtrValue.ptr2;
1612 Ns_ObjvSpec *optSpec, *argSpec, *specPtr;
1613 size_t numSpecs = 2u;
1614
1615 for (specPtr = oldOptSpec; specPtr->key != NULL((void*)0); specPtr++) {
1616 numSpecs++;
1617 }
1618 for (specPtr = oldArgSpec; specPtr->key != NULL((void*)0); specPtr++) {
1619 numSpecs++;
1620 }
1621
1622 optSpec = ns_malloc(numSpecs * sizeof(Ns_ObjvSpec));
1623 memcpy(optSpec, oldOptSpec, numSpecs * sizeof(Ns_ObjvSpec));
1624
1625 specPtr = optSpec;
1626 argSpec = NULL((void*)0);
1627 for (;;) {
1628 if (specPtr->key == NULL((void*)0)) {
1629 if (argSpec != NULL((void*)0)) {
1630 break;
1631 }
1632 argSpec = ++specPtr;
1633 continue;
1634 }
1635 specPtr->key = ns_strdup(specPtr->key);
1636 if (specPtr->arg != NULL((void*)0)) {
1637 Tcl_IncrRefCount((Tcl_Obj *) specPtr->arg)++((Tcl_Obj *) specPtr->arg)->refCount;
1638 }
1639 specPtr++;
1640 }
1641 Ns_TclSetTwoPtrValue(dupObj, &specType, optSpec, argSpec);
1642}
1643
1644
1645/*
1646 *----------------------------------------------------------------------
1647 *
1648 * ObjvTcl --
1649 *
1650 * Consume exactly one argument, setting it as the value of a
1651 * variable named after the key in the given interp.
1652 *
1653 * Results:
1654 * TCL_OK or TCL_ERROR.
1655 *
1656 * Side effects:
1657 * None.
1658 *
1659 *----------------------------------------------------------------------
1660 */
1661
1662static int
1663ObjvTcl(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, Tcl_Obj *const* objv)
1664{
1665 int result;
1666
1667 if (likely(*objcPtr > 0)(__builtin_expect((*objcPtr > 0), 1))) {
1668 result = SetValue(interp, spec->key, objv[0]);
1669 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1670 *objcPtr -= 1;
1671 spec->dest = VALUE_SUPPLIED(((void *)(intptr_t)(1)));
1672 }
1673 } else {
1674 Ns_TclPrintfResult(interp, "missing argument to %s", spec->key);
1675 result = TCL_ERROR1;
1676 }
1677
1678 return result;
1679}
1680
1681
1682/*
1683 *----------------------------------------------------------------------
1684 *
1685 * ObjvTclArgs --
1686 *
1687 * Consume all remaining args and reset the local variable "args"
1688 * in the given interp to contain them.
1689 *
1690 * Results:
1691 * TCL_OK and objcPtr set to 0, or TCL_ERROR.
1692 *
1693 * Side effects:
1694 * Value of existing Tcl variable "args" overwritten.
1695 *
1696 *----------------------------------------------------------------------
1697 */
1698
1699static int
1700ObjvTclArgs(Ns_ObjvSpec *spec, Tcl_Interp *interp, int *objcPtr, Tcl_Obj *const* objv)
1701{
1702 Tcl_Obj *listObj;
1703 int result;
1704
1705 listObj = Tcl_NewListObj(*objcPtr, objv);
1706 if (listObj == NULL((void*)0)) {
1
Assuming 'listObj' is not equal to NULL
2
Taking false branch
1707 result = TCL_ERROR1;
1708 } else {
1709 if (Tcl_SetVar2Ex(interp, "args", NULL((void*)0), listObj,
3
Assuming the condition is false
4
Taking false branch
1710 TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0)) {
1711 result = TCL_ERROR1;
1712 } else {
1713 *objcPtr = 0;
1714 spec->dest = VALUE_SUPPLIED(((void *)(intptr_t)(1)));
5
Using a fixed address is not portable because that address will probably not be valid in all environments or platforms
1715 result = TCL_OK0;
1716 }
1717 }
1718
1719 return result;
1720}
1721
1722
1723/*
1724 *----------------------------------------------------------------------
1725 *
1726 * SetValue --
1727 *
1728 * Strip any leading "-" or "?" from the key and set a variable
1729 * with the resulting name. If value starts with "[" and ends
1730 * with "]" then evaluate Tcl script and assign result to the
1731 * variable.
1732 *
1733 * Results:
1734 * Tcl result code.
1735 *
1736 * Side effects:
1737 * None.
1738 *
1739 *----------------------------------------------------------------------
1740 */
1741
1742static int
1743SetValue(Tcl_Interp *interp, const char *key, Tcl_Obj *valueObj)
1744{
1745 size_t len;
1746 const char *value;
1747 int result = TCL_OK0;
1748
1749 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1750 NS_NONNULL_ASSERT(key != NULL)((void) (0));
1751 NS_NONNULL_ASSERT(valueObj != NULL)((void) (0));
1752
1753 value = Tcl_GetString(valueObj);
1754
1755 if (key[0] == '-' || key[0] == '?') {
1756 key++;
1757 }
1758
1759 len = strlen(value);
1760 if (value[0] == '[' && value[len - 1u] == ']') {
1761 value++;
1762 len -= 2u;
1763
1764 result = Tcl_EvalEx(interp, value, (int)len, 0);
1765 if (result == TCL_OK0) {
1766 valueObj = Tcl_GetObjResult(interp);
1767 }
1768 }
1769
1770 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1771 if (Tcl_SetVar2Ex(interp, key, NULL((void*)0), valueObj,
1772 TCL_LEAVE_ERR_MSG0x200) == NULL((void*)0)) {
1773 result = TCL_ERROR1;
1774 } else {
1775 Tcl_ResetResult(interp);
1776 }
1777 }
1778 return result;
1779}
1780
1781
1782/*
1783 *----------------------------------------------------------------------
1784 *
1785 * WrongNumArgs --
1786 *
1787 * Leave a usage message in the interpreters result.
1788 *
1789 * Results:
1790 * None.
1791 *
1792 * Side effects:
1793 * None.
1794 *
1795 *----------------------------------------------------------------------
1796 */
1797
1798static void AppendRange(Ns_DStringTcl_DString *dsPtr, const Ns_ObjvValueRange *r)
1799{
1800 if (r->minValue == LLONG_MIN(-9223372036854775807LL -1LL)) {
1801 Tcl_DStringAppend(dsPtr, "[MIN,", 5);
1802 } else {
1803 Ns_DStringPrintf(dsPtr, "[%" TCL_LL_MODIFIER"l" "d,", r->minValue);
1804 }
1805
1806 if (r->maxValue == LLONG_MAX9223372036854775807LL) {
1807 Tcl_DStringAppend(dsPtr, "MAX]", 4);
1808 } else {
1809 Ns_DStringPrintf(dsPtr, "%" TCL_LL_MODIFIER"l" "d]", r->maxValue);
1810 }
1811}
1812
1813static void
1814WrongNumArgs(const Ns_ObjvSpec *optSpec, Ns_ObjvSpec *argSpec, Tcl_Interp *interp,
1815 int objc, Tcl_Obj *const* objv)
1816{
1817 const Ns_ObjvSpec *specPtr;
1818 Ns_DStringTcl_DString ds;
1819
1820 Ns_DStringInitTcl_DStringInit(&ds);
1821 if (optSpec != NULL((void*)0)) {
1822 for (specPtr = optSpec; specPtr->key != NULL((void*)0); ++specPtr) {
1823 if (STREQ(specPtr->key, "--")(((*(specPtr->key)) == (*("--"))) && (strcmp((specPtr
->key),("--")) == 0))
) {
1824 Ns_DStringAppend(&ds, "?--? ")Tcl_DStringAppend((&ds), ("?--? "), -1);
1825 } else if (specPtr->proc == Ns_ObjvBool && specPtr->arg != NULL((void*)0)) {
1826 Ns_DStringPrintf(&ds, "?%s? ", specPtr->key);
1827 } else {
1828 const char *p = specPtr->key;
1829 if (*specPtr->key == '-') {
1830 ++p;
1831 }
1832 Ns_DStringPrintf(&ds, "?%s %s", specPtr->key, p);
1833
1834 if ((specPtr->proc == Ns_ObjvInt
1835 || specPtr->proc == Ns_ObjvLong
1836 || specPtr->proc == Ns_ObjvWideInt
1837 ) && specPtr->arg != NULL((void*)0)) {
1838 AppendRange(&ds, specPtr->arg);
1839 }
1840 Tcl_DStringAppend(&ds, "? ", 2);
1841 }
1842 }
1843 }
1844 if (argSpec != NULL((void*)0)) {
1845 for (specPtr = argSpec; specPtr->key != NULL((void*)0); ++specPtr) {
1846 Tcl_DStringAppend(&ds, specPtr->key, -1);
1847
1848 if ((specPtr->proc == Ns_ObjvInt
1849 || specPtr->proc == Ns_ObjvLong
1850 || specPtr->proc == Ns_ObjvWideInt
1851 ) && specPtr->arg != NULL((void*)0)) {
1852 AppendRange(&ds, specPtr->arg);
1853 }
1854 if (*specPtr->key == '?') {
1855 Tcl_DStringAppend(&ds, "?", 1);
1856 }
1857 Tcl_DStringAppend(&ds, " ", 1);
1858 }
1859 }
1860 if (ds.length > 0) {
1861 Ns_DStringSetLengthTcl_DStringSetLength(&ds, ds.length - 1);
1862 Tcl_WrongNumArgs(interp, objc, objv, ds.string);
1863 } else {
1864 Tcl_WrongNumArgs(interp, objc, objv, NULL((void*)0));
1865 }
1866
1867 Ns_DStringFreeTcl_DStringFree(&ds);
1868}
1869
1870/*
1871 *----------------------------------------------------------------------
1872 *
1873 * Ns_SubcmdObjv --
1874 *
1875 * Call subcommand based on the provided name and associated
1876 * functions.
1877 *
1878 * Results:
1879 * Tcl result code.
1880 *
1881 * Side effects:
1882 * Depends on the Ns_ObjvTypeProcs which run.
1883 *
1884 *----------------------------------------------------------------------
1885 */
1886int
1887Ns_SubcmdObjv(const Ns_SubCmdSpec *subcmdSpec, ClientData clientData, Tcl_Interp *interp,
1888 int objc, Tcl_Obj *const* objv)
1889{
1890 int opt = 0, result;
1891
1892 if (objc < 2) {
1893 /*
1894 * The command was called without selector for the
1895 * subcmd. With out own machinery (as used in
1896 * GetOptIndexSubcmdSpec()) we could list the available
1897 * options, but that is just used for shared objects.
1898 */
1899 Tcl_WrongNumArgs(interp, 1, objv, "command ?args?");
1900 result = TCL_ERROR1;
1901 } else {
1902 Tcl_Obj *selectorObj = objv[1];
1903 /*
1904 * If the obj is shared, don't trust its internal representation.
1905 */
1906 result = Tcl_IsShared(selectorObj)((selectorObj)->refCount > 1)
1907 ? GetOptIndexSubcmdSpec(interp, selectorObj, "subcmd", subcmdSpec, &opt)
1908 : Tcl_GetIndexFromObjStruct(interp, objv[1], subcmdSpec, sizeof(Ns_SubCmdSpec), "subcmd",
1909 TCL_EXACT1, &opt);
1910 if (likely(result == TCL_OK)(__builtin_expect((result == 0), 1))) {
1911 result = (*subcmdSpec[opt].proc)(clientData, interp, objc, objv);
1912 }
1913 }
1914 return result;
1915}
1916
1917
1918/*
1919 *----------------------------------------------------------------------
1920 *
1921 * GetOptIndexSubcmdSpec --
1922 *
1923 * Process options similar to Tcl_GetIndexFromObj() but don't
1924 * cache results as internal reps.
1925 *
1926 * Background: see GetOptIndexObjvSpec()
1927 *
1928 * Results:
1929 * Tcl result
1930 *
1931 * Side effects:
1932 * None.
1933 *
1934 *----------------------------------------------------------------------
1935 */
1936static int
1937GetOptIndexSubcmdSpec(Tcl_Interp *interp, Tcl_Obj *obj, const char *msg, const Ns_SubCmdSpec *tablePtr, int *idxPtr)
1938{
1939 const Ns_SubCmdSpec *entryPtr;
1940 const char *key;
1941 int idx, result = TCL_ERROR1;
1942
1943 NS_NONNULL_ASSERT(interp != NULL)((void) (0));
1944 NS_NONNULL_ASSERT(obj != NULL)((void) (0));
1945 NS_NONNULL_ASSERT(msg != NULL)((void) (0));
1946 NS_NONNULL_ASSERT(tablePtr != NULL)((void) (0));
1947 NS_NONNULL_ASSERT(idxPtr != NULL)((void) (0));
1948
1949 key = Tcl_GetString(obj);
1950
1951 for (entryPtr = tablePtr, idx = 0; entryPtr->key != NULL((void*)0); entryPtr++, idx++) {
1952 const char *p1, *p2;
1953
1954 for (p1 = key, p2 = entryPtr->key; *p1 == *p2; p1++, p2++) {
1955 if (*p1 == '\0') {
1956 /*
1957 * Both words are at their ends. Match is successful.
1958 */
1959 *idxPtr = idx;
1960 result = TCL_OK0;
1961 break;
1962 }
1963 }
1964 if (*p1 == '\0') {
1965 /*
1966 * The value is an abbreviation for this entry.
1967 */
1968 break;
1969 }
1970 }
1971
1972 if (result == TCL_ERROR1) {
1973 Tcl_Obj *resultPtr;
1974
1975 /*
1976 * Produce a fancy error message.
1977 */
1978 resultPtr = Tcl_NewObj();
1979 Tcl_AppendStringsToObj(resultPtr, "bad ", msg, " \"", key, (char *)0L);
1980
1981 entryPtr = tablePtr;
1982 if (entryPtr->key == NULL((void*)0)) {
1983 /*
1984 * The table is empty
1985 */
1986 Tcl_AppendStringsToObj(resultPtr, "\": no valid options", (char *)0L);
1987 } else {
1988 int count = 0;
1989 /*
1990 * The table has keys
1991 */
1992 Tcl_AppendStringsToObj(resultPtr, "\": must be ", entryPtr->key, (char *)0L);
1993 entryPtr++;
1994 while (entryPtr->key != NULL((void*)0)) {
1995 if ((entryPtr+1)->key == NULL((void*)0)) {
1996 Tcl_AppendStringsToObj(resultPtr, (count > 0 ? "," : NS_EMPTY_STRING),
1997 " or ", entryPtr->key, (char *)0L);
1998 } else {
1999 Tcl_AppendStringsToObj(resultPtr, ", ", entryPtr->key, (char *)0L);
2000 count++;
2001 }
2002 entryPtr++;
2003 }
2004 }
2005 Tcl_SetObjResult(interp, resultPtr);
2006 Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "INDEX", msg, key, (char *)0L);
2007 }
2008
2009 return result;
2010}
2011
2012
2013/*
2014 * Local Variables:
2015 * mode: c
2016 * c-basic-offset: 4
2017 * fill-column: 70
2018 * indent-tabs-mode: nil
2019 * End:
2020 */