/*----------------------------------------------------------------------------
/ cio.c - This is a C program to replace the over-grown shell script
/ that started as something small...
/
/ Authors:	Jason P. Winters and Craig J. Kim
/
/  We hereby declare this software to be Public Domain.  That means we don't
/  care what you do with it.  We _would_ prefer that you at least leave our
/  names in it, as befits Authors, but we can't force you.  Of course, since
/  it's public domain, all risks/gains using it are your problems.  You don't
/  have any reason to call us up and complain about it deleting 30megs of
/  your source code without telling you. ( Software should at least *tell* you
/  when it does something like that, right? )  :)
/
/ Start Date:	November 23, 1988
/ Revisions:	29-Nov-88 jpw - initial coding completed.
/		29-Nov-88 cjk - rewrite of front-end logic
/		30-Nov-88 jpw - Added signals, cleanup of temp files.
/		30-Nov-88 cjk - added -N option
/		01-Dec-88 cjk - added usage
/		01-Dec-88 jpw - added SUID controls for secure files.
/		02-Dec-88 jpw - added -A option
/		05-Dec-88 cjk - added '~' commands for message entering
/		05-Dec-88 cjk - fixed cp to check file status before copying
/		06-Dec-88 cjk - use separate process for user file input
/		06-Dec-88 jpw - source now passes System V.2 lint
/		07-Dec-88 cjk - added environment variable check routine
/		20-Dec-88 cjk - chmod files in source dir to 0640
/		21-Dec-88 cjk - put RCS header if not exists
/		08-Mar-89 cjk - SCCS version
/		02-Oct-89 jpw - Fixed parsing for header type to insert
/		                Fixed small bug in directory creation routine
/		10-May-90 jpw - Changed st += sprintf() code to allow for
/		                broken sprint() calls.  Failed on Sun
/		                machines.
/
/ Known bugs:
/       On some systems, the Control-D as end of input in the log entry
/       routines will cause stdin to be closed, which means the next call
/       to get an entry (such as a Title file) will fail.  A call to clearerr()
/       has been added to fix this, but it has not been tested.
/
/ To be done:
/	1. When -U is used, the destination directory should be created if
/	   it does not exist already.  Also, the file mode should be changed to
/	   0640 so that overwriting is not possible by anyone other than
/	   $RCSOWN for security.
/	2. Full "interactive" mode support
/   3. Actually use the SCCS version. (Ugh!)
/---------------------------------------------------------------------------*/

/*#define DEBUG		/* wanna know what's goin' on? */
/*#define INTERACTIVE	/* enable -I option (incomplete) */
/*#define void	int	/* if system can't handle void types. */
#define V_RCS		/* RCS version */
/*#define V_SCCS		/* SCCS version */

/*--------------------------------------------------- includes -------------*/
#ifdef __CRTRSXNT__
#include <crtrsxnt.h>
#endif
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>

#ifdef __EMX__
#define getcwd _getcwd2
#define mkdir(d) mkdir (d, 0755)
#endif

#ifndef OS2
#define stricmp   strcmp
#define strnicmp  strncmp
#endif

/*--------------------------------------------------- externals ------------*/
extern FILE *fopen();               /* open a stream (should be in stdio.h) */
extern FILE *popen();                 /* open a pipe (should be in stdio.h) */
extern char *getenv();                      /* read an environment variable */
extern char *tmpnam();                      /* create a temporary file name */
extern char *mktemp();                  /* create temp file with a template */
extern char *malloc();                        /* allocate a chunk of memory */
extern char *getcwd();                             /* get current directory */
extern int  unlink();                             /* unlink (delete) a file */
extern void exit();                            /* define these for LINT!!!! */
extern void perror();                          /* define these for LINT!!!! */
extern void free();                            /* define these for LINT!!!! */

/*--------------------------------------------------- defines --------------*/
#ifndef TRUE
# define TRUE	1
# define FALSE	0
#endif

#define TYPE_LOG	0		/* log message */
#define TYPE_TITLE	1		/* title message */
#define MAX_LOG		1020		/* max # of characters for a log */

#define VL(buf)     (buf), (int) sizeof((buf))       /* shorten strrd calls */

/*--------------------------------------------------- forwards -------------*/
void usage(/*_ void _*/);                    /* print program usage message */
int  getfinput(/*_ char *name, int type _*/);             /* get title file */
int  child_getfinput(/*_ char *name, int type _*/);      /* actual get file */
void doincmds(/*_ char *argv[], int argc, int in _*/); /* perform user wish */
void do_ciodir(/*_ char *filenm _*/);    /* do user wish in recursive if -R */
int  cio(/*_ char *filenme _*/);                          /* the work horse */
int  addrcsext(/*_ char *fname _*/);         /* add RCS file name extension */
int  rmrcsext(/*_ char *fname _*/);       /* remove RCS file name extension */
void inshdr(/*_ char *fname _*/);                      /* insert RCS header */
int  makedir(/*_ char *path _*/);    /* make a directory and all its parent */
char *strstr(/*_ char *s1, char *s2 _*/);  /* find a string within a string */
char *strlwr(/*_ char *s _*/);               /* convert upper case to lower */
int  asciifile(/*_ char *filename _*/);                  /* check file type */
int  rcsfile(/*_ char *filename _*/);             /* check file is RCS file */
int  strrd(/*_ char *buf, int max_size,  FILE *fle _*/);/* read from a file */
void getdir(/*_ void _*/);        /* obtain necessary directory information */
int  fix_envstr(/*_ char *cs _*/);  /* remove leading/trailing blanks, etc. */
void getrcsdir(/*_ char *dir _*/);          /* build rcs dir out of working */
void getworkdir(/*_ char *dir _*/);         /* build working dir out of rcs */
void getsrcdir(/*_ char *dir _*/);       /* build source dir out of working */
char *justname(/*_ char *fpath _*/);        /* return just filename portion */
char *memalloc(/*_ int size _*/);  /* allocate memory and check for success */
void sigcleanup(/*_ void _*/);                 /* cleanup in case interrupt */
void get_final_id(/*_ void _*/);           /* get user id of RCS file owner */
int  nextent(/*_ FILE *fp _*/);    /* read next entry from /etc/passwd file */

/*--------------------------------------------------- globals --------------*/
#ifdef V_RCS
char *ci_cmd = "ci";                /* command to use to check in a module. */
char *co_cmd = "co";               /* command to use to check out a module. */
#else
char *ici_cmd = "admin";      /* command to check in a module for 1st time. */
char *ci_cmd = "delta";                    /* command to check in a module. */
char *co_cmd = "get";                     /* command to check out a module. */
#endif
char *currentdir;                                     /* current directory. */
char *homedir;                                    /* user's home directory. */
char *rcswrk;                                                  /* $RCSWORK. */
char *rcsown;                                                   /* $RCSOWN. */
char *rcsdir;                                                   /* $RCSDIR. */
char *srcdir;                                                   /* $RCSSRC. */
char *headdir;                                                 /* $RCSHEAD. */
#ifdef OS2
char *ciobinext;                                             /* $CIOBINEXT. */
#endif
char *path;                                                       /* $PATH. */
char *pwdfile = "/etc/passwd";                      /* default passwd file. */
char cioopt[100];                      /* large buffer for command options. */
char d_ent[258];                            /* small buffer for file reads. */
char final[400];                    /* final dir to pass on as destination. */
#ifdef V_SCCS
char finalfile[400];                  /* final file without SCCS extension. */
#endif
char logstr[MAX_LOG + 4];                            /* log string to pass. */
char title[100];                                     /* log string to pass. */
char cmdbuf[2400];                                     /* do a single file. */
char editfile[400];                  /* temp edit file, makes cleanup easy. */
char pwdname[20];                                    /* Name found in file. */
char ftypestr[82];                               /* command file(1) output. */
char cii = FALSE;                       /* if set, we are in check in mode. */
char recurse = FALSE;               /* if set, do recursive check in/out's. */
char usertitle = FALSE;                       /* user specified title file. */
char interactive = FALSE;                /* user friendly interactive mode. */
char allfiles = FALSE;                    /* set it TRUE to copy all files. */
char binfiles = FALSE;                    /* set TRUE to process all files. */
char insertheader = FALSE;     /* insert RCS header at the top of the file. */
#ifdef DEBUG
char noexec = TRUE;                     /* set it FALSE for execution mode. */
char verbose = TRUE;                                    /* be a chatterbox. */
#else
char noexec = FALSE;                  /* set it TRUE for no execution mode. */
char verbose = FALSE;                                  /* not a chatterbox. */
#endif
char updsrcdir = FALSE;           /* update master source directory on cii. */
#ifdef V_SCCS
char do_admin = FALSE;                    /* set it TRUE if first check in. */
#endif
char *prognam;                                     /* name of this program. */
struct stat filestat;                                  /* file status info. */
int s_currentdir;                           /* length of current directory. */
int s_homedir;                          /* length of user's home directory. */
int s_rcswrk;                                        /* length of $RCSWORK. */
int s_rcsdir;                                         /* length of $RCSDIR. */
int s_srcdir;                                         /* length of $RCSSRC. */
int s_path;                                             /* length of $PATH. */
int s_rcsown;                                         /* length of $RCSOWN. */
int s_headdir;                                       /* length of $RCSHEAD. */
int user_id;                   /* Effective user id to use if ROOT process. */
int real_user_id;                      /* Save buffer for original user_id. */
int do_unlink;  /* add -l option if not set to avoid unlink of source file. */

/*--------------------------------------------------- main() ----------------
/ where we begin!
/---------------------------------------------------------------------------*/
main(argc, argv)
int argc;
char *argv[];
{
	register int in;
	register char *cp;		/* used in string updates. */

#ifdef __EMX__
	_response(&argc, &argv);
	_wildcard(&argc, &argv);
	_emxload_env("RCSLOAD");
#endif

	prognam = justname(argv[0]);	/* program name */
#ifdef OS2
        if ( stricmp(prognam + 3, ".exe") == 0 )
          prognam[3] = 0;
#endif
	getdir();			/* go get the enviroment pointers. */

	/*
	 * figure what the user wants us to be by reading program name
	 */
	if (!stricmp("ciitest", prognam) || !stricmp("cootest", prognam))
	{
		getrcsdir(final, currentdir);	/* setup variables. */
		/*
		 * print out our variables here.
		*/
#ifdef V_RCS
		(void) printf("Homedir:%s:  rcsdir:%s:  rcswrk:%s:  rcssrc:%s: CIOBINEXT:%s:\n",
#else
		(void) printf("HOME:%s:  SCCSDIR:%s:  SCCSWRK:%s:  SCCSSRC:%s: CIOBINEXT:%s:\n",
#endif
				homedir ? homedir : "",
				rcsdir ? rcsdir : "",
				rcswrk ? rcswrk : "",
				srcdir ? srcdir : "",
				ciobinext ? ciobinext : "" );
		(void) printf("Final dir:%s:\n", final);
		return(0);
	}
	if (!stricmp("cii", prognam))	/* this is input.. */
		cii = TRUE;		/* show we are inputs. */
	else if (stricmp("coo", prognam))/* it's not this either.. */
	{
		(void) printf("Just what did you think this program was, anyway??\n");
		return(-1);
	}

#ifndef OS2        
	if (!(user_id = geteuid())) /* we are a root process.. */
	{
		(void) umask(027); /* set general mask to private modes. */
		real_user_id = getuid();
		if (cii)	/* only set this if we are in checkin mode. */
			get_final_id(); /* go get the final user ID for file creation. */
		else
			user_id = real_user_id;	/* otherwise, use owners real one. */
		(void) setuid(user_id); /* and, fix up the user id now. */
#ifdef DEBUG
		(void) printf("Using Uid:%d:\n", user_id);
#endif
	}
	else
		real_user_id = user_id;	/* else they should match. */
#else 
	real_user_id = 1;	/* else they should match. */
#endif

	/* prepare for disasters */
	(void) signal(SIGINT, sigcleanup);
#ifndef OS2
	(void) signal(SIGQUIT, sigcleanup);
#else
	(void) signal(SIGBREAK, sigcleanup); /* OS/2 */
	(void) signal(SIGTERM, sigcleanup);  /* signals */
#endif

        if ( argc <= 1 )
        {
	        usage();
	        return(0);
        }

	cp = cioopt;
	title[0] = '\0';  /* make sure no titles are required. */
	for (in = 1; argv[in][0] == '-'; in++) /* while chars here.. */
	{
		switch ((int) argv[in][1])	/* what option? */
		{
		case '?':		/* print program usage */
		case '-':
			usage();
			return(0);
		case 'A':		/* copy all files */
			allfiles = !allfiles;
            if( allfiles ) binfiles= FALSE;
			break;
#ifdef V_RCS
        case 'B':       /* process (not copy) binary files */
            binfiles = !binfiles;
            if( binfiles ) allfiles= FALSE;
            break;
#endif
#ifdef INTERACTIVE
		case 'I':		/* interactive mode */
			interactive = !interactive;
			break;
#endif
		case 'H':		/* RCS/SCCS header */
			insertheader = !insertheader;
			break;
		case 'N':		/* do not execute */
			noexec = !noexec;
			break;
		case 'R':		/* Recursion flag. */
			recurse = !recurse;
			break;
		case 'T':		/* get Title flag. */
			if(cii)	/* get a title file and name. */
				(void) getfinput(title, TYPE_TITLE);
			break;
		case 'U':		/* update working directory */
			updsrcdir = !updsrcdir;
			break;
		case 'V':		/* verbose */
			verbose = !verbose;
			break;
#ifdef V_RCS
		case 'm':		/* they gave us one */
#else
		case 'y':		/* they gave us one */
#endif
			(void) strcpy(logstr, " -");
			(void) strcat(logstr, &argv[in][1]); /* copy across. */
			break;
		case 't':		/* user gave us one */
			(void) strcpy(title, &argv[in][1]);
			usertitle = TRUE;
			break;
		default:		/* must be a ci or co command */
			(void) sprintf(cp, " %s", argv[in]); /* append */
			cp += strlen(cp); /* skip to end of string. */
			break;
		}
	}
#ifdef INTERACTIVE
	if (noexec && interactive)	/* resolve conflict of interest */
		interactive = FALSE;
#endif
	doincmds(argv, argc, in);	/* do the check in command. */
	if (title[0] && !usertitle)	/* if file is here. */
		(void) unlink(title);	/* zap it! */
	return(0);			/* we did it good! */
}

/*--------------------------------------------------- usage() ---------------
/ print program usage information
/---------------------------------------------------------------------------*/
void
usage()
{
	char *inout = cii ? "in" : "out";

#ifdef INTERACTIVE
#ifdef V_RCS
	(void) fprintf(stderr, "Usage: %s [-A|B] %s [-I] [-N] [-R] %s ",
#else
	(void) fprintf(stderr, "Usage: %s [-A] %s [-I] [-N] [-R] %s ",
#endif
#else
#ifdef V_RCS
	(void) fprintf(stderr, "Usage: %s [-A|B] %s [-N] [-R] %s ",
#else
	(void) fprintf(stderr, "Usage: %s [-A] %s [-N] [-R] %s ",
#endif
#endif
			prognam, cii ? "[-H] " : "",
				 cii ? "[-T] [-U] " : "");
	(void) fprintf(stderr, "[-V] [%s options] [[filename]...]\n",
			cii ? ci_cmd : co_cmd);
	(void) fprintf(stderr, "     -A : check %s all files (just copy binary files)\n", inout);
#ifdef V_RCS
	(void) fprintf(stderr, "     -B : check %s all files (use RCS -kb mode for binary files)\n", inout);
#endif
#ifdef INTERACTIVE
	(void) fprintf(stderr, "     -I : interactive mode\n");
#endif
	if (cii)
#ifdef V_RCS
		(void) fprintf(stderr, "     -H : attach RCS header\n");
#else
		(void) fprintf(stderr, "     -H : attach SCCS header\n");
#endif
	(void) fprintf(stderr, "     -N : no execute mode\n");
	(void) fprintf(stderr, "     -R : recursive check %s\n", inout);
	if (cii)
	{
		(void) fprintf(stderr, "     -T : create title file\n");
		(void) fprintf(stderr, "     -U : update source directory\n");
	}
	(void) fprintf(stderr, "     -V : verbose mode\n");
}

/*--------------------------------------------------- sigcleanup() ----------
/ cleanup before exiting.
/---------------------------------------------------------------------------*/
void
sigcleanup()
{
	(void) printf("\n[%s: Interrupted]\n", prognam);
	if (title[0] && !usertitle)	/* remove title file, if here. */
		(void) unlink(title);
	if (editfile[0])		/* if a temp file might be here. */
		(void) unlink(editfile);	/* attempt to remove it.*/
	exit(0);
}

/*--------------------------------------------------- doincmds() ------------
/ actual do routine
/---------------------------------------------------------------------------*/
void
doincmds(argv, argc, in)
char *argv[];
int argc, in;
{
	register char *cp;
	char entry[400];

	if (cii)
	{
		if (argc == in)		/* no arguments given */
		{
			do_ciodir(currentdir);
			return /* void */;
		}
		for ( ; in < argc; in++)
		{
#ifdef OS2
                        UnixFileName(argv[in]);
#endif
			(void) sprintf(entry, "%s%s", currentdir, argv[in]);
#ifdef DEBUG
			(void) printf("Processing %s\n", entry);
#endif
			if (stat(entry, &filestat))
			{
#ifdef DEBUG
				(void) printf("Unable to stat(2) %s\n", entry);
#endif
				continue;
			}
			do_unlink = (filestat.st_uid == real_user_id);
			if ((filestat.st_mode & S_IFMT) == S_IFDIR)
				do_ciodir(entry);
			else
				(void) cio(entry);
		}
	}
	else /* coo */
	{
		char *ep;

		getrcsdir(entry, currentdir);
		if (argc == in)
		{
			do_ciodir(entry);
			return /* void */;
		}
		for (ep = entry; *ep; ep++)
			;
		for ( ; in < argc; in++)
		{
#ifdef OS2
                        UnixFileName(argv[in]);
#endif
			(void) sprintf(ep, "%s", argv[in]);
#ifdef DEBUG
			(void) printf("Processing %s\n", entry);
#endif
			if (stat(entry, &filestat))
			{
				if (!addrcsext(entry))
					continue;
				if (stat(entry, &filestat))
					continue;
			}
			do_unlink = (filestat.st_uid == real_user_id);
			if ((filestat.st_mode & S_IFMT) == S_IFDIR)
				do_ciodir(entry);
			else
				(void) cio(entry);
		}

	}
	return /* void */;
}

/*--------------------------------------------------- do_ciodir() -----------
/ actual do routine - Warning: RECURSIVE
/---------------------------------------------------------------------------*/
void
do_ciodir(dir)
char *dir;
{
	FILE *pp;
	char *cmd, *entry;

        if ( dir[strlen(dir) - 1] == '/' )
          dir[strlen(dir) - 1] = 0;
#ifdef OS2
	(void) sprintf(cmd = memalloc(strlen(dir) + 10), "dir /b \"%s\"", dir);
#else
	(void) sprintf(cmd = memalloc(strlen(dir) + 4), "ls %s", dir);
#endif
	pp = popen(cmd, "r");
	free(cmd);
	if (!pp)
		return /* void */;
	entry = memalloc(strlen(dir) + sizeof(d_ent) + 1);
	while (strrd( VL(d_ent), pp) != -1)
	{

#ifdef OS2
        if (recurse)
		   (void) strcpy( entry,               d_ent);
        else
#endif
		(void) sprintf(entry, "%s/%s", dir, d_ent);
		if (stat(entry, &filestat))
		{
#ifdef DEBUG
			(void) printf("Unable to stat(2) %s\n", entry);
#endif
			continue;
		}
		do_unlink = (filestat.st_uid == real_user_id);
		if ((filestat.st_mode & S_IFMT) == S_IFDIR)
		{
			if (recurse)
				do_ciodir(entry);
			else
				continue;
		}
		else
			(void) cio(entry);
	}
	free(entry);
	(void) pclose(pp);
	return /* void */;
}

/*--------------------------------------------------- cio() -----------------
/ do this to file, if we can.
/---------------------------------------------------------------------------*/
int
cio(filename)
char *filename;
{
	register char *cp, *st;		/* some char pointers */
	int do_copy = FALSE;		/* do cp(1) */
    int do_ibin = FALSE;        /* do initialize for binary file */
	char titlest[100];		/* for file names. */

	if (cii)
	{
		if (!asciifile(filename))	/* if not ascii file */
		{
            if (binfiles)          /* process all files ? */
            {
                do_ibin= TRUE;
                if (insertheader)
                {
                   inshdr(filename);
                }
            }
			else if (allfiles)
            {
                do_copy= TRUE;
            }
            else
            {
				return(FALSE);	/* don't if not forced copy */
            }
		}
		else if (insertheader)		/* inserting default header */
		{
			inshdr(filename);
		}
	}
	else if (!rcsfile(filename))		/* not an RCS file */
	{
		if (!allfiles)
			return(FALSE);
		do_copy = TRUE;			/* simply cp(1) it */
	}

	if (cii && !do_copy && !logstr[0]) /* we don't have a log entry yet. */
	{
		if (noexec)
		{
			(void) printf("Logfile entry bypassed.\n");
			(void) strcpy(logstr, "");	/* fake it */
		}
		else if (!getfinput(titlest, TYPE_LOG)) /* if we entered anything */
		{
			FILE *fp;
			char buf[100];
			register char *bp;
			register int numchars = 0;

			if (fp = fopen(titlest, "r"))
			{
				st = logstr;
#ifdef V_RCS
				(void) sprintf(st, " -m\"");
#else
				(void) sprintf(st, " -y\"");
#endif
				st += strlen(st); /* skip to end of string. */
				while (strrd( VL(buf), fp) != -1)
				{	/*
					 * escape quotes (") characters
					 */
					bp = buf;
					while (*bp)
					{
						if (*bp == '"')
							if (numchars < MAX_LOG)
							{
								numchars++;
								*st++ = '\\'; /*escape*/
							}
						if (numchars < MAX_LOG)
						{
							numchars++;
							*st++ = *bp++; /* append */
						}
					}
					*st++ = '\n'; /* add end of line now. */
				}
                if (numchars==0) *st++= '.'; /* to avoid -m"" */
				if (*(st - 1) == '\n')
					*(st - 1) = '"';
				else
					*st++ = '"';
				*st = '\0';
				(void) fclose(fp);
				if (numchars >= MAX_LOG)
					(void) printf("Log entry truncated.\n");
			}
			(void) unlink(titlest); /* cleanup. */
		}
	}

	titlest[0] = '\0'; /* cleanup string reference. */
	if (cii)
		getrcsdir(final, filename);
	else
		getworkdir(final, filename);

	if (st = strrchr(final, '/'))	/* if this has a sub dir. */
	{
		*st = '\0';	/* terminate it at the directoy level. */
		if (access(final, 0))	/* it's not here... */
			if (!makedir(final))	/* so try and make it. */
			{
				(void) printf("Could not create directory: %s:\n", final);
				return(FALSE);
			}
		*st = '/';	/* restore the rest of the file name. */
	}

	if (cii)
	{
		if (!do_copy)	/* not in binary mode. */
		{	/*
			 * make the new file name and check if it's here
			 */
#ifdef V_SCCS
			(void) strcpy(finalfile, final);
#endif
#ifdef OS2
			char *rcsinit = getenv("RCSINIT");
			/* only add ,v if RCS itself is configured to use ,v */
			if (rcsinit && strstr(rcsinit, "-x,v/") != 0)
			  (void) addrcsext(final);
#else
			(void) addrcsext(final);
#endif
			if (!noexec && access(final, 0))
			{
				/*
				 * if not, need to get a title message for it
				 */
				if (!title[0]) /* no title file yet. */
					(void) getfinput(title, TYPE_TITLE); /* get one */
				if (title[0])  /* if we've one, build command */
					(void) sprintf(titlest, " -t%s ", title);
#ifdef V_SCCS
				do_admin = TRUE;	/* 1st check-in */
#endif
			}
#ifdef V_SCCS
			else
				do_admin = FALSE;
#endif
		}
		else if (noexec)
			(void) strcpy(titlest, " -tfile_name ");
	}
	else	/* check out mode. */
	{	/*
		 * find a comma to make the new name.
		 * if found one, strip it to make a new name.
		 */
		(void) rmrcsext(final);
	}

	/*
	 * Build command string
	 */
	if (do_copy)	/* we want to just copy the file now. */
	{
		if (!interactive && !noexec && !access(final, 0))
		{
			(void) printf("%s already exists.  Overwrite? (yes) ", final);
			(void) strrd( VL(cmdbuf), stdin);
			(void) strlwr(cmdbuf);
			if (strnicmp(cmdbuf, "yes", strlen(cmdbuf)))
				return(TRUE);
		}
		(void) sprintf(cmdbuf, "cp -p %s %s", filename, final); /* copy command */
	}
	else if (cii)
	{
#ifdef V_RCS
        if (do_ibin) 
        {
            (void) sprintf(cmdbuf,"rcs -i -kb %s %s %s",
                           titlest, filename, final );
            if (verbose)
			       (void) printf("%s command: %s:\n", prognam, cmdbuf);
            if (!noexec)
                   system( cmdbuf );
        }
		(void) sprintf(cmdbuf, "%s%s%s%s%s %s %s",
			ci_cmd, cioopt, titlest, logstr,
			do_unlink ? "" : " -l", filename, final);
#else
		st = cmdbuf;
		(void) sprintf(st, "cp %s %s; ", filename, finalfile);
		st += strlen(st); /* skip to end of string. */
		if (cp = strrchr(final, '/'))
		{
			*cp = '\0';
			(void) sprintf(st, "cd %s; ", final);
			st += strlen(st); /* skip to end of string. */
			*cp = '/';
		}
		if (do_admin)
		{
			(void) sprintf(st, "%s -i%s%s%s%s %s",
				ici_cmd, justname(finalfile),
				cioopt, titlest, logstr, justname(final));
			st += strlen(st); /* skip to end of string. */
		}
		else
		{
			(void) sprintf(st, "%s%s%s %s",
				ci_cmd, cioopt, logstr, justname(final));
			st += strlen(st); /* skip to end of string. */
		}
		if (do_unlink)
		{
			(void) sprintf(st, "; rm %s", filename);
			st += strlen(st); /* skip to end of string. */
		}
#endif
	}
	else	/* coo */
	{
#ifdef V_RCS
		(void) sprintf(cmdbuf, "%s%s %s %s",
				co_cmd, cioopt, filename, final);
#else
		st = cmdbuf;
		(void) sprintf(st, "rm -f %s; ", final);
		st += strlen(st); /* skip to end of string. */
		if (cp = strrchr(filename, '/'))
		{
			*cp = '\0';
			(void) sprintf(st, "cd %s; ", filename);
			st += strlen(st); /* skip to end of string. */
			*cp = '/';
		}
		addrcsext(filename);
		(void) sprintf(st, "%s%s %s", co_cmd, cioopt,
			justname(filename));
		st += strlen(st); /* skip to end of string. */
		if (cp = strrchr(final, '/'))
		{
			cp++;
			(void) sprintf(st, "; mv %s %s", cp, final);
			st += strlen(st); /* skip to end of string. */
		}
#endif
	}

#ifdef INTERACTIVE
	if (interactive)
	{
		char ans[20];
		int done = FALSE;

		do
		{
			(void) printf("%s\nExecute? (yes) ", cmdbuf);
			(void) strrd( VL(ans), stdin);
			st = ans;
			while (isspace(*st))
				st++;
			if (!*st)
				(void) strcpy(st, "yes");
			(void) strlwr(st);
			if (!strnicmp(st, "yes", strlen(st))) {
				done = TRUE;
				(void) system(cmdbuf);	/* do it. */
			}
			else if (*st == '?')
			{
				(void) printf("\n");
				(void) printf("yes     Do it\n");
				(void) printf("no      Don't do it\n");
				(void) printf("view    View current file\n");
				(void) printf("?       Print this message\n");
				(void) printf("\n");
			}
			else if (!strnicmp(st, "view", strlen(st)))
			{
				(void) printf("Not implemented yet!\n\n");
			}
			else /* take it as "no" */
				done = TRUE;
		} while (!done);
	}
	else
	{
#endif /* INTERACTIVE */
		if (verbose)
			(void) printf("%s command: %s:\n", prognam, cmdbuf);
		if (!noexec)
		{
			if (do_copy && !verbose)
				(void) printf("%s\n", cmdbuf);
			(void) system(cmdbuf);	/* do it. */
		}
		else if (!verbose)	/* if don't want to exec, don't */
			(void) printf("%s\n", cmdbuf);
#ifdef INTERACTIVE
	}
#endif /* INTERACTIVE */

	if (updsrcdir)			/* update source directory */
	{
		getsrcdir(final, filename);

	        if (st = strrchr(final, '/'))	/* if this has a sub dir. */
	        {
	        	*st = '\0';	/* terminate it at the directoy level. */
	        	if (access(final, 0))	/* it's not here... */
	        		if (!makedir(final))	/* so try and make it. */
	        		{
	        			(void) printf("Could not create directory: %s:\n", final);
	        			return(FALSE);
	        		}
	        	*st = '/';	/* restore the rest of the file name. */
	        }

		(void) sprintf(cmdbuf, "cp -p %s %s", filename, final);
		if (noexec)		/* don't actual do it */
			(void) printf("%s\n", cmdbuf);
		else
		{
			if (verbose)	/* speak, yo wise one! */
				(void) printf("%s command: %s:\n", prognam, cmdbuf);
			if (!stricmp(filename, final))
				(void) printf("Source and destination identical.  Not updated.\n");
			else
			{
				(void) chmod(final, 0640);
				(void) system(cmdbuf);	/* do it! */
			}
		}
	}
	return(TRUE);
}

/*--------------------------------------------------- addrcsext() -----------
/ add RCS file extension ",v" if there isn't one already
/---------------------------------------------------------------------------*/
int
addrcsext(fname)
char *fname;
{
	register char *cp;

#ifdef V_RCS
	for (cp = fname; *cp; cp++)
		;
	if (*--cp == 'v' && *(cp - 1) == ',')
		return(0);		/* already there */

	*++cp = ',';			/* add ",v" */
	*++cp = 'v';
	*++cp = '\0';
#else
	char t_name[20];

	cp = justname(fname);
	if (*cp == 's' && *(cp + 1) == '.')
		return(0);
	(void) strcpy(t_name, cp);
	*cp++ = 's';			/* add "s." in front of file name */
	*cp++ = '.';
	(void) strcpy(cp, t_name);
#endif
	return(1);
}

/*--------------------------------------------------- rmrcsext() ------------
/ remove RCS extension if there is one; returns 1 if remove, else 0
/---------------------------------------------------------------------------*/
int
rmrcsext(fname)
char *fname;
{
	register char *cp;

#ifdef V_RCS
	for (cp = fname; *cp; cp++)
		;
	if (*--cp == 'v' && *--cp == ',')
	{
		*cp = '\0';
		return(1);
	}
#else
	cp = justname(fname);
	if (*cp == 's' && *(cp + 1) == '.')
	{
		(void) strcpy(cp, cp + 2);
		return(1);
	}
#endif
	return(0);
}

/*--------------------------------------------------- inshdr() --------------
/ insert RCS header if none exists already
/---------------------------------------------------------------------------*/
void
inshdr(t_name)
char *t_name;
{
#	define FTYPE_C		0	/* C program text */
#	define FTYPE_S		1	/* assembly program text */
#	define FTYPE_SH		2	/* shell script */
#	define FTYPE_ROFF	3	/* nroff, tbl, eqn, etc */
#	define FTYPE_F		4	/* Fortran program text */
#	define FTYPE_DEFAULT	5	/* don't know */
#	define FTYPE_MK		6	/* makefile script */
#	define FTYPE_H		7	/* C header file text */

	static struct _ftype {
		char *keyword;		/* phrase may exist in file(1) output */
		char *header;		/* header template file name. */
	} ftype[] = {
#ifdef V_RCS
		{ "c program",	"rcshead.c"	},	/* FTYPE_C */
		{ "assembler",	"rcshead.s"	},	/* FTYPE_S */
		{ "command",	"rcshead.sh"	},	/* FTYPE_SH */
		{ "roff, tbl",	"rcshead.rof"	},	/* FTYPE_ROFF */
		{ "fortran",	"rcshead.f"	},	/* FTYPE_F */
		{ 0,		"rcshead"	},	/* FTYPE_DEFAULT */
		{ 0,		"rcshead.mk"	},	/* FTYPE_MK */
		{ 0,		"rcshead.h"	} };	/* FTYPE_H */
#else
		{ "c program",	"sccshead.c"	},	/* FTYPE_C */
		{ "assembler",	"sccshead.s"	},	/* FTYPE_S */
		{ "command",	"sccshead.sh"	},	/* FTYPE_SH */
		{ "roff, tbl",	"sccshead.rof"},	/* FTYPE_ROFF */
		{ "fortran",	"sccshead.f"	},	/* FTYPE_F */
		{ 0,		"sccshead"	},	/* FTYPE_DEFAULT */
		{ 0,		"sccshead.mk"	},	/* FTYPE_MK */
		{ 0,		"sccshead.h"	} };	/* FTYPE_H */
#endif /* V_RCS */
	static struct _fext {
		char *name;
		int type;
	} fext[] = {
		{ ".c",		FTYPE_C		},
		{ ".h",		FTYPE_H		},
		{ ".s",		FTYPE_S		},
		{ ".f",		FTYPE_F		},
		{ ".man",	FTYPE_ROFF	},
		{ ".mk",	FTYPE_MK	},
		{ ".1",		FTYPE_ROFF	},
		{ ".2",		FTYPE_ROFF	},
		{ ".3",		FTYPE_ROFF	},
		{ ".4",		FTYPE_ROFF	},
		{ ".5",		FTYPE_ROFF	},
		{ ".6",		FTYPE_ROFF	},
		{ ".7",		FTYPE_ROFF	},
		{ ".8",		FTYPE_ROFF	},
		{ ".9",		FTYPE_ROFF	},
		{ 0,		0		} };
	FILE *ifp, *ofp;
	char buf[4096], headfile[128], tempfile[20], fname[40];
	register int i, c, ft = FTYPE_DEFAULT, err=0;
	register char *ext;

	strcpy(fname, justname(t_name)); /* copy over only name. */

	if (!(ifp = fopen(t_name, "r")))	/* quickly check for RCS header */
		return;
					/* we are looking for "$Header" */
					/* within first 50 lines of the file */
	for (i = 0; strrd( VL(buf), ifp) > 0 && i < 50; i++)
#ifdef V_RCS
		if (strstr(buf, "$Header"))
#else
		if (strstr(buf, "#ident"))
#endif
		{
			(void) fclose(ifp);
			if (verbose)
#ifdef V_RCS
				(void) printf("%s already has a RCS header.\n",
#else
				(void) printf("%s already has a SCCS header.\n",
#endif /* V_RCS */
						t_name);
			return;
		}

	(void) fclose(ifp);
					/* examine file(1) output */
	for (i = 0; ftype[i].keyword; i++)
		if (strstr(ftypestr, ftype[i].keyword))
		{
			ft = i;
			break;		/* found one */
		}

	if (!ftype[i].keyword)		/* file(1) didn't help */
	{				/* examine file extension */
		if (ext = strrchr(fname, '.'))
		{
			for (i = 0; fext[i].name; i++)
				if (!stricmp(ext, fext[i].name))
					ft = fext[i].type;
		}
		else
		{			/* check if makefile script */
			(void) strcpy(buf, fname);
			(void) strlwr(buf);
			if (!strcmp(buf, "makefile") || !strcmp(buf, "Makefile"))
				ft = FTYPE_MK; /* If either of two fixed names.. */
		}
	}
	else if (ft == FTYPE_C)		/* see if source or header */
	{
		if ((ext = strrchr(fname, '.')) && !stricmp(ext, ".h"))
			ft = FTYPE_H;
	}
	if (verbose)			/* is this necessary */
		(void) printf("%s is type [%d]\n", fname, ft);
	if (noexec)			/* no execution mode */
		return;

	(void) sprintf(headfile, "%s%s", headdir, ftype[ft].header);
	if (!(ifp = fopen(headfile, "r")))
	{
		(void) printf("Unable to open header template file [%s]\n",
				headfile);
		return;
	}
	/* build a tmp file in the same directory as old file. */
	strcpy(tempfile, t_name);
	ext = justname(tempfile); /* find end of path. */
	*ext = '\0'; /* and terminate path there. */
	(void) strcat(tempfile, "ctXXXXXX"); /* add tmp name */
	(void) mktemp(tempfile);		/* generate temp file name */
	if (!(ofp = fopen(tempfile, "w")))	/* open temp file */
	{
		(void) printf("Unable to open temporary file [%s]\n", tempfile);
		(void) fclose(ifp);
		return;
	}
	while ((c = fgetc(ifp)) != EOF)		/* copy header first */
		(void) fputc(c, ofp);
	(void) fclose(ifp);
	if (!(ifp = fopen(t_name, "r")))		/* open check-in file */
	{
		(void) printf("Unable to open [%s] for read\n", t_name);
		(void) fclose(ofp);
		(void) unlink(tempfile);
		return;
	}
	while ((c = fgetc(ifp)) != EOF)		/* append to temp file */
		if(fputc(c, ofp) == EOF)
			err=1; /* couldn't write error. */
	(void) fclose(ifp);			/* done */
	(void) fclose(ofp);

/* ok.  It's hard to make sure that everthing has gone well; if we unlink
   the src file and can't link the temp file, we could lose everthing.
   So, if copy fails, leave temp file alone, as it may be the only copy
   we have left!  If the unlinking the original fails, we can remove the
   copy, as we don't need it.
*/
	if(!err && !unlink(t_name))	/* 'mv tempfile fname' */
	{           
#ifndef OS2                
		if(!link(tempfile, t_name)) /* 'cp tempfile t_name' */
			(void) unlink(tempfile);		/* 'rm tempfile' */
		else
			(void) printf("Link of %s and %s failed after removing %s.\n%s not removed.\n",
				tempfile, t_name, t_name, tempfile);
#else
		if(!rename(tempfile, t_name)) /* 'cp tempfile t_name' */
			(void) printf("Rename of %s to %s failed after removing %s.\n%s not removed.\n",
				tempfile, t_name, t_name, tempfile);
#endif
	}
	else
	{
		(void) unlink(tempfile);		/* 'rm tempfile' */
		(void) printf("Could not insert header into %s.  Copy failed.\n", t_name);
	}
}

/*--------------------------------------------------- makedir() -------------
/ make a directory path, with recursion.
/ returns TRUE if successful, FALSE otherwise.
/
/ This really needs to be re-written.  It works, but that's all I can really
/ say for it...  Hey, *I* don't have mkdir() calls!
/---------------------------------------------------------------------------*/
int
makedir(newpath)
char *newpath;		/* path name to make. */
{
	register char *st, *cp;

	if(!*newpath) return(FALSE); /* skip last directory attempt. */
	if (noexec)
	{
		(void) printf("mkdir: %s\n", newpath);
		return(TRUE);
	}
	else if(verbose)
		(void) printf("mkdir: %s\n", newpath);

	cp = memalloc(strlen(newpath) + 24);

	if (mkdir(newpath))			/* it failed.. */
	{
		(void) strcpy(cp, newpath);	/* get current one. */
		st = strrchr(cp, '/'); /* remove one more layer.. */
		*st = '\0';		/* terminate here. */
		if (makedir(cp) == FALSE)	/* try and build next level back. */
		{
			free(cp);
			return(FALSE);
		}
                /* ok, so.. it passed on back. Try this again. */
		if(mkdir(newpath))
		{
			free(cp);
			return(FALSE);
		}
	}

	free(cp);
	return(TRUE);
}

/*--------------------------------------------------- strstr() --------------
/ find a substring within a string
/---------------------------------------------------------------------------*/
#ifndef OS2
char *
strstr(s1, s2)
register char *s1, *s2;
{
	register int l;

	if (l = strlen(s2))
		for ( ; s1 = strchr(s1, s2[0]); s1++)
			if (memcmp(s1, s2, l) == 0)
				break;
	return(s1);
}

/*--------------------------------------------------- strlwr() ---------------
/ strlwr.c - convert passed string to its equivalent lowercases
/----------------------------------------------------------------------------*/
char *
strlwr(s)
register char *s;
{
	char *op;

	for (op = s; *s; s++)
		if (isupper(*s))
			*s = _tolower(*s);
	return(op);
}
#endif

/*--------------------------------------------------- asciifile() -----------
/ check if passed file is an ascii file using file(1) command
/---------------------------------------------------------------------------*/
int
asciifile(fn)
char *fn;
{
	char cmdstr[256];
	register FILE *fp;
        int file, cnt, i;

        { /* Detect binary files by extensions declared in $CIOBINEXT */
           char *ext= strrchr(fn,'.');
           if( ciobinext && ext )
           {
#ifdef OS2
               strlwr( ext );
#endif
               if( strstr(ciobinext, ext ) )
                   return(FALSE);
           }
        }

#ifdef OS2
	if ((file = open(fn, O_RDONLY|O_BINARY)) == -1)
		return(FALSE);
	cnt = read(file, cmdstr, sizeof(cmdstr)); /* get a block. */
	close(file);	/* and done. */

        for ( i = 0; i < cnt; i++ )
                if ( cmdstr[i] == 0 || cmdstr[i] == 127 )
                	return(FALSE);

        return(TRUE);
#else /* OS2 */
	(void) sprintf(cmdstr, "file %s", fn);
	if (!(fp = popen(cmdstr, "r")))
		return(FALSE);
	(void) strrd( VL(ftypestr), fp); /* get a line. */
	(void) pclose(fp);	/* and done. */
#ifdef DEBUG
	(void) printf("%s\n", cmdstr);
#endif
	if (strstr(ftypestr, "text"))
		return(TRUE);
	return(FALSE);
#endif /* OS2 */
}

/*--------------------------------------------------- rcsfile() -------------
/ check if passed file is an RCS file using file(1) command
/---------------------------------------------------------------------------*/
int
rcsfile(fn)
char *fn;
{
	char cmdstr[256];
	register FILE *fp;

#ifdef OS2
	if (!(fp = fopen(fn, "r")))
		return(FALSE);
	(void) strrd( VL(ftypestr), fp); /* get a line. */
	(void) fclose(fp);	/* and done. */
#ifdef DEBUG
	(void) printf("%s\n", cmdstr);
#endif
	if (strcmp(ftypestr, "head     "))
		return(TRUE);
#else /* OS2 */
	(void) sprintf(cmdstr, "file %s", fn);
	if (!(fp = popen(cmdstr, "r")))
		return(FALSE);
	(void) strrd( VL(ftypestr), fp); /* get a line. */
	(void) pclose(fp);	/* and done. */
#ifdef DEBUG
	(void) printf("%s\n", cmdstr);
#endif
#ifdef V_RCS
	if (strstr(ftypestr, "text"))
#else
	if (strstr(ftypestr, "sccs"))
#endif
		return(TRUE);
#endif /* OS2 */
	return(FALSE);
}

/*--------------------------------------------------- strrd() ----------------
/ read from given file pointer until a line separator or end-of-file or
/ (len) characters excluding the terminator.
/---------------------------------------------------------------------------*/
int
strrd(buf, len, fle)
char *buf;
int len;
FILE *fle;
{
	int c0, i0 = 0;

	while (((c0 = getc(fle)) != EOF) && c0 && c0 != '\n')
		if(i0 < len)  /* if room in buffer..*/
			buf[i0++] = (char) c0;  /* save it. */
	buf[i0] = 0;
	if (i0 == 0 && c0 == EOF)
		return(-1);
	return(i0);
}

/*--------------------------------------------------- getdir() --------------
/ get and readin variables for later.
/---------------------------------------------------------------------------*/
void
getdir()
{
	register char *cp;

	if(cp = getenv("HOME"))		/* get user's home dir. */
	{
		(void) strcpy(homedir = memalloc(strlen(cp) + 2), cp);
		if (!(s_homedir = fix_envstr(homedir)))
		{
			free(homedir);
			homedir = (char *) 0;
		}
	}
	else				/* this should NEVER happen */
	{
		(void) fprintf(stderr, "No home directory???\n");
		exit(-1);
	}

#ifdef V_RCS
	if(cp = getenv("RCSDIR"))	/* RCS directory */
#else
	if(cp = getenv("SCCSDIR"))	/* SCCS directory */
#endif
		(void) strcpy(rcsdir = memalloc(strlen(cp) + 2), cp);
	else				/* RCS is $HOME/RCS */
		(void) sprintf(rcsdir = memalloc(s_homedir + 6),
#ifdef V_RCS
				"%s/RCS", homedir);
#else
				"%s/SCCS", homedir);
#endif
	if (!(s_rcsdir = fix_envstr(rcsdir)))
	{
		free(rcsdir);
		rcsdir = (char *) 0;
	}

#ifdef V_RCS
	if(cp = getenv("RCSWORK"))	/* user's working directory */
#else
	if(cp = getenv("SCCSWORK"))	/* user's working directory */
#endif
	{
		(void) strcpy(rcswrk = memalloc(strlen(cp) + 2), cp);
		if (!(s_rcswrk = fix_envstr(rcswrk)))
		{
			free(rcswrk);
			rcswrk = (char *) 0;
		}
	}

#ifdef V_RCS
	if (cp = getenv("RCSSRC"))	/* master source directory */
#else
	if (cp = getenv("SCCSSRC"))	/* master source directory */
#endif
	{
		(void) strcpy(srcdir = memalloc(strlen(cp) + 2), cp);
		if (!(s_srcdir = fix_envstr(srcdir)))
		{
			free(srcdir);
			srcdir = (char *) 0;
		}
	}

#ifdef V_RCS
	if (cp = getenv("RCSHEAD"))	/* RCS header file directory */
#else
	if (cp = getenv("SCCSHEAD"))	/* SCCS header file directory */
#endif
	{
		(void) strcpy(headdir = memalloc(strlen(cp) + 2), cp);
		if (!(s_headdir = fix_envstr(headdir)))
		{
			free(headdir);
			headdir = homedir;
		}
	}
	else
		headdir = homedir;

#ifdef V_RCS
	if (cp = getenv("RCSOWN"))	/* the owner of RCS files */
#else
	if (cp = getenv("SCCSOWN"))	/* the owner of SCCS files */
#endif
	{
		s_rcsown = strlen(cp);
		(void) strcpy(rcsown = memalloc(s_rcsown + 1), cp);
	}

	if(cp = getenv("PATH"))		/* current path, ie. $PATH */
	{
		s_path = strlen(cp);
		(void) strcpy(path = memalloc(s_path + 1), cp);
	}

	if((currentdir = getcwd((char *)NULL, 200)) == NULL)
	{
		(void) fprintf(stderr, "Cannot get working dir.\n");
		exit(-1);
	}
#ifdef OS2
        /* strcpy(currentdir, currentdir + 2); */
        UnixFileName(currentdir);
#endif
        if( cp= getenv("CIOBINEXT") )
        {
		(void) strcpy(ciobinext = memalloc(strlen(cp) + 2), cp);
#ifdef OS2
		strlwr( ciobinext );
#endif
        }
        strcat(currentdir, "/");
	s_currentdir = strlen(currentdir);
}

/*--------------------------------------------------- fix_envstr() ----------
/ fix environment variable to avoid problems later.
/ 1. strip leading/trailing white spaces
/ 2. strip duplicate slashes
/ 3. add trailing slash
/ 4. return string length
/---------------------------------------------------------------------------*/
int
fix_envstr(cs)
char *cs;
{
	register char *cp, *dp;
	register int was_slash = FALSE;

#ifdef OS2
        UnixFileName(cs);
#endif

	cp = dp = cs;
	while (isspace(*cp))		/* remove leading white spaces */
		cp++;

	if (!*cp)			/* string was a full of blanks */
		return(0);

	while (*cp)
	{
		if (*cp == '/')
		{
			if (was_slash)
			{
				cp++;	/* strip duplicate slashes */
				continue;
			}
			else
				was_slash = TRUE;
		}
		else
			was_slash = FALSE;
		*dp++ = *cp++;
	}

	do				/* remove trailing while spaces */
	{
		dp--;
	} while (isspace(*dp));

	if (*dp != '/')			/* add trailing slash */
		*++dp = '/';
	*++dp = '\0';			/* null terminate */
	return(strlen(cs));
}

/*--------------------------------------------------- getrcsdir() -----------
/ get $RCSDIR + tail directory
/---------------------------------------------------------------------------*/
void
getrcsdir(tdir, sdir)
char *tdir, *sdir;
{
	register char *cp = sdir;

	if(rcswrk && !strnicmp(rcswrk, cp, s_rcswrk))
		cp += s_rcswrk;
	if(homedir && !strnicmp(homedir, cp, s_homedir))
		cp += s_homedir;
#ifdef OS2
        if(isalpha(cp[0]) && cp[1] == ':')
                cp += 2;
#endif
        if(cp[0] == '/')
                cp++;
	/*
	 * build the final directory name
	 */
	(void) sprintf(tdir, "%s%s", rcsdir, cp);
}

/*--------------------------------------------------- getworkdir() ----------
/ get $RCSWORK or $HOME + tail
/---------------------------------------------------------------------------*/
void
getworkdir(tdir, sdir)
char *tdir, *sdir;
{
	register char *cp = sdir;

	if (rcsdir && !strnicmp(rcsdir, cp, s_rcsdir))
		cp += s_rcsdir;
#ifdef OS2
        if(isalpha(cp[0]) && cp[1] == ':')
                cp += 2;
#endif
        if(cp[0] == '/')
                cp++;
	(void) sprintf(tdir, "%s%s", rcswrk ? rcswrk : homedir, cp);
}

/*--------------------------------------------------- getsrcdir() -----------
/ get $RCSSRC + tail
/---------------------------------------------------------------------------*/
void
getsrcdir(tdir, sdir)
char *tdir, *sdir;
{
	register char *cp = sdir;

	if (rcswrk && !strnicmp(rcswrk, cp, s_rcswrk))
		cp += s_rcswrk;
	if(homedir && !strnicmp(homedir, cp, s_homedir))
		cp += s_homedir;
#ifdef OS2
        if(isalpha(cp[0]) && cp[1] == ':')
                cp += 2;
#endif
        if(cp[0] == '/')
                cp++;
	(void) sprintf(tdir, "%s%s", srcdir ? srcdir : homedir, cp);
}

/*--------------------------------------------------- getfinput() ------------
/ get a title file.
/---------------------------------------------------------------------------*/
int
getfinput(name, type)
char *name;		/* buffer to put file name into. */
int type;		/* what data we want. */
{
	int stat_loc;

	(void) strcpy(name, tmpnam(editfile));
#ifndef OS2
	if (fork())
	{	/* parent just waits for the child to finish */
		(void) wait(&stat_loc);
	}
	else
	{	/* child does his/her stuff */
		(void) signal(SIGINT, SIG_DFL);
		(void) signal(SIGQUIT, SIG_DFL);
		(void) signal(SIGBREAK, SIG_DFL);
		(void) signal(SIGTERM, SIG_DFL);
		exit(child_getfinput(name, type) == TRUE ? 0 : -1);
	}
	return((stat_loc >> 8) & 0xff);
#else
	return child_getfinput(name, type) == TRUE ? 0 : -1;
#endif
}

/*--------------------------------------------------- child_getfinput() -----
/ actual get title file.
/---------------------------------------------------------------------------*/
int
child_getfinput(name, type)
char *name;		/* buffer to put file name into. */
int type;		/* what data we want. */
{
	static char *input_type[2] = { "log", "title" };
	FILE *fp, *xfp;
	register char *st;
	int c, done = FALSE;
	char buf[82];			/* just larger than input buffer. */
                  
#ifndef OS2
	(void) setuid(real_user_id);		/* user's real user id */
#endif        
	if((fp = fopen(name, "w")) == NULL) /* failed open. */
	{
		(void) unlink(name);		/* remove it. */
		name[0] = '\0';
		(void) printf("Unable to create tmp file.\n");
		return(FALSE);
	}
	(void) printf("Enter %s message, <ret>.<ret> or Control-Z to end:\n",
		input_type[type]);
	while (!done)
	{
		(void) printf(">>");
		if(strrd( VL(buf), stdin) == -1) /* read in one line. */
		{
			/* ok, read somewhere that this is possible.  By doing this,
			   we should be able to continue after a control-D.
			*/
			clearerr(stdin);
			break;
		}
		if(!strcmp(".", buf))	/* end of message */
			break;
		if (buf[0] == '~')	/* special command */
		{
			switch (buf[1])	/* command character */
			{
			case '?':	/* print usage, help message */
				(void) printf("\n");
				(void) printf("~.    End of input\n");
				(void) printf("~!    Invoke shell\n");
				(void) printf("~e    Edit message using an editor\n");
				(void) printf("~p    Print message buffer\n");
				(void) printf("~r    Read in a file\n");
				(void) printf("~w    Write message to a file\n");
				(void) printf("~?    Print this message\n");
				(void) printf("\n");
				break;
			case '!':	/* shell */
				st = getenv("SHELL");
				(void) system(st ? st : "/bin/sh");
				(void) printf("[Press RETURN to continue]");
				(void) strrd( VL(buf), stdin);
				break;
			case 'p':	/* print message buffer content */
				(void) fclose(fp);
				fp = fopen(name, "r");
				while ((c = fgetc(fp)) != EOF)
					(void) fputc(c, stdout);
				(void) fclose(fp);
				fp = fopen(name, "a");
				(void) printf("Continue entering %s message.\n",
					input_type[type]);
				break;
			case 'e':	/* editor */
			case 'v':	/* visual */
				(void) fclose(fp);
				st = getenv(buf[1] == 'e' ?"EDITOR":"VISUAL");
				(void) sprintf(buf, "%s %s",
					st ? st : "/usr/bin/vi", name);
				(void) system(buf);
				fp = fopen(name, "a");
				(void) printf("Continue entering %s message.\n",
					input_type[type]);
				break;
			case 'r':	/* read in a file */
				st = &buf[2];
				while (isspace(*st))
					st++;
				if (!*st)
				{
					(void) printf("File name missing!\n");
					break;
				}
				if (xfp = fopen(st, "r"))
				{
					while ((c = fgetc(xfp)) != EOF)
						(void) fputc(c, fp);
					(void) fclose(xfp);
				}
				else
					(void) printf("Unable to open %s.\n",
							st);
				break;
			case 'w':	/* write message to a file */
				st = &buf[2];
				while (isspace(*st))
					st++;
				if (!*st)
					(void) printf("File name missing!\n");
				else
				{
					if (xfp = fopen(st, "a"))
					{
						(void) fclose(fp);
						fp = fopen(name, "r");
						while ((c = fgetc(fp)) != EOF)
							(void) fputc(c, xfp);
						(void) fclose(xfp);
						(void) fclose(fp);
						fp = fopen(name, "a");
					}
					else
						(void) printf("Unable to open %s.\n",
							st);
				}
				break;
			case '.':	/* end of message */
				done = TRUE;
				break;
			default:	/* user doesn't know */
				(void) printf("Unrecognized command %c -- ignored\n",
					buf[1] & 0x7f);
				break;
			}
			continue;
		}
		(void) fprintf(fp, "%s\n", buf);
	}
	(void) fclose(fp);
	(void) printf("\n");
	return(TRUE);
}

/*--------------------------------------------------- justname() ------------
/ extract just filename from a full path
/---------------------------------------------------------------------------*/
char *
justname(fpath)
char *fpath;
{
	register char *cp;

#ifdef OS2
        UnixFileName(fpath);
#endif
	if (cp = strrchr(fpath, '/'))
		return(++cp);

	return(fpath);
}

/*--------------------------------------------------- memalloc() ------------
/ allocate specified amount of memory.  If not successful, exit.
/---------------------------------------------------------------------------*/
char *
memalloc(size)
register int size;
{
	register char *cp;

	if (!(cp = malloc((unsigned)size)))
	{
		perror(prognam);
		exit(99);
	}
	return(cp);
}

/*--------------------------------------------------- get_final_id() --------
/ Get the RCSOWN user id to create files with.  If none found, use user id.
/---------------------------------------------------------------------------*/
void
get_final_id()
{
	FILE *fp;

	if(!rcsown)			/* if there isn't one of these. */
#ifdef V_RCS
		rcsown = "rcsfiles";	/* default name. */
#else
		rcsown = "sccsfiles";	/* default name. */
#endif

	if ((fp = fopen (pwdfile, "r")) == NULL)
	{
#ifdef DEBUG
		(void) fprintf(stderr, "setpwent: %s non-existant or unreadable.\n",
				pwdfile);
#endif
		user_id = real_user_id;		/* make sure it's owners id now. */
		return;		/* couldn't do it. */
	}
	while (nextent(fp))		/* while entries in file.. */
		if (!strcmp(pwdname, rcsown))   /* If name matches. */
			break;
	(void) fclose(fp);	/* close the file. */
	return;			/* found it or not, return. */
}

/*--------------------------------------------------- nextent() -------------
/ get one entry from a password file.  Return TRUE if there is one found,
/ FALSE otherwise.
/---------------------------------------------------------------------------*/
int
nextent(fle)
FILE *fle;	 /* file pointer. */
{
	register char *cp, *pwp;
	char savbuf[200];	/* usually large enough for a password entry. */

	while (strrd( VL(savbuf), fle) != -1)
	{
		pwp = pwdname;
		cp = savbuf;		/* get user name */
		while (*cp && *cp != ':')
			*pwp++ = *cp++;
		*pwp = '\0';		/* terminate name. */
		for (cp++; *cp && *cp != ':'; cp++)
			;		/* skip over password. */
		user_id = atoi(++cp);	/* ok, save this users id number. */
		return (TRUE);
	}
	user_id = real_user_id;		/* make sure it's owners id now. */
	return (FALSE);
}

/*----------------------------- End of cio.c -------------------------------*/

#ifdef OS2
UnixFileName(char *name)
{
  /* strlwr(name); */
  for ( ; *name; name++ )
    if ( *name == '\\' )
      *name = '/';
}
#endif

