xref: /freebsd/contrib/less/lessecho.c (revision c77c488926555ca344ae3a417544cf7a720e1de1)
1a5f0fb15SPaul Saab /*
2*c77c4889SXin LI  * Copyright (C) 1984-2024  Mark Nudelman
3a5f0fb15SPaul Saab  *
4a5f0fb15SPaul Saab  * You may distribute under the terms of either the GNU General Public
5a5f0fb15SPaul Saab  * License or the Less License, as specified in the README file.
6a5f0fb15SPaul Saab  *
796e55cc7SXin LI  * For more information, see the README file.
8a5f0fb15SPaul Saab  */
9a5f0fb15SPaul Saab 
10a5f0fb15SPaul Saab 
11a5f0fb15SPaul Saab /*
12a5f0fb15SPaul Saab  * lessecho [-ox] [-cx] [-pn] [-dn] [-a] file ...
13a5f0fb15SPaul Saab  * Simply echos its filename arguments on standard output.
14a5f0fb15SPaul Saab  * But any argument containing spaces is enclosed in quotes.
15a5f0fb15SPaul Saab  *
16a5f0fb15SPaul Saab  * -ox  Specifies "x" to be the open quote character.
17a5f0fb15SPaul Saab  * -cx  Specifies "x" to be the close quote character.
18a5f0fb15SPaul Saab  * -pn  Specifies "n" to be the open quote character, as an integer.
19a5f0fb15SPaul Saab  * -dn  Specifies "n" to be the close quote character, as an integer.
20000ba3e8STim J. Robbins  * -mx  Specifies "x" to be a metachar.
21000ba3e8STim J. Robbins  * -nn  Specifies "n" to be a metachar, as an integer.
22000ba3e8STim J. Robbins  * -ex  Specifies "x" to be the escape char for metachars.
23000ba3e8STim J. Robbins  * -fn  Specifies "x" to be the escape char for metachars, as an integer.
24a5f0fb15SPaul Saab  * -a   Specifies that all arguments are to be quoted.
25a5f0fb15SPaul Saab  *      The default is that only arguments containing spaces are quoted.
26a5f0fb15SPaul Saab  */
27a5f0fb15SPaul Saab 
28a5f0fb15SPaul Saab #include "less.h"
29a5f0fb15SPaul Saab 
3096e55cc7SXin LI static char *version = "$Revision: 1.15 $";
31a5f0fb15SPaul Saab 
32a5f0fb15SPaul Saab static int quote_all = 0;
33a5f0fb15SPaul Saab static char openquote = '"';
34a5f0fb15SPaul Saab static char closequote = '"';
35000ba3e8STim J. Robbins static char *meta_escape = "\\";
36000ba3e8STim J. Robbins static char meta_escape_buf[2];
3795270f73SXin LI static char* metachars = NULL;
38000ba3e8STim J. Robbins static int num_metachars = 0;
3995270f73SXin LI static int size_metachars = 0;
40a5f0fb15SPaul Saab 
41d713e089SXin LI static void pr_usage(void)
42a5f0fb15SPaul Saab {
43a5f0fb15SPaul Saab 	fprintf(stderr,
44000ba3e8STim J. Robbins 		"usage: lessecho [-ox] [-cx] [-pn] [-dn] [-mx] [-nn] [-ex] [-fn] [-a] file ...\n");
45a5f0fb15SPaul Saab }
46a5f0fb15SPaul Saab 
47d713e089SXin LI static void pr_version(void)
48a5f0fb15SPaul Saab {
49a5f0fb15SPaul Saab 	char *p;
50a5f0fb15SPaul Saab 	char buf[10];
51a5f0fb15SPaul Saab 	char *pbuf = buf;
52a5f0fb15SPaul Saab 
53a5f0fb15SPaul Saab 	for (p = version;  *p != ' ';  p++)
54a5f0fb15SPaul Saab 		if (*p == '\0')
55a5f0fb15SPaul Saab 			return;
56a5f0fb15SPaul Saab 	for (p++;  *p != '$' && *p != ' ' && *p != '\0';  p++)
57a5f0fb15SPaul Saab 		*pbuf++ = *p;
58a5f0fb15SPaul Saab 	*pbuf = '\0';
59a5f0fb15SPaul Saab 	printf("%s\n", buf);
60a5f0fb15SPaul Saab }
61a5f0fb15SPaul Saab 
62d713e089SXin LI static void pr_error(char *s)
63a5f0fb15SPaul Saab {
64a5f0fb15SPaul Saab 	fprintf(stderr, "%s\n", s);
65a5f0fb15SPaul Saab 	exit(1);
66a5f0fb15SPaul Saab }
67a5f0fb15SPaul Saab 
68d713e089SXin LI static long lstrtol(char *s, char **pend, int radix)
69a5f0fb15SPaul Saab {
70a5f0fb15SPaul Saab 	int v;
71a5f0fb15SPaul Saab 	int neg = 0;
72a5f0fb15SPaul Saab 	long n = 0;
73a5f0fb15SPaul Saab 
74a5f0fb15SPaul Saab 	/* Skip leading white space. */
75a5f0fb15SPaul Saab 	while (*s == ' ' || *s == '\t')
76a5f0fb15SPaul Saab 		s++;
77a5f0fb15SPaul Saab 
78a5f0fb15SPaul Saab 	/* Check for a leading + or -. */
79a5f0fb15SPaul Saab 	if (*s == '-')
80a5f0fb15SPaul Saab 	{
81a5f0fb15SPaul Saab 		neg = 1;
82a5f0fb15SPaul Saab 		s++;
83a5f0fb15SPaul Saab 	} else if (*s == '+')
84a5f0fb15SPaul Saab 	{
85a5f0fb15SPaul Saab 		s++;
86a5f0fb15SPaul Saab 	}
87a5f0fb15SPaul Saab 
88a5f0fb15SPaul Saab 	/* Determine radix if caller does not specify. */
89a5f0fb15SPaul Saab 	if (radix == 0)
90a5f0fb15SPaul Saab 	{
91a5f0fb15SPaul Saab 		radix = 10;
92a5f0fb15SPaul Saab 		if (*s == '0')
93a5f0fb15SPaul Saab 		{
94a5f0fb15SPaul Saab 			switch (*++s)
95a5f0fb15SPaul Saab 			{
96a5f0fb15SPaul Saab 			case 'x':
97a5f0fb15SPaul Saab 				radix = 16;
98a5f0fb15SPaul Saab 				s++;
99a5f0fb15SPaul Saab 				break;
100a5f0fb15SPaul Saab 			default:
101a5f0fb15SPaul Saab 				radix = 8;
102a5f0fb15SPaul Saab 				break;
103a5f0fb15SPaul Saab 			}
104a5f0fb15SPaul Saab 		}
105a5f0fb15SPaul Saab 	}
106a5f0fb15SPaul Saab 
107a5f0fb15SPaul Saab 	/* Parse the digits of the number. */
108a5f0fb15SPaul Saab 	for (;;)
109a5f0fb15SPaul Saab 	{
110a5f0fb15SPaul Saab 		if (*s >= '0' && *s <= '9')
111a5f0fb15SPaul Saab 			v = *s - '0';
112a5f0fb15SPaul Saab 		else if (*s >= 'a' && *s <= 'f')
113a5f0fb15SPaul Saab 			v = *s - 'a' + 10;
114a5f0fb15SPaul Saab 		else if (*s >= 'A' && *s <= 'F')
115a5f0fb15SPaul Saab 			v = *s - 'A' + 10;
116a5f0fb15SPaul Saab 		else
117a5f0fb15SPaul Saab 			break;
118a5f0fb15SPaul Saab 		if (v >= radix)
119a5f0fb15SPaul Saab 			break;
120a5f0fb15SPaul Saab 		n = n * radix + v;
121a5f0fb15SPaul Saab 		s++;
122a5f0fb15SPaul Saab 	}
123a5f0fb15SPaul Saab 
124a5f0fb15SPaul Saab 	if (pend != NULL)
125a5f0fb15SPaul Saab 	{
126a5f0fb15SPaul Saab 		/* Skip trailing white space. */
127a5f0fb15SPaul Saab 		while (*s == ' ' || *s == '\t')
128a5f0fb15SPaul Saab 			s++;
129a5f0fb15SPaul Saab 		*pend = s;
130a5f0fb15SPaul Saab 	}
131a5f0fb15SPaul Saab 	if (neg)
132a5f0fb15SPaul Saab 		return (-n);
133a5f0fb15SPaul Saab 	return (n);
134a5f0fb15SPaul Saab }
135a5f0fb15SPaul Saab 
136*c77c4889SXin LI static void add_metachar(char ch)
13795270f73SXin LI {
13895270f73SXin LI 	if (num_metachars+1 >= size_metachars)
13995270f73SXin LI 	{
14095270f73SXin LI 		char *p;
14195270f73SXin LI 		size_metachars = (size_metachars > 0) ? size_metachars*2 : 16;
142*c77c4889SXin LI 		p = (char *) malloc((size_t) size_metachars);
14395270f73SXin LI 		if (p == NULL)
14495270f73SXin LI 			pr_error("Cannot allocate memory");
14595270f73SXin LI 
14695270f73SXin LI 		if (metachars != NULL)
14795270f73SXin LI 		{
14895270f73SXin LI 			strcpy(p, metachars);
14995270f73SXin LI 			free(metachars);
15095270f73SXin LI 		}
15195270f73SXin LI 		metachars = p;
15295270f73SXin LI 	}
15395270f73SXin LI 	metachars[num_metachars++] = ch;
15495270f73SXin LI 	metachars[num_metachars] = '\0';
15595270f73SXin LI }
15695270f73SXin LI 
157d713e089SXin LI static int is_metachar(int ch)
15895270f73SXin LI {
15995270f73SXin LI 	return (metachars != NULL && strchr(metachars, ch) != NULL);
16095270f73SXin LI }
161a5f0fb15SPaul Saab 
162a5f0fb15SPaul Saab #if !HAVE_STRCHR
163d713e089SXin LI char * strchr(char *s, char c)
164a5f0fb15SPaul Saab {
165a5f0fb15SPaul Saab 	for ( ;  *s != '\0';  s++)
166a5f0fb15SPaul Saab 		if (*s == c)
167a5f0fb15SPaul Saab 			return (s);
168a5f0fb15SPaul Saab 	if (c == '\0')
169a5f0fb15SPaul Saab 		return (s);
170a5f0fb15SPaul Saab 	return (NULL);
171a5f0fb15SPaul Saab }
172a5f0fb15SPaul Saab #endif
173a5f0fb15SPaul Saab 
174d713e089SXin LI int main(int argc, char *argv[])
175a5f0fb15SPaul Saab {
176a5f0fb15SPaul Saab 	char *arg;
177a5f0fb15SPaul Saab 	char *s;
178a5f0fb15SPaul Saab 	int no_more_options;
179a5f0fb15SPaul Saab 
180a5f0fb15SPaul Saab 	no_more_options = 0;
181a5f0fb15SPaul Saab 	while (--argc > 0)
182a5f0fb15SPaul Saab 	{
183a5f0fb15SPaul Saab 		arg = *++argv;
184a5f0fb15SPaul Saab 		if (*arg != '-' || no_more_options)
185a5f0fb15SPaul Saab 			break;
186a5f0fb15SPaul Saab 		switch (*++arg)
187a5f0fb15SPaul Saab 		{
188a5f0fb15SPaul Saab 		case 'a':
189a5f0fb15SPaul Saab 			quote_all = 1;
190a5f0fb15SPaul Saab 			break;
191a5f0fb15SPaul Saab 		case 'c':
192a5f0fb15SPaul Saab 			closequote = *++arg;
193a5f0fb15SPaul Saab 			break;
194a5f0fb15SPaul Saab 		case 'd':
195*c77c4889SXin LI 			closequote = (char) lstrtol(++arg, &s, 0);
196a5f0fb15SPaul Saab 			if (s == arg)
197000ba3e8STim J. Robbins 				pr_error("Missing number after -d");
198000ba3e8STim J. Robbins 			break;
199000ba3e8STim J. Robbins 		case 'e':
200000ba3e8STim J. Robbins 			if (strcmp(++arg, "-") == 0)
201000ba3e8STim J. Robbins 				meta_escape = "";
202000ba3e8STim J. Robbins 			else
203000ba3e8STim J. Robbins 				meta_escape = arg;
204000ba3e8STim J. Robbins 			break;
205000ba3e8STim J. Robbins 		case 'f':
206*c77c4889SXin LI 			meta_escape_buf[0] = (char) lstrtol(++arg, &s, 0);
20795270f73SXin LI 			meta_escape_buf[1] = '\0';
208000ba3e8STim J. Robbins 			meta_escape = meta_escape_buf;
209000ba3e8STim J. Robbins 			if (s == arg)
210000ba3e8STim J. Robbins 				pr_error("Missing number after -f");
211000ba3e8STim J. Robbins 			break;
212000ba3e8STim J. Robbins 		case 'o':
213000ba3e8STim J. Robbins 			openquote = *++arg;
214000ba3e8STim J. Robbins 			break;
215000ba3e8STim J. Robbins 		case 'p':
216*c77c4889SXin LI 			openquote = (char) lstrtol(++arg, &s, 0);
217000ba3e8STim J. Robbins 			if (s == arg)
218000ba3e8STim J. Robbins 				pr_error("Missing number after -p");
219000ba3e8STim J. Robbins 			break;
220000ba3e8STim J. Robbins 		case 'm':
22195270f73SXin LI 			add_metachar(*++arg);
222000ba3e8STim J. Robbins 			break;
223000ba3e8STim J. Robbins 		case 'n':
224*c77c4889SXin LI 			add_metachar((char) lstrtol(++arg, &s, 0));
225000ba3e8STim J. Robbins 			if (s == arg)
226000ba3e8STim J. Robbins 				pr_error("Missing number after -n");
227a5f0fb15SPaul Saab 			break;
228a5f0fb15SPaul Saab 		case '?':
229a5f0fb15SPaul Saab 			pr_usage();
230a5f0fb15SPaul Saab 			return (0);
231a5f0fb15SPaul Saab 		case '-':
232a5f0fb15SPaul Saab 			if (*++arg == '\0')
233a5f0fb15SPaul Saab 			{
234a5f0fb15SPaul Saab 				no_more_options = 1;
235a5f0fb15SPaul Saab 				break;
236a5f0fb15SPaul Saab 			}
237a5f0fb15SPaul Saab 			if (strcmp(arg, "version") == 0)
238a5f0fb15SPaul Saab 			{
239a5f0fb15SPaul Saab 				pr_version();
240a5f0fb15SPaul Saab 				return (0);
241a5f0fb15SPaul Saab 			}
242a5f0fb15SPaul Saab 			if (strcmp(arg, "help") == 0)
243a5f0fb15SPaul Saab 			{
244a5f0fb15SPaul Saab 				pr_usage();
245a5f0fb15SPaul Saab 				return (0);
246a5f0fb15SPaul Saab 			}
247a5f0fb15SPaul Saab 			pr_error("Invalid option after --");
248*c77c4889SXin LI 			return (0);
249a5f0fb15SPaul Saab 		default:
250a5f0fb15SPaul Saab 			pr_error("Invalid option letter");
251a5f0fb15SPaul Saab 		}
252a5f0fb15SPaul Saab 	}
253a5f0fb15SPaul Saab 
254a5f0fb15SPaul Saab 	while (argc-- > 0)
255a5f0fb15SPaul Saab 	{
256000ba3e8STim J. Robbins 		int has_meta = 0;
257a5f0fb15SPaul Saab 		arg = *argv++;
258000ba3e8STim J. Robbins 		for (s = arg;  *s != '\0';  s++)
259000ba3e8STim J. Robbins 		{
26095270f73SXin LI 			if (is_metachar(*s))
261000ba3e8STim J. Robbins 			{
262000ba3e8STim J. Robbins 				has_meta = 1;
263000ba3e8STim J. Robbins 				break;
264000ba3e8STim J. Robbins 			}
265000ba3e8STim J. Robbins 		}
266000ba3e8STim J. Robbins 		if (quote_all || (has_meta && strlen(meta_escape) == 0))
267a5f0fb15SPaul Saab 			printf("%c%s%c", openquote, arg, closequote);
268a5f0fb15SPaul Saab 		else
269000ba3e8STim J. Robbins 		{
270000ba3e8STim J. Robbins 			for (s = arg;  *s != '\0';  s++)
271000ba3e8STim J. Robbins 			{
27295270f73SXin LI 				if (is_metachar(*s))
273000ba3e8STim J. Robbins 					printf("%s", meta_escape);
274000ba3e8STim J. Robbins 				printf("%c", *s);
275000ba3e8STim J. Robbins 			}
276000ba3e8STim J. Robbins 		}
277a5f0fb15SPaul Saab 		if (argc > 0)
278a5f0fb15SPaul Saab 			printf(" ");
279a5f0fb15SPaul Saab 		else
280a5f0fb15SPaul Saab 			printf("\n");
281a5f0fb15SPaul Saab 	}
282a5f0fb15SPaul Saab 	return (0);
283a5f0fb15SPaul Saab }
284