PageRenderTime 360ms CodeModel.GetById 115ms app.highlight 225ms RepoModel.GetById 2ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsfile.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2255 lines | 1724 code | 329 blank | 202 comment | 347 complexity | 3bf1c2ece828bedc0e49f097d9a62449 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   2 * vim: set ts=8 sw=4 et tw=78:
   3 *
   4 * ***** BEGIN LICENSE BLOCK *****
   5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
   6 *
   7 * The contents of this file are subject to the Mozilla Public License Version
   8 * 1.1 (the "License"); you may not use this file except in compliance with
   9 * the License. You may obtain a copy of the License at
  10 * http://www.mozilla.org/MPL/
  11 *
  12 * Software distributed under the License is distributed on an "AS IS" basis,
  13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14 * for the specific language governing rights and limitations under the
  15 * License.
  16 *
  17 * The Original Code is Mozilla Communicator client code, released
  18 * March 31, 1998.
  19 *
  20 * The Initial Developer of the Original Code is
  21 * Netscape Communications Corporation.
  22 * Portions created by the Initial Developer are Copyright (C) 1998
  23 * the Initial Developer. All Rights Reserved.
  24 *
  25 * Contributor(s):
  26 *
  27 * Alternatively, the contents of this file may be used under the terms of
  28 * either of the GNU General Public License Version 2 or later (the "GPL"),
  29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30 * in which case the provisions of the GPL or the LGPL are applicable instead
  31 * of those above. If you wish to allow use of your version of this file only
  32 * under the terms of either the GPL or the LGPL, and not to allow others to
  33 * use your version of this file under the terms of the MPL, indicate your
  34 * decision by deleting the provisions above and replace them with the notice
  35 * and other provisions required by the GPL or the LGPL. If you do not delete
  36 * the provisions above, a recipient may use your version of this file under
  37 * the terms of any one of the MPL, the GPL or the LGPL.
  38 *
  39 * ***** END LICENSE BLOCK ***** */
  40
  41/*
  42 * JS File object
  43 */
  44#if JS_HAS_FILE_OBJECT
  45
  46#include "jsstddef.h"
  47#include "jsfile.h"
  48
  49/* ----------------- Platform-specific includes and defines ----------------- */
  50#if defined(XP_WIN) || defined(XP_OS2)
  51#   include <direct.h>
  52#   include <io.h>
  53#   include <sys/types.h>
  54#   include <sys/stat.h>
  55#   define FILESEPARATOR        '\\'
  56#   define FILESEPARATOR2       '/'
  57#   define CURRENT_DIR          "c:\\"
  58#   define POPEN                _popen
  59#   define PCLOSE               _pclose
  60#elif defined(XP_UNIX) || defined(XP_BEOS)
  61#   include <strings.h>
  62#   include <stdio.h>
  63#   include <stdlib.h>
  64#   include <unistd.h>
  65#   define FILESEPARATOR        '/'
  66#   define FILESEPARATOR2       '\0'
  67#   define CURRENT_DIR          "/"
  68#   define POPEN                popen
  69#   define PCLOSE               pclose
  70#endif
  71
  72/* --------------- Platform-independent includes and defines ---------------- */
  73#include "jsapi.h"
  74#include "jsatom.h"
  75#include "jscntxt.h"
  76#include "jsdate.h"
  77#include "jsdbgapi.h"
  78#include "jsemit.h"
  79#include "jsfun.h"
  80#include "jslock.h"
  81#include "jsobj.h"
  82#include "jsparse.h"
  83#include "jsscan.h"
  84#include "jsscope.h"
  85#include "jsscript.h"
  86#include "jsstr.h"
  87#include "jsutil.h" /* Added by JSIFY */
  88#include <string.h>
  89
  90/* NSPR dependencies */
  91#include "prio.h"
  92#include "prerror.h"
  93
  94#define SPECIAL_FILE_STRING     "Special File"
  95#define CURRENTDIR_PROPERTY     "currentDir"
  96#define SEPARATOR_PROPERTY      "separator"
  97#define FILE_CONSTRUCTOR        "File"
  98#define PIPE_SYMBOL             '|'
  99
 100#define ASCII                   0
 101#define UTF8                    1
 102#define UCS2                    2
 103
 104#define asciistring             "text"
 105#define utfstring               "binary"
 106#define unicodestring           "unicode"
 107
 108#define MAX_PATH_LENGTH         1024
 109#define MODE_SIZE               256
 110#define NUMBER_SIZE             32
 111#define MAX_LINE_LENGTH         256
 112#define URL_PREFIX              "file://"
 113
 114#define STDINPUT_NAME           "Standard input stream"
 115#define STDOUTPUT_NAME          "Standard output stream"
 116#define STDERROR_NAME           "Standard error stream"
 117
 118#define RESOLVE_PATH            js_canonicalPath        /* js_absolutePath */
 119
 120/* Error handling */
 121typedef enum JSFileErrNum {
 122#define MSG_DEF(name, number, count, exception, format) \
 123    name = number,
 124#include "jsfile.msg"
 125#undef MSG_DEF
 126    JSFileErr_Limit
 127#undef MSGDEF
 128} JSFileErrNum;
 129
 130#define JSFILE_HAS_DFLT_MSG_STRINGS 1
 131
 132JSErrorFormatString JSFile_ErrorFormatString[JSFileErr_Limit] = {
 133#if JSFILE_HAS_DFLT_MSG_STRINGS
 134#define MSG_DEF(name, number, count, exception, format) \
 135    { format, count },
 136#else
 137#define MSG_DEF(name, number, count, exception, format) \
 138    { NULL, count },
 139#endif
 140#include "jsfile.msg"
 141#undef MSG_DEF
 142};
 143
 144const JSErrorFormatString *
 145JSFile_GetErrorMessage(void *userRef, const char *locale,
 146                                                        const uintN errorNumber)
 147{
 148    if ((errorNumber > 0) && (errorNumber < JSFileErr_Limit))
 149        return &JSFile_ErrorFormatString[errorNumber];
 150    else
 151        return NULL;
 152}
 153
 154#define JSFILE_CHECK_NATIVE(op)                                               \
 155    if (file->isNative) {                                                     \
 156        JS_ReportWarning(cx, "Cannot call or access \"%s\" on native file %s",\
 157                         op, file->path);                                     \
 158        goto out;                                                             \
 159    }
 160
 161#define JSFILE_CHECK_WRITE                                                    \
 162    if (!file->isOpen) {                                                      \
 163        JS_ReportWarning(cx,                                                  \
 164                "File %s is closed, will open it for writing, proceeding",    \
 165                file->path);                                                  \
 166        js_FileOpen(cx, obj, file, "write,append,create");                    \
 167    }                                                                         \
 168    if (!js_canWrite(cx, file)) {                                             \
 169        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
 170                             JSFILEMSG_CANNOT_WRITE, file->path);             \
 171        goto out;                                                             \
 172    }
 173
 174#define JSFILE_CHECK_READ                                                     \
 175    if (!file->isOpen) {                                                      \
 176        JS_ReportWarning(cx,                                                  \
 177                "File %s is closed, will open it for reading, proceeding",    \
 178                file->path);                                                  \
 179        js_FileOpen(cx, obj, file, "read");                                   \
 180    }                                                                         \
 181    if (!js_canRead(cx, file)) {                                              \
 182        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
 183                             JSFILEMSG_CANNOT_READ, file->path);              \
 184        goto out;                                                             \
 185    }
 186
 187#define JSFILE_CHECK_OPEN(op)                                                 \
 188    if (!file->isOpen) {                                                      \
 189        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
 190                             JSFILEMSG_FILE_MUST_BE_OPEN, op);                \
 191        goto out;                                                             \
 192    }
 193
 194#define JSFILE_CHECK_CLOSED(op)                                               \
 195    if (file->isOpen) {                                                       \
 196        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
 197                             JSFILEMSG_FILE_MUST_BE_CLOSED, op);              \
 198        goto out;                                                             \
 199    }
 200
 201#define JSFILE_CHECK_ONE_ARG(op)                                              \
 202    if (argc != 1) {                                                          \
 203        char str[NUMBER_SIZE];                                                \
 204        sprintf(str, "%d", argc);                                             \
 205        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,                \
 206                             JSFILEMSG_EXPECTS_ONE_ARG_ERROR, op, str);       \
 207        goto out;                                                             \
 208    }
 209
 210
 211/*
 212    Security mechanism, should define a callback for this.
 213    The parameters are as follows:
 214    SECURITY_CHECK(JSContext *cx, JSPrincipals *ps, char *op_name, JSFile *file)
 215    XXX Should this be a real function returning a JSBool result (and getting
 216    some typesafety help from the compiler?).
 217*/
 218#define SECURITY_CHECK(cx, ps, op, file)    \
 219        /* Define a callback here... */
 220
 221
 222/* Structure representing the file internally */
 223typedef struct JSFile {
 224    char        *path;          /* the path to the file. */
 225    JSBool      isOpen;
 226    int32       mode;           /* mode used to open the file: read, write, append, create, etc.. */
 227    int32       type;           /* Asciiz, utf, unicode */
 228    char        byteBuffer[3];  /* bytes read in advance by js_FileRead ( UTF8 encoding ) */
 229    jsint       nbBytesInBuf;   /* number of bytes stored in the buffer above */
 230    jschar      charBuffer;     /* character read in advance by readln ( mac files only ) */
 231    JSBool      charBufferUsed; /* flag indicating if the buffer above is being used */
 232    JSBool      hasRandomAccess;/* can the file be randomly accessed? false for stdin, and
 233                                 UTF-encoded files. */
 234    JSBool      hasAutoflush;   /* should we force a flush for each line break? */
 235    JSBool      isNative;       /* if the file is using OS-specific file FILE type */
 236    /* We can actually put the following two in a union since they should never be used at the same time */
 237    PRFileDesc  *handle;        /* the handle for the file, if open.  */
 238    FILE        *nativehandle;  /* native handle, for stuff NSPR doesn't do. */
 239    JSBool      isPipe;         /* if the file is really an OS pipe */
 240} JSFile;
 241
 242/* a few forward declarations... */
 243JS_PUBLIC_API(JSObject*) js_NewFileObject(JSContext *cx, char *filename);
 244static JSBool file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 245static JSBool file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval);
 246
 247/* New filename manipulation procesures */
 248/* assumes we don't have leading/trailing spaces */
 249static JSBool
 250js_filenameHasAPipe(const char *filename)
 251{
 252    if (!filename)
 253        return JS_FALSE;
 254
 255    return  filename[0] == PIPE_SYMBOL ||
 256            filename[strlen(filename) - 1] == PIPE_SYMBOL;
 257}
 258
 259static JSBool
 260js_isAbsolute(const char *name)
 261{
 262#if defined(XP_WIN) || defined(XP_OS2)
 263    return *name && name[1] == ':';
 264#else
 265    return (name[0]
 266#   if defined(XP_UNIX) || defined(XP_BEOS)
 267            ==
 268#   else
 269            !=
 270#   endif
 271            FILESEPARATOR);
 272#endif
 273}
 274
 275/*
 276 * Concatenates base and name to produce a valid filename.
 277 * Returned string must be freed.
 278*/
 279static char*
 280js_combinePath(JSContext *cx, const char *base, const char *name)
 281{
 282    int len = strlen(base);
 283    char* result = JS_malloc(cx, len + strlen(name) + 2);
 284
 285    if (!result)
 286        return NULL;
 287
 288    strcpy(result, base);
 289
 290    if (base[len - 1] != FILESEPARATOR && base[len - 1] != FILESEPARATOR2) {
 291        result[len] = FILESEPARATOR;
 292        result[len + 1] = '\0';
 293    }
 294    strcat(result, name);
 295    return result;
 296}
 297
 298/* Extract the last component from a path name. Returned string must be freed */
 299static char *
 300js_fileBaseName(JSContext *cx, const char *pathname)
 301{
 302    jsint index, aux;
 303    char *result;
 304
 305    index = strlen(pathname)-1;
 306
 307    /* Chop off trailing seperators. */
 308    while (index > 0 && (pathname[index]==FILESEPARATOR ||
 309                         pathname[index]==FILESEPARATOR2)) {
 310        --index;
 311    }
 312
 313    aux = index;
 314
 315    /* Now find the next separator. */
 316    while (index >= 0 && pathname[index] != FILESEPARATOR &&
 317                         pathname[index] != FILESEPARATOR2) {
 318        --index;
 319    }
 320
 321    /* Allocate and copy. */
 322    result = JS_malloc(cx, aux - index + 1);
 323    if (!result)
 324        return NULL;
 325    strncpy(result, pathname + index + 1, aux - index);
 326    result[aux - index] = '\0';
 327    return result;
 328}
 329
 330/*
 331 * Returns everything but the last component from a path name.
 332 * Returned string must be freed.
 333 */
 334static char *
 335js_fileDirectoryName(JSContext *cx, const char *pathname)
 336{
 337    char *result;
 338    const char *cp, *end;
 339    size_t pathsize;
 340
 341    end = pathname + strlen(pathname);
 342    cp = end - 1;
 343
 344    /* If this is already a directory, chop off the trailing /s. */
 345    while (cp >= pathname) {
 346        if (*cp != FILESEPARATOR && *cp != FILESEPARATOR2)
 347            break;
 348        --cp;
 349    }
 350
 351    if (cp < pathname && end != pathname) {
 352        /* There were just /s, return the root. */
 353        result = JS_malloc(cx, 1 + 1); /* The separator + trailing NUL. */
 354        result[0] = FILESEPARATOR;
 355        result[1] = '\0';
 356        return result;
 357    }
 358
 359    /* Now chop off the last portion. */
 360    while (cp >= pathname) {
 361        if (*cp == FILESEPARATOR || *cp == FILESEPARATOR2)
 362            break;
 363        --cp;
 364    }
 365
 366    /* Check if this is a leaf. */
 367    if (cp < pathname) {
 368        /* It is, return "pathname/". */
 369        if (end[-1] == FILESEPARATOR || end[-1] == FILESEPARATOR2) {
 370            /* Already has its terminating /. */
 371            return JS_strdup(cx, pathname);
 372        }
 373
 374        pathsize = end - pathname + 1;
 375        result = JS_malloc(cx, pathsize + 1);
 376        if (!result)
 377            return NULL;
 378
 379        strcpy(result, pathname);
 380        result[pathsize - 1] = FILESEPARATOR;
 381        result[pathsize] = '\0';
 382
 383        return result;
 384    }
 385
 386    /* Return everything up to and including the seperator. */
 387    pathsize = cp - pathname + 1;
 388    result = JS_malloc(cx, pathsize + 1);
 389    if (!result)
 390        return NULL;
 391
 392    strncpy(result, pathname, pathsize);
 393    result[pathsize] = '\0';
 394
 395    return result;
 396}
 397
 398static char *
 399js_absolutePath(JSContext *cx, const char * path)
 400{
 401    JSObject *obj;
 402    JSString *str;
 403    jsval prop;
 404
 405    if (js_isAbsolute(path)) {
 406        return JS_strdup(cx, path);
 407    } else {
 408        obj = JS_GetGlobalObject(cx);
 409        if (!JS_GetProperty(cx, obj, FILE_CONSTRUCTOR, &prop)) {
 410            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
 411                                 JSFILEMSG_FILE_CONSTRUCTOR_UNDEFINED_ERROR);
 412            return JS_strdup(cx, path);
 413        }
 414
 415        obj = JSVAL_TO_OBJECT(prop);
 416        if (!JS_GetProperty(cx, obj, CURRENTDIR_PROPERTY, &prop)) {
 417            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
 418                                 JSFILEMSG_FILE_CURRENTDIR_UNDEFINED_ERROR);
 419            return JS_strdup(cx, path);
 420        }
 421
 422        str = JS_ValueToString(cx, prop);
 423        if (!str)
 424            return JS_strdup(cx, path);
 425
 426        /* should we have an array of curr dirs indexed by drive for windows? */
 427        return js_combinePath(cx, JS_GetStringBytes(str), path);
 428    }
 429}
 430
 431/* Side effect: will remove spaces in the beginning/end of the filename */
 432static char *
 433js_canonicalPath(JSContext *cx, char *oldpath)
 434{
 435    char *tmp;
 436    char *path = oldpath;
 437    char *base, *dir, *current, *result;
 438    jsint c;
 439    jsint back = 0;
 440    unsigned int i = 0, j = strlen(path)-1;
 441
 442    /* This is probably optional */
 443	/* Remove possible spaces in the beginning and end */
 444    while (i < j && path[i] == ' ')
 445        i++;
 446    while (j >= 0 && path[j] == ' ')
 447        j--;
 448
 449    tmp = JS_malloc(cx, j-i+2);
 450    if (!tmp)
 451        return NULL;
 452
 453    strncpy(tmp, path + i, j - i + 1);
 454    tmp[j - i + 1] = '\0';
 455
 456    path = tmp;
 457
 458    /* Pipe support. */
 459    if (js_filenameHasAPipe(path))
 460        return path;
 461
 462    /* file:// support. */
 463    if (!strncmp(path, URL_PREFIX, strlen(URL_PREFIX))) {
 464        tmp = js_canonicalPath(cx, path + strlen(URL_PREFIX));
 465        JS_free(cx, path);
 466        return tmp;
 467    }
 468
 469    if (!js_isAbsolute(path)) {
 470        tmp = js_absolutePath(cx, path);
 471        if (!tmp)
 472            return NULL;
 473        JS_free(cx, path);
 474        path = tmp;
 475    }
 476
 477    result = JS_strdup(cx, "");
 478
 479    current = path;
 480
 481    base = js_fileBaseName(cx, current);
 482    dir = js_fileDirectoryName(cx, current);
 483
 484    while (strcmp(dir, current)) {
 485        if (!strcmp(base, "..")) {
 486            back++;
 487        } else {
 488            if (back > 0) {
 489                back--;
 490            } else {
 491                tmp = result;
 492                result = JS_malloc(cx, strlen(base) + 1 + strlen(tmp) + 1);
 493                if (!result)
 494                    goto out;
 495
 496                strcpy(result, base);
 497                c = strlen(result);
 498                if (*tmp) {
 499                    result[c] = FILESEPARATOR;
 500                    result[c + 1] = '\0';
 501                    strcat(result, tmp);
 502                }
 503                JS_free(cx, tmp);
 504            }
 505        }
 506        JS_free(cx, current);
 507        JS_free(cx, base);
 508        current = dir;
 509        base =  js_fileBaseName(cx, current);
 510        dir = js_fileDirectoryName(cx, current);
 511    }
 512
 513    tmp = result;
 514    result = JS_malloc(cx, strlen(dir)+1+strlen(tmp)+1);
 515    if (!result)
 516        goto out;
 517
 518    strcpy(result, dir);
 519    c = strlen(result);
 520    if (tmp[0]!='\0') {
 521        if ((result[c-1]!=FILESEPARATOR)&&(result[c-1]!=FILESEPARATOR2)) {
 522            result[c] = FILESEPARATOR;
 523            result[c+1] = '\0';
 524        }
 525        strcat(result, tmp);
 526    }
 527
 528out:
 529    if (tmp)
 530        JS_free(cx, tmp);
 531    if (dir)
 532        JS_free(cx, dir);
 533    if (base)
 534        JS_free(cx, base);
 535    if (current)
 536        JS_free(cx, current);
 537
 538    return result;
 539}
 540
 541/* -------------------------- Text conversion ------------------------------- */
 542/* The following is ripped from libi18n/unicvt.c and include files.. */
 543
 544/*
 545 * UTF8 defines and macros
 546 */
 547#define ONE_OCTET_BASE          0x00    /* 0xxxxxxx */
 548#define ONE_OCTET_MASK          0x7F    /* x1111111 */
 549#define CONTINUING_OCTET_BASE   0x80    /* 10xxxxxx */
 550#define CONTINUING_OCTET_MASK   0x3F    /* 00111111 */
 551#define TWO_OCTET_BASE          0xC0    /* 110xxxxx */
 552#define TWO_OCTET_MASK          0x1F    /* 00011111 */
 553#define THREE_OCTET_BASE        0xE0    /* 1110xxxx */
 554#define THREE_OCTET_MASK        0x0F    /* 00001111 */
 555#define FOUR_OCTET_BASE         0xF0    /* 11110xxx */
 556#define FOUR_OCTET_MASK         0x07    /* 00000111 */
 557#define FIVE_OCTET_BASE         0xF8    /* 111110xx */
 558#define FIVE_OCTET_MASK         0x03    /* 00000011 */
 559#define SIX_OCTET_BASE          0xFC    /* 1111110x */
 560#define SIX_OCTET_MASK          0x01    /* 00000001 */
 561
 562#define IS_UTF8_1ST_OF_1(x) (( (x)&~ONE_OCTET_MASK  ) == ONE_OCTET_BASE)
 563#define IS_UTF8_1ST_OF_2(x) (( (x)&~TWO_OCTET_MASK  ) == TWO_OCTET_BASE)
 564#define IS_UTF8_1ST_OF_3(x) (( (x)&~THREE_OCTET_MASK) == THREE_OCTET_BASE)
 565#define IS_UTF8_1ST_OF_4(x) (( (x)&~FOUR_OCTET_MASK ) == FOUR_OCTET_BASE)
 566#define IS_UTF8_1ST_OF_5(x) (( (x)&~FIVE_OCTET_MASK ) == FIVE_OCTET_BASE)
 567#define IS_UTF8_1ST_OF_6(x) (( (x)&~SIX_OCTET_MASK  ) == SIX_OCTET_BASE)
 568#define IS_UTF8_2ND_THRU_6TH(x) \
 569                    (( (x)&~CONTINUING_OCTET_MASK  ) == CONTINUING_OCTET_BASE)
 570#define IS_UTF8_1ST_OF_UCS2(x) \
 571            IS_UTF8_1ST_OF_1(x) \
 572            || IS_UTF8_1ST_OF_2(x) \
 573            || IS_UTF8_1ST_OF_3(x)
 574
 575
 576#define MAX_UCS2            0xFFFF
 577#define DEFAULT_CHAR        0x003F  /* Default char is "?" */
 578#define BYTE_MASK           0xBF
 579#define BYTE_MARK           0x80
 580
 581
 582/* Function: one_ucs2_to_utf8_char
 583 *
 584 * Function takes one UCS-2 char and writes it to a UTF-8 buffer.
 585 * We need a UTF-8 buffer because we don't know before this
 586 * function how many bytes of utf-8 data will be written. It also
 587 * takes a pointer to the end of the UTF-8 buffer so that we don't
 588 * overwrite data. This function returns the number of UTF-8 bytes
 589 * of data written, or -1 if the buffer would have been overrun.
 590 */
 591
 592#define LINE_SEPARATOR      0x2028
 593#define PARAGRAPH_SEPARATOR 0x2029
 594static int16 one_ucs2_to_utf8_char(unsigned char *tobufp,
 595                                   unsigned char *tobufendp,
 596                                   uint16 onechar)
 597{
 598    int16 numUTF8bytes = 0;
 599
 600    if (onechar == LINE_SEPARATOR || onechar == PARAGRAPH_SEPARATOR) {
 601        strcpy((char*)tobufp, "\n");
 602        return strlen((char*)tobufp);
 603    }
 604
 605    if (onechar < 0x80) {
 606        numUTF8bytes = 1;
 607    } else if (onechar < 0x800) {
 608        numUTF8bytes = 2;
 609    } else {
 610        /* 0x800 >= onechar <= MAX_UCS2 */
 611        numUTF8bytes = 3;
 612    }
 613
 614    tobufp += numUTF8bytes;
 615
 616    /* return error if we don't have space for the whole character */
 617    if (tobufp > tobufendp) {
 618        return(-1);
 619    }
 620
 621    switch(numUTF8bytes) {
 622      case 3: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
 623              *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
 624              *--tobufp = onechar |  THREE_OCTET_BASE;
 625              break;
 626
 627      case 2: *--tobufp = (onechar | BYTE_MARK) & BYTE_MASK; onechar >>=6;
 628              *--tobufp = onechar | TWO_OCTET_BASE;
 629              break;
 630
 631      case 1: *--tobufp = (unsigned char)onechar;
 632              break;
 633    }
 634
 635    return numUTF8bytes;
 636}
 637
 638/*
 639 * utf8_to_ucs2_char
 640 *
 641 * Convert a utf8 multibyte character to ucs2
 642 *
 643 * inputs: pointer to utf8 character(s)
 644 *         length of utf8 buffer ("read" length limit)
 645 *         pointer to return ucs2 character
 646 *
 647 * outputs: number of bytes in the utf8 character
 648 *          -1 if not a valid utf8 character sequence
 649 *          -2 if the buffer is too short
 650 */
 651static int16
 652utf8_to_ucs2_char(const unsigned char *utf8p, int16 buflen, uint16 *ucs2p)
 653{
 654    uint16 lead, cont1, cont2;
 655
 656    /*
 657     * Check for minimum buffer length
 658     */
 659    if ((buflen < 1) || (utf8p == NULL)) {
 660        return -2;
 661    }
 662    lead = (uint16) (*utf8p);
 663
 664    /*
 665     * Check for a one octet sequence
 666     */
 667    if (IS_UTF8_1ST_OF_1(lead)) {
 668        *ucs2p = lead & ONE_OCTET_MASK;
 669        return 1;
 670    }
 671
 672    /*
 673     * Check for a two octet sequence
 674     */
 675    if (IS_UTF8_1ST_OF_2(*utf8p)) {
 676        if (buflen < 2)
 677            return -2;
 678        cont1 = (uint16) *(utf8p+1);
 679        if (!IS_UTF8_2ND_THRU_6TH(cont1))
 680            return -1;
 681        *ucs2p =  (lead & TWO_OCTET_MASK) << 6;
 682        *ucs2p |= cont1 & CONTINUING_OCTET_MASK;
 683        return 2;
 684    }
 685
 686    /*
 687     * Check for a three octet sequence
 688     */
 689    else if (IS_UTF8_1ST_OF_3(lead)) {
 690        if (buflen < 3)
 691            return -2;
 692        cont1 = (uint16) *(utf8p+1);
 693        cont2 = (uint16) *(utf8p+2);
 694        if (   (!IS_UTF8_2ND_THRU_6TH(cont1))
 695            || (!IS_UTF8_2ND_THRU_6TH(cont2)))
 696            return -1;
 697        *ucs2p =  (lead & THREE_OCTET_MASK) << 12;
 698        *ucs2p |= (cont1 & CONTINUING_OCTET_MASK) << 6;
 699        *ucs2p |= cont2 & CONTINUING_OCTET_MASK;
 700        return 3;
 701    }
 702    else { /* not a valid utf8/ucs2 character */
 703        return -1;
 704    }
 705}
 706
 707/* ----------------------------- Helper functions --------------------------- */
 708/* Ripped off from lm_win.c .. */
 709/* where is strcasecmp?.. for now, it's case sensitive..
 710 *
 711 * strcasecmp is in strings.h, but on windows it's called _stricmp...
 712 * will need to #ifdef this
 713*/
 714
 715static int32
 716js_FileHasOption(JSContext *cx, const char *oldoptions, const char *name)
 717{
 718    char *comma, *equal, *current;
 719    char *options = JS_strdup(cx, oldoptions);
 720    int32 found = 0;
 721
 722    current = options;
 723    for (;;) {
 724        comma = strchr(current, ',');
 725        if (comma) *comma = '\0';
 726        equal = strchr(current, '=');
 727        if (equal) *equal = '\0';
 728        if (strcmp(current, name) == 0) {
 729            if (!equal || strcmp(equal + 1, "yes") == 0)
 730                found = 1;
 731            else
 732                found = atoi(equal + 1);
 733        }
 734        if (equal) *equal = '=';
 735        if (comma) *comma = ',';
 736        if (found || !comma)
 737            break;
 738        current = comma + 1;
 739    }
 740    JS_free(cx, options);
 741    return found;
 742}
 743
 744/* empty the buffer */
 745static void
 746js_ResetBuffers(JSFile * file)
 747{
 748    file->charBufferUsed = JS_FALSE;
 749    file->nbBytesInBuf = 0;
 750}
 751
 752/* Reset file attributes */
 753static void
 754js_ResetAttributes(JSFile * file)
 755{
 756    file->mode = file->type = 0;
 757    file->isOpen = JS_FALSE;
 758    file->handle = NULL;
 759    file->nativehandle = NULL;
 760    file->hasRandomAccess = JS_TRUE; /* Innocent until proven guilty. */
 761    file->hasAutoflush = JS_FALSE;
 762    file->isNative = JS_FALSE;
 763    file->isPipe = JS_FALSE;
 764
 765    js_ResetBuffers(file);
 766}
 767
 768static JSBool
 769js_FileOpen(JSContext *cx, JSObject *obj, JSFile *file, char *mode){
 770    JSString *type, *mask;
 771    jsval v[2];
 772    jsval rval;
 773
 774    type =  JS_InternString(cx, asciistring);
 775    mask =  JS_NewStringCopyZ(cx, mode);
 776    v[0] = STRING_TO_JSVAL(mask);
 777    v[1] = STRING_TO_JSVAL(type);
 778
 779    if (!file_open(cx, obj, 2, v, &rval))
 780        return JS_FALSE;
 781    return JS_TRUE;
 782}
 783
 784/* Buffered version of PR_Read. Used by js_FileRead */
 785static int32
 786js_BufferedRead(JSFile *f, unsigned char *buf, int32 len)
 787{
 788    int32 count = 0;
 789
 790    while (f->nbBytesInBuf>0&&len>0) {
 791        buf[0] = f->byteBuffer[0];
 792        f->byteBuffer[0] = f->byteBuffer[1];
 793        f->byteBuffer[1] = f->byteBuffer[2];
 794        f->nbBytesInBuf--;
 795        len--;
 796        buf+=1;
 797        count++;
 798    }
 799
 800    if (len > 0) {
 801        count += (!f->isNative)
 802                 ? PR_Read(f->handle, buf, len)
 803                 : fread(buf, 1, len, f->nativehandle);
 804    }
 805    return count;
 806}
 807
 808static int32
 809js_FileRead(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
 810{
 811    unsigned char *aux;
 812    int32 count = 0, i;
 813    jsint remainder;
 814    unsigned char utfbuf[3];
 815
 816    if (file->charBufferUsed) {
 817        buf[0] = file->charBuffer;
 818        buf++;
 819        len--;
 820        file->charBufferUsed = JS_FALSE;
 821    }
 822
 823    switch (mode) {
 824      case ASCII:
 825        aux = (unsigned char*)JS_malloc(cx, len);
 826        if (!aux)
 827            return 0;
 828
 829        count = js_BufferedRead(file, aux, len);
 830        if (count == -1) {
 831            JS_free(cx, aux);
 832            return 0;
 833        }
 834
 835        for (i = 0; i < len; i++)
 836            buf[i] = (jschar)aux[i];
 837
 838        JS_free(cx, aux);
 839        break;
 840
 841      case UTF8:
 842        remainder = 0;
 843        for (count = 0;count<len;count++) {
 844            i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
 845            if (i<=0) {
 846                return count;
 847            }
 848            i = utf8_to_ucs2_char(utfbuf, (int16)i, &buf[count] );
 849            if (i<0) {
 850                return count;
 851            } else {
 852                if (i==1) {
 853                    utfbuf[0] = utfbuf[1];
 854                    utfbuf[1] = utfbuf[2];
 855                    remainder = 2;
 856                } else if (i==2) {
 857                    utfbuf[0] = utfbuf[2];
 858                    remainder = 1;
 859                } else if (i==3) {
 860                    remainder = 0;
 861                }
 862            }
 863        }
 864        while (remainder>0) {
 865            file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
 866            file->nbBytesInBuf++;
 867            utfbuf[0] = utfbuf[1];
 868            utfbuf[1] = utfbuf[2];
 869            remainder--;
 870        }
 871        break;
 872
 873      case UCS2:
 874        count = js_BufferedRead(file, (unsigned char *)buf, len * 2) >> 1;
 875        if (count == -1)
 876            return 0;
 877
 878        break;
 879
 880      default:
 881        /* Not reached. */
 882        JS_ASSERT(0);
 883    }
 884
 885    if(count == -1) {
 886        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
 887                             JSFILEMSG_OP_FAILED, "read", file->path);
 888    }
 889
 890    return count;
 891}
 892
 893static int32
 894js_FileSeek(JSContext *cx, JSFile *file, int32 len, int32 mode)
 895{
 896    int32 count = 0, i;
 897    jsint remainder;
 898    unsigned char utfbuf[3];
 899    jschar tmp;
 900
 901    switch (mode) {
 902      case ASCII:
 903        count = PR_Seek(file->handle, len, PR_SEEK_CUR);
 904        break;
 905
 906      case UTF8:
 907        remainder = 0;
 908        for (count = 0;count<len;count++) {
 909            i = js_BufferedRead(file, utfbuf+remainder, 3-remainder);
 910            if (i<=0) {
 911                return 0;
 912            }
 913            i = utf8_to_ucs2_char(utfbuf, (int16)i, &tmp );
 914            if (i<0) {
 915                return 0;
 916            } else {
 917                if (i==1) {
 918                    utfbuf[0] = utfbuf[1];
 919                    utfbuf[1] = utfbuf[2];
 920                    remainder = 2;
 921                } else if (i==2) {
 922                    utfbuf[0] = utfbuf[2];
 923                    remainder = 1;
 924                } else if (i==3) {
 925                    remainder = 0;
 926                }
 927            }
 928        }
 929        while (remainder>0) {
 930            file->byteBuffer[file->nbBytesInBuf] = utfbuf[0];
 931            file->nbBytesInBuf++;
 932            utfbuf[0] = utfbuf[1];
 933            utfbuf[1] = utfbuf[2];
 934            remainder--;
 935        }
 936        break;
 937
 938      case UCS2:
 939        count = PR_Seek(file->handle, len*2, PR_SEEK_CUR)/2;
 940        break;
 941
 942      default:
 943        /* Not reached. */
 944        JS_ASSERT(0);
 945    }
 946
 947    if(count == -1) {
 948        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
 949                             JSFILEMSG_OP_FAILED, "seek", file->path);
 950    }
 951
 952    return count;
 953}
 954
 955static int32
 956js_FileWrite(JSContext *cx, JSFile *file, jschar *buf, int32 len, int32 mode)
 957{
 958    unsigned char   *aux;
 959    int32           count = 0, i, j;
 960    unsigned char   *utfbuf;
 961
 962    switch (mode) {
 963      case ASCII:
 964        aux = (unsigned char*)JS_malloc(cx, len);
 965        if (!aux)
 966            return 0;
 967
 968        for (i = 0; i<len; i++)
 969            aux[i] = buf[i] % 256;
 970
 971        count = (!file->isNative)
 972                ? PR_Write(file->handle, aux, len)
 973                : fwrite(aux, 1, len, file->nativehandle);
 974
 975        if (count==-1) {
 976            JS_free(cx, aux);
 977            return 0;
 978        }
 979
 980        JS_free(cx, aux);
 981        break;
 982
 983      case UTF8:
 984        utfbuf = (unsigned char*)JS_malloc(cx, len*3);
 985        if (!utfbuf)  return 0;
 986        i = 0;
 987        for (count = 0;count<len;count++) {
 988            j = one_ucs2_to_utf8_char(utfbuf+i, utfbuf+len*3, buf[count]);
 989            if (j==-1) {
 990                JS_free(cx, utfbuf);
 991                return 0;
 992            }
 993            i+=j;
 994        }
 995        j = (!file->isNative)
 996            ? PR_Write(file->handle, utfbuf, i)
 997            : fwrite(utfbuf, 1, i, file->nativehandle);
 998
 999        if (j<i) {
1000            JS_free(cx, utfbuf);
1001            return 0;
1002        }
1003        JS_free(cx, utfbuf);
1004        break;
1005
1006      case UCS2:
1007        count = (!file->isNative)
1008                ? PR_Write(file->handle, buf, len*2) >> 1
1009                : fwrite(buf, 1, len*2, file->nativehandle) >> 1;
1010
1011        if (count == -1)
1012            return 0;
1013        break;
1014
1015      default:
1016        /* Not reached. */
1017        JS_ASSERT(0);
1018    }
1019
1020    if(count == -1) {
1021        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1022                             JSFILEMSG_OP_FAILED, "write", file->path);
1023    }
1024
1025    return count;
1026}
1027
1028/* ----------------------------- Property checkers -------------------------- */
1029static JSBool
1030js_exists(JSContext *cx, JSFile *file)
1031{
1032    if (file->isNative) {
1033        /* It doesn't make sense for a pipe of stdstream. */
1034        return JS_FALSE;
1035    }
1036
1037    return PR_Access(file->path, PR_ACCESS_EXISTS) == PR_SUCCESS;
1038}
1039
1040static JSBool
1041js_canRead(JSContext *cx, JSFile *file)
1042{
1043    if (!file->isNative) {
1044        if (file->isOpen && !(file->mode & PR_RDONLY))
1045            return JS_FALSE;
1046        return PR_Access(file->path, PR_ACCESS_READ_OK) == PR_SUCCESS;
1047    }
1048
1049    if (file->isPipe) {
1050        /* Is this pipe open for reading? */
1051        return file->path[0] == PIPE_SYMBOL;
1052    }
1053
1054    return !strcmp(file->path, STDINPUT_NAME);
1055}
1056
1057static JSBool
1058js_canWrite(JSContext *cx, JSFile *file)
1059{
1060    if (!file->isNative) {
1061        if (file->isOpen && !(file->mode & PR_WRONLY))
1062            return JS_FALSE;
1063        return PR_Access(file->path, PR_ACCESS_WRITE_OK) == PR_SUCCESS;
1064    }
1065
1066    if(file->isPipe) {
1067        /* Is this pipe open for writing? */
1068        return file->path[strlen(file->path)-1] == PIPE_SYMBOL;
1069    }
1070
1071    return !strcmp(file->path, STDOUTPUT_NAME) ||
1072           !strcmp(file->path, STDERROR_NAME);
1073}
1074
1075static JSBool
1076js_isFile(JSContext *cx, JSFile *file)
1077{
1078    if (!file->isNative) {
1079        PRFileInfo info;
1080
1081        if (file->isOpen
1082            ? PR_GetOpenFileInfo(file->handle, &info)
1083            : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1084            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1085                                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1086            return JS_FALSE;
1087        }
1088
1089        return info.type == PR_FILE_FILE;
1090    }
1091
1092    /* This doesn't make sense for a pipe of stdstream. */
1093    return JS_FALSE;
1094}
1095
1096static JSBool
1097js_isDirectory(JSContext *cx, JSFile *file)
1098{
1099    if(!file->isNative){
1100        PRFileInfo info;
1101
1102        /* Hack needed to get get_property to work. */
1103        if (!js_exists(cx, file))
1104            return JS_FALSE;
1105
1106        if (file->isOpen
1107            ? PR_GetOpenFileInfo(file->handle, &info)
1108            : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1109            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1110                                 JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1111            return JS_FALSE;
1112        }
1113
1114        return info.type == PR_FILE_DIRECTORY;
1115    }
1116
1117    /* This doesn't make sense for a pipe of stdstream. */
1118    return JS_FALSE;
1119}
1120
1121static jsval
1122js_size(JSContext *cx, JSFile *file)
1123{
1124    PRFileInfo info;
1125
1126    JSFILE_CHECK_NATIVE("size");
1127
1128    if (file->isOpen
1129        ? PR_GetOpenFileInfo(file->handle, &info)
1130        : PR_GetFileInfo(file->path, &info) != PR_SUCCESS) {
1131        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1132                             JSFILEMSG_CANNOT_ACCESS_FILE_STATUS, file->path);
1133        return JSVAL_VOID;
1134    }
1135
1136    return INT_TO_JSVAL(info.size);
1137
1138out:
1139    return JSVAL_VOID;
1140}
1141
1142/*
1143 * Return the parent object
1144 */
1145static JSBool
1146js_parent(JSContext *cx, JSFile *file, jsval *resultp)
1147{
1148    char *str;
1149
1150    /* Since we only care about pipes and native files, return NULL. */
1151    if (file->isNative) {
1152        *resultp = JSVAL_VOID;
1153        return JS_TRUE;
1154    }
1155
1156    str = js_fileDirectoryName(cx, file->path);
1157    if (!str)
1158        return JS_FALSE;
1159
1160    /* If the directory is equal to the original path, we're at the root. */
1161    if (!strcmp(file->path, str)) {
1162        *resultp = JSVAL_NULL;
1163    } else {
1164        JSObject *obj = js_NewFileObject(cx, str);
1165        if (!obj) {
1166            JS_free(cx, str);
1167            return JS_FALSE;
1168        }
1169        *resultp = OBJECT_TO_JSVAL(obj);
1170    }
1171
1172    JS_free(cx, str);
1173    return JS_TRUE;
1174}
1175
1176static JSBool
1177js_name(JSContext *cx, JSFile *file, jsval *vp)
1178{
1179    char *name;
1180    JSString *str;
1181
1182    if (file->isPipe) {
1183        *vp = JSVAL_VOID;
1184        return JS_TRUE;
1185    }
1186
1187    name = js_fileBaseName(cx, file->path);
1188    if (!name)
1189        return JS_FALSE;
1190
1191    str = JS_NewString(cx, name, strlen(name));
1192    if (!str) {
1193        JS_free(cx, name);
1194        return JS_FALSE;
1195    }
1196
1197    *vp = STRING_TO_JSVAL(str);
1198    return JS_TRUE;
1199}
1200
1201/* ------------------------------ File object methods ---------------------------- */
1202static JSBool
1203file_open(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1204{
1205    JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1206    JSString	*strmode, *strtype;
1207    char        *ctype, *mode;
1208    int32       mask, type;
1209    int         len;
1210
1211    mode = NULL;
1212
1213    SECURITY_CHECK(cx, NULL, "open", file);
1214
1215    /* A native file that is already open */
1216    if(file->isOpen && file->isNative) {
1217        JS_ReportWarning(cx, "Native file %s is already open, proceeding",
1218                         file->path);
1219        goto good;
1220    }
1221
1222    /* Close before proceeding */
1223    if (file->isOpen) {
1224        JS_ReportWarning(cx, "File %s is already open, we will close it and "
1225                         "reopen, proceeding", file->path);
1226        if(!file_close(cx, obj, 0, NULL, rval))
1227            goto out;
1228    }
1229
1230    if (js_isDirectory(cx, file)) {
1231        JS_ReportWarning(cx, "%s seems to be a directory, there is no point in "
1232                         "trying to open it, proceeding", file->path);
1233        goto good;
1234    }
1235
1236    /* Path must be defined at this point */
1237    len = strlen(file->path);
1238
1239    /* Mode */
1240    if (argc >= 1) {
1241        strmode = JS_ValueToString(cx, argv[0]);
1242        if (!strmode) {
1243            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1244                                 JSFILEMSG_FIRST_ARGUMENT_OPEN_NOT_STRING_ERROR,
1245                                 argv[0]);
1246            goto out;
1247        }
1248        mode = JS_strdup(cx, JS_GetStringBytes(strmode));
1249    } else {
1250        if(file->path[0]==PIPE_SYMBOL) {
1251            /* pipe default mode */
1252            mode = JS_strdup(cx, "read");
1253        } else if(file->path[len-1]==PIPE_SYMBOL) {
1254            /* pipe default mode */
1255            mode = JS_strdup(cx, "write");
1256        } else {
1257            /* non-destructive, permissive defaults. */
1258            mode = JS_strdup(cx, "readWrite,append,create");
1259        }
1260    }
1261
1262    /* Process the mode */
1263    mask = 0;
1264    /* TODO: this is pretty ugly, we walk thru the string too many times */
1265    mask |= js_FileHasOption(cx, mode, "read")     ? PR_RDONLY       :   0;
1266    mask |= js_FileHasOption(cx, mode, "write")    ? PR_WRONLY       :   0;
1267    mask |= js_FileHasOption(cx, mode, "readWrite")? PR_RDWR         :   0;
1268    mask |= js_FileHasOption(cx, mode, "append")   ? PR_APPEND       :   0;
1269    mask |= js_FileHasOption(cx, mode, "create")   ? PR_CREATE_FILE  :   0;
1270    mask |= js_FileHasOption(cx, mode, "replace")  ? PR_TRUNCATE     :   0;
1271
1272    if (mask & PR_RDWR)
1273        mask |= (PR_RDONLY | PR_WRONLY);
1274    if ((mask & PR_RDONLY) && (mask & PR_WRONLY))
1275        mask |= PR_RDWR;
1276
1277    file->hasAutoflush |= js_FileHasOption(cx, mode, "autoflush");
1278
1279    /* Type */
1280    if (argc > 1) {
1281        strtype = JS_ValueToString(cx, argv[1]);
1282        if (!strtype) {
1283            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1284                                JSFILEMSG_SECOND_ARGUMENT_OPEN_NOT_STRING_ERROR,
1285                                 argv[1]);
1286            goto out;
1287        }
1288        ctype = JS_GetStringBytes(strtype);
1289
1290        if(!strcmp(ctype, utfstring)) {
1291            type = UTF8;
1292        } else if (!strcmp(ctype, unicodestring)) {
1293            type = UCS2;
1294        } else {
1295            if (strcmp(ctype, asciistring)) {
1296                JS_ReportWarning(cx, "File type %s is not supported, using "
1297                                 "'text' instead, proceeding", ctype);
1298            }
1299            type = ASCII;
1300        }
1301    } else {
1302        type = ASCII;
1303    }
1304
1305    /* Save the relevant fields */
1306    file->type = type;
1307    file->mode = mask;
1308    file->nativehandle = NULL;
1309    file->hasRandomAccess = (type != UTF8);
1310
1311    /*
1312     * Deal with pipes here. We can't use NSPR for pipes, so we have to use
1313     * POPEN.
1314     */
1315    if (file->path[0]==PIPE_SYMBOL || file->path[len-1]==PIPE_SYMBOL) {
1316        if (file->path[0] == PIPE_SYMBOL && file->path[len-1] == PIPE_SYMBOL) {
1317            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1318                                 JSFILEMSG_BIDIRECTIONAL_PIPE_NOT_SUPPORTED);
1319            goto out;
1320        } else {
1321            int i = 0;
1322            char pipemode[3];
1323            SECURITY_CHECK(cx, NULL, "pipe_open", file);
1324
1325            if(file->path[0] == PIPE_SYMBOL){
1326                if(mask & (PR_WRONLY | PR_APPEND | PR_CREATE_FILE | PR_TRUNCATE)){
1327                    JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1328                                   JSFILEMSG_OPEN_MODE_NOT_SUPPORTED_WITH_PIPES,
1329                                         mode, file->path);
1330                    goto out;
1331                }
1332                /* open(SPOOLER, "| cat -v | lpr -h 2>/dev/null") -- pipe for writing */
1333                pipemode[i++] = 'r';
1334#ifndef XP_UNIX
1335                pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1336#endif
1337                pipemode[i++] = '\0';
1338                file->nativehandle = POPEN(&file->path[1], pipemode);
1339            } else if(file->path[len-1] == PIPE_SYMBOL) {
1340                char *command = JS_malloc(cx, len);
1341
1342                strncpy(command, file->path, len-1);
1343                command[len-1] = '\0';
1344                /* open(STATUS, "netstat -an 2>&1 |") */
1345                pipemode[i++] = 'w';
1346#ifndef XP_UNIX
1347                pipemode[i++] = file->type==UTF8 ? 'b' : 't';
1348#endif
1349                pipemode[i++] = '\0';
1350                file->nativehandle = POPEN(command, pipemode);
1351                JS_free(cx, command);
1352            }
1353            /* set the flags */
1354            file->isNative = JS_TRUE;
1355            file->isPipe  = JS_TRUE;
1356            file->hasRandomAccess = JS_FALSE;
1357        }
1358    } else {
1359        /* TODO: what about the permissions?? Java ignores the problem... */
1360        file->handle = PR_Open(file->path, mask, 0644);
1361    }
1362
1363    js_ResetBuffers(file);
1364    JS_free(cx, mode);
1365    mode = NULL;
1366
1367    /* Set the open flag and return result */
1368    if (file->handle == NULL && file->nativehandle == NULL) {
1369        file->isOpen = JS_FALSE;
1370
1371        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1372                             JSFILEMSG_OP_FAILED, "open", file->path);
1373        goto out;
1374    }
1375
1376good:
1377    file->isOpen = JS_TRUE;
1378    *rval = JSVAL_TRUE;
1379    return JS_TRUE;
1380
1381out:
1382    if(mode)
1383        JS_free(cx, mode);
1384    return JS_FALSE;
1385}
1386
1387static JSBool
1388file_close(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1389{
1390    JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1391
1392    SECURITY_CHECK(cx, NULL, "close", file);
1393
1394    if(!file->isOpen){
1395        JS_ReportWarning(cx, "File %s is not open, can't close it, proceeding",
1396            file->path);
1397        goto out;
1398    }
1399
1400    if(!file->isPipe){
1401        if(file->isNative){
1402            JS_ReportWarning(cx, "Unable to close a native file, proceeding", file->path);
1403            goto out;
1404        }else{
1405            if(file->handle && PR_Close(file->handle)){
1406                JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1407                    JSFILEMSG_OP_FAILED, "close", file->path);
1408
1409                goto out;
1410            }
1411        }
1412    }else{
1413        if(PCLOSE(file->nativehandle)==-1){
1414            JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1415                JSFILEMSG_OP_FAILED, "pclose", file->path);
1416            goto out;
1417        }
1418    }
1419
1420    js_ResetAttributes(file);
1421    *rval = JSVAL_TRUE;
1422    return JS_TRUE;
1423
1424out:
1425    return JS_FALSE;
1426}
1427
1428
1429static JSBool
1430file_remove(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1431{
1432	JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1433
1434    SECURITY_CHECK(cx, NULL, "remove", file);
1435    JSFILE_CHECK_NATIVE("remove");
1436    JSFILE_CHECK_CLOSED("remove");
1437
1438    if ((js_isDirectory(cx, file) ?
1439            PR_RmDir(file->path) : PR_Delete(file->path))==PR_SUCCESS) {
1440        js_ResetAttributes(file);
1441        *rval = JSVAL_TRUE;
1442        return JS_TRUE;
1443    } else {
1444        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1445            JSFILEMSG_OP_FAILED, "remove", file->path);
1446        goto out;
1447    }
1448out:
1449    *rval = JSVAL_FALSE;
1450    return JS_FALSE;
1451}
1452
1453/* Raw PR-based function. No text processing. Just raw data copying. */
1454static JSBool
1455file_copyTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1456{
1457    JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1458    char        *dest = NULL;
1459    PRFileDesc  *handle = NULL;
1460    char        *buffer;
1461    jsval		count, size;
1462    JSBool      fileInitiallyOpen=JS_FALSE;
1463
1464    SECURITY_CHECK(cx, NULL, "copyTo", file);   /* may need a second argument!*/
1465    JSFILE_CHECK_ONE_ARG("copyTo");
1466    JSFILE_CHECK_NATIVE("copyTo");
1467    /* remeber the state */
1468    fileInitiallyOpen = file->isOpen;
1469    JSFILE_CHECK_READ;
1470
1471    dest = JS_GetStringBytes(JS_ValueToString(cx, argv[0]));
1472
1473    /* make sure we are not reading a file open for writing */
1474    if (file->isOpen && !js_canRead(cx, file)) {
1475        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1476                JSFILEMSG_CANNOT_COPY_FILE_OPEN_FOR_WRITING_ERROR, file->path);
1477        goto out;
1478    }
1479
1480    if (file->handle==NULL){
1481        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1482            JSFILEMSG_OP_FAILED, "open", file->path);
1483        goto out;
1484    }
1485
1486    handle = PR_Open(dest, PR_WRONLY|PR_CREATE_FILE|PR_TRUNCATE, 0644);
1487
1488    if(!handle){
1489        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1490            JSFILEMSG_OP_FAILED, "open", dest);
1491        goto out;
1492    }
1493
1494    if ((size=js_size(cx, file))==JSVAL_VOID) {
1495        goto out;
1496    }
1497
1498    buffer = JS_malloc(cx, size);
1499
1500    count = INT_TO_JSVAL(PR_Read(file->handle, buffer, size));
1501
1502    /* reading panic */
1503    if (count!=size) {
1504        JS_free(cx, buffer);
1505        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1506              JSFILEMSG_COPY_READ_ERROR, file->path);
1507        goto out;
1508    }
1509
1510    count = INT_TO_JSVAL(PR_Write(handle, buffer, JSVAL_TO_INT(size)));
1511
1512    /* writing panic */
1513    if (count!=size) {
1514        JS_free(cx, buffer);
1515        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1516              JSFILEMSG_COPY_WRITE_ERROR, file->path);
1517        goto out;
1518    }
1519
1520    JS_free(cx, buffer);
1521
1522	if(!fileInitiallyOpen){
1523		if(!file_close(cx, obj, 0, NULL, rval)) goto out;
1524	}
1525
1526    if(PR_Close(handle)!=PR_SUCCESS){
1527        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1528              JSFILEMSG_OP_FAILED, "close", dest);
1529        goto out;
1530    }
1531
1532    *rval = JSVAL_TRUE;
1533    return JS_TRUE;
1534out:
1535    if(file->isOpen && !fileInitiallyOpen){
1536        if(PR_Close(file->handle)!=PR_SUCCESS){
1537            JS_ReportWarning(cx, "Can't close %s, proceeding", file->path);
1538        }
1539    }
1540
1541    if(handle && PR_Close(handle)!=PR_SUCCESS){
1542        JS_ReportWarning(cx, "Can't close %s, proceeding", dest);
1543    }
1544
1545    *rval = JSVAL_FALSE;
1546    return JS_FALSE;
1547}
1548
1549static JSBool
1550file_renameTo(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1551{
1552    JSFile  *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1553    char    *dest;
1554
1555    SECURITY_CHECK(cx, NULL, "renameTo", file); /* may need a second argument!*/
1556    JSFILE_CHECK_ONE_ARG("renameTo");
1557    JSFILE_CHECK_NATIVE("renameTo");
1558    JSFILE_CHECK_CLOSED("renameTo");
1559
1560    dest = RESOLVE_PATH(cx, JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
1561
1562    if (PR_Rename(file->path, dest)==PR_SUCCESS){
1563        /* copy the new filename */
1564        JS_free(cx, file->path);
1565        file->path = dest;
1566        *rval = JSVAL_TRUE;
1567        return JS_TRUE;
1568    }else{
1569        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1570            JSFILEMSG_RENAME_FAILED, file->path, dest);
1571        goto out;
1572    }
1573out:
1574    *rval = JSVAL_FALSE;
1575    return JS_FALSE;
1576}
1577
1578static JSBool
1579file_flush(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1580{
1581    JSFile *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1582
1583    SECURITY_CHECK(cx, NULL, "flush", file);
1584    JSFILE_CHECK_NATIVE("flush");
1585    JSFILE_CHECK_OPEN("flush");
1586
1587    if (PR_Sync(file->handle)==PR_SUCCESS){
1588      *rval = JSVAL_TRUE;
1589      return JS_TRUE;
1590    }else{
1591        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1592           JSFILEMSG_OP_FAILED, "flush", file->path);
1593       goto out;
1594    }
1595out:
1596    *rval = JSVAL_FALSE;
1597    return JS_FALSE;
1598}
1599
1600static JSBool
1601file_write(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1602{
1603    JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1604    JSString    *str;
1605    int32       count;
1606    uintN       i;
1607
1608    SECURITY_CHECK(cx, NULL, "write", file);
1609    JSFILE_CHECK_WRITE;
1610
1611    for (i = 0; i<argc; i++) {
1612        str = JS_ValueToString(cx, argv[i]);
1613        count = js_FileWrite(cx, file, JS_GetStringChars(str),
1614            JS_GetStringLength(str), file->type);
1615        if (count==-1){
1616          *rval = JSVAL_FALSE;
1617          return JS_FALSE;
1618        }
1619    }
1620
1621    *rval = JSVAL_TRUE;
1622    return JS_TRUE;
1623out:
1624    *rval = JSVAL_FALSE;
1625    return JS_FALSE;
1626}
1627
1628static JSBool
1629file_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1630{
1631    JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1632    JSString    *str;
1633
1634    SECURITY_CHECK(cx, NULL, "writeln", file);
1635    JSFILE_CHECK_WRITE;
1636
1637    /* don't report an error here */
1638    if(!file_write(cx, obj, argc, argv, rval))  return JS_FALSE;
1639    /* don't do security here -- we passed the check in file_write */
1640    str = JS_NewStringCopyZ(cx, "\n");
1641
1642    if (js_FileWrite(cx, file, JS_GetStringChars(str), JS_GetStringLength(str),
1643            file->type)==-1){
1644        *rval = JSVAL_FALSE;
1645        return JS_FALSE;
1646    }
1647
1648    /* eol causes flush if hasAutoflush is turned on */
1649    if (file->hasAutoflush)
1650        file_flush(cx, obj, 0, NULL, rval);
1651
1652    *rval =  JSVAL_TRUE;
1653    return JS_TRUE;
1654out:
1655    *rval = JSVAL_FALSE;
1656    return JS_FALSE;
1657}
1658
1659static JSBool
1660file_writeAll(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1661{
1662    JSFile      *file = JS_GetInstancePrivate(cx, obj, &js_FileClass, NULL);
1663    jsuint      i;
1664    jsuint      limit;
1665    JSObject    *array;
1666    JSObject    *elem;
1667    jsval       elemval;
1668
1669    SECURITY_CHECK(cx, NULL, "writeAll", file);
1670    JSFILE_CHECK_ONE_ARG("writeAll");
1671    JSFILE_CHECK_WRITE;
1672
1673    if (!JS_IsArrayObject(cx, JSVAL_TO_OBJECT(argv[0]))) {
1674        JS_ReportErrorNumber(cx, JSFile_GetErrorMessage, NULL,
1675            JSFILEMSG_FIRST_ARGUMENT_WRITEALL_NOT_ARRAY_ERROR)

Large files files are truncated, but you can click here to view the full file