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