
/********************************************
cast.c

libmawk changes (C) 2009-2010, Tibor 'Igor2' Palinkas;
based on mawk code coming with the below copyright:

copyright 1991, Michael D. Brennan

This is a source file for mawk, an implementation of
the AWK programming language.

Mawk is distributed without warranty under the terms of
the GNU General Public License, version 2, 1991.
********************************************/
#include <stdio.h>
#include <string.h>
#include "mawk.h"
#include "field.h"
#include "memory.h"
#include "scan.h"
#include "repl.h"
#include "num.h"
#include "cell.h"


void mawk_cast1_to_num(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	switch (cp->type) {
	case C_NOINIT:
		cp->d.dval = MAWK_NUM_ZERO;
		break;

	case C_NUM:
		return;

	case C_MBSTRN:
	case C_STRING:
		{
			register mawk_string_t *s = (mawk_string_t *) cp->ptr;

#if FPE_TRAPS_ON								/* look for mawk_overflow error */
			errno = 0;
			cp->d.dval = strtonum(s->str, (char **) 0);
			if (errno && cp->d.dval != MAWK_NUM_ZERO)	/* ignore underflow */
				mawk_rt_error("mawk_overflow converting %s to number", s->str);
#else
			cp->d.dval = strtonum(s->str, (char **) 0);
#endif
			free_STRING(s);
		}
		break;

	case C_STRNUM:
		/* don't need to convert, but do need to free the mawk_string_t part */
		free_STRING(string(cp));
		break;


	default:
		mawk_bozo(MAWK, "mawk_cast on bad type");
	}
	cp->type = C_NUM;
}

void mawk_cast2_to_num(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	register mawk_string_t *s;

	switch (cp->type) {
	case C_NOINIT:
		cp->d.dval = MAWK_NUM_ZERO;
		break;

	case C_NUM:
		goto two;
	case C_STRNUM:
		free_STRING(string(cp));
		break;

	case C_MBSTRN:
	case C_STRING:
		s = (mawk_string_t *) cp->ptr;

#if FPE_TRAPS_ON								/* look for mawk_overflow error */
		errno = 0;
		cp->d.dval = strtonum(s->str, (char **) 0);
		if (errno && cp->d.dval != MAWK_NUM_ZERO)	/* ignore underflow */
			mawk_rt_error("mawk_overflow converting %s to number", s->str);
#else
		cp->d.dval = strtonum(s->str, (char **) 0);
#endif
		free_STRING(s);
		break;

	default:
		mawk_bozo(MAWK, "mawk_cast on bad type");
	}
	cp->type = C_NUM;

two:cp++;

	switch (cp->type) {
	case C_NOINIT:
		cp->d.dval = MAWK_NUM_ZERO;
		break;

	case C_NUM:
		return;
	case C_STRNUM:
		free_STRING(string(cp));
		break;

	case C_MBSTRN:
	case C_STRING:
		s = (mawk_string_t *) cp->ptr;

#if FPE_TRAPS_ON								/* look for mawk_overflow error */
		errno = 0;
		cp->d.dval = strtonum(s->str, (char **) 0);
		if (errno && cp->d.dval != MAWK_NUM_ZERO)	/* ignore underflow */
			mawk_rt_error("mawk_overflow converting %s to number", s->str);
#else
		cp->d.dval = strtonum(s->str, (char **) 0);
#endif
		free_STRING(s);
		break;

	default:
		mawk_bozo(MAWK, "mawk_cast on bad type");
	}
	cp->type = C_NUM;
}

void mawk_cast1_to_str(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	register Int lval;
	char xbuff[260];

	switch (cp->type) {
	case C_NOINIT:
		MAWK->null_str.ref_cnt++;
		cp->ptr = (PTR) & MAWK->null_str;
		break;

	case C_NUM:

		lval = mawk_d_to_I(cp->d.dval);
		if (lval == cp->d.dval)
			sprintf(xbuff, INT_FMT, lval);
		else
			sprintf(xbuff, string(MAWK_CONVFMT)->str, cp->d.dval);

		cp->ptr = (PTR) mawk_new_STRING(MAWK, xbuff);
		break;

	case C_STRING:
		return;

	case C_MBSTRN:
	case C_STRNUM:
		break;

	default:
		mawk_bozo(MAWK, "bad type on mawk_cast");
	}
	cp->type = C_STRING;
}

void mawk_cast2_to_str(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	register Int lval;
	char xbuff[260];

	switch (cp->type) {
	case C_NOINIT:
		MAWK->null_str.ref_cnt++;
		cp->ptr = (PTR) & (MAWK->null_str);
		break;

	case C_NUM:

		lval = mawk_d_to_I(cp->d.dval);
		if (lval == cp->d.dval)
			sprintf(xbuff, INT_FMT, lval);
		else
			sprintf(xbuff, string(MAWK_CONVFMT)->str, cp->d.dval);

		cp->ptr = (PTR) mawk_new_STRING(MAWK, xbuff);
		break;

	case C_STRING:
		goto two;

	case C_MBSTRN:
	case C_STRNUM:
		break;

	default:
		mawk_bozo(MAWK, "bad type on mawk_cast");
	}
	cp->type = C_STRING;

two:
	cp++;

	switch (cp->type) {
	case C_NOINIT:
		MAWK->null_str.ref_cnt++;
		cp->ptr = (PTR) & MAWK->null_str;
		break;

	case C_NUM:

		lval = mawk_d_to_I(cp->d.dval);
		if (lval == cp->d.dval)
			sprintf(xbuff, INT_FMT, lval);
		else
			sprintf(xbuff, string(MAWK_CONVFMT)->str, cp->d.dval);

		cp->ptr = (PTR) mawk_new_STRING(MAWK, xbuff);
		break;

	case C_STRING:
		return;

	case C_MBSTRN:
	case C_STRNUM:
		break;

	default:
		mawk_bozo(MAWK, "bad type on mawk_cast");
	}
	cp->type = C_STRING;
}

void mawk_cast_to_RE(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	register PTR p;

	if (cp->type < C_STRING)
		mawk_cast1_to_str(MAWK, cp);

	p = mawk_re_compile(MAWK, string(cp));
	free_STRING(string(cp));
	cp->type = C_RE;
	cp->ptr = p;

}

void mawk_cast_for_split(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	static const char meta[] = "^$.*+?|[]()";
	char xbuff[] = "\\X";
	int c;
	unsigned len;

	if (cp->type < C_STRING)
		mawk_cast1_to_str(MAWK, cp);

	if ((len = string(cp)->len) == 1) {
		if ((c = string(cp)->str[0]) == ' ') {
			free_STRING(string(cp));
			cp->type = C_SPACE;
			return;
		}
		else if (strchr(meta, c)) {
			xbuff[1] = c;
			free_STRING(string(cp));
			cp->ptr = (PTR) mawk_new_STRING(MAWK, xbuff);
		}
	}
	else if (len == 0) {
		free_STRING(string(cp));
		cp->type = C_SNULL;
		return;
	}

	mawk_cast_to_RE(MAWK, cp);
}

/* input: cp-> a mawk_cell_t of type C_MBSTRN (maybe strnum)
   mawk_test it -- mawk_casting it to the appropriate type
   which is C_STRING or C_STRNUM
*/

void mawk_check_strnum(mawk_state_t *MAWK, mawk_cell_t *cp)
{
	char *mawk_test;
	register unsigned char *s, *q;

	cp->type = C_STRING;					/* assume not C_STRNUM */
	s = (unsigned char *) string(cp)->str;
	q = s + string(cp)->len;
	while (MAWK->scan_code[*s] == SC_SPACE)
		s++;
	if (s == q)
		return;

	while (MAWK->scan_code[q[-1]] == SC_SPACE)
		q--;
	if (MAWK->scan_code[q[-1]] != SC_DIGIT && q[-1] != '.')
		return;

	switch (MAWK->scan_code[*s]) {
	case SC_DIGIT:
	case SC_PLUS:
	case SC_MINUS:
	case SC_DOT:

#if FPE_TRAPS_ON
		errno = 0;
		cp->d.dval = strtonum((char *) s, &mawk_test);
		/* make mawk_overflow pure string */
		if (errno && cp->d.dval != MAWK_NUM_ZERO)
			return;
#else
		cp->d.dval = strtonum((char *) s, &mawk_test);
#endif

		if ((char *) q <= mawk_test)
			cp->type = C_STRNUM;
		/*  <= instead of == , for some buggy strtod
		   e.g. Apple Unix */
	}
}

/* mawk_cast a mawk_cell_t to a replacement cell */

void mawk_cast_to_REPL(mawk_state_t *MAWK, register mawk_cell_t *cp)
{
	register mawk_string_t *sval;

	if (cp->type < C_STRING)
		mawk_cast1_to_str(MAWK, cp);
	sval = (mawk_string_t *) cp->ptr;

	mawk_cellcpy(MAWK, cp, mawk_repl_compile(MAWK, sval));
	free_STRING(sval);
}

