/* "p2c", a Pascal to C translator.
   Copyright (C) 1989 David Gillespie.
   Author's address: daveg@csvax.caltech.edu; 256-80 Caltech/Pasadena CA 91125.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation (any version).

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#define PROTO_FUNCS1_C
#include "trans.h"

Static Strlist *enumnames;
Static int enumnamecount;

void setup_funcs()
{
    enumnames = NULL;
    enumnamecount = 0;
}





int isvar(ex, mp)
Expr *ex;
Meaning *mp;
{
    return (ex->kind == EK_VAR && (Meaning *)ex->val.i == mp);
}




char *getstring(ex)
Expr *ex;
{
    ex = makeexpr_stringify(ex);
    if (ex->kind != EK_CONST || ex->val.type->kind != TK_STRING) {
        intwarning("getstring", "Not a string literal [206]");
	return "";
    }
    return ex->val.s;
}




Expr *p_parexpr(target)
Type *target;
{
    Expr *ex;

    if (wneedtok(TOK_LPAR)) {
	ex = p_expr(target);
	if (!wneedtok(TOK_RPAR))
	    skippasttotoken(TOK_RPAR, TOK_SEMI);
    } else
	ex = p_expr(target);
    return ex;
}



Type *argbasetype(ex)
Expr *ex;
{
    if (ex->kind == EK_CAST)
        ex = ex->args[0];
    if (ex->val.type->kind == TK_POINTER)
        return ex->val.type->basetype;
    else
        return ex->val.type;
}



Type *choosetype(t1, t2)
Type *t1, *t2;
{
    if (t1 == tp_void ||
        (type_sizeof(t2, 1) && !type_sizeof(t1, 1)))
        return t2;
    else
        return t1;
}



Expr *convert_offset(type, ex2)
Type *type;
Expr *ex2;
{
    long size;
    int i;
    Value val;
    Expr *ex3;

    if (type->kind == TK_POINTER ||
        type->kind == TK_ARRAY ||
        type->kind == TK_SET ||
        type->kind == TK_STRING)
        type = type->basetype;
    size = type_sizeof(type, 1);
    if (size == 1)
        return ex2;
    val = eval_expr_pasc(ex2);
    if (val.type) {
        if (val.i == 0)
            return ex2;
        if (size && val.i % size == 0) {
            freeexpr(ex2);
            return makeexpr_long(val.i / size);
        }
    } else {     /* look for terms like "n*sizeof(foo)" */
	while (ex2->kind == EK_CAST || ex2->kind == EK_ACTCAST)
	    ex2 = ex2->args[0];
        if (ex2->kind == EK_TIMES) {
	    for (i = 0; i < ex2->nargs; i++) {
		ex3 = convert_offset(type, ex2->args[i]);
		if (ex3) {
		    ex2->args[i] = ex3;
		    return resimplify(ex2);
		}
	    }
            for (i = 0;
                 i < ex2->nargs && ex2->args[i]->kind != EK_SIZEOF;
                 i++) ;
            if (i < ex2->nargs) {
                if (ex2->args[i]->args[0]->val.type == type) {
                    delfreearg(&ex2, i);
                    if (ex2->nargs == 1)
                        return ex2->args[0];
                    else
                        return ex2;
                }
            }
        } else if (ex2->kind == EK_PLUS) {
	    ex3 = copyexpr(ex2);
	    for (i = 0; i < ex2->nargs; i++) {
		ex3->args[i] = convert_offset(type, ex3->args[i]);
		if (!ex3->args[i]) {
		    freeexpr(ex3);
		    return NULL;
		}
	    }
	    freeexpr(ex2);
	    return resimplify(ex3);
        } else if (ex2->kind == EK_SIZEOF) {
            if (ex2->args[0]->val.type == type) {
                freeexpr(ex2);
                return makeexpr_long(1);
            }
        } else if (ex2->kind == EK_NEG) {
	    ex3 = convert_offset(type, ex2->args[0]);
	    if (ex3)
                return makeexpr_neg(ex3);
        }
    }
    return NULL;
}



Expr *convert_size(type, ex, name)
Type *type;
Expr *ex;
char *name;
{
    long size;
    Expr *ex2;
    int i, okay;
    Value val;

    if (debug>2) { fprintf(outf,"convert_size("); dumpexpr(ex); fprintf(outf,")\n"); }
    while (type->kind == TK_ARRAY || type->kind == TK_STRING)
        type = type->basetype;
    if (type == tp_void)
        return ex;
    size = type_sizeof(type, 1);
    if (size == 1)
        return ex;
    while (ex->kind == EK_CAST || ex->kind == EK_ACTCAST)
	ex = ex->args[0];
    switch (ex->kind) {

        case EK_TIMES:
            for (i = 0; i < ex->nargs; i++) {
                ex2 = convert_size(type, ex->args[i], NULL);
                if (ex2) {
                    ex->args[i] = ex2;
                    return resimplify(ex);
                }
            }
            break;

        case EK_PLUS:
            okay = 1;
            for (i = 0; i < ex->nargs; i++) {
                ex2 = convert_size(type, ex->args[i], NULL);
                if (ex2)
                    ex->args[i] = ex2;
                else
                    okay = 0;
            }
            ex = distribute_plus(ex);
            if ((ex->kind != EK_TIMES || !okay) && name)
                note(format_s("Suspicious mixture of sizes in %s [173]", name));
            return ex;

        case EK_SIZEOF:
            return ex;

	default:
	    break;
    }
    val = eval_expr_pasc(ex);
    if (val.type) {
        if (val.i == 0)
            return ex;
        if (size && val.i % size == 0) {
            freeexpr(ex);
            return makeexpr_times(makeexpr_long(val.i / size),
                                  makeexpr_sizeof(makeexpr_type(type), 0));
        }
    }
    if (name) {
        note(format_s("Can't interpret size in %s [174]", name));
        return ex;
    } else
        return NULL;
}

Expr *func_abs()
{
    Expr *ex;
    Meaning *tvar;
    int lness;

    ex = p_parexpr(tp_integer);
    if (ex->val.type->kind == TK_REAL)
        return makeexpr_bicall_1("fabs", tp_longreal, ex);
    else {
        lness = exprlongness(ex);
        if (lness < 0)
            return makeexpr_bicall_1("abs", tp_int, ex);
        else if (lness > 0 && *absname) {
            if (ansiC > 0) {
                return makeexpr_bicall_1("labs", tp_integer, ex);
            } else if (*absname == '*' && (exprspeed(ex) >= 5 || !nosideeffects(ex, 0))) {
                tvar = makestmttempvar(tp_integer, name_TEMP);
                return makeexpr_comma(makeexpr_assign(makeexpr_var(tvar),
                                                      ex),
                                      makeexpr_bicall_1(absname, tp_integer,
                                                        makeexpr_var(tvar)));
            } else {
                return makeexpr_bicall_1(absname, tp_integer, ex);
            }
        } else if (exprspeed(ex) < 5 && nosideeffects(ex, 0)) {
            return makeexpr_cond(makeexpr_rel(EK_LT, copyexpr(ex),
                                                     makeexpr_long(0)),
                                 makeexpr_neg(copyexpr(ex)),
                                 ex);
        } else {
            tvar = makestmttempvar(tp_integer, name_TEMP);
            return makeexpr_cond(makeexpr_rel(EK_LT, makeexpr_assign(makeexpr_var(tvar),
                                                                     ex),
                                                     makeexpr_long(0)),
                                 makeexpr_neg(makeexpr_var(tvar)),
                                 makeexpr_var(tvar));
        }
    }
}

Expr *func_addr()
{
    Expr *ex, *ex2, *ex3;
    Type *type, *tp2;
    int haspar;

    haspar = wneedtok(TOK_LPAR);
    ex = p_expr(tp_proc);
    if (curtok == TOK_COMMA) {
        gettok();
        ex2 = p_expr(tp_integer);
        ex3 = convert_offset(ex->val.type, ex2);
        if (checkconst(ex3, 0)) {
            ex = makeexpr_addrf(ex);
        } else {
            ex = makeexpr_addrf(ex);
            if (ex3) {
                ex = makeexpr_plus(ex, ex3);
            } else {
                note("Don't know how to reduce offset for ADDR [175]");
                type = makepointertype(tp_abyte);
		tp2 = ex->val.type;
                ex = makeexpr_cast(makeexpr_plus(makeexpr_cast(ex, type), ex2), tp2);
            }
        }
    } else {
	if ((ex->val.type->kind != TK_PROCPTR &&
	     ex->val.type->kind != TK_CPROCPTR) ||
	    (ex->kind == EK_VAR &&
	     ex->val.type == ((Meaning *)ex->val.i)->type))
	    ex = makeexpr_addrf(ex);
    }
    if (haspar) {
	if (!wneedtok(TOK_RPAR))
	    skippasttotoken(TOK_RPAR, TOK_SEMI);
    }
    return ex;
}

Expr *func_iaddress()
{
    return makeexpr_cast(func_addr(), tp_integer);
}

Expr *func_addtopointer()
{
    Expr *ex, *ex2, *ex3;
    Type *type, *tp2;

    if (!skipopenparen())
	return NULL;
    ex = p_expr(tp_anyptr);
    if (skipcomma()) {
	ex2 = p_expr(tp_integer);
    } else
	ex2 = makeexpr_long(0);
    skipcloseparen();
    ex3 = convert_offset(ex->val.type, ex2);
    if (!checkconst(ex3, 0)) {
	if (ex3) {
	    ex = makeexpr_plus(ex, ex3);
	} else {
	    note("Don't know how to reduce offset for ADDTOPOINTER [175]");
	    type = makepointertype(tp_abyte);
	    tp2 = ex->val.type;
	    ex = makeexpr_cast(makeexpr_plus(makeexpr_cast(ex, type), ex2), tp2);
	}
    }
    return ex;
}

Stmt *proc_assert()
{
    Expr *ex;

    ex = p_parexpr(tp_boolean);
    return makestmt_call(makeexpr_bicall_1("assert", tp_void, ex));
}

Stmt *wrapopencheck(sp, fex)
Stmt *sp;
Expr *fex;
{
    Stmt *sp2;

    if (FCheck(checkfileisopen) && !is_std_file(fex)) {
        sp2 = makestmt(SK_IF);
        sp2->exp1 = makeexpr_rel(EK_NE, fex, makeexpr_nil());
        sp2->stm1 = sp;
        if (iocheck_flag) {
            sp2->stm2 = makestmt_call(makeexpr_bicall_1(name_ESCIO, tp_integer,
							makeexpr_name(filenotopenname, tp_int)));
        } else {
            sp2->stm2 = makestmt_assign(makeexpr_var(mp_ioresult),
					makeexpr_name(filenotopenname, tp_int));
        }
        return sp2;
    } else {
        freeexpr(fex);
        return sp;
    }
}



Static Expr *checkfilename(nex)
Expr *nex;
{
    Expr *ex;

    nex = makeexpr_stringcast(nex);
    if (nex->kind == EK_CONST && nex->val.type->kind == TK_STRING) {
        switch (which_lang) {

            case LANG_HP:
                if (!strncmp(nex->val.s, "#1:", 3) ||
                    !strncmp(nex->val.s, "console:", 8) ||
                    !strncmp(nex->val.s, "CONSOLE:", 8)) {
                    freeexpr(nex);
                    nex = makeexpr_string("/dev/tty");
                } else if (!strncmp(nex->val.s, "#2:", 3) ||
                           !strncmp(nex->val.s, "systerm:", 8) ||
                           !strncmp(nex->val.s, "SYSTERM:", 8)) {
                    freeexpr(nex);
                    nex = makeexpr_string("/dev/tty");     /* should do more? */
                } else if (!strncmp(nex->val.s, "#6:", 3) ||
                           !strncmp(nex->val.s, "printer:", 8) ||
                           !strncmp(nex->val.s, "PRINTER:", 8)) {
                    note("Opening a file named PRINTER: [176]");
                } else if (my_strchr(nex->val.s, ':')) {
                    note("Opening a file whose name contains a ':' [177]");
                }
                break;

            case LANG_TURBO:
                if (checkstring(nex, "con") ||
                    checkstring(nex, "CON") ||
                    checkstring(nex, "")) {
                    freeexpr(nex);
                    nex = makeexpr_string("/dev/tty");
                } else if (checkstring(nex, "nul") ||
                           checkstring(nex, "NUL")) {
                    freeexpr(nex);
                    nex = makeexpr_string("/dev/null");
                } else if (checkstring(nex, "lpt1") ||
                           checkstring(nex, "LPT1") ||
                           checkstring(nex, "lpt2") ||
                           checkstring(nex, "LPT2") ||
                           checkstring(nex, "lpt3") ||
                           checkstring(nex, "LPT3") ||
                           checkstring(nex, "com1") ||
                           checkstring(nex, "COM1") ||
                           checkstring(nex, "com2") ||
                           checkstring(nex, "COM2")) {
                    note("Opening a DOS device file name [178]");
                }
                break;

	    default:
		break;
        }
    } else {
	if (*filenamefilter && strcmp(filenamefilter, "0")) {
	    ex = makeexpr_sizeof(copyexpr(nex), 0);
	    nex = makeexpr_bicall_2(filenamefilter, tp_str255, nex, ex);
	} else
	    nex = makeexpr_stringify(nex);
    }
    return nex;
}



Static Stmt *assignfilename(fex, nex)
Expr *fex, *nex;
{
    Meaning *mp;

    mp = isfilevar(fex);
    if (mp && mp->namedfile) {
        freeexpr(fex);
        return makestmt_call(makeexpr_assign(makeexpr_name(format_s(name_FNVAR, mp->name),
                                                           tp_str255),
                                             nex));
    } else {
        if (mp)
            warning("Don't know how to ASSIGN to a non-explicit file variable [207]");
        else
            note("Encountered an ASSIGN statement [179]");
        return makestmt_call(makeexpr_bicall_2("assign", tp_void, fex, nex));
    }
}



Stmt *proc_assign()
{
    Expr *fex, *nex;

    if (!skipopenparen())
	return NULL;
    fex = p_expr(tp_text);
    if (!skipcomma())
	return NULL;
    nex = checkfilename(p_expr(tp_str255));
    skipcloseparen();
    return assignfilename(fex, nex);
}

Stmt *handleopen(code)
int code;
{
    Stmt *sp, *spassign;
    Expr *fex, *nex, *ex;
    Meaning *fmp;
    int storefilename, needcheckopen = 1;
    char modebuf[5], *cp;

    if (!skipopenparen())
	return NULL;
    fex = p_expr(tp_text);
    fmp = isfilevar(fex);
    storefilename = (fmp && fmp->namedfile);
    spassign = NULL;
    if (curtok == TOK_COMMA) {
        gettok();
        ex = p_expr(tp_str255);
    } else
        ex = NULL;
    if (ex && (ex->val.type->kind == TK_STRING ||
	       ex->val.type->kind == TK_ARRAY)) {
        nex = checkfilename(ex);
        if (storefilename) {
            spassign = assignfilename(copyexpr(fex), nex);
            nex = makeexpr_name(format_s(name_FNVAR, fmp->name), tp_str255);
        }
        if (curtok == TOK_COMMA) {
            gettok();
            ex = p_expr(tp_str255);
        } else
            ex = NULL;
    } else if (storefilename) {
        nex = makeexpr_name(format_s(name_FNVAR, fmp->name), tp_str255);
    } else {
	switch (code) {
	    case 0:
	        if (ex)
		    note("Can't interpret name argument in RESET [180]");
		break;
  	    case 1:
	        note("REWRITE does not specify a name [181]");
		break;
	    case 2:
		note("OPEN does not specify a name [181]");
		break;
	    case 3:
		note("APPEND does not specify a name [181]");
		break;
	}
	nex = NULL;
    }
    if (ex) {
        if (ord_type(ex->val.type)->kind == TK_INTEGER) {
	    if (!checkconst(ex, 1))
		note("Ignoring block size in binary file [182]");
            freeexpr(ex);
        } else {
	    if (ex->kind == EK_CONST && ex->val.type->kind == TK_STRING) {
		cp = getstring(ex);
		if (strcicmp(cp, "SHARED"))
		    note(format_s("Ignoring option string \"%s\" in open [183]", cp));
	    } else
		note("Ignoring option string in open [183]");
        }
    }
    switch (code) {

        case 0:  /* reset */
            strcpy(modebuf, "r");
            break;

        case 1:  /* rewrite */
            strcpy(modebuf, "w");
            break;

        case 2:  /* open */
            strcpy(modebuf, openmode);
            break;

        case 3:  /* append */
            strcpy(modebuf, "a");
            break;

    }
    if (!*modebuf) {
        strcpy(modebuf, "r+");
    }
    if (readwriteopen == 2 ||
	(readwriteopen && fex->val.type != tp_text)) {
	if (!my_strchr(modebuf, '+'))
	    strcat(modebuf, "+");
    }
    if (fex->val.type != tp_text && binarymode != 0) {
        if (binarymode == 1)
            strcat(modebuf, "b");
        else
            note("Opening a binary file [184]");
    }
    if (!nex && fmp &&
	!is_std_file(fex) &&
	(literalfilesflag == 1 ||
	 strlist_cifind(literalfiles, fmp->name))) {
	nex = makeexpr_string(fmp->name);
    }
    if (!nex) {
	if (isvar(fex, mp_output)) {
	    note("RESET/REWRITE ignored for file OUTPUT [319]");
	    sp = NULL;
	} else {
	    sp = makestmt_call(makeexpr_bicall_1("rewind", tp_void,
						 copyexpr(fex)));
	    if (code == 0 || is_std_file(fex)) {
		sp = wrapopencheck(sp, copyexpr(fex));
		needcheckopen = 0;
	    } else
		sp = makestmt_if(makeexpr_rel(EK_NE, copyexpr(fex),
					      makeexpr_nil()),
				 sp,
				 makestmt_assign(copyexpr(fex),
						 makeexpr_bicall_0("tmpfile",
								   tp_text)));
	}
    } else if (!strcmp(freopenname, "fclose") ||
	       !strcmp(freopenname, "fopen")) {
        sp = makestmt_assign(copyexpr(fex),
                             makeexpr_bicall_2("fopen", tp_text,
                                               copyexpr(nex),
                                               makeexpr_string(modebuf)));
        if (!strcmp(freopenname, "fclose")) {
            sp = makestmt_seq(makestmt_if(makeexpr_rel(EK_NE, copyexpr(fex), makeexpr_nil()),
                                          makestmt_call(makeexpr_bicall_1("fclose", tp_void,
                                                                          copyexpr(fex))),
                                          NULL),
                              sp);
        }
    } else {
        sp = makestmt_assign(copyexpr(fex),
                             makeexpr_bicall_3((*freopenname) ? freopenname : "freopen",
                                               tp_text,
                                               copyexpr(nex),
                                               makeexpr_string(modebuf),
                                               copyexpr(fex)));
        if (!*freopenname) {
            sp = makestmt_if(makeexpr_rel(EK_NE, copyexpr(fex), makeexpr_nil()),
                             sp,
                             makestmt_assign(copyexpr(fex),
                                             makeexpr_bicall_2("fopen", tp_text,
                                                               copyexpr(nex),
                                                               makeexpr_string(modebuf))));
        }
    }
    if (code == 2 && !*openmode && nex) {
        sp = makestmt_seq(sp, makestmt_if(makeexpr_rel(EK_EQ, copyexpr(fex), makeexpr_nil()),
                                          makestmt_assign(copyexpr(fex),
                                                          makeexpr_bicall_2("fopen", tp_text,
                                                                            copyexpr(nex),
                                                                            makeexpr_string("w+"))),
                                          NULL));
    }
    if (nex)
	freeexpr(nex);
    if (FCheck(checkfileopen) && needcheckopen) {
        sp = makestmt_seq(sp, makestmt_call(makeexpr_bicall_2("~SETIO", tp_void,
                                                              makeexpr_rel(EK_NE, copyexpr(fex), makeexpr_nil()),
							      makeexpr_name(filenotfoundname, tp_int))));
    }
    sp = makestmt_seq(spassign, sp);
    cp = (code == 0) ? resetbufname : setupbufname;
    if (*cp && fmp)   /* (may be eaten later, if buffering isn't needed) */
	sp = makestmt_seq(sp,
	         makestmt_call(
                     makeexpr_bicall_2(cp, tp_void, fex,
			 makeexpr_type(fex->val.type->basetype->basetype))));
    else
	freeexpr(fex);
    skipcloseparen();
    return sp;
}

Stmt *proc_append()
{
    return handleopen(3);
}

Expr *func_arccos(ex)
Expr *ex;
{
    return makeexpr_bicall_1("acos", tp_longreal, grabarg(ex, 0));
}

Expr *func_arcsin(ex)
Expr *ex;
{
    return makeexpr_bicall_1("asin", tp_longreal, grabarg(ex, 0));
}

Expr *func_arctan(ex)
Expr *ex;
{
    ex = grabarg(ex, 0);
    if (atan2flag && ex->kind == EK_DIVIDE)
        return makeexpr_bicall_2("atan2", tp_longreal, 
                                 ex->args[0], ex->args[1]);
    return makeexpr_bicall_1("atan", tp_longreal, ex);
}

Expr *func_arctanh(ex)
Expr *ex;
{
    return makeexpr_bicall_1("atanh", tp_longreal, grabarg(ex, 0));
}

Stmt *proc_argv()
{
    Expr *ex, *aex, *lex;

    if (!skipopenparen())
	return NULL;
    ex = p_expr(tp_integer);
    if (skipcomma()) {
	aex = p_expr(tp_str255);
    } else
	return NULL;
    skipcloseparen();
    lex = makeexpr_sizeof(copyexpr(aex), 0);
    aex = makeexpr_addrstr(aex);
    return makestmt_call(makeexpr_bicall_3("P_sun_argv", tp_void,
					   aex, lex, makeexpr_arglong(ex, 0)));
}

Expr *func_asr()
{
    Expr *ex;

    if (!skipopenparen())
	return NULL;
    ex = p_expr(tp_integer);
    if (skipcomma()) {
        if (signedshift == 0 || signedshift == 2) {
            ex = makeexpr_bicall_2("P_asr", ex->val.type, ex,
				   p_expr(tp_unsigned));
	} else {
	    ex = force_signed(ex);
	    ex = makeexpr_bin(EK_RSH, ex->val.type, ex, p_expr(tp_unsigned));
	    if (signedshift != 1)
		note("Assuming >> is an arithmetic shift [320]");
	}
	skipcloseparen();
    }
    return ex;
}

Expr *func_lsl()
{
    Expr *ex;

    if (!skipopenparen())
	return NULL;
    ex = p_expr(tp_integer);
    if (skipcomma()) {
	ex = makeexpr_bin(EK_LSH, ex->val.type, ex, p_expr(tp_unsigned));
	skipcloseparen();
    }
    return ex;
}

Expr *func_lsr()
{
    Expr *ex;

    if (!skipopenparen())
	return NULL;
    ex = p_expr(tp_integer);
    if (skipcomma()) {
	ex = force_unsigned(ex);
	ex = makeexpr_bin(EK_RSH, ex->val.type, ex, p_expr(tp_unsigned));
	skipcloseparen();
    }
    return ex;
}

Expr *func_bin()
{
    note("Using %b for binary printf format [185]");
    return handle_vax_hex(NULL, "b", 1);
}

Expr *func_binary(ex)
Expr *ex;
{
    char *cp;

    ex = grabarg(ex, 0);
    if (ex->kind == EK_CONST) {
        cp = getstring(ex);
        ex = makeexpr_long(my_strtol(cp, NULL, 2));
        insertarg(&ex, 0, makeexpr_name("%#lx", tp_integer));
        return ex;
    } else {
        return makeexpr_bicall_3("strtol", tp_integer, 
                                 ex, makeexpr_nil(), makeexpr_long(2));
    }
}

Static Expr *handle_bitsize(next)
int next;
{
    Expr *ex;
    Type *type;
    int lpar;
    long psize;

    lpar = (curtok == TOK_LPAR);
    if (lpar)
	gettok();
    if (curtok == TOK_IDENT && curtokmeaning &&
	curtokmeaning->kind == MK_TYPE) {
        ex = makeexpr_type(curtokmeaning->type);
        gettok();
    } else
        ex = p_expr(NULL);
    type = ex->val.type;
    if (lpar)
	skipcloseparen();
    psize = 0;
    packedsize(NULL, &type, &psize, 0);
    if (psize > 0 && psize < 32 && next) {
	if (psize > 16)
	    psize = 32;
	else if (psize > 8)
	    psize = 16;
	else if (psize > 4)
	    psize = 8;
	else if (psize > 2)
	    psize = 4;
	else if (psize > 1)
	    psize = 2;
	else
	    psize = 1;
    }
    if (psize)
	return makeexpr_long(psize);
    else
	return makeexpr_times(makeexpr_sizeof(ex, 0),
			      makeexpr_long(sizeof_char ? sizeof_char : 8));
}

Expr *func_bitsize()
{
    return handle_bitsize(0);
}

Expr *func_bitnext()
{
    return handle_bitsize(1);
}

Expr *func_blockread()
{
    Expr *ex, *ex2, *vex, *sex, *fex;
    Type *type;

    if (!skipopenparen())
	return NULL;
    fex = p_expr(tp_text);
    if (!skipcomma())
	return NULL;
    vex = p_expr(NULL);
    if (!skipcomma())
	return NULL;
    ex2 = p_expr(tp_integer);
    if (curtok == TOK_COMMA) {
        gettok();
        sex = p_expr(tp_integer);
	sex = doseek(copyexpr(fex),
		     makeexpr_times(sex, makeexpr_long(512)))->exp1;
    } else
        sex = NULL;
    skipcloseparen();
    type = vex->val.type;
    ex = makeexpr_bicall_4("fread", tp_integer,
			   makeexpr_addr(vex),
			   makeexpr_long(512),
			   convert_size(type, ex2, "BLOCKREAD"),
			   copyexpr(fex));
    return makeexpr_comma(sex, ex);
}

Expr *func_blockwrite()
{
    Expr *ex, *ex2, *vex, *sex, *fex;
    Type *type;

    if (!skipopenparen())
	return NULL;
    fex = p_expr(tp_text);
    if (!skipcomma())
	return NULL;
    vex = p_expr(NULL);
    if (!skipcomma())
	return NULL;
    ex2 = p_expr(tp_integer);
    if (curtok == TOK_COMMA) {
        gettok();
        sex = p_expr(tp_integer);
	sex = doseek(copyexpr(fex),
		     makeexpr_times(sex, makeexpr_long(512)))->exp1;
    } else
        sex = NULL;
    skipcloseparen();
    type = vex->val.type;
    ex = makeexpr_bicall_4("fwrite", tp_integer,
			   makeexpr_addr(vex),
			   makeexpr_long(512),
			   convert_size(type, ex2, "BLOCKWRITE"),
			   copyexpr(fex));
    return makeexpr_comma(sex, ex);
}

Stmt *proc_blockread()
{
    Expr *ex, *ex2, *vex, *rex, *fex;
    Type *type;

    if (!skipopenparen())
	return NULL;
    fex = p_expr(tp_text);
    if (!skipcomma())
	return NULL;
    vex = p_expr(NULL);
    if (!skipcomma())
	return NULL;
    ex2 = p_expr(tp_integer);
    if (curtok == TOK_COMMA) {
        gettok();
        rex = p_expr(tp_integer);
    } else
        rex = NULL;
    skipcloseparen();
    type = vex->val.type;
    if (rex) {
        ex = makeexpr_bicall_4("fread", tp_integer,
                               makeexpr_addr(vex),
                               makeexpr_long(1),
                               convert_size(type, ex2, "BLOCKREAD"),
                               copyexpr(fex));
        ex = makeexpr_assign(rex, ex);
        if (!iocheck_flag)
            ex = makeexpr_comma(ex,
                                makeexpr_assign(makeexpr_var(mp_ioresult),
                                                makeexpr_long(0)));
    } else {
        ex = makeexpr_bicall_4("fread", tp_integer,
                               makeexpr_addr(vex),
                               convert_size(type, ex2, "BLOCKREAD"),
                               makeexpr_long(1),
                               copyexpr(fex));
        if (checkeof(fex)) {
            ex = makeexpr_bicall_2(name_SETIO, tp_void,
                                   makeexpr_rel(EK_EQ, ex, makeexpr_long(1)),
				   makeexpr_name(endoffilename, tp_int));
        }
    }
    return wrapopencheck(makestmt_call(ex), fex);
}

