ident.c revision 285890
1/*-
2 * Copyright (c) 2015 Baptiste Daroussin <bapt@FreeBSD.org>
3 * All rights reserved.
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: head/usr.bin/ident/ident.c 285890 2015-07-26 11:21:36Z bapt $");
29
30#include <sys/types.h>
31#include <sys/sbuf.h>
32
33#include <ctype.h>
34#include <err.h>
35#include <stdbool.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <unistd.h>
39#include <xlocale.h>
40
41static bool
42parse_id(FILE *fp, struct sbuf *buf, locale_t l)
43{
44	int c;
45	bool isid = false;
46	bool subversion = false;
47
48	sbuf_putc(buf, '$');
49	while ((c = fgetc(fp)) != EOF) {
50		sbuf_putc(buf, c);
51		if (!isid) {
52			if (c == '$') {
53				sbuf_clear(buf);
54				sbuf_putc(buf, '$');
55				continue;
56			}
57			if (c == ':') {
58				 c = fgetc(fp);
59				 /* accept :: for subversion compatibility */
60				 if (c == ':') {
61					subversion = true;
62					sbuf_putc(buf, c);
63					c = fgetc(fp);
64				}
65				if (c == ' ') {
66					sbuf_putc(buf, c);
67					isid = true;
68					continue;
69				}
70				return (false);
71			}
72
73			if (!isalpha_l(c, l))
74				return (false);
75		} else {
76			if (c == '\n')
77				return (false);
78			if (c == '$') {
79				sbuf_finish(buf);
80				/* should end with a space */
81				c = sbuf_data(buf)[sbuf_len(buf) - 2];
82				if (!subversion) {
83					if (c != ' ')
84						return (0);
85				} else if (subversion) {
86					if (c != ' ' && c != '#')
87						return (0);
88				}
89				printf("     %s\n", sbuf_data(buf));
90				return (true);
91			}
92		}
93	}
94
95	return (false);
96}
97
98static int
99scan(FILE *fp, const char *name, bool quiet)
100{
101	int c;
102	bool hasid = false;
103	struct sbuf *id = sbuf_new_auto();
104	locale_t l;
105
106	l = newlocale(LC_ALL_MASK, "C", NULL);
107
108	if (name != NULL)
109		printf("%s:\n", name);
110
111	while ((c = fgetc(fp)) != EOF) {
112		if (c == '$') {
113			sbuf_clear(id);
114			if (parse_id(fp, id, l))
115				hasid = true;
116		}
117	}
118	sbuf_delete(id);
119	freelocale(l);
120
121	if (!hasid) {
122		if (!quiet)
123			fprintf(stderr, "%s warning: no id keywords in %s\n",
124			    getprogname(), name ? name : "standard input");
125
126		return (EXIT_FAILURE);
127	}
128
129	return (EXIT_SUCCESS);
130}
131
132int
133main(int argc, char **argv)
134{
135	bool quiet = false;
136	int ch, i;
137	int ret = EXIT_SUCCESS;
138	FILE *fp;
139
140	while ((ch = getopt(argc, argv, "qV")) != -1) {
141		switch (ch) {
142		case 'q':
143			quiet = true;
144			break;
145		case 'V':
146			/* Do nothing, compat with GNU rcs's ident */
147			return (EXIT_SUCCESS);
148		default:
149			errx(EXIT_FAILURE, "usage: %s [-q] [-V] [file...]",
150			    getprogname());
151		}
152	}
153
154	argc -= optind;
155	argv += optind;
156
157	if (argc == 0)
158		return (scan(stdin, NULL, quiet));
159
160	for (i = 0; i < argc; i++) {
161		fp = fopen(argv[i], "r");
162		if (fp == NULL) {
163			warn("%s", argv[i]);
164			ret = EXIT_FAILURE;
165			continue;
166		}
167		if (scan(fp, argv[i], quiet) != EXIT_SUCCESS)
168			ret = EXIT_FAILURE;
169		fclose(fp);
170	}
171
172	return (ret);
173}
174