1238438Sdteske/* Identify RCS keyword strings in files.  */
2238438Sdteske
3247280Sdteske/* Copyright 1982, 1988, 1989 Walter Tichy
4252980Sdteske   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5238438Sdteske   Distributed under license by the Free Software Foundation, Inc.
6238438Sdteske
7238438SdteskeThis file is part of RCS.
8238438Sdteske
9238438SdteskeRCS is free software; you can redistribute it and/or modify
10238438Sdteskeit under the terms of the GNU General Public License as published by
11238438Sdteskethe Free Software Foundation; either version 2, or (at your option)
12238438Sdteskeany later version.
13238438Sdteske
14238438SdteskeRCS is distributed in the hope that it will be useful,
15238438Sdteskebut WITHOUT ANY WARRANTY; without even the implied warranty of
16252987SdteskeMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17238438SdteskeGNU General Public License for more details.
18238438Sdteske
19238438SdteskeYou should have received a copy of the GNU General Public License
20252987Sdteskealong with RCS; see the file COPYING.
21238438SdteskeIf not, write to the Free Software Foundation,
22238438Sdteske59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23238438Sdteske
24238438SdteskeReport problems and direct all questions to:
25238438Sdteske
26238438Sdteske    rcs-bugs@cs.purdue.edu
27238438Sdteske
28249751Sdteske*/
29256361Sdteske
30256361Sdteske/*
31256361Sdteske * Revision 5.9  1995/06/16 06:19:24  eggert
32256361Sdteske * Update FSF address.
33256361Sdteske *
34249751Sdteske * Revision 5.8  1995/06/01 16:23:43  eggert
35238438Sdteske * (exiterr, reportError): New functions, needed for DOS and OS/2 ports.
36249751Sdteske * (scanfile): Use them.
37263980Sdteske *
38263980Sdteske * Revision 5.7  1994/03/20 04:52:58  eggert
39263980Sdteske * Remove `exiting' from identExit.
40263980Sdteske *
41263980Sdteske * Revision 5.6  1993/11/09 17:40:15  eggert
42263980Sdteske * Add -V.
43249751Sdteske *
44249751Sdteske * Revision 5.5  1993/11/03 17:42:27  eggert
45249751Sdteske * Test for char == EOF, not char < 0.
46249751Sdteske *
47249751Sdteske * Revision 5.4  1992/01/24  18:44:19  eggert
48249751Sdteske * lint -> RCS_lint
49249751Sdteske *
50249751Sdteske * Revision 5.3  1991/09/10  22:15:46  eggert
51249751Sdteske * Open files with FOPEN_R, not FOPEN_R_WORK,
52249751Sdteske * because they might be executables, not working files.
53249751Sdteske *
54249751Sdteske * Revision 5.2  1991/08/19  03:13:55  eggert
55258420Sdteske * Report read errors immediately.
56238438Sdteske *
57238438Sdteske * Revision 5.1  1991/02/25  07:12:37  eggert
58238438Sdteske * Don't report empty keywords.  Check for I/O errors.
59238438Sdteske *
60238438Sdteske * Revision 5.0  1990/08/22  08:12:37  eggert
61238438Sdteske * Don't limit output to known keywords.
62238438Sdteske * Remove arbitrary limits and lint.  Ansify and Posixate.
63238438Sdteske *
64238438Sdteske * Revision 4.5  89/05/01  15:11:54  narten
65258420Sdteske * changed copyright header to reflect current distribution rules
66250701Sdteske *
67250701Sdteske * Revision 4.4  87/10/23  17:09:57  narten
68258420Sdteske * added exit(0) so exit return code would be non random
69258420Sdteske *
70250701Sdteske * Revision 4.3  87/10/18  10:23:55  narten
71250701Sdteske * Updating version numbers. Changes relative to 1.1 are actually relative
72250701Sdteske * to 4.1
73250701Sdteske *
74258420Sdteske * Revision 1.3  87/07/09  09:20:52  trinkle
75258420Sdteske * Added check to make sure there is at least one arg before comparing argv[1]
76258420Sdteske * with "-q".  This necessary on machines that don't allow dereferncing null
77250701Sdteske * pointers (i.e. Suns).
78250701Sdteske *
79250701Sdteske * Revision 1.2  87/03/27  14:21:47  jenkins
80250701Sdteske * Port to suns
81250701Sdteske *
82250701Sdteske * Revision 4.1  83/05/10  16:31:02  wft
83250701Sdteske * Added option -q and input from reading stdin.
84250701Sdteske * Marker matching is now done with trymatch() (independent of keywords).
85250701Sdteske *
86258420Sdteske * Revision 3.4  83/02/18  17:37:49  wft
87258420Sdteske * removed printing of new line after last file.
88258420Sdteske *
89258420Sdteske * Revision 3.3  82/12/04  12:48:55  wft
90258420Sdteske * Added LOCKER.
91258420Sdteske *
92258420Sdteske * Revision 3.2  82/11/28  18:24:17  wft
93258420Sdteske * removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
94258420Sdteske *
95258420Sdteske * Revision 3.1  82/10/13  15:58:51  wft
96258420Sdteske * fixed type of variables receiving from getc() (char-->int).
97258420Sdteske*/
98251278Sdteske
99251278Sdteske#include  "rcsbase.h"
100251278Sdteske
101251278Sdteskestatic int match P((FILE*));
102251278Sdteskestatic int scanfile P((FILE*,char const*,int));
103251278Sdteskestatic void reportError P((char const*));
104251278Sdteske
105251278SdteskemainProg(identId, "ident", "$FreeBSD: releng/10.2/gnu/usr.bin/rcs/ident/ident.c 87625 2001-12-10 20:44:31Z peter $")
106251278Sdteske/*  Ident searches the named files for all occurrences
107251278Sdteske *  of the pattern $@: text $ where @ is a keyword.
108251278Sdteske */
109251278Sdteske
110251278Sdteske{
111251278Sdteske   FILE *fp;
112251278Sdteske   int quiet = 0;
113251278Sdteske   int status = EXIT_SUCCESS;
114251278Sdteske   char const *a;
115251278Sdteske
116251278Sdteske   while ((a = *++argv)  &&  *a=='-')
117251278Sdteske	while (*++a)
118251278Sdteske	    switch (*a) {
119251278Sdteske		case 'q':
120251278Sdteske		    quiet = 1;
121251278Sdteske		    break;
122251278Sdteske
123251278Sdteske		case 'V':
124251278Sdteske		    VOID printf("RCS version %s\n", RCS_version_string);
125251278Sdteske		    quiet = -1;
126251278Sdteske		    break;
127251278Sdteske
128251278Sdteske		default:
129251278Sdteske		    VOID fprintf(stderr,
130251278Sdteske			"ident: usage: ident -{qV} [file...]\n"
131251278Sdteske		    );
132251278Sdteske		    exitmain(EXIT_FAILURE);
133258420Sdteske		    break;
134258420Sdteske	    }
135258420Sdteske
136258420Sdteske   if (0 <= quiet)
137258420Sdteske       if (!a)
138258420Sdteske	    VOID scanfile(stdin, (char*)0, quiet);
139258420Sdteske       else
140258420Sdteske	    do {
141258420Sdteske		if (!(fp = fopen(a, FOPEN_RB))) {
142258420Sdteske		    reportError(a);
143238438Sdteske		    status = EXIT_FAILURE;
144238438Sdteske		} else if (
145238438Sdteske		    scanfile(fp, a, quiet) != 0
146238438Sdteske		    || (argv[1]  &&  putchar('\n') == EOF)
147238438Sdteske		)
148238438Sdteske		    break;
149238438Sdteske	    } while ((a = *++argv));
150238438Sdteske
151238438Sdteske   if (ferror(stdout) || fclose(stdout)!=0) {
152238438Sdteske      reportError("standard output");
153238438Sdteske      status = EXIT_FAILURE;
154238438Sdteske   }
155238438Sdteske   exitmain(status);
156238438Sdteske}
157238438Sdteske
158238438Sdteske#if RCS_lint
159238438Sdteske#	define exiterr identExit
160238438Sdteske#endif
161238438Sdteske	void
162238438Sdteskeexiterr()
163238438Sdteske{
164238438Sdteske	_exit(EXIT_FAILURE);
165238438Sdteske}
166238438Sdteske
167238438Sdteske	static void
168238438SdteskereportError(s)
169238438Sdteske	char const *s;
170238438Sdteske{
171238438Sdteske	int e = errno;
172238438Sdteske	VOID fprintf(stderr, "%s error: ", cmdid);
173238438Sdteske	errno = e;
174238438Sdteske	perror(s);
175238438Sdteske}
176238438Sdteske
177238438Sdteske
178238438Sdteske	static int
179238438Sdteskescanfile(file, name, quiet)
180238438Sdteske	register FILE *file;
181238438Sdteske	char const *name;
182241700Sdteske	int quiet;
183238438Sdteske/* Function: scan an open file with descriptor file for keywords.
184238438Sdteske * Return -1 if there's a write error; exit immediately on a read error.
185238438Sdteske */
186238438Sdteske{
187238438Sdteske   register int c;
188238438Sdteske
189238438Sdteske   if (name) {
190238438Sdteske      VOID printf("%s:\n", name);
191238438Sdteske      if (ferror(stdout))
192238438Sdteske	 return -1;
193238438Sdteske   } else
194238438Sdteske      name = "standard input";
195238438Sdteske   c = 0;
196238438Sdteske   while (c != EOF  ||  ! (feof(file)|ferror(file))) {
197260678Sdteske      if (c == KDELIM) {
198263980Sdteske	 if ((c = match(file)))
199238438Sdteske	    continue;
200238438Sdteske	 if (ferror(stdout))
201247280Sdteske	    return -1;
202247280Sdteske	 quiet = true;
203247280Sdteske      }
204247280Sdteske      c = getc(file);
205247280Sdteske   }
206247280Sdteske   if (ferror(file) || fclose(file) != 0) {
207247280Sdteske      reportError(name);
208247280Sdteske      /*
209247280Sdteske      * 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);
214247280Sdteske      exiterr();
215247280Sdteske   }
216247280Sdteske   if (!quiet)
217247280Sdteske      VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
218247280Sdteske   return 0;
219247280Sdteske}
220247280Sdteske
221247280Sdteske
222247280Sdteske
223247280Sdteske	static int
224247280Sdteskematch(fp)   /* group substring between two KDELIM's; then do pattern match */
225247280Sdteske   register FILE *fp;
226247280Sdteske{
227247280Sdteske   char line[BUFSIZ];
228247280Sdteske   register int c;
229247280Sdteske   register char * tp;
230247280Sdteske
231247280Sdteske   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: case DIGIT:
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*/
267249751Sdteske   *tp   = '\0';
268249751Sdteske   VOID printf("     %c%s\n", KDELIM, line);
269250702Sdteske   return 0;
270249751Sdteske}
271249751Sdteske