ident.c revision 21673
1247280Sdteske/* Identify RCS keyword strings in files.  */
2247280Sdteske
3247280Sdteske/* Copyright 1982, 1988, 1989 Walter Tichy
4247280Sdteske   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5247280Sdteske   Distributed under license by the Free Software Foundation, Inc.
6247280Sdteske
7247280SdteskeThis file is part of RCS.
8247280Sdteske
9247280SdteskeRCS is free software; you can redistribute it and/or modify
10247280Sdteskeit under the terms of the GNU General Public License as published by
11247280Sdteskethe Free Software Foundation; either version 2, or (at your option)
12247280Sdteskeany later version.
13247280Sdteske
14247280SdteskeRCS is distributed in the hope that it will be useful,
15247280Sdteskebut WITHOUT ANY WARRANTY; without even the implied warranty of
16247280SdteskeMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17247280SdteskeGNU General Public License for more details.
18247280Sdteske
19247280SdteskeYou should have received a copy of the GNU General Public License
20247280Sdteskealong with RCS; see the file COPYING.
21247280SdteskeIf not, write to the Free Software Foundation,
22247280Sdteske59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23247280Sdteske
24247280SdteskeReport problems and direct all questions to:
25247280Sdteske
26247280Sdteske    rcs-bugs@cs.purdue.edu
27247280Sdteske
28247280Sdteske*/
29247280Sdteske
30247280Sdteske/*
31247280Sdteske * Revision 5.9  1995/06/16 06:19:24  eggert
32247280Sdteske * Update FSF address.
33247280Sdteske *
34247280Sdteske * Revision 5.8  1995/06/01 16:23:43  eggert
35247280Sdteske * (exiterr, reportError): New functions, needed for DOS and OS/2 ports.
36247280Sdteske * (scanfile): Use them.
37247280Sdteske *
38247280Sdteske * Revision 5.7  1994/03/20 04:52:58  eggert
39247280Sdteske * Remove `exiting' from identExit.
40247280Sdteske *
41247280Sdteske * Revision 5.6  1993/11/09 17:40:15  eggert
42247280Sdteske * Add -V.
43247280Sdteske *
44247280Sdteske * Revision 5.5  1993/11/03 17:42:27  eggert
45247280Sdteske * Test for char == EOF, not char < 0.
46247280Sdteske *
47247280Sdteske * Revision 5.4  1992/01/24  18:44:19  eggert
48247280Sdteske * lint -> RCS_lint
49247280Sdteske *
50247280Sdteske * Revision 5.3  1991/09/10  22:15:46  eggert
51247280Sdteske * Open files with FOPEN_R, not FOPEN_R_WORK,
52247280Sdteske * because they might be executables, not working files.
53247280Sdteske *
54247280Sdteske * Revision 5.2  1991/08/19  03:13:55  eggert
55247280Sdteske * Report read errors immediately.
56247280Sdteske *
57247280Sdteske * Revision 5.1  1991/02/25  07:12:37  eggert
58251190Sdteske * Don't report empty keywords.  Check for I/O errors.
59247280Sdteske *
60247280Sdteske * Revision 5.0  1990/08/22  08:12:37  eggert
61247280Sdteske * Don't limit output to known keywords.
62247280Sdteske * Remove arbitrary limits and lint.  Ansify and Posixate.
63247280Sdteske *
64247280Sdteske * Revision 4.5  89/05/01  15:11:54  narten
65247280Sdteske * changed copyright header to reflect current distribution rules
66247280Sdteske *
67247280Sdteske * Revision 4.4  87/10/23  17:09:57  narten
68247280Sdteske * added exit(0) so exit return code would be non random
69249751Sdteske *
70247280Sdteske * Revision 4.3  87/10/18  10:23:55  narten
71247280Sdteske * Updating version numbers. Changes relative to 1.1 are actually relative
72247280Sdteske * to 4.1
73247280Sdteske *
74247280Sdteske * Revision 1.3  87/07/09  09:20:52  trinkle
75247280Sdteske * Added check to make sure there is at least one arg before comparing argv[1]
76247280Sdteske * with "-q".  This necessary on machines that don't allow dereferncing null
77247280Sdteske * pointers (i.e. Suns).
78247280Sdteske *
79247280Sdteske * Revision 1.2  87/03/27  14:21:47  jenkins
80247280Sdteske * Port to suns
81247280Sdteske *
82247280Sdteske * Revision 4.1  83/05/10  16:31:02  wft
83247280Sdteske * Added option -q and input from reading stdin.
84247280Sdteske * Marker matching is now done with trymatch() (independent of keywords).
85247280Sdteske *
86247280Sdteske * Revision 3.4  83/02/18  17:37:49  wft
87247280Sdteske * removed printing of new line after last file.
88247280Sdteske *
89247280Sdteske * Revision 3.3  82/12/04  12:48:55  wft
90247280Sdteske * Added LOCKER.
91247280Sdteske *
92247280Sdteske * Revision 3.2  82/11/28  18:24:17  wft
93247280Sdteske * removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
94247280Sdteske *
95247280Sdteske * Revision 3.1  82/10/13  15:58:51  wft
96247280Sdteske * fixed type of variables receiving from getc() (char-->int).
97247280Sdteske*/
98247280Sdteske
99247280Sdteske#include  "rcsbase.h"
100247280Sdteske
101247280Sdteskestatic int match P((FILE*));
102247280Sdteskestatic int scanfile P((FILE*,char const*,int));
103247280Sdteskestatic void reportError P((char const*));
104247280Sdteske
105247280SdteskemainProg(identId, "ident", "$FreeBSD: head/gnu/usr.bin/rcs/ident/ident.c 21673 1997-01-14 07:20:47Z jkh $")
106247280Sdteske/*  Ident searches the named files for all occurrences
107247280Sdteske *  of the pattern $@: text $ where @ is a keyword.
108247280Sdteske */
109247280Sdteske
110247280Sdteske{
111247280Sdteske   FILE *fp;
112247280Sdteske   int quiet = 0;
113247280Sdteske   int status = EXIT_SUCCESS;
114250323Sdteske   char const *a;
115250323Sdteske
116250323Sdteske   while ((a = *++argv)  &&  *a=='-')
117250323Sdteske	while (*++a)
118250323Sdteske	    switch (*a) {
119250323Sdteske		case 'q':
120250323Sdteske		    quiet = 1;
121250323Sdteske		    break;
122250323Sdteske
123247280Sdteske		case 'V':
124247280Sdteske		    VOID printf("RCS version %s\n", RCS_version_string);
125247280Sdteske		    quiet = -1;
126247280Sdteske		    break;
127247280Sdteske
128247280Sdteske		default:
129247280Sdteske		    VOID fprintf(stderr,
130247280Sdteske			"ident: usage: ident -{qV} [file...]\n"
131247280Sdteske		    );
132247280Sdteske		    exitmain(EXIT_FAILURE);
133247280Sdteske		    break;
134247280Sdteske	    }
135247280Sdteske
136247280Sdteske   if (0 <= quiet)
137247280Sdteske       if (!a)
138247280Sdteske	    VOID scanfile(stdin, (char*)0, quiet);
139247280Sdteske       else
140247280Sdteske	    do {
141247280Sdteske		if (!(fp = fopen(a, FOPEN_RB))) {
142247280Sdteske		    reportError(a);
143247280Sdteske		    status = EXIT_FAILURE;
144247280Sdteske		} else if (
145247280Sdteske		    scanfile(fp, a, quiet) != 0
146247280Sdteske		    || (argv[1]  &&  putchar('\n') == EOF)
147247280Sdteske		)
148247280Sdteske		    break;
149247280Sdteske	    } while ((a = *++argv));
150247280Sdteske
151247280Sdteske   if (ferror(stdout) || fclose(stdout)!=0) {
152247280Sdteske      reportError("standard output");
153247280Sdteske      status = EXIT_FAILURE;
154247280Sdteske   }
155247280Sdteske   exitmain(status);
156247280Sdteske}
157247280Sdteske
158247280Sdteske#if RCS_lint
159247280Sdteske#	define exiterr identExit
160247280Sdteske#endif
161247280Sdteske	void
162247280Sdteskeexiterr()
163247280Sdteske{
164247280Sdteske	_exit(EXIT_FAILURE);
165247280Sdteske}
166247280Sdteske
167247280Sdteske	static void
168247280SdteskereportError(s)
169247280Sdteske	char const *s;
170247280Sdteske{
171247280Sdteske	int e = errno;
172247280Sdteske	VOID fprintf(stderr, "%s error: ", cmdid);
173247280Sdteske	errno = e;
174247280Sdteske	perror(s);
175247280Sdteske}
176247280Sdteske
177247280Sdteske
178247280Sdteske	static int
179247280Sdteskescanfile(file, name, quiet)
180247280Sdteske	register FILE *file;
181247280Sdteske	char const *name;
182247280Sdteske	int quiet;
183247280Sdteske/* Function: scan an open file with descriptor file for keywords.
184247280Sdteske * Return -1 if there's a write error; exit immediately on a read error.
185247280Sdteske */
186247280Sdteske{
187247280Sdteske   register int c;
188247280Sdteske
189247280Sdteske   if (name) {
190247280Sdteske      VOID printf("%s:\n", name);
191250323Sdteske      if (ferror(stdout))
192247280Sdteske	 return -1;
193250323Sdteske   } else
194250323Sdteske      name = "standard input";
195250323Sdteske   c = 0;
196250323Sdteske   while (c != EOF  ||  ! (feof(file)|ferror(file))) {
197247280Sdteske      if (c == KDELIM) {
198247280Sdteske	 if ((c = match(file)))
199247280Sdteske	    continue;
200247280Sdteske	 if (ferror(stdout))
201247280Sdteske	    return -1;
202247280Sdteske	 quiet = true;
203251190Sdteske      }
204251190Sdteske      c = getc(file);
205251190Sdteske   }
206251190Sdteske   if (ferror(file) || fclose(file) != 0) {
207251190Sdteske      reportError(name);
208251190Sdteske      /*
209251190Sdteske      * The following is equivalent to exit(EXIT_FAILURE), but we invoke
210247280Sdteske      * exiterr to keep lint happy.  The DOS and OS/2 ports need exiterr.
211247280Sdteske      */
212247280Sdteske      VOID fflush(stderr);
213247280Sdteske      VOID fflush(stdout);
214251190Sdteske      exiterr();
215251190Sdteske   }
216251190Sdteske   if (!quiet)
217251190Sdteske      VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
218251190Sdteske   return 0;
219251190Sdteske}
220251190Sdteske
221251190Sdteske
222251190Sdteske
223251190Sdteske	static int
224251190Sdteskematch(fp)   /* group substring between two KDELIM's; then do pattern match */
225251190Sdteske   register FILE *fp;
226251190Sdteske{
227247280Sdteske   char line[BUFSIZ];
228247280Sdteske   register int c;
229247280Sdteske   register char * tp;
230247280Sdteske
231249751Sdteske   tp = line;
232247280Sdteske   while ((c = getc(fp)) != VDELIM) {
233247280Sdteske      if (c == EOF  &&  feof(fp) | ferror(fp))
234247280Sdteske	 return c;
235247280Sdteske      switch (ctab[c]) {
236247280Sdteske	 case LETTER: case Letter:
237247280Sdteske	    *tp++ = c;
238247280Sdteske	    if (tp < line+sizeof(line)-4)
239247280Sdteske	       break;
240247280Sdteske	    /* fall into */
241247280Sdteske	 default:
242247280Sdteske	    return c ? c : '\n'/* anything but 0 or KDELIM or EOF */;
243247280Sdteske      }
244247280Sdteske   }
245247280Sdteske   if (tp == line)
246247280Sdteske      return c;
247247280Sdteske   *tp++ = c;
248247280Sdteske   if ((c = getc(fp)) != ' ')
249247280Sdteske      return c ? c : '\n';
250247280Sdteske   *tp++ = c;
251247280Sdteske   while( (c = getc(fp)) != KDELIM ) {
252247280Sdteske      if (c == EOF  &&  feof(fp) | ferror(fp))
253247280Sdteske	    return c;
254247280Sdteske      switch (ctab[c]) {
255247280Sdteske	 default:
256247280Sdteske	    *tp++ = c;
257247280Sdteske	    if (tp < line+sizeof(line)-2)
258247280Sdteske	       break;
259247280Sdteske	    /* fall into */
260247280Sdteske	 case NEWLN: case UNKN:
261247280Sdteske	    return c ? c : '\n';
262247280Sdteske      }
263247280Sdteske   }
264247280Sdteske   if (tp[-1] != ' ')
265247280Sdteske      return c;
266247280Sdteske   *tp++ = c;     /*append trailing KDELIM*/
267247280Sdteske   *tp   = '\0';
268247280Sdteske   VOID printf("     %c%s\n", KDELIM, line);
269247280Sdteske   return 0;
270247280Sdteske}
271247280Sdteske