diff options
author | Arnold D. Robbins <arnold@skeeve.com> | 2010-07-16 11:58:26 +0300 |
---|---|---|
committer | Arnold D. Robbins <arnold@skeeve.com> | 2010-07-16 11:58:26 +0300 |
commit | 765c7494b3dac62207e6cd57fb839997e237f292 (patch) | |
tree | f7da12ffdb85d9f82671cb3122775b2ce73f7ad9 /vms/vms_fwrite.c | |
parent | cce5115e21db1702e0617afdca36633e7e2c9eae (diff) | |
download | egawk-765c7494b3dac62207e6cd57fb839997e237f292.tar.gz egawk-765c7494b3dac62207e6cd57fb839997e237f292.tar.bz2 egawk-765c7494b3dac62207e6cd57fb839997e237f292.zip |
Moving to 2.13.2.
Diffstat (limited to 'vms/vms_fwrite.c')
-rw-r--r-- | vms/vms_fwrite.c | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/vms/vms_fwrite.c b/vms/vms_fwrite.c new file mode 100644 index 00000000..c0282c14 --- /dev/null +++ b/vms/vms_fwrite.c @@ -0,0 +1,209 @@ +/* + * vms_fwrite.c - augmentation for the fwrite() function. + */ + +/* + * Copyright (C) 1991 the Free Software Foundation, Inc. + * + * This file is part of GAWK, the GNU implementation of the + * AWK Progamming Language. + * + * GAWK is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GAWK is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GAWK; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "awk.h" /* really "../awk.h" */ + +#ifndef NO_TTY_FWRITE +#include "vms.h" +#include <stdio.h> +#include <errno.h> + +#ifdef VAXC_BUILTINS +#pragma builtins /* VAXC V3.0 & up */ +# define find_c(s,n,c) ((n) - _LOCC((c),(n),(s))) +#else /*VAXC_BUILTINS*/ +static int find_c( const char *s, int n, char c ) { + register const char *t = (const char *)memchr(s, c, n); + return (t == 0 ? n : t - s); /* 0..n-1, or n if not found */ +} +#endif /*VAXC_BUILTINS*/ +#define is_stdout(file_no) ((file_no) == 1) /* fileno(stdout) */ +#define is_stderr(file_no) ((file_no) == 2) /* fileno(stderr) */ + +#define PREFIX_CR (0x8D << 16) /* leading carriage return */ +#define POSTFIX_CR (0x8D << 24) /* trailing carriage return (=> lf/cr) */ + +static short channel[_NFILE] = {0}; +static FILE *prev_file = 0; +static int prev_file_num; + + /* + * VAXCRTL's fwrite() seems to flush after every character when + * writing to a terminal. This routine is a limited functionality + * substitute that is *much* faster. However, calls to fwrite() + * should not be mixed with other stdio calls to the same file + * unless fflush() is always called first. Also, this routine + * will not detect that a freopen() call has finished with the + * original terminal; tty_fclose() should be used to close a file. + */ +#ifdef fwrite +# undef fwrite +#endif +/* tty_fwrite() - performance hack for fwrite() to a terminal */ +size_t +tty_fwrite( const void *buf, size_t size, size_t number, FILE *file ) +{ + static long evfn = -1; + short chan; + int file_num, result; + + if (!file || !*file) + return 0 * (errno = EBADF); /* kludge alert! */ + else if (file == prev_file) + file_num = prev_file_num; + else /* note: VAXCRTL's fileno() is a function, not just a macro */ + prev_file_num = file_num = fileno(file), prev_file = file; + + chan = file_num < _NFILE ? channel[file_num] : -1; + if (chan == 0) { /* if not initialized, need to assign a channel */ + if (isatty(file_num) > 0) { /* isatty: 1=yes, 0=no, -1=problem */ + Dsc device; + char devnam[255+1]; + fgetname(file, devnam); /* get 'file's name */ + device.len = strlen(device.adr = devnam); /* create descriptor */ + if (vmswork(SYS$ASSIGN(&device, &chan, 0, (Dsc *)0))) { + /* get an event flag; use #0 if problem */ + if (evfn == -1 && vmsfail(LIB$GET_EF(&evfn))) evfn = 0; + } else chan = 0; /* $ASSIGN failed */ + } + /* store channel for later use; -1 => don't repeat failed init attempt */ + channel[file_num] = (chan > 0 ? chan : -1); + } + if (chan > 0) { /* chan > 0 iff 'file' is a terminal */ + struct _iosbw { u_short status, count; u_long rt_kludge; } iosb; + register u_long sts = 1; + register char *pt = (char *)buf; + register int offset, pos, count = size * number; + u_long cc_fmt, io_func = IO$_WRITEVBLK; + int extra = 0; + result = 0; + if (is_stderr(file_num)) /* if it's SYS$ERROR (stderr)... */ + io_func |= IO$M_CANCTRLO; /* cancel ^O (resume tty output) */ + while (count > 0) { + /* special handling for line-feeds to make them be 'newlines' */ + offset = 0; + if (*pt == '\n') { /* got at least one leading line-feed */ + cc_fmt = PREFIX_CR, extra++; /* precede 1st LF with a CR */ + do offset++; + while (offset < count && *(pt + offset) == '\n'); + } else + cc_fmt = 0; + /* look for another line-feed; if found, break line there */ + pos = offset + find_c(pt + offset, count - offset, '\n'); + if (pos >= BUFSIZ) pos = BUFSIZ - 1; /* throttle quota usage */ + else if (pos < count) pos++, cc_fmt |= POSTFIX_CR, extra++; + /* wait for previous write, if any, to complete */ + if (pt > (char *)buf) { + sts = SYS$SYNCH(evfn, &iosb); + if (vmswork(sts)) sts = iosb.status, result += iosb.count; + if (vmsfail(sts)) break; + } + /* queue an asynchronous write */ + sts = SYS$QIO(evfn, chan, io_func, &iosb, (u_long (*)())0, 0, + pt, pos, 0, cc_fmt, 0, 0); + if (vmsfail(sts)) break; /*(should never happen)*/ + pt += pos, count -= pos; + } + /* wait for last write to complete */ + if (pt > (char *)buf && vmswork(sts)) { + sts = SYS$SYNCH(evfn, &iosb); + if (vmswork(sts)) sts = iosb.status, result += iosb.count; + } + if (vmsfail(sts)) errno = EVMSERR, vaxc$errno = sts; + else if (iosb.rt_kludge == 0) result = number + extra; + result -= extra; /* subtract the additional carriage-returns */ + } else { /* use stdio */ + /* Note: we assume that we're writing text, not binary data. + For stream format files, 'size' and 'number' are effectively + interchangable, and fwrite works fine. However, for record + format files, 'size' governs the maximum record length, so + fwrite(string, size(char), strlen(string), file) + will produce a sequence of 1-byte records, which is hardly + what we want in this (assumed) situation. Line-feeds ('\n') + are converted into newlines (ie, record separators) by the + run-time library, but strings that don't end with a newline + still become separate records. The simplest work around + is just to use fputs() instead of fwrite(); unfortunately, + we have to provide special treatment for NULs ('\0's). + At present, only stdout might be in record format (via + >$'filename' redirection on the command line). + */ + if (size > 1) { /* not used by GAWK */ + result = fwrite((void *)buf, size, number, file); + } else if (*((char *)buf + number - 1) == '\n' || !is_stdout(file_num)) { + result = fwrite((void *)buf, number, size, file); + result = result * number / size; /*(same as 'result = number')*/ + } else { +#ifdef NO_ALLOCA +# define alloca(n) ((n) <= abuf_siz ? abuf : \ + (abuf_siz > 0 ? (void *)free(abuf) : (void *)0), \ + (abuf = malloc(abuf_siz = (n)+20))) + static void *abuf = 0; + static size_t abuf_siz = 0; +#endif /*NO_ALLOCA*/ + register char *pt = (char *)buf; + register int pos, count = number; + if (pt[count] != '\0') { /*(out of bounds, but relatively safe)*/ + pt = (char *)alloca(count + 1); + memcpy(pt, buf, count), pt[count] = '\0'; + /* if exiting this block undoes the alloca(), we're hosed :-( */ + } + result = 0; + while (count > 0) { + pos = find_c(pt, count, '\0'); + if (fputs(pt, file) < 0) break; + if (pos < count) { + if (fputc('\0', file) < 0) break; + pos++; /* 0..n-1 -> 1..n */ + } + result += pos, pt += pos, count -= pos; + } + } + } + return result; +} +#define fwrite(b,s,n,f) tty_fwrite((b),(s),(n),(f)) + +#ifdef fclose +# undef fclose +#endif +/* tty_fclose() - keep tty_fwrite() up to date when closing a file */ +int +tty_fclose( FILE *file ) +{ + if (file && *file) { /* note: VAXCRTL stdio has extra level of indirection */ + int file_num = fileno(file); + short chan = file_num < _NFILE ? channel[file_num] : -1; + if (chan > 0) + (void)SYS$DASSGN(chan); /* deassign the channel (ie, close) */ + if (file_num < _NFILE) + channel[file_num] = 0; /* clear stale info */ + } + prev_file = 0; /* force tty_fwrite() to reset */ + return fclose(file); +} +#define fclose(f) tty_fclose(f) + +#endif /*!NO_TTY_FWRITE*/ |