]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/ident/ident.c
Remove spurious newline
[FreeBSD/FreeBSD.git] / usr.bin / ident / ident.c
1 /*-
2  * Copyright (c) 2015 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 <unistd.h>
42 #include <xlocale.h>
43
44 typedef enum {
45         /* state        condition to transit to next state */
46         INIT,           /* '$' */
47         DELIM_SEEN,     /* letter */
48         KEYWORD,        /* punctuation mark */
49         PUNC_SEEN,      /* ':' -> _SVN; space -> TEXT */
50         PUNC_SEEN_SVN,  /* space */
51         TEXT
52 } analyzer_states;
53
54 static int
55 scan(FILE *fp, const char *name, bool quiet)
56 {
57         int c;
58         bool hasid = false;
59         bool subversion = false;
60         analyzer_states state = INIT;
61         struct sbuf *id = sbuf_new_auto();
62         locale_t l;
63
64         l = newlocale(LC_ALL_MASK, "C", NULL);
65
66         if (name != NULL)
67                 printf("%s:\n", name);
68
69         while ((c = fgetc(fp)) != EOF) {
70                 switch (state) {
71                 case INIT:
72                         if (c == '$') {
73                                 /* Transit to DELIM_SEEN if we see $ */
74                                 state = DELIM_SEEN;
75                         } else {
76                                 /* Otherwise, stay in INIT state */
77                                 continue;
78                         }
79                         break;
80                 case DELIM_SEEN:
81                         if (isalpha_l(c, l)) {
82                                 /* Transit to KEYWORD if we see letter */
83                                 sbuf_clear(id);
84                                 sbuf_putc(id, '$');
85                                 sbuf_putc(id, c);
86                                 state = KEYWORD;
87
88                                 continue;
89                         } else if (c == '$') {
90                                 /* Or, stay in DELIM_SEEN if more $ */
91                                 continue;
92                         } else {
93                                 /* Otherwise, transit back to INIT */
94                                 state = INIT;
95                         }
96                         break;
97                 case KEYWORD:
98                         sbuf_putc(id, c);
99
100                         if (isalpha_l(c, l)) {
101                                 /*
102                                  * Stay in KEYWORD if additional letter is seen
103                                  */
104                                 continue;
105                         } else if (c == ':') {
106                                 /*
107                                  * See ':' for the first time, transit to
108                                  * PUNC_SEEN.
109                                  */
110                                 state = PUNC_SEEN;
111                                 subversion = false;
112                         } else if (c == '$') {
113                                 /*
114                                  * Incomplete ident.  Go back to DELIM_SEEN
115                                  * state because we see a '$' which could be
116                                  * the beginning of a keyword.
117                                  */
118                                 state = DELIM_SEEN;
119                         } else {
120                                 /*
121                                  * Go back to INIT state otherwise.
122                                  */
123                                 state = INIT;
124                         }
125                         break;
126                 case PUNC_SEEN:
127                 case PUNC_SEEN_SVN:
128                         sbuf_putc(id, c);
129
130                         switch (c) {
131                         case ':':
132                                 /*
133                                  * If we see '::' (seen : in PUNC_SEEN),
134                                  * activate subversion treatment and transit
135                                  * to PUNC_SEEN_SVN state.
136                                  *
137                                  * If more than two :'s were seen, the ident
138                                  * is invalid and we would therefore go back
139                                  * to INIT state.
140                                  */
141                                 if (state == PUNC_SEEN) {
142                                         state = PUNC_SEEN_SVN;
143                                         subversion = true;
144                                 } else {
145                                         state = INIT;
146                                 }
147                                 break;
148                         case ' ':
149                                 /*
150                                  * A space after ':' or '::' indicates we are at the
151                                  * last component of potential ident.
152                                  */
153                                 state = TEXT;
154                                 break;
155                         default:
156                                 /* All other characters are invalid */
157                                 state = INIT;
158                                 break;
159                         }
160                         break;
161                 case TEXT:
162                         sbuf_putc(id, c);
163
164                         if (iscntrl_l(c, l)) {
165                                 /* Control characters are not allowed in this state */
166                                 state = INIT;
167                         } else if (c == '$') {
168                                 sbuf_finish(id);
169                                 /*
170                                  * valid ident should end with a space.
171                                  *
172                                  * subversion extension uses '#' to indicate that
173                                  * the keyword expansion have exceeded the fixed
174                                  * width, so it is also permitted if we are in
175                                  * subversion mode.  No length check is enforced
176                                  * because GNU RCS ident(1) does not do it either.
177                                  */
178                                 c = sbuf_data(id)[sbuf_len(id) - 2];
179                                 if (c == ' ' || (subversion && c == '#')) {
180                                         printf("     %s\n", sbuf_data(id));
181                                         hasid = true;
182                                 }
183                                 state = INIT;
184                         }
185                         /* Other characters: stay in the state */
186                         break;
187                 }
188         }
189         sbuf_delete(id);
190         freelocale(l);
191
192         if (!hasid) {
193                 if (!quiet)
194                         fprintf(stderr, "%s warning: no id keywords in %s\n",
195                             getprogname(), name ? name : "standard input");
196
197                 return (EXIT_FAILURE);
198         }
199
200         return (EXIT_SUCCESS);
201 }
202
203 int
204 main(int argc, char **argv)
205 {
206         bool quiet = false;
207         int ch, i, *fds, fd;
208         int ret = EXIT_SUCCESS;
209         size_t nfds;
210         FILE *fp;
211
212         while ((ch = getopt(argc, argv, "qV")) != -1) {
213                 switch (ch) {
214                 case 'q':
215                         quiet = true;
216                         break;
217                 case 'V':
218                         /* Do nothing, compat with GNU rcs's ident */
219                         return (EXIT_SUCCESS);
220                 default:
221                         errx(EXIT_FAILURE, "usage: %s [-q] [-V] [file...]",
222                             getprogname());
223                 }
224         }
225
226         argc -= optind;
227         argv += optind;
228
229         if (caph_limit_stdio() < 0)
230                 err(EXIT_FAILURE, "unable to limit stdio");
231
232         if (argc == 0) {
233                 nfds = 1;
234                 fds = malloc(sizeof(*fds));
235                 if (fds == NULL)
236                         err(EXIT_FAILURE, "unable to allocate fds array");
237                 fds[0] = STDIN_FILENO;
238         } else {
239                 nfds = argc;
240                 fds = malloc(sizeof(*fds) * nfds);
241                 if (fds == NULL)
242                         err(EXIT_FAILURE, "unable to allocate fds array");
243
244                 for (i = 0; i < argc; i++) {
245                         fds[i] = fd = open(argv[i], O_RDONLY);
246                         if (fd < 0) {
247                                 warn("%s", argv[i]);
248                                 ret = EXIT_FAILURE;
249                                 continue;
250                         }
251                         if (caph_limit_stream(fd, CAPH_READ) < 0)
252                                 err(EXIT_FAILURE,
253                                     "unable to limit fcntls/rights for %s",
254                                     argv[i]);
255                 }
256         }
257
258         /* Enter Capsicum sandbox. */
259         if (caph_enter() < 0)
260                 err(EXIT_FAILURE, "unable to enter capability mode");
261
262         for (i = 0; i < (int)nfds; i++) {
263                 if (fds[i] < 0)
264                         continue;
265
266                 fp = fdopen(fds[i], "r");
267                 if (fp == NULL) {
268                         warn("%s", argv[i]);
269                         ret = EXIT_FAILURE;
270                         continue;
271                 }
272                 if (scan(fp, argc == 0 ? NULL : argv[i], quiet) != EXIT_SUCCESS)
273                         ret = EXIT_FAILURE;
274                 fclose(fp);
275         }
276
277         return (ret);
278 }