3 (C) 1995-2001 AROS - The Amiga Research OS
4 (C) 2002-2005 Harry Sintonen
5 (C) 2005-2007 Pavel Fedin
6 $Id: Assign.c,v 1.6 2007/09/21 06:51:45 sonic_amiga Exp $
8 Desc: Assign CLI command
13 /******************************************************************************
21 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
25 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
26 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
34 ASSIGN creates a reference to a file or directory. The reference
35 is a logical device name which makes it very convenient to specify
36 assigned objects using the reference instead of their paths.
37 If the NAME and TARGET arguments are given, ASSIGN assigns
38 the given logical name to the specified target. If the NAME given is
39 already assigned to a file or directory the new target replaces the
41 A colon must be included after the NAME argument.
42 If only the NAME argument is given, any assigns to that NAME are
43 removed. If no arguments whatsoever are given, all logical assigns
49 NAME -- the name that should be assigned to a file or dir
50 TARGET -- one or more files or directories to assign the NAME to
51 LIST -- list all assigns made
52 EXISTS -- if NAME is not assigned, set the condition flag to
54 DISMOUNT -- remove the volume or device NAME from the dos list
55 DEFER -- make an ASSIGN to a path or directory that not need to
56 exist at the time of assignment. The first time the NAME
57 is referenced the NAME is bound to the object
58 PATH -- path to assign with a non-binding assign. This mean
59 that the assign is re-evaluated each time a reference
60 to NAME is done. Like for DEFER, the path doesn't have
61 to exist when the ASSIGN command is executed
62 ADD -- don't replace an assign but add another object for a
64 REMOVE -- remove an ASSIGN
65 VOLS -- show assigned volumes if in LIST mode
66 DIRS -- show assigned directories if in LIST mode
67 DEVICES -- show assigned devices if in LIST mode
83 ******************************************************************************/
85 #define AROS_ALMOST_COMPATIBLE
88 #include <aros/asmcall.h>
89 #include <proto/arossupport.h>
91 #include <clib/debug_protos.h>
94 #include <exec/types.h>
95 #include <exec/lists.h>
96 #include <exec/memory.h>
97 #include <exec/execbase.h>
99 #include <dos/dosextens.h>
100 #include <dos/exall.h>
101 #include <utility/tagitem.h>
102 #include <proto/dos.h>
103 #include <proto/exec.h>
107 #define DEBUG_ASSIGN(x)
109 /******************************************************************************
114 Assign [(name):] [{(target)}] [LIST] [EXISTS] [DISMOUNT] [DEFER]
115 [PATH] [ADD] [REMOVE] [VOLS] [DIRS] [DEVICES]
119 NAME, TARGET/M, LIST/S, EXISTS/S, DISMOUNT/S, DEFER/S, PATH/S, ADD/S,
120 REMOVE/S, VOLS/S, DIRS/S, DEVICES/S
128 ASSIGN creates a reference to a file or directory. The reference
129 is a logical device name which makes it very convenient to specify
130 assigned objects using the reference instead of their paths.
132 If the NAME and TARGET arguments are given, ASSIGN assigns the
133 given logical name to the specified target. If the NAME given is
134 already assigned to a file or directory the new target replaces the
135 previous target. A colon must be included after the NAME argument.
137 If only the NAME argument is given, any assigns to that NAME are
138 removed. If no arguments whatsoever are given, all logical
143 NAME -- the name that should be assigned to a file or directory
144 TARGET -- one or more files or directories to assign the NAME to
145 LIST -- list all assigns made
146 EXISTS -- if NAME is not assigned, set the condition flag to
148 DISMOUNT -- remove the volume or device NAME from the dos list
149 DEFER -- make an ASSIGN to a path or directory that not need to
150 exist at the time of assignment. The first time the
151 NAME is referenced the NAME is bound to the object
152 PATH -- path to assign with a non-binding assign. This means
153 that the assign is re-evaluated each time a reference
154 to NAME is done. Like for DEFER, the path doesn't have
155 to exist when the ASSIGN command is executed
156 ADD -- don't replace an assign but add another object for a
158 REMOVE -- remove an ASSIGN
159 VOLS -- show assigned volumes if in LIST mode
160 DIRS -- show assigned directories if in LIST mode
161 DEVICES -- show assigned devices if in LIST mode
176 The assign command has many switches. This together with the somewhat
177 messy handling of DosList:s by dos.library makes the operation rather
180 There are some fundamental building blocks that defines the semantics
181 of the Assign command.
183 Only one of the switches ADD, REMOVE, PATH and DEFER may be specified.
185 If EXISTS is specified, only the name parameter is important.
187 The implementation is split up in two fundamental procedures.
189 doAssign() -- make [a number of] assigns
190 showAssigns() -- show the available assigns
191 checkAssign() -- check if a particular assign exists
195 ******************************************************************************/
198 #define AROS_BSTR_strlen(s) *((UBYTE *)BADDR(s))
202 #define AROS_ASMSYMNAME(s) (&s)
204 static const int __abox__
= 1;
205 static const char version
[] = "\0$VER: Assign unofficial 50.9 (18.10.07) © AROS" ;
207 static const char version
[] = "\0$VER: Assign 50.9 (18.10.07) © AROS" ;
212 struct ExecBase
*ld_SysBase
;
213 struct DosLibrary
*ld_DOSBase
;
214 struct MinList ld_DeferList
;
217 #define SysBase ld->ld_SysBase
218 #define DOSBase ld->ld_DOSBase
219 #define DeferList ld->ld_DeferList
224 static int Main(struct ExecBase
*sBase
);
225 static int checkAssign(struct localdata
*ld
, STRPTR name
);
226 static int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
227 BOOL add
, BOOL remove
);
228 static void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
);
229 static int removeAssign(struct localdata
*ld
, STRPTR name
);
230 static STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
);
232 static void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
);
233 static void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, IPTR
*args
);
234 static void _DeferFlush(struct localdata
*ld
, BPTR fh
);
236 #define DeferPutStr(str) _DeferPutStr(ld,str)
237 #define DeferPrintf(fmt,args...) \
238 DEBUG_ASSIGN(kprintf(fmt, ## args);) \
239 do { IPTR __args[] = {0 , ## args}; _DeferVPrintf(ld, fmt, __args + 1); } while (0)
240 #define DeferFlush(fh) _DeferFlush(ld,fh)
244 const UBYTE
template[] =
275 AROS_UFH3(__startup
static int, Start
,
276 AROS_UFHA(char *, argstr
, A0
),
277 AROS_UFHA(ULONG
, argsize
, D0
),
278 AROS_UFHA(struct ExecBase
*, sBase
, A6
))
287 struct ExecBase
*sBase
;
289 sBase
= *((struct ExecBase
**) 4);
294 static int Main(struct ExecBase
*sBase
)
296 struct localdata _ld
, *ld
= &_ld
;
297 struct RDArgs
*readarg
;
298 struct ArgList arglist
;
299 struct ArgList
*MyArgList
= &arglist
;
300 int error
= RETURN_OK
;
303 DOSBase
= (struct DosLibrary
*) OpenLibrary("dos.library",37);
306 memset(&arglist
, 0, sizeof(arglist
));
310 readarg
= ReadArgs(template, (IPTR
*)MyArgList
, NULL
);
313 /* Verify mutually exclusive args
315 if ((MyArgList
->add
!=0) + (MyArgList
->remove
!=0) + (MyArgList
->path
!=0) + (MyArgList
->defer
!=0) > 1)
317 PutStr("Only one of ADD, REMOVE, PATH or DEFER is allowed\n");
319 CloseLibrary((struct Library
*) DOSBase
);
329 /* Correct assign name construction? The rule is that the device name
330 * should end with a colon at the same time as no other colon may be
333 pos
= strchr(MyArgList
->name
, ':');
336 Printf("Invalid device name %s\n", (IPTR
)MyArgList
->name
);
338 CloseLibrary((struct Library
*) DOSBase
);
343 /* If the EXISTS keyword is specified, we only care about NAME */
344 if (MyArgList
->exists
)
346 error
= checkAssign(ld
, MyArgList
->name
);
347 DEBUG_ASSIGN(Printf("checkassign error %ld\n",error
));
349 else if (MyArgList
->name
)
351 /* If a NAME is specified, our primary task is to add or
354 error
= doAssign(ld
, MyArgList
->name
, MyArgList
->target
, MyArgList
->dismount
, MyArgList
->defer
,
355 MyArgList
->path
, MyArgList
->add
, MyArgList
->remove
);
356 DEBUG_ASSIGN(Printf("doassign error %ld\n",error
));
359 /* With the LIST keyword, the current assigns will be
360 displayed also when (after) making an assign */
362 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
367 /* If no NAME was given, we just show the current assigns
368 as specified by the user (VOLS, DIRS, DEVICES) */
370 showAssigns(ld
, MyArgList
->vols
, MyArgList
->dirs
, MyArgList
->devices
);
376 CloseLibrary((struct Library
*) DOSBase
);
379 DEBUG_ASSIGN(Printf("error %ld\n", error
));
386 void showAssigns(struct localdata
*ld
, BOOL vols
, BOOL dirs
, BOOL devices
)
388 ULONG lockBits
= LDF_READ
;
391 /* If none of the parameters are specified, everything should be
393 if (!(vols
|| dirs
|| devices
))
400 lockBits
|= vols
? LDF_VOLUMES
: 0;
401 lockBits
|= dirs
? LDF_ASSIGNS
: 0;
402 lockBits
|= devices
? LDF_DEVICES
: 0;
404 dl
= LockDosList(lockBits
);
406 #warning "FIXME: GetFullPath() breaks LockDosList()'s Forbid()!"
407 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
411 struct DosList
*tdl
= dl
;
413 DeferPutStr("Volumes:\n");
415 /* Print all mounted volumes */
416 while ((tdl
= NextDosEntry(tdl
, LDF_VOLUMES
)))
418 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
424 struct DosList
*tdl
= dl
;
427 DeferPutStr("\nDirectories:\n");
429 /* Print all assigned directories */
430 while ((tdl
= NextDosEntry(tdl
, LDF_ASSIGNS
)))
432 DeferPrintf("%b ", tdl
->dol_Name
);
434 for (count
= 14 - AROS_BSTR_strlen(tdl
->dol_Name
); count
> 0; count
--)
439 switch (tdl
->dol_Type
)
442 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
446 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
451 STRPTR dirName
; /* For NameFromLock() */
452 struct AssignList
*nextAssign
; /* For multiassigns */
454 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
458 DeferPutStr(dirName
);
463 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
467 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
471 DeferPrintf(" + %s\n", (IPTR
)dirName
);
475 nextAssign
= nextAssign
->al_Next
;
481 } /* while (NextDosEntry()) */
486 struct DosList
*tdl
= dl
;
487 int count
= 0; /* Used to make sure that as most 5 entries are printed per line */
489 DeferPutStr("\nDevices:\n");
491 /* Print all assigned devices */
492 while ((tdl
= NextDosEntry(tdl
, LDF_DEVICES
)))
494 DeferPrintf("%b%lc", tdl
->dol_Name
, ++count
% 5 ? ' ' : '\n');
503 UnLockDosList(lockBits
);
505 DeferFlush(Output());
510 STRPTR
GetFullPath(struct localdata
*ld
, BPTR lock
)
512 STRPTR buf
; /* Pointer to the memory allocated for the string */
513 ULONG size
; /* Holder of the (growing) size of the string */
515 for (size
= 512; ; size
+= 512)
517 buf
= AllocVec(size
, MEMF_ANY
);
523 if (NameFromLock(lock
, buf
, size
))
530 if (IoErr() != ERROR_LINE_TOO_LONG
)
541 int doAssign(struct localdata
*ld
, STRPTR name
, STRPTR
*target
, BOOL dismount
, BOOL defer
, BOOL path
,
542 BOOL add
, BOOL remove
)
548 int error
= RETURN_OK
;
556 /* TODO: AROS currently doesn't support packet handlers directly
557 and we currently don't support shutting down IOFS handlers */
559 ioerr
= ERROR_ACTION_NOT_KNOWN
;
564 tp
=(struct Process
*)FindTask(NULL
);
565 tp
->pr_WindowPtr
= (APTR
)-1;
566 dp
= DeviceProc(name
);
567 DEBUG_ASSIGN(Printf("doassign: dp <%08X>\n",dp
));
570 success
= DoPkt(dp
,ACTION_DIE
,0,0,0,0,0);
572 DEBUG_ASSIGN(Printf("doassign: ACTION_DIE returned %ld\n",success
));
577 colon
= strchr(name
, ':');
579 *colon
= '\0'; /* Remove trailing colon; name[] is changed! */
581 DEBUG_ASSIGN(Printf("doassign: name <%s>\n", name
));
583 /* This is a little bit messy... We first remove the 'name' assign
584 * and later in the loop the target assigns.
589 if ((!success
) && (ioerr
== ERROR_ACTION_NOT_KNOWN
))
594 DEBUG_ASSIGN(PutStr("Removing device node\n"));
595 dl
= LockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
597 fdl
= FindDosEntry(dl
, name
, LDF_VOLUMES
| LDF_DEVICES
);
599 /* Note the ! for conversion to boolean value */
602 success
= RemDosEntry(fdl
);
606 ioerr
= ERROR_OBJECT_IN_USE
;
608 ioerr
= ERROR_OBJECT_NOT_FOUND
;
610 UnLockDosList(LDF_VOLUMES
| LDF_DEVICES
| LDF_WRITE
);
615 if (target
== NULL
|| *target
== NULL
)
617 error
= removeAssign(ld
, name
);
626 /* AmigaDOS doesn't use RETURN_WARN here... but it should? */
627 error
= success
? error
: RETURN_WARN
;
628 DEBUG_ASSIGN(Printf("error: %d\n", error
));
630 // The Loop over multiple targets starts here
634 for (i
= 0; target
[i
]; i
++)
638 DEBUG_ASSIGN(Printf("doassign: target <%s>\n", target
[i
]));
639 if (!(path
|| defer
|| dismount
))
641 lock
= Lock(target
[i
], SHARED_LOCK
);
645 Printf("Can't find %s\n", (IPTR
)target
[i
]);
652 if (!RemAssignList(name
, lock
))
654 Printf("Can't subtract %s from %s\n", (IPTR
)target
[i
], (IPTR
)name
);
661 if (!AssignPath(name
, target
[i
]))
665 DEBUG_ASSIGN(Printf("doassign AssignPath error %ld\n",error
));
670 if (!AssignAdd(name
, lock
))
676 DEBUG_ASSIGN(Printf("doassign AssignAdd error %ld\n",error
));
678 /* Check if the assign doesn't exist at all. If so, create it.
679 * This fix bug id 145. - Piru
681 dl
= LockDosList(LDF_ASSIGNS
| LDF_READ
);
682 dl
= FindDosEntry(dl
, name
, LDF_ASSIGNS
);
683 UnLockDosList(LDF_ASSIGNS
| LDF_READ
);
687 if (AssignLock(name
, lock
))
695 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n", error
));
702 if (error
&& ioerr
!= ERROR_OBJECT_EXISTS
)
704 Printf("Can't add %s to %s\n", (IPTR
)target
[i
], (IPTR
)name
);
710 if (!AssignLate(name
, target
[i
]))
715 DEBUG_ASSIGN(Printf("doassign AssignLate error %ld\n",error
));
720 /* If no extra parameters are specified we just do a regular
721 assign (replacing any possible previous assign with that
722 name. The case of target being NULL is taken care of above.
724 if (!AssignLock(name
, lock
))
730 DEBUG_ASSIGN(Printf("doassign AssignLock error %ld\n",error
));
732 /* If there are several targets, the next ones have to be added. */
736 /* We break as soon as we get a serious error */
737 if (error
>= RETURN_FAIL
)
742 } /* loop through all targets */
747 if (ioerr
== ERROR_OBJECT_EXISTS
)
749 Printf("Can't %s %s\n", (IPTR
)(cancel
? "cancel" : "assign"), (IPTR
)name
);
753 PrintFault(ioerr
, NULL
);
762 int removeAssign(struct localdata
*ld
, STRPTR name
)
764 /* In case no target is given, the 'name' assign should be removed.
765 * The AmigaDOS semantics for this is apparently that the error
766 * code is never set even if the assign didn't exist.
769 if (!AssignLock(name
, NULL
))
778 int checkAssign(struct localdata
*ld
, STRPTR name
)
782 int error
= RETURN_OK
;
787 colon
= strchr(name
, ':');
793 dl
= LockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
795 #warning "Note: GetFullPath() breaks LockDosList()'s Forbid()!"
796 #warning "Note: This should be ok as long as we don't have ks 1.x compatibility."
798 dl
= FindDosEntry(dl
, name
, LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
);
801 struct DosList
*tdl
= dl
;
804 switch (dl
->dol_Type
)
807 DeferPrintf("%b\n", tdl
->dol_Name
);
811 DeferPrintf("%b [Mounted]\n", tdl
->dol_Name
);
818 DeferPrintf("%b ", tdl
->dol_Name
);
820 for (count
= 14 - *((UBYTE
*)BADDR(tdl
->dol_Name
)); count
> 0; count
--)
825 switch (tdl
->dol_Type
)
828 DeferPrintf("<%s>\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
832 DeferPrintf("[%s]\n", (IPTR
)tdl
->dol_misc
.dol_assign
.dol_AssignName
);
837 STRPTR dirName
; /* For NameFromLock() */
838 struct AssignList
*nextAssign
; /* For multiassigns */
840 dirName
= GetFullPath(ld
, tdl
->dol_Lock
);
844 DeferPutStr(dirName
);
849 nextAssign
= tdl
->dol_misc
.dol_assign
.dol_List
;
853 dirName
= GetFullPath(ld
, nextAssign
->al_Lock
);
857 DeferPrintf(" + %s\n", (IPTR
)dirName
);
861 nextAssign
= nextAssign
->al_Next
;
871 DeferPrintf("%s: not assigned\n", (IPTR
)name
);
876 UnLockDosList(LDF_DEVICES
| LDF_ASSIGNS
| LDF_VOLUMES
| LDF_READ
);
878 DeferFlush(Output());
887 /* Feferred printing routines - Piru
890 #define MAXDEFERBUF 4096
891 #define MAXOUTPUT 128
896 UBYTE buf
[MAXDEFERBUF
];
899 static void deferputch(UBYTE ch
, struct localdata
*ld
)
901 struct deferbufnode
*cur
;
906 cur
= (struct deferbufnode
*) GetTail(&DeferList
);
908 if (!cur
|| cur
->pos
>= MAXDEFERBUF
)
910 cur
= AllocMem(sizeof(struct deferbufnode
), MEMF_ANY
);
916 ADDTAIL(&DeferList
, cur
);
919 cur
->buf
[cur
->pos
] = ch
;
924 void _DeferPutStr(struct localdata
*ld
, CONST_STRPTR str
)
928 DEBUG_ASSIGN(kprintf(str
);)
936 AROS_UFH2(static void, deferputch_gate
,
937 AROS_UFHA(UBYTE
, ch
, D0
),
938 AROS_UFHA(struct localdata
*, ld
, A3
))
948 void deferputch_trampoline(void)
950 UBYTE ch
= (UBYTE
) REG_D0
;
951 struct localdata
*ld
= (struct localdata
*) REG_A3
;
957 const struct EmulLibEntry deferputch_gate
=
959 TRAP_LIBNR
, 0, (void (*)(void)) deferputch_trampoline
964 void _DeferVPrintf(struct localdata
*ld
, CONST_STRPTR fmt
, IPTR
*args
)
966 RawDoFmt(fmt
, args
, (void (*)(void))AROS_ASMSYMNAME(deferputch_gate
), ld
);
970 void _DeferFlush(struct localdata
*ld
, BPTR fh
)
972 struct deferbufnode
*node
;
977 while ((node
= REMHEAD(&DeferList
)))
980 LONG left
= node
->pos
;
982 while (!broken
&& left
)
986 if (SetSignal(0, SIGBREAKF_CTRL_C
) & SIGBREAKF_CTRL_C
)
992 len
= left
> MAXOUTPUT
? MAXOUTPUT
: left
;
994 Write(fh
, node
->buf
+ offs
, len
);
999 FreeMem(node
, sizeof(struct deferbufnode
));
1006 PrintFault(ERROR_BREAK
, NULL
);