root/trunk/thune/files.c

Revision 561, 30.2 kB (checked in by krobillard, 10 days ago)

Tokenizer now accepts 0x as hexidecimal prefix.
File port can now be opened on standard I/O.
widget! make accepts none for parent again.

</
Line 
1/*============================================================================
2    Urlan Interpreter
3    Copyright (C) 2005-2007  Karl Robillard
4
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18===========================================================================*/
19
20
21#include <assert.h>
22#include "os.h"
23#include "urlan.h"
24#include "urlan_atoms.h"
25#include "internal.h"
26#include "bignum.h"
27
28
29extern int  ur_fileModified( const char* path, UCell* res );
30extern int  ur_isDir( const char* path );
31extern void ur_readDir( UThread*, const char* filename, UCell* res );
32
33extern void uc_system_run( UThread*, UCell* );
34extern void uc_changeDir( UThread*, UCell* );
35extern void uc_whatDir( UThread*, UCell* );
36extern void uc_makeDir( UThread*, UCell* );
37extern void uc_file_info( UThread*, UCell* );
38extern void uc_rename( UThread*, UCell* );
39extern void uc_delete( UThread*, UCell* );
40extern void uc_getenv( UThread*, UCell* );
41
42
43/*
44  Returns pointer to port device.
45  Throws error if port is invalid.
46*/
47static UPortDevice* _devicePtr( UThread* ut, UCell* portCell )
48{
49    UArray* arr = &((UrlanEnv*) ut->env)->devices;
50    int devID = portCell->port.deviceId;
51
52    if( (devID > -1) && (devID < arr->used) )
53    {
54        return ((UPortDevice**) arr->ptr.v)[ devID ];
55    }
56
57    ur_throwErr( UR_ERR_SCRIPT, "Invalid port" );
58    return 0;
59}
60
61
62/*
63  (url [options] -- port)
64
65  The UPortDevice open method must return the following:
66     1  Port opened successfully.
67     0  Url not handled; try other devices.
68    -1  Url handled, but error occured.
69*/
70UR_CALL( uc_open )
71{
72    UArray* arr = &((UrlanEnv*) ut->env)->devices;
73    UPortDevice** it  = (UPortDevice**) arr->ptr.v;
74    UPortDevice** end = it + arr->used;
75
76    while( it != end )
77    {
78        if( 0 != (*it)->open( *it, ut, tos ) )
79            return;
80        ++it;
81    }
82
83    ur_throwErr( UR_ERR_INTERNAL, "No valid port device found" );
84}
85
86
87// (port -- )
88UR_CALL( uc_close )
89{
90    if( ur_is(tos, UT_PORT) )
91    {
92        UPortDevice* dev = _devicePtr( ut, tos );
93        if( dev )
94            dev->close( ut, tos );   // Device close must drop TOS.
95    }
96}
97
98
99// (file -- logic)
100UR_CALL( uc_existsQ )
101{
102    const char* cp;
103    int logic = 0;
104
105    UR_CALL_UNUSED_TH
106
107    cp = ur_cstring( ut, tos );
108    if( cp )
109    {
110        if( ur_isDir( cp ) > -1 )
111            logic = 1;
112
113        ur_initType(tos, UT_LOGIC);
114        ur_logic(tos) = logic;
115    }
116}
117
118
119// (file -- int)
120UR_CALL( uc_sizeQ )
121{
122    const char* cp;
123    int64_t size;
124
125    UR_CALL_UNUSED_TH
126
127    cp = ur_cstring( ut, tos );
128    if( cp )
129    {
130        size = ur_fileSize( cp );
131        if( size < 0 )
132        {
133            ur_setNone(tos);
134        }
135        else if( size > INT32_MAX )
136        {
137            ur_initType(tos, UT_BIGNUM);
138            bignum_setl(tos, size);
139        }
140        else
141        {
142            ur_initType(tos, UT_INT);
143            ur_int(tos) = (int) size;
144        }
145    }
146}
147
148
149// (file -- logic)
150UR_CALL( uc_dirQ )
151{
152    const char* cp;
153    int status;
154
155    UR_CALL_UNUSED_TH
156
157    cp = ur_cstring( ut, tos );
158    if( cp )
159    {
160        status = ur_isDir( cp );
161        if( status < 0 )
162            status = 0;
163
164        ur_initType(tos, UT_LOGIC);
165        ur_logic(tos) = status;
166    }
167}
168
169
170// (file -- time)
171UR_CALL( uc_modifiedQ )
172{
173    const char* cp;
174    int status;
175
176    UR_CALL_UNUSED_TH
177
178    cp = ur_cstring( ut, tos );
179    if( cp )
180    {
181        status = ur_fileModified( cp, tos );
182        if( status < 0 )
183        {
184            ur_setNone(tos);
185        }
186    }
187}
188
189
190#if 0
191/*
192   Returns zero if no newlines are found.
193*/
194static const char* findNewline( const char* it, const char* end )
195{
196    while( it != end )
197    {
198        if( *it == '\n' )
199            return it;
200        ++it;
201    }
202    return 0;
203}
204
205
206#define LINEBUF_SIZE        (1024 * 4)
207
208static void _readLines( const char* fn, OBlock* blk )
209{
210    //orError( "read /lines not implemented" );
211
212    FileHandle fp;
213    int n;
214    int len;
215    int totalRead;
216    int seen;
217    int offset;
218    char* buffer;
219    const char* npos;
220    const char* pos;
221    const char* end;
222
223
224    fp = fileOpen( fn, FILE_READ );
225    if( ! fp )
226    {
227        orErrorT( OR_ERROR_ACCESS, "cannot open file %s", fn );
228        return;
229    }
230
231    buffer = memAlloc( LINEBUF_SIZE );
232    totalRead = seen = offset = 0;
233
234    do
235    {
236        n = fileRead( fp, buffer, LINEBUF_SIZE );
237        if( n < 1 )
238            break;
239
240        offset += n;
241
242        pos = buffer;
243        end = buffer + n;
244
245        while( 1 )
246        {
247            npos = findNewline( pos, end );
248            if( ! npos )
249            {
250                seen += end - pos;
251                break;
252            }
253            else if( seen > totalRead )
254            {
255                UString* str;
256                int n2;
257
258                seen += npos - pos;
259                len = seen - totalRead;
260
261                str = ur_makeString( len + OR_CTERM_LEN );
262                orAppendString( blk, str - orSTRINGS );
263
264                // Would be good to avoid seek/read just to pick up end of buffer.
265                fileSeek( fp, totalRead );
266                n2 = fileRead( fp, str->ptr.c, len );
267                fileSeek( fp, offset );
268
269                str->used = n2;
270                totalRead += n2 + 1;
271                seen = totalRead;
272                pos = npos + 1;
273            }
274            else
275            {
276                len = npos - pos;
277                orAppendString( blk, orMakeCString( pos, len ) );
278                totalRead += len + 1;
279                seen = totalRead;
280                pos = npos + 1;
281            }
282        }
283    }
284    while( n == LINEBUF_SIZE );
285
286    if( seen > totalRead )
287    {
288        cprint( "FIXME: read/lines excess %d\n", seen - totalRead );
289        //len = seen - totalRead;
290        //orAppendString( blk, orMakeCString( buffer + n - len, len ) );
291    }
292
293    fileClose( fp );
294    memFree( buffer );
295}
296#endif
297
298
299/*
300  (port data [len] -- port data)
301  (file [len] -- string)
302*/
303UR_CALL( uc_read )
304{
305    const char* fn;
306    int part = 0;
307
308    if( ur_is(tos, UT_INT) )
309    {
310        part = ur_int(tos);
311        UR_S_DROP;
312        tos = UR_TOS;
313    }
314
315    if( ur_is(ur_s_prev(tos), UT_PORT) )
316    {
317        UPortDevice* dev = _devicePtr( ut, ur_s_prev(tos) );
318        if( dev )
319            dev->read( ut, tos, part );
320        return;
321    }
322
323    fn = ur_cstring( ut, tos );
324    if( fn )
325    {
326        int err;
327
328        err = ur_isDir( fn );
329        if( err < 0 )
330            goto error;
331
332        if( err )
333        {
334            ur_readDir( ut, fn, tos );
335        }
336        else
337        {
338#if 0
339            if( orRefineSet(REF_READ_LINES) )
340            {
341                OBlock* blk;
342
343                if( orRefineSet(REF_READ_BINARY) )
344                {
345                    orError( "cannot use /binary with /lines" );
346                    return;
347                }
348
349                blk = orMakeBlock(0);
350                orRefPush( OT_BLOCK, orBlockN(blk) );
351                _readLines( fn, blk );
352                orRefPop( 1 );
353                orResultBLOCK( blk - orBLOCKS );
354            }
355            else
356#endif
357            {
358                FileHandle fp;
359                UIndex binN;
360                UString* buf;
361                int n;
362                int size;
363                int skip = 0;
364
365#if 0
366                if( orRefineSet(REF_READ_SKIP) )
367                {
368                    UCell* rval = REF_READ_LENGTH;
369                    skip = orNumberToInt( rval );
370                }
371#endif
372                if( part )
373                {
374                    size = part;
375                }
376                else
377                {
378                    size = ur_fileSize( fn );
379                    if( size < 0 )
380                    {
381error:
382                        ur_throwErr( UR_ERR_ACCESS,
383                                     "cannot query file %s", fn );
384                        return;
385                    }
386                }
387
388                binN = ur_makeBinary( size /*+ OR_CTERM_LEN*/, &buf );
389
390                fp = fileOpen( fn, FILE_READ_BINARY );
391                        //orRefineSet(REF_READ_BINARY) ?
392                        //       FILE_READ_BINARY : FILE_READ );
393                if( fp )
394                {
395                    if( skip )
396                    {
397                        n = fileSeek( fp, skip );
398                        if( n == -1 )
399                        {
400                            fileClose( fp );
401                            ur_throwErr( UR_ERR_ACCESS,
402                                         "skip failed on %s", fn);
403                            return;
404                        }
405                    }
406
407                    n = fileRead( fp, buf->ptr.v, size );
408                    err = fileError(fp);
409                    fileClose( fp );
410
411                    if( err )
412                    {
413                        ur_throwErr( UR_ERR_ACCESS,
414                                     "read failed on %s", fn );
415                        return;
416                    }
417
418                    buf->used = n;
419
420                    ur_initType( tos, UT_STRING );
421                    //orRefineSet(REF_READ_BINARY) ? UT_BINARY : UT_STRING
422                    ur_setSeries( tos, binN, 0 );
423                }
424                else
425                {
426                    ur_throwErr( UR_ERR_ACCESS,
427                                 "cannot open file %s", fn );
428                }
429            }
430        }
431    }
432    else
433    {
434        ur_throwErr( UR_ERR_DATATYPE, "read expected string!/port!" );
435    }
436}
437
438
439/*
440  (port data -- port)
441*/
442UR_CALL( uc_write )
443{
444    UCell* pc;
445
446    pc = ur_s_prev(tos);
447
448#if 0
449    if( a1->type == UT_FILE )
450    {
451        FileHandle fp;
452        FileMode mode;
453        UCell* a2;
454        UString* file;
455
456        a2 = a1 + 1;
457        file = ur_bin(a1);
458        ur_termCStr( file );
459
460        if( orRefineSet(REF_APPEND) )
461        {
462            if( orIs(a2, OT_BINARY) || orRefineSet(REF_BINARY) )
463                mode = FILE_APPEND_BINARY;
464            else
465                mode = FILE_APPEND;
466        }
467        else
468        {
469            if( orIs(a2, OT_BINARY) || orRefineSet(REF_BINARY) )
470                mode = FILE_WRITE_BINARY;
471            else
472                mode = FILE_WRITE;
473        }
474
475        fp = fileOpen( file->ptr.c, mode );
476        if( fp )
477        {
478            UString* data = 0;
479
480            if( orIs(a2, OT_OBJECT) )
481            {
482                data = ur_makeString(0);
483                orForm( data, a2 );
484            }
485            else if( orIs(a2, OT_STRING) || orIs(a2, OT_BINARY) )
486            {
487                data = ur_bin(a2);
488            }
489
490            if( data )
491            {
492                int n;
493
494                n = fileWrite( fp, data->ptr.c, data->used );
495                fileClose( fp );
496
497                if( n != data->used )
498                {
499                    orErrorT( OR_ERROR_ACCESS,
500                              "Only wrote %d bytes to %s", n, file->ptr.c );
501                    return;
502                }
503            }
504            else
505            {
506                fileClose( fp );
507            }
508
509            orResultUNSET;
510            return;
511        }
512        orErrorT( OR_ERROR_ACCESS, "Cannot open %s", file->ptr.c );
513    }
514    else
515#endif
516    if( ur_is(pc, UT_PORT) )
517    {
518        UPortDevice* dev = _devicePtr( ut, pc );
519        if( dev )
520            dev->write( ut, tos );
521    }
522    else
523    {
524        ur_throwErr( UR_ERR_DATATYPE, "write expected port!" );
525    }
526}
527
528
529// (port position -- port)
530UR_CALL( uc_seek )
531{
532    UCell* pc = ur_s_prev(tos);
533    if( ur_is(pc, UT_PORT) )
534    {
535        UPortDevice* dev = _devicePtr( ut, pc );
536        if( dev )
537            dev->seek( ut, tos );
538    }
539    else
540    {
541        ur_throwErr( UR_ERR_DATATYPE, "seek expected port!" );
542    }
543}
544
545
546#define IS_SLASH(c)  (((c) == '/') || ((c) == '\\'))
547
548static char* upDir( char* start, char* it )
549{
550    char* oit = it;
551    if( it != start )
552    {
553        if( IS_SLASH(*it) )
554            --it;
555        while( it != start )
556        {
557            if( IS_SLASH(*it) )
558            {
559                if( it != oit )
560                    ++it;
561                break;
562            }
563            --it;
564        }
565    }
566    return it;
567}
568
569
570/*
571  (path -- absolute)
572*/
573UR_CALL( uc_fullPath )
574{
575    uint8_t* cpA;
576    uint8_t* cpB;
577
578    if( ur_stringSlice(ut, tos, &cpA, &cpB) )
579    {
580#ifdef _WIN32
581        if( ((cpB - cpA) > 1) && (cpA[1] == ':') )
582            return;
583#endif
584        if( (*cpA != '/') && (*cpA != '\\') )
585        {
586            uc_whatDir( ut, tos );
587            UR_S_NIP;
588
589            if( cpA != cpB )
590            {
591                UString* str;
592                char* dst;
593                int len;
594
595                str = ur_bin( tos );
596                ur_arrayReserve( str, 1, str->used + (cpB - cpA) );
597                dst = str->ptr.c + str->used;
598
599#define INC_A   ++cpA; if(cpA == cpB) break;
600
601                while( cpA != cpB )
602                {
603                    if( *cpA == '.' )
604                    {
605                        len = cpB - cpA;
606                        if( len > 1 )
607                        {
608                            if( IS_SLASH(cpA[1]) )
609                            {
610                                cpA += 2;
611                                continue;
612                            }
613                            else if( (len > 2) &&
614                                     (cpA[1] == '.') &&
615                                     IS_SLASH(cpA[2]) )
616                            {
617                                cpA += 3;
618                                dst = upDir( str->ptr.c, dst - 1 );
619                                continue;
620                            }
621                        }
622                    }
623                    else if( IS_SLASH(*cpA) )
624                    {
625                        if( IS_SLASH(dst[-1]) )
626                        {
627                            ++cpA;
628                            continue;
629                        }
630                    }
631                    *dst++ = *cpA++;
632                }
633
634                str->used = dst - str->ptr.c;
635            }
636        }
637    }
638    else
639    {
640        ur_throwErr( UR_ERR_DATATYPE, "full-path expected string!" );
641    }
642}
643
644
645/*--------------------------------------------------------------------------*/
646
647
648#ifdef UR_CONFIG_BZIP2
649#include <bzlib.h>
650
651
652// (string -- binary)
653UR_CALL( uc_compress )
654{
655    uint8_t* cpA;
656    uint8_t* cpB;
657    UString* bin;
658    UIndex binN;
659    int ret;
660    unsigned int len;
661    unsigned int blen;
662
663    if( ur_binaryMem( ut, tos, &cpA, &cpB ) )
664    {
665        if( cpA )
666        {
667            len = cpB - cpA;
668            blen = len + (len / 99) + 600;
669
670            binN = ur_makeBinary( blen /*+ 4*/, &bin );
671
672            if( len > 0 )
673            {
674                ret = BZ2_bzBuffToBuffCompress( bin->ptr.c /*+ 4*/, &blen,
675                                                (char*) cpA, len,
676                                                3, 0, 0 );
677                if( ret == BZ_OUTBUFF_FULL )
678                {
679                    ur_arrayFree( bin );
680                    ur_throwErr( UR_ERR_ACCESS, "compress buffer full" );
681                    return;
682                }
683                bin->used = blen;
684            }
685
686            ur_initType( tos, UT_BINARY );
687            ur_setSeries( tos, binN, 0 );
688        }
689    }
690    else
691    {
692        ur_throwErr( UR_ERR_DATATYPE,
693                     "compress expected string!/binary!/vector!" );
694    }
695}
696
697
698/**
699   Decompress data into intialized array.
700
701   \return  Non-zero if successful.
702*/
703int ur_decompress( const void* data, int len, UArray* arr )
704{
705#define BUF_LOW     32
706    bz_stream strm;
707    int ok;
708
709    strm.bzalloc = 0;
710    strm.bzfree  = 0;
711    strm.opaque  = 0;
712
713    ok = BZ2_bzDecompressInit( &strm, 0, 0 );
714    if( ok != BZ_OK )
715    {
716        //fprintf( stderr, "BZ2_bzDecompressInit failure (%d)", ok );
717        return 0;
718    }
719
720    strm.next_in  = (char*) data;
721    strm.avail_in = len;
722
723    ur_arrayReserve( arr, 1, (len < BUF_LOW) ? BUF_LOW : len );
724    strm.next_out  = arr->ptr.c;
725    strm.avail_out = arr->avail;
726
727    do
728    {
729        if( strm.avail_out < BUF_LOW )
730        {
731            ur_arrayReserve( arr, 1, arr->avail + (2 * BUF_LOW) );
732            strm.next_out  = arr->ptr.c + strm.total_out_lo32;
733            strm.avail_out = arr->avail - strm.total_out_lo32;
734        }
735
736        ok = BZ2_bzDecompress( &strm );
737        arr->used = strm.total_out_lo32;
738    }
739    while( ok == BZ_OK );
740
741    BZ2_bzDecompressEnd( &strm );
742
743    return (ok == BZ_STREAM_END) ? 1 : 0;
744}
745
746
747// (binary -- string)
748UR_CALL( uc_decompress )
749{
750    UIndex strN;
751    UString* str;
752    uint8_t* cpA;
753    uint8_t* cpB;
754
755    if( ! ur_binarySlice( ut, tos, &cpA, &cpB ) )
756    {
757        ur_throwErr( UR_ERR_DATATYPE, "decompress expected binary!" );
758        return;
759    }
760    if( cpA == 0 )
761        return;
762
763    strN = ur_makeBinary( 0, &str );
764
765    if( ur_decompress( cpA, cpB - cpA, str ) )
766    {
767        ur_initString( tos, strN, 0 );
768    }
769    else
770    {
771        ur_throwErr( UR_ERR_INTERNAL, "decompress failure" );
772    }
773}
774#endif
775
776
777/*--------------------------------------------------------------------------*/
778
779
780/*
781  CRC-16 Checksum - DDCMP and IBM Bisync
782
783  x^16 + x^15 + x^2 + 1
784*/
785
786#define CRC16_POLYNOMIAL    0xa001
787#define CRC16_INITIAL       0
788
789uint16_t checksum_crc16( uint8_t* data, int byteCount )
790{
791    uint16_t crc, bit;
792    int i;
793
794    crc = CRC16_INITIAL;
795    while( byteCount-- > 0 )
796    {
797        for( i = 1; i <= 0x80; i <<= 1 )
798        {
799            bit = ((*data) & i) ? 0x8000 : 0;
800            if( crc & 1 )
801                bit ^= 0x8000;
802            crc >>= 1;
803            if( bit )
804                crc ^= CRC16_POLYNOMIAL;
805        }
806        ++data;
807    }
808    return crc;
809}
810
811
812#define SHA1HANDSOFF    1
813#include <support/sha1.c>
814
815
816// (binary type -- int)
817UR_CALL( uc_checksum )
818{
819    UCell* res;
820    uint8_t* cpA;
821    uint8_t* cpB;
822
823    if( ! ur_isAWord( tos ) )
824        goto typeErr;
825
826    UR_S_DROP;
827    res = UR_TOS;
828
829    if( ur_binaryMem( ut, res, &cpA, &cpB ) )
830    {
831        switch( ur_atom(tos) )
832        {
833            case UR_ATOM_CRC16:
834                ur_initType(res, UT_INT);
835                ur_int(res) = checksum_crc16( (uint8_t*) cpA, cpB - cpA );
836                return;
837
838            case UR_ATOM_SHA1:
839            {
840                SHA1_CTX context;
841                UIndex binN;
842                UBinary* bin;
843
844                binN = ur_makeBinary( 20, &bin );
845                bin->used = 20;
846
847                SHA1_Init( &context );
848                SHA1_Update( &context, (uint8_t*) cpA, cpB - cpA );
849                SHA1_Final( &context, bin->ptr.b );
850
851                ur_initType(res, UT_BINARY);
852                ur_setSeries(res, binN, 0);
853            }
854                return;
855        }
856        goto typeErr;
857    }
858    else
859    {
860        ur_throwErr( UR_ERR_DATATYPE, "checksum expected string!/binary!" );
861        return;
862    }
863
864typeErr:
865
866    ur_throwErr( UR_ERR_DATATYPE, "checksum expected type 'crc16 or 'sha1" );
867}
868
869
870/*--------------------------------------------------------------------------*/
871
872
873#ifdef _WIN32
874#define ur_winHandle(cell)  (cell)->slist.ptr
875#include <windows.h>
876#if _MSC_VER < 1300
877#define INVALID_SET_FILE_POINTER    ((DWORD)-1)
878#endif
879#else
880#include <sys/types.h>
881#include <sys/stat.h>
882#include <fcntl.h>
883#include <unistd.h>
884#include <errno.h>
885#include <string.h>
886#endif
887
888
889#define FV_FD   0   
890
891
892/*
893  (url [options] -- port)
894  ('std fd -- port)
895*/
896int _fileOpen( UPortDevice* dev, UThread* ut, UCell* tos )
897{
898#ifdef _WIN32
899    static DWORD _stdWinDevice[3] = {
900        STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, STD_ERROR_HANDLE
901    };
902    HANDLE fh;
903#else
904    int fd;
905#endif
906    const char* path;
907    UBlock* optBlk = 0;
908
909
910    if( ur_is(tos, UT_INT) )
911    {
912        // Standard I/O
913        // 0,1,2 - stdin, stdout, stderr
914        int n = ur_int(tos);
915
916        tos = ur_s_prev(tos);
917        if( ur_is(tos, UT_WORD) && ur_atom(tos) == UR_ATOM_STD )
918        {
919            if( n < 0 || n > 2 )
920                return -1;
921#ifdef _WIN32
922            fh = GetStdHandle( _stdWinDevice[ n ] );
923            if( fh == INVALID_HANDLE_VALUE )
924            {
925                ur_throwErr( UR_ERR_ACCESS, "GetStdHandle error %d",
926                             GetLastError() );
927                return -1;
928            }
929#else
930            fd = n;
931#endif
932            UR_S_DROP;
933            goto make_port;
934        }
935        return 0;
936    }
937    else if( ur_is(tos, UT_BLOCK) )
938    {
939        optBlk = ur_block(tos);
940        tos = ur_s_prev(tos);
941    }
942
943    path = ur_cstring( ut, tos );
944    if( path )
945    {
946#ifdef _WIN32
947        DWORD access = GENERIC_READ | GENERIC_WRITE;
948        DWORD create = OPEN_ALWAYS;
949        int append = 0;
950
951        if( optBlk )
952        {
953            // Scan options block.
954            UCell* it;
955            UCell* end;
956
957            UR_ITER_BLOCK( it, end, optBlk, ur_s_next(tos) );
958            while( it != end )
959            {
960                if( ur_is(it, UT_WORD) )
961                {
962                    switch( ur_atom(it) )
963                    {
964                        //case UR_ATOM_BINARY:
965
966                        case UR_ATOM_READ:
967                            access = GENERIC_READ;
968                            create = OPEN_EXISTING;
969                            break;
970
971                        case UR_ATOM_WRITE:
972                            access = GENERIC_WRITE;
973                            create = CREATE_ALWAYS;
974                            break;
975
976                        case UR_ATOM_APPEND:
977                            create = OPEN_ALWAYS;
978                            append = 1;
979                            break;
980                    }
981                }
982                ++it;
983            }
984            UR_S_DROP;
985        }
986
987        if( ! ur_userAllows( ut, "Open file \"%s\"", path ) )
988        {
989            ur_throwErr( UR_ERR_ACCESS, "User denied open" );
990            return -1;
991        }
992
993        fh = CreateFile( path, access, FILE_SHARE_READ, NULL,
994                         create, FILE_ATTRIBUTE_NORMAL, NULL );
995        if( fh == INVALID_HANDLE_VALUE )
996        {
997            ur_throwErr( UR_ERR_ACCESS, "CreateFile error %d",
998                         GetLastError() );
999            return -1;
1000        }
1001
1002        if( append )
1003        {
1004            if( SetFilePointer( fh, 0, NULL, FILE_END ) ==
1005                INVALID_SET_FILE_POINTER )
1006            {
1007                ur_throwErr( UR_ERR_ACCESS, "SetFilePointer error %d",
1008                             GetLastError() );
1009                CloseHandle( fh );
1010                return -1;
1011            }
1012        }
1013
1014make_port:
1015        {
1016        UCell* val = ur_makePort( ut, dev, tos );
1017        if( val )
1018        {
1019            ur_initType(val, UT_INT);   // FV_FD
1020            ur_int(val) = 1;
1021            ur_winHandle(val) = fh;
1022        }
1023        }
1024        return 1;
1025#else
1026        int flags = O_CREAT|O_RDWR;
1027        int append = 0;
1028
1029        if( optBlk )
1030        {
1031            // Scan options block.
1032            UCell* it;
1033            UCell* end;
1034
1035            UR_ITER_BLOCK( it, end, optBlk, ur_s_next(tos) );
1036            while( it != end )
1037            {
1038                if( ur_is(it, UT_WORD) )
1039                {
1040                    switch( ur_atom(it) )
1041                    {
1042                        //case UR_ATOM_BINARY:
1043
1044                        case UR_ATOM_READ:
1045                            flags = O_RDONLY;
1046                            break;
1047
1048                        case UR_ATOM_WRITE:
1049                            flags = O_CREAT|O_WRONLY|O_TRUNC;
1050                            break;
1051
1052                        case UR_ATOM_APPEND:
1053                            flags &= ~O_TRUNC;
1054                            append = 1;
1055                            break;
1056                    }
1057                }
1058                ++it;
1059            }
1060            UR_S_DROP;
1061        }
1062
1063        if( ! ur_userAllows( ut, "Open file \"%s\"", path ) )
1064        {
1065            ur_throwErr( UR_ERR_ACCESS, "User denied open" );
1066            return -1;
1067        }
1068
1069        fd = open( path, flags, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH );
1070        if( fd == -1 )
1071        {
1072            ur_throwErr( UR_ERR_ACCESS, strerror( errno ) );
1073            return -1;
1074        }
1075
1076        if( append )
1077        {
1078            if( lseek( fd, 0, SEEK_END ) == -1 )
1079            {
1080                ur_throwErr( UR_ERR_ACCESS, strerror( errno ) );
1081                close( fd );
1082                return -1;
1083            }
1084        }
1085
1086make_port:
1087        {
1088        UCell* val = ur_makePort( ut, dev, tos );
1089        if( val )
1090        {
1091            ur_initType(val, UT_INT);   // FV_FD
1092            ur_int(val) = fd;
1093        }
1094        }
1095        return 1;
1096#endif
1097    }
1098
1099    return 0;
1100}
1101
1102
1103// (port -- )
1104UR_CALL( _fileClose )
1105{
1106    UBlock* blk;
1107    UCell* val;
1108
1109    blk = ur_blockPtr( tos->port.valBlk );
1110    val = blk->ptr.cells + FV_FD;
1111
1112    if( ur_is(val, UT_INT) )
1113    {
1114#ifdef _WIN32
1115        HANDLE fh = ur_winHandle(val);
1116
1117        ur_setNone( val );
1118
1119        if( ! CloseHandle( fh ) )
1120        {
1121            ur_throwErr( UR_ERR_ACCESS, "CloseHandle error %d",
1122                         GetLastError() );
1123            return;
1124        }
1125#else
1126        int fd = ur_int(val);
1127
1128        ur_setNone( val );
1129
1130        if( close( fd ) == -1 )
1131        {
1132            ur_throwErr( UR_ERR_ACCESS, strerror( errno ) );
1133            return;
1134        }
1135#endif
1136    }
1137
1138    UR_S_DROP;
1139}
1140
1141
1142#define READ_BUF_SIZE   1024
1143
1144
1145// (port buf -- port buf)
1146void _fileRead( UThread* ut, UCell* tos, int part )
1147{
1148    UBlock* blk;
1149    UCell* val;
1150    UCell* pc = ur_s_prev(tos);
1151
1152    blk = ur_blockPtr( pc->port.valBlk );
1153    val = blk->ptr.cells + FV_FD;
1154
1155    if( ur_is(val, UT_INT) )
1156    {
1157        UBinary* bin;
1158        char* buf;
1159#ifdef _WIN32
1160        DWORD len;
1161        DWORD count;
1162#else
1163        size_t len;
1164        ssize_t count;
1165#endif
1166
1167
1168        len = part ? part : READ_BUF_SIZE;
1169
1170        if( ur_is(tos, UT_BINARY) || ur_is(tos, UT_STRING) )
1171        {
1172            bin = ur_bin(tos);