2 * SmartReadArgs.c -- CLI/Workbench transparent ReadArgs()
4 * $VER: SmartReadArgs.c 1.7 (7.9.98)
6 * Copyright 1998 by Thomas Aglassinger <agi@sbox.tu-graz.ac.at>
8 * Based on ExtReadArgs Copyright 1994,1995 by Stefan Ruppert
10 * Ported to OS4 by Alexandre Balaban <alexandre@balaban.name>
13 /****** SmartReadArgs/--history-- *******************************************
16 * - TODO: use dos/FindArg() instead of is_in_template()
17 * - Fixed two minor "size_t vs. LONG" warnings in
19 * Version 1.6, 7-Sep-1998
21 * - Changed name to SmartReadArgs to avoid confusion with other work based
22 * on the same material
23 * - Changed function parameters for SmartReadArgs() so that no more SAS/c
24 * specific values of argc/argv are required (Of course it still works
25 * with SAS/c, but you have to provide the WBStartup from "outside").
26 * - Changed all #include <clib/...> to #include <proto/..>, except for
27 * <clib/alib_stdio_protos.h> in "test.c". Where the hell is this one?
28 * - Added feature to ignore tooltypes that are not in the template
29 * - Added some missing includes in SmartReadArgs.c so the source codes
30 * compile without warnings
31 * - Changed #include <debug.h> to #include "debug.h" and provided a proper
33 * - The WINDOW tooltype is handled properly even if it is not entirely
34 * written in upper case.
35 * - Requires "utility.library" to be open as Stricmp() is used several
37 * - Changed from Printf() to printf() using stdio of amiga.lib to make the
38 * code compile easier on non-SAS environments
39 * - Changed autodoc tool to Robodoc
40 * - Fixed enforcer hit if no tooltypes were provided at all
41 * - Remove some "char filename[34]" stuff and replaced the array dimension
42 * by MAXIMUM_FILENAME_LENGTH for future compatibility
43 * - Cleaned-up autodocs
45 * ExtReadArgs() by Stefan Ruppert
47 * See aminet:dev/misc/extrdargs_v1.5.lha for the original version.
50 * 08.01.95 : 001.005 : changed to ExtReadArgs()
51 * 24.09.94 : 001.004 : now checks after ReadArgs the SIGBREAKF_CTRL_C
52 * flag, thus if anyone canceled during ReadArgs()
53 * help ExtReadArgs() fails
54 * 08.09.94 : 001.003 : between two switches (no equal sign) there was
55 * no space, this is fixed
56 * 08.09.94 : 001.002 : wb files now enclosed in quotes
57 * 04.09.94 : 001.001 : bumped to version 1
58 * 19.05.94 : 000.001 : initial
59 ***************************************************************************/
61 /* ------------------------------ include's ------------------------------- */
64 #include "SmartReadArgs.h"
65 #include "SDI_compiler.h"
67 #include <exec/memory.h>
68 #include <workbench/startup.h>
69 #include <workbench/workbench.h>
70 #include <dos/exall.h>
71 #include <utility/tagitem.h>
75 #include <proto/dos.h>
76 #include <proto/icon.h>
77 #include <proto/exec.h>
78 #include <proto/utility.h>
80 #if defined(__amigaos4__)
81 #include <dos/obsolete.h>
84 /* ---------------------------- local defines ----------------------------- */
88 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
96 #define ZERO ((BPTR)0)
101 #define MAXIMUM_FILENAME_LENGTH 108
103 #if defined(__amigaos4__)
104 #define AllocVecShared(size, flags) AllocVecTags((size), AVT_Type, MEMF_SHARED, AVT_Lock, FALSE, ((flags)&MEMF_CLEAR) ? AVT_ClearWithValue : TAG_IGNORE, 0, TAG_DONE)
106 #define AllocVecShared(size, flags) AllocVec((size), (flags))
109 /* --------------------------- library bases ------------------------------ */
110 extern struct Library
*IconBase
;
111 #if defined(__AROS__)
112 extern struct UtilityBase
*UtilityBase
;
114 extern struct Library
*UtilityBase
;
117 #if defined(__GNUC__) && !defined(__amigaos4__)
118 extern struct WBStartup
*_WBenchMsg
;
121 /* -------------------------- static prototypes --------------------------- */
123 static struct DiskObject
*smart_get_icon(struct SmartArgs
*args
, struct WBStartup
*wbarg
);
124 static void fstrcpy(struct SmartArgs
*args
, CONST_STRPTR string
);
125 static void get_arg_name(struct SmartArgs
*args
, STRPTR buffer
, ULONG size
, ULONG
* modes
);
126 static void get_wbarg_name(struct WBArg
*wbarg
, STRPTR buffer
, ULONG size
);
127 static BOOL
is_in_template(STRPTR name
, CONST_STRPTR
template);
129 /****** SmartReadArgs/--background-- ****************************************
131 * SmartReadArgs is Copyright 1998 Thomas Aglassinger
133 * ExtReadArgs, its prequel, is Copyright 1994,1995 Stefan Ruppert
135 * Permission is granted to freely distribute the material (also only
136 * parts of it) as long this ReadMe is included and all the copyright
137 * notes are left unaltered except for a description of your changes.
139 * The way of parsing ToolTypes provided by "icon.library" is rather
140 * clumsy. This is particular annoying as many programmers and users got
141 * used to ReadArgs(), which does the argument handling for many CLI
142 * commands and ARexx ports.
144 * Unfortunately, ReadArgs lacks a interface to Workbench tooltypes, thus
145 * its usage preventes your programs from being started from Workbench.
147 * SmartReadArgs() copies all Workbench arguments in a single string and
148 * passes this string to the ReadArgs() function. If started from CLI, it
149 * calls ReadArgs() without this step.
151 * Stefan Ruppert wrote most parts of the source code, designed the general
152 * interface and implemented loads of nice features. Basically he did the
155 * He got the main idea for the implementation from Stefan Winterstein,
156 * the author of ARoach.
158 * Thomas Aglassinger <agi@sbox.tu-graz.ac.at> did some minor changes,
159 * established a more consistent naming schema, reworked the documentation
160 * and also added support for gcc/libnix.
162 * Contact him in case of problems or if you made some enhancements.
164 * Updates are available from aminet:dev/misc/SmartReadArgs.lha.
166 * There is no warranty for this software package. Although the author
167 * has tried to prevent errors, he can't guarantee that the software
168 * package described in this document is 100% reliable. You are
169 * therefore using this material at your own risk. The author cannot be
170 * made responsible for any damage which is caused by using this
172 ****************************************************************************/
174 /* The define below is used to rename the example main() used in the autodoc
175 * to dummy_main(). Using two main()s would cause problems for the linker. */
176 #define main dummy_main
178 /****** SmartReadArgs/SmartReadArgs ******************************************
180 * SmartReadArgs -- Workbench/CLI transparent ReadArgs().
182 * error = SmartReadArgs(wb_startup, smart_args);
184 * LONG SmartReadArgs(struct WBStartup *, struct SmartArgs *);
186 * This function is a CLI/Workbench transparent interface to ReadArgs().
188 * In case of a Workbench start, it analyzes the WBStartup message and
189 * possible tooltypes. These are converted to a text string that can be
190 * passed to ReadArgs() like before.
192 * Tooltypes that are not part of the template are ignored. This includes
193 * tooltypes being disabled with "(...)", NewIcons image data on systems
194 * without NewIcons installed and all this «« Icon by some idiot »» crap.
196 * If the application was stared from CLI, it simply calls ReadArgs()
197 * without the conversion step.
199 * If all went well you get a return value of zero. This means the passed
200 * arguments fit the template and are ready to use. Otherwise you get a
201 * IoErr()-like return code.
203 * wb_startup - Workbench startup message. Refer to your compiler manual
204 * to learn how to obtain it. If this is NULL, the program was
206 * smart_args - structure which holds all information used by
207 * SmartReadArgs(). You have to setup the following fields before the
210 * sa_Template - The template passed to ReadArgs()
211 * sa_Parameter - ReadArgs() LONG WORD array to hold the arguments
212 * sa_FileParameter - number of Argument in the template to use
213 * for the files passed via WBStartup->sm_ArgList or -1, that
214 * means you don't want any files
215 * sa_Window - Window description string to open when the program
216 * is started from the workbench. NULL means no window. If the
217 * icon has a WINDOW tooltype, this value is ignored.
218 * sa_RDArgs - RDArgs structure to use for ReadArgs() call. This
219 * can be used to provide extended help.
220 * sa_Buffer - Pointer to a buffer to use for the Workbench startup
221 * or NULL, that means SmartReadArgs() allocates a buffer for you
222 * sa_BufferSize - Size of the optional buffer. If it is smaller than
223 * SA_MINIMUM_BUFFER_SIZE it will be adjusted.
225 * All other fields should be set to NULL.
227 * Zero for success. You can check the sa_Flags field for the
228 * SAF_WORKBENCH flag to learn how the program was started.
230 * Otherwise an IoErr()-like error code is returned. This can be passed
231 * directly to PrintFault() or similar.
233 * Always call SmartFreeArgs(), even if SmartReadArgs() failed! See example
236 * This function requires "dos.library", "icon.library" and
237 * "utility.library" to be opened by the application. Normally this
238 * already has been done by the compiler startup code.
240 * There is a not widely known feature of ReadArgs(): with templates like
241 * "FROM/M/A,TO/A", you can select the files from workbench performing the
244 * - Select the program
245 * - Select the FROM files
246 * - Select and double click the TO file
248 * This is available because ReadArgs() grabs the last string from a
249 * multi-argument FROM and uses it as the TO parameter, if none is passed
252 * There are some known problems when used with GCC, mostly related to the
253 * fact that I never bothered creating a useable developer environment
254 * around it (and I'm not sure if this is even possible >:) ...):
256 * - Debugging output shows up in the console instead of SER:. Does
257 * debug.lib exist for gcc? (Wasn't there this strange hunk2gcc
259 * - "Read from 0" Enforcer hit in SmartReadArgs(). Couldn't figure out
260 * the exact location yet because of the asynchronous debugging output
263 * For someone with a reasonable experience with GCC, it should be easy to
266 * The SAS/c implementation does not have these problems.
268 * SmartFreeArgs(), dos.library/ReadArgs(), icon.library/GetDiskObjectNew()
270 * The main archiev comes with a "test.c" and a couple of icons to start
271 * the corresponding executable "test". Take a look at the source code and
274 * See below for a smaller code segment that expects the "dos.library",
275 * "icon.library" and "utility.library" to be open already.
278 /****************************************************************************/
279 LONG
SmartReadArgs(struct WBStartup
* wb_startup
, struct SmartArgs
* args
)
283 #if defined(__amigaos4__)
284 struct ExecIFace
*IExec
= (struct ExecIFace
*)(((struct ExecBase
*)SysBase
)->MainInterface
);
289 D(DBF_STARTUP
, "UtilityBase = 0x%08lx", (ULONG
)UtilityBase
);
290 D(DBF_STARTUP
, "IconBase = 0x%08lx", (ULONG
)IconBase
);
291 D(DBF_STARTUP
, "WBStartup = 0x%08lx", (ULONG
)wb_startup
);
293 if (wb_startup
!= NULL
)
295 struct WBArg
*wbarg
= wb_startup
->sm_ArgList
;
296 LONG arg_counter
= 0;
298 D(DBF_STARTUP
, " numArgs = %ld", wb_startup
->sm_NumArgs
);
299 while (arg_counter
< wb_startup
->sm_NumArgs
)
301 D(DBF_STARTUP
, " name[%ld] = '%s'", arg_counter
, wbarg
->wa_Name
);
307 if (wb_startup
!= NULL
)
309 if (!(args
->sa_RDArgs
= AllocDosObject(DOS_RDARGS
, NULL
)))
311 return (ERROR_NO_FREE_STORE
);
315 args
->sa_Flags
|= SAF_ALLOCRDARGS
;
317 if (!args
->sa_Buffer
)
319 args
->sa_BufferSize
= MAX(SA_MINIMUM_BUFFER_SIZE
, args
->sa_BufferSize
);
320 args
->sa_Buffer
= AllocVecShared(args
->sa_BufferSize
, MEMF_ANY
);
321 args
->sa_Flags
|= SAF_ALLOCBUFFER
;
324 if (!args
->sa_Buffer
)
325 return (ERROR_NO_FREE_STORE
);
328 struct DiskObject
*dobj
;
330 args
->sa_ActualPtr
= args
->sa_Buffer
;
331 args
->sa_EndPtr
= args
->sa_Buffer
+ args
->sa_BufferSize
- 1;
333 if (!(dobj
= smart_get_icon(args
, wb_startup
)))
335 return (ERROR_OBJECT_NOT_FOUND
);
339 struct WBArg
*wbarg
= args
->sa_WBArg
;
340 ULONG num
= args
->sa_NumArgs
;
342 STRPTR
*tooltypes
= (STRPTR
*) dobj
->do_ToolTypes
;
347 if (num
> 1 && args
->sa_FileParameter
>= 0 && (temp
= AllocVecShared(TEMPSIZE
, MEMF_ANY
)))
351 get_arg_name(args
, temp
, TEMPSIZE
, &modes
);
355 /* no "/M" specifier in the ReadArgs() template, thus use only the first file */
356 if (modes
!= MODE_MULTI
)
361 get_wbarg_name(wbarg
, temp
, TEMPSIZE
);
364 fstrcpy(args
, "\" ");
372 D(DBF_STARTUP
, "tooltypes=%08lx", (ULONG
)tooltypes
);
380 /* check if this tooltype enabled and part of the
383 && is_in_template(name
, args
->sa_Template
))
385 while (*ptr
!= '=' && *ptr
!= EOS
)
392 if (!Stricmp(name
, "WINDOW"))
395 if ((win
= AllocVecShared((ULONG
) strlen(ptr
+ 1) + 1, MEMF_ANY
)))
397 strcpy(win
, ptr
+ 1);
398 args
->sa_Window
= win
;
399 args
->sa_Flags
|= SAF_ALLOCWINDOW
;
407 /* enclose the argument in "" */
408 if (*(ptr
+ 1) == '"')
411 fstrcpy(args
, ptr
+ 1);
415 fstrcpy(args
, "=\"");
416 fstrcpy(args
, ptr
+ 1);
429 } /* while (*tooltypes) */
430 } /* if (tooltypes) */
433 D(DBF_STARTUP
, "final wb command line : '%s'", args
->sa_Buffer
);
438 args
->sa_RDArgs
->RDA_Source
.CS_Buffer
= args
->sa_Buffer
;
439 args
->sa_RDArgs
->RDA_Source
.CS_Length
= strlen(args
->sa_Buffer
);
441 args
->sa_Flags
|= SAF_WORKBENCH
;
446 args
->sa_FreeArgs
= ReadArgs(args
->sa_Template
, (APTR
)args
->sa_Parameter
, args
->sa_RDArgs
);
448 if (SetSignal(0L, SIGBREAKF_CTRL_C
) & SIGBREAKF_CTRL_C
)
450 SetIoErr(ERROR_BREAK
);
453 if ((error
= IoErr()) == 0 && (wb_startup
!= NULL
))
457 args
->sa_WindowFH
= Open(args
->sa_Window
, MODE_NEWFILE
);
458 if (args
->sa_WindowFH
)
460 args
->sa_OldOutput
= SelectOutput(args
->sa_WindowFH
);
461 args
->sa_OldInput
= SelectInput(args
->sa_WindowFH
);
469 /****** SmartReadArgs/SmartFreeArgs ******************************************
471 * SmartFreeArgs -- Free all resources allocated by SmartReadArgs().
473 * SmartFreeArgs(smart_args);
475 * void SmartFreeArgs(struct SmartArgs *);
477 * Free all resources allocated by a previous call to SmartReadArgs().
479 * smart_args - Same pointer as passed to SmartReadArgs() before
481 * Always call SmartFreeArgs(), even if SmartReadArgs() failed! Take a look
482 * at the example for SmartReadArgs().
485 ****************************************************************************/
486 void SmartFreeArgs(struct SmartArgs
*args
)
488 /* FreeArgs() can handle a NULL pointer */
489 FreeArgs(args
->sa_FreeArgs
);
491 if (args
->sa_Flags
& SAF_ALLOCRDARGS
)
493 FreeDosObject(DOS_RDARGS
, args
->sa_RDArgs
);
495 if (args
->sa_Flags
& SAF_ALLOCBUFFER
)
496 FreeVec(args
->sa_Buffer
);
498 if (args
->sa_WindowFH
)
500 SelectOutput(args
->sa_OldOutput
);
501 SelectInput(args
->sa_OldInput
);
502 Close(args
->sa_WindowFH
);
505 if (args
->sa_Flags
& SAF_ALLOCWINDOW
&& args
->sa_Window
)
506 FreeVec(args
->sa_Window
);
510 /* This code was grapped from IconImage/wbarg.c/IconFromWBArg()
511 * Commodore-Amiga Example code
513 static struct DiskObject
*smart_get_icon(struct SmartArgs
*args
, struct WBStartup
*wb_startup
)
515 struct DiskObject
*dob
= NULL
;
516 struct WBArg
*wbarg
= wb_startup
->sm_ArgList
;
517 ULONG num
= wb_startup
->sm_NumArgs
;
519 TEXT work_name
[MAXIMUM_FILENAME_LENGTH
];
520 BPTR old_lock
, new_lock
;
522 /* Copy the WBArg contents */
523 strncpy(work_name
, wbarg
->wa_Name
, MAXIMUM_FILENAME_LENGTH
);
525 new_lock
= DupLock(wbarg
->wa_Lock
);
526 if (new_lock
!= ZERO
)
528 D(DBF_STARTUP
, "work_name : '%s'", work_name
);
530 /* go to the directory where the icon resides */
531 old_lock
= CurrentDir(new_lock
);
533 dob
= GetDiskObjectNew(work_name
);
535 /* test, if the first icon is a project icon and if so, get its icon */
536 if (wb_startup
->sm_NumArgs
> 1)
540 if ((new_lock2
= DupLock(wbarg
[1].wa_Lock
)))
542 struct DiskObject
*prj
;
544 CurrentDir(new_lock2
);
547 new_lock
= new_lock2
;
549 strncpy(work_name
, wbarg
[1].wa_Name
, MAXIMUM_FILENAME_LENGTH
);
550 D(DBF_STARTUP
, "work_name2 : '%s'", work_name
);
552 if ((prj
= GetDiskObjectNew(work_name
)))
554 if (prj
->do_Type
== WBPROJECT
)
558 /* if this is only an icon skip it */
559 if (!(test
= Lock(work_name
, SHARED_LOCK
)))
578 D(DBF_STARTUP
, "dobj window : '%s'", dob
->do_ToolWindow
);
581 /* go back to where we used to be */
582 CurrentDir(old_lock
);
584 /* release the duplicated lock */
587 args
->sa_WBArg
= wbarg
+ 1;
588 args
->sa_NumArgs
= num
;
591 D(DBF_STARTUP
, "return (dob)");
596 static void fstrcpy(struct SmartArgs
*args
, CONST_STRPTR string
)
598 STRPTR ptr
= args
->sa_ActualPtr
;
599 STRPTR end
= args
->sa_EndPtr
;
601 while (ptr
< end
&& *string
)
604 *ptr
= EOS
; /* Mark end of string */
606 args
->sa_ActualPtr
= ptr
;
609 static void get_arg_name(struct SmartArgs
*args
, STRPTR buffer
, ULONG size
, ULONG
* modes
)
611 ULONG num
= args
->sa_FileParameter
;
612 CONST_STRPTR ptr
= args
->sa_Template
;
618 while (*ptr
!= ',' && *ptr
!= EOS
)
628 while (*ptr
!= ',' && *ptr
!= '/' && *ptr
!= EOS
&& size
> 0)
638 if (*ptr
== 'M' || *ptr
== 'm')
648 static void get_wbarg_name(struct WBArg
*wbarg
, STRPTR buffer
, ULONG size
)
652 if ((new = DupLock(wbarg
->wa_Lock
)))
654 if (!NameFromLock(new, buffer
, size
))
656 else if (!AddPart(buffer
, wbarg
->wa_Name
, size
))
665 static BOOL
is_in_template(STRPTR name
, CONST_STRPTR
template)
668 CONST_STRPTR current_word
= template;
669 BOOL skip_switch
= FALSE
;
672 /* Evaluate length of name part of whole tooltype */
674 while ((name
[name_length
] != EOS
)
675 && (name
[name_length
] != '='))
680 D(DBF_TEMPLATE
, "find '%s' in template '%s'\n", name
, template);
681 while ((current_word
[0] != '\0') && (!found
))
683 STRPTR next_word
= strpbrk(current_word
, "/=,");
684 size_t current_word_length
;
686 if (next_word
== NULL
)
688 next_word
= (STRPTR
)current_word
+ strlen(current_word
);
690 current_word_length
= next_word
- current_word
;
694 D(DBF_TEMPLATE
, " skip ('%s', %lu)", current_word
, current_word_length
);
699 D(DBF_TEMPLATE
, " check ('%s', %lu)", current_word
, current_word_length
);
700 if ((name_length
== current_word_length
)
701 && !Strnicmp(name
, current_word
, (LONG
) name_length
))
703 D(DBF_TEMPLATE
, " found!");
708 current_word
= next_word
;
709 if (current_word
[0] != '\0')
711 if (current_word
[0] == '/')