1 /*        $NetBSD: lessecho.c,v 1.5 2023/10/06 05:49:49 simonb Exp $  */
2 
3 /*
4  * Copyright (C) 1984-2023  Mark Nudelman
5  *
6  * You may distribute under the terms of either the GNU General Public
7  * License or the Less License, as specified in the README file.
8  *
9  * For more information, see the README file.
10  */
11 
12 
13 /*
14  * lessecho [-ox] [-cx] [-pn] [-dn] [-a] file ...
15  * Simply echos its filename arguments on standard output.
16  * But any argument containing spaces is enclosed in quotes.
17  *
18  * -ox  Specifies "x" to be the open quote character.
19  * -cx  Specifies "x" to be the close quote character.
20  * -pn  Specifies "n" to be the open quote character, as an integer.
21  * -dn  Specifies "n" to be the close quote character, as an integer.
22  * -mx  Specifies "x" to be a metachar.
23  * -nn  Specifies "n" to be a metachar, as an integer.
24  * -ex  Specifies "x" to be the escape char for metachars.
25  * -fn  Specifies "x" to be the escape char for metachars, as an integer.
26  * -a   Specifies that all arguments are to be quoted.
27  *      The default is that only arguments containing spaces are quoted.
28  */
29 
30 #include "less.h"
31 
32 static char *version = "$Revision: 1.5 $";
33 
34 static int quote_all = 0;
35 static char openquote = '"';
36 static char closequote = '"';
37 static char *meta_escape = "\\";
38 static char meta_escape_buf[2];
39 static char* metachars = NULL;
40 static int num_metachars = 0;
41 static int size_metachars = 0;
42 
pr_usage(void)43 static void pr_usage(void)
44 {
45           fprintf(stderr,
46                     "usage: lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-fn] [-a] file ...\n");
47 }
48 
pr_version(void)49 static void pr_version(void)
50 {
51           char *p;
52           char buf[10];
53           char *pbuf = buf;
54 
55           for (p = version;  *p != ' ';  p++)
56                     if (*p == '\0')
57                               return;
58           for (p++;  *p != '$' && *p != ' ' && *p != '\0';  p++)
59                     *pbuf++ = *p;
60           *pbuf = '\0';
61           printf("%s\n", buf);
62 }
63 
pr_error(char * s)64 static void pr_error(char *s)
65 {
66           fprintf(stderr, "%s\n", s);
67           exit(1);
68 }
69 
lstrtol(char * s,char ** pend,int radix)70 static long lstrtol(char *s, char **pend, int radix)
71 {
72           int v;
73           int neg = 0;
74           long n = 0;
75 
76           /* Skip leading white space. */
77           while (*s == ' ' || *s == '\t')
78                     s++;
79 
80           /* Check for a leading + or -. */
81           if (*s == '-')
82           {
83                     neg = 1;
84                     s++;
85           } else if (*s == '+')
86           {
87                     s++;
88           }
89 
90           /* Determine radix if caller does not specify. */
91           if (radix == 0)
92           {
93                     radix = 10;
94                     if (*s == '0')
95                     {
96                               switch (*++s)
97                               {
98                               case 'x':
99                                         radix = 16;
100                                         s++;
101                                         break;
102                               default:
103                                         radix = 8;
104                                         break;
105                               }
106                     }
107           }
108 
109           /* Parse the digits of the number. */
110           for (;;)
111           {
112                     if (*s >= '0' && *s <= '9')
113                               v = *s - '0';
114                     else if (*s >= 'a' && *s <= 'f')
115                               v = *s - 'a' + 10;
116                     else if (*s >= 'A' && *s <= 'F')
117                               v = *s - 'A' + 10;
118                     else
119                               break;
120                     if (v >= radix)
121                               break;
122                     n = n * radix + v;
123                     s++;
124           }
125 
126           if (pend != NULL)
127           {
128                     /* Skip trailing white space. */
129                     while (*s == ' ' || *s == '\t')
130                               s++;
131                     *pend = s;
132           }
133           if (neg)
134                     return (-n);
135           return (n);
136 }
137 
add_metachar(int ch)138 static void add_metachar(int ch)
139 {
140           if (num_metachars+1 >= size_metachars)
141           {
142                     char *p;
143                     size_metachars = (size_metachars > 0) ? size_metachars*2 : 16;
144                     p = (char *) malloc(size_metachars);
145                     if (p == NULL)
146                               pr_error("Cannot allocate memory");
147 
148                     if (metachars != NULL)
149                     {
150                               strcpy(p, metachars);
151                               free(metachars);
152                     }
153                     metachars = p;
154           }
155           metachars[num_metachars++] = ch;
156           metachars[num_metachars] = '\0';
157 }
158 
is_metachar(int ch)159 static int is_metachar(int ch)
160 {
161           return (metachars != NULL && strchr(metachars, ch) != NULL);
162 }
163 
164 #if !HAVE_STRCHR
strchr(char * s,char c)165 char * strchr(char *s, char c)
166 {
167           for ( ;  *s != '\0';  s++)
168                     if (*s == c)
169                               return (s);
170           if (c == '\0')
171                     return (s);
172           return (NULL);
173 }
174 #endif
175 
main(int argc,char * argv[])176 int main(int argc, char *argv[])
177 {
178           char *arg;
179           char *s;
180           int no_more_options;
181 
182           no_more_options = 0;
183           while (--argc > 0)
184           {
185                     arg = *++argv;
186                     if (*arg != '-' || no_more_options)
187                               break;
188                     switch (*++arg)
189                     {
190                     case 'a':
191                               quote_all = 1;
192                               break;
193                     case 'c':
194                               closequote = *++arg;
195                               break;
196                     case 'd':
197                               closequote = lstrtol(++arg, &s, 0);
198                               if (s == arg)
199                                         pr_error("Missing number after -d");
200                               break;
201                     case 'e':
202                               if (strcmp(++arg, "-") == 0)
203                                         meta_escape = "";
204                               else
205                                         meta_escape = arg;
206                               break;
207                     case 'f':
208                               meta_escape_buf[0] = lstrtol(++arg, &s, 0);
209                               meta_escape_buf[1] = '\0';
210                               meta_escape = meta_escape_buf;
211                               if (s == arg)
212                                         pr_error("Missing number after -f");
213                               break;
214                     case 'o':
215                               openquote = *++arg;
216                               break;
217                     case 'p':
218                               openquote = lstrtol(++arg, &s, 0);
219                               if (s == arg)
220                                         pr_error("Missing number after -p");
221                               break;
222                     case 'm':
223                               add_metachar(*++arg);
224                               break;
225                     case 'n':
226                               add_metachar(lstrtol(++arg, &s, 0));
227                               if (s == arg)
228                                         pr_error("Missing number after -n");
229                               break;
230                     case '?':
231                               pr_usage();
232                               return (0);
233                     case '-':
234                               if (*++arg == '\0')
235                               {
236                                         no_more_options = 1;
237                                         break;
238                               }
239                               if (strcmp(arg, "version") == 0)
240                               {
241                                         pr_version();
242                                         return (0);
243                               }
244                               if (strcmp(arg, "help") == 0)
245                               {
246                                         pr_usage();
247                                         return (0);
248                               }
249                               pr_error("Invalid option after --");
250                     default:
251                               pr_error("Invalid option letter");
252                     }
253           }
254 
255           while (argc-- > 0)
256           {
257                     int has_meta = 0;
258                     arg = *argv++;
259                     for (s = arg;  *s != '\0';  s++)
260                     {
261                               if (is_metachar(*s))
262                               {
263                                         has_meta = 1;
264                                         break;
265                               }
266                     }
267                     if (quote_all || (has_meta && strlen(meta_escape) == 0))
268                               printf("%c%s%c", openquote, arg, closequote);
269                     else
270                     {
271                               for (s = arg;  *s != '\0';  s++)
272                               {
273                                         if (is_metachar(*s))
274                                                   printf("%s", meta_escape);
275                                         printf("%c", *s);
276                               }
277                     }
278                     if (argc > 0)
279                               printf(" ");
280                     else
281                               printf("\n");
282           }
283           return (0);
284 }
285