3 (C) 1995-2011 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>
17 #include <aros/debug.h>
19 #include <clib/debug_protos.h>
22 #include <exec/types.h>
23 #include <exec/lists.h>
24 #include <exec/memory.h>
25 #include <exec/execbase.h>
27 #include <dos/dosextens.h>
28 #include <dos/exall.h>
29 #include <utility/tagitem.h>
30 #include <proto/dos.h>
31 #include <proto/exec.h>
35 #define DEBUG_ASSIGN(x)
37 /******************************************************************************
42 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
43 [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]
47 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
48 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
56 ASSIGN creates a reference to a file or directory. The reference
57 is a logical device name which makes it very convenient to specify
58 assigned objects using the reference instead of their paths.
60 If the NAME and TARGET arguments are given, ASSIGN assigns the
61 given logical name to the specified target. If the NAME given is
62 already assigned to a file or directory the new target replaces the
63 previous target. A colon must be included after the NAME argument.
65 If only the NAME argument is given, any assigns to that NAME are
66 removed. If no arguments whatsoever are given, all logical
71 NAME -- the name that should be assigned to a file or directory
72 TARGET -- one or more files or directories to assign the NAME to
73 LIST -- list all assigns made
74 EXISTS -- display the target of the specified assign. If NAME is
75 not assigned, set the condition flag to WARN
76 DISMOUNT -- remove the volume or device NAME from the dos list
77 DEFER -- make an ASSIGN to a path or directory that not need to
78 exist at the time of assignment. The first time the
79 NAME is referenced the NAME is bound to the object
80 PATH -- path is assigned with a non-binding assign. This means
81 that the assign is re-evaluated each time a reference
82 to NAME is done. Like for DEFER, the path doesn't have
83 to exist when the ASSIGN command is executed
84 ADD -- don't replace an assign but add another object for a
86 REMOVE -- remove an ASSIGN
87 VOLS -- show assigned volumes if in LIST mode
88 DIRS -- show assigned directories if in LIST mode
89 DEVICES -- show assigned devices if in LIST mode
104 The assign command has many switches. This together with the somewhat
105 messy handling of DosList:s by dos.library makes the operation rather
108 There are some fundamental building blocks that defines the semantics
109 of the Assign command.
111 Only one of the switches ADD, REMOVE, PATH and DEFER may be specified.
113 If EXISTS is specified, only the name parameter is important.
115 The implementation is split up in two fundamental procedures.
117 doAssign() -- make [a number of] assigns
118 showAssigns() -- show the available assigns
119 checkAssign() -- check if a particular assign exists
121 ******************************************************************************/
124 #define AROS_BSTR_strlen(s) *((UBYTE *)BADDR(s))
128 #define AROS_ASMSYMNAME(s) (&s)
130 static const int __abox__
= 1;
131 static const char version
[] = "\0$VER: Assign unofficial 50.9 (18.10.07) © AROS" ;
133 static const char version
[] __attribute__((used
)) = "\0$VER: Assign 50.9 (18.10.07) © AROS" ;
138 struct ExecBase
*ld_SysBase
;
139 struct DosLibrary
*ld_DOSBase
;
140 struct MinList ld_DeferList
;
143 /* FIXME: Remove these #define xxxBase hacks
144 Do not use this in new code !
146 #define SysBase ld->ld_SysBase
147 #define DOSBase ld->ld_DOSBase
148 #define DeferList ld->ld_DeferList
153 static int Main(struct ExecBase
*sBase
);
154 static int checkAssign(struct localdata
*ld
, STRPTR name
);
155 static int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
156 BOOL add
, BOOL remove
);
157 static void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
);
158 static int removeAssign(struct localdata
*ld
, STRPTR name
);
159 static STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
);
161 static void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
);
162 static void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, ...);
163 static void _DeferFlush(struct localdata
*ld
, BPTR fh
);
165 #define DeferPutStr(str) _DeferPutStr(ld,str)
166 #define DeferPrintf(fmt,...) \
167 DEBUG_ASSIGN(kprintf(fmt, __VA_ARGS__);) \
168 _DeferVPrintf(ld, fmt, __VA_ARGS__);
169 #define DeferFlush(fh) _DeferFlush(ld,fh)
173 const UBYTE
template[] =
203 __startup
AROS_PROCH(Start
, argstr
, argsize
, sBase
)
210 static int Main(struct ExecBase
*sBase
)
212 struct localdata _ld
, *ld
= &_ld
;
213 struct RDArgs
*readarg
;
214 struct ArgList arglist
;
215 struct ArgList
*MyArgList
= &arglist
;
216 int error
= RETURN_OK
;
219 DOSBase
= (struct DosLibrary
*) OpenLibrary("dos.library",37);
222 memset(&arglist
, 0, sizeof(arglist
));
226 readarg
= ReadArgs(template, (IPTR
*)MyArgList
, NULL
);
229 /* Verify mutually exclusive args
231 if ((MyArgList
->add
!=0) + (MyArgList
->remove
!=0) + (MyArgList
->path
!=0) + (MyArgList
->defer
!=0) > 1)
233 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
235 CloseLibrary((struct Library
*) DOSBase
);
245 /* Correct assign name construction? The rule is that the device name
246 * should end with a colon at the same time as no other colon may be
249 pos
= strchr(MyArgList
->name
, ':');
252 Printf("Invalid device name %s\n", (IPTR
)MyArgList
->name
);
254 CloseLibrary((struct Library
*) DOSBase
);
259 /* If the EXISTS keyword is specified, we only care about NAME */
260 if (MyArgList
->exists
)
262 error
= checkAssign(ld
, MyArgList
->name
);
263 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error
));
265 else if (MyArgList
->name
)
267 /* If a NAME is specified, our primary task is to add or
270 error
= doAssign(ld
, MyArgList
->name
, MyArgList
->target
, MyArgList
->dismount
, MyArgList
->defer
,
271 MyArgList
->path
, MyArgList
->add
, MyArgList
->remove
);
272 DEBUG_ASSIGN(Printf("doassign error %ld\n",error
));
275 /* With the LIST keyword, the current assigns will be
276 displayed also when (after) making an assign */
278 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
283 /* If no NAME was given, we just show the current assigns
284 as specified by the user (VOLS, DIRS, DEVICES) */
286 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
292 CloseLibrary((struct Library
*) DOSBase
);
295 DEBUG_ASSIGN(Printf("error %ld\n", error
));
302 void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
)
304 ULONG lockBits
= LDF_READ
;
307 /* If none of the parameters are specified, everything should be
309 if (!(vols
|| dirs
|| devices
))
316 lockBits
|= vols
? LDF_VOLUMES
: 0;
317 lockBits
|= dirs
? LDF_ASSIGNS
: 0;
318 lockBits
|= devices
? LDF_DEVICES
: 0;
320 dl
= LockDosList(lockBits
);
322 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
323 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
328 struct DosList
*tdl
= dl
;
330 DeferPutStr("Volumes:\n");
332 /* Print all mounted volumes */
333 while ((tdl
= NextDosEntry(tdl
, LDF_VOLUMES
)))
335 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
341 struct DosList
*tdl
= dl
;
344 DeferPutStr("\nDirectories:\n");
346 /* Print all assigned directories */
347 while ((tdl
= NextDosEntry(tdl
, LDF_ASSIGNS
)))
349 DeferPrintf("%b ", tdl
->dol_Name
);
351 for (count
= 14 - AROS_BSTR_strlen(tdl
->dol_Name
); count
> 0; count
--)
356 switch (tdl
->dol_Type
)
359 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
363 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
368 STRPTR dirName
; /* For NameFromLock() */
369 struct AssignList
*nextAssign
; /* For multiassigns */
371 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
375 DeferPutStr(dirName
);
380 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
384 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
388 DeferPrintf(" + %s\n", (IPTR
)dirName
);
392 nextAssign
= nextAssign
->al_Next
;
398 } /* while (NextDosEntry()) */
403 struct DosList
*tdl
= dl
;
404 int count
= 0; /* Used to make sure that as most 5 entries are printed per line */
406 DeferPutStr("\nDevices:\n");
408 /* Print all assigned devices */
409 while ((tdl
= NextDosEntry(tdl
, LDF_DEVICES
)))
411 DeferPrintf("%b%lc", tdl
->dol_Name
, ++count
% 5 ? ' ' : '\n');
420 UnLockDosList(lockBits
);
422 DeferFlush(Output());
427 STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
)
429 STRPTR buf
; /* Pointer to the memory allocated for the string */
430 ULONG size
; /* Holder of the (growing) size of the string */
432 for (size
= 512; ; size
+= 512)
434 buf
= AllocVec(size
, MEMF_ANY
);
440 if (NameFromLock(lock
, buf
, size
))
447 if (IoErr() != ERROR_LINE_TOO_LONG
)
458 int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
459 BOOL add
, BOOL remove
)
465 int error
= RETURN_OK
;
475 tp
=(struct Process
*)FindTask(NULL
);
476 tp
->pr_WindowPtr
= (APTR
)-1;
477 dp
= DeviceProc(name
);
478 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp
));
481 success
= DoPkt(dp
,ACTION_DIE
,0,0,0,0,0);
483 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success
));
487 colon
= strchr(name
, ':');
489 *colon
= '\0'; /* Remove trailing colon; name[] is changed! */
491 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name
));
493 /* This is a little bit messy... We first remove the 'name' assign
494 * and later in the loop the target assigns.
499 if ((!success
) && (ioerr
== ERROR_ACTION_NOT_KNOWN
))
504 DEBUG_ASSIGN(PutStr("Removing device node\n"));
505 dl
= LockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
507 fdl
= FindDosEntry(dl
, name
, LDF_VOLUMES
| LDF_DEVICES
);
509 /* Note the ! for conversion to boolean value */
512 success
= RemDosEntry(fdl
);
516 ioerr
= ERROR_OBJECT_IN_USE
;
518 ioerr
= ERROR_OBJECT_NOT_FOUND
;
520 UnLockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
525 if (target
== NULL
|| *target
== NULL
)
527 error
= removeAssign(ld
, name
);
536 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
537 error
= success
? error
: RETURN_WARN
;
538 DEBUG_ASSIGN(Printf("error: %d\n", error
));
540 // The Loop over multiple targets starts here
544 for (i
= 0; target
[i
]; i
++)
548 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target
[i
]));
549 if (!(path
|| defer
|| dismount
))
551 lock
= Lock(target
[i
], SHARED_LOCK
);
555 Printf("Can't find %s\n", (IPTR
)target
[i
]);
562 if (!RemAssignList(name
, lock
))
564 Printf("Can't subtract %s from %s\n", (IPTR
)target
[i
], (IPTR
)name
);
571 if (!AssignPath(name
, target
[i
]))
575 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error
));
580 if (!AssignAdd(name
, lock
))
586 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error
));
588 /* Check if the assign doesn't exist at all. If so, create it.
589 * This fix bug id 145. - Piru
591 dl
= LockDosList(LDF_ASSIGNS
| LDF_READ
);
592 dl
= FindDosEntry(dl
, name
, LDF_ASSIGNS
);
593 UnLockDosList(LDF_ASSIGNS
| LDF_READ
);
597 if (AssignLock(name
, lock
))
605 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error
));
612 if (error
&& ioerr
!= ERROR_OBJECT_EXISTS
)
614 Printf("Can't add %s to %s\n", (IPTR
)target
[i
], (IPTR
)name
);
620 if (!AssignLate(name
, target
[i
]))
625 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error
));
630 /* If no extra parameters are specified we just do a regular
631 assign (replacing any possible previous assign with that
632 name. The case of target being NULL is taken care of above.
634 if (!AssignLock(name
, lock
))
640 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error
));
642 /* If there are several targets, the next ones have to be added. */
646 /* We break as soon as we get a serious error */
647 if (error
>= RETURN_FAIL
)
652 } /* loop through all targets */
657 if (ioerr
== ERROR_OBJECT_EXISTS
)
659 Printf("Can't %s %s\n", (IPTR
)(cancel
? "cancel" : "assign"), (IPTR
)name
);
663 PrintFault(ioerr
, NULL
);
672 int removeAssign(struct localdata
*ld
, STRPTR name
)
674 /* In case no target is given, the 'name' assign should be removed.
675 * The AmigaDOS semantics for this is apparently that the error
676 * code is never set even if the assign didn't exist.
679 if (!AssignLock(name
, BNULL
))
688 int checkAssign(struct localdata
*ld
, STRPTR name
)
692 int error
= RETURN_OK
;
697 colon
= strchr(name
, ':');
703 dl
= LockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
705 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
706 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
709 dl
= FindDosEntry(dl
, name
, LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
);
712 struct DosList
*tdl
= dl
;
715 switch (dl
->dol_Type
)
718 DeferPrintf("%b\n", tdl
->dol_Name
);
722 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
729 DeferPrintf("%b ", tdl
->dol_Name
);
731 for (count
= 14 - *((UBYTE
*)BADDR(tdl
->dol_Name
)); count
> 0; count
--)
736 switch (tdl
->dol_Type
)
739 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
743 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
748 STRPTR dirName
; /* For NameFromLock() */
749 struct AssignList
*nextAssign
; /* For multiassigns */
751 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
755 DeferPutStr(dirName
);
760 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
764 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
768 DeferPrintf(" + %s\n", (IPTR
)dirName
);
772 nextAssign
= nextAssign
->al_Next
;
782 DeferPrintf("%s: not assigned\n", (IPTR
)name
);
787 UnLockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
789 DeferFlush(Output());
798 /* Feferred printing routines - Piru
801 #define MAXDEFERBUF 4096
802 #define MAXOUTPUT 128
807 UBYTE buf
[MAXDEFERBUF
];
810 static void deferputch(UBYTE ch
, struct localdata
*ld
)
812 struct deferbufnode
*cur
;
817 cur
= (struct deferbufnode
*) GetTail(&DeferList
);
819 if (!cur
|| cur
->pos
>= MAXDEFERBUF
)
821 cur
= AllocMem(sizeof(struct deferbufnode
), MEMF_ANY
);
827 ADDTAIL(&DeferList
, cur
);
830 cur
->buf
[cur
->pos
] = ch
;
835 void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
)
839 DEBUG_ASSIGN(kprintf(str
));
847 AROS_UFH2S(void, deferputch_gate
,
848 AROS_UFHA(UBYTE
, ch
, D0
),
849 AROS_UFHA(struct localdata
*, ld
, A3
))
859 void deferputch_trampoline(void)
861 UBYTE ch
= (UBYTE
) REG_D0
;
862 struct localdata
*ld
= (struct localdata
*) REG_A3
;
868 const struct EmulLibEntry deferputch_gate
=
870 TRAP_LIBNR
, 0, (void (*)(void)) deferputch_trampoline
875 void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, ...)
877 AROS_SLOWSTACKFORMAT_PRE(fmt
);
878 RawDoFmt(fmt
, AROS_SLOWSTACKFORMAT_ARG(fmt
), (void (*)(void))AROS_ASMSYMNAME(deferputch_gate
), ld
);
879 AROS_SLOWSTACKFORMAT_POST(fmt
);
883 void _DeferFlush(struct localdata
*ld
, BPTR fh
)
885 struct deferbufnode
*node
;
890 while ((node
= (struct deferbufnode
*)REMHEAD(&DeferList
)))
893 LONG left
= node
->pos
;
895 while (!broken
&& left
)
899 if (SetSignal(0, SIGBREAKF_CTRL_C
) & SIGBREAKF_CTRL_C
)
905 len
= left
> MAXOUTPUT
? MAXOUTPUT
: left
;
907 Write(fh
, node
->buf
+ offs
, len
);
912 FreeMem(node
, sizeof(struct deferbufnode
));
919 PrintFault(ERROR_BREAK
, NULL
);