Hint added.
[AROS.git] / workbench / c / Which.c
blob41e1416dabf8e79dff9bd0999d91b75e4a45a54d
1 /*
2 Copyright © 1995-2016, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Find the whereabouts of an executable file
6 Lang: English
7 */
9 /******************************************************************************
11 NAME
13 Which
15 SYNOPSIS
17 FILE/A, NORES/S, RES/S, ALL/S
19 LOCATION
23 FUNCTION
25 Find and print the location of a specific program.
26 Resident programs are marked as RES if they are not
27 internal resident in which case they are marked as INTERNAL.
29 Which searches the resident list, the current directory,
30 the command paths and the C: assign. If the item was not
31 found the condition flag is set to WARN but no error is
32 printed.
34 INPUTS
36 FILE -- the command to search for
37 NORES -- don't include resident programs in the search
38 RES -- consider resident programs only
39 ALL -- find all locations of the FILE. This may cause the
40 printing of the same location several times, for
41 instance if the current directory is C: and the
42 FILE was found in C:
44 RESULT
46 NOTES
48 For compatibility reasons these cases are handled specially:
50 Absolute path:
51 Prints the expanded path if it exists and is a file
52 and no RES argument is given.
54 Path which ends with a ':':
55 Prints the expanded path if it exists and no RES argument is given.
57 EXAMPLE
59 BUGS
61 SEE ALSO
63 INTERNALS
65 Executable files in AROS currently haven't got the e-flag set,
66 which makes Which unusable for now in emulated mode.
68 HISTORY
71 ******************************************************************************/
73 //#define DEBUG 1
74 #include <aros/debug.h>
75 #include <proto/exec.h>
76 #include <proto/dos.h>
77 #include <exec/memory.h>
78 #include <dos/dosextens.h>
79 #include <utility/tagitem.h>
81 #define ARG_COUNT 4 /* Number of ReadArgs() arguments */
83 const TEXT version[] = "$VER: Which 41.2 (14.2.2016)";
85 /* NOTE: For now, compatibility to the Amiga Which command is kept, but
86 I think that the restriction to only executable files should be
87 removed, especially considering soft links and such. */
91 * Check the resident list for the command 'name'.
93 static BOOL FindResidentCommand(STRPTR name);
97 * Check the paths for the command 'name'.
99 static BOOL FindCommandinPath(STRPTR name, BOOL checkAll, struct FileInfoBlock *fib);
103 * Handle absolute path for the command 'name'.
106 static BOOL FindCommandInAbsolutePath(STRPTR name, TEXT *colon, struct FileInfoBlock *fib);
110 * Check the C: multiassign for the command 'name'.
112 static BOOL FindCommandinC(STRPTR name, BOOL checkAll, struct FileInfoBlock *fib);
116 * Look in the current directory for the command 'name'.
118 static BOOL CheckDirectory(STRPTR name, BOOL directory, struct FileInfoBlock *fib);
122 * Get a string that specifies the full path to a file or NULL if there
123 * was not enough memory to allocate the string. This string should be
124 * freed using FreeVec().
126 static STRPTR GetFullPath(BPTR lock);
129 int __nocommandline;
131 int main(void)
133 /* Array filled by ReadArgs() call */
134 IPTR args[ARG_COUNT] = {0, 0, 0, 0};
136 struct RDArgs *rda; /* ReadArgs standard struct */
137 BOOL found = FALSE; /* Indicates whether we've found a file
138 or not -- used for ALL ReadArgs() tag. */
139 int error = RETURN_WARN; /* Error value to return */
141 struct FileInfoBlock *fib; /* Used in Examine(). Allocated at top level
142 to skip multiple calls to
143 AllocDosObject() / FreeDosObject */
145 if((rda = ReadArgs("FILE/A,NORES/S,RES/S,ALL/S", args, NULL)) != NULL)
147 BOOL noRes = (BOOL)args[1]; /* Don't check resident commands */
148 BOOL resOnly = (BOOL)args[2]; /* Check resident commands only */
149 BOOL checkAll = (BOOL)args[3]; /* Check for multiple occurances */
151 STRPTR commandName = (STRPTR)args[0]; /* Command to look for */
153 TEXT *colon;
155 fib = AllocDosObject(DOS_FIB, NULL);
157 if(fib != NULL)
159 if (!resOnly && (colon = strchr(commandName, ':')))
161 /* Check for absolute path */
162 found |= FindCommandInAbsolutePath(commandName, colon, fib);
163 D(bug("Absolute path\n"));
165 else
167 if(!found && !noRes)
169 /* Check resident lists */
170 found |= FindResidentCommand(commandName);
171 D(bug("Resident list\n"));
174 if(!found && !resOnly)
176 /* Check all available paths */
177 found |= FindCommandinPath(commandName, checkAll, fib);
178 D(bug("Path\n"));
181 if(!found && !resOnly)
183 /* Check C: multiassign */
184 found |= FindCommandinC(commandName, checkAll, fib);
185 D(bug("C:\n"));
189 if (found)
191 error = RETURN_OK;
194 FreeDosObject(DOS_FIB, fib);
197 FreeArgs(rda);
199 else
201 PrintFault(IoErr(), "Which");
202 error = RETURN_FAIL;
205 return error;
209 /* NOTE: The filesystemtask stuff is only necessary for this to work
210 correctly on AmigaOS. AROS doesn't use this concept. */
211 static BOOL FindCommandinC(STRPTR name, BOOL checkAll, struct FileInfoBlock *fib)
213 BOOL found = FALSE; /* Object found? */
214 struct DevProc *dp = NULL; /* For GetDeviceProc() call */
215 BPTR oldCurDir; /* Temporary holder of old current dir */
216 // struct MsgPort *oldFST; /* Temporary holder of old FileSysTask */
218 /* If FilePart(name) is not name itself, it can't be in the C: directory;
219 or rather, it isn't in the C: directory or we found it in
220 FindCommandinPath(). */
221 if(FilePart(name) != name)
222 return FALSE;
224 oldCurDir = CurrentDir(BNULL); /* Just to save the old current dir... */
225 // oldFST = GetFileSysTask(); /* ... and the filesystem task */
227 while(((dp = GetDeviceProc("C:", dp)) != NULL) && (!found || checkAll))
229 // SetFileSysTask(dp2->dvp_Port);
230 CurrentDir(dp->dvp_Lock);
231 found |= CheckDirectory(name, FALSE, fib);
233 /* Is this a multi assign? */
234 if(!(dp->dvp_Flags & DVPF_ASSIGN))
235 break;
238 // SetFileSysTask(oldFST);
239 CurrentDir(oldCurDir);
240 FreeDeviceProc(dp);
242 return found;
246 static BOOL FindCommandinPath(STRPTR name, BOOL checkAll, struct FileInfoBlock *fib)
248 BOOL found; /* Have we found the 'file' yet? */
249 BPTR oldCurDir; /* Space to store the current dir */
250 BPTR *paths; /* Loop variable */
252 struct CommandLineInterface *cli = Cli();
253 D(bug("checkAll = %ld\n", checkAll));
254 /* Can this happen at all? */
255 if(cli == NULL)
256 return FALSE;
258 /* Check the current directory */
259 D(bug("Calling CheckDirectory()\n"));
260 found = CheckDirectory(name, FALSE, fib);
262 oldCurDir = CurrentDir(BNULL);
264 /* Check all paths */
265 paths = (BPTR *)BADDR(cli->cli_CommandDir);
266 D(bug("paths = 0x%08lx\n", paths));
268 while((!found || checkAll) && (paths != NULL))
270 CurrentDir(paths[1]);
272 D(bug("Calling CheckDirectory()\n"));
273 found |= CheckDirectory(name, FALSE, fib);
275 paths = (BPTR *)BADDR(paths[0]); /* Go on with the next path */
278 CurrentDir(oldCurDir);
280 return found;
284 static BOOL FindCommandInAbsolutePath(STRPTR name, TEXT *colon, struct FileInfoBlock *fib)
286 BOOL found; /* Have we found the 'file' yet? */
288 if (*(colon + 1) == '\0')
290 /* In case path ends with ':' we print the directory */
291 D(bug("Path ends with ':'\n"));
292 /* Check the current directory */
293 D(bug("Calling CheckDirectory()\n"));
294 found = CheckDirectory(name, TRUE, fib);
296 else
298 /* Check the current directory */
299 D(bug("Calling CheckDirectory()\n"));
300 found = CheckDirectory(name, FALSE, fib);
303 return found;
307 static BOOL CheckDirectory(STRPTR name, BOOL directory, struct FileInfoBlock *fib)
309 BPTR lock; /* Lock on 'name' */
310 BOOL found = FALSE; /* For return value purposes */
311 STRPTR pathName;
313 lock = Lock(name, SHARED_LOCK);
315 D(bug("Locked command %s\n", name));
317 if(lock != BNULL)
319 D(bug("Calling Examine()\n"));
321 if(Examine(lock, fib) == DOSTRUE)
323 D(bug("Calling GetFullPath()\n"));
325 pathName = GetFullPath(lock);
327 if(pathName != NULL)
329 /* File or directory? */
330 if(fib->fib_DirEntryType < 0)
332 /* FIBF_EXECUTE is active low! */
333 if(!(fib->fib_Protection & FIBF_EXECUTE))
335 Printf("%s\n", pathName);
336 found = TRUE;
339 else if (directory)
341 Printf("%s\n", pathName);
342 found = TRUE;
345 FreeVec(pathName); /* Free memory holding the full path name */
349 UnLock(lock);
352 return found;
356 static STRPTR GetFullPath(BPTR lock)
358 UBYTE *buf; /* Pointer to the memory allocated for the string */
359 ULONG size; /* Holder of the (growing) size of the string */
361 for(size = 512; ; size += 512)
363 buf = AllocVec(size, MEMF_ANY);
365 if(buf == NULL)
366 break;
368 if(NameFromLock(lock, buf, size))
370 return (STRPTR)buf;
373 FreeVec(buf);
376 return NULL;
380 static BOOL FindResidentCommand(STRPTR name)
382 BOOL found = FALSE; /* For return value purposes */
383 struct Segment *seg; /* Holder of segment if 'name' is a
384 resident command */
386 /* Look in both system and normal list. Or rather, in the normal list
387 ONLY if it wasn't found in the system list. This is what the Amiga
388 Which does thus not giving the whole picture if you have 'cmd'
389 resident while 'cmd' is an internal command also. However, if this
390 is the case, you may never access it as the system list is searched
391 first by the Shell? */
392 if((seg = FindSegment(name, NULL, TRUE)) == NULL)
394 seg = FindSegment(name, NULL, FALSE);
397 if(seg != NULL)
399 found = TRUE;
401 if(seg->seg_UC == CMD_INTERNAL)
403 Printf("INTERNAL %s\n", name);
405 else if(seg->seg_UC == CMD_DISABLED)
407 Printf("INTERNAL %s ;(DISABLED)\n", name);
409 else
410 Printf("RES %s\n", name);
413 return found;