/* $Id: rt_prton.c,v 1.15 2009/01/25 17:43:14 kiesling Exp $ */

/*
  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 <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include "object.h"
#include "message.h"
#include "ctalk.h"
#include "rtinfo.h"
#include "typeof.h"

extern char ptr_args[512][8192];    /* Declared in rt_stdarg.c. */
extern int ptr_arg_idx;
char fmt_tokens[512][8192];
int fmt_tok_idx;

static char tmparg0buf[0xFFFF];

static int __check_printon_args (METHOD *method) {
  int n, n_th_conv_arg;
  for (n = 0, n_th_conv_arg = 0; n < fmt_tok_idx; n++) {
    if (strchr (fmt_tokens[n], '%'))
      ++n_th_conv_arg;
  }
  if (n_th_conv_arg != ptr_arg_idx)
    return ERROR;
  return SUCCESS;
}

int __call_printon_fn_w_args (OBJECT *arg0_obj, char *fmt, METHOD *method, 
			      STDARG_CALL_INFO *stdarg_call_info) {

  int i, j, k, retval;
  int fmtsize;
  int bufsize = MAXMSG;
  char *arg0_buf;

  retval = 0;

  arg0_buf = __xalloc (bufsize + 1);
  fmtsize = strlen (fmt);

  if (stdarg_call_info -> libcfn == (int (*)())sprintf) {
    *arg0_buf = '\0';
    for (i = 0, j = 0, k = 1; (i <= fmt_tok_idx) && *fmt_tokens[i]; i++) {
    *tmparg0buf = '\0';
      if (strchr (fmt_tokens[i], '%')) {
	retval += strcat (tmparg0buf, 
			   __scalar_fmt_conv (fmt_tokens[i], 
					      ptr_args[j++],
					      method -> args[k++]));
      } else {
	retval += strcat (tmparg0buf, fmt_tokens[i]);
      }
      fmtsize += strlen (tmparg0buf);
      if (fmtsize >= bufsize) {
	while (fmtsize > bufsize)
	  bufsize *= 2;
	arg0_buf = realloc (arg0_buf, bufsize);
      }
      strcat (arg0_buf, tmparg0buf);
    }
    __ctalkSetObjectValueVar(arg0_obj, arg0_buf);
    free (arg0_buf);
    return retval;
  }

  if (stdarg_call_info -> libcfn == (int (*)())fprintf) {
    OBJECT *arg0_val_obj;
    FILE *f_arg0;
    if ((arg0_val_obj = 
	 __ctalkGetInstanceVariable (arg0_obj, "value", FALSE)) != NULL) {
      sscanf (arg0_val_obj -> __o_value, "0x%p", &f_arg0);
    } else {
      sscanf (arg0_obj -> __o_value, "0x%p", &f_arg0);
    }
    for (i = 0, j = 0, k = 1; (i <= fmt_tok_idx) && *fmt_tokens[i]; i++) {
      if (strchr (fmt_tokens[i], '%')) {
 	retval += fputs (__scalar_fmt_conv 
			   (fmt_tokens[i], 
			    ptr_args[j++],
			    method -> args[k++]),
			 f_arg0);
       } else {
	 retval += fputs (fmt_tokens[i], f_arg0);
       }
     }
     return retval;
   }
  return ERROR;
}

int __ctalkSelfPrintOn (void) {

  char fmtbuf[MAXMSG];
  OBJECT *fmt_value_obj;
  int retval = 0; /* Avoid a warning. */
  STDARG_CALL_INFO stdarg_call_info;
  OBJECT *arg0_obj = NULL;        /* Avoid a warning. */
  int k;
  OBJECT *read_value_obj;
  METHOD *method;
  
  method = __ctalkRtGetMethod ();

#if ((defined(__linux__) && defined(__i386__)) || \
      defined (__CYGWIN__) || defined (__DJGPP__) || \
     (defined(__sparc__) && defined(__svr4__)))

  if (!strcmp (method -> rcvr_class_obj -> __o_name, STRING_CLASSNAME) ||
      __ctalkIsSubClassOf (method -> rcvr_class_obj -> __o_name,
			   STRING_CLASSNAME)) {
    stdarg_call_info.libcfn = (int (*)())sprintf;
  } else {
    if (!strcmp (method -> rcvr_class_obj -> __o_name, 
		 "WriteFileStream") ||
	__ctalkIsSubClassOf (method -> rcvr_class_obj -> __o_name,
			     "WriteFileStream")) {
      stdarg_call_info.libcfn = (int (*)())fprintf;
    } else {
      _warning 
	("__ctalkSelfPrintOn: Unimplemented receiver class, \"%s.\"\n",
	 method -> rcvr_class_obj -> __o_name);
      return ERROR;
    }
  }

  arg0_obj = __ctalk_receiver_pop ();
  __ctalk_receiver_push (arg0_obj);
  stdarg_call_info.fmt_arg_n = 0;

  if ((fmt_value_obj = 
       __ctalkGetInstanceVariable (method->args[stdarg_call_info.fmt_arg_n], 
				   "value", FALSE))
      == NULL)
    fmt_value_obj = method -> args[stdarg_call_info.fmt_arg_n];

  if (strcmp (fmt_value_obj -> __o_classname, STRING_CLASSNAME)) {
    _warning ("__ctalkLibcFnWithMethodArgs: Argument type mismatch.\n");
    return ERROR;
  }

  if (*fmt_value_obj -> __o_value == '"')
    strcpy (fmtbuf, TRIM_LITERAL(fmt_value_obj -> __o_value));
  else
    strcpy (fmtbuf, fmt_value_obj -> __o_value);

  tokenize_fmt (fmtbuf);

  for (k = stdarg_call_info.fmt_arg_n + 1, ptr_arg_idx = 0; 
       method->args[k]; k++) {
    if ((read_value_obj = 
	 __ctalkGetInstanceVariable (method->args[k], 
				     "value", FALSE)) == NULL)
      read_value_obj = method -> args[k];
    strcpy (ptr_args[ptr_arg_idx++], read_value_obj -> __o_value);
  }

  if (__check_printon_args (method)) {
    _warning ("__ctalkSelfPrintOn: Wrong number of arguments.\n");
    return ERROR;
  }

  retval = __call_printon_fn_w_args (arg0_obj, fmtbuf, method, &stdarg_call_info);

  /*
   *  All functions in the C library that use stdargs return int.
   */

  return retval;

#else /* #if defined(__linux__) && defined(__i386__) ...*/

  _warning ("Functions that use stdargs are not implemented for this system.\n");
  return NULL;
#endif
}

int __ctalkObjectPrintOn (OBJECT *prt_obj) {

  char fmtbuf[MAXMSG];
  OBJECT *fmt_value_obj;
  int retval = 0; /* Avoid a warning. */
  STDARG_CALL_INFO stdarg_call_info;
  OBJECT *prt_value_obj;
  int k;
  OBJECT *read_value_obj;
  METHOD *method;
  
  method = __ctalkRtGetMethod ();

#if ((defined(__linux__) && defined(__i386__)) || \
      defined (__CYGWIN__) || defined (__DJGPP__) || \
     (defined(__sparc__) && defined(__svr4__)))

  if ((prt_value_obj = __ctalkGetInstanceVariable (prt_obj, "value", FALSE))
      == NULL) {
    prt_value_obj = prt_obj;
  }

  if (!strcmp (prt_value_obj -> __o_classname, STRING_CLASSNAME) ||
      __ctalkIsSubClassOf (prt_value_obj -> __o_classname, 
			   STRING_CLASSNAME)) {
    stdarg_call_info.libcfn = (int (*)())sprintf;
  } else {
    if (!strcmp (prt_value_obj -> __o_classname, "WriteFileStream") ||
	__ctalkIsSubClassOf (prt_value_obj -> __o_classname, 
			     "WriteFileStream")) {
      stdarg_call_info.libcfn = (int (*)())fprintf;
    } else {
      _warning 
	("__ctalkObjectPrintOn: Unimplemented stream class, \"%s.\"\n",
	 prt_value_obj -> __o_classname);
      return ERROR;
    }
  }

  stdarg_call_info.fmt_arg_n = 0;

  if ((fmt_value_obj = 
       __ctalkGetInstanceVariable (method->args[stdarg_call_info.fmt_arg_n], 
				   "value", FALSE))
      == NULL)
    fmt_value_obj = method -> args[stdarg_call_info.fmt_arg_n];

  if (strcmp (fmt_value_obj -> __o_classname, STRING_CLASSNAME)) {
    _warning ("__ctalkLibcFnWithMethodArgs: Argument type mismatch.\n");
    return ERROR;
  }

  if (*fmt_value_obj -> __o_value == '"')
    strcpy (fmtbuf, TRIM_LITERAL(fmt_value_obj -> __o_value));
  else
    strcpy (fmtbuf, fmt_value_obj -> __o_value);

  tokenize_fmt (fmtbuf);

  for (k = stdarg_call_info.fmt_arg_n + 1, ptr_arg_idx = 0; 
       method->args[k]; k++) {
    if ((read_value_obj = 
	 __ctalkGetInstanceVariable (method->args[k], 
				     "value", FALSE)) == NULL)
      read_value_obj = method -> args[k];
    if (!read_value_obj || !IS_OBJECT(read_value_obj)) {
      _warning ("__ctalkObjectPrintOn: Invalid argument object.\n");
      strcpy (ptr_args[ptr_arg_idx++], NULLSTR);
    } else {
      strcpy (ptr_args[ptr_arg_idx++], read_value_obj -> __o_value);
    }
  }

  if (__check_printon_args (method)) {
    _warning ("__ctalkObjectPrintOn: Wrong number of arguments.\n");
    return ERROR;
  }
  retval = __call_printon_fn_w_args (prt_obj, fmtbuf, method, &stdarg_call_info);

  /*
   *  All functions in the C library that use stdargs return int.
   */

  return retval;

#else /* #if defined(__linux__) && defined(__i386__) ...*/

  _warning ("Functions that use stdargs are not implemented for this system.\n");
  return NULL;
#endif
}

