1 /* yes - output a string repeatedly until killed
2    Copyright (C) 1991-2023 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 /* David MacKenzie <djm@gnu.ai.mit.edu> */
18 
19 #include <config.h>
20 #include <stdio.h>
21 #include <sys/types.h>
22 
23 #include "system.h"
24 
25 #include "full-write.h"
26 #include "long-options.h"
27 
28 /* The official name of this program (e.g., no 'g' prefix).  */
29 #define PROGRAM_NAME "yes"
30 
31 #define AUTHORS proper_name ("David MacKenzie")
32 
33 void
usage(int status)34 usage (int status)
35 {
36   if (status != EXIT_SUCCESS)
37     emit_try_help ();
38   else
39     {
40       printf (_("\
41 Usage: %s [STRING]...\n\
42   or:  %s OPTION\n\
43 "),
44               program_name, program_name);
45 
46       fputs (_("\
47 Repeatedly output a line with all specified STRING(s), or 'y'.\n\
48 \n\
49 "), stdout);
50       fputs (HELP_OPTION_DESCRIPTION, stdout);
51       fputs (VERSION_OPTION_DESCRIPTION, stdout);
52       emit_ancillary_info (PROGRAM_NAME);
53     }
54   exit (status);
55 }
56 
57 int
main(int argc,char ** argv)58 main (int argc, char **argv)
59 {
60   initialize_main (&argc, &argv);
61   set_program_name (argv[0]);
62   setlocale (LC_ALL, "");
63   bindtextdomain (PACKAGE, LOCALEDIR);
64   textdomain (PACKAGE);
65 
66   atexit (close_stdout);
67 
68   parse_gnu_standard_options_only (argc, argv, PROGRAM_NAME, PACKAGE_NAME,
69                                    Version, true, usage, AUTHORS,
70                                    (char const *) nullptr);
71 
72   char **operands = argv + optind;
73   char **operand_lim = argv + argc;
74   if (optind == argc)
75     *operand_lim++ = bad_cast ("y");
76 
77   /* Buffer data locally once, rather than having the
78      large overhead of stdio buffering each item.  */
79   size_t bufalloc = 0;
80   bool reuse_operand_strings = true;
81   char **operandp = operands;
82   do
83     {
84       size_t operand_len = strlen (*operandp);
85       bufalloc += operand_len + 1;
86       if (operandp + 1 < operand_lim
87           && *operandp + operand_len + 1 != operandp[1])
88         reuse_operand_strings = false;
89     }
90   while (++operandp < operand_lim);
91 
92   /* Improve performance by using a buffer size greater than BUFSIZ / 2.  */
93   if (bufalloc <= BUFSIZ / 2)
94     {
95       bufalloc = BUFSIZ;
96       reuse_operand_strings = false;
97     }
98 
99   /* Fill the buffer with one copy of the output.  If possible, reuse
100      the operands strings; this wins when the buffer would be large.  */
101   char *buf = reuse_operand_strings ? *operands : xmalloc (bufalloc);
102   size_t bufused = 0;
103   operandp = operands;
104   do
105     {
106       size_t operand_len = strlen (*operandp);
107       if (! reuse_operand_strings)
108         memcpy (buf + bufused, *operandp, operand_len);
109       bufused += operand_len;
110       buf[bufused++] = ' ';
111     }
112   while (++operandp < operand_lim);
113   buf[bufused - 1] = '\n';
114 
115   /* If a larger buffer was allocated, fill it by repeating the buffer
116      contents.  */
117   size_t copysize = bufused;
118   for (size_t copies = bufalloc / copysize; --copies; )
119     {
120       memcpy (buf + bufused, buf, copysize);
121       bufused += copysize;
122     }
123 
124   /* Repeatedly output the buffer until there is a write error; then fail.  */
125   while (full_write (STDOUT_FILENO, buf, bufused) == bufused)
126     continue;
127   error (0, errno, _("standard output"));
128   main_exit (EXIT_FAILURE);
129 }
130