1 | /* vms_fwrite.c - augmentation for the fwrite() function.
|
---|
2 |
|
---|
3 | Copyright (C) 1991-1996 the Free Software Foundation, Inc.
|
---|
4 |
|
---|
5 | This program is free software; you can redistribute it and/or modify
|
---|
6 | it under the terms of the GNU General Public License as published by
|
---|
7 | the Free Software Foundation; either version 2, or (at your option)
|
---|
8 | any later version.
|
---|
9 |
|
---|
10 | This program is distributed in the hope that it will be useful,
|
---|
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
13 | GNU General Public License for more details.
|
---|
14 |
|
---|
15 | You should have received a copy of the GNU General Public License
|
---|
16 | along with this program; if not, write to the Free Software Foundation,
|
---|
17 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
|
---|
18 |
|
---|
19 | #include "awk.h" /* really "../awk.h" */
|
---|
20 |
|
---|
21 | #ifndef NO_TTY_FWRITE
|
---|
22 | #include "vms.h"
|
---|
23 | #include <stdio.h>
|
---|
24 | #include <errno.h>
|
---|
25 |
|
---|
26 | #ifdef VAXC_BUILTINS
|
---|
27 | #pragma builtins /* VAXC V3.0 & up */
|
---|
28 | # define find_c(s,n,c) ((n) - _LOCC((c),(n),(s)))
|
---|
29 | #else /*VAXC_BUILTINS*/
|
---|
30 | static int find_c( const char *s, int n, char c ) {
|
---|
31 | register const char *t = (const char *)memchr(s, c, n);
|
---|
32 | return (t == 0 ? n : t - s); /* 0..n-1, or n if not found */
|
---|
33 | }
|
---|
34 | #endif /*VAXC_BUILTINS*/
|
---|
35 | #define is_stdout(file_no) ((file_no) == 1) /* fileno(stdout) */
|
---|
36 | #define is_stderr(file_no) ((file_no) == 2) /* fileno(stderr) */
|
---|
37 |
|
---|
38 | #define PREFIX_CR 0x008D0000 /* leading carriage return */
|
---|
39 | #define POSTFIX_CR 0x8D000000 /* trailing carriage return (=> lf/cr) */
|
---|
40 |
|
---|
41 | static short channel[_NFILE] = {0};
|
---|
42 | static FILE *prev_file = 0;
|
---|
43 | static int prev_file_num;
|
---|
44 |
|
---|
45 | /*
|
---|
46 | * VAXCRTL's fwrite() seems to flush after every character when
|
---|
47 | * writing to a terminal. This routine is a limited functionality
|
---|
48 | * substitute that is *much* faster. However, calls to fwrite()
|
---|
49 | * should not be mixed with other stdio calls to the same file
|
---|
50 | * unless fflush() is always called first. Also, this routine
|
---|
51 | * will not detect that a freopen() call has finished with the
|
---|
52 | * original terminal; tty_fclose() should be used to close a file.
|
---|
53 | */
|
---|
54 | #ifdef fwrite
|
---|
55 | # undef fwrite
|
---|
56 | #endif
|
---|
57 | /* tty_fwrite() - performance hack for fwrite() to a terminal */
|
---|
58 | size_t
|
---|
59 | tty_fwrite( const void *buf, size_t size, size_t number, FILE *file )
|
---|
60 | {
|
---|
61 | static long evfn = -1;
|
---|
62 | short chan;
|
---|
63 | int file_num, result;
|
---|
64 |
|
---|
65 | if (!size || !number)
|
---|
66 | return 0;
|
---|
67 | else if (!file || !*file)
|
---|
68 | return 0 * (errno = EBADF); /* kludge alert! */
|
---|
69 | else if (file == prev_file)
|
---|
70 | file_num = prev_file_num;
|
---|
71 | else /* note: VAXCRTL's fileno() is a function, not just a macro */
|
---|
72 | prev_file_num = file_num = fileno(file), prev_file = file;
|
---|
73 |
|
---|
74 | chan = file_num < _NFILE ? channel[file_num] : -1;
|
---|
75 | if (chan == 0) { /* if not initialized, need to assign a channel */
|
---|
76 | if (isatty(file_num) > 0) { /* isatty: 1=yes, 0=no, -1=problem */
|
---|
77 | Dsc device;
|
---|
78 | char devnam[255+1];
|
---|
79 | fgetname(file, devnam); /* get 'file's name */
|
---|
80 | device.len = strlen(device.adr = devnam); /* create descriptor */
|
---|
81 | if (vmswork(sys$assign(&device, &chan, 0, (Dsc *)0))) {
|
---|
82 | /* get an event flag; use #0 if problem */
|
---|
83 | if (evfn == -1 && vmsfail(lib$get_ef(&evfn))) evfn = 0;
|
---|
84 | } else chan = 0; /* $ASSIGN failed */
|
---|
85 | }
|
---|
86 | /* store channel for later use; -1 => don't repeat failed init attempt */
|
---|
87 | channel[file_num] = (chan > 0 ? chan : -1);
|
---|
88 | }
|
---|
89 | if (chan > 0) { /* chan > 0 iff 'file' is a terminal */
|
---|
90 | struct _iosbw { U_Short status, count; U_Long rt_kludge; } iosb;
|
---|
91 | register U_Long sts = 1;
|
---|
92 | register char *pt = (char *)buf;
|
---|
93 | register int offset, pos, count = size * number;
|
---|
94 | U_Long cc_fmt, io_func = IO$_WRITEVBLK;
|
---|
95 | int extra = 0;
|
---|
96 | result = 0;
|
---|
97 | if (is_stderr(file_num)) /* if it's SYS$ERROR (stderr)... */
|
---|
98 | io_func |= IO$M_CANCTRLO; /* cancel ^O (resume tty output) */
|
---|
99 | while (count > 0) {
|
---|
100 | /* special handling for line-feeds to make them be 'newlines' */
|
---|
101 | offset = 0;
|
---|
102 | if (*pt == '\n') { /* got at least one leading line-feed */
|
---|
103 | cc_fmt = PREFIX_CR, extra++; /* precede 1st LF with a CR */
|
---|
104 | do offset++;
|
---|
105 | while (offset < count && *(pt + offset) == '\n');
|
---|
106 | } else
|
---|
107 | cc_fmt = 0;
|
---|
108 | /* look for another line-feed; if found, break line there */
|
---|
109 | pos = offset + find_c(pt + offset, count - offset, '\n');
|
---|
110 | if (pos >= BUFSIZ) pos = BUFSIZ - 1; /* throttle quota usage */
|
---|
111 | else if (pos < count) pos++, cc_fmt |= POSTFIX_CR, extra++;
|
---|
112 | /* wait for previous write, if any, to complete */
|
---|
113 | if (pt > (char *)buf) {
|
---|
114 | sts = sys$synch(evfn, &iosb);
|
---|
115 | if (vmswork(sts)) sts = iosb.status, result += iosb.count;
|
---|
116 | if (vmsfail(sts)) break;
|
---|
117 | }
|
---|
118 | /* queue an asynchronous write */
|
---|
119 | sts = sys$qio(evfn, chan, io_func, &iosb, (void (*)(U_Long))0, 0L,
|
---|
120 | pt, pos, 0, cc_fmt, 0, 0);
|
---|
121 | if (vmsfail(sts)) break; /*(should never happen)*/
|
---|
122 | pt += pos, count -= pos;
|
---|
123 | }
|
---|
124 | /* wait for last write to complete */
|
---|
125 | if (pt > (char *)buf && vmswork(sts)) {
|
---|
126 | sts = sys$synch(evfn, &iosb);
|
---|
127 | if (vmswork(sts)) sts = iosb.status, result += iosb.count;
|
---|
128 | }
|
---|
129 | if (vmsfail(sts)) errno = EVMSERR, vaxc$errno = sts;
|
---|
130 | else if (iosb.rt_kludge == 0) result = number + extra;
|
---|
131 | result -= extra; /* subtract the additional carriage-returns */
|
---|
132 | } else { /* use stdio */
|
---|
133 | /* Note: we assume that we're writing text, not binary data.
|
---|
134 | For stream format files, 'size' and 'number' are effectively
|
---|
135 | interchangable, and fwrite works fine. However, for record
|
---|
136 | format files, 'size' governs the maximum record length, so
|
---|
137 | fwrite(string, size(char), strlen(string), file)
|
---|
138 | will produce a sequence of 1-byte records, which is hardly
|
---|
139 | what we want in this (assumed) situation. Line-feeds ('\n')
|
---|
140 | are converted into newlines (ie, record separators) by the
|
---|
141 | run-time library, but strings that don't end with a newline
|
---|
142 | still become separate records. The simplest work around
|
---|
143 | is just to use fputs() instead of fwrite(); unfortunately,
|
---|
144 | we have to provide special treatment for NULs ('\0's).
|
---|
145 | At present, only stdout might be in record format (via
|
---|
146 | >$'filename' redirection on the command line).
|
---|
147 | */
|
---|
148 | if (size > 1) { /* not used by GAWK */
|
---|
149 | result = fwrite((void *)buf, size, number, file);
|
---|
150 | } else if (*((char *)buf + number - 1) == '\n' || !is_stdout(file_num)) {
|
---|
151 | result = fwrite((void *)buf, number, size, file);
|
---|
152 | result = result * number / size; /*(same as 'result = number')*/
|
---|
153 | } else {
|
---|
154 | #ifdef NO_ALLOCA
|
---|
155 | # define alloca(n) ((n) <= abuf_siz ? abuf : \
|
---|
156 | ((abuf_siz > 0 ? (free(abuf),0) : 0), \
|
---|
157 | (abuf = malloc(abuf_siz = (n)+20))))
|
---|
158 | static void *abuf = 0;
|
---|
159 | static size_t abuf_siz = 0;
|
---|
160 | #endif /*NO_ALLOCA*/
|
---|
161 | register char *pt = (char *)buf;
|
---|
162 | register int pos, count = number;
|
---|
163 | if (pt[count] != '\0') { /*(out of bounds, but relatively safe)*/
|
---|
164 | pt = (char *)alloca(count + 1);
|
---|
165 | memcpy(pt, buf, count), pt[count] = '\0';
|
---|
166 | /* if exiting this block undoes the alloca(), we're hosed :-( */
|
---|
167 | }
|
---|
168 | result = 0;
|
---|
169 | while (count > 0) {
|
---|
170 | pos = find_c(pt, count, '\0');
|
---|
171 | if (fputs(pt, file) < 0) break;
|
---|
172 | if (pos < count) {
|
---|
173 | if (fputc('\0', file) < 0) break;
|
---|
174 | pos++; /* 0..n-1 -> 1..n */
|
---|
175 | }
|
---|
176 | result += pos, pt += pos, count -= pos;
|
---|
177 | }
|
---|
178 | }
|
---|
179 | }
|
---|
180 | return result;
|
---|
181 | }
|
---|
182 | #define fwrite(b,s,n,f) tty_fwrite((b),(s),(n),(f))
|
---|
183 |
|
---|
184 | #ifdef fclose
|
---|
185 | # undef fclose
|
---|
186 | #endif
|
---|
187 | /* tty_fclose() - keep tty_fwrite() up to date when closing a file */
|
---|
188 | int
|
---|
189 | tty_fclose( FILE *file )
|
---|
190 | {
|
---|
191 | if (file && *file) { /* note: VAXCRTL stdio has extra level of indirection */
|
---|
192 | int file_num = fileno(file);
|
---|
193 | short chan = file_num < _NFILE ? channel[file_num] : -1;
|
---|
194 | if (chan > 0)
|
---|
195 | (void)sys$dassgn(chan); /* deassign the channel (ie, close) */
|
---|
196 | if (file_num < _NFILE)
|
---|
197 | channel[file_num] = 0; /* clear stale info */
|
---|
198 | }
|
---|
199 | prev_file = 0; /* force tty_fwrite() to reset */
|
---|
200 | return fclose(file);
|
---|
201 | }
|
---|
202 | #define fclose(f) tty_fclose(f)
|
---|
203 |
|
---|
204 | #endif /*!NO_TTY_FWRITE*/
|
---|