Update to lasso handling. Adjust scroll amount based on difference between mouse...
[AROS.git] / rom / dos / getdeviceproc.c
blobcde33ce8fc09117692f989fd121b7cb908952616
1 /*
2 Copyright © 1995-2008, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: GetDeviceProc() - Find the filesystem for a path.
6 Lang: english
7 */
8 #include "dos_intern.h"
9 #include <proto/exec.h>
10 #include <proto/utility.h>
12 #define DEBUG 0
13 #include <aros/debug.h>
15 static BOOL VolumeIsOffline(struct DosList *dl);
17 /*****************************************************************************
19 NAME */
20 #include <proto/dos.h>
22 AROS_LH2(struct DevProc *, GetDeviceProc,
24 /* SYNOPSIS */
25 AROS_LHA(CONST_STRPTR, name, D1),
26 AROS_LHA(struct DevProc *, dp, D2),
28 /* LOCATION */
29 struct DosLibrary *, DOSBase, 107, Dos)
31 /* FUNCTION
32 GetDeviceProc() will search for the filesystem handler which
33 you should send a command to for a specific path.
35 By calling GetDeviceProc() multiple times, the caller will
36 be able to handle multi-assign paths.
38 The first call to GetDeviceProc() should have the |dp| parameter
39 as NULL.
41 INPUTS
42 name - Name of the object to find.
43 dp - Previous result of GetDeviceProc() or NULL.
45 RESULT
46 A pointer to a DevProc structure containing the information
47 required to send a command to a filesystem.
49 NOTES
51 EXAMPLE
53 BUGS
54 Currently doesn't return dvp_DevNode for locks which are
55 relative to "PROGDIR:", ":", or the current directory.
57 SEE ALSO
58 FreeDeviceProc()
60 INTERNALS
62 *****************************************************************************/
64 AROS_LIBFUNC_INIT
66 struct Process *pr = (struct Process *)FindTask(NULL);
67 struct DosList *dl = NULL;
68 char vol[32];
69 LONG len;
70 char buf[256];
71 BPTR cur = NULL, lock = NULL;
72 BOOL stdio = FALSE;
73 BOOL res;
75 /* if they passed us the result of a previous call, then they're wanted to
76 * loop over the targets of a multidirectory assign */
77 if (dp != NULL) {
79 /* if what they passed us is not a multidirectory assign, then there's
80 * nothing for us to do */
81 if (dp->dvp_DevNode != NULL &&
82 (dp->dvp_DevNode->dol_Type != DLT_DIRECTORY || !(dp->dvp_Flags & DVPF_ASSIGN))) {
84 /* cleanup */
85 if (dp->dvp_Flags & DVPF_UNLOCK)
86 UnLock(dp->dvp_Lock);
88 FreeMem(dp, sizeof(struct DevProc));
89 return NULL;
92 /* it's fine, we'll start from here */
93 dl = dp->dvp_DevNode;
95 /* lock the dos list here, to match the result of the next block */
96 LockDosList(LDF_ALL | LDF_READ);
99 /* otherwise we need to find a place to start in the doslist based on the
100 * name they passed in */
101 else {
103 /* handle standard I/O streams as "virtual" devices */
104 if (Stricmp(name, "IN:") == 0 || Stricmp(name, "STDIN:") == 0) {
105 cur = pr->pr_CIS != NULL ? pr->pr_CIS : (BPTR) -1;
106 stdio = TRUE;
108 else if (Stricmp(name, "OUT:") == 0 || Stricmp(name, "STDOUT:") == 0) {
109 cur = pr->pr_COS != NULL ? pr->pr_COS : (BPTR) -1;
110 stdio = TRUE;
112 else if (Stricmp(name, "ERR:") == 0 || Stricmp(name, "STDERR:") == 0) {
113 cur = pr->pr_CES != NULL ? pr->pr_CES :
114 pr->pr_COS != NULL ? pr->pr_COS : (BPTR) -1;
115 stdio = TRUE;
118 if (stdio) {
119 /* handle doesn't exist */
120 if (cur == (BPTR) -1) {
121 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
122 return NULL;
125 /* got it, make a fake devproc */
126 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
127 SetIoErr(ERROR_NO_FREE_STORE);
128 return NULL;
131 /* we need a lock for the devproc */
132 if ((lock = DupLockFromFH(cur)) == NULL) {
133 FreeMem(dp, sizeof(struct DevProc));
134 return NULL;
137 /* build the devproc for return */
138 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(lock))->fh_Device;
139 dp->dvp_Lock = lock;
140 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
142 /* finding the device node
143 * XXX this is naive - if the device appears on the doslist twice
144 * we have no way to tell which one is ours. we can't even use
145 * NameFromLock() to get the volume name and then find the doslist
146 * entry from that as console handlers probably don't even
147 * implement names. bring on packets I say */
149 dl = LockDosList(LDF_ALL | LDF_READ);
150 while (dl != NULL
151 && ((struct MsgPort *)dl->dol_Ext.dol_AROS.dol_Device
152 != dp->dvp_Port))
153 dl = dl->dol_Next;
155 UnLockDosList(LDF_READ | LDF_ALL);
157 /* not found */
158 if (dl == NULL) {
159 FreeMem(dp, sizeof(struct DevProc));
160 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
161 return NULL;
164 /* take it */
165 dp->dvp_DevNode = dl;
167 return dp;
170 /* something real, work out what it's relative to */
171 if (Strnicmp(name, "PROGDIR:", 8) == 0) {
172 cur = pr->pr_HomeDir;
174 /* move past the "volume" name, so that we end up in the "no
175 * volume name" case below */;
176 name = &name[8];
179 else
180 cur = pr->pr_CurrentDir;
182 /* if we got NULL, then it's relative to the system root lock */
183 if (cur == NULL)
184 cur = DOSBase->dl_SYSLock;
186 /* extract the volume name */
187 len = SplitName(name, ':', vol, 0, sizeof(vol)-1);
189 /* move the name past it, it's now relative to the volume */
190 name += len;
192 /* if there wasn't one (or we found a lone ':'), then we need to
193 * extract it from the name of the current dir */
195 /* XXX this block sucks. NameFromLock () calls DupLock(), which calls
196 * Lock(), which would end up back here if it wasn't for the
197 * special-case in Lock(). once we have real FileLock locks, then this
198 * code will go and we can just look at cur->fl_Volume to get the
199 * doslist entry. see the morphos version for details */
200 if (len <= 1) {
202 /* if we didn't find a ':' at all, then we'll need to return the
203 * lock that it's relative to, so make a note */
204 if (len == -1)
205 lock = cur;
207 if (NameFromLock(cur, buf, 255) != DOSTRUE)
208 return NULL;
210 len = SplitName(buf, ':', vol, 0, sizeof(vol)-1);
212 /* if there isn't one, something is horribly wrong */
213 if (len <= 1) {
214 kprintf("%s:%d: NameFromLock() returned a path without a volume. Probably a bug, report it!\n"
215 " GetDeviceProc() called for '%s'\n"
216 " NameFromLock() called on 0x%08x, returned '%s'\n",
217 __FILE__, __LINE__, name, cur, buf);
218 SetIoErr(ERROR_INVALID_COMPONENT_NAME);
219 return NULL;
223 /* allocate structure for return */
224 if ((dp = AllocMem(sizeof(struct DevProc), MEMF_ANY | MEMF_CLEAR)) == NULL) {
225 SetIoErr(ERROR_NO_FREE_STORE);
226 return NULL;
229 do {
230 /* now find the doslist entry for the named volume */
231 dl = LockDosList(LDF_ALL | LDF_READ);
232 dl = FindDosEntry(dl, vol, LDF_ALL);
234 /* not found, bail out */
235 if (dl == NULL) {
236 UnLockDosList(LDF_ALL | LDF_READ);
238 if (ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_INSERT, (ULONG) vol, NULL) == DOSTRUE) {
239 FreeMem(dp, sizeof(struct DevProc));
240 return NULL;
243 } while(dl == NULL);
245 /* relative to the current dir, then we have enough to get out of here */
246 if (lock != NULL) {
247 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(cur))->fh_Device;
248 dp->dvp_Lock = lock;
249 dp->dvp_Flags = 0;
250 dp->dvp_DevNode = dl;
252 UnLockDosList(LDF_ALL | LDF_READ);
254 return dp;
258 /* at this point, we have an allocated devproc in dp, the doslist is
259 * locked for read, and we have the entry for the named "volume"
260 * (device, assign, etc) in dl and a filename relative to that in name */
262 /* late assign. we resolve the target and then promote the doslist entry
263 * to full assign */
264 if (dl->dol_Type == DLT_LATE) {
265 /* obtain a lock on the target */
266 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
268 /* unlock the list, either we have a lock and need to assign it, or we
269 * don't and need to bail out */
270 UnLockDosList(LDF_ALL | LDF_READ);
272 /* XXX there's a race here. with the doslist unlocked, it's possible
273 * that some other process will add or remove this assign, blowing
274 * everything up. need more tuits before attempting a fix */
276 /* didn't find the target */
277 if (lock == NULL) {
278 FreeMem(dp, sizeof(struct DevProc));
279 return NULL;
282 /* try to assign it */
283 if (AssignLock(vol, lock) == DOSFALSE) {
284 UnLock(lock);
285 FreeMem(dp, sizeof(struct DevProc));
286 return NULL;
289 /* we made the assign! now we have to go back over the list and find
290 * the new entry */
291 dl = LockDosList(LDF_ALL | LDF_READ);
292 dl = FindDosEntry(dl, vol, LDF_ALL);
294 /* not found. XXX this will only happen if we hit that race above */
295 if (dl == NULL) {
296 UnLockDosList(LDF_ALL | LDF_READ);
297 FreeMem(dp, sizeof(struct DevProc));
298 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
299 return NULL;
302 /* the added entry will be a DLT_DIRECTORY, so we can just copy the
303 * details in and get out of here */
304 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
305 dp->dvp_Lock = dl->dol_Lock;
306 dp->dvp_Flags = 0;
307 dp->dvp_DevNode = dl;
309 UnLockDosList(LDF_ALL | LDF_READ);
311 return dp;
314 /* nonbinding assign. like a late assign, but with no doslist promotion */
315 if (dl->dol_Type == DLT_NONBINDING) {
316 lock = Lock(dl->dol_misc.dol_assign.dol_AssignName, SHARED_LOCK);
318 /* just fill out the dp and return */
319 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(lock))->fh_Device;
320 dp->dvp_Lock = lock;
321 dp->dvp_Flags = DVPF_UNLOCK; /* remember to unlock in FreeDeviceNode() */
322 dp->dvp_DevNode = dl;
324 UnLockDosList(LDF_ALL | LDF_READ);
326 return dp;
329 /* devices and volumes are easy */
330 if (dl->dol_Type == DLT_DEVICE || dl->dol_Type == DLT_VOLUME) {
331 res = TRUE;
332 if (dl->dol_Type == DLT_DEVICE) {
333 if (!dl->dol_Ext.dol_AROS.dol_Device) {
334 D(bug("Accessing offline device %s\n", dl->dol_Ext.dol_AROS.dol_DevName));
335 res = RunHandler((struct DeviceNode *)dl, DOSBase);
337 } else {
338 while (res && VolumeIsOffline(dl)) {
339 D(bug("Accessing offline volume %s\n", dl->dol_Ext.dol_AROS.dol_DevName));
340 res = !ErrorReport(ERROR_DEVICE_NOT_MOUNTED, REPORT_VOLUME, (ULONG)dl, NULL);
343 if (!res) {
344 UnLockDosList(LDF_ALL | LDF_READ);
345 FreeMem(dp, sizeof(struct DevProc));
346 SetIoErr(ERROR_DEVICE_NOT_MOUNTED);
347 return NULL;
349 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
350 dp->dvp_Lock = NULL;
351 dp->dvp_Flags = 0;
352 dp->dvp_DevNode = dl;
354 UnLockDosList(LDF_ALL | LDF_READ);
356 return dp;
359 /* sanity check */
360 if (dl->dol_Type != DLT_DIRECTORY) {
361 UnLockDosList(LDF_ALL | LDF_READ);
362 FreeMem(dp, sizeof(struct DevProc));
363 kprintf("%s:%d: DosList entry 0x%08x has unknown type %d. Probably a bug, report it!\n"
364 " GetDeviceProc() called for '%s'\n",
365 __FILE__, __LINE__, dl, dl->dol_Type, name);
366 SetIoErr(ERROR_BAD_NUMBER);
367 return NULL;
370 /* real assigns. first, see if it's just pointing to a single dir */
371 if (dp->dvp_Flags != DVPF_ASSIGN) {
372 /* just a plain assign, easy */
373 dp->dvp_Port = (struct MsgPort *) dl->dol_Ext.dol_AROS.dol_Device;
374 dp->dvp_Lock = dl->dol_Lock;
375 dp->dvp_DevNode = dl;
377 /* note multidirectory assigns so the caller knows to loop */
378 dp->dvp_Flags = dl->dol_misc.dol_assign.dol_List != NULL ? DVPF_ASSIGN : 0;
380 UnLockDosList(LDF_ALL | LDF_READ);
382 return dp;
385 /* finally the tricky bit - multidirectory assigns */
387 /* if we're pointing at the "primary" lock, then we just take the first
388 * one in the list */
389 if (dp->dvp_Lock == dl->dol_Lock)
390 dp->dvp_Lock = dl->dol_misc.dol_assign.dol_List->al_Lock;
392 /* otherwise we're finding the next */
393 else {
394 struct AssignList *al = dl->dol_misc.dol_assign.dol_List;
396 /* find our current lock (ie the one we returned last time) */
397 for (; al != NULL && al->al_Lock != dp->dvp_Lock; al = al->al_Next);
399 /* if we didn't find it, or we didn't but there's none after it, then
400 * we've run out */
401 if (al == NULL || (al = al->al_Next) == NULL) {
402 UnLockDosList(LDF_ALL | LDF_READ);
403 FreeMem(dp, sizeof(struct DevProc));
405 SetIoErr(ERROR_NO_MORE_ENTRIES);
406 return NULL;
409 /* fill out the lock from the new entry */
410 dp->dvp_Lock = al->al_Lock;
413 /* final pieces */
414 dp->dvp_Port = (struct MsgPort *) ((struct FileHandle *) BADDR(dp->dvp_Lock))->fh_Device;
415 dp->dvp_Flags = DVPF_ASSIGN;
416 dp->dvp_DevNode = dl;
418 UnLockDosList(LDF_READ|LDF_ALL);
420 /* phew */
421 SetIoErr(0);
422 return dp;
424 AROS_LIBFUNC_EXIT
425 } /* GetDeviceProc */
427 /* Attempt to start a handler for the DeviceNode */
428 BOOL RunHandler(struct DeviceNode *deviceNode, struct DosLibrary *DOSBase)
430 struct MsgPort *mp;
431 struct IOFileSys *iofs;
432 BOOL ok = FALSE;
434 mp = CreateMsgPort();
436 if (mp != NULL)
438 iofs = (struct IOFileSys *)CreateIORequest(mp, sizeof(struct IOFileSys));
440 if (iofs != NULL)
442 STRPTR handler;
443 struct FileSysStartupMsg *fssm;
444 ULONG fssmFlags = 0;
446 if (deviceNode->dn_Handler == NULL)
448 handler = "afs.handler";
450 else
452 handler = AROS_BSTR_ADDR(deviceNode->dn_Handler);
455 /* FIXME: this assumes that dol_Startup points to struct FileSysStartupMsg.
456 This is not true for plain handlers, dol_Startup is a BSTR in this case.
457 In order to make use of this we should implement direct support for
458 packet-style handlers in dos.library */
459 fssm = (struct FileSysStartupMsg *)BADDR(deviceNode->dn_Startup);
460 if (fssm != NULL)
462 iofs->io_Union.io_OpenDevice.io_DeviceName = AROS_BSTR_ADDR(fssm->fssm_Device);
463 iofs->io_Union.io_OpenDevice.io_Unit = fssm->fssm_Unit;
464 iofs->io_Union.io_OpenDevice.io_Environ = (IPTR *)BADDR(fssm->fssm_Environ);
465 fssmFlags = fssm->fssm_Flags;
467 iofs->io_Union.io_OpenDevice.io_DosName = deviceNode->dn_Ext.dn_AROS.dn_DevName;
468 iofs->io_Union.io_OpenDevice.io_DeviceNode = deviceNode;
470 D(bug("Starting up %s\n", handler));
471 if (!OpenDevice(handler, 0, &iofs->IOFS, fssmFlags) ||
472 !OpenDevice("packet.handler", 0, &iofs->IOFS, fssmFlags))
474 /* Ok, this means that the handler was able to open. */
475 D(bug("Handler started\n"));
476 deviceNode->dn_Ext.dn_AROS.dn_Device = iofs->IOFS.io_Device;
477 deviceNode->dn_Ext.dn_AROS.dn_Unit = iofs->IOFS.io_Unit;
478 ok = TRUE;
481 DeleteIORequest(&iofs->IOFS);
484 DeleteMsgPort(mp);
486 return ok;
489 static BOOL VolumeIsOffline(struct DosList *dl)
491 if (strcmp(dl->dol_Ext.dol_AROS.dol_Device->dd_Library.lib_Node.ln_Name,
492 "packet.handler"))
493 return !dl->dol_Ext.dol_AROS.dol_Unit;
494 else
495 return !dl->dol_Task;