/* $Id: sformat.c,v 1.18 2009/02/26 09:51:01 kiesling Exp $ -*-c-*-*/

/*
  This file is part of Ctalk.
  Copyright © 2005-2009  Robert Kiesling, ctalk@ctalklang.org.
  Permission is granted to copy this software provided that this copyright
  notice is included in all source code modules.

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Lesser General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software Foundation, 
  Inc., 51 Franklin St., Fifth Floor, Boston, MA 02110-1301 USA.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include "object.h"
#include "typeof.h"
#include "defcls.h"

#ifndef MAXMSG
#define MAXMSG 8192
#endif

extern char *substrcpy (char *, char *, int, int);
extern void _warning (char *, ...);
extern int _lextype (char *);
extern int __ctalkIsSubClassOf (char *, char *);

/*    format_string ()
 *
 *    Formats a string using a printf-like format and an argument 
 *    list.  The calling function must call va_start first.
 *    The function recognizes the following format escapes.
 *
 *    %s - Character string.
 *    %p - Pointer, formatted as a hexadecimal number.
 *    %d - Decimal integer.
 */

char *_format_str (char *buf, char *fmt, va_list ap) {

  char *s;
  int d;
  double db;
  long double ldb = 0.0;         /* Avoid warnings. */
  long int li;
  long long int lli = 0L;
  char tbuf[MAXMSG];

  *buf = 0;

  while (*fmt) {
    if (*fmt == '%') {
      (void) *fmt++;
      switch (*fmt++)
	{
	case 's':
	  s = va_arg (ap, char *);
	  if (s)
	    strcat (buf, s);
	  else 
	    strcat (buf, "(null)");
	  break;
	case 'p':
	  s = va_arg (ap, char *);
	  if (s) {
 	    sprintf (tbuf, "0x%p", (void *)s);
	    strcat (buf, tbuf);
	  } else {
 	    sprintf (tbuf, "0x%p", (void *)NULL);
	    strcat (buf, tbuf);
	  }
	case 'd':
	  d = va_arg (ap, int);
	  sprintf (tbuf, "%d", d);
	  strcat (buf, tbuf);
	  break;
	case 'f':
	  db = va_arg (ap, double);
	  sprintf (tbuf, "%f", db);
	  strcat (buf, tbuf);
	  break;
	case 'L':
	  switch (*fmt++)
	    {
	    case 'd':
	      lli = va_arg (ap, long long int);
	      sprintf (tbuf, "%lld", lli);
	      strcat (buf, tbuf);
	      break;
	    case 'f':
	      db = va_arg (ap, long double);
	      sprintf (buf, "%Lf", ldb);
	      strcat (buf, tbuf);
	      break;
	    }
	  break;
	case 'l':
	  switch (*fmt++)
	    {
	    case 'd':
	      li = va_arg (ap, long int);
	      sprintf (tbuf, "%ld", li);
	      strcat (buf, tbuf);
	      break;
	    case 'f':
	      db = va_arg (ap, double);
	      sprintf (tbuf, "%f", db);
	      strcat (buf, tbuf);
	      break;
	    case 'l':
	      switch (*fmt++)
		{
		case 'd':
		  li = va_arg (ap, long long int);
		  sprintf (tbuf, "%lld", lli);
		  strcat (buf, tbuf);
		  break;
		case 'f':
		  db = va_arg (ap, long double);
		  sprintf (tbuf, "%Lf", ldb);
		  strcat (buf, tbuf);
		  break;
		}
	      break;  /* case 'l': */
	    }
	  break;  /* case 'l': */
	}
    } else {
      sprintf (tbuf, "%c", *fmt++);
      strcat (buf, tbuf);
    }
  }

  return buf;
}

char *__ctalkCPrintFmtToCtalkFmt (char *c_fmt, char *ctalk_fmt) {

  int i, j;

  for (i = 0, j = 0; c_fmt[i];) {
    if (c_fmt[i] == '%') {
      ctalk_fmt[j++] = c_fmt[i++];
      while (!strpbrk(&c_fmt[i++], "diouxXeEfFgGaAcsp"))
	;
      ctalk_fmt[j++] = 's';
    } else {
      ctalk_fmt[j++] = c_fmt[i++];
    }
  }
  ctalk_fmt[j] = '\0';
  return ctalk_fmt;
}

static char __fmtbuf[0xFFFF];
static char __tmpbuf[0xFFFF];

#ifndef TRIM_CHAR_BUF
#define TRIM_CHAR_BUF(s) \
  { \
    while ((s[0] == '\'')&&(s[1] != '\0')) \
       substrcpy (s, s, 1, strlen (s) - 2); \
  } \

#endif
#ifndef TRIM_LITERAL_BUF
#define TRIM_LITERAL_BUF(s) \
  { \
    while ((s[0] == '\"')&&(s[1] != '\0')) \
       substrcpy (s, s, 1, strlen (s) - 2); \
  } \

#endif

#define ARG_VAL_OBJ(__o) ((__o)->instancevars ? \
  (__o)->instancevars : (__o))

char *__scalar_fmt_conv (char *fmt, char *arg, OBJECT *arg_obj) {

  strcpy (__tmpbuf, arg);
  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, STRING_CLASSNAME)) {
    int __idx;
    for (__idx = 1; fmt[__idx]; __idx++) {
      if (strpbrk (&fmt[__idx], "diouaAcXxfeE")) {
	char __arg_val;
	if (*arg == '\'') {
	  TRIM_CHAR_BUF(__tmpbuf);
	} else {
	  if (*arg == '\"') {
	    TRIM_LITERAL_BUF(__tmpbuf);
	  }
	}
	__arg_val = *__tmpbuf;
	sprintf (__fmtbuf, fmt, __arg_val);
	return __fmtbuf;
      } else {
	return __tmpbuf;
      }
    }
  }

  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, CHARACTER_CLASSNAME) ||
      __ctalkIsSubClassOf (ARG_VAL_OBJ(arg_obj) -> __o_classname, LONGINTEGER_CLASSNAME)){
    char __arg_val;
    if (*arg == '\'') {
      TRIM_CHAR_BUF(__tmpbuf);
    } else {
      if (*arg == '\"') {
	TRIM_LITERAL_BUF(__tmpbuf);
      }
    }
    __arg_val = *__tmpbuf;
    sprintf (__fmtbuf, fmt, __arg_val);
    return __fmtbuf;
  }
  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, LONGINTEGER_CLASSNAME) ||
      __ctalkIsSubClassOf (ARG_VAL_OBJ(arg_obj) -> __o_classname, LONGINTEGER_CLASSNAME)) {
    /*
     *  Solaris workaround for what seems  to be a bug in  sprintf, 
     *  below.
     *  TODO - Could use a truncation warning.
     */
#if defined(__sparc__) && defined(__svr4__)
    int __arg_val;
#else
    long long int __arg_val;
#endif
    if (*arg == '\'') {
      TRIM_CHAR_BUF(__tmpbuf);
    } else {
      if (*arg == '\"') {
	TRIM_LITERAL_BUF(__tmpbuf);
      }
    }
#ifndef DJGPP
    __arg_val = atoll (__tmpbuf);
#else
# if defined(__sparc__) && defined(__svr4__)
    sscanf (__tmpbuf, "%d", &__arg_val);
# else
    sscanf (__tmpbuf, "%lld", &__arg_val);
# endif
#endif
    sprintf (__fmtbuf, fmt, __arg_val);
    return __fmtbuf;
  }
  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, INTEGER_CLASSNAME) ||
      __ctalkIsSubClassOf (ARG_VAL_OBJ(arg_obj) -> __o_classname, INTEGER_CLASSNAME)) {
    int __arg_val;
    if (*arg == '\'') {
      TRIM_CHAR_BUF(__tmpbuf);
    } else {
      if (*arg == '\"') {
	TRIM_LITERAL_BUF(__tmpbuf);
      }
    }
    __arg_val = atoi (__tmpbuf);
    sprintf (__fmtbuf, fmt, __arg_val);
    return __fmtbuf;
  }
  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, FLOAT_CLASSNAME) ||
      __ctalkIsSubClassOf (ARG_VAL_OBJ(arg_obj) -> __o_classname, FLOAT_CLASSNAME)) {
    double __arg_val;
    if (*arg == '\'') {
      TRIM_CHAR_BUF(__tmpbuf);
    } else {
      if (*arg == '\"') {
	TRIM_LITERAL_BUF(__tmpbuf);
      }
    }
    sscanf (__tmpbuf, "%lf", &__arg_val);
    sprintf (__fmtbuf, fmt, __arg_val);
    return __fmtbuf;
  }
  if (!strcmp (ARG_VAL_OBJ(arg_obj) -> __o_classname, SYMBOL_CLASSNAME) ||
      __ctalkIsSubClassOf (ARG_VAL_OBJ(arg_obj) -> __o_classname, SYMBOL_CLASSNAME)){
    void *__arg_val;
    if (*arg == '\'') {
      TRIM_CHAR_BUF(__tmpbuf);
    } else {
      if (*arg == '\"') {
	TRIM_LITERAL_BUF(__tmpbuf);
      }
    }
    sscanf (__tmpbuf, "0x%p", &__arg_val);
    sprintf (__fmtbuf, fmt, __arg_val);
    return __fmtbuf;
  }

  return __tmpbuf;
}

void *__ctalkStrToPtr (char *s) {
  static void *__p;
  sscanf (s, "0x%p", &__p);
  return __p;
}
