ident.c revision 11894
1/* Identify RCS keyword strings in files.  */
2
3/* Copyright 1982, 1988, 1989 Walter Tichy
4   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
5   Distributed under license by the Free Software Foundation, Inc.
6
7This file is part of RCS.
8
9RCS is free software; you can redistribute it and/or modify
10it under the terms of the GNU General Public License as published by
11the Free Software Foundation; either version 2, or (at your option)
12any later version.
13
14RCS is distributed in the hope that it will be useful,
15but WITHOUT ANY WARRANTY; without even the implied warranty of
16MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17GNU General Public License for more details.
18
19You should have received a copy of the GNU General Public License
20along with RCS; see the file COPYING.
21If not, write to the Free Software Foundation,
2259 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23
24Report problems and direct all questions to:
25
26    rcs-bugs@cs.purdue.edu
27
28*/
29
30/*
31 * $Log: ident.c,v $
32 * Revision 5.9  1995/06/16 06:19:24  eggert
33 * Update FSF address.
34 *
35 * Revision 5.8  1995/06/01 16:23:43  eggert
36 * (exiterr, reportError): New functions, needed for DOS and OS/2 ports.
37 * (scanfile): Use them.
38 *
39 * Revision 5.7  1994/03/20 04:52:58  eggert
40 * Remove `exiting' from identExit.
41 *
42 * Revision 5.6  1993/11/09 17:40:15  eggert
43 * Add -V.
44 *
45 * Revision 5.5  1993/11/03 17:42:27  eggert
46 * Test for char == EOF, not char < 0.
47 *
48 * Revision 5.4  1992/01/24  18:44:19  eggert
49 * lint -> RCS_lint
50 *
51 * Revision 5.3  1991/09/10  22:15:46  eggert
52 * Open files with FOPEN_R, not FOPEN_R_WORK,
53 * because they might be executables, not working files.
54 *
55 * Revision 5.2  1991/08/19  03:13:55  eggert
56 * Report read errors immediately.
57 *
58 * Revision 5.1  1991/02/25  07:12:37  eggert
59 * Don't report empty keywords.  Check for I/O errors.
60 *
61 * Revision 5.0  1990/08/22  08:12:37  eggert
62 * Don't limit output to known keywords.
63 * Remove arbitrary limits and lint.  Ansify and Posixate.
64 *
65 * Revision 4.5  89/05/01  15:11:54  narten
66 * changed copyright header to reflect current distribution rules
67 *
68 * Revision 4.4  87/10/23  17:09:57  narten
69 * added exit(0) so exit return code would be non random
70 *
71 * Revision 4.3  87/10/18  10:23:55  narten
72 * Updating version numbers. Changes relative to 1.1 are actually relative
73 * to 4.1
74 *
75 * Revision 1.3  87/07/09  09:20:52  trinkle
76 * Added check to make sure there is at least one arg before comparing argv[1]
77 * with "-q".  This necessary on machines that don't allow dereferncing null
78 * pointers (i.e. Suns).
79 *
80 * Revision 1.2  87/03/27  14:21:47  jenkins
81 * Port to suns
82 *
83 * Revision 4.1  83/05/10  16:31:02  wft
84 * Added option -q and input from reading stdin.
85 * Marker matching is now done with trymatch() (independent of keywords).
86 *
87 * Revision 3.4  83/02/18  17:37:49  wft
88 * removed printing of new line after last file.
89 *
90 * Revision 3.3  82/12/04  12:48:55  wft
91 * Added LOCKER.
92 *
93 * Revision 3.2  82/11/28  18:24:17  wft
94 * removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
95 *
96 * Revision 3.1  82/10/13  15:58:51  wft
97 * fixed type of variables receiving from getc() (char-->int).
98*/
99
100#include  "rcsbase.h"
101
102static int match P((FILE*));
103static int scanfile P((FILE*,char const*,int));
104static void reportError P((char const*));
105
106mainProg(identId, "ident", "$Id: ident.c,v 5.9 1995/06/16 06:19:24 eggert Exp $")
107/*  Ident searches the named files for all occurrences
108 *  of the pattern $@: text $ where @ is a keyword.
109 */
110
111{
112   FILE *fp;
113   int quiet = 0;
114   int status = EXIT_SUCCESS;
115   char const *a;
116
117   while ((a = *++argv)  &&  *a=='-')
118	while (*++a)
119	    switch (*a) {
120		case 'q':
121		    quiet = 1;
122		    break;
123
124		case 'V':
125		    VOID printf("RCS version %s\n", RCS_version_string);
126		    quiet = -1;
127		    break;
128
129		default:
130		    VOID fprintf(stderr,
131			"ident: usage: ident -{qV} [file...]\n"
132		    );
133		    exitmain(EXIT_FAILURE);
134		    break;
135	    }
136
137   if (0 <= quiet)
138       if (!a)
139	    VOID scanfile(stdin, (char*)0, quiet);
140       else
141	    do {
142		if (!(fp = fopen(a, FOPEN_RB))) {
143		    reportError(a);
144		    status = EXIT_FAILURE;
145		} else if (
146		    scanfile(fp, a, quiet) != 0
147		    || (argv[1]  &&  putchar('\n') == EOF)
148		)
149		    break;
150	    } while ((a = *++argv));
151
152   if (ferror(stdout) || fclose(stdout)!=0) {
153      reportError("standard output");
154      status = EXIT_FAILURE;
155   }
156   exitmain(status);
157}
158
159#if RCS_lint
160#	define exiterr identExit
161#endif
162	void
163exiterr()
164{
165	_exit(EXIT_FAILURE);
166}
167
168	static void
169reportError(s)
170	char const *s;
171{
172	int e = errno;
173	VOID fprintf(stderr, "%s error: ", cmdid);
174	errno = e;
175	perror(s);
176}
177
178
179	static int
180scanfile(file, name, quiet)
181	register FILE *file;
182	char const *name;
183	int quiet;
184/* Function: scan an open file with descriptor file for keywords.
185 * Return -1 if there's a write error; exit immediately on a read error.
186 */
187{
188   register int c;
189
190   if (name) {
191      VOID printf("%s:\n", name);
192      if (ferror(stdout))
193	 return -1;
194   } else
195      name = "standard input";
196   c = 0;
197   while (c != EOF  ||  ! (feof(file)|ferror(file))) {
198      if (c == KDELIM) {
199	 if ((c = match(file)))
200	    continue;
201	 if (ferror(stdout))
202	    return -1;
203	 quiet = true;
204      }
205      c = getc(file);
206   }
207   if (ferror(file) || fclose(file) != 0) {
208      reportError(name);
209      /*
210      * The following is equivalent to exit(EXIT_FAILURE), but we invoke
211      * exiterr to keep lint happy.  The DOS and OS/2 ports need exiterr.
212      */
213      VOID fflush(stderr);
214      VOID fflush(stdout);
215      exiterr();
216   }
217   if (!quiet)
218      VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
219   return 0;
220}
221
222
223
224	static int
225match(fp)   /* group substring between two KDELIM's; then do pattern match */
226   register FILE *fp;
227{
228   char line[BUFSIZ];
229   register int c;
230   register char * tp;
231
232   tp = line;
233   while ((c = getc(fp)) != VDELIM) {
234      if (c == EOF  &&  feof(fp) | ferror(fp))
235	 return c;
236      switch (ctab[c]) {
237	 case LETTER: case Letter:
238	    *tp++ = c;
239	    if (tp < line+sizeof(line)-4)
240	       break;
241	    /* fall into */
242	 default:
243	    return c ? c : '\n'/* anything but 0 or KDELIM or EOF */;
244      }
245   }
246   if (tp == line)
247      return c;
248   *tp++ = c;
249   if ((c = getc(fp)) != ' ')
250      return c ? c : '\n';
251   *tp++ = c;
252   while( (c = getc(fp)) != KDELIM ) {
253      if (c == EOF  &&  feof(fp) | ferror(fp))
254	    return c;
255      switch (ctab[c]) {
256	 default:
257	    *tp++ = c;
258	    if (tp < line+sizeof(line)-2)
259	       break;
260	    /* fall into */
261	 case NEWLN: case UNKN:
262	    return c ? c : '\n';
263      }
264   }
265   if (tp[-1] != ' ')
266      return c;
267   *tp++ = c;     /*append trailing KDELIM*/
268   *tp   = '\0';
269   VOID printf("     %c%s\n", KDELIM, line);
270   return 0;
271}
272