xref: /netbsd-src/external/bsd/less/dist/lessecho.c (revision 838f5788460f0f133b15d706e644d692a9d4d6ec)
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