/* ***************************************************************** *
 * Copyright 1998 International Business Machines Corporation. All   *
 * Rights Reserved.                                                  *
 *                                                                   *
 * Please read this carefully.  Your use of this reference           *
 * implementation of certain of the IETF public-key infrastructure   *
 * specifications ("Software") indicates your acceptance of the      *
 * following.  If you do not agree to the following, do not install  *
 * or use any of the Software.                                       *
 *                                                                   *
 * Permission to use, reproduce, distribute and create derivative    *
 * works from the Software ("Software Derivative Works"), and to     *
 * distribute such Software Derivative Works is hereby granted to    *
 * you by International Business Machines Corporation ("IBM").  This *
 * permission includes a license under the patents of IBM that are   *
 * necessarily infringed by your use of the Software as provided by  *
 * IBM.                                                              *
 *                                                                   *
 * IBM licenses the Software to you on an "AS IS" basis, without     *
 * warranty of any kind.  IBM HEREBY EXPRESSLY DISCLAIMS ALL         *
 * WARRANTIES OR CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING,   *
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OR CONDITIONS OF       *
 * MERCHANTABILITY, NON INFRINGEMENT AND FITNESS FOR A PARTICULAR    *
 * PURPOSE.  You are solely responsible for determining the          *
 * appropriateness of using this Software and assume all risks       *
 * associated with the use of this Software, including but not       *
 * limited to the risks of program errors, damage to or loss of      *
 * data, programs or equipment, and unavailability or interruption   *
 * of operations.                                                    *
 *                                                                   *
 * IBM WILL NOT BE LIABLE FOR ANY DIRECT DAMAGES OR FOR ANY SPECIAL, *
 * INCIDENTAL, OR  INDIRECT DAMAGES OR FOR ANY ECONOMIC              *
 * CONSEQUENTIAL DAMAGES (INCLUDING LOST PROFITS OR SAVINGS), EVEN   *
 * IF IBM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.  IBM  *
 * will not be liable for the loss of, or damage to, your records or *
 * data, or any damages claimed by you based on a third party claim. *
 *                                                                   *
 * IBM wishes to obtain your feedback to assist in improving the     *
 * Software.  You grant IBM a world-wide, royalty-free right to use, *
 * copy, distribute, sublicense and prepare derivative works based   *
 * upon any feedback, including materials, error corrections,        *
 * Software Derivatives, enhancements, suggestions and the like that *
 * you provide to IBM relating to the Software (this does not        *
 * include products for which you charge a royalty and distribute to *
 * IBM under other terms and conditions).                            *
 *                                                                   *
 * You agree to distribute the Software and any Software Derivatives *
 * under a license agreement that: 1) is sufficient to notify all    *
 * licensees of the Software and Software Derivatives that IBM       *
 * assumes no liability for any claim that may arise regarding the   *
 * Software or Software Derivatives, and 2) that disclaims all       *
 * warranties, both express and implied, from IBM regarding the      *
 * Software and Software Derivatives.  (If you include this          *
 * Agreement with any distribution of the Software or Software       *
 * Derivatives you will have met this requirement.)  You agree that  *
 * you will not delete any copyright notices in the Software.        *
 *                                                                   *
 * This Agreement is the exclusive statement of your rights in the   *
 * Software as provided by IBM.   Except for the rights granted to   *
 * you in the second paragraph above, You are not granted any other  *
 * patent rights, including but not limited to the right to make     *
 * combinations of the Software with products that infringe IBM      *
 * patents. You agree to comply with all applicable laws and         *
 * regulations, including all export and import laws and regulation. *
 * This Agreement is governed by the laws of the State of New York.  *
 * This Agreement supersedes all other communications,               *
 * understandings or agreements we may have had prior to this        *
 * Agreement.                                                        *
 * ***************************************************************** */

#include "pkiMsg.h"
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fsys.h>

#ifndef min
# define min(a,b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef max
# define max(a,b) (((a) > (b)) ? (a) : (b))
#endif



int pkiStsSet(uint32 s) {
  return ((s & PKI_STS_SET_MASK) >> PKI_STS_SET_POS);
}


int pkiStsCode(uint32 s) {
  return ((s & PKI_STS_COD_MASK) >> PKI_STS_COD_POS);
}


uint32 pkiMakeSts (int setId, int msgId) {
  return (uint32(setId) << PKI_STS_SET_POS) | ((uint32)msgId << PKI_STS_COD_POS);
}

#ifdef NEED_XPG_MESSAGES

// This is a Q&D XPG4 message catalog implementation for
// systems that don't have one of their own.  It should only
// be used if there isn't a native XPG4 implementation.
//
// In this implementation, catalogs are loaded only once, regardless
// of how many times they're opened, and catclose doesn't do anything.
// 

#include "pkiqdxpg.h"

#if OS == WIN32
#include <direct.h>
#endif

static FILE * tryopen(const char * name, 
                      const char * p, 
                      const char * lang) {
// We have a single directory in path.  Expand it according to lang if 
// necessary, and see if the file exists.
  char path[512];
  int s = 0;
  int d = 0;

  const char * lang_lang = NULL;
  const char * lang_territory = NULL;
  const char * lang_codeset = NULL;
  size_t lang_lang_len = 0;
  size_t lang_territory_len = 0;
  size_t lang_codeset_len = 0;
  char * cpos;
  FILE * f;

  if (lang != NULL) {
    lang_lang = lang;
    cpos = strchr(lang_lang, '_');
    if (cpos) lang_lang_len = cpos-lang_lang;
    else lang_lang_len = 0;

    if (cpos) {
      lang_territory = cpos+1;
      cpos = strchr(lang_territory, '.');
      if (cpos) lang_territory_len = cpos-lang_territory;
      else lang_territory_len = 0;
    } else lang_territory_len = 0;

    if (cpos) {
      lang_codeset = cpos+1;
      lang_codeset_len = strlen(lang_codeset);
    } else lang_codeset_len = 0;
  };
  
  if ((p != NULL) && (p[0] != 0)) {
    while (p[s] != 0) {
      if (p[s] == '%') {
        s++;
        if (p[s] == '%') {
          path[d++] = p[s++]; 
        } else if (p[s] == 'N') {
          strncat(path, name, sizeof(path) - strlen(path));
          d = strlen(path);
        } else if (p[s] == 'L') {
          strncat(path, lang, sizeof(path) - strlen(path));
          d = strlen(path);
        } else if (p[s] == 'l') {
          strncat(path, lang_lang, min(lang_lang_len,sizeof(path) - strlen(path)));
          d = strlen(path);
        } else if (p[s] == 't') {
          strncat(path, lang_territory, min(lang_territory_len,sizeof(path) - strlen(path)));
          d = strlen(path);
        } else if (p[s] == 'c') {
          strncat(path, lang_codeset, min(lang_codeset_len,sizeof(path) - strlen(path)));
          d = strlen(path);
        } else {
          path[d++] = p[s++];
        };
      } else path[d++] = p[s++];
    };
    path[d] = 0;
  } else path[0] = 0;

  f = fnopen(path, name, "rb");
  return f;
}

static FILE * findcat(const char * name, 
                      const char * path, 
                      const char * lang) {
// path may be a colon- (semicolon- on WIN32) separated list of directories.
// Split it up into individual directories, expand each using lang and name,
// and try to open the catalog...

  int c;
  int i;
  char fnam[512];
  FILE * f = NULL;

  c = i = 0;

  if (name == NULL) return NULL;

  if (path == NULL) {
    return tryopen(name, "", lang);
  };

  if ((name[0] == '/') || (name[0] == '\\')) {
// The name includes an absolute path...
    if ((f = tryopen(name, "", lang)) != NULL) return f;
  };

  while (path[c] != 0) {
#if OS == WIN32
    if (path[c] == ';') { 
#else      
    if (path[c] == ':') {
#endif
      fnam[i] = 0;
      if ((f = tryopen(name, fnam, lang)) != NULL) break;
      i = 0; c++;
      continue;
    };
    fnam[i++] = path[c++];
  }
  fnam[i] = 0;
  if ((f==NULL) && (i > 0)) f = tryopen(name, fnam, lang);

  return f;

}

pki_msgcat_handle_t pkiCatOpen(const char * name) {
  const char * locale;
  char lang[50];
  int i;
  FILE * f;
  pki_msgcat_handle_t h;
#if OS == WIN32
  char cwd[_MAX_PATH];
#endif

  locale = getenv("LC_ALL");
  if (locale == NULL) locale = getenv("LC_MESSAGES");
  if (locale == NULL) locale = getenv("LANG");
  if (locale) strncpy(lang, locale, sizeof(lang));
  else strncpy(lang, "en_US.ISO8859-1", sizeof(lang));
  for (i=0; lang[i] != 0; i++) if (lang[i] == '@') {lang[i] = 0; break;};
// Now lang should contain an XPG locale-name (with no modifier).
// Try and find an appropriate catalog...

  f = findcat(name, getenv("NLSPATH"), lang);
  if (f == NULL) f = findcat(name, getenv("PATH"), lang);
#if OS == WIN32
  if (f == NULL) { 
    if (getcwd(cwd, sizeof(cwd)) != NULL) f = findcat(name, cwd, lang);
  };
#endif

// Now f is either NULL (in which case we didn't find a catalog), or an
// open file handle.

  if (f == NULL) return 0;

// Found the file.  Read it...
  h = xpg_catalog_t::read(f);
  fclose(f);
  return h;
 
}

int pkiCatClose(pki_msgcat_handle_t h) {
  return 0;
}

const char * pkiCatgets(pki_msgcat_handle_t catalog,
                        uint32 status,
                        const char * defaultMsg) {
  const char * str;

  if ((str = xpg_catalog_t::lookupMessage(catalog,  pkiStsSet(status), pkiStsCode(status))) == NULL)
    str = defaultMsg;
  return str;
}

#else

// N.B. The following  assumes that on an XPG4-compliant system, 
// pki_msgcat_handle_t is defined to be equivalent to nl_catd.

pki_msgcat_handle_t pkiCatOpen(const char * name) {
  return catopen(name, NL_CAT_LOCALE);
}

int pkiCatClose(pki_msgcat_handle_t h) {
  return catclose(h);
}

char * pkiCatgets(pki_msgcat_handle_t catalog,
                  uint32 status,
                  const char * defaultMsg) {
  return catgets(catalog, pkiStsFac(status), pkiStsCode(status), defaultMsg);
}
#endif




#ifdef NEED_XPG_PRINTF

#define MAX_ARGS 20

static unsigned readNum(const char * format,
                        unsigned & i) {
  unsigned num = 0;
  while (isdigit(format[i])) {
    num=num*10;
    switch (format[i]) {
    case '1': num+=1; break;
    case '2': num+=2; break;
    case '3': num+=3; break;
    case '4': num+=4; break;
    case '5': num+=5; break;
    case '6': num+=6; break;
    case '7': num+=7; break;
    case '8': num+=8; break;
    case '9': num+=9; break;
    };
    i++;
  };
  return num;
}

#define TM_SHORT 1
#define TM_LONG 2

#define TYPE_NONE     0
#define TYPE_INT      1
#define TYPE_INT_L    2
#define TYPE_INT_S    3
#define TYPE_UINT     4
#define TYPE_UINT_L   5
#define TYPE_UINT_S   6
#define TYPE_HEX      7
#define TYPE_HEX_L    8
#define TYPE_HEX_S    9
#define TYPE_UHEX     10
#define TYPE_UHEX_L   11
#define TYPE_UHEX_S   12
#define TYPE_CHAR     13
#define TYPE_STRING   14
#define TYPE_PTR      15

#define FMT_MINUS 1
#define FMT_PLUS 2
#define FMT_HASH 4
#define FMT_SPACE 8
#define FMT_ZERO 16

static bool readType(const char * format, unsigned & i, unsigned & type) {
  int typeMod = 0;
  if (format[i] == 'h') {
    typeMod= TM_SHORT;
    i++;
  } else if (format[i] == 'l') {
    typeMod = TM_LONG;
    i++;
  };
  switch (format[i]) {
  case 'd': 
    switch (typeMod) {
    case TM_SHORT : type = TYPE_INT_S; break;
    case TM_LONG : type = TYPE_INT_L; break;
    default : type = TYPE_INT; break;
    };
    break;
  case 'u':
    switch (typeMod) {
    case TM_SHORT : type = TYPE_UINT_S; break;
    case TM_LONG : type = TYPE_UINT_L; break;
    default : type = TYPE_UINT; break;
    };
    break;
  case 'x':
    switch (typeMod) {
    case TM_SHORT : type = TYPE_HEX_S; break;
    case TM_LONG : type = TYPE_HEX_L; break;
    default : type = TYPE_HEX; break;
    };
    break;
  case 'X':
    switch (typeMod) {
    case TM_SHORT : type = TYPE_UHEX_S; break;
    case TM_LONG : type = TYPE_UHEX_L; break;
    default : type = TYPE_UHEX; break;
    }; 
    break;
  case 'c': type = TYPE_CHAR; break;
  case 's': type = TYPE_STRING; break;
  case 'p': type = TYPE_PTR; break;
  default : return false;
  };
  return true;
}


static bool parseFormat(const char * format, 
												unsigned & i, 
												unsigned & parNum, 
												uint16 & flags, 
                        bool & parWidth,
                        unsigned &width, 
                        bool & parPrecision,
												unsigned &precision, 
												unsigned &type) {
  unsigned num;
  bool doneWidth = false;

  parNum = 0;
  flags = 0;
  width = 0;
  precision = 0;
  type = 0;
  parWidth = false;
  parPrecision = false;

  if (format[i]!='%') return false;
  
  if (isdigit(format[++i])) {
    num=readNum(format, i);
    if (format[i] == '$') {
      parNum = num; i++;
    } else {
      parNum = 0; // No parameter position specifier, so the number we read
                  // must be a width specifier
      width = num;
      doneWidth = true;
    };
  };  
  if (!doneWidth) {
// We may have some flags to process...
    while ((format[i] == '-') || (format[i] == '+') || (format[i] == ' ') || (format[i] == '#') || (format[i] == '0')) {
      switch (format[i]) {
      case '-': flags |= FMT_MINUS; break;
      case '+': flags |= FMT_PLUS; break;
      case ' ': flags |= FMT_SPACE; break;
      case '#': flags |= FMT_HASH; break;
      case '0': flags |= FMT_ZERO; break;
      };
      i++;
    };
    if (format[i] == '*') parWidth = true;
    else if (isdigit(format[i])) width=readNum(format, i);
  };
  if (format[i] == '.') {
    if (format[++i] == '*') parPrecision = true;
    else if (isdigit(format[i])) precision=readNum(format, i);
  };
  return readType(format,i, type);
}

			
class argtype_t {
public:
  unsigned type;
  union {
    int v_int;
    sint16 v_int_s;
    sint32 v_int_l;
    unsigned v_uint;
    uint16 v_uint_s;
    uint32 v_uint_l;
    unsigned char v_char;
    char * v_string;
    void * v_ptr;
  } val;
  argtype_t(void) {
    type = TYPE_NONE;
    val.v_ptr= NULL;
  };
};

#define APPEND_CHAR(b,s,i,c) \
if (i>=s) b[i-1] = 0; \
else { b[i++]=c;b[i]=0; }
  
void append_buf(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr, 
                const char * b) {
  while ((c_ptr < bufferSize) && (*b != 0)) buffer[c_ptr++] = *b++;
  if (c_ptr < bufferSize) buffer[c_ptr] = 0;
  buffer[bufferSize-1] = 0;
}


void do_flags(uint16 flags,
              char * f) {
  if (flags & FMT_MINUS) *f++ = '-';
  if (flags & FMT_PLUS) *f++ = '+';
  if (flags & FMT_HASH) *f++ = '#';
  if (flags & FMT_SPACE) *f++ = ' ';
  if (flags & FMT_ZERO) *f++ = '0';
  *f = 0;
}

void append_int(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr,
                sint32 val,
                uint16 flags, 
                bool parWidth, 
                unsigned width, 
                bool parPrecision, 
                unsigned precision) {
  char b[40];
  char f[20];

  char fl[6];
  do_flags(flags, fl);

  if (parWidth && parPrecision) {
    sprintf(f, "%%%s%d.%dld", flags, width, precision);
  } else if (parWidth) {
    sprintf(f, "%%%s%dld", flags, width);
  } else if (parPrecision) {
    sprintf(f, "%%%s.%dld", flags, precision);
  } else {
    sprintf(f, "%%%sld", flags, precision);
  };
  sprintf(b, f, val);
  append_buf(buffer, bufferSize, c_ptr, b);
}

void append_uint(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr,
                uint32 val,
                uint16 flags, 
                bool parWidth, 
                unsigned width, 
                bool parPrecision, 
                unsigned precision) {
  char b[40];
  char f[20];

  char fl[6];
  do_flags(flags, fl);

  if (parWidth && parPrecision) {
    sprintf(f, "%%%s%d.%dlu", flags, width, precision);
  } else if (parWidth) {
    sprintf(f, "%%%s%dlu", flags, width);
  } else if (parPrecision) {
    sprintf(f, "%%%s.%dlu", flags, precision);
  } else {
    sprintf(f, "%%%slu", flags, precision);
  };
  sprintf(b, f, val);
  append_buf(buffer, bufferSize, c_ptr, b);
}

void append_hex(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr,
                uint32 val,
                bool uppercase,
                uint16 flags, 
                bool parWidth, 
                unsigned width, 
                bool parPrecision, 
                unsigned precision) {
  char b[40];
  char fl[6];
  char f[20];

  do_flags(flags, fl);

  if (parWidth && parPrecision) {
    if (uppercase) sprintf(f, "%%%s%d.%dlX", flags, width, precision);
    else sprintf(f, "%%%s%d.%dlx", flags, width, precision);
  } else if (parWidth) {
    if (uppercase) sprintf(f, "%%%s%dlX", flags, width);
    else sprintf(f, "%%%s%dlx", flags, width);
  } else if (parPrecision) {
    if (uppercase) sprintf(f, "%%%s.%dlX", flags, precision);
    else sprintf(f, "%%%s.%dlx", flags, precision);
  } else {
    if (uppercase) sprintf(f, "%%%slX", flags);
    else sprintf(f, "%%%slx", flags);
  };
  sprintf(b, f, val);
  append_buf(buffer, bufferSize, c_ptr, b);
}

void append_string(char * buffer, 
                   size_t bufferSize, 
                   size_t & c_ptr,
                   const char * val,
                   uint16 flags, 
                   bool parWidth, 
                   unsigned width, 
                   bool parPrecision, 
                   unsigned precision) {
  char fl[6];
  char f[20];
  size_t maxlen = 0;

  char * b;

  do_flags(flags, fl);

  if (parWidth && parPrecision) {
    sprintf(f, "%%%s%d.%ds", flags, width, precision);
    maxlen = min(width, precision);
  } else if (parWidth) {
    sprintf(f, "%%%s%ds", flags, width);
    maxlen = width;
  } else if (parPrecision) {
    sprintf(f, "%%%s.%ds", flags, precision);
  } else {
    sprintf(f, "%%%ss", flags);
    maxlen = precision;
  };
  if (maxlen == 0) maxlen = strlen(val);
  b = (char *)malloc(maxlen+1);
  if (b == NULL) return;
  sprintf(b, f, val);
  append_buf(buffer, bufferSize, c_ptr, b);
  free(b);
}

void append_ptr(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr,
                void * val,
                uint16 flags, 
                bool parWidth, 
                unsigned width, 
                bool parPrecision, 
                unsigned precision) {
// For the moment, only display the least significant 32 bits of 
// a pointer, regardless of its actual size.
  uint32 v = (uint32)val;
  append_hex(buffer, 
             bufferSize, 
             c_ptr,
             v,
             true,
             flags, 
             parWidth, 
             width, 
             parPrecision, 
             precision);
}

void append_char(char * buffer, 
                 size_t bufferSize, 
                 size_t & c_ptr,
                 unsigned char val,
                 uint16 flags, 
                 bool parWidth, 
                 unsigned width, 
                 bool parPrecision, 
                 unsigned precision) {
  
  if (c_ptr<bufferSize) buffer[c_ptr++] = val;
  if (c_ptr<bufferSize) buffer[c_ptr] = 0;
  buffer[bufferSize-1] = 0;
}

void append_par(char * buffer, 
                size_t bufferSize, 
                size_t & c_ptr,
                const argtype_t & arg,
                uint16 flags, 
                bool parWidth, 
                unsigned width, 
                bool parPrecision, 
                unsigned precision) {
  sint32 int_val;
  uint32 uint_val;

  switch (arg.type) {
  case TYPE_INT :
    int_val = arg.val.v_int;
    append_int(buffer, 
               bufferSize, 
               c_ptr,
               int_val,
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_INT_L :
    int_val = arg.val.v_int_l;
    append_int(buffer, 
               bufferSize, 
               c_ptr,
               int_val,
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_INT_S :
    int_val = arg.val.v_int_s;
    append_int(buffer, 
               bufferSize, 
               c_ptr,
               int_val,
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_UINT :
    int_val = arg.val.v_uint;
    append_uint(buffer, 
                bufferSize, 
                c_ptr,
                uint_val,
                flags, 
                parWidth, 
                width, 
                parPrecision, 
                precision);
    break;
  case TYPE_UINT_L :
    uint_val = arg.val.v_uint_l;
    append_uint(buffer, 
                bufferSize, 
                c_ptr,
                uint_val,
                flags, 
                parWidth, 
                width, 
                parPrecision, 
                precision);
    break;
  case TYPE_UINT_S :
    uint_val = arg.val.v_uint_s;
    append_uint(buffer, 
                bufferSize, 
                c_ptr,
                uint_val,
                flags, 
                parWidth, 
                width, 
                parPrecision, 
                precision);
    break;
  case TYPE_HEX :
  case TYPE_UHEX :
    uint_val = arg.val.v_uint;
    append_hex(buffer, 
               bufferSize, 
               c_ptr,
               uint_val,
               arg.type == TYPE_UHEX, 
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_HEX_S :
  case TYPE_UHEX_S :
    uint_val = arg.val.v_uint_s;
    append_hex(buffer, 
               bufferSize, 
               c_ptr,
               uint_val,
               arg.type == TYPE_UHEX_S, 
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_HEX_L :
  case TYPE_UHEX_L :
    uint_val = arg.val.v_uint_l;
    append_hex(buffer, 
               bufferSize, 
               c_ptr,
               uint_val,
               arg.type == TYPE_UHEX_L, 
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_CHAR :
    append_char(buffer, 
               bufferSize, 
               c_ptr,
               arg.val.v_char,
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  case TYPE_STRING :
    append_string(buffer, 
                  bufferSize, 
                  c_ptr,
                  arg.val.v_string,
                  flags, 
                  parWidth, 
                  width, 
                  parPrecision, 
                  precision);
    break;
  case TYPE_PTR :
    append_ptr(buffer, 
               bufferSize, 
               c_ptr,
               arg.val.v_ptr,
               flags, 
               parWidth, 
               width, 
               parPrecision, 
               precision);
    break;
  default:
    APPEND_CHAR(buffer, bufferSize, c_ptr, '?');
  };
}

size_t PKI_OSSRV_vprintMsg(char * buffer,
                           size_t bufferSize,
                           pki_msgcat_handle_t catalog,
                           uint32 status,
                           const char * defaultMsg,
                           va_list ap)
{
  size_t written = 0;

  unsigned parameterMax = 0;
  unsigned parameterCount = 0;
  unsigned i;
  size_t c_ptr = 0;
  const char * format;
  argtype_t arg[MAX_ARGS];
  bool positional_found = false;
  bool ordered_found = false;
  
  unsigned parNum;
  unsigned parType;
  uint16 flags;
  bool parWidth;
  unsigned width;
  bool parPrecision;
  unsigned precision;

// Since we are likely to get positional parameters (%n$...), we have 
// to scan the entire format string first and make a note of the type
// of each parameter, then go and pull them out in order.

  format = pkiCatgets(catalog, status, defaultMsg);
	
  for (i=0;format[i]!=0;i++) {
    parNum = 0;
    if (format[i]=='%') {
      if (!parseFormat(format, i, 
                       parNum, 
                       flags, 
                       parWidth, 
                       width, 
                       parPrecision, 
                       precision, 
                       parType)) {
        if (format[i]!='%') {
          strncpy(buffer, "Format Error - bad specifier: ", bufferSize);
          strncat(buffer, format, bufferSize-strlen(buffer));
          return strlen(buffer);
        };
      } else {
        if (parNum == 0) {
// The format didn't use positional notation, so just use the next parameter
          parNum = parameterCount + 1;
          ordered_found = true;
        } else {
          positional_found = true;
        };
// Make a note of the parameter type.
        if ((arg[parNum].type != TYPE_NONE) &&
            (arg[parNum].type != parType)) {
          strncpy(buffer, "Format Error - type mismatch: ", bufferSize);
          strncat(buffer, format, bufferSize-strlen(buffer));
          return strlen(buffer);
        };
        arg[parNum].type = parType;
        parameterCount++;
      };
    };
  };

// Now we've scanned the entire argument-list.  Make sure we didn't get both
// positional and ordered parameters, and that we know the type of every
// parameter we did get...

  if (ordered_found && positional_found) {
    strncpy(buffer, "Format error - mixed specifier types: ", bufferSize);
    strncat(buffer, format, bufferSize-strlen(buffer));
    return strlen(buffer);
  };

// If the format used ordered parameters, we can just pass the whole 
// thing on to the C RTL to handle.  The C RTL doesn't limit the text
// written to the buffer, so we may want to revisit this later...

  if (!positional_found) {
    return vsprintf(buffer, format, ap);
  };

// The format used positional parameters.  Handle them ourselves...

  for (i=1;i<=parameterCount;i++) {
    if (arg[i].type = TYPE_NONE) {
      strncpy(buffer, "Format error - missing parameter: ", bufferSize);
      strncat(buffer, format, bufferSize-strlen(buffer));
      return strlen(buffer);
    };
    switch (arg[i].type) {
    case TYPE_INT :
      arg[i].val.v_int = va_arg(ap, int);
      break;
    case TYPE_INT_L :
      arg[i].val.v_int_l = va_arg(ap, sint32);
      break;
    case TYPE_INT_S :
      arg[i].val.v_int_s = va_arg(ap, sint16);
      break;
    case TYPE_UINT :
    case TYPE_HEX :
    case TYPE_UHEX :
      arg[i].val.v_uint = va_arg(ap, unsigned);
      break;
    case TYPE_UINT_L :
    case TYPE_HEX_L :
    case TYPE_UHEX_L :
      arg[i].val.v_uint_l = va_arg(ap, uint32);
      break;
    case TYPE_UINT_S :
    case TYPE_HEX_S :
    case TYPE_UHEX_S :
      arg[i].val.v_uint_s = va_arg(ap, uint16);
      break;
    case TYPE_CHAR :
      arg[i].val.v_char = va_arg(ap, int);
      break;
    case TYPE_STRING :
      arg[i].val.v_string = va_arg(ap, char *);
      break;
    case TYPE_PTR :
      arg[i].val.v_ptr = va_arg(ap, void *);
      break;
    default:
      strncpy(buffer, "Format error - unknown parameter type: ", bufferSize);
      strncat(buffer, format, bufferSize-strlen(buffer));
      return strlen(buffer);
    };
  };

// Now we've pulled out the arguments.  Re-scan the format string, 
// this time generating output...

  parameterCount = 0;
  buffer[0] = 0;
  c_ptr = 0;

  for (i=0;format[i]!=0;i++) {
    parNum = 0;
    if (format[i]=='%') {
      if (!parseFormat(format, i, 
                       parNum, 
                       flags, 
                       parWidth, 
                       width, 
                       parPrecision, 
                       precision, 
                       parType)) {
        APPEND_CHAR(buffer, bufferSize, c_ptr, format[i]);
        continue;
      };
      if (parNum == 0) {
// The format didn't use positional notation, so just use the next parameter
        parNum = parameterCount + 1;
      };
      append_par(buffer, bufferSize, c_ptr, arg[parNum], flags, parWidth, width, parPrecision, precision);
    } else APPEND_CHAR(buffer, bufferSize, c_ptr, format[i]);
  };
  return c_ptr;
}


#else
#endif

#ifndef MAX_RES_MSG_SIZE
#ifdef MAX_MSG_SIZE
#define MAX_RES_MSG_SIZE MAX_MSG_SIZE*2
#else
#define MAX_RES_MSG_SIZE 2048
#endif
#endif

void PKI_OSSRV_fprintMsg (FILE * file, 
                          pki_msgcat_handle_t catalog, 
                          uint32 status, 
                          const char * defaultMsg,
                          ...) {
  char buffer[MAX_MSG_SIZE];
  va_list args;

  va_start(args, defaultMsg);
  PKI_OSSRV_vprintMsg(buffer, 
                      sizeof(buffer),
                      catalog,
                      status,
                      defaultMsg,
                      args);
  va_end(args);
  fputs(buffer, file);
}

size_t PKI_OSSRV_sprintMsg (char * buffer,
                            size_t bufferSize,
                            pki_msgcat_handle_t catalog,
                            uint32 status,
                            const char * defaultMsg,
                            ...) {
  size_t charsWritten;
  va_list args;

  va_start(args, defaultMsg);

  charsWritten = PKI_OSSRV_vprintMsg(buffer, 
                                     bufferSize,
                                     catalog,
                                     status,
                                     defaultMsg,
                                     args);
  va_end(args);
  return charsWritten;
}


size_t PKI_OSSRV_getMsg(char * buffer,
                        size_t bufferSize,
                        pki_msgcat_handle_t catalog,
                        uint32 status,
                        const char * defaultMsg) {
  const char * s;
  s = pkiCatgets(catalog,
                 status,
                 defaultMsg);
  strncpy(buffer, s, bufferSize);
  buffer[bufferSize-1] = 0;
  return strlen(s);
}

