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