1
/* vim: set sw=8 ts=8 si et: */
3
* Author: Guido Socher. This program is distributed
4
* under the terms of the Gnu Public License (GPL).
16
static char **splitpath();
17
static int checkftype(char *path, char *filename);
18
static void matchfilename(char *path, char *key);
19
static int streq(const char *a,const char *b);
21
/*global for simplicity:*/
22
static int printdist=0;
23
static int casesensitive=0;
24
static int nomatchfound=1;
25
/*matchlimit indicates the tolerance level for Lev. Dist search
27
* 0 = exact match with wildcards,
28
* 1-255 max distance value*/
29
static int matchlimit=-2;
32
int main(int argc, char *argv[])
34
char searchkey[MAXLEN +1];
39
if (argc < 2 ) help();
41
* After this while loop. argv[0] is the next argument if available
43
* options that take an argument write *c=0;break; others write
46
* This parser looks a bit complicated but it can take options
47
* like -23 and -ac is the same as "-a -c"
52
c = *argv; /* c is pointer to one argument/option */
55
break; /*stop at the first non option argument*/
58
if (*c == '-' && *(c + 1)=='\0'){
59
/* you can write a double -- to stop option
60
* parsing. This is useful if you need to search
61
* for a string that is called "-something" */
63
break; /*stop option parsing*/
69
/*no break, help does not return*/
77
/* you can write -t11 or -t 11*/
78
/* first the normal -t11 case: */
80
/* then the -t 11 case: */
82
if(--optnum) oarg=*++argv;
84
matchlimit = atoi(oarg);
85
if (matchlimit > 255 || matchlimit <0) matchlimit = 255;
88
if (isdigit((int)*c)){
89
if (matchlimit !=-1 ){
92
if (matchlimit > 255 || matchlimit <0) matchlimit = 255;
95
fprintf(stderr,"ERROR: No such option. Use -h to get help.\n");
102
if (optnum<0) optnum=0; /* can be less then zero due to a option
103
* with arg at last pos and missing arg*/
105
*optnum is now the number of argmunts left after
106
*option parsing. Example: option a,c take no arg, option b has arg:
107
* thisprogram -a -b xx -c d e
109
* optnum==2 and argv[1] points to d
110
* argv[2] points to e
113
/* you must provide exactly one argument */
115
/*help does not return */
117
strncpy(searchkey, argv[1],MAXLEN);
118
if (matchlimit == -2){
119
/*tolerance for Levenshtein Distance search: */
120
matchlimit = stdtolerance(searchkey);
123
pathcomp = splitpath();
125
matchfilename(*pathcomp, searchkey);
128
return (nomatchfound);
133
* check that the file is a normal executable.
134
* The abs. path to the file is "path/filename"
135
* Return 1 if it is normal executable or a link to an normal executable
137
#define MAXFULLPATHLEN 500
138
int checkftype(char *path, char *filename)
141
static char fullpath[MAXFULLPATHLEN];
143
/*construct the full path to the file:*/
145
while(*path && i < MAXFULLPATHLEN-7){ /* -7 so, we have some room */
149
if (fullpath[i-1]!='/'){
153
while(*filename && i < MAXFULLPATHLEN-1){
154
fullpath[i]=*filename;
158
/*check if file exists:*/
159
if (stat(fullpath,&stbuf)!=0) return(0);
160
/*not a regular file*/
161
if ((stbuf.st_mode & S_IFREG)==0) return(0);
162
/* test if executable for user or grp or others */
163
if ((stbuf.st_mode & S_IXUSR) || (stbuf.st_mode & S_IXGRP)\
164
||(stbuf.st_mode & S_IXOTH)) return(1);
168
* match all entries in the directory variable path points to
170
* This function sets the global variable nomatchfound
172
void matchfilename(char *path, char *key)
176
struct dirent *entry;
177
if ((dp = opendir(path)) != NULL) {
178
while ((entry = readdir(dp)) != NULL) {
179
/*ignore . or .. or .file: */
180
if (*(entry->d_name) == '.') continue;
181
dist=levdist(entry->d_name, key, matchlimit,casesensitive);
182
if (dist != -1 && checkftype(path,entry->d_name)){
183
if (printdist) printf("%03d ",dist);
184
printf("%s/%s\n", path, entry->d_name);
194
* split PATH into substrings and return a pointer to
195
* a 2 dimmentional array with all the sub-strings of the
200
char *spath; /* search path */
201
char *cspath;/* copy of search path */
202
static char **pathcomp;
206
int compind = 0; /* index of *pathcomp[] */
207
int complen = 1; /* len of one component in the path + 16 */
209
spath = (char *) getenv("PATH");
210
if (spath == NULL || *spath == '\0') {
211
fprintf(stderr,"Warning: PATH not defined, using /bin:/usr/bin\n");
212
spath="/bin:/usr/bin";
214
/* make a copy that is static */
215
cspath = (char *)malloc(strlen(spath) + 2);
216
strcpy(cspath, spath);
218
/* count the number of colons in the path to find how many
219
* components we have in the path */
221
if (*cspath == ':') i++;
224
i+=3; /*some space in case we have corruped path below*/
225
pathcomp=(char **)malloc(sizeof(char *)*i);
226
/* split the path by replacing : with \0 */
228
pathcomp[compind]=cspath;
230
if (*cspath == ':') {
232
if (complen > 1) { /*ignore zero length comp. e.g :: */
233
/* now check if it is a duplicate path
234
* component that we can just ignore: */
236
while(notseen && i < compind){
237
if(streq(pathcomp[compind],pathcomp[i])){
242
if (notseen) compind++;
244
/* remove any tailing slashes but not if it is
245
* only the root dir (just a "/") */
246
if (complen > 2 && *(cspath-1) == '/') *(cspath-1)='\0';
247
pathcomp[compind] = cspath+1;
254
/*we have some corrupted path, this happens when you have a
255
*colon at the end of the path */
256
pathcomp[compind] = NULL; /*terminate */
258
/* now check if the last one is a duplicate path
259
* component that we can just ignore: */
261
while(notseen && i < compind){
262
if(streq(pathcomp[compind],pathcomp[i])){
267
if (notseen) compind++;
268
pathcomp[compind] = NULL; /*terminate */
270
return (&pathcomp[0]);
272
/* return 1 if the 2 strings a and b are equal */
273
int streq(const char *a,const char *b){
275
if(*a != *b) return(0);
278
/* both must be at \0 */
279
if(*a || *b) return(0);
287
printf("ftwhich -- fault tolerant approximate search command for programs\n\
289
USAGE: ftwhich [-#hIp][-t#] [--] program-name\n\
291
Supported wildcards: * -- any arbitrary number of character\n\
292
? -- one character\n\
294
OPTIONS: -h this help\n\
295
-I search case sensitive (default is case in-sensitive)\n\
296
-p print the actual distance (tolerance) value in front of the\n\
298
-# set fault tolerance level to # (integer in the range 0-255)\n\
299
It specifies the maximum distance. This is the number of\n\
300
errors permitted for finding the approximate match.\n\
301
-t# same as -# for backward compatibility\n\
303
With no option specified search is fault tolerant using a tolerance\n\
304
level of: (string length of searchpattern - number of wildcards)/6 +1\n");
305
printf("%s\n",MYVERSION);