(root)/
gawk-5.2.2/
extension/
revtwoway.c
       1  /*
       2   * revtwoway.c --- Provide a two-way processor that reverses lines.
       3   *
       4   * Arnold Robbins
       5   * arnold@skeeve.com
       6   * Written 8/2012
       7   */
       8  
       9  /*
      10   * Copyright (C) 2012-2014, 2016, 2018 the Free Software Foundation, Inc.
      11   *
      12   * This file is part of GAWK, the GNU implementation of the
      13   * AWK Programming Language.
      14   *
      15   * GAWK is free software; you can redistribute it and/or modify
      16   * it under the terms of the GNU General Public License as published by
      17   * the Free Software Foundation; either version 3 of the License, or
      18   * (at your option) any later version.
      19   *
      20   * GAWK is distributed in the hope that it will be useful,
      21   * but WITHOUT ANY WARRANTY; without even the implied warranty of
      22   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      23   * GNU General Public License for more details.
      24   *
      25   * You should have received a copy of the GNU General Public License
      26   * along with this program; if not, write to the Free Software
      27   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
      28   */
      29  
      30  #ifdef HAVE_CONFIG_H
      31  #include <config.h>
      32  #endif
      33  
      34  #define _BSD_SOURCE
      35  
      36  #include <stdio.h>
      37  #include <stdlib.h>
      38  #include <string.h>
      39  #include <unistd.h>
      40  
      41  #include <sys/types.h>
      42  #include <sys/stat.h>
      43  
      44  #include "gawkapi.h"
      45  
      46  #include "gettext.h"
      47  #define _(msgid)  gettext(msgid)
      48  #define N_(msgid) msgid
      49  
      50  static const gawk_api_t *api;	/* for convenience macros to work */
      51  static awk_ext_id_t ext_id;
      52  static const char *ext_version = "revtwoway extension: version 1.0";
      53  
      54  static awk_bool_t init_revtwoway(void);
      55  static awk_bool_t (*init_func)(void) = init_revtwoway;
      56  
      57  int plugin_is_GPL_compatible;
      58  
      59  /*
      60   * Use this variable to provide a value != INVALID_HANDLE in the awk_input_buf_t
      61   * and != NULL in the awk_output_buf_t.  The idea is to have a value that
      62   * is greater than the largest allowable file descriptor.
      63   */
      64  static size_t max_fds;
      65  
      66  #ifndef HAVE_GETDTABLESIZE
      67  /* gawk_getdtablesize --- replacement version that should be good enough */
      68  
      69  static inline int
      70  gawk_getdtablesize()
      71  {
      72  	/*
      73  	 * Algorithm for the GNULIB folks:
      74  	 *
      75  	 * Set up a bitmap of 2048 elements.
      76  	 * Initialize it to zero.
      77  	 * In a loop, do
      78  	 * 	fd = open("/dev/null", O_RDONLY)
      79  	 * 	set the bit corresponding to fd in the bit map
      80  	 * until it fails.
      81  	 * Get the highest value that succeeded and increment it by one
      82  	 * --> that is how many descriptors we have.
      83  	 * Loop over the bitmap to close all the file descriptors we opened.
      84  	 *
      85  	 * Do all this upon the first call and return static values upon
      86  	 * subsequent calls.
      87  	 */
      88  
      89  	/* In the meantime, this is good enough for us: */
      90  	return 1024;
      91  }
      92  
      93  #define getdtablesize() gawk_getdtablesize()
      94  #endif
      95  
      96  /*
      97   * IMPORTANT NOTE: This is a NOT a true general purpose
      98   * extension.  It is intended to demonstrate how to set up
      99   * all the "plumbing" and to work one record at a time, ONLY.
     100   *
     101   * While it would be possible to set up buffering and manage it,
     102   * that would duplicate a large chunk of the code in gawk's
     103   * get_a_record() function, and there's no real point in doing that.
     104   */
     105  
     106  /* the data in the opaque pointer */
     107  typedef struct two_way_proc_data {
     108  	size_t size;	/* size of allocated buffer */
     109  	size_t len;	/* how much is actually in use */
     110  	char *data;
     111  	size_t in_use;	/* use count, must hit zero to be freed */
     112  } two_way_proc_data_t;
     113  
     114  /* close_two_proc_data --- release the data */
     115  
     116  static void
     117  close_two_proc_data(two_way_proc_data_t *proc_data)
     118  {
     119  	if (proc_data->in_use > 1) {
     120  		proc_data->in_use--;
     121  		return;
     122  	}
     123  
     124  	gawk_free(proc_data->data);
     125  	gawk_free(proc_data);
     126  }
     127  
     128  /*
     129   * Input side of the two-way processor (input TO gawk)
     130   */
     131  
     132  /* rev2way_get_record --- get one record at a time out of a directory */
     133  
     134  static int
     135  rev2way_get_record(char **out, awk_input_buf_t *iobuf, int *errcode,
     136  		char **rt_start, size_t *rt_len,
     137  		const awk_fieldwidth_info_t **unused)
     138  {
     139  	int len = 0;	/* for now */
     140  	two_way_proc_data_t *proc_data;
     141  
     142  	/*
     143  	 * The caller sets *errcode to 0, so we should set it only if an
     144  	 * error occurs.
     145  	 */
     146  
     147  	(void) errcode;		/* silence warnings */
     148  	if (out == NULL || iobuf == NULL || iobuf->opaque == NULL)
     149  		return EOF;
     150  
     151  	proc_data = (two_way_proc_data_t *) iobuf->opaque;
     152  	if (proc_data->len == 0)
     153  		return 0;
     154  
     155  	*out = proc_data->data;
     156  
     157  	len = proc_data->len;
     158  	proc_data->len = 0;
     159  
     160  	*rt_len = 0;	/* default: set RT to "" */
     161  	if (proc_data->data[len-1] == '\n') {
     162  		while (proc_data->data[len-1] == '\n') {
     163  			len--;
     164  			(*rt_len)++;
     165  		}
     166  		*rt_start = proc_data->data + len;
     167  	}
     168  
     169  	return len;
     170  }
     171  
     172  /* rev2way_close --- close up input side when done */
     173  
     174  static void
     175  rev2way_close(awk_input_buf_t *iobuf)
     176  {
     177  	two_way_proc_data_t *proc_data;
     178  
     179  	if (iobuf == NULL || iobuf->opaque == NULL)
     180  		return;
     181  
     182  	proc_data = (two_way_proc_data_t *) iobuf->opaque;
     183  	close_two_proc_data(proc_data);
     184  
     185  	iobuf->fd = INVALID_HANDLE;
     186  }
     187  
     188  
     189  /*
     190   * Output side of the two-way processor (output FROM gawk)
     191   */
     192  
     193  /* rev2way_fwrite --- write out characters in reverse order */
     194  
     195  static size_t
     196  rev2way_fwrite(const void *buf, size_t size, size_t count, FILE *fp, void *opaque)
     197  {
     198  	two_way_proc_data_t *proc_data;
     199  	size_t amount, char_count;
     200  	char *src, *dest;
     201  
     202  	(void) fp;	/* silence warnings */
     203  	if (opaque == NULL)
     204  		return 0;	/* error */
     205  
     206  	proc_data = (two_way_proc_data_t *) opaque;
     207  	amount = size * count;
     208  
     209  	/* do the dance */
     210  	if (amount > proc_data->size || proc_data->len > 0) {
     211  		if (proc_data->data == NULL)
     212  			emalloc(proc_data->data, char *,  amount, "rev2way_fwrite");
     213  		else
     214  			erealloc(proc_data->data, char *, proc_data->size + amount, "rev2way_fwrite");
     215  		proc_data->size += amount;
     216  	}
     217  
     218  	src = (char *) buf + amount -1;
     219  	dest = proc_data->data + proc_data->len;
     220  	for (char_count = amount; char_count > 0; char_count--) {
     221  		/* copy in backwards */
     222  		*dest++ = *src--;
     223  	}
     224  	proc_data->len += amount;
     225  
     226  	return amount;
     227  }
     228  
     229  /* rev2way_fflush --- do nothing hook for fflush */
     230  
     231  static int
     232  rev2way_fflush(FILE *fp, void *opaque)
     233  {
     234  	(void) fp;
     235  	(void) opaque;
     236  
     237  	return 0;
     238  }
     239  
     240  /* rev2way_ferror --- do nothing hook for ferror */
     241  
     242  static int
     243  rev2way_ferror(FILE *fp, void *opaque)
     244  {
     245  	(void) fp;
     246  	(void) opaque;
     247  
     248  	return 0;
     249  }
     250  
     251  /* rev2way_fclose --- close output side of two-way processor */
     252  
     253  static int
     254  rev2way_fclose(FILE *fp, void *opaque)
     255  {
     256  	two_way_proc_data_t *proc_data;
     257  
     258  	if (opaque == NULL)
     259  		return EOF;	/* error */
     260  
     261  	(void) fp;
     262  
     263  	proc_data = (two_way_proc_data_t *) opaque;
     264  	close_two_proc_data(proc_data);
     265  
     266  	return 0;
     267  }
     268  
     269  
     270  /* revtwoway_can_two_way --- return true if we want the file */
     271  
     272  static awk_bool_t
     273  revtwoway_can_take_two_way(const char *name)
     274  {
     275  	return (name != NULL && strcmp(name, "/magic/mirror") == 0);
     276  }
     277  
     278  /*
     279   * revtwoway_take_control_of --- set up two way processor
     280   * We can assume that revtwoway_can_take_two_way just returned true,
     281   * and no state has changed since then.
     282   */
     283  
     284  static awk_bool_t
     285  revtwoway_take_control_of(const char *name, awk_input_buf_t *inbuf, awk_output_buf_t *outbuf)
     286  {
     287  	two_way_proc_data_t *proc_data;
     288  
     289  	(void) name;	/* silence warnings */
     290  	if (inbuf == NULL || outbuf == NULL)
     291  		return awk_false;
     292  
     293  	emalloc(proc_data, two_way_proc_data_t *, sizeof(two_way_proc_data_t), "revtwoway_take_control_of");
     294  	proc_data->in_use = 2;
     295  	proc_data->size = 0;
     296  	proc_data->len = 0;
     297  	proc_data->data = NULL;
     298  
     299  	if (max_fds + 1 == 0)	/* wrapped. ha! */
     300  		max_fds = getdtablesize();
     301  
     302  	/* input side: */
     303  	inbuf->get_record = rev2way_get_record;
     304  	inbuf->close_func = rev2way_close;
     305  	inbuf->fd = max_fds;
     306  	inbuf->opaque = proc_data;
     307  
     308  	/* output side: */
     309  	outbuf->fp = (FILE *) max_fds++;
     310  	outbuf->opaque = proc_data;
     311  	outbuf->gawk_fwrite = rev2way_fwrite;
     312  	outbuf->gawk_fflush = rev2way_fflush;
     313  	outbuf->gawk_ferror = rev2way_ferror;
     314  	outbuf->gawk_fclose = rev2way_fclose;
     315  	outbuf->redirected = awk_true;
     316  
     317  	return awk_true;
     318  }
     319  
     320  static awk_two_way_processor_t two_way_processor = {
     321  	"revtwoway",
     322  	revtwoway_can_take_two_way,
     323  	revtwoway_take_control_of,
     324  	NULL
     325  };
     326  
     327  /* init_revtwoway --- set things ups */
     328  
     329  static awk_bool_t
     330  init_revtwoway()
     331  {
     332  	register_two_way_processor(& two_way_processor);
     333  
     334  	max_fds = getdtablesize();
     335  
     336  	return awk_true;
     337  }
     338  
     339  static awk_ext_func_t func_table[] = {
     340  	{ NULL, NULL, 0, 0, awk_false, NULL }
     341  };
     342  
     343  /* define the dl_load function using the boilerplate macro */
     344  
     345  dl_load_func(func_table, revtwoway, "")