]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/ident/ident.c
Add 'contrib/bsddialog/' from commit '857c66bb5f3c5651b012beb1b5ea6ba39354ea94'
[FreeBSD/FreeBSD.git] / usr.bin / ident / ident.c
1 /*-
2  * Copyright (c) 2015-2021 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2015 Xin LI <delphij@FreeBSD.org>
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/capsicum.h>
31 #include <sys/types.h>
32 #include <sys/sbuf.h>
33
34 #include <capsicum_helpers.h>
35 #include <ctype.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <stdbool.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <unistd.h>
43 #include <xlocale.h>
44
45 typedef enum {
46         /* state        condition to transit to next state */
47         INIT,           /* '$' */
48         DELIM_SEEN,     /* letter */
49         KEYWORD,        /* punctuation mark */
50         PUNC_SEEN,      /* ':' -> _SVN; space -> TEXT */
51         PUNC_SEEN_SVN,  /* space */
52         TEXT
53 } analyzer_states;
54
55 static int
56 scan(FILE *fp, const char *name, bool quiet)
57 {
58         int c;
59         bool hasid = false;
60         bool subversion = false;
61         analyzer_states state = INIT;
62         FILE* buffp;
63         char *buf;
64         size_t sz;
65         locale_t l;
66
67         l = newlocale(LC_ALL_MASK, "C", NULL);
68         sz = 0;
69         buf = NULL;
70         buffp = open_memstream(&buf, &sz);
71         if (buffp == NULL)
72                 err(EXIT_FAILURE, "open_memstream()");
73
74         if (name != NULL)
75                 printf("%s:\n", name);
76
77         while ((c = fgetc(fp)) != EOF) {
78                 switch (state) {
79                 case INIT:
80                         if (c == '$') {
81                                 /* Transit to DELIM_SEEN if we see $ */
82                                 state = DELIM_SEEN;
83                         } else {
84                                 /* Otherwise, stay in INIT state */
85                                 continue;
86                         }
87                         break;
88                 case DELIM_SEEN:
89                         if (isalpha_l(c, l)) {
90                                 /* Transit to KEYWORD if we see letter */
91                                 if (buf != NULL)
92                                         memset(buf, 0, sz);
93                                 rewind(buffp);
94                                 fputc('$', buffp);
95                                 fputc(c, buffp);
96                                 state = KEYWORD;
97
98                                 continue;
99                         } else if (c == '$') {
100                                 /* Or, stay in DELIM_SEEN if more $ */
101                                 continue;
102                         } else {
103                                 /* Otherwise, transit back to INIT */
104                                 state = INIT;
105                         }
106                         break;
107                 case KEYWORD:
108                         fputc(c, buffp);
109
110                         if (isalpha_l(c, l)) {
111                                 /*
112                                  * Stay in KEYWORD if additional letter is seen
113                                  */
114                                 continue;
115                         } else if (c == ':') {
116                                 /*
117                                  * See ':' for the first time, transit to
118                                  * PUNC_SEEN.
119                                  */
120                                 state = PUNC_SEEN;
121                                 subversion = false;
122                         } else if (c == '$') {
123                                 /*
124                                  * Incomplete ident.  Go back to DELIM_SEEN
125                                  * state because we see a '$' which could be
126                                  * the beginning of a keyword.
127                                  */
128                                 state = DELIM_SEEN;
129                         } else {
130                                 /*
131                                  * Go back to INIT state otherwise.
132                                  */
133                                 state = INIT;
134                         }
135                         break;
136                 case PUNC_SEEN:
137                 case PUNC_SEEN_SVN:
138                         fputc(c, buffp);
139
140                         switch (c) {
141                         case ':':
142                                 /*
143                                  * If we see '::' (seen : in PUNC_SEEN),
144                                  * activate subversion treatment and transit
145                                  * to PUNC_SEEN_SVN state.
146                                  *
147                                  * If more than two :'s were seen, the ident
148                                  * is invalid and we would therefore go back
149                                  * to INIT state.
150                                  */
151                                 if (state == PUNC_SEEN) {
152                                         state = PUNC_SEEN_SVN;
153                                         subversion = true;
154                                 } else {
155                                         state = INIT;
156                                 }
157                                 break;
158                         case ' ':
159                                 /*
160                                  * A space after ':' or '::' indicates we are at the
161                                  * last component of potential ident.
162                                  */
163                                 state = TEXT;
164                                 break;
165                         default:
166                                 /* All other characters are invalid */
167                                 state = INIT;
168                                 break;
169                         }
170                         break;
171                 case TEXT:
172                         fputc(c, buffp);
173
174                         if (iscntrl_l(c, l)) {
175                                 /* Control characters are not allowed in this state */
176                                 state = INIT;
177                         } else if (c == '$') {
178                                 fflush(buffp);
179                                 /*
180                                  * valid ident should end with a space.
181                                  *
182                                  * subversion extension uses '#' to indicate that
183                                  * the keyword expansion have exceeded the fixed
184                                  * width, so it is also permitted if we are in
185                                  * subversion mode.  No length check is enforced
186                                  * because GNU RCS ident(1) does not do it either.
187                                  */
188                                 c = buf[strlen(buf) -2 ];
189                                 if (c == ' ' || (subversion && c == '#')) {
190                                         printf("     %s\n", buf);
191                                         hasid = true;
192                                 }
193                                 state = INIT;
194                         }
195                         /* Other characters: stay in the state */
196                         break;
197                 }
198         }
199         fclose(buffp);
200         free(buf);
201         freelocale(l);
202
203         if (!hasid) {
204                 if (!quiet)
205                         fprintf(stderr, "%s warning: no id keywords in %s\n",
206                             getprogname(), name ? name : "standard input");
207
208                 return (EXIT_FAILURE);
209         }
210
211         return (EXIT_SUCCESS);
212 }
213
214 int
215 main(int argc, char **argv)
216 {
217         bool quiet = false;
218         int ch, i, *fds, fd;
219         int ret = EXIT_SUCCESS;
220         size_t nfds;
221         FILE *fp;
222
223         while ((ch = getopt(argc, argv, "qV")) != -1) {
224                 switch (ch) {
225                 case 'q':
226                         quiet = true;
227                         break;
228                 case 'V':
229                         /* Do nothing, compat with GNU rcs's ident */
230                         return (EXIT_SUCCESS);
231                 default:
232                         errx(EXIT_FAILURE, "usage: %s [-q] [-V] [file...]",
233                             getprogname());
234                 }
235         }
236
237         argc -= optind;
238         argv += optind;
239
240         if (caph_limit_stdio() < 0)
241                 err(EXIT_FAILURE, "unable to limit stdio");
242
243         if (argc == 0) {
244                 nfds = 1;
245                 fds = malloc(sizeof(*fds));
246                 if (fds == NULL)
247                         err(EXIT_FAILURE, "unable to allocate fds array");
248                 fds[0] = STDIN_FILENO;
249         } else {
250                 nfds = argc;
251                 fds = malloc(sizeof(*fds) * nfds);
252                 if (fds == NULL)
253                         err(EXIT_FAILURE, "unable to allocate fds array");
254
255                 for (i = 0; i < argc; i++) {
256                         fds[i] = fd = open(argv[i], O_RDONLY);
257                         if (fd < 0) {
258                                 warn("%s", argv[i]);
259                                 ret = EXIT_FAILURE;
260                                 continue;
261                         }
262                         if (caph_limit_stream(fd, CAPH_READ) < 0)
263                                 err(EXIT_FAILURE,
264                                     "unable to limit fcntls/rights for %s",
265                                     argv[i]);
266                 }
267         }
268
269         /* Enter Capsicum sandbox. */
270         if (caph_enter() < 0)
271                 err(EXIT_FAILURE, "unable to enter capability mode");
272
273         for (i = 0; i < (int)nfds; i++) {
274                 if (fds[i] < 0)
275                         continue;
276
277                 fp = fdopen(fds[i], "r");
278                 if (fp == NULL) {
279                         warn("%s", argv[i]);
280                         ret = EXIT_FAILURE;
281                         continue;
282                 }
283                 if (scan(fp, argc == 0 ? NULL : argv[i], quiet) != EXIT_SUCCESS)
284                         ret = EXIT_FAILURE;
285                 fclose(fp);
286         }
287
288         return (ret);
289 }