1 /* Shuffle lines of text.
3 Copyright (C) 2006-2010 Free Software Foundation, Inc.
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 Written by Paul Eggert. */
22 #include <sys/types.h>
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "shuf"
38 #define AUTHORS proper_name ("Paul Eggert")
43 if (status
!= EXIT_SUCCESS
)
44 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
49 Usage: %s [OPTION]... [FILE]\n\
50 or: %s -e [OPTION]... [ARG]...\n\
51 or: %s -i LO-HI [OPTION]...\n\
53 program_name
, program_name
, program_name
);
55 Write a random permutation of the input lines to standard output.\n\
59 Mandatory arguments to long options are mandatory for short options too.\n\
62 -e, --echo treat each ARG as an input line\n\
63 -i, --input-range=LO-HI treat each number LO through HI as an input line\n\
64 -n, --head-count=COUNT output at most COUNT lines\n\
65 -o, --output=FILE write result to FILE instead of standard output\n\
66 --random-source=FILE get random bytes from FILE\n\
67 -z, --zero-terminated end lines with 0 byte, not newline\n\
69 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
70 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
73 With no FILE, or when FILE is -, read standard input.\n\
75 emit_ancillary_info ();
81 /* For long options that have no equivalent short option, use a
82 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
85 RANDOM_SOURCE_OPTION
= CHAR_MAX
+ 1
88 static struct option
const long_opts
[] =
90 {"echo", no_argument
, NULL
, 'e'},
91 {"input-range", required_argument
, NULL
, 'i'},
92 {"head-count", required_argument
, NULL
, 'n'},
93 {"output", required_argument
, NULL
, 'o'},
94 {"random-source", required_argument
, NULL
, RANDOM_SOURCE_OPTION
},
95 {"zero-terminated", no_argument
, NULL
, 'z'},
96 {GETOPT_HELP_OPTION_DECL
},
97 {GETOPT_VERSION_OPTION_DECL
},
102 input_numbers_option_used (size_t lo_input
, size_t hi_input
)
104 return ! (lo_input
== SIZE_MAX
&& hi_input
== 0);
108 input_from_argv (char **operand
, int n_operands
, char eolbyte
)
111 size_t size
= n_operands
;
114 for (i
= 0; i
< n_operands
; i
++)
115 size
+= strlen (operand
[i
]);
118 for (i
= 0; i
< n_operands
; i
++)
120 char *p1
= stpcpy (p
, operand
[i
]);
126 operand
[n_operands
] = p
;
129 /* Return the start of the next line after LINE. The current line
130 ends in EOLBYTE, and is guaranteed to end before LINE + N. */
133 next_line (char *line
, char eolbyte
, size_t n
)
135 char *p
= memchr (line
, eolbyte
, n
);
139 /* Read data from file IN. Input lines are delimited by EOLBYTE;
140 silently append a trailing EOLBYTE if the file ends in some other
141 byte. Store a pointer to the resulting array of lines into *PLINE.
142 Return the number of lines read. Report an error and exit on
146 read_input (FILE *in
, char eolbyte
, char ***pline
)
153 size_t next_alloc
= (1 << 13) + 1;
154 size_t bytes_to_read
;
162 if (fstat (fileno (in
), &instat
) == 0 && S_ISREG (instat
.st_mode
))
164 off_t file_size
= instat
.st_size
;
165 off_t current_offset
= ftello (in
);
166 if (0 <= current_offset
)
168 off_t remaining_size
=
169 (current_offset
< file_size
? file_size
- current_offset
: 0);
170 if (SIZE_MAX
- 2 < remaining_size
)
172 next_alloc
= remaining_size
+ 2;
178 if (alloc
<= used
+ 1)
180 if (alloc
== SIZE_MAX
)
183 next_alloc
= alloc
* 2;
184 if (next_alloc
< alloc
)
185 next_alloc
= SIZE_MAX
;
186 buf
= xrealloc (buf
, alloc
);
189 bytes_to_read
= alloc
- used
- 1;
190 nread
= fread (buf
+ used
, sizeof (char), bytes_to_read
, in
);
193 while (nread
== bytes_to_read
);
197 if (used
&& buf
[used
- 1] != eolbyte
)
198 buf
[used
++] = eolbyte
;
203 for (p
= buf
; p
< lim
; p
= next_line (p
, eolbyte
, lim
- p
))
206 *pline
= line
= xnmalloc (n_lines
+ 1, sizeof *line
);
209 for (i
= 1; i
<= n_lines
; i
++)
210 line
[i
] = p
= next_line (p
, eolbyte
, lim
- p
);
217 write_permuted_output (size_t n_lines
, char * const *line
, size_t lo_input
,
218 size_t const *permutation
, char eolbyte
)
223 for (i
= 0; i
< n_lines
; i
++)
225 char * const *p
= line
+ permutation
[i
];
226 size_t len
= p
[1] - p
[0];
227 if (fwrite (p
[0], sizeof *p
[0], len
, stdout
) != len
)
231 for (i
= 0; i
< n_lines
; i
++)
233 unsigned long int n
= lo_input
+ permutation
[i
];
234 if (printf ("%lu%c", n
, eolbyte
) < 0)
242 main (int argc
, char **argv
)
245 size_t lo_input
= SIZE_MAX
;
247 size_t head_lines
= SIZE_MAX
;
248 char const *outfile
= NULL
;
249 char *random_source
= NULL
;
251 char **input_lines
= NULL
;
258 struct randint_source
*randint_source
;
261 initialize_main (&argc
, &argv
);
262 set_program_name (argv
[0]);
263 setlocale (LC_ALL
, "");
264 bindtextdomain (PACKAGE
, LOCALEDIR
);
265 textdomain (PACKAGE
);
267 atexit (close_stdout
);
269 while ((optc
= getopt_long (argc
, argv
, "ei:n:o:z", long_opts
, NULL
)) != -1)
278 unsigned long int argval
= 0;
279 char *p
= strchr (optarg
, '-');
280 char const *hi_optarg
= optarg
;
283 if (input_numbers_option_used (lo_input
, hi_input
))
284 error (EXIT_FAILURE
, 0, _("multiple -i options specified"));
289 invalid
= ((xstrtoul (optarg
, NULL
, 10, &argval
, NULL
)
291 || SIZE_MAX
< argval
);
297 invalid
|= ((xstrtoul (hi_optarg
, NULL
, 10, &argval
, NULL
)
299 || SIZE_MAX
< argval
);
301 n_lines
= hi_input
- lo_input
+ 1;
302 invalid
|= ((lo_input
<= hi_input
) == (n_lines
== 0));
304 error (EXIT_FAILURE
, 0, _("invalid input range %s"),
311 unsigned long int argval
;
312 strtol_error e
= xstrtoul (optarg
, NULL
, 10, &argval
, NULL
);
315 head_lines
= MIN (head_lines
, argval
);
316 else if (e
!= LONGINT_OVERFLOW
)
317 error (EXIT_FAILURE
, 0, _("invalid line count %s"),
323 if (outfile
&& !STREQ (outfile
, optarg
))
324 error (EXIT_FAILURE
, 0, _("multiple output files specified"));
328 case RANDOM_SOURCE_OPTION
:
329 if (random_source
&& !STREQ (random_source
, optarg
))
330 error (EXIT_FAILURE
, 0, _("multiple random sources specified"));
331 random_source
= optarg
;
338 case_GETOPT_HELP_CHAR
;
339 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
341 usage (EXIT_FAILURE
);
344 n_operands
= argc
- optind
;
345 operand
= argv
+ optind
;
349 if (input_numbers_option_used (lo_input
, hi_input
))
350 error (EXIT_FAILURE
, 0, _("cannot combine -e and -i options"));
351 input_from_argv (operand
, n_operands
, eolbyte
);
352 n_lines
= n_operands
;
355 else if (input_numbers_option_used (lo_input
, hi_input
))
359 error (0, 0, _("extra operand %s"), quote (operand
[0]));
360 usage (EXIT_FAILURE
);
362 n_lines
= hi_input
- lo_input
+ 1;
373 if (! (STREQ (operand
[0], "-") || freopen (operand
[0], "r", stdin
)))
374 error (EXIT_FAILURE
, errno
, "%s", operand
[0]);
378 error (0, 0, _("extra operand %s"), quote (operand
[1]));
379 usage (EXIT_FAILURE
);
382 fadvise (stdin
, FADVISE_SEQUENTIAL
);
384 n_lines
= read_input (stdin
, eolbyte
, &input_lines
);
388 head_lines
= MIN (head_lines
, n_lines
);
390 randint_source
= randint_all_new (random_source
,
391 randperm_bound (head_lines
, n_lines
));
392 if (! randint_source
)
393 error (EXIT_FAILURE
, errno
, "%s", quotearg_colon (random_source
));
395 /* Close stdin now, rather than earlier, so that randint_all_new
396 doesn't have to worry about opening something other than
398 if (! (echo
|| input_numbers_option_used (lo_input
, hi_input
))
399 && (ferror (stdin
) || fclose (stdin
) != 0))
400 error (EXIT_FAILURE
, errno
, _("read error"));
402 permutation
= randperm_new (randint_source
, head_lines
, n_lines
);
404 if (outfile
&& ! freopen (outfile
, "w", stdout
))
405 error (EXIT_FAILURE
, errno
, "%s", quotearg_colon (outfile
));
406 if (write_permuted_output (head_lines
, line
, lo_input
, permutation
, eolbyte
)
408 error (EXIT_FAILURE
, errno
, _("write error"));
412 randint_all_free (randint_source
);
415 free (input_lines
[0]);