1*11983Sslatteng /* @(#)path.c	1.2	04/18/83
211958Sslatteng  *
311958Sslatteng  * Copyright -C- 1982 Barry S. Roitblat
411958Sslatteng  *
511958Sslatteng  *
611958Sslatteng  * This file contains routines that a) implement a path mechanism, whereby
711958Sslatteng  * several places may be searched for files, and b) provide a defaulting
811958Sslatteng  * mechanism for file name extensions.
911958Sslatteng  *
1011958Sslatteng  * (modified from code originally written by John Ousterhout for the caesar
1111958Sslatteng  *  design system)
1211958Sslatteng  */
1311958Sslatteng 
1411958Sslatteng #include "gremlin.h"
1511958Sslatteng #include <pwd.h>
1611958Sslatteng #include <ctype.h>
1711958Sslatteng 
1811958Sslatteng /* Imports from config.c: */
1911958Sslatteng 
2011958Sslatteng extern char GLibrary[];
2111958Sslatteng 
2211958Sslatteng /* Library routines: */
2311958Sslatteng 
2411958Sslatteng extern char *strcpy(), *strcpyn(), *index(), *sprintf(), *malloc();
2511958Sslatteng 
2611958Sslatteng /* The following string holds the current path,which consists of a bunch
2711958Sslatteng  * of directory names separated by spaces.
2811958Sslatteng  */
2911958Sslatteng 
3011958Sslatteng #define PATHSIZE 400
3111958Sslatteng static char path[PATHSIZE] = ".";
3211958Sslatteng 
3311958Sslatteng /* The following string pointers constitute a cache of recently
3411958Sslatteng  * referenced tilde names.
3511958Sslatteng  */
3611958Sslatteng 
3711958Sslatteng #define NTILDE 10
3811958Sslatteng static char *tildename[NTILDE] = {NULL, NULL, NULL, NULL, NULL,
3911958Sslatteng     NULL, NULL, NULL, NULL, NULL};
4011958Sslatteng static char *realname[NTILDE]  = {NULL, NULL, NULL, NULL, NULL,
4111958Sslatteng     NULL, NULL, NULL, NULL, NULL};
4211958Sslatteng static int discard = 0;
4311958Sslatteng 
44*11983Sslatteng 
4511958Sslatteng int
PConvertTilde(psource,pdest,size)4611958Sslatteng PConvertTilde(psource, pdest, size)
4711958Sslatteng char **psource;			/* Pointer to a pointer to the source string */
4811958Sslatteng char **pdest;			/* Pointer to a pointer to the dest. string */
4911958Sslatteng int *size;			/* Pointer to no. bytes available at pdest */
5011958Sslatteng 
5111958Sslatteng /*---------------------------------------------------------
5211958Sslatteng  *	This routine converts tilde notation into standard directory names.
5311958Sslatteng  *
5411958Sslatteng  *	Results:
5511958Sslatteng  *	If the conversion was done successfully, then TRUE is returned.
5611958Sslatteng  *	If a user name couldn't be found in the password file, then
5711958Sslatteng  *	FALSE is returned.
5811958Sslatteng  *
5911958Sslatteng  *	Side Effects:
6011958Sslatteng  *	If the first character of the string indicated by psource is a
6111958Sslatteng  *	tilde ("~") then the subsequent user name is converted to a login
6211958Sslatteng  *	directory name and stored in the string indicated by pdest.  Then
6311958Sslatteng  *	remaining characters in the file name at psource are copied to
6411958Sslatteng  *	pdest and both pointers are updated.  Upon return, psource points
6511958Sslatteng  *	to the terminating character in the source file name and pdest
6611958Sslatteng  *	points to the next character after the last character in the file
6711958Sslatteng  *	name at that location.  If a tilde cannot be converted because the
6811958Sslatteng  *	user name cannot be found, psource is still advanced past the current
6911958Sslatteng  *	entry, but pdest is unaffected.  At most size characters will be
7011958Sslatteng  *	stored at pdest, and the size is decremented by the number of
7111958Sslatteng  *	characters we actually stored.
7211958Sslatteng  *---------------------------------------------------------
7311958Sslatteng  */
7411958Sslatteng 
7511958Sslatteng {
7611958Sslatteng     register char *ps, *pd;
7711958Sslatteng     struct passwd *passwd, *getpwnam();
7811958Sslatteng     char username[17], *string;
7911958Sslatteng     int i, length;
8011958Sslatteng 
8111958Sslatteng     ps = *psource;
8211958Sslatteng     if (*ps == '~')
8311958Sslatteng     {
8411958Sslatteng 	/* Copy the user name into a string (at most 16 characters).
8511958Sslatteng 	 * If we don't have a cached entry for this name, then
8611958Sslatteng 	 * read the password file entry for the user and grab out
8711958Sslatteng  	 * the login directory.
8811958Sslatteng 	 */
8911958Sslatteng 
9011958Sslatteng 	pd = username;
9111958Sslatteng 	for (i=0; ; i++)
9211958Sslatteng 	{
9311958Sslatteng 	    *pd = *++ps;
9411958Sslatteng 	    if (isspace(*pd) || (*pd=='\0') || (*pd=='/') || (*pd==':'))
9511958Sslatteng 		break;
9611958Sslatteng 	    if (i < 16) pd++;
9711958Sslatteng 	}
9811958Sslatteng 	*pd = '\0';
9911958Sslatteng 	for (i=0;  i<NTILDE; i++)
10011958Sslatteng 	{
10111958Sslatteng 	    if (strcmp(tildename[i], username) == 0)
10211958Sslatteng 	    {
10311958Sslatteng 		string = realname[i];
10411958Sslatteng 		goto gotname;
10511958Sslatteng 	    }
10611958Sslatteng 	}
10711958Sslatteng 
10811958Sslatteng 	/* Since we don't have a cached entry, read the password file
10911958Sslatteng 	 * and make a new cache entry for this one.  Note that this
11011958Sslatteng 	 * means discarding an old entry.
11111958Sslatteng 	 */
11211958Sslatteng 
11311958Sslatteng 	passwd = getpwnam(username);
11411958Sslatteng 	string = passwd->pw_dir;
11511958Sslatteng 	if (passwd != NULL)
11611958Sslatteng 	{
11711958Sslatteng 	    discard++;
11811958Sslatteng 	    if (discard >= NTILDE) discard = 0;
11911958Sslatteng 	    if (tildename[discard] != NULL)
12011958Sslatteng 	    {
12111958Sslatteng 	    	free(tildename[discard]);
12211958Sslatteng 	    	free(realname[discard]);
12311958Sslatteng 	    }
12411958Sslatteng 	    tildename[discard] = malloc((unsigned) strlen(username)+1);
12511958Sslatteng 	    strcpy(tildename[discard], username);
12611958Sslatteng 	    realname[discard] = malloc((unsigned) strlen(string)+1);
12711958Sslatteng 	    strcpy(realname[discard], string);
12811958Sslatteng 	}
12911958Sslatteng 	else
13011958Sslatteng 
13111958Sslatteng 	/* The entry can't be found, so skip the source entry and return */
13211958Sslatteng 
13311958Sslatteng 	{
13411958Sslatteng 	    while ((*ps != '\0') && !isspace(*ps) && (*ps != ':')) ps++;
13511958Sslatteng 	    *psource = ps;
13611958Sslatteng 	    return FALSE;
13711958Sslatteng 	}
13811958Sslatteng 	gotname: length = strlen(string);
13911958Sslatteng 	if (length > *size) length = *size;
14011958Sslatteng 	strcpyn(*pdest, string, length);
14111958Sslatteng 	*size -= length;
14211958Sslatteng 	pd = *pdest+length;
14311958Sslatteng     }
14411958Sslatteng     else pd = *pdest;
14511958Sslatteng 
14611958Sslatteng     /* Copy the rest of the directory name from the source to the dest. */
14711958Sslatteng 
14811958Sslatteng     while ((*ps != '\0') && !isspace(*ps) && (*ps != ':'))
14911958Sslatteng 	if (*size > 0)
15011958Sslatteng 	{
15111958Sslatteng 	    *pd++ = *ps++;
15211958Sslatteng 	    (*size)--;
15311958Sslatteng 	}
15411958Sslatteng 	else ps++;
15511958Sslatteng     *psource = ps;
15611958Sslatteng     *pdest = pd;
15711958Sslatteng     return TRUE;
15811958Sslatteng }
15911958Sslatteng 
160*11983Sslatteng 
16111958Sslatteng int
PSetPath(string)16211958Sslatteng PSetPath(string)
16311958Sslatteng char *string;			/* Pointer to a string that is to become
16411958Sslatteng 				 * the new fle search path.  Must consist
16511958Sslatteng 				 * of one or more directory names separated
16611958Sslatteng 				 * by white space or colons.  Two adjacent
16711958Sslatteng 				 * colons are the same as ":.:".  Tilde
16811958Sslatteng 				 * notation is ok.
16911958Sslatteng 				 */
17011958Sslatteng 
17111958Sslatteng /*---------------------------------------------------------
17211958Sslatteng  *	PSetPath sets up the current search path.
17311958Sslatteng  *
17411958Sslatteng  *	Results:
17511958Sslatteng  *	-1 is returned if one or more of the paths contained a tilde
17611958Sslatteng  *	notation that couldn't be converted.  -2 is returned if the
17711958Sslatteng  *	path space was exhausted.  Otherwise a value >= 0 is returned.
17811958Sslatteng  *
17911958Sslatteng  *	Side Effects:
18011958Sslatteng  *	The string is stored as the current path, and all entries with
18111958Sslatteng  *	tilde notation are converted to non-tilde notation.  Tilde entries
18211958Sslatteng  *	that cannot be converted are ignored.  Note:  only PATHSIZE total
18311958Sslatteng  *	bytes of path are stored, after tilde conversion.  Excess bytes
18411958Sslatteng  *	are truncated.
18511958Sslatteng  *---------------------------------------------------------
18611958Sslatteng  */
18711958Sslatteng 
18811958Sslatteng {
18911958Sslatteng     int result, spaceleft;
19011958Sslatteng     char *p;
19111958Sslatteng 
19211958Sslatteng     result = 0;
19311958Sslatteng     spaceleft = PATHSIZE-1;
19411958Sslatteng     p = path;
19511958Sslatteng 
19611958Sslatteng     if (*string == '\0')
19711958Sslatteng     {
19811958Sslatteng 	path[0] = '.';
19911958Sslatteng 	path[1] = '\0';
20011958Sslatteng 	return 0;
20111958Sslatteng     }
20211958Sslatteng 
20311958Sslatteng     while (*string != '\0')
20411958Sslatteng     {
20511958Sslatteng 	if (*string == ':')
20611958Sslatteng 	    if ((*++string == ':') && (spaceleft >= 2))
20711958Sslatteng 	    {
20811958Sslatteng 		*p++ = '.';
20911958Sslatteng 		*p++ = ' ';
21011958Sslatteng 		spaceleft -= 2;
21111958Sslatteng 	    }
21211958Sslatteng 	if (spaceleft <= 0) break;
21311958Sslatteng 	while ((*string == ':') || isspace(*string)) string++;
21411958Sslatteng 	if (!PConvertTilde(&string, &p, &spaceleft)) result = -1;
21511958Sslatteng 	else if (spaceleft-- > 0) *p++ = ' ';
21611958Sslatteng     }
21711958Sslatteng     *p = '\0';
21811958Sslatteng     if (spaceleft < 2) return -2;
21911958Sslatteng     return result;
22011958Sslatteng }
22111958Sslatteng 
222*11983Sslatteng 
22311958Sslatteng char *
PGetPath()22411958Sslatteng PGetPath()
22511958Sslatteng 
22611958Sslatteng /*---------------------------------------------------------
22711958Sslatteng  *	This routine merely returns a poiner to the current path.
22811958Sslatteng  *
22911958Sslatteng  *	Results:
23011958Sslatteng  *	The address of the current path (with all tildes expanded).
23111958Sslatteng  *
23211958Sslatteng  *	Side Effects:	None.
23311958Sslatteng  *---------------------------------------------------------
23411958Sslatteng  */
23511958Sslatteng 
23611958Sslatteng {
23711958Sslatteng     return path;
23811958Sslatteng }
23911958Sslatteng 
240*11983Sslatteng 
24111958Sslatteng FILE *
POpen(file,prealname,search)24211958Sslatteng POpen(file, prealname, search)
24311958Sslatteng char *file;			/* Name of the file to be opened. */
24411958Sslatteng char **prealname;		/* Pointer to a location that will be filled
24511958Sslatteng 				 * in with the address of the real name of
24611958Sslatteng 				 * the file that was successfully opened.
24711958Sslatteng 				 * If NULL, then nothing is stored.
24811958Sslatteng                                  */
24911958Sslatteng int search;			/* If FALSE, then the search path business
25011958Sslatteng 				 * doesn't happen.  A default extension
25111958Sslatteng 				 * will still be added.
25211958Sslatteng 				 */
25311958Sslatteng 
25411958Sslatteng /*---------------------------------------------------------
25511958Sslatteng  *	This routine does a file lookup using the current path and
25611958Sslatteng  *	supplying a default extension.
25711958Sslatteng  *
25811958Sslatteng  *	Results:
25911958Sslatteng  *	A pointer to a FILE, or NULL if the file couldn't be found.
26011958Sslatteng  *
26111958Sslatteng  *	Side Effects:
26211958Sslatteng  *	If the first character of the
26311958Sslatteng  *	file name is "~" or "/" or if search is FALSE, then we try
26411958Sslatteng  *	to look up the file with the original name, doing tilde
26511958Sslatteng  *	expansion of course and returning that result.  If none of
26611958Sslatteng  *	these conditions is met, we go through the current path
26711958Sslatteng  *	trying to look up the file once for each path
26811958Sslatteng  *	entry by prepending the path entry to the original file name.
26911958Sslatteng  *	This concatenated name is stored in a static string and made
27011958Sslatteng  *	available to the caller through prealname if the open succeeds.
27111958Sslatteng  *	Note: the static string will be trashed on the next call to this
27211958Sslatteng  *	routine.  Also, note that no individual file name is allowed to
27311958Sslatteng  *	be more than NAMESIZE characters long.  Excess characters are lost.
27411958Sslatteng  *---------------------------------------------------------
27511958Sslatteng  */
27611958Sslatteng 
27711958Sslatteng {
27811958Sslatteng #define NAMESIZE 50
27911958Sslatteng     static char realname[NAMESIZE];
28011958Sslatteng     char extendedname[NAMESIZE], *p, *p2;
28111958Sslatteng     int length, spaceleft, size;
28211958Sslatteng     FILE *f;
28311958Sslatteng 
28411958Sslatteng     if (file == NULL) return (FILE *) NULL;
28511958Sslatteng     if (file[0] == '\0') return (FILE *) NULL;
28611958Sslatteng 
28711958Sslatteng     if (prealname != NULL) *prealname = realname;
28811958Sslatteng 
28911958Sslatteng     /* Now try the original name.  If it starts with a ~ or / look
29011958Sslatteng      * it up directly.
29111958Sslatteng      */
29211958Sslatteng 
29311958Sslatteng     if (file[0] == '~')
29411958Sslatteng     {
29511958Sslatteng 	p = realname;
29611958Sslatteng 	length = NAMESIZE-1;
29711958Sslatteng 	if (!PConvertTilde(&file, &p, &length)) return NULL;
29811958Sslatteng 	*p = '\0';
29911958Sslatteng 	return fopen(realname, "r");
30011958Sslatteng     }
30111958Sslatteng 
30211958Sslatteng     if ((file[0] == '/') || (search == FALSE))
30311958Sslatteng     {
30411958Sslatteng 	strcpyn(realname, file, NAMESIZE-1);
30511958Sslatteng 	realname[NAMESIZE-1] = '\0';
30611958Sslatteng 	return fopen(realname, "r");
30711958Sslatteng     }
30811958Sslatteng 
30911958Sslatteng     /* Now try going through the path. */
31011958Sslatteng 
31111958Sslatteng     p = path;
31211958Sslatteng     while (*p != '\0')
31311958Sslatteng     {
31411958Sslatteng 	spaceleft = NAMESIZE-1;
31511958Sslatteng 	p2 = realname;
31611958Sslatteng 	while (isspace(*p)) p++;
31711958Sslatteng 	while ((*p != '\0') && !isspace(*p))
31811958Sslatteng 	    if (spaceleft-- > 0) *p2++ = *p++;
31911958Sslatteng 	    else p++;
32011958Sslatteng 	if (spaceleft-- > 0) *p2++ = '/';
32111958Sslatteng 	if (spaceleft > 0) strcpyn(p2, file, spaceleft);
32211958Sslatteng 	realname[NAMESIZE-1] = '\0';
32311958Sslatteng 	f = fopen(realname, "r");
32411958Sslatteng 	if (f != NULL) return f;
32511958Sslatteng     }
32611958Sslatteng 
32711958Sslatteng     /* We've tried the path and that didn't work.  As a last shot,
32811958Sslatteng      * try the library area.  Only use the library for reads.
32911958Sslatteng      */
33011958Sslatteng 
33111958Sslatteng     p = GLibrary;
33211958Sslatteng     p2 = realname;
33311958Sslatteng     size = NAMESIZE;
33411958Sslatteng     if (!PConvertTilde(&p, &p2, &size)) return FALSE;
33511958Sslatteng     (void) strcpyn(p2, file, size);
33611958Sslatteng     realname[NAMESIZE-1] = '\0';
33711958Sslatteng     return fopen(realname, "r");
33811958Sslatteng }
339