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, "")