111894Speter/* Identify RCS keyword strings in files.  */
211894Speter
311894Speter/* Copyright 1982, 1988, 1989 Walter Tichy
411894Speter   Copyright 1990, 1991, 1992, 1993, 1994, 1995 Paul Eggert
59Sjkh   Distributed under license by the Free Software Foundation, Inc.
69Sjkh
79SjkhThis file is part of RCS.
89Sjkh
99SjkhRCS is free software; you can redistribute it and/or modify
109Sjkhit under the terms of the GNU General Public License as published by
119Sjkhthe Free Software Foundation; either version 2, or (at your option)
129Sjkhany later version.
139Sjkh
149SjkhRCS is distributed in the hope that it will be useful,
159Sjkhbut WITHOUT ANY WARRANTY; without even the implied warranty of
169SjkhMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
179SjkhGNU General Public License for more details.
189Sjkh
199SjkhYou should have received a copy of the GNU General Public License
2011894Speteralong with RCS; see the file COPYING.
2111894SpeterIf not, write to the Free Software Foundation,
2211894Speter59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
239Sjkh
249SjkhReport problems and direct all questions to:
259Sjkh
269Sjkh    rcs-bugs@cs.purdue.edu
279Sjkh
289Sjkh*/
299Sjkh
309Sjkh/*
3111894Speter * Revision 5.9  1995/06/16 06:19:24  eggert
3211894Speter * Update FSF address.
338858Srgrimes *
3411894Speter * Revision 5.8  1995/06/01 16:23:43  eggert
3511894Speter * (exiterr, reportError): New functions, needed for DOS and OS/2 ports.
3611894Speter * (scanfile): Use them.
3711894Speter *
3811894Speter * Revision 5.7  1994/03/20 04:52:58  eggert
3911894Speter * Remove `exiting' from identExit.
4011894Speter *
4111894Speter * Revision 5.6  1993/11/09 17:40:15  eggert
4211894Speter * Add -V.
4311894Speter *
4411894Speter * Revision 5.5  1993/11/03 17:42:27  eggert
4511894Speter * Test for char == EOF, not char < 0.
4611894Speter *
4711894Speter * Revision 5.4  1992/01/24  18:44:19  eggert
4811894Speter * lint -> RCS_lint
4911894Speter *
509Sjkh * Revision 5.3  1991/09/10  22:15:46  eggert
519Sjkh * Open files with FOPEN_R, not FOPEN_R_WORK,
529Sjkh * because they might be executables, not working files.
539Sjkh *
549Sjkh * Revision 5.2  1991/08/19  03:13:55  eggert
559Sjkh * Report read errors immediately.
569Sjkh *
579Sjkh * Revision 5.1  1991/02/25  07:12:37  eggert
589Sjkh * Don't report empty keywords.  Check for I/O errors.
599Sjkh *
609Sjkh * Revision 5.0  1990/08/22  08:12:37  eggert
619Sjkh * Don't limit output to known keywords.
629Sjkh * Remove arbitrary limits and lint.  Ansify and Posixate.
639Sjkh *
649Sjkh * Revision 4.5  89/05/01  15:11:54  narten
659Sjkh * changed copyright header to reflect current distribution rules
668858Srgrimes *
679Sjkh * Revision 4.4  87/10/23  17:09:57  narten
689Sjkh * added exit(0) so exit return code would be non random
698858Srgrimes *
709Sjkh * Revision 4.3  87/10/18  10:23:55  narten
719Sjkh * Updating version numbers. Changes relative to 1.1 are actually relative
729Sjkh * to 4.1
738858Srgrimes *
749Sjkh * Revision 1.3  87/07/09  09:20:52  trinkle
759Sjkh * Added check to make sure there is at least one arg before comparing argv[1]
769Sjkh * with "-q".  This necessary on machines that don't allow dereferncing null
779Sjkh * pointers (i.e. Suns).
788858Srgrimes *
799Sjkh * Revision 1.2  87/03/27  14:21:47  jenkins
809Sjkh * Port to suns
818858Srgrimes *
829Sjkh * Revision 4.1  83/05/10  16:31:02  wft
839Sjkh * Added option -q and input from reading stdin.
849Sjkh * Marker matching is now done with trymatch() (independent of keywords).
858858Srgrimes *
869Sjkh * Revision 3.4  83/02/18  17:37:49  wft
879Sjkh * removed printing of new line after last file.
889Sjkh *
899Sjkh * Revision 3.3  82/12/04  12:48:55  wft
909Sjkh * Added LOCKER.
919Sjkh *
929Sjkh * Revision 3.2  82/11/28  18:24:17  wft
939Sjkh * removed Suffix; added ungetc to avoid skipping over trailing KDELIM.
949Sjkh *
959Sjkh * Revision 3.1  82/10/13  15:58:51  wft
969Sjkh * fixed type of variables receiving from getc() (char-->int).
979Sjkh*/
989Sjkh
999Sjkh#include  "rcsbase.h"
1009Sjkh
1019Sjkhstatic int match P((FILE*));
10211894Speterstatic int scanfile P((FILE*,char const*,int));
10311894Speterstatic void reportError P((char const*));
1049Sjkh
10550472SpetermainProg(identId, "ident", "$FreeBSD$")
1069Sjkh/*  Ident searches the named files for all occurrences
10711894Speter *  of the pattern $@: text $ where @ is a keyword.
1089Sjkh */
1099Sjkh
1109Sjkh{
1119Sjkh   FILE *fp;
11211894Speter   int quiet = 0;
1139Sjkh   int status = EXIT_SUCCESS;
11411894Speter   char const *a;
1159Sjkh
11611894Speter   while ((a = *++argv)  &&  *a=='-')
11711894Speter	while (*++a)
11811894Speter	    switch (*a) {
11911894Speter		case 'q':
12011894Speter		    quiet = 1;
12111894Speter		    break;
1229Sjkh
12311894Speter		case 'V':
12411894Speter		    VOID printf("RCS version %s\n", RCS_version_string);
12511894Speter		    quiet = -1;
12611894Speter		    break;
1279Sjkh
12811894Speter		default:
12911894Speter		    VOID fprintf(stderr,
13011894Speter			"ident: usage: ident -{qV} [file...]\n"
13111894Speter		    );
13211894Speter		    exitmain(EXIT_FAILURE);
13311894Speter		    break;
13411894Speter	    }
13511894Speter
13611894Speter   if (0 <= quiet)
13711894Speter       if (!a)
13811894Speter	    VOID scanfile(stdin, (char*)0, quiet);
13911894Speter       else
14011894Speter	    do {
14111894Speter		if (!(fp = fopen(a, FOPEN_RB))) {
14211894Speter		    reportError(a);
14311894Speter		    status = EXIT_FAILURE;
14411894Speter		} else if (
14511894Speter		    scanfile(fp, a, quiet) != 0
14611894Speter		    || (argv[1]  &&  putchar('\n') == EOF)
14711894Speter		)
14811894Speter		    break;
14911894Speter	    } while ((a = *++argv));
15011894Speter
1519Sjkh   if (ferror(stdout) || fclose(stdout)!=0) {
15211894Speter      reportError("standard output");
1539Sjkh      status = EXIT_FAILURE;
1549Sjkh   }
1559Sjkh   exitmain(status);
1569Sjkh}
1579Sjkh
15811894Speter#if RCS_lint
15911894Speter#	define exiterr identExit
1609Sjkh#endif
16111894Speter	void
16211894Speterexiterr()
16311894Speter{
16411894Speter	_exit(EXIT_FAILURE);
16511894Speter}
1669Sjkh
16711894Speter	static void
16811894SpeterreportError(s)
16911894Speter	char const *s;
17011894Speter{
17111894Speter	int e = errno;
17211894Speter	VOID fprintf(stderr, "%s error: ", cmdid);
17311894Speter	errno = e;
17411894Speter	perror(s);
17511894Speter}
1769Sjkh
17711894Speter
17811894Speter	static int
1799Sjkhscanfile(file, name, quiet)
1809Sjkh	register FILE *file;
1819Sjkh	char const *name;
1829Sjkh	int quiet;
1839Sjkh/* Function: scan an open file with descriptor file for keywords.
18411894Speter * Return -1 if there's a write error; exit immediately on a read error.
1859Sjkh */
1869Sjkh{
1879Sjkh   register int c;
1889Sjkh
18911894Speter   if (name) {
1909Sjkh      VOID printf("%s:\n", name);
19111894Speter      if (ferror(stdout))
19211894Speter	 return -1;
19311894Speter   } else
19411894Speter      name = "standard input";
1959Sjkh   c = 0;
19611894Speter   while (c != EOF  ||  ! (feof(file)|ferror(file))) {
1979Sjkh      if (c == KDELIM) {
1989Sjkh	 if ((c = match(file)))
1999Sjkh	    continue;
20011894Speter	 if (ferror(stdout))
20111894Speter	    return -1;
2029Sjkh	 quiet = true;
2039Sjkh      }
2049Sjkh      c = getc(file);
2059Sjkh   }
20611894Speter   if (ferror(file) || fclose(file) != 0) {
20711894Speter      reportError(name);
20811894Speter      /*
20911894Speter      * The following is equivalent to exit(EXIT_FAILURE), but we invoke
21011894Speter      * exiterr to keep lint happy.  The DOS and OS/2 ports need exiterr.
21111894Speter      */
21211894Speter      VOID fflush(stderr);
21311894Speter      VOID fflush(stdout);
21411894Speter      exiterr();
21511894Speter   }
2169Sjkh   if (!quiet)
2179Sjkh      VOID fprintf(stderr, "%s warning: no id keywords in %s\n", cmdid, name);
21811894Speter   return 0;
2199Sjkh}
2209Sjkh
2219Sjkh
2229Sjkh
2239Sjkh	static int
2249Sjkhmatch(fp)   /* group substring between two KDELIM's; then do pattern match */
2259Sjkh   register FILE *fp;
2269Sjkh{
2279Sjkh   char line[BUFSIZ];
2289Sjkh   register int c;
2299Sjkh   register char * tp;
2309Sjkh
2319Sjkh   tp = line;
2329Sjkh   while ((c = getc(fp)) != VDELIM) {
23311894Speter      if (c == EOF  &&  feof(fp) | ferror(fp))
2349Sjkh	 return c;
2359Sjkh      switch (ctab[c]) {
23687625Speter	 case LETTER: case Letter: case DIGIT:
2379Sjkh	    *tp++ = c;
2389Sjkh	    if (tp < line+sizeof(line)-4)
2399Sjkh	       break;
2409Sjkh	    /* fall into */
2419Sjkh	 default:
2429Sjkh	    return c ? c : '\n'/* anything but 0 or KDELIM or EOF */;
2439Sjkh      }
2449Sjkh   }
2459Sjkh   if (tp == line)
2469Sjkh      return c;
2479Sjkh   *tp++ = c;
2489Sjkh   if ((c = getc(fp)) != ' ')
2499Sjkh      return c ? c : '\n';
2509Sjkh   *tp++ = c;
2519Sjkh   while( (c = getc(fp)) != KDELIM ) {
25211894Speter      if (c == EOF  &&  feof(fp) | ferror(fp))
2539Sjkh	    return c;
2549Sjkh      switch (ctab[c]) {
2559Sjkh	 default:
2569Sjkh	    *tp++ = c;
2579Sjkh	    if (tp < line+sizeof(line)-2)
2589Sjkh	       break;
2599Sjkh	    /* fall into */
2609Sjkh	 case NEWLN: case UNKN:
2619Sjkh	    return c ? c : '\n';
2629Sjkh      }
2639Sjkh   }
2649Sjkh   if (tp[-1] != ' ')
2659Sjkh      return c;
2669Sjkh   *tp++ = c;     /*append trailing KDELIM*/
2679Sjkh   *tp   = '\0';
26811894Speter   VOID printf("     %c%s\n", KDELIM, line);
2699Sjkh   return 0;
2709Sjkh}
271