root/trunk/orca/files.c

Revision 412, 15.2 kB (checked in by krobillard, 1 year ago)

dir? throws error if cannot query file

Line 
1 /*============================================================================
2     ORCA Interpreter
3     Copyright (C) 2005-2006  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 "ovalue.h"
24
25
26 extern int orIsDir( const char* path );
27 extern int orFileModified( const char* path, OValue* res );
28 extern void orReadDir( OValue*, const char* filename );
29 extern void orNowNative( OValue* a1 );
30 extern void orChangeDirNative( OValue* a1 );
31 extern void orWhatDirNative();
32 extern void orMakeDirNative( OValue* a1 );
33 extern void orRenameNative( OValue* a1 );
34 extern void orDeleteNative( OValue* a1 );
35 extern void orGetenvNative( OValue* a1 );
36
37 #ifdef OR_CONFIG_OS_CALL
38 extern void orCallNative( OValue* a1 );
39 #endif
40
41
42 #define DEVICE_CLASS  argc
43
44
45 OR_NATIVE( orOpenNative )
46 {
47     OArray* arr = &orEnv->devices;
48     OPortDevice** it  = (OPortDevice**) arr->buf;
49     OPortDevice** end = it + arr->used;
50     OContext ctx;
51
52     ctx.vblkN = 0;
53
54     while( it != end )
55     {
56         if( (*it)->open( a1, &ctx ) )
57         {
58             if( ctx.vblkN )
59             {
60                 orSetTF( a1, OT_PORT );
61                 a1->DEVICE_CLASS = it - ((OPortDevice**) arr->buf);
62                 a1->ctx          = ctx;
63             }
64             return;
65         }
66         ++it;
67     }
68
69     orResultNONE;
70 }
71
72
73 OR_NATIVE( orCloseNative )
74 {
75     OArray* arr = &orEnv->devices;
76     int devID = a1->DEVICE_CLASS;
77
78     assert( (devID > -1) && (devID < arr->used) );
79     ((OPortDevice**) arr->buf)[ devID ]->close( &a1->ctx );
80 }
81
82
83 OR_NATIVE( orExistsQNative )
84 {
85     int logic = 0;
86     OString* str = orSTRING(a1);
87     orTermCStr( str );
88     if( orIsDir( str->charArray + a1->series.it ) > -1 )
89         logic = 1;
90     orResult( OT_LOGIC, logic );
91 }
92
93
94 OR_NATIVE( orSizeQNative )
95 {
96     int size;
97     OString* str = orSTRING(a1);
98     orTermCStr( str );
99     size = orFileSize( str->charArray + a1->series.it );
100     if( size < 0 )
101     {
102         orResultNONE;
103     }
104     else
105     {
106         orResult( OT_INTEGER, size );
107     }
108 }
109
110
111 OR_NATIVE( orDirQNative )
112 {
113     int status;
114     OString* str = orSTRING(a1);
115     orTermCStr( str );
116     status = orIsDir( str->charArray + a1->series.it );
117     if( status < 0 )
118     {
119         orErrorT( OR_ERROR_ACCESS, "cannot query \"%s\"",
120                   str->charArray + a1->series.it );
121         return;
122     }
123     orResult( OT_LOGIC, status );
124 }
125
126
127 OR_NATIVE( orModifiedQNative )
128 {
129     int status;
130     OString* str = orSTRING(a1);
131     orTermCStr( str );
132     status = orFileModified( str->charArray + a1->series.it, a1 );
133     if( status < 0 )
134     {
135         orResultNONE;
136     }
137 }
138
139
140 /*
141    Returns zero if no newlines are found.
142 */
143 static const char* findNewline( const char* it, const char* end )
144 {
145     while( it != end )
146     {
147         if( *it == '\n' )
148             return it;
149         ++it;
150     }
151     return 0;
152 }
153
154
155 #define LINEBUF_SIZE        (1024 * 4)
156
157 static void readLines( const char* fn, OBlock* blk )
158 {
159     //orError( "read /lines not implemented" );
160
161     FileHandle fp;
162     int n;
163     int len;
164     int totalRead;
165     int seen;
166     int offset;
167     char* buffer;
168     const char* npos;
169     const char* pos;
170     const char* end;
171
172
173     fp = fileOpen( fn, FILE_READ );
174     if( ! fp )
175     {
176         orErrorT( OR_ERROR_ACCESS, "cannot open file %s", fn );
177         return;
178     }
179
180     buffer = memAlloc( LINEBUF_SIZE );
181     totalRead = seen = offset = 0;
182
183     do
184     {
185         n = fileRead( fp, buffer, LINEBUF_SIZE );
186         if( n < 1 )
187             break;
188
189         offset += n;
190
191         pos = buffer;
192         end = buffer + n;
193
194         while( 1 )
195         {
196             npos = findNewline( pos, end );
197             if( ! npos )
198             {
199                 seen += end - pos;
200                 break;
201             }
202             else if( seen > totalRead )
203             {
204                 OString* str;
205                 int n2;
206
207                 seen += npos - pos;
208                 len = seen - totalRead;
209
210                 str = orMakeString( len + OR_CTERM_LEN );
211                 orAppendString( blk, str - orSTRINGS );
212
213                 // Would be good to avoid seek/read just to pick up end of buffer.
214                 fileSeek( fp, totalRead );
215                 n2 = fileRead( fp, str->charArray, len );
216                 fileSeek( fp, offset );
217
218                 str->used = n2;
219                 totalRead += n2 + 1;
220                 seen = totalRead;
221                 pos = npos + 1;
222             }
223             else
224             {
225                 len = npos - pos;
226                 orAppendString( blk, orMakeCString( pos, len ) );
227                 totalRead += len + 1;
228                 seen = totalRead;
229                 pos = npos + 1;
230             }
231         }
232     }
233     while( n == LINEBUF_SIZE );
234
235     if( seen > totalRead )
236     {
237         cprint( "FIXME: read/lines excess %d\n", seen - totalRead );
238         //len = seen - totalRead;
239         //orAppendString( blk, orMakeCString( buffer + n - len, len ) );
240     }
241
242     fileClose( fp );
243     memFree( buffer );
244 }
245
246
247 #define REF_READ_BINARY     a1+1
248 #define REF_READ_LINES      a1+2
249 #define REF_READ_SKIP       a1+3
250 #define REF_READ_LENGTH     a1+4
251 #define REF_READ_PART       a1+5
252 #define REF_READ_SIZE       a1+6
253
254 /*
255     read: native [
256         source [file! port!]
257         /binary
258         /lines
259         /skip length [number!]
260         /part size [number!]
261     ]
262 */
263 OR_NATIVE( orReadNative )
264 {
265     if( orIs(a1, OT_FILE) )
266     {
267         int n;
268         const char* fn;
269         OString* str = orSTRING(a1);
270
271         orTermCStr( str );
272         fn = str->charArray + a1->series.it;
273
274         n = orIsDir( fn );
275         if( n < 0 )
276         {
277             goto read_error;
278         }
279         else if( n == 1 )
280         {
281             orReadDir( a1, fn );
282         }
283         else
284         {
285             if( orRefineSet(REF_READ_LINES) )
286             {
287                 OBlock* blk;
288
289                 if( orRefineSet(REF_READ_BINARY) )
290                 {
291                     orError( "cannot use /binary with /lines" );
292                     return;
293                 }
294
295                 blk = orMakeBlock(0);
296                 orRefPush( OT_BLOCK, orBlockN(blk) );
297                 readLines( fn, blk );
298                 orRefPop( 1 );
299                 orResultBLOCK( blk - orBLOCKS );
300             }
301             else
302             {
303                 FileHandle fp;
304                 OString* buf;
305                 int err;
306                 int size;
307                 int skip = 0;
308
309                 if( orRefineSet(REF_READ_SKIP) )
310                 {
311                     OValue* rval = REF_READ_LENGTH;
312                     skip = orNumberToInt( rval );
313                 }
314
315                 if( orRefineSet(REF_READ_PART) )
316                 {
317                     OValue* rval = REF_READ_SIZE;
318                     size = orNumberToInt( rval );
319                 }
320                 else
321                 {
322                     size = orFileSize( fn );
323                     if( size < 0 )
324                     {
325                         orErrorT( OR_ERROR_ACCESS, "cannot query file %s", fn );
326                         return;
327                     }
328                 }
329
330                 buf = orMakeString( size + OR_CTERM_LEN );
331
332                 fp = fileOpen( fn, orRefineSet(REF_READ_BINARY) ?
333                                FILE_READ_BINARY : FILE_READ );
334                 if( fp )
335                 {
336                     if( skip )
337                     {
338                         n = fileSeek( fp, skip );
339                         if( n == -1 )
340                         {
341                             fileClose( fp );
342                             orErrorT(OR_ERROR_ACCESS, "skip failed on %s", fn);
343                             return;
344                         }
345                     }
346
347                     n = fileRead( fp, buf->buf, size );
348                     err = fileError( fp );
349                     fileClose( fp );
350
351                     if( err )
352                     {
353 read_error:
354                         orErrorT( OR_ERROR_ACCESS, "read failed on %s", fn );
355                         return;
356                     }
357
358                     buf->used = n;
359                     orResultSeries( orRefineSet(REF_READ_BINARY) ?
360                                     OT_BINARY : OT_STRING,
361                                     buf - orSTRINGS, 0 );
362                 }
363                 else
364                 {
365                     orErrorT( OR_ERROR_ACCESS, "cannot open file %s", fn );
366                 }
367             }
368         }
369     }
370     else if( orIs(a1, OT_PORT) )
371     {
372         OArray* arr = &orEnv->devices;
373         int devID = a1->DEVICE_CLASS;
374
375         assert( (devID > -1) && (devID < arr->used) );
376         ((OPortDevice**) arr->buf)[ devID ]->read( &a1->ctx, a1 );
377     }
378 }
379
380
381 #define REF_BINARY    a1+2
382 #define REF_APPEND    a1+3
383
384 OR_NATIVE( orWriteNative )
385 {
386     if( a1->type == OT_FILE )
387     {
388         FileHandle fp;
389         FileMode mode;
390         OValue* a2;
391         OString* file;
392
393         a2 = a1 + 1;
394         file = orSTRING(a1);
395         orTermCStr( file );
396
397         if( orRefineSet(REF_APPEND) )
398         {
399             if( orIs(a2, OT_BINARY) || orRefineSet(REF_BINARY) )
400                 mode = FILE_APPEND_BINARY;
401             else
402                 mode = FILE_APPEND;
403         }
404         else
405         {
406             if( orIs(a2, OT_BINARY) || orRefineSet(REF_BINARY) )
407                 mode = FILE_WRITE_BINARY;
408             else
409                 mode = FILE_WRITE;
410         }
411
412         fp = fileOpen( file->charArray, mode );
413         if( fp )
414         {
415             OString* data = 0;
416
417             if( orIs(a2, OT_OBJECT) )
418             {
419                 data = orMakeString(0);
420                 orForm( data, a2 );
421             }
422             else if( orIs(a2, OT_STRING) || orIs(a2, OT_BINARY) )
423             {
424                 data = orSTRING(a2);
425             }
426
427             if( data )
428             {
429                 int n;
430
431                 n = fileWrite( fp, data->charArray, data->used );
432                 fileClose( fp );
433
434                 if( n != data->used )
435                 {
436                     orErrorT( OR_ERROR_ACCESS,
437                               "Only wrote %d bytes to %s", n, file->charArray );
438                     return;
439                 }
440             }
441             else
442             {
443                 fileClose( fp );
444             }
445
446             orResultUNSET;
447             return;
448         }
449         orErrorT( OR_ERROR_ACCESS, "Cannot open %s", file->charArray );
450     }
451     else if( a1->type == OT_PORT )
452     {
453         OArray* arr = &orEnv->devices;
454         int devID = a1->DEVICE_CLASS;
455
456         assert( (devID > -1) && (devID < arr->used) );
457         ((OPortDevice**) arr->buf)[ devID ]->write( &a1->ctx, a1 );
458     }
459 }
460
461
462 char* orCleanPath( const char* src, const char* end, char* clean )
463 {
464     char* dst = clean;
465     while( src != end )
466     {
467         if( *src == '\\' || *src == '/' )
468         {
469             int len = end - src;
470             if( (len > 2) && (src[1] == '.') && (src[2] == '.') )
471             {
472                 src += 3;
473                 while( dst != clean )
474                 {
475                     --dst;
476                     if( *dst == '/' )
477                         break;
478                 }
479             }
480             else
481             {
482                 ++src;
483                 *dst++ = '/';
484             }
485         }
486         else
487         {
488             *dst++ = *src++;
489         }
490     }
491     return dst;
492 }
493
494
495 OR_NATIVE( cleanPathNative )
496 {
497     OString* str;
498     OString* clean;
499     char* dst;
500
501     str = orSTRING(a1);
502     clean = orMakeString( orSeriesLen(str,a1) );
503
504     dst = orCleanPath( orStrChars(str, a1), str->charArray + str->used,
505                        clean->charArray );
506     clean->used = dst - clean->charArray;
507
508     orSetSeries( a1, orStringN(clean), 0 );
509 }
510
511
512 #ifdef OR_CONFIG_COMPRESS
513 #include <bzlib.h>
514
515
516 OR_NATIVE( orCompressNative )
517 {
518     OString* str;
519     OString* bin;
520     int ret;
521     unsigned int len;
522     unsigned int blen;
523
524     str = orSTRING(a1);
525     len = str->used - a1->series.it;
526     blen = len + (len / 99) + 600;
527     bin = orMakeString( blen /*+ 4*/ );
528
529     if( len > 0 )
530     {
531         ret = BZ2_bzBuffToBuffCompress( bin->charArray /*+ 4*/, &blen,
532                                         str->charArray + a1->series.it, len,
533                                         3, 0, 0 );
534         if( ret == BZ_OUTBUFF_FULL )
535         {
536             orArrayFree( bin );
537             orError( "compress buffer full" );
538             return;
539         }
540         bin->used = blen;
541     }
542     orResultBINARY( bin - orSTRINGS );
543 }
544
545
546 OR_NATIVE_PUB( orDecompressNative )
547 {
548 #define BUF_LOW     32
549     int ok;
550     int len;
551     bz_stream strm;
552     OString* str;
553     OString* bin;
554
555     strm.bzalloc = 0;
556     strm.bzfree  = 0;
557     strm.opaque  = 0;
558
559     ok = BZ2_bzDecompressInit( &strm, 0, 0 );
560     if( ok == BZ_OK )
561     {
562         bin = orSTRING(a1);
563         len = orSeriesLen( bin, a1 );
564         strm.next_in   = orStrChars( bin, a1 );
565         strm.avail_in  = len;
566
567         str = orMakeString( (len < BUF_LOW) ? BUF_LOW : len );
568         strm.next_out  = str->charArray;
569         strm.avail_out = str->avail;
570
571         while( ok == BZ_OK )
572         {
573             if( strm.avail_out < BUF_LOW )
574             {
575                 orArrayReserve( str, 1, str->avail + (2 * BUF_LOW) );
576                 strm.next_out  = str->charArray + strm.total_out_lo32;
577                 strm.avail_out = str->avail - strm.total_out_lo32;
578             }
579
580             ok = BZ2_bzDecompress( &strm );
581             str->used = strm.total_out_lo32;
582         }
583
584         BZ2_bzDecompressEnd( &strm );
585
586         if( ok == BZ_STREAM_END )
587         {
588             orResultSTRING( str - orSTRINGS );
589             return;
590         }
591         orError( "decompress failure (%d)", ok );
592     }
593     orError( "decompress init failure (%d)", ok );
594 }
595 #endif
596
597
598 void orFileNatives()
599 {
600     orNative( orOpenNative,       "open"       );
601     orNative( orCloseNative,      "close"      );
602     orNative( orExistsQNative,    "exists?"    );
603     orNative( orDirQNative,       "dir?"       );
604     orNative( orSizeQNative,      "size?"      );
605     orNative( orModifiedQNative,  "modified?"  );
606     orNative( orReadNative,       "read"       );
607     orNative( orWriteNative,      "write"      );
608     orNative( orRenameNative,     "rename"     );
609     orNative( orDeleteNative,     "delete"     );
610     orNative( orChangeDirNative,  "change-dir" );
611     orNative( orWhatDirNative,    "what-dir"   );
612     orNative( orMakeDirNative,    "make-dir"   );
613     orNative( orNowNative,        "now"        );
614     orNative( orGetenvNative,     "getenv"     );
615     orNative( cleanPathNative,    "clean-path" );
616
617 #ifdef OR_CONFIG_OS_CALL
618     orNative( orCallNative,       "call"       );
619 #endif
620
621 #ifdef OR_CONFIG_COMPRESS
622     orNative( orCompressNative,   "compress"   );
623     orNative( orDecompressNative, "decompress" );
624 #endif
625 }
626
627
628 /*EOF*/
Note: See TracBrowser for help on using the browser.