root/usr/src/cmd/fm/eversholt/common/out.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * out.c -- some basic output routines
 *
 */

#include <stdio.h>
#include <fm/fmd_api.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <time.h>
#include "out.h"
#include "stats.h"
#include "io.h"

/* stats we keep for "out" module */
static struct stats *Outcount;
static struct stats *Errcount;
static struct stats *Warncount;

static int Exitcode;

static const char *Myname;
static FILE *Altfp;

/* buffer used to format all prints */
#define MAXOUT 8192
static char Outbuf[MAXOUT];
static int Outidx;              /* next unused char in Outbuf[] */

/*
 * out_init -- initialize this module
 */
void
out_init(const char *myname)
{
        Outcount = stats_new_counter("output.calls", "total calls", 1);
        Errcount = stats_new_counter("output.errors", "total errors", 0);
        Warncount = stats_new_counter("output.warnings", "total warnings", 0);

        if (myname == NULL)
                return;

        if ((Myname = strrchr(myname, '/')) == NULL &&
            (Myname = strrchr(myname, '\\')) == NULL)
                Myname = myname;
        else
                Myname++;
}

void
out_fini(void)
{
        stats_delete(Outcount);
        Outcount = NULL;
        stats_delete(Errcount);
        Errcount = NULL;
        stats_delete(Warncount);
        Warncount = NULL;
}

/*
 * out_altfp -- store an alternate fp for O_ALTFP
 */
void
out_altfp(FILE *fp)
{
        Altfp = fp;
}

/*
 * voutbufprintf -- like vprintf, but appends to Outbuf
 */
static void
voutbufprintf(const char *fmt, va_list ap)
{
        int len = vsnprintf(&Outbuf[Outidx], MAXOUT - Outidx, fmt, ap);

        Outidx += len;
        if (Outidx >= MAXOUT)
                Outidx = MAXOUT - 1;
}

/*
 * outbufprintf -- like printf, but appends to Outbuf
 */
/*PRINTFLIKE1*/
static void
outbufprintf(const char *fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);
        voutbufprintf(fmt, ap);
        va_end(ap);
}

/*
 * vout -- va_list version of out()
 *
 * all the output processing work is done here.
 */
static void
vout(int flags, const char *fmt, va_list ap)
{
        int safe_errno = errno;

        stats_counter_bump(Outcount);

        /*
         * just return if called with a disabled output type.  this
         * prevents debug prints when Debug is off, verbose prints
         * when Verbose is off, and language warnings when warnings
         * are quenched (which they are when we're loading a .eft file).
         *
         */
        if ((flags & O_DEBUG) && Debug == 0)
                return;

        if ((flags & O_VERB) && Verbose == 0)
                return;

        if ((flags & O_VERB2) && Verbose < 2)
                return;

        if ((flags & O_VERB3) && Verbose < 3)
                return;

        if ((flags & O_WARN) && Warn == 0)
                return;

        if ((flags & O_ALTFP) && Altfp == NULL)
                return;

        /* some things only happen at the beginning of a print */
        if (Outidx == 0) {
                if (flags & O_USAGE) {
                        Exitcode++;
                        outbufprintf("usage: %s ", Myname);
                } else {
                        if (Myname && flags & (O_DIE|O_ERR|O_WARN|O_PROG))
                                outbufprintf("%s: ", Myname);

                        if (flags & O_DIE) {
                                Exitcode++;
                                outbufprintf("fatal error: ");
                        } else if (flags & O_ERR) {
                                Exitcode++;
                                stats_counter_bump(Errcount);
                                outbufprintf("error: ");
                        } else if (flags & O_WARN) {
                                stats_counter_bump(Warncount);
                                outbufprintf("warning: ");
                        }
                }
        }

        /* fmt can be NULL if the caller just wanted flags processed */
        if (fmt != NULL)
                voutbufprintf(fmt, ap);

        /* O_SYS means convert errno to a string and append it */
        if (flags & O_SYS) {
                const char *msg = strerror(safe_errno);

                if (Outidx != 0)
                        outbufprintf(": ");

                if (msg)
                        outbufprintf("%s", msg);
                else
                        outbufprintf("(error %d)", safe_errno);
        }

        /* O_STAMP means convert add a timestamp */
        if (flags & O_STAMP) {
                time_t clock;
                char *tmsg;

                (void) time(&clock);
                tmsg = ctime(&clock);
                if (tmsg && *tmsg) {
                        tmsg[strlen(tmsg) - 1] = '\0';

                        if (Outidx != 0)
                                outbufprintf(" ");

                        outbufprintf("%s", tmsg);
                }
        }

        if (flags & O_NONL)
                return;         /* not done filling Outbuf */

        /* done filling Outbuf, platform calls will add newline */
        if (flags & O_ALTFP)
                (void) fprintf(Altfp, "%s\n", Outbuf);
        else if (flags & O_ABORT)
                io_abort(Outbuf);
        else if (flags & O_DIE)
                io_die(Outbuf);
        else if (flags & O_ERR)
                io_err(Outbuf);
        else
                io_out(Outbuf);

        /* reset output buffer */
        Outidx = 0;
        Outbuf[0] = '\0';
}

/*
 * out -- spew a line of output, with various options
 */
/*PRINTFLIKE2*/
void
out(int flags, const char *fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);
        vout(flags, fmt, ap);
        va_end(ap);
}

/*
 * outfl -- spew a filename:linenumber message
 */
/*PRINTFLIKE4*/
void
outfl(int flags, const char *fname, int line, const char *fmt, ...)
{
        va_list ap;
        va_start(ap, fmt);

        if (fname)
                out(flags|O_NONL, "%s:%d: ", fname, line);

        vout(flags, fmt, ap);

        va_end(ap);
}

/*
 * out_exit -- exit the program
 */
void
out_exit(int code)
{
        io_exit(Exitcode + code);
}

/*
 * out_errcount -- return the number of O_ERR messages issued so far
 */
int
out_errcount(void)
{
        return (stats_counter_value(Errcount));
}

/*
 * out_warncount -- return the number of O_WARN messages issued so far
 */
int
out_warncount(void)
{
        return (stats_counter_value(Warncount));
}