Annotation of embedaddon/libpdel/tmpl/tmpl_exec.c, revision 1.1
1.1 ! misho 1:
! 2: /*
! 3: * Copyright (c) 2001-2002 Packet Design, LLC.
! 4: * All rights reserved.
! 5: *
! 6: * Subject to the following obligations and disclaimer of warranty,
! 7: * use and redistribution of this software, in source or object code
! 8: * forms, with or without modifications are expressly permitted by
! 9: * Packet Design; provided, however, that:
! 10: *
! 11: * (i) Any and all reproductions of the source or object code
! 12: * must include the copyright notice above and the following
! 13: * disclaimer of warranties; and
! 14: * (ii) No rights are granted, in any manner or form, to use
! 15: * Packet Design trademarks, including the mark "PACKET DESIGN"
! 16: * on advertising, endorsements, or otherwise except as such
! 17: * appears in the above copyright notice or in the software.
! 18: *
! 19: * THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
! 20: * TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
! 21: * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
! 22: * THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
! 23: * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
! 24: * OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
! 25: * OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
! 26: * OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
! 27: * RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
! 28: * LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
! 29: * OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
! 30: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
! 31: * DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
! 32: * USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
! 33: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
! 34: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
! 35: * THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
! 36: * THE POSSIBILITY OF SUCH DAMAGE.
! 37: *
! 38: * Author: Archie Cobbs <archie@freebsd.org>
! 39: */
! 40:
! 41: #include "tmpl_internal.h"
! 42:
! 43: /*
! 44: * Internal functions
! 45: */
! 46: static char *tmpl_invoke_defined(struct tmpl_ctx *ctx,
! 47: struct exec_func *func, char **errmsgp,
! 48: int argc, char **argv);
! 49: static int tmpl_invoke_defined2(struct tmpl_ctx *ctx,
! 50: struct exec_func *func, char **errmsgp,
! 51: int argc, char **argv);
! 52: static void tmpl_exec_cleanup(void *arg);
! 53: static char *evaluate_arg(struct tmpl_ctx *ctx,
! 54: char **errmsgp, const struct func_arg *arg);
! 55: static void pr_err(struct tmpl_ctx *ctx, int errnum, const char *msg);
! 56: #if TMPL_DEBUG
! 57: static const char *tmpl_rtnstr(enum exec_rtn rtn);
! 58: #endif
! 59:
! 60: /*
! 61: * Execute a template.
! 62: */
! 63: int
! 64: tmpl_execute(struct tmpl *tmpl, struct tmpl_ctx *ctx, FILE *output, int flags)
! 65: {
! 66: /* Avoid reentrant execution with the same context */
! 67: if (ctx->output != NULL) {
! 68: errno = EBUSY;
! 69: return (-1);
! 70: }
! 71:
! 72: /* Allow output to be NULL */
! 73: if (output == NULL) {
! 74: if ((output = fopen(_PATH_DEVNULL, "w")) == NULL)
! 75: return (-1);
! 76: ctx->close_output = 1;
! 77: } else
! 78: ctx->close_output = 0;
! 79:
! 80: /* Push cleanup hook */
! 81: pthread_cleanup_push(tmpl_exec_cleanup, ctx);
! 82:
! 83: /* Set up context info for this run */
! 84: ctx->output = output;
! 85: ctx->orig_output = output;
! 86: ctx->flags = flags;
! 87:
! 88: /* Execute template */
! 89: _tmpl_execute_elems(ctx, tmpl->elems, 0, tmpl->num_elems);
! 90:
! 91: /* Finish up */
! 92: pthread_cleanup_pop(1);
! 93:
! 94: /* Done */
! 95: return (0);
! 96: }
! 97:
! 98: /*
! 99: * Clean up a context after execution.
! 100: */
! 101: static void
! 102: tmpl_exec_cleanup(void *arg)
! 103: {
! 104: struct tmpl_ctx *const ctx = arg;
! 105:
! 106: /* Close output if needed */
! 107: if (ctx->close_output)
! 108: fclose(ctx->output);
! 109: ctx->output = NULL;
! 110: ctx->orig_output = NULL;
! 111: ctx->flags = 0;
! 112: }
! 113:
! 114: /*
! 115: * Invoke a single template function.
! 116: */
! 117: int
! 118: tmpl_execute_func(struct tmpl_ctx *ctx, FILE *output,
! 119: char **errmsgp, int argc, char **argv, int flags)
! 120: {
! 121: struct tmpl *t;
! 122: char *input;
! 123: FILE *sbuf;
! 124: int rtn;
! 125: int i;
! 126:
! 127: /* Avoid reentrant execution with the same context */
! 128: if (ctx->output != NULL) {
! 129: errno = EBUSY;
! 130: return (-1);
! 131: }
! 132:
! 133: /* Initialize error string */
! 134: *errmsgp = NULL;
! 135:
! 136: /* Sanity */
! 137: if (argc < 1) {
! 138: errno = EINVAL;
! 139: return (-1);
! 140: }
! 141:
! 142: /* Create template input containing the function call */
! 143: if ((sbuf = string_buf_output(TYPED_MEM_TEMP)) == NULL)
! 144: return (-1);
! 145: fprintf(sbuf, "@%s(", argv[0]);
! 146: for (i = 1; i < argc; i++) {
! 147: char *qarg;
! 148:
! 149: /* Enquote and add argument */
! 150: if ((qarg = string_enquote(argv[i], TYPED_MEM_TEMP)) == NULL) {
! 151: fclose(sbuf);
! 152: return (-1);
! 153: }
! 154: fprintf(sbuf, "%s%s", i > 1 ? "," : "", qarg);
! 155: FREE(TYPED_MEM_TEMP, qarg);
! 156: }
! 157: fprintf(sbuf, ")");
! 158: if ((input = string_buf_content(sbuf, 1)) == NULL) {
! 159: fclose(sbuf);
! 160: return (-1);
! 161: }
! 162: fclose(sbuf);
! 163:
! 164: /* Create a new template from the input string */
! 165: if ((sbuf = string_buf_input(input, strlen(input), 0)) == NULL) {
! 166: FREE(TYPED_MEM_TEMP, input);
! 167: return (-1);
! 168: }
! 169: t = tmpl_create(sbuf, NULL, TYPED_MEM_TEMP);
! 170: fclose(sbuf);
! 171: FREE(TYPED_MEM_TEMP, input);
! 172: if (t == NULL)
! 173: return (-1);
! 174:
! 175: /* Execute template */
! 176: rtn = tmpl_execute(t, ctx, output, flags);
! 177:
! 178: /* Clean up */
! 179: tmpl_destroy(&t);
! 180: return (rtn);
! 181: }
! 182:
! 183: /*
! 184: * Execute elements.
! 185: */
! 186: enum exec_rtn
! 187: _tmpl_execute_elems(struct tmpl_ctx *ctx,
! 188: struct tmpl_elem *const elems, int first_elem, int nelems)
! 189: {
! 190: #if TMPL_DEBUG
! 191: char indent[80];
! 192: #endif
! 193: enum exec_rtn rtn;
! 194: int i;
! 195:
! 196: /* Increase nesting */
! 197: ctx->depth++;
! 198:
! 199: /* Check for infinite loop */
! 200: if (ctx->depth >= INFINITE_LOOP) {
! 201: pr_err(ctx, 0, "too much recursion in template execution");
! 202: rtn = RTN_NORMAL;
! 203: goto done;
! 204: }
! 205:
! 206: /* Debug */
! 207: #if TMPL_DEBUG
! 208: *indent = '\0';
! 209: for (i = 1; i < ctx->depth; i++)
! 210: strlcat(indent, " ", sizeof(indent));
! 211: DBG("%sExecuting %d elems starting at %d\n",
! 212: indent, nelems, first_elem);
! 213: #endif
! 214:
! 215: /* Execute elements in order */
! 216: for (i = first_elem; i < first_elem + nelems; i++) {
! 217: struct tmpl_elem *const elem = &elems[i];
! 218:
! 219: DBG("%s%4d: %s\n", indent, i, _tmpl_elemstr(&elems[i], i));
! 220:
! 221: /* Handle normal text */
! 222: if (elem->text != NULL) {
! 223:
! 224: /* Optionally skip initial NL + whitespace */
! 225: if (((elem->flags & TMPL_ELEM_NL_WHITE) != 0
! 226: && ctx->flags & TMPL_SKIP_NL_WHITE) != 0)
! 227: continue;
! 228:
! 229: /* Output text */
! 230: fwrite(elem->text, 1, elem->len, ctx->output);
! 231: continue;
! 232: }
! 233:
! 234: /* Handle function call */
! 235: switch (elem->call.type) {
! 236: case TY_NORMAL:
! 237: {
! 238: char *errmsg = NULL;
! 239: char *result;
! 240:
! 241: if ((result = _tmpl_invoke(ctx,
! 242: &errmsg, &elem->call)) == NULL) {
! 243: pr_err(ctx, errno, errmsg);
! 244: FREE(ctx->mtype, errmsg);
! 245: break;
! 246: }
! 247: DBG("%s --> \"%s\"\n", indent, result);
! 248: (void)fputs(result, ctx->output);
! 249: FREE(ctx->mtype, result);
! 250: break;
! 251: }
! 252:
! 253: case TY_LOOP:
! 254: {
! 255: struct loop_ctx this;
! 256: char *errmsg = NULL;
! 257: long count;
! 258: char *eptr;
! 259: char *x;
! 260:
! 261: if (!(x = evaluate_arg(ctx,
! 262: &errmsg, &elem->call.args[0]))) {
! 263: pr_err(ctx, errno, errmsg);
! 264: FREE(ctx->mtype, errmsg);
! 265: i += elem->u.u_loop.endloop;
! 266: break;
! 267: }
! 268: count = strtoul(x, &eptr, 10);
! 269: if (*x == '\0' || *eptr != '\0'
! 270: || count < 0 || count == LONG_MAX) {
! 271: char buf[32];
! 272:
! 273: snprintf(buf, sizeof(buf),
! 274: "invalid loop count \"%s\"", x);
! 275: pr_err(ctx, 0, buf);
! 276: FREE(ctx->mtype, x);
! 277: i += elem->u.u_loop.endloop;
! 278: break;
! 279: }
! 280: FREE(ctx->mtype, x);
! 281: this.outer = ctx->loop;
! 282: ctx->loop = &this;
! 283: for (this.index = 0; this.index < count; this.index++) {
! 284: rtn = _tmpl_execute_elems(ctx, elems,
! 285: i + 1, elem->u.u_loop.endloop - 1);
! 286: if (rtn == RTN_BREAK)
! 287: break;
! 288: if (rtn == RTN_RETURN) {
! 289: ctx->loop = this.outer;
! 290: goto done;
! 291: }
! 292: }
! 293: ctx->loop = this.outer;
! 294: i += elem->u.u_loop.endloop;
! 295: break;
! 296: }
! 297:
! 298: case TY_WHILE:
! 299: while (1) {
! 300: char *errmsg = NULL;
! 301: int truth;
! 302: char *x;
! 303:
! 304: if (!(x = evaluate_arg(ctx,
! 305: &errmsg, &elem->call.args[0]))) {
! 306: pr_err(ctx, errno, errmsg);
! 307: FREE(ctx->mtype, errmsg);
! 308: i += elem->u.u_while.endwhile;
! 309: break;
! 310: }
! 311: truth = _tmpl_true(x);
! 312: FREE(ctx->mtype, x);
! 313: if (!truth)
! 314: break;
! 315: rtn = _tmpl_execute_elems(ctx, elems, i + 1,
! 316: elem->u.u_while.endwhile - 1);
! 317: if (rtn == RTN_BREAK)
! 318: break;
! 319: if (rtn == RTN_RETURN)
! 320: goto done;
! 321: }
! 322: i += elem->u.u_while.endwhile;
! 323: break;
! 324:
! 325: case TY_IF:
! 326: case TY_ELIF:
! 327: {
! 328: char *errmsg = NULL;
! 329: int first = -1;
! 330: int num = 0;
! 331: int truth;
! 332: char *x;
! 333:
! 334: if (!(x = evaluate_arg(ctx,
! 335: &errmsg, &elem->call.args[0]))) {
! 336: pr_err(ctx, errno, errmsg);
! 337: FREE(ctx->mtype, errmsg);
! 338: i += elem->u.u_if.endif;
! 339: break;
! 340: }
! 341: truth = _tmpl_true(x);
! 342: FREE(ctx->mtype, x);
! 343: if (truth) {
! 344: first = i + 1;
! 345: num = (elem->u.u_if.elsie != -1) ?
! 346: elem->u.u_if.elsie - 1 :
! 347: elem->u.u_if.endif - 1;
! 348: } else if (elem->u.u_if.elsie != -1) {
! 349: first = i + elem->u.u_if.elsie;
! 350: num = elem->u.u_if.endif - elem->u.u_if.elsie;
! 351: }
! 352: if (first != -1) {
! 353: rtn = _tmpl_execute_elems(ctx,
! 354: elems, first, num);
! 355: if (rtn == RTN_BREAK
! 356: || rtn == RTN_RETURN
! 357: || rtn == RTN_CONTINUE)
! 358: goto done;
! 359: }
! 360: i += elem->u.u_if.endif;
! 361: break;
! 362: }
! 363:
! 364: case TY_DEFINE:
! 365: {
! 366: char *errmsg = NULL;
! 367: char *name;
! 368:
! 369: if (!(name = evaluate_arg(ctx,
! 370: &errmsg, &elem->call.args[0]))) {
! 371: pr_err(ctx, errno, errmsg);
! 372: FREE(ctx->mtype, errmsg);
! 373: i += elem->u.u_define.enddef;
! 374: break;
! 375: }
! 376: #ifdef TMPL_DEBUG
! 377: DBG("%s%6sDefining function \"%s\" as %d elems"
! 378: " starting at %d\n", indent, "", name,
! 379: elem->u.u_define.enddef - 1, i + 1);
! 380: #endif
! 381: if (_tmpl_set_func(ctx, name, elems + i + 1,
! 382: elem->u.u_define.enddef - 1) == -1) {
! 383: pr_err(ctx, errno, NULL);
! 384: FREE(ctx->mtype, name);
! 385: i += elem->u.u_define.enddef;
! 386: break;
! 387: }
! 388: FREE(ctx->mtype, name);
! 389: i += elem->u.u_define.enddef;
! 390: break;
! 391: }
! 392:
! 393: case TY_ENDLOOP:
! 394: case TY_ENDWHILE:
! 395: case TY_ELSE:
! 396: case TY_ENDIF:
! 397: case TY_ENDDEF:
! 398: break;
! 399:
! 400: case TY_BREAK:
! 401: rtn = RTN_BREAK;
! 402: goto done;
! 403:
! 404: case TY_CONTINUE:
! 405: rtn = RTN_CONTINUE;
! 406: goto done;
! 407:
! 408: case TY_RETURN:
! 409: rtn = RTN_RETURN;
! 410: goto done;
! 411:
! 412: default:
! 413: assert(0);
! 414: }
! 415: }
! 416:
! 417: /* If we finished all the elements, that's a normal return */
! 418: rtn = RTN_NORMAL;
! 419:
! 420: done:
! 421: /* Debug */
! 422: #if TMPL_DEBUG
! 423: DBG("%sDone, return is %s\n", indent, tmpl_rtnstr(rtn));
! 424: #endif
! 425:
! 426: /* Decrease nesting */
! 427: ctx->depth--;
! 428:
! 429: /* Done */
! 430: return (rtn);
! 431: }
! 432:
! 433: /*
! 434: * Invoke a function and return the result. The function is
! 435: * assumed to not be a special built-in (eg, "@if").
! 436: */
! 437: char *
! 438: _tmpl_invoke(struct tmpl_ctx *ctx, char **errmsgp, const struct func_call *call)
! 439: {
! 440: char **args_copy = NULL;
! 441: char **args = NULL;
! 442: char *r = NULL;
! 443: int i;
! 444:
! 445: /* Evaluate arguments; first function argument is the function name */
! 446: if ((args = MALLOC(ctx->mtype,
! 447: (1 + call->nargs) * sizeof(*args))) == NULL)
! 448: return (NULL);
! 449: memset(args, 0, (1 + call->nargs) * sizeof(*args));
! 450: if ((args[0] = STRDUP(ctx->mtype, call->funcname)) == NULL)
! 451: goto fail;
! 452: for (i = 0; i < call->nargs; i++) {
! 453: if ((args[i + 1] = evaluate_arg(ctx,
! 454: errmsgp, &call->args[i])) == NULL)
! 455: goto fail;
! 456: }
! 457:
! 458: /* Save a copy of argument pointers so handlers can modify them */
! 459: if ((args_copy = MALLOC(ctx->mtype,
! 460: (1 + call->nargs) * sizeof(*args))) == NULL)
! 461: return (NULL);
! 462: memcpy(args_copy, args, (1 + call->nargs) * sizeof(*args));
! 463:
! 464: /* Invoke function, either run-time defined, built-in, or user */
! 465: assert(call->type == TY_NORMAL);
! 466: if ((i = _tmpl_find_func(ctx, call->funcname)) != -1) {
! 467: r = tmpl_invoke_defined(ctx, &ctx->funcs[i],
! 468: errmsgp, 1 + call->nargs, args_copy);
! 469: } else if (call->handler != NULL)
! 470: r = (*call->handler)(ctx, errmsgp, 1 + call->nargs, args_copy);
! 471: else
! 472: r = (*ctx->handler)(ctx, errmsgp, 1 + call->nargs, args_copy);
! 473:
! 474: fail:
! 475: for (i = 0; i < 1 + call->nargs; i++)
! 476: FREE(ctx->mtype, args[i]);
! 477: FREE(ctx->mtype, args);
! 478: FREE(ctx->mtype, args_copy);
! 479: return (r);
! 480: }
! 481:
! 482: /*
! 483: * Invoke a run-time defined function and return the resulting
! 484: * output as a string.
! 485: */
! 486: static char *
! 487: tmpl_invoke_defined(struct tmpl_ctx *ctx, struct exec_func *func,
! 488: char **errmsgp, int argc, char **argv)
! 489: {
! 490: FILE *const output_save = ctx->output;
! 491: char *rtn = NULL;
! 492: int esave;
! 493:
! 494: /* Create string output buffer to capture output */
! 495: if ((ctx->output = string_buf_output(ctx->mtype)) == NULL)
! 496: goto fail;
! 497:
! 498: /* Invoke function using existing context */
! 499: if (tmpl_invoke_defined2(ctx, func, errmsgp, argc, argv) == -1)
! 500: goto fail;
! 501:
! 502: /* Return the resulting output as a string */
! 503: rtn = string_buf_content(ctx->output, 1);
! 504:
! 505: fail:
! 506: /* Clean up and return */
! 507: esave = errno;
! 508: if (ctx->output != NULL)
! 509: fclose(ctx->output);
! 510: ctx->output = output_save;
! 511: errno = esave;
! 512: return (rtn);
! 513: }
! 514:
! 515: /*
! 516: * Invoke a runtime-defined function.
! 517: */
! 518: static int
! 519: tmpl_invoke_defined2(struct tmpl_ctx *ctx,
! 520: struct exec_func *func, char **errmsgp, int argc, char **argv)
! 521: {
! 522: char **saved_vars;
! 523: int rtn = 0;
! 524: int i;
! 525:
! 526: /* Save variables argc and arg0, arg1, ... */
! 527: if ((saved_vars = MALLOC(ctx->mtype,
! 528: (argc + 1) * sizeof(*saved_vars))) == NULL)
! 529: return (-1);
! 530: memset(saved_vars, 0, (argc + 1) * sizeof(*saved_vars));
! 531: for (i = 0; i <= argc; i++) {
! 532: char namebuf[32];
! 533: int j;
! 534:
! 535: if (i == argc)
! 536: strlcpy(namebuf, "argc", sizeof(namebuf));
! 537: else
! 538: snprintf(namebuf, sizeof(namebuf), "arg%u", i);
! 539: if ((j = _tmpl_find_var(ctx, namebuf)) != -1) {
! 540: if ((saved_vars[i] = STRDUP(ctx->mtype,
! 541: ctx->vars[j].value)) == NULL) {
! 542: while (--i >= 0)
! 543: FREE(ctx->mtype, saved_vars[i]);
! 544: FREE(ctx->mtype, saved_vars);
! 545: return (-1);
! 546: }
! 547: }
! 548: }
! 549:
! 550: /* Set new values for those variables */
! 551: for (i = 0; i <= argc; i++) {
! 552: char namebuf[32], valbuf[32];
! 553: char *argval;
! 554:
! 555: if (i == argc) {
! 556: strlcpy(namebuf, "argc", sizeof(namebuf));
! 557: snprintf(valbuf, sizeof(valbuf), "%u", argc);
! 558: argval = valbuf;
! 559: } else {
! 560: snprintf(namebuf, sizeof(namebuf), "arg%u", i);
! 561: argval = argv[i];
! 562: }
! 563: if (tmpl_ctx_set_var(ctx, namebuf, argval) == -1) {
! 564: rtn = -1;
! 565: goto fail;
! 566: }
! 567: }
! 568:
! 569: /* Execute function elements */
! 570: _tmpl_execute_elems(ctx, func->elems, 0, func->num_elems);
! 571:
! 572: fail:
! 573: /* Restore saved variables */
! 574: for (i = 0; i <= argc; i++) {
! 575: char argname[32];
! 576: int j;
! 577:
! 578: if (i == argc)
! 579: strlcpy(argname, "argc", sizeof(argname));
! 580: else
! 581: snprintf(argname, sizeof(argname), "arg%u", i);
! 582: if ((j = _tmpl_find_var(ctx, argname)) == -1) {
! 583: FREE(ctx->mtype, saved_vars[i]);
! 584: continue; /* we get here only in the 'goto fail' case */
! 585: }
! 586: if (saved_vars[i] != NULL) {
! 587: FREE(ctx->mtype, ctx->vars[j].value);
! 588: ctx->vars[j].value = saved_vars[i];
! 589: } else {
! 590: FREE(ctx->mtype, ctx->vars[j].name);
! 591: FREE(ctx->mtype, ctx->vars[j].value);
! 592: memmove(ctx->vars + j, ctx->vars + j + 1,
! 593: (--ctx->num_vars - j) * sizeof(*ctx->vars));
! 594: }
! 595: }
! 596: FREE(ctx->mtype, saved_vars);
! 597: return (rtn);
! 598: }
! 599:
! 600: /*
! 601: * Evaluate a function argument.
! 602: */
! 603: static char *
! 604: evaluate_arg(struct tmpl_ctx *ctx, char **errmsgp, const struct func_arg *arg)
! 605: {
! 606: if (arg->is_literal)
! 607: return (STRDUP(ctx->mtype, arg->u.literal));
! 608: else
! 609: return (_tmpl_invoke(ctx, errmsgp, &arg->u.call));
! 610: }
! 611:
! 612: /*
! 613: * Output an error string.
! 614: *
! 615: * If "msg" is not NULL, use that, otherwise use strerror(error).
! 616: */
! 617: static void
! 618: pr_err(struct tmpl_ctx *ctx, int error, const char *msg)
! 619: {
! 620: const int errno_save = errno;
! 621: char *cooked;
! 622:
! 623: /* Use strerror() error string if none more specific provided */
! 624: if (msg == NULL)
! 625: msg = strerror(errno);
! 626:
! 627: /* Format error string (if possible) and output it */
! 628: if (ctx->errfmtr != NULL
! 629: && (cooked = (*ctx->errfmtr)(ctx, msg)) != NULL) {
! 630: (void)fputs(cooked, ctx->output);
! 631: FREE(ctx->mtype, cooked);
! 632: } else
! 633: (void)fputs(msg, ctx->output);
! 634: errno = errno_save;
! 635: }
! 636:
! 637: /*
! 638: * Determine truth of a string.
! 639: */
! 640: int
! 641: _tmpl_true(const char *s)
! 642: {
! 643: char *eptr;
! 644: u_long val;
! 645:
! 646: val = strtol(s, &eptr, 0);
! 647: return (*s != '\0' && *eptr == '\0' && val != 0);
! 648: }
! 649:
! 650: #if TMPL_DEBUG
! 651: static const char *
! 652: tmpl_rtnstr(enum exec_rtn rtn)
! 653: {
! 654: switch (rtn) {
! 655: case RTN_NORMAL:
! 656: return ("NORMAL");
! 657: case RTN_BREAK:
! 658: return ("BREAK");
! 659: case RTN_CONTINUE:
! 660: return ("CONTINUE");
! 661: case RTN_RETURN:
! 662: return ("RETURN");
! 663: default:
! 664: assert(0);
! 665: }
! 666: return (NULL);
! 667: }
! 668: #endif
! 669:
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>