OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [mac/] [tclMacChan.c] - Blame information for rev 1765

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclMacChan.c
3
 *
4
 *      Channel drivers for Macintosh channels for the
5
 *      console fds.
6
 *
7
 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
8
 *
9
 * See the file "license.terms" for information on usage and redistribution
10
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
11
 *
12
 * RCS: @(#) $Id: tclMacChan.c,v 1.1.1.1 2002-01-16 10:25:30 markom Exp $
13
 */
14
 
15
#include "tclInt.h"
16
#include "tclPort.h"
17
#include "tclMacInt.h"
18
#include <Aliases.h>
19
#include <Errors.h>
20
#include <Files.h>
21
#include <Gestalt.h>
22
#include <Processes.h>
23
#include <Strings.h>
24
#include <FSpCompat.h>
25
#include <MoreFiles.h>
26
#include <MoreFilesExtras.h>
27
 
28
/*
29
 * The following variable is used to tell whether this module has been
30
 * initialized.
31
 */
32
 
33
static int initialized = 0;
34
 
35
/*
36
 * The following are flags returned by GetOpenMode.  They
37
 * are or'd together to determine how opening and handling
38
 * a file should occur.
39
 */
40
 
41
#define TCL_RDONLY              (1<<0)
42
#define TCL_WRONLY              (1<<1)
43
#define TCL_RDWR                (1<<2)
44
#define TCL_CREAT               (1<<3)
45
#define TCL_TRUNC               (1<<4)
46
#define TCL_APPEND              (1<<5)
47
#define TCL_ALWAYS_APPEND       (1<<6)
48
#define TCL_EXCL                (1<<7)
49
#define TCL_NOCTTY              (1<<8)
50
#define TCL_NONBLOCK            (1<<9)
51
#define TCL_RW_MODES            (TCL_RDONLY|TCL_WRONLY|TCL_RDWR)
52
 
53
/*
54
 * This structure describes per-instance state of a
55
 * macintosh file based channel.
56
 */
57
 
58
typedef struct FileState {
59
    short fileRef;              /* Macintosh file reference number. */
60
    Tcl_Channel fileChan;       /* Pointer to the channel for this file. */
61
    int watchMask;              /* OR'ed set of flags indicating which events
62
                                 * are being watched. */
63
    int appendMode;             /* Flag to tell if in O_APPEND mode or not. */
64
    int volumeRef;              /* Flag to tell if in O_APPEND mode or not. */
65
    int pending;                /* 1 if message is pending on queue. */
66
    struct FileState *nextPtr;  /* Pointer to next registered file. */
67
} FileState;
68
 
69
/*
70
 * The following pointer refers to the head of the list of files managed
71
 * that are being watched for file events.
72
 */
73
 
74
static FileState *firstFilePtr;
75
 
76
/*
77
 * The following structure is what is added to the Tcl event queue when
78
 * file events are generated.
79
 */
80
 
81
typedef struct FileEvent {
82
    Tcl_Event header;           /* Information that is standard for
83
                                 * all events. */
84
    FileState *infoPtr;         /* Pointer to file info structure.  Note
85
                                 * that we still have to verify that the
86
                                 * file exists before dereferencing this
87
                                 * pointer. */
88
} FileEvent;
89
 
90
 
91
/*
92
 * Static routines for this file:
93
 */
94
 
95
static int              CommonGetHandle _ANSI_ARGS_((ClientData instanceData,
96
                            int direction, ClientData *handlePtr));
97
static void             CommonWatch _ANSI_ARGS_((ClientData instanceData,
98
                            int mask));
99
static int              FileBlockMode _ANSI_ARGS_((ClientData instanceData,
100
                            int mode));
101
static void             FileChannelExitHandler _ANSI_ARGS_((
102
                            ClientData clientData));
103
static void             FileCheckProc _ANSI_ARGS_((ClientData clientData,
104
                            int flags));
105
static int              FileClose _ANSI_ARGS_((ClientData instanceData,
106
                            Tcl_Interp *interp));
107
static int              FileEventProc _ANSI_ARGS_((Tcl_Event *evPtr,
108
                            int flags));
109
static void             FileInit _ANSI_ARGS_((void));
110
static int              FileInput _ANSI_ARGS_((ClientData instanceData,
111
                            char *buf, int toRead, int *errorCode));
112
static int              FileOutput _ANSI_ARGS_((ClientData instanceData,
113
                            char *buf, int toWrite, int *errorCode));
114
static int              FileSeek _ANSI_ARGS_((ClientData instanceData,
115
                            long offset, int mode, int *errorCode));
116
static void             FileSetupProc _ANSI_ARGS_((ClientData clientData,
117
                            int flags));
118
static int              GetOpenMode _ANSI_ARGS_((Tcl_Interp *interp,
119
                            char *string));
120
static Tcl_Channel      OpenFileChannel _ANSI_ARGS_((char *fileName, int mode,
121
                            int permissions, int *errorCodePtr));
122
static int              StdIOBlockMode _ANSI_ARGS_((ClientData instanceData,
123
                            int mode));
124
static int              StdIOClose _ANSI_ARGS_((ClientData instanceData,
125
                            Tcl_Interp *interp));
126
static int              StdIOInput _ANSI_ARGS_((ClientData instanceData,
127
                            char *buf, int toRead, int *errorCode));
128
static int              StdIOOutput _ANSI_ARGS_((ClientData instanceData,
129
                            char *buf, int toWrite, int *errorCode));
130
static int              StdIOSeek _ANSI_ARGS_((ClientData instanceData,
131
                            long offset, int mode, int *errorCode));
132
static int              StdReady _ANSI_ARGS_((ClientData instanceData,
133
                            int mask));
134
 
135
/*
136
 * This structure describes the channel type structure for file based IO:
137
 */
138
 
139
static Tcl_ChannelType consoleChannelType = {
140
    "file",                     /* Type name. */
141
    StdIOBlockMode,             /* Set blocking/nonblocking mode.*/
142
    StdIOClose,                 /* Close proc. */
143
    StdIOInput,                 /* Input proc. */
144
    StdIOOutput,                /* Output proc. */
145
    StdIOSeek,                  /* Seek proc. */
146
    NULL,                       /* Set option proc. */
147
    NULL,                       /* Get option proc. */
148
    CommonWatch,                /* Initialize notifier. */
149
    CommonGetHandle             /* Get OS handles out of channel. */
150
};
151
 
152
/*
153
 * This variable describes the channel type structure for file based IO.
154
 */
155
 
156
static Tcl_ChannelType fileChannelType = {
157
    "file",                     /* Type name. */
158
    FileBlockMode,              /* Set blocking or
159
                                 * non-blocking mode.*/
160
    FileClose,                  /* Close proc. */
161
    FileInput,                  /* Input proc. */
162
    FileOutput,                 /* Output proc. */
163
    FileSeek,                   /* Seek proc. */
164
    NULL,                       /* Set option proc. */
165
    NULL,                       /* Get option proc. */
166
    CommonWatch,                /* Initialize notifier. */
167
    CommonGetHandle             /* Get OS handles out of channel. */
168
};
169
 
170
 
171
/*
172
 * Hack to allow Mac Tk to override the TclGetStdChannels function.
173
 */
174
 
175
typedef void (*TclGetStdChannelsProc) _ANSI_ARGS_((Tcl_Channel *stdinPtr,
176
        Tcl_Channel *stdoutPtr, Tcl_Channel *stderrPtr));
177
 
178
TclGetStdChannelsProc getStdChannelsProc = NULL;
179
 
180
/*
181
 * Static variables to hold channels for stdin, stdout and stderr.
182
 */
183
 
184
static Tcl_Channel stdinChannel = NULL;
185
static Tcl_Channel stdoutChannel = NULL;
186
static Tcl_Channel stderrChannel = NULL;
187
 
188
/*
189
 *----------------------------------------------------------------------
190
 *
191
 * FileInit --
192
 *
193
 *      This function initializes the file channel event source.
194
 *
195
 * Results:
196
 *      None.
197
 *
198
 * Side effects:
199
 *      Creates a new event source.
200
 *
201
 *----------------------------------------------------------------------
202
 */
203
 
204
static void
205
FileInit()
206
{
207
    initialized = 1;
208
    firstFilePtr = NULL;
209
    Tcl_CreateEventSource(FileSetupProc, FileCheckProc, NULL);
210
    Tcl_CreateExitHandler(FileChannelExitHandler, NULL);
211
}
212
 
213
/*
214
 *----------------------------------------------------------------------
215
 *
216
 * FileChannelExitHandler --
217
 *
218
 *      This function is called to cleanup the channel driver before
219
 *      Tcl is unloaded.
220
 *
221
 * Results:
222
 *      None.
223
 *
224
 * Side effects:
225
 *      Destroys the communication window.
226
 *
227
 *----------------------------------------------------------------------
228
 */
229
 
230
static void
231
FileChannelExitHandler(
232
    ClientData clientData)      /* Old window proc */
233
{
234
    Tcl_DeleteEventSource(FileSetupProc, FileCheckProc, NULL);
235
    initialized = 0;
236
}
237
 
238
/*
239
 *----------------------------------------------------------------------
240
 *
241
 * FileSetupProc --
242
 *
243
 *      This procedure is invoked before Tcl_DoOneEvent blocks waiting
244
 *      for an event.
245
 *
246
 * Results:
247
 *      None.
248
 *
249
 * Side effects:
250
 *      Adjusts the block time if needed.
251
 *
252
 *----------------------------------------------------------------------
253
 */
254
 
255
void
256
FileSetupProc(
257
    ClientData data,            /* Not used. */
258
    int flags)                  /* Event flags as passed to Tcl_DoOneEvent. */
259
{
260
    FileState *infoPtr;
261
    Tcl_Time blockTime = { 0, 0 };
262
 
263
    if (!(flags & TCL_FILE_EVENTS)) {
264
        return;
265
    }
266
 
267
    /*
268
     * Check to see if there is a ready file.  If so, poll.
269
     */
270
 
271
    for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
272
        if (infoPtr->watchMask) {
273
            Tcl_SetMaxBlockTime(&blockTime);
274
            break;
275
        }
276
    }
277
}
278
 
279
/*
280
 *----------------------------------------------------------------------
281
 *
282
 * FileCheckProc --
283
 *
284
 *      This procedure is called by Tcl_DoOneEvent to check the file
285
 *      event source for events.
286
 *
287
 * Results:
288
 *      None.
289
 *
290
 * Side effects:
291
 *      May queue an event.
292
 *
293
 *----------------------------------------------------------------------
294
 */
295
 
296
static void
297
FileCheckProc(
298
    ClientData data,            /* Not used. */
299
    int flags)                  /* Event flags as passed to Tcl_DoOneEvent. */
300
{
301
    FileEvent *evPtr;
302
    FileState *infoPtr;
303
    int sentMsg = 0;
304
    Tcl_Time blockTime = { 0, 0 };
305
 
306
    if (!(flags & TCL_FILE_EVENTS)) {
307
        return;
308
    }
309
 
310
    /*
311
     * Queue events for any ready files that don't already have events
312
     * queued (caused by persistent states that won't generate WinSock
313
     * events).
314
     */
315
 
316
    for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
317
        if (infoPtr->watchMask && !infoPtr->pending) {
318
            infoPtr->pending = 1;
319
            evPtr = (FileEvent *) ckalloc(sizeof(FileEvent));
320
            evPtr->header.proc = FileEventProc;
321
            evPtr->infoPtr = infoPtr;
322
            Tcl_QueueEvent((Tcl_Event *) evPtr, TCL_QUEUE_TAIL);
323
        }
324
    }
325
}
326
 
327
/*----------------------------------------------------------------------
328
 *
329
 * FileEventProc --
330
 *
331
 *      This function is invoked by Tcl_ServiceEvent when a file event
332
 *      reaches the front of the event queue.  This procedure invokes
333
 *      Tcl_NotifyChannel on the file.
334
 *
335
 * Results:
336
 *      Returns 1 if the event was handled, meaning it should be removed
337
 *      from the queue.  Returns 0 if the event was not handled, meaning
338
 *      it should stay on the queue.  The only time the event isn't
339
 *      handled is if the TCL_FILE_EVENTS flag bit isn't set.
340
 *
341
 * Side effects:
342
 *      Whatever the notifier callback does.
343
 *
344
 *----------------------------------------------------------------------
345
 */
346
 
347
static int
348
FileEventProc(
349
    Tcl_Event *evPtr,           /* Event to service. */
350
    int flags)                  /* Flags that indicate what events to
351
                                 * handle, such as TCL_FILE_EVENTS. */
352
{
353
    FileEvent *fileEvPtr = (FileEvent *)evPtr;
354
    FileState *infoPtr;
355
 
356
    if (!(flags & TCL_FILE_EVENTS)) {
357
        return 0;
358
    }
359
 
360
    /*
361
     * Search through the list of watched files for the one whose handle
362
     * matches the event.  We do this rather than simply dereferencing
363
     * the handle in the event so that files can be deleted while the
364
     * event is in the queue.
365
     */
366
 
367
    for (infoPtr = firstFilePtr; infoPtr != NULL; infoPtr = infoPtr->nextPtr) {
368
        if (fileEvPtr->infoPtr == infoPtr) {
369
            infoPtr->pending = 0;
370
            Tcl_NotifyChannel(infoPtr->fileChan, infoPtr->watchMask);
371
            break;
372
        }
373
    }
374
    return 1;
375
}
376
 
377
/*
378
 *----------------------------------------------------------------------
379
 *
380
 * StdIOBlockMode --
381
 *
382
 *      Set blocking or non-blocking mode on channel.
383
 *
384
 * Results:
385
 *      0 if successful, errno when failed.
386
 *
387
 * Side effects:
388
 *      Sets the device into blocking or non-blocking mode.
389
 *
390
 *----------------------------------------------------------------------
391
 */
392
 
393
static int
394
StdIOBlockMode(
395
    ClientData instanceData,            /* Unused. */
396
    int mode)                           /* The mode to set. */
397
{
398
    /*
399
     * Do not allow putting stdin, stdout or stderr into nonblocking mode.
400
     */
401
 
402
    if (mode == TCL_MODE_NONBLOCKING) {
403
        return EFAULT;
404
    }
405
 
406
    return 0;
407
}
408
 
409
/*
410
 *----------------------------------------------------------------------
411
 *
412
 * StdIOClose --
413
 *
414
 *      Closes the IO channel.
415
 *
416
 * Results:
417
 *      0 if successful, the value of errno if failed.
418
 *
419
 * Side effects:
420
 *      Closes the physical channel
421
 *
422
 *----------------------------------------------------------------------
423
 */
424
 
425
static int
426
StdIOClose(
427
    ClientData instanceData,    /* Unused. */
428
    Tcl_Interp *interp)         /* Unused. */
429
{
430
    int fd, errorCode = 0;
431
 
432
    /*
433
     * Invalidate the stdio cache if necessary.  Note that we assume that
434
     * the stdio file and channel pointers will become invalid at the same
435
     * time.
436
     */
437
 
438
    fd = (int) ((FileState*)instanceData)->fileRef;
439
    if (fd == 0) {
440
        fd = 0;
441
        stdinChannel = NULL;
442
    } else if (fd == 1) {
443
        stdoutChannel = NULL;
444
    } else if (fd == 2) {
445
        stderrChannel = NULL;
446
    } else {
447
        panic("recieved invalid std file");
448
    }
449
 
450
    if (close(fd) < 0) {
451
        errorCode = errno;
452
    }
453
 
454
    return errorCode;
455
}
456
 
457
/*
458
 *----------------------------------------------------------------------
459
 *
460
 * CommonGetHandle --
461
 *
462
 *      Called from Tcl_GetChannelFile to retrieve OS handles from inside
463
 *      a file based channel.
464
 *
465
 * Results:
466
 *      The appropriate handle or NULL if not present.
467
 *
468
 * Side effects:
469
 *      None.
470
 *
471
 *----------------------------------------------------------------------
472
 */
473
 
474
static int
475
CommonGetHandle(
476
    ClientData instanceData,            /* The file state. */
477
    int direction,                      /* Which handle to retrieve? */
478
    ClientData *handlePtr)
479
{
480
    if ((direction == TCL_READABLE) || (direction == TCL_WRITABLE)) {
481
        *handlePtr = (ClientData) ((FileState*)instanceData)->fileRef;
482
        return TCL_OK;
483
    }
484
    return TCL_ERROR;
485
}
486
 
487
/*
488
 *----------------------------------------------------------------------
489
 *
490
 * StdIOInput --
491
 *
492
 *      Reads input from the IO channel into the buffer given. Returns
493
 *      count of how many bytes were actually read, and an error indication.
494
 *
495
 * Results:
496
 *      A count of how many bytes were read is returned and an error
497
 *      indication is returned in an output argument.
498
 *
499
 * Side effects:
500
 *      Reads input from the actual channel.
501
 *
502
 *----------------------------------------------------------------------
503
 */
504
 
505
int
506
StdIOInput(
507
    ClientData instanceData,            /* Unused. */
508
    char *buf,                          /* Where to store data read. */
509
    int bufSize,                        /* How much space is available
510
                                         * in the buffer? */
511
    int *errorCode)                     /* Where to store error code. */
512
{
513
    int fd;
514
    int bytesRead;                      /* How many bytes were read? */
515
 
516
    *errorCode = 0;
517
    errno = 0;
518
    fd = (int) ((FileState*)instanceData)->fileRef;
519
    bytesRead = read(fd, buf, (size_t) bufSize);
520
    if (bytesRead > -1) {
521
        return bytesRead;
522
    }
523
    *errorCode = errno;
524
    return -1;
525
}
526
 
527
/*
528
 *----------------------------------------------------------------------
529
 *
530
 * StdIOOutput--
531
 *
532
 *      Writes the given output on the IO channel. Returns count of how
533
 *      many characters were actually written, and an error indication.
534
 *
535
 * Results:
536
 *      A count of how many characters were written is returned and an
537
 *      error indication is returned in an output argument.
538
 *
539
 * Side effects:
540
 *      Writes output on the actual channel.
541
 *
542
 *----------------------------------------------------------------------
543
 */
544
 
545
static int
546
StdIOOutput(
547
    ClientData instanceData,            /* Unused. */
548
    char *buf,                          /* The data buffer. */
549
    int toWrite,                        /* How many bytes to write? */
550
    int *errorCode)                     /* Where to store error code. */
551
{
552
    int written;
553
    int fd;
554
 
555
    *errorCode = 0;
556
    errno = 0;
557
    fd = (int) ((FileState*)instanceData)->fileRef;
558
    written = write(fd, buf, (size_t) toWrite);
559
    if (written > -1) {
560
        return written;
561
    }
562
    *errorCode = errno;
563
    return -1;
564
}
565
 
566
/*
567
 *----------------------------------------------------------------------
568
 *
569
 * StdIOSeek --
570
 *
571
 *      Seeks on an IO channel. Returns the new position.
572
 *
573
 * Results:
574
 *      -1 if failed, the new position if successful. If failed, it
575
 *      also sets *errorCodePtr to the error code.
576
 *
577
 * Side effects:
578
 *      Moves the location at which the channel will be accessed in
579
 *      future operations.
580
 *
581
 *----------------------------------------------------------------------
582
 */
583
 
584
static int
585
StdIOSeek(
586
    ClientData instanceData,                    /* Unused. */
587
    long offset,                                /* Offset to seek to. */
588
    int mode,                                   /* Relative to where
589
                                                 * should we seek? */
590
    int *errorCodePtr)                          /* To store error code. */
591
{
592
    int newLoc;
593
    int fd;
594
 
595
    *errorCodePtr = 0;
596
    fd = (int) ((FileState*)instanceData)->fileRef;
597
    newLoc = lseek(fd, offset, mode);
598
    if (newLoc > -1) {
599
        return newLoc;
600
    }
601
    *errorCodePtr = errno;
602
    return -1;
603
}
604
 
605
/*
606
 *----------------------------------------------------------------------
607
 *
608
 * Tcl_PidObjCmd --
609
 *
610
 *      This procedure is invoked to process the "pid" Tcl command.
611
 *      See the user documentation for details on what it does.
612
 *
613
 * Results:
614
 *      A standard Tcl result.
615
 *
616
 * Side effects:
617
 *      See the user documentation.
618
 *
619
 *----------------------------------------------------------------------
620
 */
621
 
622
        /* ARGSUSED */
623
int
624
Tcl_PidObjCmd(dummy, interp, objc, objv)
625
    ClientData dummy;           /* Not used. */
626
    Tcl_Interp *interp;         /* Current interpreter. */
627
    int objc;                   /* Number of arguments. */
628
    Tcl_Obj *CONST *objv;       /* Argument strings. */
629
{
630
    ProcessSerialNumber psn;
631
    char buf[20];
632
    Tcl_Channel chan;
633
    Tcl_Obj *resultPtr;
634
 
635
    if (objc > 2) {
636
        Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
637
        return TCL_ERROR;
638
    }
639
    if (objc == 1) {
640
        resultPtr = Tcl_GetObjResult(interp);
641
        GetCurrentProcess(&psn);
642
        sprintf(buf, "0x%08x%08x", psn.highLongOfPSN, psn.lowLongOfPSN);
643
        Tcl_SetStringObj(resultPtr, buf, -1);
644
    } else {
645
        chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
646
                NULL);
647
        if (chan == (Tcl_Channel) NULL) {
648
            return TCL_ERROR;
649
        }
650
        /*
651
         * We can't create pipelines on the Mac so
652
         * this will always return an empty list.
653
         */
654
    }
655
 
656
    return TCL_OK;
657
}
658
 
659
/*
660
 *----------------------------------------------------------------------
661
 *
662
 * TclGetDefaultStdChannel --
663
 *
664
 *      Constructs a channel for the specified standard OS handle.
665
 *
666
 * Results:
667
 *      Returns the specified default standard channel, or NULL.
668
 *
669
 * Side effects:
670
 *      May cause the creation of a standard channel and the underlying
671
 *      file.
672
 *
673
 *----------------------------------------------------------------------
674
 */
675
 
676
Tcl_Channel
677
TclGetDefaultStdChannel(
678
    int type)                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
679
{
680
    Tcl_Channel channel = NULL;
681
    int fd = 0;                  /* Initializations needed to prevent */
682
    int mode = 0;                /* compiler warning (used before set). */
683
    char *bufMode = NULL;
684
    char channelName[20];
685
    int channelPermissions;
686
    FileState *fileState;
687
 
688
    /*
689
     * If the channels were not created yet, create them now and
690
     * store them in the static variables.
691
     */
692
 
693
    switch (type) {
694
        case TCL_STDIN:
695
            fd = 0;
696
            channelPermissions = TCL_READABLE;
697
            bufMode = "line";
698
            break;
699
        case TCL_STDOUT:
700
            fd = 1;
701
            channelPermissions = TCL_WRITABLE;
702
            bufMode = "line";
703
            break;
704
        case TCL_STDERR:
705
            fd = 2;
706
            channelPermissions = TCL_WRITABLE;
707
            bufMode = "none";
708
            break;
709
        default:
710
            panic("TclGetDefaultStdChannel: Unexpected channel type");
711
            break;
712
    }
713
 
714
    sprintf(channelName, "console%d", (int) fd);
715
    fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
716
    channel = Tcl_CreateChannel(&consoleChannelType, channelName,
717
            (ClientData) fileState, channelPermissions);
718
    fileState->fileChan = channel;
719
    fileState->fileRef = fd;
720
 
721
    /*
722
     * Set up the normal channel options for stdio handles.
723
     */
724
 
725
    Tcl_SetChannelOption(NULL, channel, "-translation", "cr");
726
    Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
727
 
728
    return channel;
729
}
730
 
731
/*
732
 *----------------------------------------------------------------------
733
 *
734
 * TclpOpenFileChannel --
735
 *
736
 *      Open an File based channel on Unix systems.
737
 *
738
 * Results:
739
 *      The new channel or NULL. If NULL, the output argument
740
 *      errorCodePtr is set to a POSIX error.
741
 *
742
 * Side effects:
743
 *      May open the channel and may cause creation of a file on the
744
 *      file system.
745
 *
746
 *----------------------------------------------------------------------
747
 */
748
 
749
Tcl_Channel
750
TclpOpenFileChannel(
751
    Tcl_Interp *interp,                 /* Interpreter for error reporting;
752
                                         * can be NULL. */
753
    char *fileName,                     /* Name of file to open. */
754
    char *modeString,                   /* A list of POSIX open modes or
755
                                         * a string such as "rw". */
756
    int permissions)                    /* If the open involves creating a
757
                                         * file, with what modes to create
758
                                         * it? */
759
{
760
    Tcl_Channel chan;
761
    int mode;
762
    char *nativeName;
763
    Tcl_DString buffer;
764
    int errorCode;
765
 
766
    mode = GetOpenMode(interp, modeString);
767
    if (mode == -1) {
768
        return NULL;
769
    }
770
 
771
    nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
772
    if (nativeName == NULL) {
773
        return NULL;
774
    }
775
 
776
    chan = OpenFileChannel(nativeName, mode, permissions, &errorCode);
777
    Tcl_DStringFree(&buffer);
778
 
779
    if (chan == NULL) {
780
        Tcl_SetErrno(errorCode);
781
        if (interp != (Tcl_Interp *) NULL) {
782
            Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
783
                    Tcl_PosixError(interp), (char *) NULL);
784
        }
785
        return NULL;
786
    }
787
 
788
    return chan;
789
}
790
 
791
/*
792
 *----------------------------------------------------------------------
793
 *
794
 * OpenFileChannel--
795
 *
796
 *      Opens a Macintosh file and creates a Tcl channel to control it.
797
 *
798
 * Results:
799
 *      A Tcl channel.
800
 *
801
 * Side effects:
802
 *      Will open a Macintosh file.
803
 *
804
 *----------------------------------------------------------------------
805
 */
806
 
807
static Tcl_Channel
808
OpenFileChannel(
809
    char *fileName,                     /* Name of file to open. */
810
    int mode,                           /* Mode for opening file. */
811
    int permissions,                    /* If the open involves creating a
812
                                         * file, with what modes to create
813
                                         * it? */
814
    int *errorCodePtr)                  /* Where to store error code. */
815
{
816
    int channelPermissions;
817
    Tcl_Channel chan;
818
    char macPermision;
819
    FSSpec fileSpec;
820
    OSErr err;
821
    short fileRef;
822
    FileState *fileState;
823
    char channelName[64];
824
 
825
    /*
826
     * Note we use fsRdWrShPerm instead of fsRdWrPerm which allows shared
827
     * writes on a file.  This isn't common on a mac but is common with
828
     * Windows and UNIX and the feature is used by Tcl.
829
     */
830
 
831
    switch (mode & (TCL_RDONLY | TCL_WRONLY | TCL_RDWR)) {
832
        case TCL_RDWR:
833
            channelPermissions = (TCL_READABLE | TCL_WRITABLE);
834
            macPermision = fsRdWrShPerm;
835
            break;
836
        case TCL_WRONLY:
837
            /*
838
             * Mac's fsRdPerm permission actually defaults to fsRdWrPerm because
839
             * the Mac OS doesn't realy support write only access.  We explicitly
840
             * set the permission fsRdWrShPerm so that we can have shared write
841
             * access.
842
             */
843
            channelPermissions = TCL_WRITABLE;
844
            macPermision = fsRdWrShPerm;
845
            break;
846
        case TCL_RDONLY:
847
        default:
848
            channelPermissions = TCL_READABLE;
849
            macPermision = fsRdPerm;
850
            break;
851
    }
852
 
853
    err = FSpLocationFromPath(strlen(fileName), fileName, &fileSpec);
854
    if ((err != noErr) && (err != fnfErr)) {
855
        *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
856
        Tcl_SetErrno(errno);
857
        return NULL;
858
    }
859
 
860
    if ((err == fnfErr) && (mode & TCL_CREAT)) {
861
        err = HCreate(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, 'MPW ', 'TEXT');
862
        if (err != noErr) {
863
            *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
864
            Tcl_SetErrno(errno);
865
            return NULL;
866
        }
867
    } else if ((mode & TCL_CREAT) && (mode & TCL_EXCL)) {
868
        *errorCodePtr = errno = EEXIST;
869
        Tcl_SetErrno(errno);
870
        return NULL;
871
    }
872
 
873
    err = HOpenDF(fileSpec.vRefNum, fileSpec.parID, fileSpec.name, macPermision, &fileRef);
874
    if (err != noErr) {
875
        *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
876
        Tcl_SetErrno(errno);
877
        return NULL;
878
    }
879
 
880
    if (mode & TCL_TRUNC) {
881
        SetEOF(fileRef, 0);
882
    }
883
 
884
    sprintf(channelName, "file%d", (int) fileRef);
885
    fileState = (FileState *) ckalloc((unsigned) sizeof(FileState));
886
    chan = Tcl_CreateChannel(&fileChannelType, channelName,
887
        (ClientData) fileState, channelPermissions);
888
    if (chan == (Tcl_Channel) NULL) {
889
        *errorCodePtr = errno = EFAULT;
890
        Tcl_SetErrno(errno);
891
        FSClose(fileRef);
892
        ckfree((char *) fileState);
893
        return NULL;
894
    }
895
 
896
    fileState->fileChan = chan;
897
    fileState->volumeRef = fileSpec.vRefNum;
898
    fileState->fileRef = fileRef;
899
    fileState->pending = 0;
900
    fileState->watchMask = 0;
901
    if (mode & TCL_ALWAYS_APPEND) {
902
        fileState->appendMode = true;
903
    } else {
904
        fileState->appendMode = false;
905
    }
906
 
907
    if ((mode & TCL_ALWAYS_APPEND) || (mode & TCL_APPEND)) {
908
        if (Tcl_Seek(chan, 0, SEEK_END) < 0) {
909
            *errorCodePtr = errno = EFAULT;
910
            Tcl_SetErrno(errno);
911
            Tcl_Close(NULL, chan);
912
            FSClose(fileRef);
913
            ckfree((char *) fileState);
914
            return NULL;
915
        }
916
    }
917
 
918
    return chan;
919
}
920
 
921
/*
922
 *----------------------------------------------------------------------
923
 *
924
 * FileBlockMode --
925
 *
926
 *      Set blocking or non-blocking mode on channel.  Macintosh files
927
 *      can never really be set to blocking or non-blocking modes.
928
 *      However, we don't generate an error - we just return success.
929
 *
930
 * Results:
931
 *      0 if successful, errno when failed.
932
 *
933
 * Side effects:
934
 *      Sets the device into blocking or non-blocking mode.
935
 *
936
 *----------------------------------------------------------------------
937
 */
938
 
939
static int
940
FileBlockMode(
941
    ClientData instanceData,            /* Unused. */
942
    int mode)                           /* The mode to set. */
943
{
944
    return 0;
945
}
946
 
947
/*
948
 *----------------------------------------------------------------------
949
 *
950
 * FileClose --
951
 *
952
 *      Closes the IO channel.
953
 *
954
 * Results:
955
 *      0 if successful, the value of errno if failed.
956
 *
957
 * Side effects:
958
 *      Closes the physical channel
959
 *
960
 *----------------------------------------------------------------------
961
 */
962
 
963
static int
964
FileClose(
965
    ClientData instanceData,    /* Unused. */
966
    Tcl_Interp *interp)         /* Unused. */
967
{
968
    FileState *fileState = (FileState *) instanceData;
969
    int errorCode = 0;
970
    OSErr err;
971
 
972
    err = FSClose(fileState->fileRef);
973
    FlushVol(NULL, fileState->volumeRef);
974
    if (err != noErr) {
975
        errorCode = errno = TclMacOSErrorToPosixError(err);
976
        panic("error during file close");
977
    }
978
 
979
    ckfree((char *) fileState);
980
    Tcl_SetErrno(errorCode);
981
    return errorCode;
982
}
983
 
984
/*
985
 *----------------------------------------------------------------------
986
 *
987
 * FileInput --
988
 *
989
 *      Reads input from the IO channel into the buffer given. Returns
990
 *      count of how many bytes were actually read, and an error indication.
991
 *
992
 * Results:
993
 *      A count of how many bytes were read is returned and an error
994
 *      indication is returned in an output argument.
995
 *
996
 * Side effects:
997
 *      Reads input from the actual channel.
998
 *
999
 *----------------------------------------------------------------------
1000
 */
1001
 
1002
int
1003
FileInput(
1004
    ClientData instanceData,    /* Unused. */
1005
    char *buffer,                               /* Where to store data read. */
1006
    int bufSize,                                /* How much space is available
1007
                                 * in the buffer? */
1008
    int *errorCodePtr)                  /* Where to store error code. */
1009
{
1010
    FileState *fileState = (FileState *) instanceData;
1011
    OSErr err;
1012
    long length = bufSize;
1013
 
1014
    *errorCodePtr = 0;
1015
    errno = 0;
1016
    err = FSRead(fileState->fileRef, &length, buffer);
1017
    if ((err == noErr) || (err == eofErr)) {
1018
        return length;
1019
    } else {
1020
        switch (err) {
1021
            case ioErr:
1022
                *errorCodePtr = errno = EIO;
1023
            case afpAccessDenied:
1024
                *errorCodePtr = errno = EACCES;
1025
            default:
1026
                *errorCodePtr = errno = EINVAL;
1027
        }
1028
        return -1;
1029
    }
1030
    *errorCodePtr = errno;
1031
    return -1;
1032
}
1033
 
1034
/*
1035
 *----------------------------------------------------------------------
1036
 *
1037
 * FileOutput--
1038
 *
1039
 *      Writes the given output on the IO channel. Returns count of how
1040
 *      many characters were actually written, and an error indication.
1041
 *
1042
 * Results:
1043
 *      A count of how many characters were written is returned and an
1044
 *      error indication is returned in an output argument.
1045
 *
1046
 * Side effects:
1047
 *      Writes output on the actual channel.
1048
 *
1049
 *----------------------------------------------------------------------
1050
 */
1051
 
1052
static int
1053
FileOutput(
1054
    ClientData instanceData,            /* Unused. */
1055
    char *buffer,                       /* The data buffer. */
1056
    int toWrite,                        /* How many bytes to write? */
1057
    int *errorCodePtr)                  /* Where to store error code. */
1058
{
1059
    FileState *fileState = (FileState *) instanceData;
1060
    long length = toWrite;
1061
    OSErr err;
1062
 
1063
    *errorCodePtr = 0;
1064
    errno = 0;
1065
 
1066
    if (fileState->appendMode == true) {
1067
        FileSeek(instanceData, 0, SEEK_END, errorCodePtr);
1068
        *errorCodePtr = 0;
1069
    }
1070
 
1071
    err = FSWrite(fileState->fileRef, &length, buffer);
1072
    if (err == noErr) {
1073
        err = FlushFile(fileState->fileRef);
1074
    } else {
1075
        *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1076
        return -1;
1077
    }
1078
    return length;
1079
}
1080
 
1081
/*
1082
 *----------------------------------------------------------------------
1083
 *
1084
 * FileSeek --
1085
 *
1086
 *      Seeks on an IO channel. Returns the new position.
1087
 *
1088
 * Results:
1089
 *      -1 if failed, the new position if successful. If failed, it
1090
 *      also sets *errorCodePtr to the error code.
1091
 *
1092
 * Side effects:
1093
 *      Moves the location at which the channel will be accessed in
1094
 *      future operations.
1095
 *
1096
 *----------------------------------------------------------------------
1097
 */
1098
 
1099
static int
1100
FileSeek(
1101
    ClientData instanceData,    /* Unused. */
1102
    long offset,                                /* Offset to seek to. */
1103
    int mode,                                   /* Relative to where
1104
                                 * should we seek? */
1105
    int *errorCodePtr)                  /* To store error code. */
1106
{
1107
    FileState *fileState = (FileState *) instanceData;
1108
    IOParam pb;
1109
    OSErr err;
1110
 
1111
    *errorCodePtr = 0;
1112
    pb.ioCompletion = NULL;
1113
    pb.ioRefNum = fileState->fileRef;
1114
    if (mode == SEEK_SET) {
1115
        pb.ioPosMode = fsFromStart;
1116
    } else if (mode == SEEK_END) {
1117
        pb.ioPosMode = fsFromLEOF;
1118
    } else if (mode == SEEK_CUR) {
1119
        err = PBGetFPosSync((ParmBlkPtr) &pb);
1120
        if (pb.ioResult == noErr) {
1121
            if (offset == 0) {
1122
                return pb.ioPosOffset;
1123
            }
1124
            offset += pb.ioPosOffset;
1125
        }
1126
        pb.ioPosMode = fsFromStart;
1127
    }
1128
    pb.ioPosOffset = offset;
1129
    err = PBSetFPosSync((ParmBlkPtr) &pb);
1130
    if (pb.ioResult == noErr){
1131
        return pb.ioPosOffset;
1132
    } else if (pb.ioResult == eofErr) {
1133
        long currentEOF, newEOF;
1134
        long buffer, i, length;
1135
 
1136
        err = PBGetEOFSync((ParmBlkPtr) &pb);
1137
        currentEOF = (long) pb.ioMisc;
1138
        if (mode == SEEK_SET) {
1139
            newEOF = offset;
1140
        } else if (mode == SEEK_END) {
1141
            newEOF = offset + currentEOF;
1142
        } else if (mode == SEEK_CUR) {
1143
            err = PBGetFPosSync((ParmBlkPtr) &pb);
1144
            newEOF = offset + pb.ioPosOffset;
1145
        }
1146
 
1147
        /*
1148
         * Write 0's to the new EOF.
1149
         */
1150
        pb.ioPosOffset = 0;
1151
        pb.ioPosMode = fsFromLEOF;
1152
        err = PBGetFPosSync((ParmBlkPtr) &pb);
1153
        length = 1;
1154
        buffer = 0;
1155
        for (i = 0; i < (newEOF - currentEOF); i++) {
1156
            err = FSWrite(fileState->fileRef, &length, &buffer);
1157
        }
1158
        err = PBGetFPosSync((ParmBlkPtr) &pb);
1159
        if (pb.ioResult == noErr){
1160
            return pb.ioPosOffset;
1161
        }
1162
    }
1163
    *errorCodePtr = errno = TclMacOSErrorToPosixError(err);
1164
    return -1;
1165
}
1166
 
1167
/*
1168
 *----------------------------------------------------------------------
1169
 *
1170
 * CommonWatch --
1171
 *
1172
 *      Initialize the notifier to watch handles from this channel.
1173
 *
1174
 * Results:
1175
 *      None.
1176
 *
1177
 * Side effects:
1178
 *      None.
1179
 *
1180
 *----------------------------------------------------------------------
1181
 */
1182
 
1183
static void
1184
CommonWatch(
1185
    ClientData instanceData,            /* The file state. */
1186
    int mask)                           /* Events of interest; an OR-ed
1187
                                         * combination of TCL_READABLE,
1188
                                         * TCL_WRITABLE and TCL_EXCEPTION. */
1189
{
1190
    FileState **nextPtrPtr, *ptr;
1191
    FileState *infoPtr = (FileState *) instanceData;
1192
    int oldMask = infoPtr->watchMask;
1193
 
1194
    if (!initialized) {
1195
        FileInit();
1196
    }
1197
 
1198
    infoPtr->watchMask = mask;
1199
    if (infoPtr->watchMask) {
1200
        if (!oldMask) {
1201
            infoPtr->nextPtr = firstFilePtr;
1202
            firstFilePtr = infoPtr;
1203
        }
1204
    } else {
1205
        if (oldMask) {
1206
            /*
1207
             * Remove the file from the list of watched files.
1208
             */
1209
 
1210
            for (nextPtrPtr = &firstFilePtr, ptr = *nextPtrPtr;
1211
                 ptr != NULL;
1212
                 nextPtrPtr = &ptr->nextPtr, ptr = *nextPtrPtr) {
1213
                if (infoPtr == ptr) {
1214
                    *nextPtrPtr = ptr->nextPtr;
1215
                    break;
1216
                }
1217
            }
1218
        }
1219
    }
1220
}
1221
 
1222
/*
1223
 *----------------------------------------------------------------------
1224
 *
1225
 * GetOpenMode --
1226
 *
1227
 * Description:
1228
 *      Computes a POSIX mode mask from a given string and also sets
1229
 *      a flag to indicate whether the caller should seek to EOF during
1230
 *      opening of the file.
1231
 *
1232
 * Results:
1233
 *      On success, returns mode to pass to "open". If an error occurs, the
1234
 *      returns -1 and if interp is not NULL, sets interp->result to an
1235
 *      error message.
1236
 *
1237
 * Side effects:
1238
 *      Sets the integer referenced by seekFlagPtr to 1 if the caller
1239
 *      should seek to EOF during opening the file.
1240
 *
1241
 * Special note:
1242
 *      This code is based on a prototype implementation contributed
1243
 *      by Mark Diekhans.
1244
 *
1245
 *----------------------------------------------------------------------
1246
 */
1247
 
1248
static int
1249
GetOpenMode(
1250
    Tcl_Interp *interp,                 /* Interpreter to use for error
1251
                                         * reporting - may be NULL. */
1252
    char *string)                       /* Mode string, e.g. "r+" or
1253
                                         * "RDONLY CREAT". */
1254
{
1255
    int mode, modeArgc, c, i, gotRW;
1256
    char **modeArgv, *flag;
1257
 
1258
    /*
1259
     * Check for the simpler fopen-like access modes (e.g. "r").  They
1260
     * are distinguished from the POSIX access modes by the presence
1261
     * of a lower-case first letter.
1262
     */
1263
 
1264
    mode = 0;
1265
    if (islower(UCHAR(string[0]))) {
1266
        switch (string[0]) {
1267
            case 'r':
1268
                mode = TCL_RDONLY;
1269
                break;
1270
            case 'w':
1271
                mode = TCL_WRONLY|TCL_CREAT|TCL_TRUNC;
1272
                break;
1273
            case 'a':
1274
                mode = TCL_WRONLY|TCL_CREAT|TCL_APPEND;
1275
                break;
1276
            default:
1277
                error:
1278
                if (interp != (Tcl_Interp *) NULL) {
1279
                    Tcl_AppendResult(interp,
1280
                            "illegal access mode \"", string, "\"",
1281
                            (char *) NULL);
1282
                }
1283
                return -1;
1284
        }
1285
        if (string[1] == '+') {
1286
            mode &= ~(TCL_RDONLY|TCL_WRONLY);
1287
            mode |= TCL_RDWR;
1288
            if (string[2] != 0) {
1289
                goto error;
1290
            }
1291
        } else if (string[1] != 0) {
1292
            goto error;
1293
        }
1294
        return mode;
1295
    }
1296
 
1297
    /*
1298
     * The access modes are specified using a list of POSIX modes
1299
     * such as TCL_CREAT.
1300
     */
1301
 
1302
    if (Tcl_SplitList(interp, string, &modeArgc, &modeArgv) != TCL_OK) {
1303
        if (interp != (Tcl_Interp *) NULL) {
1304
            Tcl_AddErrorInfo(interp,
1305
                    "\n    while processing open access modes \"");
1306
            Tcl_AddErrorInfo(interp, string);
1307
            Tcl_AddErrorInfo(interp, "\"");
1308
        }
1309
        return -1;
1310
    }
1311
 
1312
    gotRW = 0;
1313
    for (i = 0; i < modeArgc; i++) {
1314
        flag = modeArgv[i];
1315
        c = flag[0];
1316
        if ((c == 'R') && (strcmp(flag, "RDONLY") == 0)) {
1317
            mode = (mode & ~TCL_RW_MODES) | TCL_RDONLY;
1318
            gotRW = 1;
1319
        } else if ((c == 'W') && (strcmp(flag, "WRONLY") == 0)) {
1320
            mode = (mode & ~TCL_RW_MODES) | TCL_WRONLY;
1321
            gotRW = 1;
1322
        } else if ((c == 'R') && (strcmp(flag, "RDWR") == 0)) {
1323
            mode = (mode & ~TCL_RW_MODES) | TCL_RDWR;
1324
            gotRW = 1;
1325
        } else if ((c == 'A') && (strcmp(flag, "APPEND") == 0)) {
1326
            mode |= TCL_ALWAYS_APPEND;
1327
        } else if ((c == 'C') && (strcmp(flag, "CREAT") == 0)) {
1328
            mode |= TCL_CREAT;
1329
        } else if ((c == 'E') && (strcmp(flag, "EXCL") == 0)) {
1330
            mode |= TCL_EXCL;
1331
        } else if ((c == 'N') && (strcmp(flag, "NOCTTY") == 0)) {
1332
            mode |= TCL_NOCTTY;
1333
        } else if ((c == 'N') && (strcmp(flag, "NONBLOCK") == 0)) {
1334
            mode |= TCL_NONBLOCK;
1335
        } else if ((c == 'T') && (strcmp(flag, "TRUNC") == 0)) {
1336
            mode |= TCL_TRUNC;
1337
        } else {
1338
            if (interp != (Tcl_Interp *) NULL) {
1339
                Tcl_AppendResult(interp, "invalid access mode \"", flag,
1340
                        "\": must be RDONLY, WRONLY, RDWR, APPEND, CREAT",
1341
                        " EXCL, NOCTTY, NONBLOCK, or TRUNC", (char *) NULL);
1342
            }
1343
            ckfree((char *) modeArgv);
1344
            return -1;
1345
        }
1346
    }
1347
    ckfree((char *) modeArgv);
1348
    if (!gotRW) {
1349
        if (interp != (Tcl_Interp *) NULL) {
1350
            Tcl_AppendResult(interp, "access mode must include either",
1351
                    " RDONLY, WRONLY, or RDWR", (char *) NULL);
1352
        }
1353
        return -1;
1354
    }
1355
    return mode;
1356
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.