ds2.9a12/bin/
ds2.9a12/extra/
ds2.9a12/extra/crat/
ds2.9a12/extra/creremote/
ds2.9a12/extra/mingw/
ds2.9a12/extra/wolfpaw/
ds2.9a12/fluffos-2.14-ds13/
ds2.9a12/fluffos-2.14-ds13/Win32/
ds2.9a12/fluffos-2.14-ds13/compat/
ds2.9a12/fluffos-2.14-ds13/compat/simuls/
ds2.9a12/fluffos-2.14-ds13/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/
ds2.9a12/fluffos-2.14-ds13/testsuite/clone/
ds2.9a12/fluffos-2.14-ds13/testsuite/command/
ds2.9a12/fluffos-2.14-ds13/testsuite/data/
ds2.9a12/fluffos-2.14-ds13/testsuite/etc/
ds2.9a12/fluffos-2.14-ds13/testsuite/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/master/
ds2.9a12/fluffos-2.14-ds13/testsuite/log/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/compiler/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/efuns/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/operators/
ds2.9a12/fluffos-2.14-ds13/testsuite/u/
ds2.9a12/lib/cmds/admins/
ds2.9a12/lib/cmds/common/
ds2.9a12/lib/cmds/creators/include/
ds2.9a12/lib/daemon/services/
ds2.9a12/lib/daemon/tmp/
ds2.9a12/lib/doc/
ds2.9a12/lib/doc/bguide/
ds2.9a12/lib/doc/efun/all/
ds2.9a12/lib/doc/efun/arrays/
ds2.9a12/lib/doc/efun/buffers/
ds2.9a12/lib/doc/efun/compile/
ds2.9a12/lib/doc/efun/floats/
ds2.9a12/lib/doc/efun/functions/
ds2.9a12/lib/doc/efun/general/
ds2.9a12/lib/doc/efun/mixed/
ds2.9a12/lib/doc/efun/numbers/
ds2.9a12/lib/doc/efun/parsing/
ds2.9a12/lib/doc/hbook/
ds2.9a12/lib/doc/help/classes/
ds2.9a12/lib/doc/help/races/
ds2.9a12/lib/doc/lfun/
ds2.9a12/lib/doc/lfun/all/
ds2.9a12/lib/doc/lfun/lib/abilities/
ds2.9a12/lib/doc/lfun/lib/armor/
ds2.9a12/lib/doc/lfun/lib/bank/
ds2.9a12/lib/doc/lfun/lib/bot/
ds2.9a12/lib/doc/lfun/lib/clay/
ds2.9a12/lib/doc/lfun/lib/clean/
ds2.9a12/lib/doc/lfun/lib/clerk/
ds2.9a12/lib/doc/lfun/lib/client/
ds2.9a12/lib/doc/lfun/lib/combat/
ds2.9a12/lib/doc/lfun/lib/connect/
ds2.9a12/lib/doc/lfun/lib/container/
ds2.9a12/lib/doc/lfun/lib/corpse/
ds2.9a12/lib/doc/lfun/lib/creator/
ds2.9a12/lib/doc/lfun/lib/daemon/
ds2.9a12/lib/doc/lfun/lib/damage/
ds2.9a12/lib/doc/lfun/lib/deterioration/
ds2.9a12/lib/doc/lfun/lib/donate/
ds2.9a12/lib/doc/lfun/lib/door/
ds2.9a12/lib/doc/lfun/lib/equip/
ds2.9a12/lib/doc/lfun/lib/file/
ds2.9a12/lib/doc/lfun/lib/fish/
ds2.9a12/lib/doc/lfun/lib/fishing/
ds2.9a12/lib/doc/lfun/lib/flashlight/
ds2.9a12/lib/doc/lfun/lib/follow/
ds2.9a12/lib/doc/lfun/lib/ftp_client/
ds2.9a12/lib/doc/lfun/lib/ftp_data_connection/
ds2.9a12/lib/doc/lfun/lib/fuel/
ds2.9a12/lib/doc/lfun/lib/furnace/
ds2.9a12/lib/doc/lfun/lib/genetics/
ds2.9a12/lib/doc/lfun/lib/holder/
ds2.9a12/lib/doc/lfun/lib/id/
ds2.9a12/lib/doc/lfun/lib/interactive/
ds2.9a12/lib/doc/lfun/lib/lamp/
ds2.9a12/lib/doc/lfun/lib/leader/
ds2.9a12/lib/doc/lfun/lib/light/
ds2.9a12/lib/doc/lfun/lib/limb/
ds2.9a12/lib/doc/lfun/lib/living/
ds2.9a12/lib/doc/lfun/lib/load/
ds2.9a12/lib/doc/lfun/lib/look/
ds2.9a12/lib/doc/lfun/lib/manipulate/
ds2.9a12/lib/doc/lfun/lib/meal/
ds2.9a12/lib/doc/lfun/lib/messages/
ds2.9a12/lib/doc/lfun/lib/player/
ds2.9a12/lib/doc/lfun/lib/poison/
ds2.9a12/lib/doc/lfun/lib/position/
ds2.9a12/lib/doc/lfun/lib/post_office/
ds2.9a12/lib/doc/lfun/lib/potion/
ds2.9a12/lib/doc/lfun/lib/room/
ds2.9a12/lib/doc/lfun/lib/server/
ds2.9a12/lib/doc/lfun/lib/spell/
ds2.9a12/lib/doc/lfun/lib/torch/
ds2.9a12/lib/doc/lfun/lib/vendor/
ds2.9a12/lib/doc/lfun/lib/virt_sky/
ds2.9a12/lib/doc/lfun/lib/weapon/
ds2.9a12/lib/doc/lfun/lib/worn_storage/
ds2.9a12/lib/doc/lpc/basic/
ds2.9a12/lib/doc/lpc/concepts/
ds2.9a12/lib/doc/lpc/constructs/
ds2.9a12/lib/doc/lpc/etc/
ds2.9a12/lib/doc/lpc/intermediate/
ds2.9a12/lib/doc/lpc/types/
ds2.9a12/lib/doc/misc/
ds2.9a12/lib/doc/old/
ds2.9a12/lib/domains/
ds2.9a12/lib/domains/Praxis/adm/
ds2.9a12/lib/domains/Praxis/attic/
ds2.9a12/lib/domains/Praxis/cemetery/mon/
ds2.9a12/lib/domains/Praxis/data/
ds2.9a12/lib/domains/Praxis/death/
ds2.9a12/lib/domains/Praxis/mountains/
ds2.9a12/lib/domains/Praxis/obj/armour/
ds2.9a12/lib/domains/Praxis/obj/magic/
ds2.9a12/lib/domains/Praxis/obj/weapon/
ds2.9a12/lib/domains/Praxis/orc_valley/
ds2.9a12/lib/domains/Ylsrim/
ds2.9a12/lib/domains/Ylsrim/adm/
ds2.9a12/lib/domains/Ylsrim/armor/
ds2.9a12/lib/domains/Ylsrim/broken/
ds2.9a12/lib/domains/Ylsrim/fish/
ds2.9a12/lib/domains/Ylsrim/meal/
ds2.9a12/lib/domains/Ylsrim/npc/
ds2.9a12/lib/domains/Ylsrim/obj/
ds2.9a12/lib/domains/Ylsrim/virtual/
ds2.9a12/lib/domains/Ylsrim/weapon/
ds2.9a12/lib/domains/campus/adm/
ds2.9a12/lib/domains/campus/etc/
ds2.9a12/lib/domains/campus/meals/
ds2.9a12/lib/domains/campus/save/
ds2.9a12/lib/domains/campus/txt/ai/charles/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds2.9a12/lib/domains/campus/txt/ai/charly/
ds2.9a12/lib/domains/campus/txt/ai/charly/bak/
ds2.9a12/lib/domains/campus/txt/jenny/
ds2.9a12/lib/domains/cave/doors/
ds2.9a12/lib/domains/cave/etc/
ds2.9a12/lib/domains/cave/meals/
ds2.9a12/lib/domains/cave/weap/
ds2.9a12/lib/domains/default/creator/
ds2.9a12/lib/domains/default/doors/
ds2.9a12/lib/domains/default/etc/
ds2.9a12/lib/domains/default/vehicles/
ds2.9a12/lib/domains/default/virtual/
ds2.9a12/lib/domains/default/weap/
ds2.9a12/lib/domains/town/txt/shame/
ds2.9a12/lib/domains/town/virtual/
ds2.9a12/lib/domains/town/virtual/bottom/
ds2.9a12/lib/domains/town/virtual/space/
ds2.9a12/lib/estates/
ds2.9a12/lib/ftp/
ds2.9a12/lib/lib/comp/
ds2.9a12/lib/lib/daemons/
ds2.9a12/lib/lib/daemons/include/
ds2.9a12/lib/lib/lvs/
ds2.9a12/lib/lib/user/
ds2.9a12/lib/lib/virtual/
ds2.9a12/lib/log/
ds2.9a12/lib/log/adm/
ds2.9a12/lib/log/archive/
ds2.9a12/lib/log/chan/
ds2.9a12/lib/log/errors/
ds2.9a12/lib/log/law/adm/
ds2.9a12/lib/log/law/email/
ds2.9a12/lib/log/law/names/
ds2.9a12/lib/log/law/sites-misc/
ds2.9a12/lib/log/law/sites-register/
ds2.9a12/lib/log/law/sites-tempban/
ds2.9a12/lib/log/law/sites-watch/
ds2.9a12/lib/log/open/
ds2.9a12/lib/log/reports/
ds2.9a12/lib/log/router/
ds2.9a12/lib/log/secure/
ds2.9a12/lib/log/watch/
ds2.9a12/lib/obj/book_source/
ds2.9a12/lib/obj/include/
ds2.9a12/lib/powers/prayers/
ds2.9a12/lib/powers/spells/
ds2.9a12/lib/realms/template/adm/
ds2.9a12/lib/realms/template/area/armor/
ds2.9a12/lib/realms/template/area/npc/
ds2.9a12/lib/realms/template/area/obj/
ds2.9a12/lib/realms/template/area/room/
ds2.9a12/lib/realms/template/area/weap/
ds2.9a12/lib/realms/template/bak/
ds2.9a12/lib/realms/template/cmds/
ds2.9a12/lib/save/kills/o/
ds2.9a12/lib/secure/cfg/classes/
ds2.9a12/lib/secure/cmds/builders/
ds2.9a12/lib/secure/cmds/creators/include/
ds2.9a12/lib/secure/cmds/players/
ds2.9a12/lib/secure/cmds/players/include/
ds2.9a12/lib/secure/daemon/imc2server/
ds2.9a12/lib/secure/daemon/include/
ds2.9a12/lib/secure/lib/
ds2.9a12/lib/secure/lib/include/
ds2.9a12/lib/secure/lib/net/include/
ds2.9a12/lib/secure/lib/std/
ds2.9a12/lib/secure/log/adm/
ds2.9a12/lib/secure/log/bak/
ds2.9a12/lib/secure/log/intermud/
ds2.9a12/lib/secure/log/network/
ds2.9a12/lib/secure/modules/
ds2.9a12/lib/secure/npc/
ds2.9a12/lib/secure/obj/include/
ds2.9a12/lib/secure/room/
ds2.9a12/lib/secure/save/
ds2.9a12/lib/secure/save/backup/
ds2.9a12/lib/secure/save/boards/
ds2.9a12/lib/secure/tmp/
ds2.9a12/lib/secure/upgrades/files/
ds2.9a12/lib/secure/verbs/creators/
ds2.9a12/lib/std/board/
ds2.9a12/lib/std/lib/
ds2.9a12/lib/tmp/
ds2.9a12/lib/verbs/admins/include/
ds2.9a12/lib/verbs/builders/
ds2.9a12/lib/verbs/common/
ds2.9a12/lib/verbs/common/include/
ds2.9a12/lib/verbs/creators/
ds2.9a12/lib/verbs/creators/include/
ds2.9a12/lib/verbs/rooms/
ds2.9a12/lib/verbs/rooms/include/
ds2.9a12/lib/www/client/
ds2.9a12/lib/www/errors/
ds2.9a12/lib/www/images/
ds2.9a12/lib/www/lpmuds/downloads_files/
ds2.9a12/lib/www/lpmuds/intermud_files/
ds2.9a12/lib/www/lpmuds/links_files/
ds2.9a12/win32/
/*
 * file: file.c
 * description: handle all file based efuns
 */

#include "std.h"
#include "file.h"
#include "comm.h"
#include "lex.h"
#include "md.h"
#include "port.h"
#include "master.h"

#ifdef PACKAGE_COMPRESS
#include <zlib.h>
#endif

int legal_path (const char *);

static int match_string (char *, char *);
static int copy (const char *from, const char *to);
static int do_move (const char *from, const char *to, int flag);
static int CDECL pstrcmp (CONST void *, CONST void *);
static int CDECL parrcmp (CONST void *, CONST void *);
static void encode_stat (svalue_t *, int, char *, struct stat *);

#define MAX_LINES 50

/*
 * These are used by qsort in get_dir().
 */
static int CDECL pstrcmp (CONST void * p1, CONST void * p2)
{
    svalue_t *x = (svalue_t *)p1;
    svalue_t *y = (svalue_t *)p2;
    
    return strcmp(x->u.string, y->u.string);
}

static int CDECL parrcmp (CONST void * p1, CONST void * p2)
{
    svalue_t *x = (svalue_t *)p1;
    svalue_t *y = (svalue_t *)p2;
    
    return strcmp(x->u.arr->item[0].u.string, y->u.arr->item[0].u.string);
}

static void encode_stat (svalue_t * vp, int flags, char * str, struct stat * st)
{
    if (flags == -1) {
        array_t *v = allocate_empty_array(3);

        v->item[0].type = T_STRING;
        v->item[0].subtype = STRING_MALLOC;
        v->item[0].u.string = string_copy(str, "encode_stat");
        v->item[1].type = T_NUMBER;
        v->item[1].u.number =
            ((st->st_mode & S_IFDIR) ? -2 : st->st_size);
        v->item[2].type = T_NUMBER;
        v->item[2].u.number = st->st_mtime;
        vp->type = T_ARRAY;
        vp->u.arr = v;
    } else {
        vp->type = T_STRING;
        vp->subtype = STRING_MALLOC;
        vp->u.string = string_copy(str, "encode_stat");
    }
}

/*
 * List files in directory. This function do same as standard list_files did,
 * but instead writing files right away to user this returns an array
 * containing those files. Actually most of code is copied from list_files()
 * function.
 * Differences with list_files:
 *
 *   - file_list("/w"); returns ({ "w" })
 *
 *   - file_list("/w/"); and file_list("/w/."); return contents of directory
 *     "/w"
 *
 *   - file_list("/");, file_list("."); and file_list("/."); return contents
 *     of directory "/"
 *
 * With second argument equal to non-zero, instead of returning an array
 * of strings, the function will return an array of arrays about files.
 * The information in each array is supplied in the order:
 *    name of file,
 *    size of file,
 *    last update of file.
 */
/* WIN32 should be fixed to do this correctly (i.e. no ifdefs for it) */
#define MAX_FNAME_SIZE 255
#define MAX_PATH_LEN   1024
array_t *get_dir (const char * path, int flags)
{
    array_t *v;
    int i, count = 0;
#ifndef WIN32
    DIR *dirp;
#endif
    int namelen, do_match = 0;

#ifndef WIN32
#ifdef USE_STRUCT_DIRENT
    struct dirent *de;
#else
    struct direct *de;
#endif
#endif
    struct stat st;
    char *endtemp;
    char temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
    char regexppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
    char *p;

#ifdef WIN32
    struct _finddata_t FindBuffer;
    long FileHandle, FileCount;
#endif

    if (!path)
        return 0;

    path = check_valid_path(path, current_object, "stat", 0);

    if (path == 0)
        return 0;

    if (strlen(path) < 2) {
        temppath[0] = path[0] ? path[0] : '.';
        temppath[1] = '\000';
        p = temppath;
    } else {
        strncpy(temppath, path, MAX_FNAME_SIZE + MAX_PATH_LEN + 1);
        temppath[MAX_FNAME_SIZE + MAX_PATH_LEN + 1] = '\0';

        /*
         * If path ends with '/' or "/." remove it
         */
        if ((p = strrchr(temppath, '/')) == 0)
            p = temppath;
        if (p[0] == '/' && ((p[1] == '.' && p[2] == '\0') || p[1] == '\0'))
            *p = '\0';
    }

    if (stat(temppath, &st) < 0) {
        if (*p == '\0')
            return 0;
        if (p != temppath) {
            strcpy(regexppath, p + 1);
            *p = '\0';
        } else {
            strcpy(regexppath, p);
            strcpy(temppath, ".");
        }
        do_match = 1;
    } else if (*p != '\0' && strcmp(temppath, ".")) {
        if (*p == '/' && *(p + 1) != '\0')
            p++;
        v = allocate_empty_array(1);
        encode_stat(&v->item[0], flags, p, &st);
        return v;
    }
#ifdef WIN32
    FileHandle = -1;
    FileCount = 1;
/*    strcat(temppath, "\\*"); */
    strcat(temppath, "/*");
    if ((FileHandle = _findfirst(temppath, &FindBuffer)) == -1) return 0;
#else
    if ((dirp = opendir(temppath)) == 0)
        return 0;
#endif

    /*
     * Count files
     */
#ifdef WIN32
    do {
        if (!do_match && (!strcmp(FindBuffer.name, ".") ||
                          !strcmp(FindBuffer.name, ".."))) {
            continue;
        }
        if (do_match && !match_string(regexppath, FindBuffer.name)) {
            continue;
        }
        count++;
        if (count >= max_array_size) {
            break;
        }
    } while (!_findnext(FileHandle, &FindBuffer));
    _findclose(FileHandle);
#else
    for (de = readdir(dirp); de; de = readdir(dirp)) {
#ifdef USE_STRUCT_DIRENT
        namelen = strlen(de->d_name);
#else
        namelen = de->d_namlen;
#endif
        if (!do_match && (strcmp(de->d_name, ".") == 0 ||
                          strcmp(de->d_name, "..") == 0))
            continue;
        if (do_match && !match_string(regexppath, de->d_name))
            continue;
        count++;
        if (count >= max_array_size)
            break;
    }
#endif

    /*
     * Make array and put files on it.
     */
    v = allocate_empty_array(count);
    if (count == 0) {
        /* This is the easy case :-) */
#ifndef WIN32
        closedir(dirp);
#endif
        return v;
    }
#ifdef WIN32
    FileHandle = -1;
    if ((FileHandle = _findfirst(temppath, &FindBuffer)) == -1) return 0;
    endtemp = temppath + strlen(temppath) - 2;
    *endtemp = 0;
/*    strcat(endtemp++, "\\"); */
    strcat(endtemp++, "/");
    i = 0;
    do {
        if (!do_match && (!strcmp(FindBuffer.name, ".") ||
                          !strcmp(FindBuffer.name, ".."))) continue;
        if (do_match && !match_string(regexppath, FindBuffer.name)) continue;
        if (flags == -1) {
            strcpy(endtemp, FindBuffer.name);
            stat(temppath, &st);
        }
        encode_stat(&v->item[i], flags, FindBuffer.name, &st);
        i++;
    } while (!_findnext(FileHandle, &FindBuffer));
    _findclose(FileHandle);
#else                           /* WIN32 */
    rewinddir(dirp);
    endtemp = temppath + strlen(temppath);

    strcat(endtemp++, "/");

    for (i = 0, de = readdir(dirp); i < count; de = readdir(dirp)) {
#ifdef USE_STRUCT_DIRENT
        namelen = strlen(de->d_name);
#else
        namelen = de->d_namlen;
#endif
        if (!do_match && (strcmp(de->d_name, ".") == 0 ||
                          strcmp(de->d_name, "..") == 0))
            continue;
        if (do_match && !match_string(regexppath, de->d_name))
            continue;
        de->d_name[namelen] = '\0';
        if (flags == -1) {
            /*
             * We'll have to .... sigh.... stat() the file to get some add'tl
             * info.
             */
            strcpy(endtemp, de->d_name);
            stat(temppath, &st);/* We assume it works. */
        }
        encode_stat(&v->item[i], flags, de->d_name, &st);
        i++;
    }
    closedir(dirp);
#endif                          /* OS2 */

    /* Sort the names. */
    qsort((void *) v->item, count, sizeof v->item[0],
          (flags == -1) ? parrcmp : pstrcmp);
    return v;
}

int remove_file (const char * path)
{
    path = check_valid_path(path, current_object, "remove_file", 1);

    if (path == 0)
        return 0;
    if (unlink(path) == -1)
        return 0;
    return 1;
}

/*
 * Check that it is an legal path. No '..' are allowed.
 */
int legal_path (const char * path)
{
    const char *p;

    if (path == NULL)
        return 0;
    if (path[0] == '/')
        return 0;
    /*
     * disallowing # seems the easiest way to solve a bug involving loading
     * files containing that character
     */
    if (strchr(path, '#')) {
        return 0;
    }
    p = path;
    while (p) {                 /* Zak, 930530 - do better checking */
        if (p[0] == '.') {
            if (p[1] == '\0')   /* trailing `.' ok */
                break;
            if (p[1] == '.')    /* check for `..' or `../' */
                p++;
            if (p[1] == '/' || p[1] == '\0')
                return 0;       /* check for `./', `..', or `../' */
        }
        p = (char *)strstr(p, "/.");    /* search next component */
        if (p)
            p++;                /* step over `/' */
    }
#if defined(WIN32)
    /*
     * I don't know what the proper define should be, just leaving an
     * appropriate place for the right stuff to happen here - Wayfarer
     */
    /* Could be a drive thingy for os2. */
    if (strchr(path, ':'))
        return 0;
#endif
    return 1;
}                               /* legal_path() */

/*
 * There is an error in a specific file. Ask the MudOS driver to log the
 * message somewhere.
 */
void smart_log (const char * error_file, int line, const char * what, int flag)
{
    char *buff;
    svalue_t *mret;
    extern int pragmas;

    buff = (char *)
        DMALLOC(strlen(error_file) + strlen(what) + 
                ((pragmas & PRAGMA_ERROR_CONTEXT) ? 100 : 40), TAG_TEMPORARY, "smart_log: 1");

    if (flag)
        sprintf(buff, "/%s line %d: Warning: %s", error_file, line, what);
    else
        sprintf(buff, "/%s line %d: %s", error_file, line, what);

    if (pragmas & PRAGMA_ERROR_CONTEXT) {
        char *ls = strrchr(buff, '\n');
        unsigned char *tmp;
        if (ls) {
            tmp = (unsigned char *)ls + 1;
            while (*tmp && isspace(*tmp)) tmp++;
            if (!*tmp) *ls = 0;
        }
        strcat(buff, show_error_context());
    } else strcat(buff, "\n");

    push_malloced_string(add_slash(error_file));
    copy_and_push_string(buff);
    mret = safe_apply_master_ob(APPLY_LOG_ERROR, 2);
    if (!mret || mret == (svalue_t *)-1) {
        debug_message("%s", buff);
    }
    FREE(buff);
}                               /* smart_log() */

/*
 * Append string to file. Return 0 for failure, otherwise 1.
 */
int write_file (const char * file, const char * str, int flags)
{
    FILE *f;
#ifdef PACKAGE_COMPRESS
    gzFile gf;
#endif
#ifdef WIN32
    char fmode[3];
#endif

    file = check_valid_path(file, current_object, "write_file", 1);
    if (!file)
        return 0;
#ifdef PACKAGE_COMPRESS
    if(flags & 2){
      gf = gzopen(file, (flags & 1)?"w":"a");
      if(!gf)
	error("Wrong permissions for opening file /%s for %s.\n\"%s\"\n",
	      file, (flags & 1) ? "overwrite" : "append", port_strerror(errno));
    } else {
#endif
#ifdef WIN32
    fmode[0] = (flags & 1) ? 'w' : 'a';
    fmode[1] = 't';
    fmode[2] = '\0';
    f = fopen(file, fmode);
#else    
    f = fopen(file, (flags & 1) ? "w" : "a");
#endif
    if (f == 0) {
      error("Wrong permissions for opening file /%s for %s.\n\"%s\"\n",
	    file, (flags & 1) ? "overwrite" : "append", port_strerror(errno));
    }
#ifdef PACKAGE_COMPRESS
    }
    if(flags & 2){
      gzwrite(gf, str, strlen(str));
    }else
#endif
      fwrite(str, strlen(str), 1, f);
#ifdef PACKAGE_COMPRESS
    if(flags & 2)
      gzclose(gf);
    else
#endif
      fclose(f);
    return 1;
}

char *read_file(const char * file, int start, int len) {
	struct stat st;
#ifndef PACKAGE_COMPRESS
	FILE *f;
#else
	gzFile f;
#endif
	int chunk;
	char *str, *p, *p2;

	if (len < 0)
		return 0;

	file = check_valid_path(file, current_object, "read_file", 0);

	if (!file)
		return 0;

	/*
	 * file doesn't exist, or is really a directory
	 */
	if (stat(file, &st) == -1 || (st.st_mode & S_IFDIR))
		return 0;
#ifndef PACKAGE_COMPRESS
	f = fopen(file, FOPEN_READ);
#else
	f = gzopen(file, "rb");
#endif
	if (f == 0)
		return 0;

	if (start < 1)
		start = 1;
	if (len == 0)
		len = 2*READ_FILE_MAX_SIZE;

	str = new_string(2*READ_FILE_MAX_SIZE, "read_file: str");
	if (st.st_size== 0) {
		/* zero length file */
		str[0] = 0;
#ifndef PACKAGE_COMPRESS
		fclose(f);
#else
		gzclose(f);
#endif
		str = extend_string(str, 0); /* fix string length */
		return str;
	}

	do {
#ifndef PACKAGE_COMPRESS
		chunk = fread(str, 1, 2*READ_FILE_MAX_SIZE, f);
#else 
		chunk = gzread(f, str, 2*READ_FILE_MAX_SIZE);
#endif
		if (chunk < 1)
			goto free_str;
		p = str;
		while (start > 1) {
			/* skip newlines */
			p2 = (char *)memchr(p, '\n', 2*READ_FILE_MAX_SIZE+str-p);
			if (p2) {
				p = p2 + 1;
				start--;
			} else
				break;
		}
	} while (start > 1);

	p2 = str;
	while (1) {
		char c;

		c = *p++;
		if (p-str > chunk) {
			if (chunk == 2*READ_FILE_MAX_SIZE) {
				goto free_str;	//file too big
			} else
				break; //reached the end
		}
		
		if (p2-str > READ_FILE_MAX_SIZE)
			goto free_str;  //file too big
		
		if (c == '\0') {
			FREE_MSTR(str);
#ifndef PACKAGE_COMPRESS
			fclose(f);
#else
			gzclose(f);
#endif
			error("Attempted to read '\\0' into string!\n");
		}
		if (c != '\r' || *p != '\n') {
			*p2++ = c;
			if (c == '\n' && --len == 0)
				break; /* done */
		}
	}

	*p2 = 0;
	str = extend_string(str, p2 - str); /* fix string length */

#ifndef PACKAGE_COMPRESS
	fclose(f);
#else
	gzclose(f);
#endif

	return str;

	/* Error path: unwind allocated resources */

free_str: 
	FREE_MSTR(str);
#ifndef PACKAGE_COMPRESS
	fclose(f);
#else
	gzclose(f);
#endif
	return 0;
}

char *read_bytes (const char * file, int start, int len, int * rlen)
{
    struct stat st;
    FILE *fptr;
    char *str;
    int size;

    if (len < 0)
        return 0;
    file = check_valid_path(file, current_object,
                            "read_bytes", 0);
    if (!file)
        return 0;
    fptr = fopen(file, "rb");
    if (fptr == NULL)
        return 0;
    if (fstat(fileno(fptr), &st) == -1)
        fatal("Could not stat an open file.\n");
    size = st.st_size;
    if (start < 0)
        start = size + start;

    if (len == 0)
        len = size;
    if (len > MAX_BYTE_TRANSFER) {
        fclose(fptr);
        error("Transfer exceeded maximum allowed number of bytes.\n");
        return 0;
    }
    if (start >= size) {
        fclose(fptr);
        return 0;
    }
    if ((start + len) > size)
        len = (size - start);

    if ((size = fseek(fptr, start, 0)) < 0) {
        fclose(fptr);
        return 0;
    }
    
    str = new_string(len, "read_bytes: str");

    size = fread(str, 1, len, fptr);

    fclose(fptr);

    if (size <= 0) {
        FREE_MSTR(str);
        return 0;
    }
    /*
     * The string has to end to '\0'!!!
     */
    str[size] = '\0';

    *rlen = size;
    return str;
}

int write_bytes (const char * file, int start, const char * str, int theLength)
{
    struct stat st;
    int size;
    FILE *fptr;

    file = check_valid_path(file, current_object, "write_bytes", 1);

    if (!file)
        return 0;
    if (theLength > MAX_BYTE_TRANSFER)
        return 0;
    /* Under system V, it isn't possible change existing data in a file
     * opened for append, so it can't be opened for append.
     * opening for r+ won't create the file if it doesn't exist.
     * opening for w or w+ will truncate it if it does exist.  So we
     * have to check if it exists first.
     */
    if (stat(file, &st) == -1) {
      fptr = fopen(file, "wb");
    } else {
      fptr = fopen(file, "r+b");
    }
    if (fptr == NULL) {
        return 0;
    }
    if (fstat(fileno(fptr), &st) == -1)
        fatal("Could not stat an open file.\n");
    size = st.st_size;
    if (start < 0)
        start = size + start;
    if (start < 0 || start > size) {
        fclose(fptr);
        return 0;
    }
    if ((size = fseek(fptr, start, 0)) < 0) {
        fclose(fptr);
        return 0;
    }
    size = fwrite(str, 1, theLength, fptr);

    fclose(fptr);

    if (size <= 0) {
        return 0;
    }
    return 1;
}

int file_size (const char * file)
{
    struct stat st;
    long ret;
    char *t;
#ifdef WIN32
    int needs_free = 0, len;
    const char *p;
#endif

    file = check_valid_path(file, current_object, "file_size", 0);
    if (!file)
        return -1;

#ifdef WIN32
    len = strlen(file);
    p = file + len - 1;
    if (*p == '/') {
        needs_free = 1;
        p = file;
        t = new_string(len - 1, "file_size");
        memcpy(t, p, len - 1);
        t[len-1] = 0;
        file = t;
    }
#endif

    if (stat(file, &st) == -1)
        ret = -1;
    else if (S_IFDIR & st.st_mode)
        ret = -2;
    else 
        ret = st.st_size;

#ifdef WIN32
    if (needs_free) FREE_MSTR(file);
#endif
    
    return ret;
}


/*
 * Check that a path to a file is valid for read or write.
 * This is done by functions in the master object.
 * The path is always treated as an absolute path, and is returned without
 * a leading '/'.
 * If the path was '/', then '.' is returned.
 * Otherwise, the returned path is temporarily allocated by apply(), which
 * means it will be deallocated at next apply().
 */
const char *check_valid_path (const char * path, object_t * call_object, const char * const  call_fun, int writeflg)
{
    svalue_t *v;

    if (call_object == 0 || call_object->flags & O_DESTRUCTED)
        return 0;

#ifdef WIN32
    {
        char *p;
        
        for(p=path; *p; p++) if (*p == '\\') *p='/';
    }
#endif

    copy_and_push_string(path);
    push_object(call_object);
    push_constant_string(call_fun);
    if (writeflg)
        v = apply_master_ob(APPLY_VALID_WRITE, 3);
    else
        v = apply_master_ob(APPLY_VALID_READ, 3);

    if (v == (svalue_t *)-1)
        v = 0;
    
    if (v && v->type == T_NUMBER && v->u.number == 0) return 0;
    if (v && v->type == T_STRING) {
        path = v->u.string;
    } else {
        extern svalue_t apply_ret_value;
        
        free_svalue(&apply_ret_value, "check_valid_path");
        apply_ret_value.type = T_STRING;
        apply_ret_value.subtype = STRING_MALLOC;
        path = apply_ret_value.u.string = string_copy(path, "check_valid_path");
    }
    
    if (path[0] == '/')
        path++;
    if (path[0] == '\0')
        path = ".";
    if (legal_path(path))
        return path;

    return 0;
}

static int match_string (char * match, char * str)
{
    int i;

  again:
    if (*str == '\0' && *match == '\0')
        return 1;
    switch (*match) {
    case '?':
        if (*str == '\0')
            return 0;
        str++;
        match++;
        goto again;
    case '*':
        match++;
        if (*match == '\0')
            return 1;
        for (i = 0; str[i] != '\0'; i++)
            if (match_string(match, str + i))
                return 1;
        return 0;
    case '\0':
        return 0;
    case '\\':
        match++;
        if (*match == '\0')
            return 0;
        /* Fall through ! */
    default:
        if (*match == *str) {
            match++;
            str++;
            goto again;
        }
        return 0;
    }
}

static struct stat to_stats, from_stats;

static int copy (const char * from, const char * to)
{
    int ifd;
    int ofd;
    char buf[1024 * 8];
    int len;                    /* Number of bytes read into `buf'. */

    if (!S_ISREG(from_stats.st_mode)) {
        return 1;
    }
    if (unlink(to) && errno != ENOENT) {
        return 1;
    }
    ifd = open(from, OPEN_READ);
    if (ifd < 0) {
        return errno;
    }
    ofd = open(to, OPEN_WRITE | O_CREAT | O_TRUNC, 0666);
    if (ofd < 0) {
        close(ifd);
        return 1;
    }
#ifdef HAS_FCHMOD
    if (fchmod(ofd, from_stats.st_mode & 0777)) {
        close(ifd);
        close(ofd);
        unlink(to);
        return 1;
    }
#endif

    while ((len = read(ifd, buf, sizeof(buf))) > 0) {
        int wrote = 0;
        char *bp = buf;

        do {
            wrote = write(ofd, bp, len);
            if (wrote < 0) {
                close(ifd);
                close(ofd);
                unlink(to);
                return 1;
            }
            bp += wrote;
            len -= wrote;
        } while (len > 0);
    }
    if (len < 0) {
        close(ifd);
        close(ofd);
        unlink(to);
        return 1;
    }
    if (close(ifd) < 0) {
        close(ofd);
        return 1;
    }
    if (close(ofd) < 0) {
        return 1;
    }
#ifdef FCHMOD_MISSING
    if (chmod(to, from_stats.st_mode & 0777)) {
        return 1;
    }
#endif

    return 0;
}

/* Move FROM onto TO.  Handles cross-filesystem moves.
   If TO is a directory, FROM must be also.
   Return 0 if successful, 1 if an error occurred.  */

#ifdef F_RENAME
static int do_move (const char * from, const char * to, int flag)
{
    if (lstat(from, &from_stats) != 0) {
        error("/%s: lstat failed\n", from);
        return 1;
    }
    if (lstat(to, &to_stats) == 0) {
#ifdef WIN32
        if (!strcmp(from, to))
#else
        if (from_stats.st_dev == to_stats.st_dev
            && from_stats.st_ino == to_stats.st_ino)
#endif
        {
            error("`/%s' and `/%s' are the same file", from, to);
            return 1;
        }
        if (S_ISDIR(to_stats.st_mode)) {
            error("/%s: cannot overwrite directory", to);
            return 1;
        }
#ifdef WIN32
        unlink(to);
#endif
    } else if (errno != ENOENT) {
        error("/%s: unknown error\n", to);
        return 1;
    }
#ifdef SYSV
    if ((flag == F_RENAME) && file_size(from) == -2) {
        char cmd_buf[100];

        sprintf(cmd_buf, "/usr/lib/mv_dir %s %s", from, to);
        return system(cmd_buf);
    } else
#endif                          /* SYSV */
    if ((flag == F_RENAME) && (rename(from, to) == 0))
        return 0;
#ifdef F_LINK
    else if (flag == F_LINK) {
#ifdef WIN32
        error("link() not supported.\n");
#else
        if (link(from, to) == 0)
            return 0;
#endif
    }
#endif

    if (errno != EXDEV) {
        if (flag == F_RENAME)
            error("cannot move `/%s' to `/%s'\n", from, to);
        else
            error("cannot link `/%s' to `/%s'\n", from, to);
        return 1;
    }
    /* rename failed on cross-filesystem link.  Copy the file instead. */

    if (flag == F_RENAME) {
        if (copy(from, to))
            return 1;
        if (unlink(from)) {
            error("cannot remove `/%s'", from);
            return 1;
        }
    }
#ifdef F_LINK
    else if (flag == F_LINK) {
        if (symlink(from, to) == 0)     /* symbolic link */
            return 0;
    }
#endif
    return 0;
}
#endif

void debug_perror (const char * what, const char * file) {
    if (file)
        debug_message("System Error: %s:%s:%s\n", what, file, port_strerror(errno));
    else
        debug_message("System Error: %s:%s\n", what, port_strerror(errno));
}

/*
 * do_rename is used by the efun rename. It is basically a combination
 * of the unix system call rename and the unix command mv.
 */

static svalue_t from_sv = { T_NUMBER };
static svalue_t to_sv = { T_NUMBER };

#ifdef DEBUGMALLOC_EXTENSIONS
void mark_file_sv() {
    mark_svalue(&from_sv);
    mark_svalue(&to_sv);
}
#endif

#ifdef F_RENAME
int do_rename (const char * fr, const char * t, int flag)
{
    const char *from;
    const char *to;
    char newfrom[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];
    int flen;
    extern svalue_t apply_ret_value;

    /*
     * important that the same write access checks are done for link() as are
     * done for rename().  Otherwise all kinds of security problems would
     * arise (e.g. creating links to files in protected directories and then
     * modifying the protected file by modifying the linked file). The idea
     * is prevent linking to a file unless the person doing the linking has
     * permission to move the file.
     */
    from = check_valid_path(fr, current_object, "rename", 1);
    if (!from)
        return 1;

    assign_svalue(&from_sv, &apply_ret_value);
    
    to = check_valid_path(t, current_object, "rename", 1);
    if (!to)
        return 1;

    assign_svalue(&to_sv, &apply_ret_value);
    if (!strlen(to) && !strcmp(t, "/")) {
        to = "./";
    }

    /* Strip trailing slashes */
    flen = strlen(from);
    if (flen > 1 && from[flen - 1] == '/') {
        const char *p = from + flen - 2;
        int n;
        
        while (*p == '/' && (p > from))
            p--;
        n = p - from + 1;
        memcpy(newfrom, from, n);
        newfrom[n] = 0;
        from = newfrom;
    }

    if (file_size(to) == -2) {
        /* Target is a directory; build full target filename. */
        const char *cp;
        char newto[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];

        cp = strrchr(from, '/');
        if (cp)
            cp++;
        else
            cp = from;

        sprintf(newto, "%s/%s", to, cp);
        return do_move(from, newto, flag);
    } else
        return do_move(from, to, flag);
}
#endif                          /* F_RENAME */

int copy_file (const char * from, const char * to)
{
    char buf[128];
    int from_fd, to_fd;
    int num_read, num_written;
    char *write_ptr;
    extern svalue_t apply_ret_value;

    from = check_valid_path(from, current_object, "move_file", 0);
    assign_svalue(&from_sv, &apply_ret_value);

    to = check_valid_path(to, current_object, "move_file", 1);
    assign_svalue(&to_sv, &apply_ret_value);

    if (from == 0)
        return -1;
    if (to == 0)
        return -2;

    if (lstat(from, &from_stats) != 0) {
        error("/%s: lstat failed\n", from);
        return 1;
    }
    if (lstat(to, &to_stats) == 0) {
#ifdef WIN32
        if (!strcmp(from, to))
#else
        if (from_stats.st_dev == to_stats.st_dev
            && from_stats.st_ino == to_stats.st_ino)
#endif
        {
            error("`/%s' and `/%s' are the same file", from, to);
            return 1;
        }
    } else if (errno != ENOENT) {
        error("/%s: unknown error\n", to);
        return 1;
    }
    
    from_fd = open(from, OPEN_READ);
    if (from_fd < 0)
        return -1;

    if (file_size(to) == -2) {
        /* Target is a directory; build full target filename. */
        const char *cp;
        char newto[MAX_FNAME_SIZE + MAX_PATH_LEN + 2];

        cp = strrchr(from, '/');
        if (cp)
            cp++;
        else
            cp = from;

        sprintf(newto, "%s/%s", to, cp);
        close(from_fd);
        return copy_file(from, newto);
    }
    to_fd = open(to, OPEN_WRITE | O_CREAT | O_TRUNC, 0666);
    if (to_fd < 0) {
        close(from_fd);
        return -2;
    }
    while ((num_read = read(from_fd, buf, 128)) != 0) {
        if (num_read < 0) {
            debug_perror("copy_file: read", from);
            close(from_fd);
            close(to_fd);
            return -3;
        }
        write_ptr = buf;
        while (write_ptr != (buf + num_read)) {
            num_written = write(to_fd, write_ptr, num_read);
            if (num_written < 0) {
                debug_perror("copy_file: write", to);
                close(from_fd);
                close(to_fd);
                return -3;
            }
            write_ptr += num_written;
        }
    }
    close(from_fd);
    close(to_fd);
    return 1;
}

void dump_file_descriptors (outbuffer_t * out)
{
    int i;
    dev_t dev;
    struct stat stbuf;

    outbuf_add(out, "Fd  Device Number  Inode   Mode    Uid    Gid      Size\n");
    outbuf_add(out, "--  -------------  -----  ------  -----  -----  ----------\n");

    for (i = 0; i < FD_SETSIZE; i++) {
        /* bug in NeXT OS 2.1, st_mode == 0 for sockets */
        if (fstat(i, &stbuf) == -1)
            continue;

#if !defined(WIN32)
        if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode))
            dev = stbuf.st_rdev;
        else
#endif
            dev = stbuf.st_dev;

        outbuf_addv(out, "%2d", i);
        outbuf_addv(out, "%13x", dev);
        outbuf_addv(out, "%9d", stbuf.st_ino);
        outbuf_add(out, "  ");

        switch (stbuf.st_mode & S_IFMT) {

        case S_IFDIR:
            outbuf_add(out, "d");
            break;
        case S_IFCHR:
            outbuf_add(out, "c");
            break;
#ifdef S_IFBLK
        case S_IFBLK:
            outbuf_add(out, "b");
            break;
#endif
        case S_IFREG:
            outbuf_add(out, "f");
            break;
#ifdef S_IFIFO
        case S_IFIFO:
            outbuf_add(out, "p");
            break;
#endif
#ifdef S_IFLNK
        case S_IFLNK:
            outbuf_add(out, "l");
            break;
#endif
#ifdef S_IFSOCK
        case S_IFSOCK:
            outbuf_add(out, "s");
            break;
#endif
        default:
            outbuf_add(out, "?");
            break;
        }

        outbuf_addv(out, "%5o", stbuf.st_mode & ~S_IFMT);
        outbuf_addv(out, "%7d", stbuf.st_uid);
        outbuf_addv(out, "%7d", stbuf.st_gid);
        outbuf_addv(out, "%12d", stbuf.st_size);
        outbuf_add(out, "\n");
    }
}