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
[] = "\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
, IPTR
*args
);
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 do { IPTR __args[] = {0 , AROS_PP_VARIADIC_CAST2IPTR(__VA_ARGS__) }; \
169 _DeferVPrintf(ld, fmt, __args + 1); } while (0)
170 #define DeferFlush(fh) _DeferFlush(ld,fh)
174 const UBYTE
template[] =
204 AROS_ENTRY(__startup
static int, Start
,
205 AROS_UFHA(char *, argstr
, A0
),
206 AROS_UFHA(ULONG
, argsize
, D0
),
207 struct ExecBase
*, sBase
)
214 static int Main(struct ExecBase
*sBase
)
216 struct localdata _ld
, *ld
= &_ld
;
217 struct RDArgs
*readarg
;
218 struct ArgList arglist
;
219 struct ArgList
*MyArgList
= &arglist
;
220 int error
= RETURN_OK
;
223 DOSBase
= (struct DosLibrary
*) OpenLibrary("dos.library",37);
226 memset(&arglist
, 0, sizeof(arglist
));
230 readarg
= ReadArgs(template, (IPTR
*)MyArgList
, NULL
);
233 /* Verify mutually exclusive args
235 if ((MyArgList
->add
!=0) + (MyArgList
->remove
!=0) + (MyArgList
->path
!=0) + (MyArgList
->defer
!=0) > 1)
237 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
239 CloseLibrary((struct Library
*) DOSBase
);
249 /* Correct assign name construction? The rule is that the device name
250 * should end with a colon at the same time as no other colon may be
253 pos
= strchr(MyArgList
->name
, ':');
256 Printf("Invalid device name %s\n", (IPTR
)MyArgList
->name
);
258 CloseLibrary((struct Library
*) DOSBase
);
263 /* If the EXISTS keyword is specified, we only care about NAME */
264 if (MyArgList
->exists
)
266 error
= checkAssign(ld
, MyArgList
->name
);
267 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error
));
269 else if (MyArgList
->name
)
271 /* If a NAME is specified, our primary task is to add or
274 error
= doAssign(ld
, MyArgList
->name
, MyArgList
->target
, MyArgList
->dismount
, MyArgList
->defer
,
275 MyArgList
->path
, MyArgList
->add
, MyArgList
->remove
);
276 DEBUG_ASSIGN(Printf("doassign error %ld\n",error
));
279 /* With the LIST keyword, the current assigns will be
280 displayed also when (after) making an assign */
282 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
287 /* If no NAME was given, we just show the current assigns
288 as specified by the user (VOLS, DIRS, DEVICES) */
290 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
296 CloseLibrary((struct Library
*) DOSBase
);
299 DEBUG_ASSIGN(Printf("error %ld\n", error
));
306 void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
)
308 ULONG lockBits
= LDF_READ
;
311 /* If none of the parameters are specified, everything should be
313 if (!(vols
|| dirs
|| devices
))
320 lockBits
|= vols
? LDF_VOLUMES
: 0;
321 lockBits
|= dirs
? LDF_ASSIGNS
: 0;
322 lockBits
|= devices
? LDF_DEVICES
: 0;
324 dl
= LockDosList(lockBits
);
326 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
327 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
332 struct DosList
*tdl
= dl
;
334 DeferPutStr("Volumes:\n");
336 /* Print all mounted volumes */
337 while ((tdl
= NextDosEntry(tdl
, LDF_VOLUMES
)))
339 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
345 struct DosList
*tdl
= dl
;
348 DeferPutStr("\nDirectories:\n");
350 /* Print all assigned directories */
351 while ((tdl
= NextDosEntry(tdl
, LDF_ASSIGNS
)))
353 DeferPrintf("%b ", tdl
->dol_Name
);
355 for (count
= 14 - AROS_BSTR_strlen(tdl
->dol_Name
); count
> 0; count
--)
360 switch (tdl
->dol_Type
)
363 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
367 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
372 STRPTR dirName
; /* For NameFromLock() */
373 struct AssignList
*nextAssign
; /* For multiassigns */
375 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
379 DeferPutStr(dirName
);
384 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
388 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
392 DeferPrintf(" + %s\n", (IPTR
)dirName
);
396 nextAssign
= nextAssign
->al_Next
;
402 } /* while (NextDosEntry()) */
407 struct DosList
*tdl
= dl
;
408 int count
= 0; /* Used to make sure that as most 5 entries are printed per line */
410 DeferPutStr("\nDevices:\n");
412 /* Print all assigned devices */
413 while ((tdl
= NextDosEntry(tdl
, LDF_DEVICES
)))
415 DeferPrintf("%b%lc", tdl
->dol_Name
, ++count
% 5 ? ' ' : '\n');
424 UnLockDosList(lockBits
);
426 DeferFlush(Output());
431 STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
)
433 STRPTR buf
; /* Pointer to the memory allocated for the string */
434 ULONG size
; /* Holder of the (growing) size of the string */
436 for (size
= 512; ; size
+= 512)
438 buf
= AllocVec(size
, MEMF_ANY
);
444 if (NameFromLock(lock
, buf
, size
))
451 if (IoErr() != ERROR_LINE_TOO_LONG
)
462 int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
463 BOOL add
, BOOL remove
)
469 int error
= RETURN_OK
;
479 tp
=(struct Process
*)FindTask(NULL
);
480 tp
->pr_WindowPtr
= (APTR
)-1;
481 dp
= DeviceProc(name
);
482 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp
));
485 success
= DoPkt(dp
,ACTION_DIE
,0,0,0,0,0);
487 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success
));
491 colon
= strchr(name
, ':');
493 *colon
= '\0'; /* Remove trailing colon; name[] is changed! */
495 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name
));
497 /* This is a little bit messy... We first remove the 'name' assign
498 * and later in the loop the target assigns.
503 if ((!success
) && (ioerr
== ERROR_ACTION_NOT_KNOWN
))
508 DEBUG_ASSIGN(PutStr("Removing device node\n"));
509 dl
= LockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
511 fdl
= FindDosEntry(dl
, name
, LDF_VOLUMES
| LDF_DEVICES
);
513 /* Note the ! for conversion to boolean value */
516 success
= RemDosEntry(fdl
);
520 ioerr
= ERROR_OBJECT_IN_USE
;
522 ioerr
= ERROR_OBJECT_NOT_FOUND
;
524 UnLockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
529 if (target
== NULL
|| *target
== NULL
)
531 error
= removeAssign(ld
, name
);
540 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
541 error
= success
? error
: RETURN_WARN
;
542 DEBUG_ASSIGN(Printf("error: %d\n", error
));
544 // The Loop over multiple targets starts here
548 for (i
= 0; target
[i
]; i
++)
552 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target
[i
]));
553 if (!(path
|| defer
|| dismount
))
555 lock
= Lock(target
[i
], SHARED_LOCK
);
559 Printf("Can't find %s\n", (IPTR
)target
[i
]);
566 if (!RemAssignList(name
, lock
))
568 Printf("Can't subtract %s from %s\n", (IPTR
)target
[i
], (IPTR
)name
);
575 if (!AssignPath(name
, target
[i
]))
579 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error
));
584 if (!AssignAdd(name
, lock
))
590 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error
));
592 /* Check if the assign doesn't exist at all. If so, create it.
593 * This fix bug id 145. - Piru
595 dl
= LockDosList(LDF_ASSIGNS
| LDF_READ
);
596 dl
= FindDosEntry(dl
, name
, LDF_ASSIGNS
);
597 UnLockDosList(LDF_ASSIGNS
| LDF_READ
);
601 if (AssignLock(name
, lock
))
609 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error
));
616 if (error
&& ioerr
!= ERROR_OBJECT_EXISTS
)
618 Printf("Can't add %s to %s\n", (IPTR
)target
[i
], (IPTR
)name
);
624 if (!AssignLate(name
, target
[i
]))
629 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error
));
634 /* If no extra parameters are specified we just do a regular
635 assign (replacing any possible previous assign with that
636 name. The case of target being NULL is taken care of above.
638 if (!AssignLock(name
, lock
))
644 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error
));
646 /* If there are several targets, the next ones have to be added. */
650 /* We break as soon as we get a serious error */
651 if (error
>= RETURN_FAIL
)
656 } /* loop through all targets */
661 if (ioerr
== ERROR_OBJECT_EXISTS
)
663 Printf("Can't %s %s\n", (IPTR
)(cancel
? "cancel" : "assign"), (IPTR
)name
);
667 PrintFault(ioerr
, NULL
);
676 int removeAssign(struct localdata
*ld
, STRPTR name
)
678 /* In case no target is given, the 'name' assign should be removed.
679 * The AmigaDOS semantics for this is apparently that the error
680 * code is never set even if the assign didn't exist.
683 if (!AssignLock(name
, BNULL
))
692 int checkAssign(struct localdata
*ld
, STRPTR name
)
696 int error
= RETURN_OK
;
701 colon
= strchr(name
, ':');
707 dl
= LockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
709 /* FIXME: GetFullPath() breaks LockDosList()'s Forbid()! */
710 /* Note: This should be ok as long as we don't have ks 1.x compatibility.
713 dl
= FindDosEntry(dl
, name
, LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
);
716 struct DosList
*tdl
= dl
;
719 switch (dl
->dol_Type
)
722 DeferPrintf("%b\n", tdl
->dol_Name
);
726 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
733 DeferPrintf("%b ", tdl
->dol_Name
);
735 for (count
= 14 - *((UBYTE
*)BADDR(tdl
->dol_Name
)); count
> 0; count
--)
740 switch (tdl
->dol_Type
)
743 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
747 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
752 STRPTR dirName
; /* For NameFromLock() */
753 struct AssignList
*nextAssign
; /* For multiassigns */
755 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
759 DeferPutStr(dirName
);
764 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
768 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
772 DeferPrintf(" + %s\n", (IPTR
)dirName
);
776 nextAssign
= nextAssign
->al_Next
;
786 DeferPrintf("%s: not assigned\n", (IPTR
)name
);
791 UnLockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
793 DeferFlush(Output());
802 /* Feferred printing routines - Piru
805 #define MAXDEFERBUF 4096
806 #define MAXOUTPUT 128
811 UBYTE buf
[MAXDEFERBUF
];
814 static void deferputch(UBYTE ch
, struct localdata
*ld
)
816 struct deferbufnode
*cur
;
821 cur
= (struct deferbufnode
*) GetTail(&DeferList
);
823 if (!cur
|| cur
->pos
>= MAXDEFERBUF
)
825 cur
= AllocMem(sizeof(struct deferbufnode
), MEMF_ANY
);
831 ADDTAIL(&DeferList
, cur
);
834 cur
->buf
[cur
->pos
] = ch
;
839 void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
)
843 DEBUG_ASSIGN(kprintf(str
));
851 AROS_UFH2S(void, deferputch_gate
,
852 AROS_UFHA(UBYTE
, ch
, D0
),
853 AROS_UFHA(struct localdata
*, ld
, A3
))
863 void deferputch_trampoline(void)
865 UBYTE ch
= (UBYTE
) REG_D0
;
866 struct localdata
*ld
= (struct localdata
*) REG_A3
;
872 const struct EmulLibEntry deferputch_gate
=
874 TRAP_LIBNR
, 0, (void (*)(void)) deferputch_trampoline
879 void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, IPTR
*args
)
881 RawDoFmt(fmt
, args
, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate
), ld
);
885 void _DeferFlush(struct localdata
*ld
, BPTR fh
)
887 struct deferbufnode
*node
;
892 while ((node
= (struct deferbufnode
*)REMHEAD(&DeferList
)))
895 LONG left
= node
->pos
;
897 while (!broken
&& left
)
901 if (SetSignal(0, SIGBREAKF_CTRL_C
) & SIGBREAKF_CTRL_C
)
907 len
= left
> MAXOUTPUT
? MAXOUTPUT
: left
;
909 Write(fh
, node
->buf
+ offs
, len
);
914 FreeMem(node
, sizeof(struct deferbufnode
));
921 PrintFault(ERROR_BREAK
, NULL
);