OpenCoverage

tac.c

Absolute File Name:/home/opencoverage/opencoverage/guest-scripts/coreutils/src/src/tac.c
Source codeSwitch to Preprocessed file
LineSourceCount
1/* tac - concatenate and print files in reverse-
2 Copyright (C) 1988-2018 Free Software Foundation, Inc.-
3-
4 This program is free software: you can redistribute it and/or modify-
5 it under the terms of the GNU General Public License as published by-
6 the Free Software Foundation, either version 3 of the License, or-
7 (at your option) any later version.-
8-
9 This program is distributed in the hope that it will be useful,-
10 but WITHOUT ANY WARRANTY; without even the implied warranty of-
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the-
12 GNU General Public License for more details.-
13-
14 You should have received a copy of the GNU General Public License-
15 along with this program. If not, see <https://www.gnu.org/licenses/>. */-
16-
17/* Written by Jay Lepreau (lepreau@cs.utah.edu).-
18 GNU enhancements by David MacKenzie (djm@gnu.ai.mit.edu). */-
19-
20/* Copy each FILE, or the standard input if none are given or when a-
21 FILE name of "-" is encountered, to the standard output with the-
22 order of the records reversed. The records are separated by-
23 instances of a string, or a newline if none is given. By default, the-
24 separator string is attached to the end of the record that it-
25 follows in the file.-
26-
27 Options:-
28 -b, --before The separator is attached to the beginning-
29 of the record that it precedes in the file.-
30 -r, --regex The separator is a regular expression.-
31 -s, --separator=separator Use SEPARATOR as the record separator.-
32-
33 To reverse a file byte by byte, use (in bash, ksh, or sh):-
34tac -r -s '.\|-
35' file */-
36-
37#include <config.h>-
38-
39#include <stdio.h>-
40#include <getopt.h>-
41#include <sys/types.h>-
42#include "system.h"-
43-
44#include <regex.h>-
45-
46#include "die.h"-
47#include "error.h"-
48#include "filenamecat.h"-
49#include "safe-read.h"-
50#include "stdlib--.h"-
51#include "xbinary-io.h"-
52-
53/* The official name of this program (e.g., no 'g' prefix). */-
54#define PROGRAM_NAME "tac"-
55-
56#define AUTHORS \-
57 proper_name ("Jay Lepreau"), \-
58 proper_name ("David MacKenzie")-
59-
60#if defined __MSDOS__ || defined _WIN32-
61/* Define this to non-zero on systems for which the regular mechanism-
62 (of unlinking an open file and expecting to be able to write, seek-
63 back to the beginning, then reread it) doesn't work. E.g., on Windows-
64 and DOS systems. */-
65# define DONT_UNLINK_WHILE_OPEN 1-
66#endif-
67-
68-
69#ifndef DEFAULT_TMPDIR-
70# define DEFAULT_TMPDIR "/tmp"-
71#endif-
72-
73/* The number of bytes per atomic read. */-
74#define INITIAL_READSIZE 8192-
75-
76/* The number of bytes per atomic write. */-
77#define WRITESIZE 8192-
78-
79/* The string that separates the records of the file. */-
80static char const *separator;-
81-
82/* True if we have ever read standard input. */-
83static bool have_read_stdin = false;-
84-
85/* If true, print 'separator' along with the record preceding it-
86 in the file; otherwise with the record following it. */-
87static bool separator_ends_record;-
88-
89/* 0 if 'separator' is to be matched as a regular expression;-
90 otherwise, the length of 'separator', used as a sentinel to-
91 stop the search. */-
92static size_t sentinel_length;-
93-
94/* The length of a match with 'separator'. If 'sentinel_length' is 0,-
95 'match_length' is computed every time a match succeeds;-
96 otherwise, it is simply the length of 'separator'. */-
97static size_t match_length;-
98-
99/* The input buffer. */-
100static char *G_buffer;-
101-
102/* The number of bytes to read at once into 'buffer'. */-
103static size_t read_size;-
104-
105/* The size of 'buffer'. This is read_size * 2 + sentinel_length + 2.-
106 The extra 2 bytes allow 'past_end' to have a value beyond the-
107 end of 'G_buffer' and 'match_start' to run off the front of 'G_buffer'. */-
108static size_t G_buffer_size;-
109-
110/* The compiled regular expression representing 'separator'. */-
111static struct re_pattern_buffer compiled_separator;-
112static char compiled_separator_fastmap[UCHAR_MAX + 1];-
113static struct re_registers regs;-
114-
115static struct option const longopts[] =-
116{-
117 {"before", no_argument, NULL, 'b'},-
118 {"regex", no_argument, NULL, 'r'},-
119 {"separator", required_argument, NULL, 's'},-
120 {GETOPT_HELP_OPTION_DECL},-
121 {GETOPT_VERSION_OPTION_DECL},-
122 {NULL, 0, NULL, 0}-
123};-
124-
125void-
126usage (int status)-
127{-
128 if (status != EXIT_SUCCESS)
status != 0Description
TRUEevaluated 3 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 9 times by 1 test
Evaluated by:
  • tac
3-9
129 emit_try_help ();
executed 3 times by 1 test: end of block
Executed by:
  • tac
3
130 else-
131 {-
132 printf (_("\-
133Usage: %s [OPTION]... [FILE]...\n\-
134"),-
135 program_name);-
136 fputs (_("\-
137Write each FILE to standard output, last line first.\n\-
138"), stdout);-
139-
140 emit_stdin_note ();-
141 emit_mandatory_arg_note ();-
142-
143 fputs (_("\-
144 -b, --before attach the separator before instead of after\n\-
145 -r, --regex interpret the separator as a regular expression\n\-
146 -s, --separator=STRING use STRING as the separator instead of newline\n\-
147"), stdout);-
148 fputs (HELP_OPTION_DESCRIPTION, stdout);-
149 fputs (VERSION_OPTION_DESCRIPTION, stdout);-
150 emit_ancillary_info (PROGRAM_NAME);-
151 }
executed 9 times by 1 test: end of block
Executed by:
  • tac
9
152 exit (status);
executed 12 times by 1 test: exit (status);
Executed by:
  • tac
12
153}-
154-
155/* Print the characters from START to PAST_END - 1.-
156 If START is NULL, just flush the buffer. */-
157-
158static void-
159output (const char *start, const char *past_end)-
160{-
161 static char buffer[WRITESIZE];-
162 static size_t bytes_in_buffer = 0;-
163 size_t bytes_to_add = past_end - start;-
164 size_t bytes_available = WRITESIZE - bytes_in_buffer;-
165-
166 if (start == 0)
start == 0Description
TRUEevaluated 167 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 4453 times by 1 test
Evaluated by:
  • tac
167-4453
167 {-
168 fwrite (buffer, 1, bytes_in_buffer, stdout);
never executed: break;
(__builtin_exp...r++))) == (-1)Description
TRUEnever evaluated
FALSEnever evaluated
__cnt > 0Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_expe...write_end), 0)Description
TRUEnever evaluated
FALSEnever evaluated
0
169 bytes_in_buffer = 0;-
170 return;
executed 167 times by 1 test: return;
Executed by:
  • tac
167
171 }-
172-
173 /* Write out as many full buffers as possible. */-
174 while (bytes_to_add >= bytes_available)
bytes_to_add >...ytes_availableDescription
TRUEevaluated 8 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 4453 times by 1 test
Evaluated by:
  • tac
8-4453
175 {-
176 memcpy (buffer + bytes_in_buffer, start, bytes_available);-
177 bytes_to_add -= bytes_available;-
178 start += bytes_available;-
179 fwrite (buffer, 1, WRITESIZE, stdout);
never executed: break;
(__builtin_exp...r++))) == (-1)Description
TRUEnever evaluated
FALSEnever evaluated
__cnt > 0Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_expe...write_end), 0)Description
TRUEnever evaluated
FALSEnever evaluated
0
180 bytes_in_buffer = 0;-
181 bytes_available = WRITESIZE;-
182 }
executed 8 times by 1 test: end of block
Executed by:
  • tac
8
183-
184 memcpy (buffer + bytes_in_buffer, start, bytes_to_add);-
185 bytes_in_buffer += bytes_to_add;-
186}
executed 4453 times by 1 test: end of block
Executed by:
  • tac
4453
187-
188/* Print in reverse the file open on descriptor FD for reading FILE.-
189 The file is already positioned at FILE_POS, which should be near its end.-
190 Return true if successful. */-
191-
192static bool-
193tac_seekable (int input_fd, const char *file, off_t file_pos)-
194{-
195 /* Pointer to the location in 'G_buffer' where the search for-
196 the next separator will begin. */-
197 char *match_start;-
198-
199 /* Pointer to one past the rightmost character in 'G_buffer' that-
200 has not been printed yet. */-
201 char *past_end;-
202-
203 /* Length of the record growing in 'G_buffer'. */-
204 size_t saved_record_size;-
205-
206 /* True if 'output' has not been called yet for any file.-
207 Only used when the separator is attached to the preceding record. */-
208 bool first_time = true;-
209 char first_char = *separator; /* Speed optimization, non-regexp. */-
210 char const *separator1 = separator + 1; /* Speed optimization, non-regexp. */-
211 size_t match_length1 = match_length - 1; /* Speed optimization, non-regexp. */-
212-
213 /* Arrange for the first read to lop off enough to leave the rest of the-
214 file a multiple of 'read_size'. Since 'read_size' can change, this may-
215 not always hold during the program run, but since it usually will, leave-
216 it here for i/o efficiency (page/sector boundaries and all that).-
217 Note: the efficiency gain has not been verified. */-
218 size_t remainder = file_pos % read_size;-
219 if (remainder != 0)
remainder != 0Description
TRUEevaluated 161 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 7 times by 1 test
Evaluated by:
  • tac
7-161
220 {-
221 file_pos -= remainder;-
222 if (lseek (input_fd, file_pos, SEEK_SET) < 0)
lseek (input_f...e_pos, 0 ) < 0Description
TRUEnever evaluated
FALSEevaluated 161 times by 1 test
Evaluated by:
  • tac
0-161
223 error (0, errno, _("%s: seek failed"), quotef (file));
never executed: error (0, (*__errno_location ()) , dcgettext (((void *)0), "%s: seek failed" , 5) , quotearg_n_style_colon (0, shell_escape_quoting_style, file));
0
224 }
executed 161 times by 1 test: end of block
Executed by:
  • tac
161
225-
226 /* Scan backward, looking for end of file. This caters to proc-like-
227 file systems where the file size is just an estimate. */-
228 while ((saved_record_size = safe_read (input_fd, G_buffer, read_size)) == 0
(saved_record_...ad_size)) == 0Description
TRUEevaluated 7 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 161 times by 1 test
Evaluated by:
  • tac
7-161
229 && file_pos != 0)
file_pos != 0Description
TRUEnever evaluated
FALSEevaluated 7 times by 1 test
Evaluated by:
  • tac
0-7
230 {-
231 off_t rsize = read_size;-
232 if (lseek (input_fd, -rsize, SEEK_CUR) < 0)
lseek (input_f...rsize, 1 ) < 0Description
TRUEnever evaluated
FALSEnever evaluated
0
233 error (0, errno, _("%s: seek failed"), quotef (file));
never executed: error (0, (*__errno_location ()) , dcgettext (((void *)0), "%s: seek failed" , 5) , quotearg_n_style_colon (0, shell_escape_quoting_style, file));
0
234 file_pos -= read_size;-
235 }
never executed: end of block
0
236-
237 /* Now scan forward, looking for end of file. */-
238 while (saved_record_size == read_size)
saved_record_size == read_sizeDescription
TRUEnever evaluated
FALSEevaluated 168 times by 1 test
Evaluated by:
  • tac
0-168
239 {-
240 size_t nread = safe_read (input_fd, G_buffer, read_size);-
241 if (nread == 0)
nread == 0Description
TRUEnever evaluated
FALSEnever evaluated
0
242 break;
never executed: break;
0
243 saved_record_size = nread;-
244 if (saved_record_size == SAFE_READ_ERROR)
saved_record_s... ((size_t) -1)Description
TRUEnever evaluated
FALSEnever evaluated
0
245 break;
never executed: break;
0
246 file_pos += nread;-
247 }
never executed: end of block
0
248-
249 if (saved_record_size == SAFE_READ_ERROR)
saved_record_s... ((size_t) -1)Description
TRUEnever evaluated
FALSEevaluated 168 times by 1 test
Evaluated by:
  • tac
0-168
250 {-
251 error (0, errno, _("%s: read error"), quotef (file));-
252 return false;
never executed: return 0 ;
0
253 }-
254-
255 match_start = past_end = G_buffer + saved_record_size;-
256 /* For non-regexp search, move past impossible positions for a match. */-
257 if (sentinel_length)
sentinel_lengthDescription
TRUEevaluated 152 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 16 times by 1 test
Evaluated by:
  • tac
16-152
258 match_start -= match_length1;
executed 152 times by 1 test: match_start -= match_length1;
Executed by:
  • tac
152
259-
260 while (true)-
261 {-
262 /* Search backward from 'match_start' - 1 to 'G_buffer' for a match-
263 with 'separator'; for speed, use strncmp if 'separator' contains no-
264 metacharacters.-
265 If the match succeeds, set 'match_start' to point to the start of-
266 the match and 'match_length' to the length of the match.-
267 Otherwise, make 'match_start' < 'G_buffer'. */-
268 if (sentinel_length == 0)
sentinel_length == 0Description
TRUEevaluated 85 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 4510 times by 1 test
Evaluated by:
  • tac
85-4510
269 {-
270 size_t i = match_start - G_buffer;-
271 regoff_t ri = i;-
272 regoff_t range = 1 - ri;-
273 regoff_t ret;-
274-
275 if (1 < range)
1 < rangeDescription
TRUEnever evaluated
FALSEevaluated 85 times by 1 test
Evaluated by:
  • tac
0-85
276 die (EXIT_FAILURE, 0, _("record too large"));
never executed: ((!!sizeof (struct { _Static_assert ( 1 , "verify_expr (" "1" ", " "(error (1, 0, dcgettext (((void *)0), \"record too large\", 5)), assume (false))" ")"); int _gl_dummy; })) ? ((error ( 1 , 0, dcgettext (((void *)0), "record too large" , 5) ), (( 0 ) ? (void) 0 : __builtin_unreachable ()))) : ((error ( 1 , 0, dcgettext (((void *)0), "record too large" , 5) ), (( 0 ) ? (void) 0 : __builtin_unreachable ()))));
0
277-
278 if (range == 1
range == 1Description
TRUEevaluated 6 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 79 times by 1 test
Evaluated by:
  • tac
6-79
279 || ((ret = re_search (&compiled_separator, G_buffer,
((ret = re_sea...&regs)) == -1)Description
TRUEevaluated 10 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 69 times by 1 test
Evaluated by:
  • tac
10-69
280 i, i - 1, range, &regs))
((ret = re_sea...&regs)) == -1)Description
TRUEevaluated 10 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 69 times by 1 test
Evaluated by:
  • tac
10-69
281 == -1))
((ret = re_sea...&regs)) == -1)Description
TRUEevaluated 10 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 69 times by 1 test
Evaluated by:
  • tac
10-69
282 match_start = G_buffer - 1;
executed 16 times by 1 test: match_start = G_buffer - 1;
Executed by:
  • tac
16
283 else if (ret == -2)
ret == -2Description
TRUEnever evaluated
FALSEevaluated 69 times by 1 test
Evaluated by:
  • tac
0-69
284 {-
285 die (EXIT_FAILURE, 0,-
286 _("error in regular expression search"));-
287 }
never executed: end of block
0
288 else-
289 {-
290 match_start = G_buffer + regs.start[0];-
291 match_length = regs.end[0] - regs.start[0];-
292 }
executed 69 times by 1 test: end of block
Executed by:
  • tac
69
293 }-
294 else-
295 {-
296 /* 'match_length' is constant for non-regexp boundaries. */-
297 while (*--match_start != first_char
*--match_start != first_charDescription
TRUEevaluated 66407 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 4510 times by 1 test
Evaluated by:
  • tac
4510-66407
298 || (match_length1 && !STREQ_LEN (match_start + 1, separator1,
never executed: __result = (((const unsigned char *) (const char *) ( match_start + 1 ))[3] - __s2[3]);
never executed: end of block
never executed: end of block
never executed: __result = (((const unsigned char *) (const char *) ( separator1 ))[3] - __s2[3]);
never executed: end of block
never executed: end of block
match_length1Description
TRUEnever evaluated
FALSEevaluated 4510 times by 1 test
Evaluated by:
  • tac
!( (__extensio...gth1 ))) == 0)Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_cons...atch_length1 )Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_cons...ch_start + 1 )Description
TRUEnever evaluated
FALSEnever evaluated
strlen ( match...tch_length1 ))Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_cons...( separator1 )Description
TRUEnever evaluated
FALSEnever evaluated
strlen ( separ...tch_length1 ))Description
TRUEnever evaluated
FALSEnever evaluated
__s1_len > 0Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s1_len > 1Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s1_len > 2Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s2_len > 0Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s2_len > 1Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s2_len > 2Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
0-4510
299 match_length1)))-
300 /* Do nothing. */ ;
executed 66407 times by 1 test: ;
Executed by:
  • tac
66407
301 }
executed 4510 times by 1 test: end of block
Executed by:
  • tac
4510
302-
303 /* Check whether we backed off the front of 'G_buffer' without finding-
304 a match for 'separator'. */-
305 if (match_start < G_buffer)
match_start < G_bufferDescription
TRUEevaluated 176 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 4419 times by 1 test
Evaluated by:
  • tac
176-4419
306 {-
307 if (file_pos == 0)
file_pos == 0Description
TRUEevaluated 168 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 8 times by 1 test
Evaluated by:
  • tac
8-168
308 {-
309 /* Hit the beginning of the file; print the remaining record. */-
310 output (G_buffer, past_end);-
311 return true;
executed 168 times by 1 test: return 1 ;
Executed by:
  • tac
168
312 }-
313-
314 saved_record_size = past_end - G_buffer;-
315 if (saved_record_size > read_size)
saved_record_size > read_sizeDescription
TRUEevaluated 3 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 5 times by 1 test
Evaluated by:
  • tac
3-5
316 {-
317 /* 'G_buffer_size' is about twice 'read_size', so since-
318 we want to read in another 'read_size' bytes before-
319 the data already in 'G_buffer', we need to increase-
320 'G_buffer_size'. */-
321 char *newbuffer;-
322 size_t offset = sentinel_length ? sentinel_length : 1;
sentinel_lengthDescription
TRUEevaluated 3 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
0-3
323 size_t old_G_buffer_size = G_buffer_size;-
324-
325 read_size *= 2;-
326 G_buffer_size = read_size * 2 + sentinel_length + 2;-
327 if (G_buffer_size < old_G_buffer_size)
G_buffer_size ..._G_buffer_sizeDescription
TRUEnever evaluated
FALSEevaluated 3 times by 1 test
Evaluated by:
  • tac
0-3
328 xalloc_die ();
never executed: xalloc_die ();
0
329 newbuffer = xrealloc (G_buffer - offset, G_buffer_size);-
330 newbuffer += offset;-
331 G_buffer = newbuffer;-
332 }
executed 3 times by 1 test: end of block
Executed by:
  • tac
3
333-
334 /* Back up to the start of the next bufferfull of the file. */-
335 if (file_pos >= read_size)
file_pos >= read_sizeDescription
TRUEevaluated 5 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 3 times by 1 test
Evaluated by:
  • tac
3-5
336 file_pos -= read_size;
executed 5 times by 1 test: file_pos -= read_size;
Executed by:
  • tac
5
337 else-
338 {-
339 read_size = file_pos;-
340 file_pos = 0;-
341 }
executed 3 times by 1 test: end of block
Executed by:
  • tac
3
342 if (lseek (input_fd, file_pos, SEEK_SET) < 0)
lseek (input_f...e_pos, 0 ) < 0Description
TRUEnever evaluated
FALSEevaluated 8 times by 1 test
Evaluated by:
  • tac
0-8
343 error (0, errno, _("%s: seek failed"), quotef (file));
never executed: error (0, (*__errno_location ()) , dcgettext (((void *)0), "%s: seek failed" , 5) , quotearg_n_style_colon (0, shell_escape_quoting_style, file));
0
344-
345 /* Shift the pending record data right to make room for the new.-
346 The source and destination regions probably overlap. */-
347 memmove (G_buffer + read_size, G_buffer, saved_record_size);-
348 past_end = G_buffer + read_size + saved_record_size;-
349 /* For non-regexp searches, avoid unnecessary scanning. */-
350 if (sentinel_length)
sentinel_lengthDescription
TRUEevaluated 8 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
0-8
351 match_start = G_buffer + read_size;
executed 8 times by 1 test: match_start = G_buffer + read_size;
Executed by:
  • tac
8
352 else-
353 match_start = past_end;
never executed: match_start = past_end;
0
354-
355 if (safe_read (input_fd, G_buffer, read_size) != read_size)
safe_read (inp...) != read_sizeDescription
TRUEnever evaluated
FALSEevaluated 8 times by 1 test
Evaluated by:
  • tac
0-8
356 {-
357 error (0, errno, _("%s: read error"), quotef (file));-
358 return false;
never executed: return 0 ;
0
359 }-
360 }
executed 8 times by 1 test: end of block
Executed by:
  • tac
8
361 else-
362 {-
363 /* Found a match of 'separator'. */-
364 if (separator_ends_record)
separator_ends_recordDescription
TRUEevaluated 4368 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 51 times by 1 test
Evaluated by:
  • tac
51-4368
365 {-
366 char *match_end = match_start + match_length;-
367-
368 /* If this match of 'separator' isn't at the end of the-
369 file, print the record. */-
370 if (!first_time || match_end != past_end)
!first_timeDescription
TRUEevaluated 4228 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 140 times by 1 test
Evaluated by:
  • tac
match_end != past_endDescription
TRUEevaluated 6 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 134 times by 1 test
Evaluated by:
  • tac
6-4228
371 output (match_end, past_end);
executed 4234 times by 1 test: output (match_end, past_end);
Executed by:
  • tac
4234
372 past_end = match_end;-
373 first_time = false;-
374 }
executed 4368 times by 1 test: end of block
Executed by:
  • tac
4368
375 else-
376 {-
377 output (match_start, past_end);-
378 past_end = match_start;-
379 }
executed 51 times by 1 test: end of block
Executed by:
  • tac
51
380-
381 /* For non-regex matching, we can back up. */-
382 if (sentinel_length > 0)
sentinel_length > 0Description
TRUEevaluated 4350 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 69 times by 1 test
Evaluated by:
  • tac
69-4350
383 match_start -= match_length - 1;
executed 4350 times by 1 test: match_start -= match_length - 1;
Executed by:
  • tac
4350
384 }
executed 4419 times by 1 test: end of block
Executed by:
  • tac
4419
385 }-
386}
never executed: end of block
0
387-
388#if DONT_UNLINK_WHILE_OPEN-
389-
390/* FIXME-someday: remove all of this DONT_UNLINK_WHILE_OPEN junk.-
391 Using atexit like this is wrong, since it can fail-
392 when called e.g. 32 or more times.-
393 But this isn't a big deal, since the code is used only on WOE/DOS-
394 systems, and few people invoke tac on that many nonseekable files. */-
395-
396static const char *file_to_remove;-
397static FILE *fp_to_close;-
398-
399static void-
400unlink_tempfile (void)-
401{-
402 fclose (fp_to_close);-
403 unlink (file_to_remove);-
404}-
405-
406static void-
407record_or_unlink_tempfile (char const *fn, FILE *fp)-
408{-
409 if (!file_to_remove)-
410 {-
411 file_to_remove = fn;-
412 fp_to_close = fp;-
413 atexit (unlink_tempfile);-
414 }-
415}-
416-
417#else-
418-
419static void-
420record_or_unlink_tempfile (char const *fn, FILE *fp _GL_UNUSED)-
421{-
422 unlink (fn);-
423}
executed 104 times by 1 test: end of block
Executed by:
  • tac
104
424-
425#endif-
426-
427/* A wrapper around mkstemp that gives us both an open stream pointer,-
428 FP, and the corresponding FILE_NAME. Always return the same FP/name-
429 pair, rewinding/truncating it upon each reuse. */-
430static bool-
431temp_stream (FILE **fp, char **file_name)-
432{-
433 static char *tempfile = NULL;-
434 static FILE *tmp_fp;-
435 if (tempfile == NULL)
tempfile == ((void *)0)Description
TRUEevaluated 105 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 2 times by 1 test
Evaluated by:
  • tac
2-105
436 {-
437 char const *t = getenv ("TMPDIR");-
438 char const *tempdir = t ? t : DEFAULT_TMPDIR;
tDescription
TRUEevaluated 105 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
0-105
439 tempfile = mfile_name_concat (tempdir, "tacXXXXXX", NULL);-
440 if (tempdir == NULL)
tempdir == ((void *)0)Description
TRUEnever evaluated
FALSEevaluated 105 times by 1 test
Evaluated by:
  • tac
0-105
441 {-
442 error (0, 0, _("memory exhausted"));-
443 return false;
never executed: return 0 ;
0
444 }-
445-
446 /* FIXME: there's a small window between a successful mkstemp call-
447 and the unlink that's performed by record_or_unlink_tempfile.-
448 If we're interrupted in that interval, this code fails to remove-
449 the temporary file. On systems that define DONT_UNLINK_WHILE_OPEN,-
450 the window is much larger -- it extends to the atexit-called-
451 unlink_tempfile.-
452 FIXME: clean up upon fatal signal. Don't block them, in case-
453 $TMPFILE is a remote file system. */-
454-
455 int fd = mkstemp (tempfile);-
456 if (fd < 0)
fd < 0Description
TRUEevaluated 1 time by 1 test
Evaluated by:
  • tac
FALSEevaluated 104 times by 1 test
Evaluated by:
  • tac
1-104
457 {-
458 error (0, errno, _("failed to create temporary file in %s"),-
459 quoteaf (tempdir));-
460 goto Reset;
executed 1 time by 1 test: goto Reset;
Executed by:
  • tac
1
461 }-
462-
463 tmp_fp = fdopen (fd, (O_BINARY ? "w+b" : "w+"));-
464 if (! tmp_fp)
! tmp_fpDescription
TRUEnever evaluated
FALSEevaluated 104 times by 1 test
Evaluated by:
  • tac
0-104
465 {-
466 error (0, errno, _("failed to open %s for writing"),-
467 quoteaf (tempfile));-
468 close (fd);-
469 unlink (tempfile);-
470 Reset:
code before this statement never executed: Reset:
0
471 free (tempfile);-
472 tempfile = NULL;-
473 return false;
executed 1 time by 1 test: return 0 ;
Executed by:
  • tac
1
474 }-
475-
476 record_or_unlink_tempfile (tempfile, tmp_fp);-
477 }
executed 104 times by 1 test: end of block
Executed by:
  • tac
104
478 else-
479 {-
480 clearerr (tmp_fp);-
481 if (fseeko (tmp_fp, 0, SEEK_SET) < 0
rpl_fseeko (tmp_fp, 0, 0 ) < 0Description
TRUEnever evaluated
FALSEevaluated 2 times by 1 test
Evaluated by:
  • tac
0-2
482 || ftruncate (fileno (tmp_fp), 0) < 0)
ftruncate (fil...mp_fp), 0) < 0Description
TRUEnever evaluated
FALSEevaluated 2 times by 1 test
Evaluated by:
  • tac
0-2
483 {-
484 error (0, errno, _("failed to rewind stream for %s"),-
485 quoteaf (tempfile));-
486 return false;
never executed: return 0 ;
0
487 }-
488 }
executed 2 times by 1 test: end of block
Executed by:
  • tac
2
489-
490 *fp = tmp_fp;-
491 *file_name = tempfile;-
492 return true;
executed 106 times by 1 test: return 1 ;
Executed by:
  • tac
106
493}-
494-
495/* Copy from file descriptor INPUT_FD (corresponding to the named FILE) to-
496 a temporary file, and set *G_TMP and *G_TEMPFILE to the resulting stream-
497 and file name. Return the number of bytes copied, or -1 on error. */-
498-
499static off_t-
500copy_to_temp (FILE **g_tmp, char **g_tempfile, int input_fd, char const *file)-
501{-
502 FILE *fp;-
503 char *file_name;-
504 uintmax_t bytes_copied = 0;-
505 if (!temp_stream (&fp, &file_name))
!temp_stream (&fp, &file_name)Description
TRUEevaluated 1 time by 1 test
Evaluated by:
  • tac
FALSEevaluated 106 times by 1 test
Evaluated by:
  • tac
1-106
506 return -1;
executed 1 time by 1 test: return -1;
Executed by:
  • tac
1
507-
508 while (1)-
509 {-
510 size_t bytes_read = safe_read (input_fd, G_buffer, read_size);-
511 if (bytes_read == 0)
bytes_read == 0Description
TRUEevaluated 104 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 105 times by 1 test
Evaluated by:
  • tac
104-105
512 break;
executed 104 times by 1 test: break;
Executed by:
  • tac
104
513 if (bytes_read == SAFE_READ_ERROR)
bytes_read == ((size_t) -1)Description
TRUEevaluated 2 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 103 times by 1 test
Evaluated by:
  • tac
2-103
514 {-
515 error (0, errno, _("%s: read error"), quotef (file));-
516 return -1;
executed 2 times by 1 test: return -1;
Executed by:
  • tac
2
517 }-
518-
519 if (fwrite (G_buffer, 1, bytes_read, fp) != bytes_read)
never executed: break;
(__extension__... != bytes_readDescription
TRUEnever evaluated
FALSEevaluated 103 times by 1 test
Evaluated by:
  • tac
(__builtin_exp...r++))) == (-1)Description
TRUEnever evaluated
FALSEnever evaluated
__cnt > 0Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_expe...write_end), 0)Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_constant_p ( 1 )Description
TRUEevaluated 103 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
__builtin_cons...( bytes_read )Description
TRUEnever evaluated
FALSEevaluated 103 times by 1 test
Evaluated by:
  • tac
(size_t) ( 1 )...es_read ) <= 8Description
TRUEnever evaluated
FALSEnever evaluated
(size_t) ( 1 ) != 0Description
TRUEnever evaluated
FALSEnever evaluated
__builtin_constant_p ( 1 )Description
TRUEevaluated 103 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
(size_t) ( 1 ) == 0Description
TRUEnever evaluated
FALSEevaluated 103 times by 1 test
Evaluated by:
  • tac
__builtin_cons...( bytes_read )Description
TRUEnever evaluated
FALSEevaluated 103 times by 1 test
Evaluated by:
  • tac
(size_t) ( bytes_read ) == 0Description
TRUEnever evaluated
FALSEnever evaluated
0-103
520 {-
521 error (0, errno, _("%s: write error"), quotef (file_name));-
522 return -1;
never executed: return -1;
0
523 }-
524-
525 /* Implicitly <= OFF_T_MAX due to preceding fwrite(),-
526 but unsigned type used to avoid compiler warnings-
527 not aware of this fact. */-
528 bytes_copied += bytes_read;-
529 }
executed 103 times by 1 test: end of block
Executed by:
  • tac
103
530-
531 if (fflush (fp) != 0)
fflush_unlocked (fp) != 0Description
TRUEnever evaluated
FALSEevaluated 104 times by 1 test
Evaluated by:
  • tac
0-104
532 {-
533 error (0, errno, _("%s: write error"), quotef (file_name));-
534 return -1;
never executed: return -1;
0
535 }-
536-
537 *g_tmp = fp;-
538 *g_tempfile = file_name;-
539 return bytes_copied;
executed 104 times by 1 test: return bytes_copied;
Executed by:
  • tac
104
540}-
541-
542/* Copy INPUT_FD to a temporary, then tac that file.-
543 Return true if successful. */-
544-
545static bool-
546tac_nonseekable (int input_fd, const char *file)-
547{-
548 FILE *tmp_stream;-
549 char *tmp_file;-
550 off_t bytes_copied = copy_to_temp (&tmp_stream, &tmp_file, input_fd, file);-
551 if (bytes_copied < 0)
bytes_copied < 0Description
TRUEevaluated 3 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 104 times by 1 test
Evaluated by:
  • tac
3-104
552 return false;
executed 3 times by 1 test: return 0 ;
Executed by:
  • tac
3
553-
554 bool ok = tac_seekable (fileno (tmp_stream), tmp_file, bytes_copied);-
555 return ok;
executed 104 times by 1 test: return ok;
Executed by:
  • tac
104
556}-
557-
558/* Print FILE in reverse, copying it to a temporary-
559 file first if it is not seekable.-
560 Return true if successful. */-
561-
562static bool-
563tac_file (const char *filename)-
564{-
565 bool ok;-
566 off_t file_size;-
567 int fd;-
568 bool is_stdin = STREQ (filename, "-");
never executed: __result = (((const unsigned char *) (const char *) ( filename ))[3] - __s2[3]);
never executed: end of block
never executed: end of block
never executed: __result = (((const unsigned char *) (const char *) ( "-" ))[3] - __s2[3]);
never executed: end of block
executed 133 times by 1 test: end of block
Executed by:
  • tac
__s1_len > 0Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s1_len > 1Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s1_len > 2Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s2_len > 0Description
TRUEevaluated 171 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
__result == 0Description
TRUEevaluated 133 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 38 times by 1 test
Evaluated by:
  • tac
__s2_len > 1Description
TRUEnever evaluated
FALSEevaluated 133 times by 1 test
Evaluated by:
  • tac
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
__s2_len > 2Description
TRUEnever evaluated
FALSEnever evaluated
__result == 0Description
TRUEnever evaluated
FALSEnever evaluated
0-171
569-
570 if (is_stdin)
is_stdinDescription
TRUEevaluated 133 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 38 times by 1 test
Evaluated by:
  • tac
38-133
571 {-
572 have_read_stdin = true;-
573 fd = STDIN_FILENO;-
574 filename = _("standard input");-
575 xset_binary_mode (STDIN_FILENO, O_BINARY);-
576 }
executed 133 times by 1 test: end of block
Executed by:
  • tac
133
577 else-
578 {-
579 fd = open (filename, O_RDONLY | O_BINARY);-
580 if (fd < 0)
fd < 0Description
TRUEnever evaluated
FALSEevaluated 38 times by 1 test
Evaluated by:
  • tac
0-38
581 {-
582 error (0, errno, _("failed to open %s for reading"),-
583 quoteaf (filename));-
584 return false;
never executed: return 0 ;
0
585 }-
586 }
executed 38 times by 1 test: end of block
Executed by:
  • tac
38
587-
588 file_size = lseek (fd, 0, SEEK_END);-
589-
590 ok = (file_size < 0 || isatty (fd)
file_size < 0Description
TRUEevaluated 107 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 64 times by 1 test
Evaluated by:
  • tac
isatty (fd)Description
TRUEnever evaluated
FALSEevaluated 64 times by 1 test
Evaluated by:
  • tac
0-107
591 ? tac_nonseekable (fd, filename)-
592 : tac_seekable (fd, filename, file_size));-
593-
594 if (!is_stdin && close (fd) != 0)
!is_stdinDescription
TRUEevaluated 38 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 133 times by 1 test
Evaluated by:
  • tac
close (fd) != 0Description
TRUEnever evaluated
FALSEevaluated 38 times by 1 test
Evaluated by:
  • tac
0-133
595 {-
596 error (0, errno, _("%s: read error"), quotef (filename));-
597 ok = false;-
598 }
never executed: end of block
0
599 return ok;
executed 171 times by 1 test: return ok;
Executed by:
  • tac
171
600}-
601-
602int-
603main (int argc, char **argv)-
604{-
605 const char *error_message; /* Return value from re_compile_pattern. */-
606 int optc;-
607 bool ok;-
608 size_t half_buffer_size;-
609-
610 /* Initializer for file_list if no file-arguments-
611 were specified on the command line. */-
612 static char const *const default_file_list[] = {"-", NULL};-
613 char const *const *file;-
614-
615 initialize_main (&argc, &argv);-
616 set_program_name (argv[0]);-
617 setlocale (LC_ALL, "");-
618 bindtextdomain (PACKAGE, LOCALEDIR);-
619 textdomain (PACKAGE);-
620-
621 atexit (close_stdout);-
622-
623 separator = "\n";-
624 sentinel_length = 1;-
625 separator_ends_record = true;-
626-
627 while ((optc = getopt_long (argc, argv, "brs:", longopts, NULL)) != -1)
(optc = getopt... *)0) )) != -1Description
TRUEevaluated 86 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 167 times by 1 test
Evaluated by:
  • tac
86-167
628 {-
629 switch (optc)-
630 {-
631 case 'b':
executed 14 times by 1 test: case 'b':
Executed by:
  • tac
14
632 separator_ends_record = false;-
633 break;
executed 14 times by 1 test: break;
Executed by:
  • tac
14
634 case 'r':
executed 16 times by 1 test: case 'r':
Executed by:
  • tac
16
635 sentinel_length = 0;-
636 break;
executed 16 times by 1 test: break;
Executed by:
  • tac
16
637 case 's':
executed 38 times by 1 test: case 's':
Executed by:
  • tac
38
638 separator = optarg;-
639 break;
executed 38 times by 1 test: break;
Executed by:
  • tac
38
640 case_GETOPT_HELP_CHAR;
never executed: break;
executed 9 times by 1 test: case GETOPT_HELP_CHAR:
Executed by:
  • tac
0-9
641 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
executed 6 times by 1 test: exit ( 0 );
Executed by:
  • tac
never executed: break;
executed 6 times by 1 test: case GETOPT_VERSION_CHAR:
Executed by:
  • tac
0-6
642 default:
executed 3 times by 1 test: default:
Executed by:
  • tac
3
643 usage (EXIT_FAILURE);-
644 }
never executed: end of block
0
645 }-
646-
647 if (sentinel_length == 0)
sentinel_length == 0Description
TRUEevaluated 14 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 153 times by 1 test
Evaluated by:
  • tac
14-153
648 {-
649 if (*separator == 0)
*separator == 0Description
TRUEnever evaluated
FALSEevaluated 14 times by 1 test
Evaluated by:
  • tac
0-14
650 die (EXIT_FAILURE, 0, _("separator cannot be empty"));
never executed: ((!!sizeof (struct { _Static_assert ( 1 , "verify_expr (" "1" ", " "(error (1, 0, dcgettext (((void *)0), \"separator cannot be empty\", 5)), assume (false))" ")"); int _gl_dummy; })) ? ((error ( 1 , 0, dcgettext (((void *)0), "separator cannot be empty" , 5) ), (( 0 ) ? (void) 0 : __builtin_unreachable ()))) : ((error ( 1 , 0, dcgettext (((void *)0), "separator cannot be empty" , 5) ), (( 0 ) ? (void) 0 : __builtin_unreachable ()))));
0
651-
652 compiled_separator.buffer = NULL;-
653 compiled_separator.allocated = 0;-
654 compiled_separator.fastmap = compiled_separator_fastmap;-
655 compiled_separator.translate = NULL;-
656 error_message = re_compile_pattern (separator, strlen (separator),-
657 &compiled_separator);-
658 if (error_message)
error_messageDescription
TRUEnever evaluated
FALSEevaluated 14 times by 1 test
Evaluated by:
  • tac
0-14
659 die (EXIT_FAILURE, 0, "%s", (error_message));
never executed: ((!!sizeof (struct { _Static_assert ( 1 , "verify_expr (" "1" ", " "(error (1, 0, \"%s\", (error_message)), assume (false))" ")"); int _gl_dummy; })) ? ((error ( 1 , 0, "%s", (error_message)), (( 0 ) ? (void) 0 : __builtin_unreachable ()))) : ((error ( 1 , 0, "%s", (error_message)), (( 0 ) ? (void) 0 : __builtin_unreachable ()))));
0
660 }
executed 14 times by 1 test: end of block
Executed by:
  • tac
14
661 else-
662 match_length = sentinel_length = *separator ? strlen (separator) : 1;
executed 153 times by 1 test: match_length = sentinel_length = *separator ? strlen (separator) : 1;
Executed by:
  • tac
*separatorDescription
TRUEevaluated 135 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 18 times by 1 test
Evaluated by:
  • tac
18-153
663-
664 read_size = INITIAL_READSIZE;-
665 while (sentinel_length >= read_size / 2)
sentinel_lengt... read_size / 2Description
TRUEnever evaluated
FALSEevaluated 167 times by 1 test
Evaluated by:
  • tac
0-167
666 {-
667 if (SIZE_MAX / 2 < read_size)
(1844674407370... 2 < read_sizeDescription
TRUEnever evaluated
FALSEnever evaluated
0
668 xalloc_die ();
never executed: xalloc_die ();
0
669 read_size *= 2;-
670 }
never executed: end of block
0
671 half_buffer_size = read_size + sentinel_length + 1;-
672 G_buffer_size = 2 * half_buffer_size;-
673 if (! (read_size < half_buffer_size && half_buffer_size < G_buffer_size))
read_size < half_buffer_sizeDescription
TRUEevaluated 167 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
half_buffer_si... G_buffer_sizeDescription
TRUEevaluated 167 times by 1 test
Evaluated by:
  • tac
FALSEnever evaluated
0-167
674 xalloc_die ();
never executed: xalloc_die ();
0
675 G_buffer = xmalloc (G_buffer_size);-
676 if (sentinel_length)
sentinel_lengthDescription
TRUEevaluated 153 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 14 times by 1 test
Evaluated by:
  • tac
14-153
677 {-
678 memcpy (G_buffer, separator, sentinel_length + 1);-
679 G_buffer += sentinel_length;-
680 }
executed 153 times by 1 test: end of block
Executed by:
  • tac
153
681 else-
682 {-
683 ++G_buffer;-
684 }
executed 14 times by 1 test: end of block
Executed by:
  • tac
14
685-
686 file = (optind < argc
optind < argcDescription
TRUEevaluated 38 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 129 times by 1 test
Evaluated by:
  • tac
38-129
687 ? (char const *const *) &argv[optind]-
688 : default_file_list);-
689-
690 xset_binary_mode (STDOUT_FILENO, O_BINARY);-
691-
692 {-
693 ok = true;-
694 for (size_t i = 0; file[i]; ++i)
file[i]Description
TRUEevaluated 171 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 167 times by 1 test
Evaluated by:
  • tac
167-171
695 ok &= tac_file (file[i]);
executed 171 times by 1 test: ok &= tac_file (file[i]);
Executed by:
  • tac
171
696 }-
697-
698 /* Flush the output buffer. */-
699 output ((char *) NULL, (char *) NULL);-
700-
701 if (have_read_stdin && close (STDIN_FILENO) < 0)
have_read_stdinDescription
TRUEevaluated 131 times by 1 test
Evaluated by:
  • tac
FALSEevaluated 36 times by 1 test
Evaluated by:
  • tac
close ( 0 ) < 0Description
TRUEevaluated 1 time by 1 test
Evaluated by:
  • tac
FALSEevaluated 130 times by 1 test
Evaluated by:
  • tac
1-131
702 {-
703 error (0, errno, "-");-
704 ok = false;-
705 }
executed 1 time by 1 test: end of block
Executed by:
  • tac
1
706-
707#ifdef lint-
708 size_t offset = sentinel_length ? sentinel_length : 1;-
709 free (G_buffer - offset);-
710#endif-
711-
712 return ok ? EXIT_SUCCESS : EXIT_FAILURE;
executed 167 times by 1 test: return ok ? 0 : 1 ;
Executed by:
  • tac
167
713}-
Source codeSwitch to Preprocessed file

Generated by Squish Coco 4.1.2