Rune Serialize - Stabilization, Generation, major overhaul
[rune.git] / classes / stdio / fopen.d
1 # STDIO/FOPEN.D - Implement fopen() and fclose()
2 #
3
4 #%doc
5 #
6 # Open or create a buffered file object with the modes specified.
7 # Bidirectional mode is enabled if the descriptor is not a regular file.
8 # Any newly created descriptors are automatically O_CLOEXEC (the caller
9 # does not have to supply the 'e' flag in the mode string).
10 #
11 # fdopen() and fdreopen() do not change the current O_CLOEXEC state of
12 # the underlying descriptor unless you pass the 'e' flag, in which case
13 # O_CLOEXEC is set.  But it is likely already set.
14 #
15 File fp;
16
17 public global
18 File @
19 File.fdopen(int fd, const char *mode)
20 {
21         heap File fp;
22
23         fp.fdreopen(fd, mode);
24
25         if (fp.fs.error)
26                 return NULL;
27         else
28                 return &fp;
29 }
30
31 public global
32 File @
33 File.fopen(const char *path, const char *mode)
34 {
35         heap File fp;
36
37         fp.freopen(path, mode);
38
39         if (fp.fs.error)
40                 return NULL;
41         else
42                 return &fp;
43 }
44
45
46 public method
47 int
48 File.fdreopen(int fd, const char *mode)
49 {
50         if (this.fs.fd >= 0)
51                 this.fclose();
52         this.fs.fd = fd;
53         this.fs.error = 0;
54         this.mode = File.M_FULL;
55         this.parsemode(mode);
56         this.autosetduplex();
57         if (this.fs.error)
58                 $ = -1;
59 }
60
61 public method
62 int
63 File.freopen(const char *path, const char *mode)
64 {
65         this.fs.error = 0;
66         if (path == NULL) {
67                 this.parsemode(mode);
68                 if (this.opflags & Fs.O_APPEND)
69                         this.fs.fappend(TRUE);
70                 else
71                         this.fs.fappend(FALSE);
72         } else {
73                 if (this.fs.fd >= 0)
74                         this.fclose();
75                 this.parsemode(mode);
76                 this.fs.open(path, this.opflags, 0666);
77         }
78         this.autosetduplex();
79         if (this.fs.error)
80                 $ = -1;
81 }
82
83 public method
84 int
85 File.fclose()
86 {
87         int r1;
88         int r2;
89
90         # Ok to call fflush() even for in-stream as long as the out
91         # buffer is empty.
92         #
93         r1 = this.fflush();
94         r2 = this.fs.close();
95         if (r1 == 0)
96                 r1 = r2;
97
98         # Clean-out the buffer in case of reuse.  This will free the buffers.
99         #
100         this.in.base = 0L;
101         this.in.index = 0L;
102         this.in.size = 0L;
103         this.in.buf = NULL;
104
105         this.out.base = 0L;
106         this.out.index = 0L;
107         this.out.size = 0L;
108         this.out.buf = NULL;
109
110         return r1;
111 }
112
113 private method
114 void
115 File.parsemode(const char *mode)
116 {
117         int op1 = 0;
118
119         # note that the Rune open() automatically sets O_CLOEXEC, but do it
120         # again here anyway.
121         #
122         this.opflags = Fs.O_CLOEXEC;
123
124         switch(*mode) {
125         case 'r':
126                 op1 = Fs.O_RDONLY;
127                 break;
128         case 'w':
129                 op1 = Fs.O_WRONLY;
130                 this.opflags |= Fs.O_TRUNC | Fs.O_CREAT;
131                 break;
132         case 'a':
133                 op1 = Fs.O_WRONLY;
134                 this.opflags |= Fs.O_TRUNC | Fs.O_CREAT | Fs.O_APPEND;
135                 break;
136         default:
137                 this.fs.error = Sys.EINVAL;
138                 break;
139         }
140         for (++mode; *mode; ++mode) {
141                 switch(*mode) {
142                 case '+':       # reading and writing
143                         op1 = Fs.O_RDWR;
144                         break;
145                 case 'x':       # O_EXCL
146                         this.opflags |= Fs.O_EXCL;
147                         break;
148                 case 'e':       # O_CLOEXEC - no effect (on by default)
149                 case 'b':       # O_BINARY  - no effect (implied by system)
150                         break;
151                 }
152         }
153         this.opflags |= op1;
154 }
155
156 public method
157 void
158 File.autosetduplex()
159 {
160         FStat st;
161
162         if (st.fstat(this.fs.fd) == 0 && st.S_ISREG())
163                 this.flags |= F_ISREG;
164         else
165                 this.flags &= ~F_ISREG;
166 }
167
168 public destructor method
169 void
170 File.destruct()
171 {
172         stdout->show("DESTRUCT");
173         stdout->fflush();
174         if (this.fs.fd >= 0)
175                 this.fclose();
176 }