This is selfdir.c in view mode; [Download] [Up]
/*
detects the path of the started executable from argv[0] and the enviroment-
variable $PATH
Copyright (c) MUFTI (Dipl. Phys. J. Scheurich (scheurich@rus.uni-stuttgart.de))
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You could have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* Fixed bug with relative paths in $PATH and on the commandline; also
* fixed some other bugs concerning '//' as directory seperator, spaces
* in $PATH and a misplaced dump_on_memoryleak.
* This file is a mess and I'm sure I've left some bugs in. Somebody should
* consider rewriting it. But it's late and I'd like to go to bed now :-)
* 6.2.95, Martin Buck <martin.buck@student.uni-ulm.de>
*
* Some fixes for brain-damaged Ultrix-systems
* 21.2.95, Martin Buck <martin.buck@student.uni-ulm.de>
*/
#include "c-auto.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifndef NeXT
#include <dirent.h>
#else
#undef HAVE_GETCWD
#include <sys/dir.h>
#include <libc.h>
#define S_IXUSR _S_IXUSR
#define S_IXGRP 0000010
#define S_IXOTH 0000001
#endif
#include <unistd.h>
#include <kpathsea/config.h>
#include <kpathsea/c-pathmx.h>
#ifndef TRUE
#define TRUE -1
#endif
#ifndef FALSE
#define FALSE 0
#endif
#define LINK 1
#define FILE 2
#define DIRECTORY 3
#define SPECIAL 4
/*
* Avoid name conflicts with other routines of a user
* only the "main"-routine(selfdir) has a common name
*/
/*
* I always thought declaring functions as static would be the solution
* to this problem... Am I missing something?
*/
#define dump_on_memoryleak _dump_on_memoryleak_
#define existinode _existinode_
#define identifyinode _identifyinode_
#define testexecutable _testexecutable_
#define inodeoflink _inodeoflink_
#define getdir _getdir_
#define getlink _getlink_
#define absolutepath _absolutepath_
#define searchfile _searchfile_
void dump_on_memoryleak(char* buff);
/*
* search if the inode pointed by directory and file exist
* give back inode on success, else return NULL
*/
char* existinode(char* directory,char* file)
{
int result;
struct stat buf;
char* inode;
char* nothing;
inode=(char*)malloc((strlen(directory)+strlen(file)+2)*sizeof(char));
dump_on_memoryleak(inode);
nothing=strcpy(inode,directory);
if (directory[0]!=(char)0)
strcat(inode,"/");
strcat(inode,file);
result=stat(inode,&buf);
if (result==0)
return(inode);
free(inode);
return(NULL);
}
/*
* identify inode, if it is link, directory, file or special
*/
int identifyinode(char* inode)
{
int result;
struct stat buf;
/*
note, that it's important to use here lstat instead of stat under POSIX,
cause stat can't get back the st_mode of the link itself (it gets the
st_mode of the pointed inode)
*/
result=lstat(inode,&buf);
if (result==-1)
return(0);
if (((buf.st_mode) & S_IFLNK)==S_IFLNK) result=LINK;
else if (((buf.st_mode) & S_IFDIR)==S_IFDIR) result=DIRECTORY;
else if (((buf.st_mode) & S_IFREG)==S_IFREG) result=FILE;
else result=SPECIAL;
return(result);
}
/*
* test if the file is executable, if yes, return inode, if no return NULL
*/
char* testexecutable(char* inode)
{
int result;
struct stat buf;
result=stat(inode,&buf);
if (result<0)
return(NULL);
result=-1;
if (((buf.st_mode) & S_IXUSR) != 0) result=0;
else if (((buf.st_mode) & S_IXGRP) != 0) result=0;
else if (((buf.st_mode) & S_IXOTH) != 0) result=0;
if (result<0)
return(NULL);
return(inode);
}
/*
* search the inode of a filelink
*/
char* inodeoflink(char* inode)
{
int result;
int i;
size_t buflen=MAXNAMLEN;
char* buf;
buf=(char*)malloc((MAXNAMLEN+1)*sizeof(char));
dump_on_memoryleak(buf);
for (i=0;i<=MAXNAMLEN;i=i+1)
buf[i]=(char)0;
result=readlink(inode,buf,buflen);
buf[buflen]=(char)0;
if (result<0)
return(NULL);
return(buf);
}
/*
* make a coredump, if there is no more memory
*/
void dump_on_memoryleak(char* buff)
{
int i;
if (buff==NULL)
{
perror(" in self.c ");
printf("can't malloc, make coredump\n");
i=0;
i=1/i;
/* avoid to optimise the former line away ... */
printf("%d\n",i);
exit(ENOMEM);
}
}
/*
* give back the directory of a file
*/
char* getdir(char* file)
{
int i;
if (file==NULL) return(file);
for (i=strlen(file)-1;i>=0;i=i-1)
if (file[i]=='/')
{
while (i >= 0 && file[i] == '/') {
file[i+1]=(char)0;
i--;
}
i = 0;
break;
}
if (i<0)
return(NULL);
return(file);
}
/*
* get a proper path of the inode of a FILElink
*/
char* getlink(char* inode)
{
char* inodepath;
char* temp;
char* returninode;
returninode=inodeoflink(inode);
if (returninode==NULL)
{
return(NULL);
free(inode);
}
/* relative links need extra code ! */
if (returninode[0]!='/')
{
inodepath=getdir(inode);
temp=malloc(strlen(inodepath)+strlen(returninode)+1);
dump_on_memoryleak(temp);
strcpy(temp,inodepath);
while ((returninode[0] == '.') && (returninode[1] == '.') &&
(returninode[2] == '/') && strlen(temp)) {
returninode += 3;
while (*returninode == '/') /* Multiple slashes are valid */
returninode++; /* directory-seperators ! */
temp[strlen(temp)-1]='\0';
getdir(temp);
}
strcat(temp,returninode);
returninode=temp;
}
free(inode);
return(returninode);
}
/*
* Make an absolute path from a relative one. We assume that nobody
* chdir'ed up to now.
*/
#ifndef HAVE_GETCWD
#define getcwd(a,b) getwd(a)
#endif
char *
absolutepath(char* path) {
char* apath;
if (path[0] == '/') {
apath = xstrdup(path);
return(apath);
} else {
while (path[0] == '.' && path[1] == '/') {
path += 2;
while (*path == '/')
path++;
}
apath = (char*)malloc((PATH_MAX + strlen(path) + 2) * sizeof(char));
dump_on_memoryleak(apath);
apath = getcwd(apath, PATH_MAX);
while (path[0] == '.' && path[1] == '.' && path[2] == '/' && strlen(apath)) {
path += 3;
while (*path == '/')
path++;
apath[strlen(apath) - 1] = '\0';
getdir(apath);
}
if (strlen(apath) && apath[strlen(apath) - 1] != '/')
strcat(apath, "/");
strcat(apath, path);
return(apath);
}
}
/*
* look if the inode pointed by directory and file is a true file
* if the inode is a filelink, search the true file
* if the inode is a directory or a special file, return error (NULL)
* on success (file found) and it's executable return the path
*/
char* searchfile(char* inode)
{
while (identifyinode(inode)==LINK)
inode=getlink(inode);
if (inode!=NULL)
if (identifyinode(inode)!=FILE)
{
free(inode);
return(NULL);
}
inode=testexecutable(inode);
return(inode);
}
/*
* detects the path of the called binary from argv[0] and the enviroment-
* variable $PATH (the path includes the last slash of the file)
*/
char *selfdir(char* argv0)
{
char* inode;
char* temp;
char* dollarpath;
int pathlen;
int i;
char* target;
char* apath;
/* if argv0 contains a path don't search any more */
for (i=0;i<=strlen(argv0);i++)
if (argv0[i]=='/')
{
target=(char*)malloc((strlen(argv0)+1)*sizeof(char));
dump_on_memoryleak(target); /* Checking first seems to be a good idea */
strcpy(target,argv0);
apath = absolutepath(target);
free(target);
return(getdir(searchfile(apath)));
}
temp=getenv("PATH");
pathlen=strlen(temp);
dollarpath=(char*)malloc((pathlen+1)*sizeof(char));
dump_on_memoryleak(dollarpath);
strcpy(dollarpath,temp);
/* strip whitespaces and ':' from path-variable */
for (i=0;i<pathlen;i++)
if ((*(dollarpath+i)==':')) /* || (*(dollarpath+i)<=' ')) Why not have spaces in $PATH ? */
*(dollarpath+i)=(char)0;
/* walk through all paths */
i=0;
while (i<pathlen)
if (*(dollarpath+i)!=(char)0)
{
inode=existinode(dollarpath+i,argv0);
if (inode!=NULL)
{
apath = absolutepath(inode);
free(inode);
target=getdir(searchfile(apath));
if (target!=NULL)
{
free(dollarpath);
return(target);
}
}
i=i+strlen(dollarpath+i);
}
else
i=i+1;
free(dollarpath);
return(NULL);
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.