]>
Commit | Line | Data |
---|---|---|
f0bcf628 | 1 | /* Copyright (C) 2008-2014 Free Software Foundation, Inc. |
7c1b4aba JB |
2 | Contributed by Janne Blomqvist |
3 | ||
4 | This file is part of the GNU Fortran runtime library (libgfortran). | |
5 | ||
6 | Libgfortran is free software; you can redistribute it and/or modify | |
7 | it under the terms of the GNU General Public License as published by | |
748086b7 | 8 | the Free Software Foundation; either version 3, or (at your option) |
7c1b4aba JB |
9 | any later version. |
10 | ||
7c1b4aba JB |
11 | Libgfortran is distributed in the hope that it will be useful, |
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | GNU General Public License for more details. | |
15 | ||
748086b7 JJ |
16 | Under Section 7 of GPL version 3, you are granted additional |
17 | permissions described in the GCC Runtime Library Exception, version | |
18 | 3.1, as published by the Free Software Foundation. | |
19 | ||
20 | You should have received a copy of the GNU General Public License and | |
21 | a copy of the GCC Runtime Library Exception along with this program; | |
22 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see | |
23 | <http://www.gnu.org/licenses/>. */ | |
7c1b4aba JB |
24 | |
25 | ||
26 | #include "io.h" | |
92cbdb68 JB |
27 | #include "fbuf.h" |
28 | #include "unix.h" | |
7c1b4aba JB |
29 | #include <string.h> |
30 | #include <stdlib.h> | |
31 | ||
32 | ||
7812c78c JD |
33 | //#define FBUF_DEBUG |
34 | ||
35 | ||
7c1b4aba | 36 | void |
7812c78c | 37 | fbuf_init (gfc_unit * u, int len) |
7c1b4aba JB |
38 | { |
39 | if (len == 0) | |
8947fd62 | 40 | len = 512; /* Default size. */ |
7c1b4aba | 41 | |
1a0fd3d3 JB |
42 | u->fbuf = xmalloc (sizeof (struct fbuf)); |
43 | u->fbuf->buf = xmalloc (len); | |
7c1b4aba | 44 | u->fbuf->len = len; |
7812c78c | 45 | u->fbuf->act = u->fbuf->pos = 0; |
7c1b4aba JB |
46 | } |
47 | ||
48 | ||
49 | void | |
50 | fbuf_destroy (gfc_unit * u) | |
51 | { | |
52 | if (u->fbuf == NULL) | |
53 | return; | |
04695783 | 54 | free (u->fbuf->buf); |
bb408e87 | 55 | free (u->fbuf); |
7812c78c JD |
56 | u->fbuf = NULL; |
57 | } | |
58 | ||
59 | ||
60 | static void | |
61 | #ifdef FBUF_DEBUG | |
62 | fbuf_debug (gfc_unit * u, const char * format, ...) | |
63 | { | |
64 | va_list args; | |
65 | va_start(args, format); | |
66 | vfprintf(stderr, format, args); | |
67 | va_end(args); | |
68 | fprintf (stderr, "fbuf_debug pos: %d, act: %d, buf: ''", | |
69 | u->fbuf->pos, u->fbuf->act); | |
70 | for (int ii = 0; ii < u->fbuf->act; ii++) | |
71 | { | |
72 | putc (u->fbuf->buf[ii], stderr); | |
73 | } | |
74 | fprintf (stderr, "''\n"); | |
75 | } | |
76 | #else | |
77 | fbuf_debug (gfc_unit * u __attribute__ ((unused)), | |
78 | const char * format __attribute__ ((unused)), | |
79 | ...) {} | |
80 | #endif | |
81 | ||
82 | ||
83 | ||
84 | /* You should probably call this before doing a physical seek on the | |
85 | underlying device. Returns how much the physical position was | |
86 | modified. */ | |
87 | ||
88 | int | |
89 | fbuf_reset (gfc_unit * u) | |
90 | { | |
91 | int seekval = 0; | |
92 | ||
93 | if (!u->fbuf) | |
94 | return 0; | |
95 | ||
96 | fbuf_debug (u, "fbuf_reset: "); | |
97 | fbuf_flush (u, u->mode); | |
98 | /* If we read past the current position, seek the underlying device | |
99 | back. */ | |
100 | if (u->mode == READING && u->fbuf->act > u->fbuf->pos) | |
101 | { | |
102 | seekval = - (u->fbuf->act - u->fbuf->pos); | |
103 | fbuf_debug (u, "fbuf_reset seekval %d, ", seekval); | |
104 | } | |
105 | u->fbuf->act = u->fbuf->pos = 0; | |
106 | return seekval; | |
7c1b4aba JB |
107 | } |
108 | ||
109 | ||
110 | /* Return a pointer to the current position in the buffer, and increase | |
111 | the pointer by len. Makes sure that the buffer is big enough, | |
7812c78c | 112 | reallocating if necessary. */ |
7c1b4aba JB |
113 | |
114 | char * | |
7812c78c | 115 | fbuf_alloc (gfc_unit * u, int len) |
7c1b4aba | 116 | { |
7812c78c | 117 | int newlen; |
7c1b4aba | 118 | char *dest; |
7812c78c | 119 | fbuf_debug (u, "fbuf_alloc len %d, ", len); |
8947fd62 | 120 | if (u->fbuf->pos + len > u->fbuf->len) |
7c1b4aba | 121 | { |
7812c78c JD |
122 | /* Round up to nearest multiple of the current buffer length. */ |
123 | newlen = ((u->fbuf->pos + len) / u->fbuf->len + 1) * u->fbuf->len; | |
124 | dest = realloc (u->fbuf->buf, newlen); | |
125 | if (dest == NULL) | |
126 | return NULL; | |
127 | u->fbuf->buf = dest; | |
128 | u->fbuf->len = newlen; | |
7c1b4aba | 129 | } |
8947fd62 JB |
130 | |
131 | dest = u->fbuf->buf + u->fbuf->pos; | |
132 | u->fbuf->pos += len; | |
133 | if (u->fbuf->pos > u->fbuf->act) | |
134 | u->fbuf->act = u->fbuf->pos; | |
7c1b4aba JB |
135 | return dest; |
136 | } | |
137 | ||
138 | ||
7812c78c JD |
139 | /* mode argument is WRITING for write mode and READING for read |
140 | mode. Return value is 0 for success, -1 on failure. */ | |
8947fd62 | 141 | |
7c1b4aba | 142 | int |
7812c78c | 143 | fbuf_flush (gfc_unit * u, unit_mode mode) |
7c1b4aba | 144 | { |
7812c78c | 145 | int nwritten; |
7c1b4aba JB |
146 | |
147 | if (!u->fbuf) | |
148 | return 0; | |
7812c78c JD |
149 | |
150 | fbuf_debug (u, "fbuf_flush with mode %d: ", mode); | |
151 | ||
152 | if (mode == WRITING) | |
7c1b4aba | 153 | { |
7812c78c JD |
154 | if (u->fbuf->pos > 0) |
155 | { | |
156 | nwritten = swrite (u->s, u->fbuf->buf, u->fbuf->pos); | |
157 | if (nwritten < 0) | |
158 | return -1; | |
159 | } | |
7c1b4aba | 160 | } |
7812c78c JD |
161 | /* Salvage remaining bytes for both reading and writing. This |
162 | happens with the combination of advance='no' and T edit | |
163 | descriptors leaving the final position somewhere not at the end | |
164 | of the record. For reading, this also happens if we sread() past | |
165 | the record boundary. */ | |
166 | if (u->fbuf->act > u->fbuf->pos && u->fbuf->pos > 0) | |
167 | memmove (u->fbuf->buf, u->fbuf->buf + u->fbuf->pos, | |
168 | u->fbuf->act - u->fbuf->pos); | |
169 | ||
170 | u->fbuf->act -= u->fbuf->pos; | |
171 | u->fbuf->pos = 0; | |
172 | ||
173 | return 0; | |
7c1b4aba JB |
174 | } |
175 | ||
176 | ||
177 | int | |
7812c78c | 178 | fbuf_seek (gfc_unit * u, int off, int whence) |
7c1b4aba | 179 | { |
7812c78c | 180 | if (!u->fbuf) |
7c1b4aba | 181 | return -1; |
7812c78c JD |
182 | |
183 | switch (whence) | |
184 | { | |
185 | case SEEK_SET: | |
186 | break; | |
187 | case SEEK_CUR: | |
188 | off += u->fbuf->pos; | |
189 | break; | |
190 | case SEEK_END: | |
191 | off += u->fbuf->act; | |
192 | break; | |
193 | default: | |
194 | return -1; | |
195 | } | |
196 | ||
197 | fbuf_debug (u, "fbuf_seek, off %d ", off); | |
198 | /* The start of the buffer is always equal to the left tab | |
199 | limit. Moving to the left past the buffer is illegal in C and | |
200 | would also imply moving past the left tab limit, which is never | |
201 | allowed in Fortran. Similarly, seeking past the end of the buffer | |
202 | is not possible, in that case the user must make sure to allocate | |
203 | space with fbuf_alloc(). So return error if that is | |
204 | attempted. */ | |
205 | if (off < 0 || off > u->fbuf->act) | |
206 | return -1; | |
207 | u->fbuf->pos = off; | |
208 | return off; | |
209 | } | |
210 | ||
211 | ||
212 | /* Fill the buffer with bytes for reading. Returns a pointer to start | |
213 | reading from. If we hit EOF, returns a short read count. If any | |
214 | other error occurs, return NULL. After reading, the caller is | |
215 | expected to call fbuf_seek to update the position with the number | |
216 | of bytes actually processed. */ | |
217 | ||
218 | char * | |
219 | fbuf_read (gfc_unit * u, int * len) | |
220 | { | |
221 | char *ptr; | |
222 | int oldact, oldpos; | |
223 | int readlen = 0; | |
224 | ||
225 | fbuf_debug (u, "fbuf_read, len %d: ", *len); | |
226 | oldact = u->fbuf->act; | |
227 | oldpos = u->fbuf->pos; | |
228 | ptr = fbuf_alloc (u, *len); | |
229 | u->fbuf->pos = oldpos; | |
230 | if (oldpos + *len > oldact) | |
231 | { | |
232 | fbuf_debug (u, "reading %d bytes starting at %d ", | |
233 | oldpos + *len - oldact, oldact); | |
234 | readlen = sread (u->s, u->fbuf->buf + oldact, oldpos + *len - oldact); | |
235 | if (readlen < 0) | |
236 | return NULL; | |
237 | *len = oldact - oldpos + readlen; | |
238 | } | |
239 | u->fbuf->act = oldact + readlen; | |
240 | fbuf_debug (u, "fbuf_read done: "); | |
241 | return ptr; | |
242 | } | |
243 | ||
244 | ||
245 | /* When the fbuf_getc() inline function runs out of buffer space, it | |
246 | calls this function to fill the buffer with bytes for | |
247 | reading. Never call this function directly. */ | |
248 | ||
249 | int | |
250 | fbuf_getc_refill (gfc_unit * u) | |
251 | { | |
252 | int nread; | |
253 | char *p; | |
254 | ||
255 | fbuf_debug (u, "fbuf_getc_refill "); | |
256 | ||
257 | /* Read 80 bytes (average line length?). This is a compromise | |
258 | between not needing to call the read() syscall all the time and | |
259 | not having to memmove unnecessary stuff when switching to the | |
260 | next record. */ | |
261 | nread = 80; | |
262 | ||
263 | p = fbuf_read (u, &nread); | |
264 | ||
265 | if (p && nread > 0) | |
266 | return (unsigned char) u->fbuf->buf[u->fbuf->pos++]; | |
267 | else | |
268 | return EOF; | |
7c1b4aba | 269 | } |