3 (C) 1995-2008 The AROS Development Team
4 (C) 2002-2005 Harry Sintonen
5 (C) 2005-2007 Pavel Fedin
8 Desc: Assign CLI command
12 #define AROS_ALMOST_COMPATIBLE
15 #include <aros/asmcall.h>
16 #include <proto/arossupport.h>
18 #include <clib/debug_protos.h>
21 #include <exec/types.h>
22 #include <exec/lists.h>
23 #include <exec/memory.h>
24 #include <exec/execbase.h>
26 #include <dos/dosextens.h>
27 #include <dos/exall.h>
28 #include <utility/tagitem.h>
29 #include <proto/dos.h>
30 #include <proto/exec.h>
34 #define DEBUG_ASSIGN(x)
36 /******************************************************************************
41 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
42 [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]
46 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
47 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
55 ASSIGN creates a reference to a file or directory. The reference
56 is a logical device name which makes it very convenient to specify
57 assigned objects using the reference instead of their paths.
59 If the NAME and TARGET arguments are given, ASSIGN assigns the
60 given logical name to the specified target. If the NAME given is
61 already assigned to a file or directory the new target replaces the
62 previous target. A colon must be included after the NAME argument.
64 If only the NAME argument is given, any assigns to that NAME are
65 removed. If no arguments whatsoever are given, all logical
70 NAME -- the name that should be assigned to a file or directory
71 TARGET -- one or more files or directories to assign the NAME to
72 LIST -- list all assigns made
73 EXISTS -- display the target of the specified assign. If NAME is
74 not assigned, set the condition flag to WARN
75 DISMOUNT -- remove the volume or device NAME from the dos list
76 DEFER -- make an ASSIGN to a path or directory that not need to
77 exist at the time of assignment. The first time the
78 NAME is referenced the NAME is bound to the object
79 PATH -- path is assigned with a non-binding assign. This means
80 that the assign is re-evaluated each time a reference
81 to NAME is done. Like for DEFER, the path doesn't have
82 to exist when the ASSIGN command is executed
83 ADD -- don't replace an assign but add another object for a
85 REMOVE -- remove an ASSIGN
86 VOLS -- show assigned volumes if in LIST mode
87 DIRS -- show assigned directories if in LIST mode
88 DEVICES -- show assigned devices if in LIST mode
103 The assign command has many switches. This together with the somewhat
104 messy handling of DosList:s by dos.library makes the operation rather
107 There are some fundamental building blocks that defines the semantics
108 of the Assign command.
110 Only one of the switches ADD, REMOVE, PATH and DEFER may be specified.
112 If EXISTS is specified, only the name parameter is important.
114 The implementation is split up in two fundamental procedures.
116 doAssign() -- make [a number of] assigns
117 showAssigns() -- show the available assigns
118 checkAssign() -- check if a particular assign exists
120 ******************************************************************************/
123 #define AROS_BSTR_strlen(s) *((UBYTE *)BADDR(s))
127 #define AROS_ASMSYMNAME(s) (&s)
129 static const int __abox__
= 1;
130 static const char version
[] = "\0$VER: Assign unofficial 50.9 (18.10.07) © AROS" ;
132 static const char version
[] = "\0$VER: Assign 50.9 (18.10.07) © AROS" ;
137 struct ExecBase
*ld_SysBase
;
138 struct DosLibrary
*ld_DOSBase
;
139 struct MinList ld_DeferList
;
142 #define SysBase ld->ld_SysBase
143 #define DOSBase ld->ld_DOSBase
144 #define DeferList ld->ld_DeferList
149 static int Main(struct ExecBase
*sBase
);
150 static int checkAssign(struct localdata
*ld
, STRPTR name
);
151 static int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
152 BOOL add
, BOOL remove
);
153 static void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
);
154 static int removeAssign(struct localdata
*ld
, STRPTR name
);
155 static STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
);
157 static void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
);
158 static void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, IPTR
*args
);
159 static void _DeferFlush(struct localdata
*ld
, BPTR fh
);
161 #define DeferPutStr(str) _DeferPutStr(ld,str)
162 #define DeferPrintf(fmt,args...) \
163 DEBUG_ASSIGN(kprintf(fmt, ## args);) \
164 do { IPTR __args[] = {0 , ## args}; _DeferVPrintf(ld, fmt, __args + 1); } while (0)
165 #define DeferFlush(fh) _DeferFlush(ld,fh)
169 const UBYTE
template[] =
200 AROS_UFH3(__startup
static int, Start
,
201 AROS_UFHA(char *, argstr
, A0
),
202 AROS_UFHA(ULONG
, argsize
, D0
),
203 AROS_UFHA(struct ExecBase
*, sBase
, A6
))
212 struct ExecBase
*sBase
;
214 sBase
= *((struct ExecBase
**) 4);
219 static int Main(struct ExecBase
*sBase
)
221 struct localdata _ld
, *ld
= &_ld
;
222 struct RDArgs
*readarg
;
223 struct ArgList arglist
;
224 struct ArgList
*MyArgList
= &arglist
;
225 int error
= RETURN_OK
;
228 DOSBase
= (struct DosLibrary
*) OpenLibrary("dos.library",37);
231 memset(&arglist
, 0, sizeof(arglist
));
235 readarg
= ReadArgs(template, (IPTR
*)MyArgList
, NULL
);
238 /* Verify mutually exclusive args
240 if ((MyArgList
->add
!=0) + (MyArgList
->remove
!=0) + (MyArgList
->path
!=0) + (MyArgList
->defer
!=0) > 1)
242 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
244 CloseLibrary((struct Library
*) DOSBase
);
254 /* Correct assign name construction? The rule is that the device name
255 * should end with a colon at the same time as no other colon may be
258 pos
= strchr(MyArgList
->name
, ':');
261 Printf("Invalid device name %s\n", (IPTR
)MyArgList
->name
);
263 CloseLibrary((struct Library
*) DOSBase
);
268 /* If the EXISTS keyword is specified, we only care about NAME */
269 if (MyArgList
->exists
)
271 error
= checkAssign(ld
, MyArgList
->name
);
272 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error
));
274 else if (MyArgList
->name
)
276 /* If a NAME is specified, our primary task is to add or
279 error
= doAssign(ld
, MyArgList
->name
, MyArgList
->target
, MyArgList
->dismount
, MyArgList
->defer
,
280 MyArgList
->path
, MyArgList
->add
, MyArgList
->remove
);
281 DEBUG_ASSIGN(Printf("doassign error %ld\n",error
));
284 /* With the LIST keyword, the current assigns will be
285 displayed also when (after) making an assign */
287 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
292 /* If no NAME was given, we just show the current assigns
293 as specified by the user (VOLS, DIRS, DEVICES) */
295 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
301 CloseLibrary((struct Library
*) DOSBase
);
304 DEBUG_ASSIGN(Printf("error %ld\n", error
));
311 void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
)
313 ULONG lockBits
= LDF_READ
;
316 /* If none of the parameters are specified, everything should be
318 if (!(vols
|| dirs
|| devices
))
325 lockBits
|= vols
? LDF_VOLUMES
: 0;
326 lockBits
|= dirs
? LDF_ASSIGNS
: 0;
327 lockBits
|= devices
? LDF_DEVICES
: 0;
329 dl
= LockDosList(lockBits
);
331 #warning "FIXME: GetFullPath() breaks LockDosList()'s Forbid()!"
332 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
336 struct DosList
*tdl
= dl
;
338 DeferPutStr("Volumes:\n");
340 /* Print all mounted volumes */
341 while ((tdl
= NextDosEntry(tdl
, LDF_VOLUMES
)))
343 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
349 struct DosList
*tdl
= dl
;
352 DeferPutStr("\nDirectories:\n");
354 /* Print all assigned directories */
355 while ((tdl
= NextDosEntry(tdl
, LDF_ASSIGNS
)))
357 DeferPrintf("%b ", tdl
->dol_Name
);
359 for (count
= 14 - AROS_BSTR_strlen(tdl
->dol_Name
); count
> 0; count
--)
364 switch (tdl
->dol_Type
)
367 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
371 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
376 STRPTR dirName
; /* For NameFromLock() */
377 struct AssignList
*nextAssign
; /* For multiassigns */
379 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
383 DeferPutStr(dirName
);
388 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
392 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
396 DeferPrintf(" + %s\n", (IPTR
)dirName
);
400 nextAssign
= nextAssign
->al_Next
;
406 } /* while (NextDosEntry()) */
411 struct DosList
*tdl
= dl
;
412 int count
= 0; /* Used to make sure that as most 5 entries are printed per line */
414 DeferPutStr("\nDevices:\n");
416 /* Print all assigned devices */
417 while ((tdl
= NextDosEntry(tdl
, LDF_DEVICES
)))
419 DeferPrintf("%b%lc", tdl
->dol_Name
, ++count
% 5 ? ' ' : '\n');
428 UnLockDosList(lockBits
);
430 DeferFlush(Output());
435 STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
)
437 STRPTR buf
; /* Pointer to the memory allocated for the string */
438 ULONG size
; /* Holder of the (growing) size of the string */
440 for (size
= 512; ; size
+= 512)
442 buf
= AllocVec(size
, MEMF_ANY
);
448 if (NameFromLock(lock
, buf
, size
))
455 if (IoErr() != ERROR_LINE_TOO_LONG
)
466 int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
467 BOOL add
, BOOL remove
)
473 int error
= RETURN_OK
;
481 /* TODO: AROS currently doesn't support packet handlers directly
482 and we currently don't support shutting down IOFS handlers */
484 ioerr
= ERROR_ACTION_NOT_KNOWN
;
489 tp
=(struct Process
*)FindTask(NULL
);
490 tp
->pr_WindowPtr
= (APTR
)-1;
491 dp
= DeviceProc(name
);
492 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp
));
495 success
= DoPkt(dp
,ACTION_DIE
,0,0,0,0,0);
497 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success
));
502 colon
= strchr(name
, ':');
504 *colon
= '\0'; /* Remove trailing colon; name[] is changed! */
506 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name
));
508 /* This is a little bit messy... We first remove the 'name' assign
509 * and later in the loop the target assigns.
514 if ((!success
) && (ioerr
== ERROR_ACTION_NOT_KNOWN
))
519 DEBUG_ASSIGN(PutStr("Removing device node\n"));
520 dl
= LockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
522 fdl
= FindDosEntry(dl
, name
, LDF_VOLUMES
| LDF_DEVICES
);
524 /* Note the ! for conversion to boolean value */
527 success
= RemDosEntry(fdl
);
531 ioerr
= ERROR_OBJECT_IN_USE
;
533 ioerr
= ERROR_OBJECT_NOT_FOUND
;
535 UnLockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
540 if (target
== NULL
|| *target
== NULL
)
542 error
= removeAssign(ld
, name
);
551 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
552 error
= success
? error
: RETURN_WARN
;
553 DEBUG_ASSIGN(Printf("error: %d\n", error
));
555 // The Loop over multiple targets starts here
559 for (i
= 0; target
[i
]; i
++)
563 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target
[i
]));
564 if (!(path
|| defer
|| dismount
))
566 lock
= Lock(target
[i
], SHARED_LOCK
);
570 Printf("Can't find %s\n", (IPTR
)target
[i
]);
577 if (!RemAssignList(name
, lock
))
579 Printf("Can't subtract %s from %s\n", (IPTR
)target
[i
], (IPTR
)name
);
586 if (!AssignPath(name
, target
[i
]))
590 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error
));
595 if (!AssignAdd(name
, lock
))
601 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error
));
603 /* Check if the assign doesn't exist at all. If so, create it.
604 * This fix bug id 145. - Piru
606 dl
= LockDosList(LDF_ASSIGNS
| LDF_READ
);
607 dl
= FindDosEntry(dl
, name
, LDF_ASSIGNS
);
608 UnLockDosList(LDF_ASSIGNS
| LDF_READ
);
612 if (AssignLock(name
, lock
))
620 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error
));
627 if (error
&& ioerr
!= ERROR_OBJECT_EXISTS
)
629 Printf("Can't add %s to %s\n", (IPTR
)target
[i
], (IPTR
)name
);
635 if (!AssignLate(name
, target
[i
]))
640 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error
));
645 /* If no extra parameters are specified we just do a regular
646 assign (replacing any possible previous assign with that
647 name. The case of target being NULL is taken care of above.
649 if (!AssignLock(name
, lock
))
655 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error
));
657 /* If there are several targets, the next ones have to be added. */
661 /* We break as soon as we get a serious error */
662 if (error
>= RETURN_FAIL
)
667 } /* loop through all targets */
672 if (ioerr
== ERROR_OBJECT_EXISTS
)
674 Printf("Can't %s %s\n", (IPTR
)(cancel
? "cancel" : "assign"), (IPTR
)name
);
678 PrintFault(ioerr
, NULL
);
687 int removeAssign(struct localdata
*ld
, STRPTR name
)
689 /* In case no target is given, the 'name' assign should be removed.
690 * The AmigaDOS semantics for this is apparently that the error
691 * code is never set even if the assign didn't exist.
694 if (!AssignLock(name
, NULL
))
703 int checkAssign(struct localdata
*ld
, STRPTR name
)
707 int error
= RETURN_OK
;
712 colon
= strchr(name
, ':');
718 dl
= LockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
720 #warning "Note: GetFullPath() breaks LockDosList()'s Forbid()!"
721 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
723 dl
= FindDosEntry(dl
, name
, LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
);
726 struct DosList
*tdl
= dl
;
729 switch (dl
->dol_Type
)
732 DeferPrintf("%b\n", tdl
->dol_Name
);
736 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
743 DeferPrintf("%b ", tdl
->dol_Name
);
745 for (count
= 14 - *((UBYTE
*)BADDR(tdl
->dol_Name
)); count
> 0; count
--)
750 switch (tdl
->dol_Type
)
753 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
757 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
762 STRPTR dirName
; /* For NameFromLock() */
763 struct AssignList
*nextAssign
; /* For multiassigns */
765 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
769 DeferPutStr(dirName
);
774 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
778 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
782 DeferPrintf(" + %s\n", (IPTR
)dirName
);
786 nextAssign
= nextAssign
->al_Next
;
796 DeferPrintf("%s: not assigned\n", (IPTR
)name
);
801 UnLockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
803 DeferFlush(Output());
812 /* Feferred printing routines - Piru
815 #define MAXDEFERBUF 4096
816 #define MAXOUTPUT 128
821 UBYTE buf
[MAXDEFERBUF
];
824 static void deferputch(UBYTE ch
, struct localdata
*ld
)
826 struct deferbufnode
*cur
;
831 cur
= (struct deferbufnode
*) GetTail(&DeferList
);
833 if (!cur
|| cur
->pos
>= MAXDEFERBUF
)
835 cur
= AllocMem(sizeof(struct deferbufnode
), MEMF_ANY
);
841 ADDTAIL(&DeferList
, cur
);
844 cur
->buf
[cur
->pos
] = ch
;
849 void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
)
853 DEBUG_ASSIGN(kprintf(str
);)
861 AROS_UFH2(static void, deferputch_gate
,
862 AROS_UFHA(UBYTE
, ch
, D0
),
863 AROS_UFHA(struct localdata
*, ld
, A3
))
873 void deferputch_trampoline(void)
875 UBYTE ch
= (UBYTE
) REG_D0
;
876 struct localdata
*ld
= (struct localdata
*) REG_A3
;
882 const struct EmulLibEntry deferputch_gate
=
884 TRAP_LIBNR
, 0, (void (*)(void)) deferputch_trampoline
889 void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, IPTR
*args
)
891 RawDoFmt(fmt
, args
, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate
), ld
);
895 void _DeferFlush(struct localdata
*ld
, BPTR fh
)
897 struct deferbufnode
*node
;
902 while ((node
= REMHEAD(&DeferList
)))
905 LONG left
= node
->pos
;
907 while (!broken
&& left
)
911 if (SetSignal(0, SIGBREAKF_CTRL_C
) & SIGBREAKF_CTRL_C
)
917 len
= left
> MAXOUTPUT
? MAXOUTPUT
: left
;
919 Write(fh
, node
->buf
+ offs
, len
);
924 FreeMem(node
, sizeof(struct deferbufnode
));
931 PrintFault(ERROR_BREAK
, NULL
);