xref: /netbsd-src/external/gpl3/gcc.old/dist/libgfortran/io/unit.c (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1*4c3eb207Smrg /* Copyright (C) 2002-2020 Free Software Foundation, Inc.
2627f7eb2Smrg    Contributed by Andy Vaught
3627f7eb2Smrg    F2003 I/O support contributed by Jerry DeLisle
4627f7eb2Smrg 
5627f7eb2Smrg This file is part of the GNU Fortran runtime library (libgfortran).
6627f7eb2Smrg 
7627f7eb2Smrg Libgfortran is free software; you can redistribute it and/or modify
8627f7eb2Smrg it under the terms of the GNU General Public License as published by
9627f7eb2Smrg the Free Software Foundation; either version 3, or (at your option)
10627f7eb2Smrg any later version.
11627f7eb2Smrg 
12627f7eb2Smrg Libgfortran is distributed in the hope that it will be useful,
13627f7eb2Smrg but WITHOUT ANY WARRANTY; without even the implied warranty of
14627f7eb2Smrg MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15627f7eb2Smrg GNU General Public License for more details.
16627f7eb2Smrg 
17627f7eb2Smrg Under Section 7 of GPL version 3, you are granted additional
18627f7eb2Smrg permissions described in the GCC Runtime Library Exception, version
19627f7eb2Smrg 3.1, as published by the Free Software Foundation.
20627f7eb2Smrg 
21627f7eb2Smrg You should have received a copy of the GNU General Public License and
22627f7eb2Smrg a copy of the GCC Runtime Library Exception along with this program;
23627f7eb2Smrg see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
24627f7eb2Smrg <http://www.gnu.org/licenses/>.  */
25627f7eb2Smrg 
26627f7eb2Smrg #include "io.h"
27627f7eb2Smrg #include "fbuf.h"
28627f7eb2Smrg #include "format.h"
29627f7eb2Smrg #include "unix.h"
30627f7eb2Smrg #include "async.h"
31627f7eb2Smrg #include <string.h>
32627f7eb2Smrg #include <assert.h>
33627f7eb2Smrg 
34627f7eb2Smrg 
35627f7eb2Smrg /* IO locking rules:
36627f7eb2Smrg    UNIT_LOCK is a master lock, protecting UNIT_ROOT tree and UNIT_CACHE.
37627f7eb2Smrg    Concurrent use of different units should be supported, so
38627f7eb2Smrg    each unit has its own lock, LOCK.
39627f7eb2Smrg    Open should be atomic with its reopening of units and list_read.c
40627f7eb2Smrg    in several places needs find_unit another unit while holding stdin
41627f7eb2Smrg    unit's lock, so it must be possible to acquire UNIT_LOCK while holding
42627f7eb2Smrg    some unit's lock.  Therefore to avoid deadlocks, it is forbidden
43627f7eb2Smrg    to acquire unit's private locks while holding UNIT_LOCK, except
44627f7eb2Smrg    for freshly created units (where no other thread can get at their
45627f7eb2Smrg    address yet) or when using just trylock rather than lock operation.
46627f7eb2Smrg    In addition to unit's private lock each unit has a WAITERS counter
47627f7eb2Smrg    and CLOSED flag.  WAITERS counter must be either only
48627f7eb2Smrg    atomically incremented/decremented in all places (if atomic builtins
49627f7eb2Smrg    are supported), or protected by UNIT_LOCK in all places (otherwise).
50627f7eb2Smrg    CLOSED flag must be always protected by unit's LOCK.
51627f7eb2Smrg    After finding a unit in UNIT_CACHE or UNIT_ROOT with UNIT_LOCK held,
52627f7eb2Smrg    WAITERS must be incremented to avoid concurrent close from freeing
53627f7eb2Smrg    the unit between unlocking UNIT_LOCK and acquiring unit's LOCK.
54627f7eb2Smrg    Unit freeing is always done under UNIT_LOCK.  If close_unit sees any
55627f7eb2Smrg    WAITERS, it doesn't free the unit but instead sets the CLOSED flag
56627f7eb2Smrg    and the thread that decrements WAITERS to zero while CLOSED flag is
57627f7eb2Smrg    set is responsible for freeing it (while holding UNIT_LOCK).
58627f7eb2Smrg    flush_all_units operation is iterating over the unit tree with
59627f7eb2Smrg    increasing UNIT_NUMBER while holding UNIT_LOCK and attempting to
60627f7eb2Smrg    flush each unit (and therefore needs the unit's LOCK held as well).
61627f7eb2Smrg    To avoid deadlocks, it just trylocks the LOCK and if unsuccessful,
62627f7eb2Smrg    remembers the current unit's UNIT_NUMBER, unlocks UNIT_LOCK, acquires
63627f7eb2Smrg    unit's LOCK and after flushing reacquires UNIT_LOCK and restarts with
64627f7eb2Smrg    the smallest UNIT_NUMBER above the last one flushed.
65627f7eb2Smrg 
66627f7eb2Smrg    If find_unit/find_or_create_unit/find_file/get_unit routines return
67627f7eb2Smrg    non-NULL, the returned unit has its private lock locked and when the
68627f7eb2Smrg    caller is done with it, it must call either unlock_unit or close_unit
69627f7eb2Smrg    on it.  unlock_unit or close_unit must be always called only with the
70627f7eb2Smrg    private lock held.  */
71627f7eb2Smrg 
72627f7eb2Smrg 
73627f7eb2Smrg 
74627f7eb2Smrg /* Table of allocated newunit values.  A simple solution would be to
75627f7eb2Smrg    map OS file descriptors (fd's) to unit numbers, e.g. with newunit =
76627f7eb2Smrg    -fd - 2, however that doesn't work since Fortran allows an existing
77627f7eb2Smrg    unit number to be reassociated with a new file. Thus the simple
78627f7eb2Smrg    approach may lead to a situation where we'd try to assign a
79627f7eb2Smrg    (negative) unit number which already exists. Hence we must keep
80627f7eb2Smrg    track of allocated newunit values ourselves. This is the purpose of
81627f7eb2Smrg    the newunits array. The indices map to newunit values as newunit =
82627f7eb2Smrg    -index + NEWUNIT_FIRST. E.g. newunits[0] having the value true
83627f7eb2Smrg    means that a unit with number NEWUNIT_FIRST exists. Similar to
84627f7eb2Smrg    POSIX file descriptors, we always allocate the lowest (in absolute
85627f7eb2Smrg    value) available unit number.
86627f7eb2Smrg  */
87627f7eb2Smrg static bool *newunits;
88627f7eb2Smrg static int newunit_size; /* Total number of elements in the newunits array.  */
89627f7eb2Smrg /* Low water indicator for the newunits array. Below the LWI all the
90627f7eb2Smrg    units are allocated, above and equal to the LWI there may be both
91627f7eb2Smrg    allocated and free units. */
92627f7eb2Smrg static int newunit_lwi;
93627f7eb2Smrg 
94627f7eb2Smrg /* Unit numbers assigned with NEWUNIT start from here.  */
95627f7eb2Smrg #define NEWUNIT_START -10
96627f7eb2Smrg 
97627f7eb2Smrg #define CACHE_SIZE 3
98627f7eb2Smrg static gfc_unit *unit_cache[CACHE_SIZE];
99627f7eb2Smrg 
100627f7eb2Smrg gfc_offset max_offset;
101627f7eb2Smrg gfc_offset default_recl;
102627f7eb2Smrg 
103627f7eb2Smrg gfc_unit *unit_root;
104627f7eb2Smrg #ifdef __GTHREAD_MUTEX_INIT
105627f7eb2Smrg __gthread_mutex_t unit_lock = __GTHREAD_MUTEX_INIT;
106627f7eb2Smrg #else
107627f7eb2Smrg __gthread_mutex_t unit_lock;
108627f7eb2Smrg #endif
109627f7eb2Smrg 
110627f7eb2Smrg /* We use these filenames for error reporting.  */
111627f7eb2Smrg 
112627f7eb2Smrg static char stdin_name[] = "stdin";
113627f7eb2Smrg static char stdout_name[] = "stdout";
114627f7eb2Smrg static char stderr_name[] = "stderr";
115627f7eb2Smrg 
116627f7eb2Smrg 
117627f7eb2Smrg #ifdef HAVE_NEWLOCALE
118627f7eb2Smrg locale_t c_locale;
119627f7eb2Smrg #else
120627f7eb2Smrg /* If we don't have POSIX 2008 per-thread locales, we need to use the
121627f7eb2Smrg    traditional setlocale().  To prevent multiple concurrent threads
122627f7eb2Smrg    doing formatted I/O from messing up the locale, we need to store a
123627f7eb2Smrg    global old_locale, and a counter keeping track of how many threads
124627f7eb2Smrg    are currently doing formatted I/O.  The first thread saves the old
125627f7eb2Smrg    locale, and the last one restores it.  */
126627f7eb2Smrg char *old_locale;
127627f7eb2Smrg int old_locale_ctr;
128627f7eb2Smrg #ifdef __GTHREAD_MUTEX_INIT
129627f7eb2Smrg __gthread_mutex_t old_locale_lock = __GTHREAD_MUTEX_INIT;
130627f7eb2Smrg #else
131627f7eb2Smrg __gthread_mutex_t old_locale_lock;
132627f7eb2Smrg #endif
133627f7eb2Smrg #endif
134627f7eb2Smrg 
135627f7eb2Smrg 
136627f7eb2Smrg /* This implementation is based on Stefan Nilsson's article in the
137627f7eb2Smrg    July 1997 Doctor Dobb's Journal, "Treaps in Java". */
138627f7eb2Smrg 
139627f7eb2Smrg /* pseudo_random()-- Simple linear congruential pseudorandom number
140627f7eb2Smrg    generator.  The period of this generator is 44071, which is plenty
141627f7eb2Smrg    for our purposes.  */
142627f7eb2Smrg 
143627f7eb2Smrg static int
pseudo_random(void)144627f7eb2Smrg pseudo_random (void)
145627f7eb2Smrg {
146627f7eb2Smrg   static int x0 = 5341;
147627f7eb2Smrg 
148627f7eb2Smrg   x0 = (22611 * x0 + 10) % 44071;
149627f7eb2Smrg   return x0;
150627f7eb2Smrg }
151627f7eb2Smrg 
152627f7eb2Smrg 
153627f7eb2Smrg /* rotate_left()-- Rotate the treap left */
154627f7eb2Smrg 
155627f7eb2Smrg static gfc_unit *
rotate_left(gfc_unit * t)156627f7eb2Smrg rotate_left (gfc_unit *t)
157627f7eb2Smrg {
158627f7eb2Smrg   gfc_unit *temp;
159627f7eb2Smrg 
160627f7eb2Smrg   temp = t->right;
161627f7eb2Smrg   t->right = t->right->left;
162627f7eb2Smrg   temp->left = t;
163627f7eb2Smrg 
164627f7eb2Smrg   return temp;
165627f7eb2Smrg }
166627f7eb2Smrg 
167627f7eb2Smrg 
168627f7eb2Smrg /* rotate_right()-- Rotate the treap right */
169627f7eb2Smrg 
170627f7eb2Smrg static gfc_unit *
rotate_right(gfc_unit * t)171627f7eb2Smrg rotate_right (gfc_unit *t)
172627f7eb2Smrg {
173627f7eb2Smrg   gfc_unit *temp;
174627f7eb2Smrg 
175627f7eb2Smrg   temp = t->left;
176627f7eb2Smrg   t->left = t->left->right;
177627f7eb2Smrg   temp->right = t;
178627f7eb2Smrg 
179627f7eb2Smrg   return temp;
180627f7eb2Smrg }
181627f7eb2Smrg 
182627f7eb2Smrg 
183627f7eb2Smrg static int
compare(int a,int b)184627f7eb2Smrg compare (int a, int b)
185627f7eb2Smrg {
186627f7eb2Smrg   if (a < b)
187627f7eb2Smrg     return -1;
188627f7eb2Smrg   if (a > b)
189627f7eb2Smrg     return 1;
190627f7eb2Smrg 
191627f7eb2Smrg   return 0;
192627f7eb2Smrg }
193627f7eb2Smrg 
194627f7eb2Smrg 
195627f7eb2Smrg /* insert()-- Recursive insertion function.  Returns the updated treap. */
196627f7eb2Smrg 
197627f7eb2Smrg static gfc_unit *
insert(gfc_unit * new,gfc_unit * t)198627f7eb2Smrg insert (gfc_unit *new, gfc_unit *t)
199627f7eb2Smrg {
200627f7eb2Smrg   int c;
201627f7eb2Smrg 
202627f7eb2Smrg   if (t == NULL)
203627f7eb2Smrg     return new;
204627f7eb2Smrg 
205627f7eb2Smrg   c = compare (new->unit_number, t->unit_number);
206627f7eb2Smrg 
207627f7eb2Smrg   if (c < 0)
208627f7eb2Smrg     {
209627f7eb2Smrg       t->left = insert (new, t->left);
210627f7eb2Smrg       if (t->priority < t->left->priority)
211627f7eb2Smrg 	t = rotate_right (t);
212627f7eb2Smrg     }
213627f7eb2Smrg 
214627f7eb2Smrg   if (c > 0)
215627f7eb2Smrg     {
216627f7eb2Smrg       t->right = insert (new, t->right);
217627f7eb2Smrg       if (t->priority < t->right->priority)
218627f7eb2Smrg 	t = rotate_left (t);
219627f7eb2Smrg     }
220627f7eb2Smrg 
221627f7eb2Smrg   if (c == 0)
222627f7eb2Smrg     internal_error (NULL, "insert(): Duplicate key found!");
223627f7eb2Smrg 
224627f7eb2Smrg   return t;
225627f7eb2Smrg }
226627f7eb2Smrg 
227627f7eb2Smrg 
228627f7eb2Smrg /* insert_unit()-- Create a new node, insert it into the treap.  */
229627f7eb2Smrg 
230627f7eb2Smrg static gfc_unit *
insert_unit(int n)231627f7eb2Smrg insert_unit (int n)
232627f7eb2Smrg {
233627f7eb2Smrg   gfc_unit *u = xcalloc (1, sizeof (gfc_unit));
234627f7eb2Smrg   u->unit_number = n;
235627f7eb2Smrg   u->internal_unit_kind = 0;
236627f7eb2Smrg #ifdef __GTHREAD_MUTEX_INIT
237627f7eb2Smrg   {
238627f7eb2Smrg     __gthread_mutex_t tmp = __GTHREAD_MUTEX_INIT;
239627f7eb2Smrg     u->lock = tmp;
240627f7eb2Smrg   }
241627f7eb2Smrg #else
242627f7eb2Smrg   __GTHREAD_MUTEX_INIT_FUNCTION (&u->lock);
243627f7eb2Smrg #endif
244627f7eb2Smrg   LOCK (&u->lock);
245627f7eb2Smrg   u->priority = pseudo_random ();
246627f7eb2Smrg   unit_root = insert (u, unit_root);
247627f7eb2Smrg   return u;
248627f7eb2Smrg }
249627f7eb2Smrg 
250627f7eb2Smrg 
251627f7eb2Smrg /* destroy_unit_mutex()-- Destroy the mutex and free memory of unit.  */
252627f7eb2Smrg 
253627f7eb2Smrg static void
destroy_unit_mutex(gfc_unit * u)254627f7eb2Smrg destroy_unit_mutex (gfc_unit *u)
255627f7eb2Smrg {
256627f7eb2Smrg   __gthread_mutex_destroy (&u->lock);
257627f7eb2Smrg   free (u);
258627f7eb2Smrg }
259627f7eb2Smrg 
260627f7eb2Smrg 
261627f7eb2Smrg static gfc_unit *
delete_root(gfc_unit * t)262627f7eb2Smrg delete_root (gfc_unit *t)
263627f7eb2Smrg {
264627f7eb2Smrg   gfc_unit *temp;
265627f7eb2Smrg 
266627f7eb2Smrg   if (t->left == NULL)
267627f7eb2Smrg     return t->right;
268627f7eb2Smrg   if (t->right == NULL)
269627f7eb2Smrg     return t->left;
270627f7eb2Smrg 
271627f7eb2Smrg   if (t->left->priority > t->right->priority)
272627f7eb2Smrg     {
273627f7eb2Smrg       temp = rotate_right (t);
274627f7eb2Smrg       temp->right = delete_root (t);
275627f7eb2Smrg     }
276627f7eb2Smrg   else
277627f7eb2Smrg     {
278627f7eb2Smrg       temp = rotate_left (t);
279627f7eb2Smrg       temp->left = delete_root (t);
280627f7eb2Smrg     }
281627f7eb2Smrg 
282627f7eb2Smrg   return temp;
283627f7eb2Smrg }
284627f7eb2Smrg 
285627f7eb2Smrg 
286627f7eb2Smrg /* delete_treap()-- Delete an element from a tree.  The 'old' value
287627f7eb2Smrg    does not necessarily have to point to the element to be deleted, it
288627f7eb2Smrg    must just point to a treap structure with the key to be deleted.
289627f7eb2Smrg    Returns the new root node of the tree. */
290627f7eb2Smrg 
291627f7eb2Smrg static gfc_unit *
delete_treap(gfc_unit * old,gfc_unit * t)292627f7eb2Smrg delete_treap (gfc_unit *old, gfc_unit *t)
293627f7eb2Smrg {
294627f7eb2Smrg   int c;
295627f7eb2Smrg 
296627f7eb2Smrg   if (t == NULL)
297627f7eb2Smrg     return NULL;
298627f7eb2Smrg 
299627f7eb2Smrg   c = compare (old->unit_number, t->unit_number);
300627f7eb2Smrg 
301627f7eb2Smrg   if (c < 0)
302627f7eb2Smrg     t->left = delete_treap (old, t->left);
303627f7eb2Smrg   if (c > 0)
304627f7eb2Smrg     t->right = delete_treap (old, t->right);
305627f7eb2Smrg   if (c == 0)
306627f7eb2Smrg     t = delete_root (t);
307627f7eb2Smrg 
308627f7eb2Smrg   return t;
309627f7eb2Smrg }
310627f7eb2Smrg 
311627f7eb2Smrg 
312627f7eb2Smrg /* delete_unit()-- Delete a unit from a tree */
313627f7eb2Smrg 
314627f7eb2Smrg static void
delete_unit(gfc_unit * old)315627f7eb2Smrg delete_unit (gfc_unit *old)
316627f7eb2Smrg {
317627f7eb2Smrg   unit_root = delete_treap (old, unit_root);
318627f7eb2Smrg }
319627f7eb2Smrg 
320627f7eb2Smrg 
321627f7eb2Smrg /* get_gfc_unit()-- Given an integer, return a pointer to the unit
322627f7eb2Smrg    structure.  Returns NULL if the unit does not exist,
323627f7eb2Smrg    otherwise returns a locked unit. */
324627f7eb2Smrg 
325627f7eb2Smrg static gfc_unit *
get_gfc_unit(int n,int do_create)326627f7eb2Smrg get_gfc_unit (int n, int do_create)
327627f7eb2Smrg {
328627f7eb2Smrg   gfc_unit *p;
329627f7eb2Smrg   int c, created = 0;
330627f7eb2Smrg 
331627f7eb2Smrg   NOTE ("Unit n=%d, do_create = %d", n, do_create);
332627f7eb2Smrg   LOCK (&unit_lock);
333627f7eb2Smrg 
334627f7eb2Smrg retry:
335627f7eb2Smrg   for (c = 0; c < CACHE_SIZE; c++)
336627f7eb2Smrg     if (unit_cache[c] != NULL && unit_cache[c]->unit_number == n)
337627f7eb2Smrg       {
338627f7eb2Smrg 	p = unit_cache[c];
339627f7eb2Smrg 	goto found;
340627f7eb2Smrg       }
341627f7eb2Smrg 
342627f7eb2Smrg   p = unit_root;
343627f7eb2Smrg   while (p != NULL)
344627f7eb2Smrg     {
345627f7eb2Smrg       c = compare (n, p->unit_number);
346627f7eb2Smrg       if (c < 0)
347627f7eb2Smrg 	p = p->left;
348627f7eb2Smrg       if (c > 0)
349627f7eb2Smrg 	p = p->right;
350627f7eb2Smrg       if (c == 0)
351627f7eb2Smrg 	break;
352627f7eb2Smrg     }
353627f7eb2Smrg 
354627f7eb2Smrg   if (p == NULL && do_create)
355627f7eb2Smrg     {
356627f7eb2Smrg       p = insert_unit (n);
357627f7eb2Smrg       created = 1;
358627f7eb2Smrg     }
359627f7eb2Smrg 
360627f7eb2Smrg   if (p != NULL)
361627f7eb2Smrg     {
362627f7eb2Smrg       for (c = 0; c < CACHE_SIZE - 1; c++)
363627f7eb2Smrg 	unit_cache[c] = unit_cache[c + 1];
364627f7eb2Smrg 
365627f7eb2Smrg       unit_cache[CACHE_SIZE - 1] = p;
366627f7eb2Smrg     }
367627f7eb2Smrg 
368627f7eb2Smrg   if (created)
369627f7eb2Smrg     {
370627f7eb2Smrg       /* Newly created units have their lock held already
371627f7eb2Smrg 	 from insert_unit.  Just unlock UNIT_LOCK and return.  */
372627f7eb2Smrg       UNLOCK (&unit_lock);
373627f7eb2Smrg       return p;
374627f7eb2Smrg     }
375627f7eb2Smrg 
376627f7eb2Smrg found:
377627f7eb2Smrg   if (p != NULL && (p->child_dtio == 0))
378627f7eb2Smrg     {
379627f7eb2Smrg       /* Fast path.  */
380627f7eb2Smrg       if (! TRYLOCK (&p->lock))
381627f7eb2Smrg 	{
382627f7eb2Smrg 	  /* assert (p->closed == 0); */
383627f7eb2Smrg 	  UNLOCK (&unit_lock);
384627f7eb2Smrg 	  return p;
385627f7eb2Smrg 	}
386627f7eb2Smrg 
387627f7eb2Smrg       inc_waiting_locked (p);
388627f7eb2Smrg     }
389627f7eb2Smrg 
390627f7eb2Smrg 
391627f7eb2Smrg   UNLOCK (&unit_lock);
392627f7eb2Smrg 
393627f7eb2Smrg   if (p != NULL && (p->child_dtio == 0))
394627f7eb2Smrg     {
395627f7eb2Smrg       LOCK (&p->lock);
396627f7eb2Smrg       if (p->closed)
397627f7eb2Smrg 	{
398627f7eb2Smrg 	  LOCK (&unit_lock);
399627f7eb2Smrg 	  UNLOCK (&p->lock);
400627f7eb2Smrg 	  if (predec_waiting_locked (p) == 0)
401627f7eb2Smrg 	    destroy_unit_mutex (p);
402627f7eb2Smrg 	  goto retry;
403627f7eb2Smrg 	}
404627f7eb2Smrg 
405627f7eb2Smrg       dec_waiting_unlocked (p);
406627f7eb2Smrg     }
407627f7eb2Smrg   return p;
408627f7eb2Smrg }
409627f7eb2Smrg 
410627f7eb2Smrg 
411627f7eb2Smrg gfc_unit *
find_unit(int n)412627f7eb2Smrg find_unit (int n)
413627f7eb2Smrg {
414627f7eb2Smrg   return get_gfc_unit (n, 0);
415627f7eb2Smrg }
416627f7eb2Smrg 
417627f7eb2Smrg 
418627f7eb2Smrg gfc_unit *
find_or_create_unit(int n)419627f7eb2Smrg find_or_create_unit (int n)
420627f7eb2Smrg {
421627f7eb2Smrg   return get_gfc_unit (n, 1);
422627f7eb2Smrg }
423627f7eb2Smrg 
424627f7eb2Smrg 
425627f7eb2Smrg /* Helper function to check rank, stride, format string, and namelist.
426627f7eb2Smrg    This is used for optimization. You can't trim out blanks or shorten
427627f7eb2Smrg    the string if trailing spaces are significant.  */
428627f7eb2Smrg static bool
is_trim_ok(st_parameter_dt * dtp)429627f7eb2Smrg is_trim_ok (st_parameter_dt *dtp)
430627f7eb2Smrg {
431627f7eb2Smrg   /* Check rank and stride.  */
432627f7eb2Smrg   if (dtp->internal_unit_desc)
433627f7eb2Smrg     return false;
434627f7eb2Smrg   /* Format strings cannot have 'BZ' or '/'.  */
435627f7eb2Smrg   if (dtp->common.flags & IOPARM_DT_HAS_FORMAT)
436627f7eb2Smrg     {
437627f7eb2Smrg       char *p = dtp->format;
438627f7eb2Smrg       if (dtp->common.flags & IOPARM_DT_HAS_BLANK)
439627f7eb2Smrg 	return false;
440627f7eb2Smrg       for (gfc_charlen_type i = 0; i < dtp->format_len; i++)
441627f7eb2Smrg 	{
442627f7eb2Smrg 	  if (p[i] == '/') return false;
443627f7eb2Smrg 	  if (p[i] == 'b' || p[i] == 'B')
444627f7eb2Smrg 	    if (p[i+1] == 'z' || p[i+1] == 'Z')
445627f7eb2Smrg 	      return false;
446627f7eb2Smrg 	}
447627f7eb2Smrg     }
448627f7eb2Smrg   if (dtp->u.p.ionml) /* A namelist.  */
449627f7eb2Smrg     return false;
450627f7eb2Smrg   return true;
451627f7eb2Smrg }
452627f7eb2Smrg 
453627f7eb2Smrg 
454627f7eb2Smrg gfc_unit *
set_internal_unit(st_parameter_dt * dtp,gfc_unit * iunit,int kind)455627f7eb2Smrg set_internal_unit (st_parameter_dt *dtp, gfc_unit *iunit, int kind)
456627f7eb2Smrg {
457627f7eb2Smrg   gfc_offset start_record = 0;
458627f7eb2Smrg 
459627f7eb2Smrg   iunit->unit_number = dtp->common.unit;
460627f7eb2Smrg   iunit->recl = dtp->internal_unit_len;
461627f7eb2Smrg   iunit->internal_unit = dtp->internal_unit;
462627f7eb2Smrg   iunit->internal_unit_len = dtp->internal_unit_len;
463627f7eb2Smrg   iunit->internal_unit_kind = kind;
464627f7eb2Smrg 
465627f7eb2Smrg   /* As an optimization, adjust the unit record length to not
466627f7eb2Smrg      include trailing blanks. This will not work under certain conditions
467627f7eb2Smrg      where trailing blanks have significance.  */
468627f7eb2Smrg   if (dtp->u.p.mode == READING && is_trim_ok (dtp))
469627f7eb2Smrg     {
470627f7eb2Smrg       int len;
471627f7eb2Smrg       if (kind == 1)
472627f7eb2Smrg 	  len = string_len_trim (iunit->internal_unit_len,
473627f7eb2Smrg 						   iunit->internal_unit);
474627f7eb2Smrg       else
475627f7eb2Smrg 	  len = string_len_trim_char4 (iunit->internal_unit_len,
476627f7eb2Smrg 			      (const gfc_char4_t*) iunit->internal_unit);
477627f7eb2Smrg       iunit->internal_unit_len = len;
478627f7eb2Smrg       iunit->recl = iunit->internal_unit_len;
479627f7eb2Smrg     }
480627f7eb2Smrg 
481627f7eb2Smrg   /* Set up the looping specification from the array descriptor, if any.  */
482627f7eb2Smrg 
483627f7eb2Smrg   if (is_array_io (dtp))
484627f7eb2Smrg     {
485627f7eb2Smrg       iunit->rank = GFC_DESCRIPTOR_RANK (dtp->internal_unit_desc);
486627f7eb2Smrg       iunit->ls = (array_loop_spec *)
487627f7eb2Smrg 	xmallocarray (iunit->rank, sizeof (array_loop_spec));
488627f7eb2Smrg       iunit->internal_unit_len *=
489627f7eb2Smrg 	init_loop_spec (dtp->internal_unit_desc, iunit->ls, &start_record);
490627f7eb2Smrg 
491627f7eb2Smrg       start_record *= iunit->recl;
492627f7eb2Smrg     }
493627f7eb2Smrg 
494627f7eb2Smrg   /* Set initial values for unit parameters.  */
495627f7eb2Smrg   if (kind == 4)
496627f7eb2Smrg     iunit->s = open_internal4 (iunit->internal_unit - start_record,
497627f7eb2Smrg 				 iunit->internal_unit_len, -start_record);
498627f7eb2Smrg   else
499627f7eb2Smrg     iunit->s = open_internal (iunit->internal_unit - start_record,
500627f7eb2Smrg 			      iunit->internal_unit_len, -start_record);
501627f7eb2Smrg 
502627f7eb2Smrg   iunit->bytes_left = iunit->recl;
503627f7eb2Smrg   iunit->last_record=0;
504627f7eb2Smrg   iunit->maxrec=0;
505627f7eb2Smrg   iunit->current_record=0;
506627f7eb2Smrg   iunit->read_bad = 0;
507627f7eb2Smrg   iunit->endfile = NO_ENDFILE;
508627f7eb2Smrg 
509627f7eb2Smrg   /* Set flags for the internal unit.  */
510627f7eb2Smrg 
511627f7eb2Smrg   iunit->flags.access = ACCESS_SEQUENTIAL;
512627f7eb2Smrg   iunit->flags.action = ACTION_READWRITE;
513627f7eb2Smrg   iunit->flags.blank = BLANK_NULL;
514627f7eb2Smrg   iunit->flags.form = FORM_FORMATTED;
515627f7eb2Smrg   iunit->flags.pad = PAD_YES;
516627f7eb2Smrg   iunit->flags.status = STATUS_UNSPECIFIED;
517627f7eb2Smrg   iunit->flags.sign = SIGN_PROCDEFINED;
518627f7eb2Smrg   iunit->flags.decimal = DECIMAL_POINT;
519627f7eb2Smrg   iunit->flags.delim = DELIM_UNSPECIFIED;
520627f7eb2Smrg   iunit->flags.encoding = ENCODING_DEFAULT;
521627f7eb2Smrg   iunit->flags.async = ASYNC_NO;
522627f7eb2Smrg   iunit->flags.round = ROUND_PROCDEFINED;
523627f7eb2Smrg 
524627f7eb2Smrg   /* Initialize the data transfer parameters.  */
525627f7eb2Smrg 
526627f7eb2Smrg   dtp->u.p.advance_status = ADVANCE_YES;
527627f7eb2Smrg   dtp->u.p.seen_dollar = 0;
528627f7eb2Smrg   dtp->u.p.skips = 0;
529627f7eb2Smrg   dtp->u.p.pending_spaces = 0;
530627f7eb2Smrg   dtp->u.p.max_pos = 0;
531627f7eb2Smrg   dtp->u.p.at_eof = 0;
532627f7eb2Smrg   return iunit;
533627f7eb2Smrg }
534627f7eb2Smrg 
535627f7eb2Smrg 
536627f7eb2Smrg /* get_unit()-- Returns the unit structure associated with the integer
537627f7eb2Smrg    unit or the internal file.  */
538627f7eb2Smrg 
539627f7eb2Smrg gfc_unit *
get_unit(st_parameter_dt * dtp,int do_create)540627f7eb2Smrg get_unit (st_parameter_dt *dtp, int do_create)
541627f7eb2Smrg {
542627f7eb2Smrg   gfc_unit *unit;
543627f7eb2Smrg 
544627f7eb2Smrg   if ((dtp->common.flags & IOPARM_DT_HAS_INTERNAL_UNIT) != 0)
545627f7eb2Smrg     {
546627f7eb2Smrg       int kind;
547627f7eb2Smrg       if (dtp->common.unit == GFC_INTERNAL_UNIT)
548627f7eb2Smrg         kind = 1;
549627f7eb2Smrg       else if (dtp->common.unit == GFC_INTERNAL_UNIT4)
550627f7eb2Smrg         kind = 4;
551627f7eb2Smrg       else
552627f7eb2Smrg 	internal_error (&dtp->common, "get_unit(): Bad internal unit KIND");
553627f7eb2Smrg 
554627f7eb2Smrg       dtp->u.p.unit_is_internal = 1;
555627f7eb2Smrg       dtp->common.unit = newunit_alloc ();
556627f7eb2Smrg       unit = get_gfc_unit (dtp->common.unit, do_create);
557627f7eb2Smrg       set_internal_unit (dtp, unit, kind);
558627f7eb2Smrg       fbuf_init (unit, 128);
559627f7eb2Smrg       return unit;
560627f7eb2Smrg     }
561627f7eb2Smrg 
562627f7eb2Smrg   /* Has to be an external unit.  */
563627f7eb2Smrg   dtp->u.p.unit_is_internal = 0;
564627f7eb2Smrg   dtp->internal_unit = NULL;
565627f7eb2Smrg   dtp->internal_unit_desc = NULL;
566627f7eb2Smrg 
567627f7eb2Smrg   /* For an external unit with unit number < 0 creating it on the fly
568627f7eb2Smrg      is not allowed, such units must be created with
569627f7eb2Smrg      OPEN(NEWUNIT=...).  */
570627f7eb2Smrg   if (dtp->common.unit < 0)
571627f7eb2Smrg     {
572627f7eb2Smrg       if (dtp->common.unit > NEWUNIT_START) /* Reserved units.  */
573627f7eb2Smrg 	return NULL;
574627f7eb2Smrg       return get_gfc_unit (dtp->common.unit, 0);
575627f7eb2Smrg     }
576627f7eb2Smrg 
577627f7eb2Smrg   return get_gfc_unit (dtp->common.unit, do_create);
578627f7eb2Smrg }
579627f7eb2Smrg 
580627f7eb2Smrg 
581627f7eb2Smrg /*************************/
582627f7eb2Smrg /* Initialize everything.  */
583627f7eb2Smrg 
584627f7eb2Smrg void
init_units(void)585627f7eb2Smrg init_units (void)
586627f7eb2Smrg {
587627f7eb2Smrg   gfc_unit *u;
588627f7eb2Smrg 
589627f7eb2Smrg #ifdef HAVE_NEWLOCALE
590627f7eb2Smrg   c_locale = newlocale (0, "C", 0);
591627f7eb2Smrg #else
592627f7eb2Smrg #ifndef __GTHREAD_MUTEX_INIT
593627f7eb2Smrg   __GTHREAD_MUTEX_INIT_FUNCTION (&old_locale_lock);
594627f7eb2Smrg #endif
595627f7eb2Smrg #endif
596627f7eb2Smrg 
597627f7eb2Smrg #ifndef __GTHREAD_MUTEX_INIT
598627f7eb2Smrg   __GTHREAD_MUTEX_INIT_FUNCTION (&unit_lock);
599627f7eb2Smrg #endif
600627f7eb2Smrg 
601627f7eb2Smrg   if (sizeof (max_offset) == 8)
602627f7eb2Smrg     {
603627f7eb2Smrg       max_offset = GFC_INTEGER_8_HUGE;
604627f7eb2Smrg       /* Why this weird value? Because if the recl specifier in the
605627f7eb2Smrg 	 inquire statement is a 4 byte value, u->recl is truncated,
606627f7eb2Smrg 	 and this trick ensures it becomes HUGE(0) rather than -1.
607627f7eb2Smrg 	 The full 8 byte value of default_recl is still 0.99999999 *
608627f7eb2Smrg 	 max_offset which is large enough for all practical
609627f7eb2Smrg 	 purposes.  */
610627f7eb2Smrg       default_recl = max_offset & ~(1LL<<31);
611627f7eb2Smrg     }
612627f7eb2Smrg   else if (sizeof (max_offset) == 4)
613627f7eb2Smrg     max_offset = default_recl = GFC_INTEGER_4_HUGE;
614627f7eb2Smrg   else
615627f7eb2Smrg     internal_error (NULL, "sizeof (max_offset) must be 4 or 8");
616627f7eb2Smrg 
617627f7eb2Smrg   if (options.stdin_unit >= 0)
618627f7eb2Smrg     {				/* STDIN */
619627f7eb2Smrg       u = insert_unit (options.stdin_unit);
620627f7eb2Smrg       u->s = input_stream ();
621627f7eb2Smrg 
622627f7eb2Smrg       u->flags.action = ACTION_READ;
623627f7eb2Smrg 
624627f7eb2Smrg       u->flags.access = ACCESS_SEQUENTIAL;
625627f7eb2Smrg       u->flags.form = FORM_FORMATTED;
626627f7eb2Smrg       u->flags.status = STATUS_OLD;
627627f7eb2Smrg       u->flags.blank = BLANK_NULL;
628627f7eb2Smrg       u->flags.pad = PAD_YES;
629627f7eb2Smrg       u->flags.position = POSITION_ASIS;
630627f7eb2Smrg       u->flags.sign = SIGN_PROCDEFINED;
631627f7eb2Smrg       u->flags.decimal = DECIMAL_POINT;
632627f7eb2Smrg       u->flags.delim = DELIM_UNSPECIFIED;
633627f7eb2Smrg       u->flags.encoding = ENCODING_DEFAULT;
634627f7eb2Smrg       u->flags.async = ASYNC_NO;
635627f7eb2Smrg       u->flags.round = ROUND_PROCDEFINED;
636627f7eb2Smrg       u->flags.share = SHARE_UNSPECIFIED;
637627f7eb2Smrg       u->flags.cc = CC_LIST;
638627f7eb2Smrg 
639627f7eb2Smrg       u->recl = default_recl;
640627f7eb2Smrg       u->endfile = NO_ENDFILE;
641627f7eb2Smrg 
642627f7eb2Smrg       u->filename = strdup (stdin_name);
643627f7eb2Smrg 
644627f7eb2Smrg       fbuf_init (u, 0);
645627f7eb2Smrg 
646627f7eb2Smrg       UNLOCK (&u->lock);
647627f7eb2Smrg     }
648627f7eb2Smrg 
649627f7eb2Smrg   if (options.stdout_unit >= 0)
650627f7eb2Smrg     {				/* STDOUT */
651627f7eb2Smrg       u = insert_unit (options.stdout_unit);
652627f7eb2Smrg       u->s = output_stream ();
653627f7eb2Smrg 
654627f7eb2Smrg       u->flags.action = ACTION_WRITE;
655627f7eb2Smrg 
656627f7eb2Smrg       u->flags.access = ACCESS_SEQUENTIAL;
657627f7eb2Smrg       u->flags.form = FORM_FORMATTED;
658627f7eb2Smrg       u->flags.status = STATUS_OLD;
659627f7eb2Smrg       u->flags.blank = BLANK_NULL;
660627f7eb2Smrg       u->flags.position = POSITION_ASIS;
661627f7eb2Smrg       u->flags.sign = SIGN_PROCDEFINED;
662627f7eb2Smrg       u->flags.decimal = DECIMAL_POINT;
663627f7eb2Smrg       u->flags.delim = DELIM_UNSPECIFIED;
664627f7eb2Smrg       u->flags.encoding = ENCODING_DEFAULT;
665627f7eb2Smrg       u->flags.async = ASYNC_NO;
666627f7eb2Smrg       u->flags.round = ROUND_PROCDEFINED;
667627f7eb2Smrg       u->flags.share = SHARE_UNSPECIFIED;
668627f7eb2Smrg       u->flags.cc = CC_LIST;
669627f7eb2Smrg 
670627f7eb2Smrg       u->recl = default_recl;
671627f7eb2Smrg       u->endfile = AT_ENDFILE;
672627f7eb2Smrg 
673627f7eb2Smrg       u->filename = strdup (stdout_name);
674627f7eb2Smrg 
675627f7eb2Smrg       fbuf_init (u, 0);
676627f7eb2Smrg 
677627f7eb2Smrg       UNLOCK (&u->lock);
678627f7eb2Smrg     }
679627f7eb2Smrg 
680627f7eb2Smrg   if (options.stderr_unit >= 0)
681627f7eb2Smrg     {				/* STDERR */
682627f7eb2Smrg       u = insert_unit (options.stderr_unit);
683627f7eb2Smrg       u->s = error_stream ();
684627f7eb2Smrg 
685627f7eb2Smrg       u->flags.action = ACTION_WRITE;
686627f7eb2Smrg 
687627f7eb2Smrg       u->flags.access = ACCESS_SEQUENTIAL;
688627f7eb2Smrg       u->flags.form = FORM_FORMATTED;
689627f7eb2Smrg       u->flags.status = STATUS_OLD;
690627f7eb2Smrg       u->flags.blank = BLANK_NULL;
691627f7eb2Smrg       u->flags.position = POSITION_ASIS;
692627f7eb2Smrg       u->flags.sign = SIGN_PROCDEFINED;
693627f7eb2Smrg       u->flags.decimal = DECIMAL_POINT;
694627f7eb2Smrg       u->flags.encoding = ENCODING_DEFAULT;
695627f7eb2Smrg       u->flags.async = ASYNC_NO;
696627f7eb2Smrg       u->flags.round = ROUND_PROCDEFINED;
697627f7eb2Smrg       u->flags.share = SHARE_UNSPECIFIED;
698627f7eb2Smrg       u->flags.cc = CC_LIST;
699627f7eb2Smrg 
700627f7eb2Smrg       u->recl = default_recl;
701627f7eb2Smrg       u->endfile = AT_ENDFILE;
702627f7eb2Smrg 
703627f7eb2Smrg       u->filename = strdup (stderr_name);
704627f7eb2Smrg 
705627f7eb2Smrg       fbuf_init (u, 256);  /* 256 bytes should be enough, probably not doing
706627f7eb2Smrg                               any kind of exotic formatting to stderr.  */
707627f7eb2Smrg 
708627f7eb2Smrg       UNLOCK (&u->lock);
709627f7eb2Smrg     }
710627f7eb2Smrg   /* The default internal units.  */
711627f7eb2Smrg   u = insert_unit (GFC_INTERNAL_UNIT);
712627f7eb2Smrg   UNLOCK (&u->lock);
713627f7eb2Smrg   u = insert_unit (GFC_INTERNAL_UNIT4);
714627f7eb2Smrg   UNLOCK (&u->lock);
715627f7eb2Smrg }
716627f7eb2Smrg 
717627f7eb2Smrg 
718627f7eb2Smrg static int
close_unit_1(gfc_unit * u,int locked)719627f7eb2Smrg close_unit_1 (gfc_unit *u, int locked)
720627f7eb2Smrg {
721627f7eb2Smrg   int i, rc;
722627f7eb2Smrg 
723627f7eb2Smrg   if (ASYNC_IO && u->au)
724627f7eb2Smrg     async_close (u->au);
725627f7eb2Smrg 
726627f7eb2Smrg   /* If there are previously written bytes from a write with ADVANCE="no"
727627f7eb2Smrg      Reposition the buffer before closing.  */
728627f7eb2Smrg   if (u->previous_nonadvancing_write)
729627f7eb2Smrg     finish_last_advance_record (u);
730627f7eb2Smrg 
731627f7eb2Smrg   rc = (u->s == NULL) ? 0 : sclose (u->s) == -1;
732627f7eb2Smrg 
733627f7eb2Smrg   u->closed = 1;
734627f7eb2Smrg   if (!locked)
735627f7eb2Smrg     LOCK (&unit_lock);
736627f7eb2Smrg 
737627f7eb2Smrg   for (i = 0; i < CACHE_SIZE; i++)
738627f7eb2Smrg     if (unit_cache[i] == u)
739627f7eb2Smrg       unit_cache[i] = NULL;
740627f7eb2Smrg 
741627f7eb2Smrg   delete_unit (u);
742627f7eb2Smrg 
743627f7eb2Smrg   free (u->filename);
744627f7eb2Smrg   u->filename = NULL;
745627f7eb2Smrg 
746627f7eb2Smrg   free_format_hash_table (u);
747627f7eb2Smrg   fbuf_destroy (u);
748627f7eb2Smrg 
749627f7eb2Smrg   if (u->unit_number <= NEWUNIT_START)
750627f7eb2Smrg     newunit_free (u->unit_number);
751627f7eb2Smrg 
752627f7eb2Smrg   if (!locked)
753627f7eb2Smrg     UNLOCK (&u->lock);
754627f7eb2Smrg 
755627f7eb2Smrg   /* If there are any threads waiting in find_unit for this unit,
756627f7eb2Smrg      avoid freeing the memory, the last such thread will free it
757627f7eb2Smrg      instead.  */
758627f7eb2Smrg   if (u->waiting == 0)
759627f7eb2Smrg     destroy_unit_mutex (u);
760627f7eb2Smrg 
761627f7eb2Smrg   if (!locked)
762627f7eb2Smrg     UNLOCK (&unit_lock);
763627f7eb2Smrg 
764627f7eb2Smrg   return rc;
765627f7eb2Smrg }
766627f7eb2Smrg 
767627f7eb2Smrg void
unlock_unit(gfc_unit * u)768627f7eb2Smrg unlock_unit (gfc_unit *u)
769627f7eb2Smrg {
770*4c3eb207Smrg   if (u)
771*4c3eb207Smrg     {
772627f7eb2Smrg       NOTE ("unlock_unit = %d", u->unit_number);
773627f7eb2Smrg       UNLOCK (&u->lock);
774627f7eb2Smrg       NOTE ("unlock_unit done");
775627f7eb2Smrg     }
776*4c3eb207Smrg }
777627f7eb2Smrg 
778627f7eb2Smrg /* close_unit()-- Close a unit.  The stream is closed, and any memory
779627f7eb2Smrg    associated with the stream is freed.  Returns nonzero on I/O error.
780627f7eb2Smrg    Should be called with the u->lock locked. */
781627f7eb2Smrg 
782627f7eb2Smrg int
close_unit(gfc_unit * u)783627f7eb2Smrg close_unit (gfc_unit *u)
784627f7eb2Smrg {
785627f7eb2Smrg   return close_unit_1 (u, 0);
786627f7eb2Smrg }
787627f7eb2Smrg 
788627f7eb2Smrg 
789627f7eb2Smrg /* close_units()-- Delete units on completion.  We just keep deleting
790627f7eb2Smrg    the root of the treap until there is nothing left.
791627f7eb2Smrg    Not sure what to do with locking here.  Some other thread might be
792627f7eb2Smrg    holding some unit's lock and perhaps hold it indefinitely
793627f7eb2Smrg    (e.g. waiting for input from some pipe) and close_units shouldn't
794627f7eb2Smrg    delay the program too much.  */
795627f7eb2Smrg 
796627f7eb2Smrg void
close_units(void)797627f7eb2Smrg close_units (void)
798627f7eb2Smrg {
799627f7eb2Smrg   LOCK (&unit_lock);
800627f7eb2Smrg   while (unit_root != NULL)
801627f7eb2Smrg     close_unit_1 (unit_root, 1);
802627f7eb2Smrg   UNLOCK (&unit_lock);
803627f7eb2Smrg 
804627f7eb2Smrg   free (newunits);
805627f7eb2Smrg 
806627f7eb2Smrg #ifdef HAVE_FREELOCALE
807627f7eb2Smrg   freelocale (c_locale);
808627f7eb2Smrg #endif
809627f7eb2Smrg }
810627f7eb2Smrg 
811627f7eb2Smrg 
812627f7eb2Smrg /* High level interface to truncate a file, i.e. flush format buffers,
813627f7eb2Smrg    and generate an error or set some flags.  Just like POSIX
814627f7eb2Smrg    ftruncate, returns 0 on success, -1 on failure.  */
815627f7eb2Smrg 
816627f7eb2Smrg int
unit_truncate(gfc_unit * u,gfc_offset pos,st_parameter_common * common)817627f7eb2Smrg unit_truncate (gfc_unit *u, gfc_offset pos, st_parameter_common *common)
818627f7eb2Smrg {
819627f7eb2Smrg   int ret;
820627f7eb2Smrg 
821627f7eb2Smrg   /* Make sure format buffer is flushed.  */
822627f7eb2Smrg   if (u->flags.form == FORM_FORMATTED)
823627f7eb2Smrg     {
824627f7eb2Smrg       if (u->mode == READING)
825627f7eb2Smrg 	pos += fbuf_reset (u);
826627f7eb2Smrg       else
827627f7eb2Smrg 	fbuf_flush (u, u->mode);
828627f7eb2Smrg     }
829627f7eb2Smrg 
830627f7eb2Smrg   /* struncate() should flush the stream buffer if necessary, so don't
831627f7eb2Smrg      bother calling sflush() here.  */
832627f7eb2Smrg   ret = struncate (u->s, pos);
833627f7eb2Smrg 
834627f7eb2Smrg   if (ret != 0)
835627f7eb2Smrg     generate_error (common, LIBERROR_OS, NULL);
836627f7eb2Smrg   else
837627f7eb2Smrg     {
838627f7eb2Smrg       u->endfile = AT_ENDFILE;
839627f7eb2Smrg       u->flags.position = POSITION_APPEND;
840627f7eb2Smrg     }
841627f7eb2Smrg 
842627f7eb2Smrg   return ret;
843627f7eb2Smrg }
844627f7eb2Smrg 
845627f7eb2Smrg 
846627f7eb2Smrg /* filename_from_unit()-- If the unit_number exists, return a pointer to the
847627f7eb2Smrg    name of the associated file, otherwise return the empty string.  The caller
848627f7eb2Smrg    must free memory allocated for the filename string.  */
849627f7eb2Smrg 
850627f7eb2Smrg char *
filename_from_unit(int n)851627f7eb2Smrg filename_from_unit (int n)
852627f7eb2Smrg {
853627f7eb2Smrg   gfc_unit *u;
854627f7eb2Smrg   int c;
855627f7eb2Smrg 
856627f7eb2Smrg   /* Find the unit.  */
857627f7eb2Smrg   u = unit_root;
858627f7eb2Smrg   while (u != NULL)
859627f7eb2Smrg     {
860627f7eb2Smrg       c = compare (n, u->unit_number);
861627f7eb2Smrg       if (c < 0)
862627f7eb2Smrg 	u = u->left;
863627f7eb2Smrg       if (c > 0)
864627f7eb2Smrg 	u = u->right;
865627f7eb2Smrg       if (c == 0)
866627f7eb2Smrg 	break;
867627f7eb2Smrg     }
868627f7eb2Smrg 
869627f7eb2Smrg   /* Get the filename.  */
870627f7eb2Smrg   if (u != NULL && u->filename != NULL)
871627f7eb2Smrg     return strdup (u->filename);
872627f7eb2Smrg   else
873627f7eb2Smrg     return (char *) NULL;
874627f7eb2Smrg }
875627f7eb2Smrg 
876627f7eb2Smrg void
finish_last_advance_record(gfc_unit * u)877627f7eb2Smrg finish_last_advance_record (gfc_unit *u)
878627f7eb2Smrg {
879627f7eb2Smrg 
880627f7eb2Smrg   if (u->saved_pos > 0)
881627f7eb2Smrg     fbuf_seek (u, u->saved_pos, SEEK_CUR);
882627f7eb2Smrg 
883627f7eb2Smrg   if (!(u->unit_number == options.stdout_unit
884627f7eb2Smrg 	|| u->unit_number == options.stderr_unit))
885627f7eb2Smrg     {
886627f7eb2Smrg #ifdef HAVE_CRLF
887627f7eb2Smrg       const int len = 2;
888627f7eb2Smrg #else
889627f7eb2Smrg       const int len = 1;
890627f7eb2Smrg #endif
891627f7eb2Smrg       char *p = fbuf_alloc (u, len);
892627f7eb2Smrg       if (!p)
893627f7eb2Smrg 	os_error ("Completing record after ADVANCE_NO failed");
894627f7eb2Smrg #ifdef HAVE_CRLF
895627f7eb2Smrg       *(p++) = '\r';
896627f7eb2Smrg #endif
897627f7eb2Smrg       *p = '\n';
898627f7eb2Smrg     }
899627f7eb2Smrg 
900627f7eb2Smrg   fbuf_flush (u, u->mode);
901627f7eb2Smrg }
902627f7eb2Smrg 
903627f7eb2Smrg 
904627f7eb2Smrg /* Assign a negative number for NEWUNIT in OPEN statements or for
905627f7eb2Smrg    internal units.  */
906627f7eb2Smrg int
newunit_alloc(void)907627f7eb2Smrg newunit_alloc (void)
908627f7eb2Smrg {
909627f7eb2Smrg   LOCK (&unit_lock);
910627f7eb2Smrg   if (!newunits)
911627f7eb2Smrg     {
912627f7eb2Smrg       newunits = xcalloc (16, 1);
913627f7eb2Smrg       newunit_size = 16;
914627f7eb2Smrg     }
915627f7eb2Smrg 
916627f7eb2Smrg   /* Search for the next available newunit.  */
917627f7eb2Smrg   for (int ii = newunit_lwi; ii < newunit_size; ii++)
918627f7eb2Smrg     {
919627f7eb2Smrg       if (!newunits[ii])
920627f7eb2Smrg         {
921627f7eb2Smrg           newunits[ii] = true;
922627f7eb2Smrg           newunit_lwi = ii + 1;
923627f7eb2Smrg 	  UNLOCK (&unit_lock);
924627f7eb2Smrg           return -ii + NEWUNIT_START;
925627f7eb2Smrg         }
926627f7eb2Smrg     }
927627f7eb2Smrg 
928627f7eb2Smrg   /* Search failed, bump size of array and allocate the first
929627f7eb2Smrg      available unit.  */
930627f7eb2Smrg   int old_size = newunit_size;
931627f7eb2Smrg   newunit_size *= 2;
932627f7eb2Smrg   newunits = xrealloc (newunits, newunit_size);
933627f7eb2Smrg   memset (newunits + old_size, 0, old_size);
934627f7eb2Smrg   newunits[old_size] = true;
935627f7eb2Smrg   newunit_lwi = old_size + 1;
936627f7eb2Smrg     UNLOCK (&unit_lock);
937627f7eb2Smrg   return -old_size + NEWUNIT_START;
938627f7eb2Smrg }
939627f7eb2Smrg 
940627f7eb2Smrg 
941627f7eb2Smrg /* Free a previously allocated newunit= unit number.  unit_lock must
942627f7eb2Smrg    be held when calling.  */
943627f7eb2Smrg 
944627f7eb2Smrg void
newunit_free(int unit)945627f7eb2Smrg newunit_free (int unit)
946627f7eb2Smrg {
947627f7eb2Smrg   int ind = -unit + NEWUNIT_START;
948627f7eb2Smrg   assert(ind >= 0 && ind < newunit_size);
949627f7eb2Smrg   newunits[ind] = false;
950627f7eb2Smrg   if (ind < newunit_lwi)
951627f7eb2Smrg     newunit_lwi = ind;
952627f7eb2Smrg }
953