2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A
2N/A/*
2N/A * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <unistd.h>
2N/A#include <strings.h>
2N/A#include <stdlib.h>
2N/A#include <errno.h>
2N/A#include <assert.h>
2N/A#include <ctype.h>
2N/A#include <alloca.h>
2N/A
2N/A#include <dt_impl.h>
2N/A#include <dt_program.h>
2N/A#include <dt_printf.h>
2N/A#include <dt_provider.h>
2N/A
2N/Adtrace_prog_t *
2N/Adt_program_create(dtrace_hdl_t *dtp)
2N/A{
2N/A dtrace_prog_t *pgp = dt_zalloc(dtp, sizeof (dtrace_prog_t));
2N/A
2N/A if (pgp != NULL) {
2N/A dt_list_append(&dtp->dt_programs, pgp);
2N/A } else {
2N/A (void) dt_set_errno(dtp, EDT_NOMEM);
2N/A return (NULL);
2N/A }
2N/A
2N/A /*
2N/A * By default, programs start with DOF version 1 so that output files
2N/A * containing DOF are backward compatible. If a program requires new
2N/A * DOF features, the version is increased as needed.
2N/A */
2N/A pgp->dp_dofversion = DOF_VERSION_1;
2N/A
2N/A return (pgp);
2N/A}
2N/A
2N/Avoid
2N/Adt_program_destroy(dtrace_hdl_t *dtp, dtrace_prog_t *pgp)
2N/A{
2N/A dt_stmt_t *stp, *next;
2N/A uint_t i;
2N/A
2N/A for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
2N/A next = dt_list_next(stp);
2N/A dtrace_stmt_destroy(dtp, stp->ds_desc);
2N/A dt_free(dtp, stp);
2N/A }
2N/A
2N/A for (i = 0; i < pgp->dp_xrefslen; i++)
2N/A dt_free(dtp, pgp->dp_xrefs[i]);
2N/A
2N/A dt_free(dtp, pgp->dp_xrefs);
2N/A dt_list_delete(&dtp->dt_programs, pgp);
2N/A dt_free(dtp, pgp);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Avoid
2N/Adtrace_program_info(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
2N/A dtrace_proginfo_t *pip)
2N/A{
2N/A dt_stmt_t *stp;
2N/A dtrace_actdesc_t *ap;
2N/A dtrace_ecbdesc_t *last = NULL;
2N/A
2N/A if (pip == NULL)
2N/A return;
2N/A
2N/A bzero(pip, sizeof (dtrace_proginfo_t));
2N/A
2N/A if (dt_list_next(&pgp->dp_stmts) != NULL) {
2N/A pip->dpi_descattr = _dtrace_maxattr;
2N/A pip->dpi_stmtattr = _dtrace_maxattr;
2N/A } else {
2N/A pip->dpi_descattr = _dtrace_defattr;
2N/A pip->dpi_stmtattr = _dtrace_defattr;
2N/A }
2N/A
2N/A for (stp = dt_list_next(&pgp->dp_stmts); stp; stp = dt_list_next(stp)) {
2N/A dtrace_ecbdesc_t *edp = stp->ds_desc->dtsd_ecbdesc;
2N/A
2N/A if (edp == last)
2N/A continue;
2N/A last = edp;
2N/A
2N/A pip->dpi_descattr =
2N/A dt_attr_min(stp->ds_desc->dtsd_descattr, pip->dpi_descattr);
2N/A
2N/A pip->dpi_stmtattr =
2N/A dt_attr_min(stp->ds_desc->dtsd_stmtattr, pip->dpi_stmtattr);
2N/A
2N/A /*
2N/A * If there aren't any actions, account for the fact that
2N/A * recording the epid will generate a record.
2N/A */
2N/A if (edp->dted_action == NULL)
2N/A pip->dpi_recgens++;
2N/A
2N/A for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
2N/A if (ap->dtad_kind == DTRACEACT_SPECULATE) {
2N/A pip->dpi_speculations++;
2N/A continue;
2N/A }
2N/A
2N/A if (DTRACEACT_ISAGG(ap->dtad_kind)) {
2N/A pip->dpi_recgens -= ap->dtad_arg;
2N/A pip->dpi_aggregates++;
2N/A continue;
2N/A }
2N/A
2N/A if (DTRACEACT_ISDESTRUCTIVE(ap->dtad_kind))
2N/A continue;
2N/A
2N/A if (ap->dtad_kind == DTRACEACT_DIFEXPR &&
2N/A ap->dtad_difo->dtdo_rtype.dtdt_kind ==
2N/A DIF_TYPE_CTF &&
2N/A ap->dtad_difo->dtdo_rtype.dtdt_size == 0)
2N/A continue;
2N/A
2N/A pip->dpi_recgens++;
2N/A }
2N/A }
2N/A}
2N/A
2N/Aint
2N/Adtrace_program_exec(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
2N/A dtrace_proginfo_t *pip)
2N/A{
2N/A void *dof;
2N/A int n, err;
2N/A
2N/A dtrace_program_info(dtp, pgp, pip);
2N/A
2N/A if ((dof = dtrace_dof_create(dtp, pgp, DTRACE_D_STRIP)) == NULL)
2N/A return (-1);
2N/A
2N/A n = dt_ioctl(dtp, DTRACEIOC_ENABLE, dof);
2N/A dtrace_dof_destroy(dtp, dof);
2N/A
2N/A if (n == -1) {
2N/A switch (errno) {
2N/A case EINVAL:
2N/A err = EDT_DIFINVAL;
2N/A break;
2N/A case EFAULT:
2N/A err = EDT_DIFFAULT;
2N/A break;
2N/A case E2BIG:
2N/A err = EDT_DIFSIZE;
2N/A break;
2N/A case EBUSY:
2N/A err = EDT_ENABLING_ERR;
2N/A break;
2N/A default:
2N/A err = errno;
2N/A }
2N/A
2N/A return (dt_set_errno(dtp, err));
2N/A }
2N/A
2N/A if (pip != NULL)
2N/A pip->dpi_matches += n;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic void
2N/Adt_ecbdesc_hold(dtrace_ecbdesc_t *edp)
2N/A{
2N/A edp->dted_refcnt++;
2N/A}
2N/A
2N/Avoid
2N/Adt_ecbdesc_release(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
2N/A{
2N/A if (--edp->dted_refcnt > 0)
2N/A return;
2N/A
2N/A dt_difo_free(dtp, edp->dted_pred.dtpdd_difo);
2N/A assert(edp->dted_action == NULL);
2N/A dt_free(dtp, edp);
2N/A}
2N/A
2N/Adtrace_ecbdesc_t *
2N/Adt_ecbdesc_create(dtrace_hdl_t *dtp, const dtrace_probedesc_t *pdp)
2N/A{
2N/A dtrace_ecbdesc_t *edp;
2N/A
2N/A if ((edp = dt_zalloc(dtp, sizeof (dtrace_ecbdesc_t))) == NULL) {
2N/A (void) dt_set_errno(dtp, EDT_NOMEM);
2N/A return (NULL);
2N/A }
2N/A
2N/A edp->dted_probe = *pdp;
2N/A dt_ecbdesc_hold(edp);
2N/A return (edp);
2N/A}
2N/A
2N/Adtrace_stmtdesc_t *
2N/Adtrace_stmt_create(dtrace_hdl_t *dtp, dtrace_ecbdesc_t *edp)
2N/A{
2N/A dtrace_stmtdesc_t *sdp;
2N/A
2N/A if ((sdp = dt_zalloc(dtp, sizeof (dtrace_stmtdesc_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A dt_ecbdesc_hold(edp);
2N/A sdp->dtsd_ecbdesc = edp;
2N/A sdp->dtsd_descattr = _dtrace_defattr;
2N/A sdp->dtsd_stmtattr = _dtrace_defattr;
2N/A
2N/A return (sdp);
2N/A}
2N/A
2N/Adtrace_actdesc_t *
2N/Adtrace_stmt_action(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
2N/A{
2N/A dtrace_actdesc_t *new;
2N/A dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
2N/A
2N/A if ((new = dt_alloc(dtp, sizeof (dtrace_actdesc_t))) == NULL)
2N/A return (NULL);
2N/A
2N/A if (sdp->dtsd_action_last != NULL) {
2N/A assert(sdp->dtsd_action != NULL);
2N/A assert(sdp->dtsd_action_last->dtad_next == NULL);
2N/A sdp->dtsd_action_last->dtad_next = new;
2N/A } else {
2N/A dtrace_actdesc_t *ap = edp->dted_action;
2N/A
2N/A assert(sdp->dtsd_action == NULL);
2N/A sdp->dtsd_action = new;
2N/A
2N/A while (ap != NULL && ap->dtad_next != NULL)
2N/A ap = ap->dtad_next;
2N/A
2N/A if (ap == NULL)
2N/A edp->dted_action = new;
2N/A else
2N/A ap->dtad_next = new;
2N/A }
2N/A
2N/A sdp->dtsd_action_last = new;
2N/A bzero(new, sizeof (dtrace_actdesc_t));
2N/A new->dtad_uarg = (uintptr_t)sdp;
2N/A
2N/A return (new);
2N/A}
2N/A
2N/Aint
2N/Adtrace_stmt_add(dtrace_hdl_t *dtp, dtrace_prog_t *pgp, dtrace_stmtdesc_t *sdp)
2N/A{
2N/A dt_stmt_t *stp = dt_alloc(dtp, sizeof (dt_stmt_t));
2N/A
2N/A if (stp == NULL)
2N/A return (-1); /* errno is set for us */
2N/A
2N/A dt_list_append(&pgp->dp_stmts, stp);
2N/A stp->ds_desc = sdp;
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Adtrace_stmt_iter(dtrace_hdl_t *dtp, dtrace_prog_t *pgp,
2N/A dtrace_stmt_f *func, void *data)
2N/A{
2N/A dt_stmt_t *stp, *next;
2N/A int status = 0;
2N/A
2N/A for (stp = dt_list_next(&pgp->dp_stmts); stp != NULL; stp = next) {
2N/A next = dt_list_next(stp);
2N/A if ((status = func(dtp, pgp, stp->ds_desc, data)) != 0)
2N/A break;
2N/A }
2N/A
2N/A return (status);
2N/A}
2N/A
2N/Avoid
2N/Adtrace_stmt_destroy(dtrace_hdl_t *dtp, dtrace_stmtdesc_t *sdp)
2N/A{
2N/A dtrace_ecbdesc_t *edp = sdp->dtsd_ecbdesc;
2N/A
2N/A /*
2N/A * We need to remove any actions that we have on this ECB, and
2N/A * remove our hold on the ECB itself.
2N/A */
2N/A if (sdp->dtsd_action != NULL) {
2N/A dtrace_actdesc_t *last = sdp->dtsd_action_last;
2N/A dtrace_actdesc_t *ap, *next;
2N/A
2N/A assert(last != NULL);
2N/A
2N/A for (ap = edp->dted_action; ap != NULL; ap = ap->dtad_next) {
2N/A if (ap == sdp->dtsd_action)
2N/A break;
2N/A
2N/A if (ap->dtad_next == sdp->dtsd_action)
2N/A break;
2N/A }
2N/A
2N/A assert(ap != NULL);
2N/A
2N/A if (ap == edp->dted_action)
2N/A edp->dted_action = last->dtad_next;
2N/A else
2N/A ap->dtad_next = last->dtad_next;
2N/A
2N/A /*
2N/A * We have now removed our action list from its ECB; we can
2N/A * safely destroy the list.
2N/A */
2N/A last->dtad_next = NULL;
2N/A
2N/A for (ap = sdp->dtsd_action; ap != NULL; ap = next) {
2N/A assert(ap->dtad_uarg == (uintptr_t)sdp);
2N/A dt_difo_free(dtp, ap->dtad_difo);
2N/A next = ap->dtad_next;
2N/A dt_free(dtp, ap);
2N/A }
2N/A }
2N/A
2N/A if (sdp->dtsd_fmtdata != NULL)
2N/A dt_printf_destroy(sdp->dtsd_fmtdata);
2N/A
2N/A dt_ecbdesc_release(dtp, sdp->dtsd_ecbdesc);
2N/A dt_free(dtp, sdp);
2N/A}
2N/A
2N/Atypedef struct dt_header_info {
2N/A dtrace_hdl_t *dthi_dtp; /* consumer handle */
2N/A FILE *dthi_out; /* output file */
2N/A char *dthi_pmname; /* provider macro name */
2N/A char *dthi_pfname; /* provider function name */
2N/A int dthi_empty; /* should we generate empty macros */
2N/A} dt_header_info_t;
2N/A
2N/Astatic void
2N/Adt_header_fmt_macro(char *buf, const char *str)
2N/A{
2N/A for (;;) {
2N/A if (islower(*str)) {
2N/A *buf++ = *str++ + 'A' - 'a';
2N/A } else if (*str == '-') {
2N/A *buf++ = '_';
2N/A str++;
2N/A } else if (*str == '.') {
2N/A *buf++ = '_';
2N/A str++;
2N/A } else if ((*buf++ = *str++) == '\0') {
2N/A break;
2N/A }
2N/A }
2N/A}
2N/A
2N/Astatic void
2N/Adt_header_fmt_func(char *buf, const char *str)
2N/A{
2N/A for (;;) {
2N/A if (*str == '-') {
2N/A *buf++ = '_';
2N/A *buf++ = '_';
2N/A str++;
2N/A } else if ((*buf++ = *str++) == '\0') {
2N/A break;
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Adt_header_decl(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
2N/A{
2N/A dt_header_info_t *infop = data;
2N/A dtrace_hdl_t *dtp = infop->dthi_dtp;
2N/A dt_probe_t *prp = idp->di_data;
2N/A dt_node_t *dnp;
2N/A char buf[DT_TYPE_NAMELEN];
2N/A char *fname;
2N/A const char *p;
2N/A int i;
2N/A
2N/A p = prp->pr_name;
2N/A for (i = 0; (p = strchr(p, '-')) != NULL; i++)
2N/A p++;
2N/A
2N/A fname = alloca(strlen(prp->pr_name) + 1 + i);
2N/A dt_header_fmt_func(fname, prp->pr_name);
2N/A
2N/A if (fprintf(infop->dthi_out, "extern void __dtrace_%s___%s(",
2N/A infop->dthi_pfname, fname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A for (dnp = prp->pr_nargs, i = 0; dnp != NULL; dnp = dnp->dn_list, i++) {
2N/A if (fprintf(infop->dthi_out, "%s",
2N/A ctf_type_name(dnp->dn_ctfp, dnp->dn_type,
2N/A buf, sizeof (buf))) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (i + 1 != prp->pr_nargc &&
2N/A fprintf(infop->dthi_out, ", ") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A if (i == 0 && fprintf(infop->dthi_out, "void") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (fprintf(infop->dthi_out, ");\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (fprintf(infop->dthi_out,
2N/A "#ifndef\t__sparc\n"
2N/A "extern int __dtraceenabled_%s___%s(void);\n"
2N/A "#else\n"
2N/A "extern int __dtraceenabled_%s___%s(long);\n"
2N/A "#endif\n",
2N/A infop->dthi_pfname, fname, infop->dthi_pfname, fname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Adt_header_probe(dt_idhash_t *dhp, dt_ident_t *idp, void *data)
2N/A{
2N/A dt_header_info_t *infop = data;
2N/A dtrace_hdl_t *dtp = infop->dthi_dtp;
2N/A dt_probe_t *prp = idp->di_data;
2N/A char *mname, *fname;
2N/A const char *p;
2N/A int i;
2N/A
2N/A p = prp->pr_name;
2N/A for (i = 0; (p = strchr(p, '-')) != NULL; i++)
2N/A p++;
2N/A
2N/A mname = alloca(strlen(prp->pr_name) + 1);
2N/A dt_header_fmt_macro(mname, prp->pr_name);
2N/A
2N/A fname = alloca(strlen(prp->pr_name) + 1 + i);
2N/A dt_header_fmt_func(fname, prp->pr_name);
2N/A
2N/A if (fprintf(infop->dthi_out, "#define\t%s_%s(",
2N/A infop->dthi_pmname, mname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A for (i = 0; i < prp->pr_nargc; i++) {
2N/A if (fprintf(infop->dthi_out, "arg%d", i) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (i + 1 != prp->pr_nargc &&
2N/A fprintf(infop->dthi_out, ", ") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A if (!infop->dthi_empty) {
2N/A if (fprintf(infop->dthi_out, ") \\\n\t") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (fprintf(infop->dthi_out, "__dtrace_%s___%s(",
2N/A infop->dthi_pfname, fname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A for (i = 0; i < prp->pr_nargc; i++) {
2N/A if (fprintf(infop->dthi_out, "arg%d", i) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (i + 1 != prp->pr_nargc &&
2N/A fprintf(infop->dthi_out, ", ") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A }
2N/A
2N/A if (fprintf(infop->dthi_out, ")\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (!infop->dthi_empty) {
2N/A if (fprintf(infop->dthi_out,
2N/A "#ifndef\t__sparc\n"
2N/A "#define\t%s_%s_ENABLED() \\\n"
2N/A "\t__dtraceenabled_%s___%s()\n"
2N/A "#else\n"
2N/A "#define\t%s_%s_ENABLED() \\\n"
2N/A "\t__dtraceenabled_%s___%s(0)\n"
2N/A "#endif\n",
2N/A infop->dthi_pmname, mname,
2N/A infop->dthi_pfname, fname,
2N/A infop->dthi_pmname, mname,
2N/A infop->dthi_pfname, fname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A } else {
2N/A if (fprintf(infop->dthi_out, "#define\t%s_%s_ENABLED() (0)\n",
2N/A infop->dthi_pmname, mname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Astatic int
2N/Adt_header_provider(dtrace_hdl_t *dtp, dt_provider_t *pvp, FILE *out)
2N/A{
2N/A dt_header_info_t info;
2N/A const char *p;
2N/A int i;
2N/A
2N/A if (pvp->pv_flags & DT_PROVIDER_IMPL)
2N/A return (0);
2N/A
2N/A /*
2N/A * Count the instances of the '-' character since we'll need to double
2N/A * those up.
2N/A */
2N/A p = pvp->pv_desc.dtvd_name;
2N/A for (i = 0; (p = strchr(p, '-')) != NULL; i++)
2N/A p++;
2N/A
2N/A info.dthi_dtp = dtp;
2N/A info.dthi_out = out;
2N/A info.dthi_empty = 0;
2N/A
2N/A info.dthi_pmname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1);
2N/A dt_header_fmt_macro(info.dthi_pmname, pvp->pv_desc.dtvd_name);
2N/A
2N/A info.dthi_pfname = alloca(strlen(pvp->pv_desc.dtvd_name) + 1 + i);
2N/A dt_header_fmt_func(info.dthi_pfname, pvp->pv_desc.dtvd_name);
2N/A
2N/A if (fprintf(out, "#if _DTRACE_VERSION\n\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
2N/A return (-1); /* dt_errno is set for us */
2N/A if (fprintf(out, "\n\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A if (dt_idhash_iter(pvp->pv_probes, dt_header_decl, &info) != 0)
2N/A return (-1); /* dt_errno is set for us */
2N/A
2N/A if (fprintf(out, "\n#else\n\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A info.dthi_empty = 1;
2N/A
2N/A if (dt_idhash_iter(pvp->pv_probes, dt_header_probe, &info) != 0)
2N/A return (-1); /* dt_errno is set for us */
2N/A
2N/A if (fprintf(out, "\n#endif\n\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Aint
2N/Adtrace_program_header(dtrace_hdl_t *dtp, FILE *out, const char *fname)
2N/A{
2N/A dt_provider_t *pvp;
2N/A char *mfname, *p;
2N/A
2N/A if (fname != NULL) {
2N/A if ((p = strrchr(fname, '/')) != NULL)
2N/A fname = p + 1;
2N/A
2N/A mfname = alloca(strlen(fname) + 1);
2N/A dt_header_fmt_macro(mfname, fname);
2N/A if (fprintf(out, "#ifndef\t_%s\n#define\t_%s\n\n",
2N/A mfname, mfname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A }
2N/A
2N/A if (fprintf(out, "#include <unistd.h>\n\n") < 0)
2N/A return (-1);
2N/A
2N/A if (fprintf(out, "#ifdef\t__cplusplus\nextern \"C\" {\n#endif\n\n") < 0)
2N/A return (-1);
2N/A
2N/A for (pvp = dt_list_next(&dtp->dt_provlist);
2N/A pvp != NULL; pvp = dt_list_next(pvp)) {
2N/A if (dt_header_provider(dtp, pvp, out) != 0)
2N/A return (-1); /* dt_errno is set for us */
2N/A }
2N/A
2N/A if (fprintf(out, "\n#ifdef\t__cplusplus\n}\n#endif\n") < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A if (fname != NULL && fprintf(out, "\n#endif\t/* _%s */\n", mfname) < 0)
2N/A return (dt_set_errno(dtp, errno));
2N/A
2N/A return (0);
2N/A}