/*  ident

    copyright (c) 1985, Gimpel Software
    James F. Gimpel, President
    3207 Hogarth Lane
    Collegeville, PA 19426
    (215) 584-4261

    Specializing in compilers, interpreters, and translators for the
    IBM PC and Unix worlds.

    This product has been released to public domain for non-profit
    use only. 

    The purpose of this program is to display or replace a given
    identifier in a file.  It will skip over any identifier in which
    the given identifier is embedded.  Thus if the given identifier
    is "i" then the identifiers "hi" and "in" will be passed over.
    identifiers follow the rules of C but other than this the program
    knows nothing about C syntax.  In particular, it knows nothing
    about comments or quotes.

    To find out how to use ident invoke it at command level with
    no arguments.
				    JFG   11/11/84
 */

#include "stdio.h"

typedef char *STRING;
#define BUFLEN 100
#define id_begin(c) (isalpha(c) || c == '_')
#define id_cont(c) (isalnum(c) || c == '_')
#define copyc(c) if(copy_flag) putc(c,stdout)
extern STRING fgets();

int copy_flag;

main(argc,argv)
    int argc;
    STRING  *argv;
    {
    int  i, j, n;
    int c;
    FILE  *f;
    char buf[BUFLEN], newbuf[BUFLEN];
    char first;
    STRING  pat, pattern, replacement, p, rest;
    int match, rest_len;
    int lineno = 0;

    if( argc < 3 )
	{
	help();
	}
    f = fopen( argv[1], "r" );
    if( !f ) error( "Can't open input file" );
    pattern = argv[2];
    first = pattern[0];
    rest = pattern+1;
    rest_len = strlen(rest);
    copy_flag = argc > 3;
    replacement = copy_flag ? argv[3] : "";
    while( fgets(buf, BUFLEN, f ) )
	{
	lineno++;
	p = buf;
	match = 0;
	while( c = *p++ )
	    {
	    if( c == first && strinb( rest, p ) &&
		!id_cont( p[rest_len] )  &&
		!after_id( p-1, buf ) )
		{
		match = 1;
		copys( replacement );
		p += rest_len ;
		}
	    else copyc(c);
	    }
	if( match && !copy_flag )
	    printf( "%d\t%s", lineno, buf );
	}
    }

/* copys(s) copies string s to stnd out (conditionally) */

copys(s)
    STRING s;
    {
    copyns( s, strlen(s) );
    }

/* copyns(s) copies n characters from string s to stnd out
   (conditionally). */

copyns(s,n)
    STRING s;
    int n;
    {
    while( n-- > 0 ) copyc(*s++);
    }

/* error(s) writes an error message to standard error and
   quits (could have used abort instead).
 */

error(s)
    STRING s;
    {
    fprintf( stderr, "ident: %s\n", s );
    exit();
    }

/*  strinb( s, buf ) returns true (1) if string s lies in buffer
    buf  */

strinb(s,buf)
    STRING s, buf;
    {
    while( *s )
	if( *s++ != *buf++ ) return 0;
    return 1;
    }

/*  after_id( p, buf ) will return true (1) if an identifier
    precedes pointer p. buf is the beginning of the buffer into
    which p points.
 */

after_id( p, buf )
    STRING p, buf;
    {
    char c;

    while( p > buf )
	{
	c = *--p;
	if( id_begin(c) ) return 1;
	if( !id_cont(c) ) return 0;
	}
    return 0;
    }

help()
    {
    p( "Format:" );
    p( "" );
    p( "ident  file-name  identifier  [replacement]" );
    p( "" );
    p( "will search the file for the occurrence of the identifier" );
    p( "given as argument.  If a replacement is provided it will" );
    p( "output the file onto standard out with all instances of the" );
    p( "identifier replaced by the replacement.  If the replacement" );
    p( "is not given, all lines bearing the identifier are printed" );
    p( "to standard out together with the line numbers." );
    p( "The significance of this program is that only identifiers" );
    p( "will be matched.  Thus, if 'a' is given 'abc' or 'dada' " );
    p( "will not match." );
    exit();
    }
    
p(s) STRING s;    
    { fprintf( stderr, "\t%s\n" , s); }
                          