1 /* Copyright (C) 2002-2021 Free Software Foundation, Inc.
2    Contributed by Andy Vaught and 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
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10 
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 
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/>.  */
24 
25 #include "io.h"
26 #include "fbuf.h"
27 #include "unix.h"
28 #include "async.h"
29 #include <string.h>
30 
31 /* file_pos.c-- Implement the file positioning statements, i.e. BACKSPACE,
32    ENDFILE, and REWIND as well as the FLUSH statement.  */
33 
34 
35 /* formatted_backspace(fpp, u)-- Move the file back one line.  The
36    current position is after the newline that terminates the previous
37    record, and we have to sift backwards to find the newline before
38    that or the start of the file, whichever comes first.  */
39 
40 #define READ_CHUNK 4096
41 
42 static void
formatted_backspace(st_parameter_filepos * fpp,gfc_unit * u)43 formatted_backspace (st_parameter_filepos *fpp, gfc_unit *u)
44 {
45   gfc_offset base;
46   char p[READ_CHUNK];
47   ssize_t n;
48 
49   base = stell (u->s) - 1;
50 
51   do
52     {
53       n = (base < READ_CHUNK) ? base : READ_CHUNK;
54       base -= n;
55       if (sseek (u->s, base, SEEK_SET) < 0)
56         goto io_error;
57       if (sread (u->s, p, n) != n)
58 	goto io_error;
59 
60       /* We have moved backwards from the current position, it should
61          not be possible to get a short read.  Because it is not
62          clear what to do about such thing, we ignore the possibility.  */
63 
64       /* There is no memrchr() in the C library, so we have to do it
65          ourselves.  */
66 
67       while (n > 0)
68 	{
69           n--;
70 	  if (p[n] == '\n')
71 	    {
72 	      base += n + 1;
73 	      goto done;
74 	    }
75 	}
76 
77     }
78   while (base != 0);
79 
80   /* base is the new pointer.  Seek to it exactly.  */
81  done:
82   if (sseek (u->s, base, SEEK_SET) < 0)
83     goto io_error;
84   u->last_record--;
85   u->endfile = NO_ENDFILE;
86   u->last_char = EOF - 1;
87   return;
88 
89  io_error:
90   generate_error (&fpp->common, LIBERROR_OS, NULL);
91 }
92 
93 
94 /* unformatted_backspace(fpp) -- Move the file backwards for an unformatted
95    sequential file.  We are guaranteed to be between records on entry and
96    we have to shift to the previous record.  Loop over subrecords.  */
97 
98 static void
unformatted_backspace(st_parameter_filepos * fpp,gfc_unit * u)99 unformatted_backspace (st_parameter_filepos *fpp, gfc_unit *u)
100 {
101   gfc_offset m, slen;
102   GFC_INTEGER_4 m4;
103   GFC_INTEGER_8 m8;
104   ssize_t length;
105   int continued;
106   char p[sizeof (GFC_INTEGER_8)];
107 
108   if (compile_options.record_marker == 0)
109     length = sizeof (GFC_INTEGER_4);
110   else
111     length = compile_options.record_marker;
112 
113   do
114     {
115       slen = - (gfc_offset) length;
116       if (sseek (u->s, slen, SEEK_CUR) < 0)
117         goto io_error;
118       if (sread (u->s, p, length) != length)
119         goto io_error;
120 
121       /* Only GFC_CONVERT_NATIVE and GFC_CONVERT_SWAP are valid here.  */
122       if (likely (u->flags.convert == GFC_CONVERT_NATIVE))
123 	{
124 	  switch (length)
125 	    {
126 	    case sizeof(GFC_INTEGER_4):
127 	      memcpy (&m4, p, sizeof (m4));
128 	      m = m4;
129 	      break;
130 
131 	    case sizeof(GFC_INTEGER_8):
132 	      memcpy (&m8, p, sizeof (m8));
133 	      m = m8;
134 	      break;
135 
136 	    default:
137 	      runtime_error ("Illegal value for record marker");
138 	      break;
139 	    }
140 	}
141       else
142 	{
143 	  uint32_t u32;
144 	  uint64_t u64;
145 	  switch (length)
146 	    {
147 	    case sizeof(GFC_INTEGER_4):
148 	      memcpy (&u32, p, sizeof (u32));
149 	      u32 = __builtin_bswap32 (u32);
150 	      memcpy (&m4, &u32, sizeof (m4));
151 	      m = m4;
152 	      break;
153 
154 	    case sizeof(GFC_INTEGER_8):
155 	      memcpy (&u64, p, sizeof (u64));
156 	      u64 = __builtin_bswap64 (u64);
157 	      memcpy (&m8, &u64, sizeof (m8));
158 	      m = m8;
159 	      break;
160 
161 	    default:
162 	      runtime_error ("Illegal value for record marker");
163 	      break;
164 	    }
165 
166 	}
167 
168       continued = m < 0;
169       if (continued)
170 	m = -m;
171 
172       if (sseek (u->s, -m -2 * length, SEEK_CUR) < 0)
173 	goto io_error;
174     } while (continued);
175 
176   u->last_record--;
177   return;
178 
179  io_error:
180   generate_error (&fpp->common, LIBERROR_OS, NULL);
181 }
182 
183 
184 extern void st_backspace (st_parameter_filepos *);
185 export_proto(st_backspace);
186 
187 void
st_backspace(st_parameter_filepos * fpp)188 st_backspace (st_parameter_filepos *fpp)
189 {
190   gfc_unit *u;
191   bool needs_unlock = false;
192 
193   library_start (&fpp->common);
194 
195   u = find_unit (fpp->common.unit);
196   if (u == NULL)
197     {
198       generate_error (&fpp->common, LIBERROR_BAD_UNIT, NULL);
199       goto done;
200     }
201 
202   /* Direct access is prohibited, and so is unformatted stream access.  */
203 
204 
205   if (u->flags.access == ACCESS_DIRECT)
206     {
207       generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT,
208 		      "Cannot BACKSPACE a file opened for DIRECT access");
209       goto done;
210     }
211 
212   if (u->flags.access == ACCESS_STREAM && u->flags.form == FORM_UNFORMATTED)
213     {
214       generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT,
215                       "Cannot BACKSPACE an unformatted stream file");
216       goto done;
217     }
218 
219   if (ASYNC_IO && u->au)
220     {
221       if (async_wait (&(fpp->common), u->au))
222 	return;
223       else
224 	{
225 	  needs_unlock = true;
226 	  LOCK (&u->au->io_lock);
227 	}
228     }
229 
230   /* Make sure format buffer is flushed and reset.  */
231   if (u->flags.form == FORM_FORMATTED)
232     {
233       int pos = fbuf_reset (u);
234       if (pos != 0)
235         sseek (u->s, pos, SEEK_CUR);
236     }
237 
238 
239   /* Check for special cases involving the ENDFILE record first.  */
240 
241   if (u->endfile == AFTER_ENDFILE)
242     {
243       u->endfile = AT_ENDFILE;
244       u->flags.position = POSITION_APPEND;
245       sflush (u->s);
246     }
247   else
248     {
249       if (stell (u->s) == 0)
250 	{
251 	  u->flags.position = POSITION_REWIND;
252 	  goto done;		/* Common special case */
253 	}
254 
255       if (u->mode == WRITING)
256 	{
257 	  /* If there are previously written bytes from a write with
258 	     ADVANCE="no", add a record marker before performing the
259 	     BACKSPACE.  */
260 
261 	  if (u->previous_nonadvancing_write)
262 	    finish_last_advance_record (u);
263 
264 	  u->previous_nonadvancing_write = 0;
265 
266 	  unit_truncate (u, stell (u->s), &fpp->common);
267 	  u->mode = READING;
268         }
269 
270       if (u->flags.form == FORM_FORMATTED)
271 	formatted_backspace (fpp, u);
272       else
273 	unformatted_backspace (fpp, u);
274 
275       u->flags.position = POSITION_UNSPECIFIED;
276       u->endfile = NO_ENDFILE;
277       u->current_record = 0;
278       u->bytes_left = 0;
279     }
280 
281  done:
282   if (u != NULL)
283     {
284       unlock_unit (u);
285 
286       if (ASYNC_IO && u->au && needs_unlock)
287 	UNLOCK (&u->au->io_lock);
288     }
289 
290   library_end ();
291 }
292 
293 
294 extern void st_endfile (st_parameter_filepos *);
295 export_proto(st_endfile);
296 
297 void
st_endfile(st_parameter_filepos * fpp)298 st_endfile (st_parameter_filepos *fpp)
299 {
300   gfc_unit *u;
301   bool needs_unlock = false;
302 
303   library_start (&fpp->common);
304 
305   u = find_unit (fpp->common.unit);
306   if (u != NULL)
307     {
308       if (u->flags.access == ACCESS_DIRECT)
309 	{
310 	  generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT,
311 			  "Cannot perform ENDFILE on a file opened "
312 			  "for DIRECT access");
313 	  goto done;
314 	}
315 
316       if (ASYNC_IO && u->au)
317 	{
318 	  if (async_wait (&(fpp->common), u->au))
319 	    return;
320 	  else
321 	    {
322 	      needs_unlock = true;
323 	      LOCK (&u->au->io_lock);
324 	    }
325 	}
326 
327       if (u->flags.access == ACCESS_SEQUENTIAL
328       	  && u->endfile == AFTER_ENDFILE)
329 	{
330 	  generate_error (&fpp->common, LIBERROR_OPTION_CONFLICT,
331 			  "Cannot perform ENDFILE on a file already "
332 			  "positioned after the EOF marker");
333 	  goto done;
334 	}
335 
336       /* If there are previously written bytes from a write with ADVANCE="no",
337 	 add a record marker before performing the ENDFILE.  */
338 
339       if (u->previous_nonadvancing_write)
340 	finish_last_advance_record (u);
341 
342       u->previous_nonadvancing_write = 0;
343 
344       if (u->current_record)
345 	{
346 	  st_parameter_dt dtp;
347 	  dtp.common = fpp->common;
348 	  memset (&dtp.u.p, 0, sizeof (dtp.u.p));
349 	  dtp.u.p.current_unit = u;
350 	  next_record (&dtp, 1);
351 	}
352 
353       unit_truncate (u, stell (u->s), &fpp->common);
354       u->endfile = AFTER_ENDFILE;
355       u->last_char = EOF - 1;
356       if (0 == stell (u->s))
357         u->flags.position = POSITION_REWIND;
358     }
359   else
360     {
361       if (fpp->common.unit < 0)
362 	{
363 	  generate_error (&fpp->common, LIBERROR_BAD_OPTION,
364 			  "Bad unit number in statement");
365 	  return;
366 	}
367 
368       u = find_or_create_unit (fpp->common.unit);
369       if (u->s == NULL)
370 	{
371 	  /* Open the unit with some default flags.  */
372 	  st_parameter_open opp;
373 	  unit_flags u_flags;
374 
375 	  memset (&u_flags, '\0', sizeof (u_flags));
376 	  u_flags.access = ACCESS_SEQUENTIAL;
377 	  u_flags.action = ACTION_READWRITE;
378 
379 	  /* Is it unformatted?  */
380 	  if (!(fpp->common.flags & (IOPARM_DT_HAS_FORMAT | IOPARM_DT_LIST_FORMAT
381 				     | IOPARM_DT_IONML_SET)))
382 	    u_flags.form = FORM_UNFORMATTED;
383 	  else
384 	    u_flags.form = FORM_UNSPECIFIED;
385 
386 	  u_flags.delim = DELIM_UNSPECIFIED;
387 	  u_flags.blank = BLANK_UNSPECIFIED;
388 	  u_flags.pad = PAD_UNSPECIFIED;
389 	  u_flags.decimal = DECIMAL_UNSPECIFIED;
390 	  u_flags.encoding = ENCODING_UNSPECIFIED;
391 	  u_flags.async = ASYNC_UNSPECIFIED;
392 	  u_flags.round = ROUND_UNSPECIFIED;
393 	  u_flags.sign = SIGN_UNSPECIFIED;
394 	  u_flags.status = STATUS_UNKNOWN;
395 	  u_flags.convert = GFC_CONVERT_NATIVE;
396 	  u_flags.share = SHARE_UNSPECIFIED;
397 	  u_flags.cc = CC_UNSPECIFIED;
398 
399 	  opp.common = fpp->common;
400 	  opp.common.flags &= IOPARM_COMMON_MASK;
401 	  u = new_unit (&opp, u, &u_flags);
402 	  if (u == NULL)
403 	    return;
404 	  u->endfile = AFTER_ENDFILE;
405 	  u->last_char = EOF - 1;
406 	}
407     }
408 
409  done:
410   if (ASYNC_IO && u->au && needs_unlock)
411     UNLOCK (&u->au->io_lock);
412 
413   unlock_unit (u);
414 
415   library_end ();
416 }
417 
418 
419 extern void st_rewind (st_parameter_filepos *);
420 export_proto(st_rewind);
421 
422 void
st_rewind(st_parameter_filepos * fpp)423 st_rewind (st_parameter_filepos *fpp)
424 {
425   gfc_unit *u;
426   bool needs_unlock = true;
427 
428   library_start (&fpp->common);
429 
430   u = find_unit (fpp->common.unit);
431   if (u != NULL)
432     {
433       if (u->flags.access == ACCESS_DIRECT)
434 	generate_error (&fpp->common, LIBERROR_BAD_OPTION,
435 			"Cannot REWIND a file opened for DIRECT access");
436       else
437 	{
438 	  if (ASYNC_IO && u->au)
439 	    {
440 	      if (async_wait (&(fpp->common), u->au))
441 		return;
442 	      else
443 		{
444 		  needs_unlock = true;
445 		  LOCK (&u->au->io_lock);
446 		}
447 	    }
448 
449 	  /* If there are previously written bytes from a write with ADVANCE="no",
450 	     add a record marker before performing the ENDFILE.  */
451 
452 	  if (u->previous_nonadvancing_write)
453 	    finish_last_advance_record (u);
454 
455 	  u->previous_nonadvancing_write = 0;
456 
457 	  fbuf_reset (u);
458 
459 	  u->last_record = 0;
460 
461 	  if (sseek (u->s, 0, SEEK_SET) < 0)
462 	    {
463 	      generate_error (&fpp->common, LIBERROR_OS, NULL);
464 	      library_end ();
465 	      return;
466 	    }
467 
468 	  /* Set this for compatibilty with g77 for /dev/null.  */
469 	  if (ssize (u->s) == 0)
470 	    u->endfile = AT_ENDFILE;
471 	  else
472 	    {
473 	      /* We are rewinding so we are not at the end.  */
474 	      u->endfile = NO_ENDFILE;
475 	    }
476 
477 	  u->current_record = 0;
478 	  u->strm_pos = 1;
479 	  u->read_bad = 0;
480 	  u->last_char = EOF - 1;
481 	}
482       /* Update position for INQUIRE.  */
483       u->flags.position = POSITION_REWIND;
484 
485       if (ASYNC_IO && u->au && needs_unlock)
486 	UNLOCK (&u->au->io_lock);
487 
488       unlock_unit (u);
489     }
490 
491   library_end ();
492 }
493 
494 
495 extern void st_flush (st_parameter_filepos *);
496 export_proto(st_flush);
497 
498 void
st_flush(st_parameter_filepos * fpp)499 st_flush (st_parameter_filepos *fpp)
500 {
501   gfc_unit *u;
502   bool needs_unlock = false;
503 
504   library_start (&fpp->common);
505 
506   u = find_unit (fpp->common.unit);
507   if (u != NULL)
508     {
509       if (ASYNC_IO && u->au)
510 	{
511 	  if (async_wait (&(fpp->common), u->au))
512 	    return;
513 	  else
514 	    {
515 	      needs_unlock = true;
516 	      LOCK (&u->au->io_lock);
517 	    }
518 	}
519 
520       /* Make sure format buffer is flushed.  */
521       if (u->flags.form == FORM_FORMATTED)
522         fbuf_flush (u, u->mode);
523 
524       sflush (u->s);
525       u->last_char = EOF - 1;
526       unlock_unit (u);
527     }
528   else
529     /* FLUSH on unconnected unit is illegal: F95 std., 9.3.5. */
530     generate_error (&fpp->common, LIBERROR_BAD_OPTION,
531 			"Specified UNIT in FLUSH is not connected");
532 
533   if (needs_unlock)
534     UNLOCK (&u->au->io_lock);
535 
536   library_end ();
537 }
538