jsfile.c

来自「一个基于alice开发的机器人」· C语言 代码 · 共 2,148 行 · 第 1/5 页

C
2,148
字号
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is Mozilla Communicator client code, released
 * March 31, 1998.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of the GNU General Public License Version 2 or later (the "GPL"),
 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

/*
 * JS File object
 */
#if JS_HAS_FILE_OBJECT

#include "jsstddef.h"

/* ----------------- Platform-specific includes and defines ----------------- */
#ifdef XP_MAC
#   define FILESEPARATOR         ':'
#   define FILESEPARATOR2        '\0'
#   define CURRENT_DIR          "HARD DISK:Desktop Folder"
/*  TODO: #include <???> */
#elif defined(XP_WIN) || defined(XP_OS2)
#   include <direct.h>
#   include <io.h>
#   include <sys/types.h>
#   include <sys/stat.h>
#   define FILESEPARATOR        '\\'
#   define FILESEPARATOR2       '/'
#   define CURRENT_DIR          "c:\\"
#   define POPEN                _popen
#   define PCLOSE               _pclose
#elif defined(XP_UNIX) || defined(XP_BEOS)
#   include <strings.h>
#   include <stdio.h>
#   include <stdlib.h>
#   include <unistd.h>
#   define FILESEPARATOR        '/'
#   define FILESEPARATOR2       '\0'
#   define CURRENT_DIR          "/"
#   define POPEN                popen
#   define PCLOSE               pclose
#endif

/* --------------- Platform-independent includes and defines ---------------- */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsdate.h"
#include "jsdbgapi.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jslock.h"
#include "jsobj.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
#include "jsutil.h" /* Added by JSIFY */
#include <string.h>

/* NSPR dependencies */
#include "prio.h"
#include "prerror.h"

#define SPECIAL_FILE_STRING     "Special File"
#define CURRENTDIR_PROPERTY     "currentDir"
#define SEPARATOR_PROPERTY      "separator"
#define FILE_CONSTRUCTOR        "File"
#define PIPE_SYMBOL             '|'

#define ASCII                   0
#define UTF8                    1
#define UCS2                    2

#define asciistring             "text"
#define utfstring               "binary"
#define unicodestring           "unicode"

#define MAX_PATH_LENGTH         1024
#define MODE_SIZE               256
#define NUMBER_SIZE             32
#define MAX_LINE_LENGTH         256
#define URL_PREFIX              "file://"

#define STDINPUT_NAME           "Standard input stream"
#define STDOUTPUT_NAME          "Standard output stream"
#define STDERROR_NAME           "Standard error stream"

#define RESOLVE_PATH            js_canonicalPath	/* js_absolutePath */

/* Error handling */
typedef enum JSFileErrNum {
#define MSG_DEF(name, number, count, exception, format) \
    name = number,
#include "jsfile.msg"
#undef MSG_DEF
    JSFileErr_Limit
#undef MSGDEF
} JSFileErrNum;

#define JSFILE_HAS_DFLT_MSG_STRINGS 1

JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
#if JSFILE_HAS_DFLT_MSG_STRINGS
#define MSG_DEF(name, number, count, exception, format) \
    { format, count } ,
#else
#define MSG_DEF(name, number, count, exception, format) \
    { NULL, count } ,
#endif
#include "jsfile.msg"
#undef MSG_DEF
};

const JSErrorFormatString *
JSFile_GetErrorMessage(void *userRef, const char *locale,
                                                        const uintN errorNumber)
{
    if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
        return &JSFile_ErrorFormatString[errorNumber];
	else
	    return NULL;
}

#define JSFILE_CHECK_NATIVE(op)     \
    if(file->isNative){         \
        JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s", \
						op, file->path);   \
        goto out;   \
    }

#define JSFILE_CHECK_WRITE      \
    if (!file->isOpen){     \
        JS_ReportWarning(cx,    \
                "File %s is closed, will open it for writing, proceeding",  \
                file->path);    \
        js_FileOpen(cx, obj, file, "write,append,create");     \
    }else   \
    if(!js_canWrite(cx, file)){     \
        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
            JSFILEMSG_CANNOT_WRITE, file->path);    \
        goto out;   \
    }

#define JSFILE_CHECK_READ      \
    if (!file->isOpen){     \
        JS_ReportWarning(cx,    \
                "File %s is closed, will open it for reading, proceeding",  \
                file->path); \
        js_FileOpen(cx, obj, file, "read");     \
    }else   \
    if(!js_canRead(cx, file)){     \
        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
            JSFILEMSG_CANNOT_READ, file->path);    \
        goto out;   \
    }

#define JSFILE_CHECK_OPEN(op)      \
    if(!file->isOpen){     \
        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
            JSFILEMSG_FILE_MUST_BE_CLOSED, op);    \
        goto out;   \
    }

#define JSFILE_CHECK_CLOSED(op)      \
    if(file->isOpen){     \
        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
            JSFILEMSG_FILE_MUST_BE_OPEN, op);    \
        goto out;   \
    }

#define JSFILE_CHECK_ONE_ARG(op)      \
    if (argc!=1){   \
        char str[NUMBER_SIZE];  \
                                \
        sprintf(str, "%d", argc);   \
        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,  \
            JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);  \
        goto out;   \
    }


/*
    Security mechanism, should define a callback for this.
    The parameters are as follows:
    SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
*/
#define SECURITY_CHECK(cx, ps, op, file)    \
        /* Define a callback here... */


/* Structure representing the file internally */
typedef struct JSFile {
    char        *path;          /* the path to the file. */
    JSBool      isOpen;
    JSString    *linebuffer;    /* temp buffer used by readln. */
    int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */
    int32       type;           /* Asciiz, utf, unicode */
    char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
    jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */
    jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */
    JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
    JSBool      hasRandomAccess;   /* can the file be randomly accessed? false for stdin, and
                                 UTF-encoded files. */
    JSBool      hasAutoflush;   /* should we force a flush for each line break? */
    JSBool      isNative;       /* if the file is using OS-specific file FILE type */
    /* We can actually put the following two in a union since they should never be used at the same time */
    PRFileDesc  *handle;        /* the handle for the file, if open.  */
    FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */
    JSBool      isPipe;         /* if the file is really an OS pipe */
} JSFile;

/* a few forward declarations... */
static JSClass file_class;
JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);

/* --------------------------- New filename manipulation procesures -------------------------- */
/* assumes we don't have leading/trailing spaces */
static JSBool
js_filenameHasAPipe(const char *filename)
{
#ifdef XP_MAC
    /* pipes are not supported on the MAC */
    return JS_FALSE;
#else
    if(!filename) return JS_FALSE;
    return  filename[0]==PIPE_SYMBOL ||
            filename[strlen(filename)-1]==PIPE_SYMBOL;
#endif
}

static JSBool
js_isAbsolute(const char *name)
{
#if defined(XP_WIN) || defined(XP_OS2)
    return (strlen(name)>1)?((name[1]==':')?JS_TRUE:JS_FALSE):JS_FALSE;
#else
    return (name[0]
#   if defined(XP_UNIX) || defined(XP_BEOS)
            ==
#   else
            !=
#   endif
            FILESEPARATOR)?JS_TRUE:JS_FALSE;
#endif
}

/*
    Concatinates base and name to produce a valid filename.
    Returned string must be freed.
*/
static char*
js_combinePath(JSContext *cx, const char *base, const char *name)
{
    int len = strlen(base);
    char* result = (char*)JS_malloc(cx, len+strlen(name)+2);

    if (!result)  return NULL;

    strcpy(result, base);

    if (base[len-1]!=FILESEPARATOR
#if defined(XP_WIN) || defined(XP_OS2)
            && base[len-1]!=FILESEPARATOR2
#endif
            ) {
      result[len] = FILESEPARATOR;
      result[len+1] = '\0';
    }
    strcat(result, name);
    return result;
}

/* Extract the last component from a path name. Returned string must be freed */
static char *
js_fileBaseName(JSContext *cx, const char *pathname)
{
    jsint index, aux;
    char *result;

#if defined(XP_WIN) || defined(XP_OS2)
    /* First, get rid of the drive selector */
    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
        pathname = &pathname[2];
    }
#endif
    index = strlen(pathname)-1;
    /*
        remove trailing separators -- don't necessarily need to check for
        FILESEPARATOR2, but that's fine
    */
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    aux = index;
    /* now find the next separator */
    while ((index>=0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;
    /* allocate and copy */
    result = (char*)JS_malloc(cx, aux-index+1);
    if (!result)  return NULL;
    strncpy(result, &pathname[index+1], aux-index);
    result[aux-index] = '\0';
    return result;
}

/*
    Returns everytynig but the last component from a path name.
    Returned string must be freed. Returned string must be freed.
*/
static char *
js_fileDirectoryName(JSContext *cx, const char *pathname)
{
    jsint index;
    char  *result;

#if defined(XP_WIN) || defined(XP_OS2)
    char  drive = '\0';
    const char *oldpathname = pathname;

    /* First, get rid of the drive selector */
    if ((strlen(pathname)>=2)&&(pathname[1]==':')) {
        drive = pathname[0];
        pathname = &pathname[2];
    }
#endif
    index = strlen(pathname)-1;
    while ((index>0)&&((pathname[index]==FILESEPARATOR)||
                       (pathname[index]==FILESEPARATOR2))) index--;
    while ((index>0)&&(pathname[index]!=FILESEPARATOR)&&
                      (pathname[index]!=FILESEPARATOR2)) index--;

    if (index>=0){
        result = (char*)JS_malloc(cx, index+4);
        if (!result)  return NULL;
#if defined(XP_WIN) || defined(XP_OS2)
        if (drive!='\0') {
            result[0] = toupper(drive);
            result[1] = ':';
            strncpy(&result[2], pathname, index);
			result[index+3] = '\0';
        }else
#endif
        {
            strncpy(result, pathname, index);
			result[index] = '\0';
        }

        /* add terminating separator */
        index = strlen(result)-1;
        result[index] = FILESEPARATOR;
        result[index+1] = '\0';
    } else{
#if defined(XP_WIN) || defined(XP_OS2)
        result = JS_strdup(cx, oldpathname); /* may include drive selector */
#else
        result = JS_strdup(cx, pathname);
#endif
    }

    return result;
}

static char *
js_absolutePath(JSContext *cx, const char * path)
{
    JSObject *obj;
    JSString *str;
    jsval prop;

    if (js_isAbsolute(path)){
        return JS_strdup(cx, path);
    }else{
        obj = JS_GetGlobalObject(cx);
        if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
                 JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
            return JS_strdup(cx, path);
        }
        obj = JSVAL_TO_OBJECT(prop);
        if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
                 JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
            return JS_strdup(cx, path);
        }
        str = JS_ValueToString(cx, prop);
        if (!str ) {
            return JS_strdup(cx, path);
        }
        /* should we have an array of curr dirs indexed by drive for windows? */
        return js_combinePath(cx, JS_GetStringBytes(str), path);
    }
}

/* Side effect: will remove spaces in the beginning/end of the filename */
static char *
js_canonicalPath(JSContext *cx, char *oldpath)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?