text: /*
   * Copyright (C) 2000-2007 Carsten Haitzler, Geoff Harrison and various contributors
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to
   * deal in the Software without restriction, including without limitation the
   * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
   * sell copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies of the Software, its documentation and marketing & publicity
   * materials, and acknowledgment shall be given in the documentation, materials
   * and software packages that this Software was used.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  /* sgi's stdio.h has:
   * 
   * #if _SGIAPI && _NO_ANSIMODE
   * extern int      vsnprintf(char *, ssize_t, const char *, char *);
   * #endif
   * 
   * so workaround...
   */
  #ifdef __sgi
  #ifdef _NO_ANSIMODE
  #undef _NO_ANSIMODE
  #endif
  #endif
  
  #include "E.h"
  #include <errno.h>
  #include <ctype.h>
  
  /* 
   * Shamelessly snarfed from sane... 
   * which shamelessly snarfed from LPR 
   * which probably shamelessly snarfed from....
   * 
   * Moved comments to end so I can actually read the code.. cleaned out useless
   * junk....
   */
  
  #define VA_LOCAL_DECL va_list ap
  #define VA_START(f) va_start(ap, f)
  #define VA_SHIFT(v,t) ;		/* no-op for ANSI */
  #define VA_END va_end(ap)
  
  /*
   * dopr(): poor man's version of doprintf
   */
  
  static void         dopr(char *buffer, const char *format, va_list args);
  static void         fmtstr(char *value, int ljust, int len, int zpad,
  			   int precision);
  static void         fmtnum(long value, int base, int dosign, int ljust, int len,
  			   int zpad, int precision);
  static void         fmtdouble(int fmt, double value, int ljust, int len,
  			      int zpad, int precision);
  static void         dostr(char *);
  static char        *output;
  static void         dopr_outch(int c);
  static char        *end;
  int                 visible_control = 1;
  
  int
  Evsnprintf(char *str, size_t count, const char *fmt, va_list args)
  {
     str[0] = 0;
     end = str + count - 1;
     dopr(str, fmt, args);
     if (count > 0)
       {
  	end[0] = 0;
       }
     return (strlen(str));
  }
  
  #ifdef HAVE_STDARG_H
  int
  Esnprintf(char *str, size_t count, const char *fmt, ...)
  #else
  int
  Esnprintf(va_alist)
       va_dcl
  #endif
  {
  #ifndef HAVE_STDARG_H
     char               *str;
     size_t              count;
     char               *fmt;
  
  #endif
     VA_LOCAL_DECL;
  
     VA_START(fmt);
     VA_SHIFT(str, char *);
  
     VA_SHIFT(count, size_t);
     VA_SHIFT(fmt, char *);
  
     (void)Evsnprintf(str, count, fmt, ap);
     VA_END;
     return (strlen(str));
  }
  
  static void
  dopr(char *buffer, const char *format, va_list args)
  {
     int                 ch;
     long                value;
     int                 longflag = 0;
     char               *strvalue;
     int                 ljust;
     int                 len;
     int                 zpad;
     int                 precision;
     int                 set_precision;
     double              dval;
  
     output = buffer;
     while ((ch = *format++))
       {
  	switch (ch)
  	  {
  	  case '%':
  	     ljust = len = zpad = 0;
  	     precision = -1;
  	     set_precision = 0;
  	   nextch:
  	     ch = *format++;
  	     switch (ch)
  	       {
  	       case 0:
  		  dostr("**end of format**");
  		  return;
  	       case '-':
  		  ljust = 1;
  		  goto nextch;
  	       case '.':
  		  set_precision = 1;
  		  precision = 0;
  		  goto nextch;
  	       case '*':
  		  len = va_arg(args, int);
  
  		  goto nextch;
  	       case '0':	/* set zero padding if len not set */
  		  if (len == 0 && set_precision == 0)
  		     zpad = '0';
  	       case '1':
  	       case '2':
  	       case '3':
  	       case '4':
  	       case '5':
  	       case '6':
  	       case '7':
  	       case '8':
  	       case '9':
  		  if (set_precision)
  		    {
  		       precision = precision * 10 + ch - '0';
  		    }
  		  else
  		    {
  		       len = len * 10 + ch - '0';
  		    }
  		  goto nextch;
  	       case 'l':
  		  longflag = 1;
  		  goto nextch;
  	       case 'u':
  	       case 'U':
  		  /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */
  		  if (longflag)
  		    {
  		       value = va_arg(args, long);
  		    }
  		  else
  		    {
  		       value = va_arg(args, int);
  		    }
  		  fmtnum(value, 10, 0, ljust, len, zpad, precision);
  		  break;
  	       case 'o':
  	       case 'O':
  		  /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */
  		  if (longflag)
  		    {
  		       value = va_arg(args, long);
  		    }
  		  else
  		    {
  		       value = va_arg(args, int);
  		    }
  		  fmtnum(value, 8, 0, ljust, len, zpad, precision);
  		  break;
  	       case 'd':
  	       case 'i':
  	       case 'D':
  		  if (longflag)
  		    {
  		       value = va_arg(args, long);
  		    }
  		  else
  		    {
  		       value = va_arg(args, int);
  		    }
  		  fmtnum(value, 10, 1, ljust, len, zpad, precision);
  		  break;
  	       case 'x':
  		  if (longflag)
  		    {
  		       value = va_arg(args, long);
  		    }
  		  else
  		    {
  		       value = va_arg(args, int);
  		    }
  		  fmtnum(value, 16, 0, ljust, len, zpad, precision);
  		  break;
  	       case 'X':
  		  if (longflag)
  		    {
  		       value = va_arg(args, long);
  		    }
  		  else
  		    {
  		       value = va_arg(args, int);
  		    }
  		  fmtnum(value, -16, 0, ljust, len, zpad, precision);
  		  break;
  	       case 's':
  		  strvalue = va_arg(args, char *);
  
  		  fmtstr(strvalue, ljust, len, zpad, precision);
  		  break;
  	       case 'c':
  		  ch = va_arg(args, int);
  
  		  {
  		     char                b[2];
  		     int                 vsb = visible_control;
  
  		     b[0] = ch;
  		     b[1] = 0;
  		     visible_control = 0;
  		     fmtstr(b, ljust, len, zpad, precision);
  		     visible_control = vsb;
  		  }
  		  break;
  	       case 'f':
  	       case 'g':
  		  dval = va_arg(args, double);
  
  		  fmtdouble(ch, dval, ljust, len, zpad, precision);
  		  break;
  	       case '%':
  		  dopr_outch(ch);
  		  continue;
  	       default:
  		  dostr("???????");
  	       }
  	     longflag = 0;
  	     break;
  	  default:
  	     dopr_outch(ch);
  	     break;
  	  }
       }
     *output = 0;
  }
  
  /*
   * Format '%[-]len[.precision]s'
   * -   = left justify (ljust)
   * len = minimum length
   * precision = numbers of chars in string to use
   */
  static void
  fmtstr(char *value, int ljust, int len, int zpad, int precision)
  {
     int                 padlen, strlen, i, c;	/* amount to pad */
  
     zpad = 0;
     if (!value)
       {
  	value = "<NULL>";
       }
     if (precision > 0)
       {
  	strlen = precision;
       }
     else
       {
  	/* cheap strlen so you do not have library call */
  	for (strlen = 0; (c = value[strlen]); ++strlen)
  	  {
  	     if (visible_control && iscntrl(c) && !isspace(c))
  	       {
  		  ++strlen;
  	       }
  	  }
       }
     padlen = len - strlen;
     if (padlen < 0)
        padlen = 0;
     if (ljust)
        padlen = -padlen;
     while (padlen > 0)
       {
  	dopr_outch(' ');
  	--padlen;
       }
     /* output characters */
     for (i = 0; (c = value[i]); ++i)
       {
  	if (visible_control && iscntrl(c) && !isspace(c))
  	  {
  	     dopr_outch('^');
  	     c = ('@' | (c & 0x1F));
  	  }
  	dopr_outch(c);
       }
     while (padlen < 0)
       {
  	dopr_outch(' ');
  	++padlen;
       }
  }
  
  static void
  fmtnum(long value, int base, int dosign, int ljust, int len, int zpad,
         int precision)
  {
     int                 signvalue = 0;
     unsigned long       uvalue;
     char                convert[20];
     int                 place = 0;
     int                 padlen = 0;	/* amount to pad */
     int                 caps = 0;
  
     precision = 0;
     /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n",
      * value, base, dosign, ljust, len, zpad )); */
     uvalue = value;
     if (dosign)
       {
  	if (value < 0)
  	  {
  	     signvalue = '-';
  	     uvalue = -value;
  	  }
       }
     if (base < 0)
       {
  	caps = 1;
  	base = -base;
       }
     do
       {
  	convert[place++] =
  	   (caps ? "0123456789ABCDEF" : "0123456789abcdef")[uvalue %
  							    (unsigned)base];
  	uvalue = (uvalue / (unsigned)base);
       }
     while (uvalue);
     convert[place] = 0;
     padlen = len - place;
     if (padlen < 0)
        padlen = 0;
     if (ljust)
        padlen = -padlen;
     /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
      * convert,place,signvalue,padlen)); */
     if (zpad && padlen > 0)
       {
  	if (signvalue)
  	  {
  	     dopr_outch(signvalue);
  	     --padlen;
  	     signvalue = 0;
  	  }
  	while (padlen > 0)
  	  {
  	     dopr_outch(zpad);
  	     --padlen;
  	  }
       }
     while (padlen > 0)
       {
  	dopr_outch(' ');
  	--padlen;
       }
     if (signvalue)
        dopr_outch(signvalue);
     while (place > 0)
        dopr_outch(convert[--place]);
     while (padlen < 0)
       {
  	dopr_outch(' ');
  	++padlen;
       }
  }
  
  static void
  fmtdouble(int fmt, double value, int ljust, int len, int zpad, int precision)
  {
     char                convert[128];
     char                fmtstr[128];
     int                 l;
  
     zpad = 0;
     if (len == 0)
        len = 10;
     if (len > (int)sizeof(convert) - 10)
       {
  	len = (int)sizeof(convert) - 10;
       }
     if (precision > (int)sizeof(convert) - 10)
       {
  	precision = (int)sizeof(convert) - 10;
       }
     if (precision > len)
        precision = len;
     strcpy(fmtstr, "%");
     if (ljust)
        strcat(fmtstr, "-");
     if (len)
       {
  	sprintf(fmtstr + strlen(fmtstr), "%d", len);
       }
     if (precision > 0)
       {
  	sprintf(fmtstr + strlen(fmtstr), ".%d", precision);
       }
     l = strlen(fmtstr);
     fmtstr[l] = fmt;
     fmtstr[l + 1] = 0;
     sprintf(convert, fmtstr, value);
     dostr(convert);
  }
  
  static void
  dostr(char *str)
  {
     while (*str)
        dopr_outch(*str++);
  }
  
  static void
  dopr_outch(int c)
  {
     if (!end || output < end)
       {
  	*output++ = c;
       }
  }
  
  /**************************************************************
   * Original:
   * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
   * A bombproof version of doprnt (dopr) included.
   * Sigh.  This sort of thing is always nasty do deal with.  Note that
   * the version here does not include floating point...
   *
   * plp_snprintf() is used instead of sprintf() as it does limit checks
   * for string length.  This covers a nasty loophole.
   *
   * The other functions are there to prevent NULL pointers from
   * causing nast effects.
   **************************************************************/
  
  /***************************************************************************
   * LPRng - An Extended Print Spooler System
   *
   * Copyright 1988-1997, Patrick Powell, San Diego, CA
   *     papowell@sdsu.edu
   * See below for conditions of use.
   *
   ***************************************************************************
   * MODULE: snprintf.c
   * PURPOSE: LPRng version of printf - absolutely bombproof (hopefully!)
   **************************************************************************/
  
  /*
   * The "Artistic License"
   * 
   * Preamble
   * 
   * The intent of this document is to state the conditions under which a
   * Package may be copied, such that the Copyright Holder maintains some
   * semblance of artistic control over the development of the package,
   * while giving the users of the package the right to use and distribute
   * the Package in a more-or-less customary fashion, plus the right to make
   * reasonable modifications.
   * 
   * Definitions:
   * 
   * "Package" refers to the collection of files distributed by the
   * Copyright Holder, and derivatives of that collection of files
   * created through textual modification.
   * 
   * "Standard Version" refers to such a Package if it has not been
   * modified, or has been modified in accordance with the wishes
   * of the Copyright Holder as specified below.
   * 
   * "Copyright Holder" is whoever is named in the copyright or
   * copyrights for the package.
   * 
   * "You" is you, if you are thinking about copying or distributing
   * this Package.
   * 
   * "Reasonable copying fee" is whatever you can justify on the
   * basis of media cost, duplication charges, time of people involved,
   * and so on.  (You will not be required to justify it to the
   * Copyright Holder, but only to the computing community at large
   * as a market that must bear the fee.)
   * 
   * "Freely Available" means that no fee is charged for the item
   * itself, though there may be fees involved in handling the item.
   * It also means that recipients of the item may redistribute it
   * under the same conditions they received it.
   * 
   * 1. You may make and give away verbatim copies of the source form of the
   * Standard Version of this Package without restriction, provided that you
   * duplicate all of the original copyright notices and associated disclaimers.
   * 
   * 2. You may apply bug fixes, portability fixes and other modifications
   * derived from the Public Domain or from the Copyright Holder.  A Package
   * modified in such a way shall still be considered the Standard Version.
   * 
   * 3. You may otherwise modify your copy of this Package in any way, provided
   * that you insert a prominent notice in each changed file stating how and
   * when you changed that file, and provided that you do at least ONE of the
   * following:
   * 
   * a) place your modifications in the Public Domain or otherwise make them
   * Freely Available, such as by posting said modifications to Usenet or
   * an equivalent medium, or placing the modifications on a major archive
   * site such as uunet.uu.net, or by allowing the Copyright Holder to include
   * your modifications in the Standard Version of the Package.
   * 
   * b) use the modified Package only within your corporation or organization.
   * 
   * c) rename any non-standard executables so the names do not conflict
   * with standard executables, which must also be provided, and provide
   * a separate manual page for each non-standard executable that clearly
   * documents how it differs from the Standard Version.
   * 
   * d) make other distribution arrangements with the Copyright Holder.
   * 
   * 4. You may distribute the programs of this Package in object code or
   * executable form, provided that you do at least ONE of the following:
   * 
   * a) distribute a Standard Version of the executables and library files,
   * together with instructions (in the manual page or equivalent) on where
   * to get the Standard Version.
   * 
   * b) accompany the distribution with the machine-readable source of
   * the Package with your modifications.
   * 
   * c) give non-standard executables non-standard names, and clearly
   * document the differences in manual pages (or equivalent), together
   * with instructions on where to get the Standard Version.
   * 
   * d) make other distribution arrangements with the Copyright Holder.
   * 
   * 5. You may charge a reasonable copying fee for any distribution of this
   * Package.  You may charge any fee you choose for support of this
   * Package.  You may not charge a fee for this Package itself.  However,
   * you may distribute this Package in aggregate with other (possibly
   * commercial) programs as part of a larger (possibly commercial) software
   * distribution provided that you do not advertise this Package as a
   * product of your own. 
   * 
   * 6. The name of the Copyright Holder may not be used to endorse or promote
   * products derived from this software without specific prior written permission.
   * 
   * 7. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
   * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
   * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
   * 
   * The End
   */
  