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