/* ioop.c: Function operators for input and output. */

#define _POSIX_SOURCE

#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include "x.tab.h"
#include "operator.h"
#include "execute.h"
#include "data.h"
#include "memory.h"
#include "io.h"
#include "cmstring.h"
#include "config.h"
#include "ident.h"
#include "util.h"

#define FILE_BUF_SIZE 100

void op_echo(void)
{
    Data *args;

    /* Accept a string to echo. */
    if (!func_init_1(&args, BUFFER))
	return;

    /* Write the string to any connection associated with this object. */
    tell(cur_frame->object->dbref, args[0].u.buffer);

    pop(1);
    push_int(1);
}

void op_echo_file(void)
{
    size_t size, i, r;
    Data *args;
    FILE *fp;
    char *fname;
    Buffer *buf;
    struct stat statbuf;
    int len;

    /* Accept the name of a file to echo. */
    if (!func_init_1(&args, STRING))
	return;

    /* Don't allow walking back up the directory tree. */
    if (strstr(string_chars(args[0].u.str), "../")) {
	cthrow(perm_id, "Filename %D is not legal.", &args[0]);
	return;
    }

    len = string_length(args[0].u.str);
    fname = TMALLOC(char, len + 6);
    memcpy(fname, "text/", 5);
    memcpy(fname + 5, string_chars(args[0].u.str), len);
    fname[len + 5] = 0;

    /* Stat the file to get its size. */
    if (stat(fname, &statbuf) < 0) {
	tfree_chars(fname);
	cthrow(perm_id, "Cannot find file %D.", &args[0]);
	return;
    }
    size = statbuf.st_size;

    /* Open the file for reading. */
    fp = open_scratch_file(fname, "r");
    tfree_chars(fname);
    pop(1);
    if (!fp) {
	cthrow(file_id, "Cannot open file %D for reading.", &args[0]);
	return;
    }

    /* Allocate a buffer to hold the file contents. */
    buf = buffer_new(size);

    /* Read in the file. */
    i = 0;
    while (i < size) {
	r = fread(buf->s + i, sizeof(unsigned char), size, fp);
	if (r <= 0) {
	    buffer_discard(buf);
	    close_scratch_file(fp);
	    cthrow(file_id, "Trouble reading file %D.", &args[0]);
	    return;
	}
	i += r;
    }

    /* Write the file. */
    tell(cur_frame->object->dbref, buf);

    /* Discard the buffer and close the file. */
    buffer_discard(buf);
    close_scratch_file(fp);

    push_int(1);
}

void op_disconnect(void)
{
    /* Accept no arguments. */
    if (!func_init_0())
	return;

    /* Kick off anyone assigned to the current object. */
    push_int(boot(cur_frame->object->dbref));
}