/*
// ColdMUD was created and is copyright 1993, 1994 by Greg Hudson
//
// ColdX is a derivitive work, and is copyright 1995 by the ColdX Project.
// Full copyright information can be found in the file doc/CREDITS
//
// File:        ioop.c
// Version:     0.1-5
// Last Edited: 5 Aug 1995
//
// ---
//
// Function operators for connection and file i/o
*/

/* posix sucks */
/* #define _POSIX_SOURCE */

#include "config.h"

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

/*
// Initialize a file path, make sure you can open it, etc
//
// return values:
//
//    0 if it failed and threw an error
//    1 if everything is O.K.
//
// Added: 30 Jul 95 - BJG
*/
int init_file_path(Data * name, struct stat * statbuf, char * fname, int max) {
    int    len;

    len = string_length(name->u.str);

    if (strstr(string_chars(name->u.str), "../") || len == 0) {
        cthrow(perm_id, "Filename %D is not legal.", name);
        return 0;
    }

    /* create a variable it will pull the base name from */
    strncpy(fname, "root/", max);
    strncat(fname, string_chars(name->u.str), max);

    /* Stat the file */
    if (stat(fname, statbuf) < 0) {
        cthrow(file_id, "Cannot find file %D.", name);
        return 0;
    }

    if (S_ISDIR(statbuf->st_mode)) {
        cthrow(directory_id, "File %D is a directory.", name);
        return 0;
    }

    return 1;
}

/*
// Echo a buffer to the connection
*/
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);
}

/*
// Get basic statistics on a file
//
// Added: 30 Jul 95 - BJG
*/
void op_stat_file(void) {
    struct stat    sbuf;
    register int   x;
    List         * s;
    Data         * args,
                 * d;
    char           fname[BIGBUF];

    if (!func_init_1(&args, STRING))
        return;

    if (!init_file_path(&args[0], &sbuf, fname, BIGBUF)) {
        return;
    }

    s = list_new(5);
    d = list_empty_spaces(s, 5);
    for (x=0; x < 5; x++)
        d[x].type = INTEGER;

    d[0].u.val = (int) sbuf.st_mode;
    d[1].u.val = (int) sbuf.st_size;
    d[2].u.val = (int) sbuf.st_atime;
    d[3].u.val = (int) sbuf.st_mtime;
    d[4].u.val = (int) sbuf.st_ctime;

    push_list(s);
    list_discard(s);
}

/*
// Read a file into the db
//
// Added: 30 Jul 95 - BJG
*/
void op_read_file(void)
{
    size_t size, i, r;
    Data *args;
    FILE *fp;
    char   fname[BIGBUF];
    Buffer *buf;
    struct stat statbuf;
    int len;

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

    if (!init_file_path(&args[0], &statbuf, fname, BIGBUF)) {
        return;
    }

    size = statbuf.st_size;

    /* Open the file for reading. */
    fp = open_scratch_file(fname, "r");
    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;
    }

    /* return the buffer (wahoo, big memory chunks) */
    push_buffer(buf);

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

/*
// echo a file to the connection
*/
void op_echo_file(void)
{
    size_t size, i, r;
    Data *args;
    FILE *fp;
    char   fname[BIGBUF];
    Buffer *buf;
    struct stat statbuf;
    int len;

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

    /* Initialize the file */
    if (!init_file_path(&args[0], &statbuf, fname, BIGBUF)) {
        return;
    }

    pop(1);

    size = statbuf.st_size;

    /* Open the file for reading. */
    fp = open_scratch_file(fname, "r");
    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_close_connection(void)
{
    /* Accept no arguments. */
    if (!func_init_0())
        return;

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