Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / openurl / Cmd / SmartReadArgs.c
blob01f1af592772f3e5769f2a9511628d8efb7d1d0e
1 /*
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-- *******************************************
14 * HISTORY
15 * Version 1.7
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
32 * debug.h
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
36 * times
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
44 * ANCIENT HISTORY
45 * ExtReadArgs() by Stefan Ruppert
47 * See aminet:dev/misc/extrdargs_v1.5.lha for the original version.
49 * $HISTORY
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 ------------------------------- */
63 #include "debug.h"
64 #include "SmartReadArgs.h"
66 #include <exec/memory.h>
67 #include <workbench/startup.h>
68 #include <workbench/workbench.h>
69 #include <dos/exall.h>
70 #include <utility/tagitem.h>
72 #include <string.h>
74 #include <proto/dos.h>
75 #include <proto/icon.h>
76 #include <proto/exec.h>
77 #include <proto/utility.h>
79 /* ---------------------------- local defines ----------------------------- */
81 #define TEMPSIZE 512
82 #ifndef MAX
83 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
84 #endif
86 #ifndef EOS
87 #define EOS '\0'
88 #endif
90 #define MODE_MULTI 1
92 #define MAXIMUM_FILENAME_LENGTH 108
94 /* --------------------------- library bases ------------------------------ */
95 extern struct Library *IconBase;
96 #ifdef __AROS__
97 extern struct UtilityBase *UtilityBase;
98 #else
99 extern struct Library *UtilityBase;
100 #endif
102 #if defined(__GNUC__) && !defined(__amigaos4__) && !defined(__AROS__)
103 extern struct WBStartup *_WBenchMsg;
104 #endif
106 /* -------------------------- static prototypes --------------------------- */
108 static struct DiskObject *smart_get_icon(struct SmartArgs *args, struct WBStartup *wbarg);
109 static void fstrcpy(struct SmartArgs *args, STRPTR string);
110 static void get_arg_name(struct SmartArgs *args, STRPTR buffer, ULONG size, ULONG * modes);
111 static void get_wbarg_name(struct WBArg *wbarg, STRPTR buffer, ULONG size);
112 static BOOL is_in_template(STRPTR name, STRPTR template);
114 /****** SmartReadArgs/--background-- ****************************************
115 * COPYRIGHT
116 * SmartReadArgs is Copyright 1998 Thomas Aglassinger
118 * ExtReadArgs, its prequel, is Copyright 1994,1995 Stefan Ruppert
120 * Permission is granted to freely distribute the material (also only
121 * parts of it) as long this ReadMe is included and all the copyright
122 * notes are left unaltered except for a description of your changes.
123 * MOTIVATION
124 * The way of parsing ToolTypes provided by "icon.library" is rather
125 * clumsy. This is particular annoying as many programmers and users got
126 * used to ReadArgs(), which does the argument handling for many CLI
127 * commands and ARexx ports.
129 * Unfortunately, ReadArgs lacks a interface to Workbench tooltypes, thus
130 * its usage preventes your programs from being started from Workbench.
132 * SmartReadArgs() copies all Workbench arguments in a single string and
133 * passes this string to the ReadArgs() function. If started from CLI, it
134 * calls ReadArgs() without this step.
135 * AUTHOR
136 * Stefan Ruppert wrote most parts of the source code, designed the general
137 * interface and implemented loads of nice features. Basically he did the
138 * "hard work".
140 * He got the main idea for the implementation from Stefan Winterstein,
141 * the author of ARoach.
143 * Thomas Aglassinger <agi@sbox.tu-graz.ac.at> did some minor changes,
144 * established a more consistent naming schema, reworked the documentation
145 * and also added support for gcc/libnix.
147 * Contact him in case of problems or if you made some enhancements.
149 * Updates are available from aminet:dev/misc/SmartReadArgs.lha.
150 * DISCLAIMER
151 * There is no warranty for this software package. Although the author
152 * has tried to prevent errors, he can't guarantee that the software
153 * package described in this document is 100% reliable. You are
154 * therefore using this material at your own risk. The author cannot be
155 * made responsible for any damage which is caused by using this
156 * software package.
157 ****************************************************************************/
159 /* The define below is used to rename the example main() used in the autodoc
160 * to dummy_main(). Using two main()s would cause problems for the linker. */
161 #define main dummy_main
163 /****** SmartReadArgs/SmartReadArgs ******************************************
164 * NAME
165 * SmartReadArgs -- Workbench/CLI transparent ReadArgs().
166 * SYNOPSIS
167 * error = SmartReadArgs(wb_startup, smart_args);
169 * LONG SmartReadArgs(struct WBStartup *, struct SmartArgs *);
170 * FUNCTION
171 * This function is a CLI/Workbench transparent interface to ReadArgs().
173 * In case of a Workbench start, it analyzes the WBStartup message and
174 * possible tooltypes. These are converted to a text string that can be
175 * passed to ReadArgs() like before.
177 * Tooltypes that are not part of the template are ignored. This includes
178 * tooltypes being disabled with "(...)", NewIcons image data on systems
179 * without NewIcons installed and all this «« Icon by some idiot »» crap.
181 * If the application was stared from CLI, it simply calls ReadArgs()
182 * without the conversion step.
184 * If all went well you get a return value of zero. This means the passed
185 * arguments fit the template and are ready to use. Otherwise you get a
186 * IoErr()-like return code.
187 * INPUTS
188 * wb_startup - Workbench startup message. Refer to your compiler manual
189 * to learn how to obtain it. If this is NULL, the program was
190 * started from CLI.
191 * smart_args - structure which holds all information used by
192 * SmartReadArgs(). You have to setup the following fields before the
193 * call:
195 * sa_Template - The template passed to ReadArgs()
196 * sa_Parameter - ReadArgs() LONG WORD array to hold the arguments
197 * sa_FileParameter - number of Argument in the template to use
198 * for the files passed via WBStartup->sm_ArgList or -1, that
199 * means you don't want any files
200 * sa_Window - Window description string to open when the program
201 * is started from the workbench. NULL means no window. If the
202 * icon has a WINDOW tooltype, this value is ignored.
203 * sa_RDArgs - RDArgs structure to use for ReadArgs() call. This
204 * can be used to provide extended help.
205 * sa_Buffer - Pointer to a buffer to use for the Workbench startup
206 * or NULL, that means SmartReadArgs() allocates a buffer for you
207 * sa_BufferSize - Size of the optional buffer. If it is smaller than
208 * SA_MINIMUM_BUFFER_SIZE it will be adjusted.
210 * All other fields should be set to NULL.
211 * RESULTS
212 * Zero for success. You can check the sa_Flags field for the
213 * SAF_WORKBENCH flag to learn how the program was started.
215 * Otherwise an IoErr()-like error code is returned. This can be passed
216 * directly to PrintFault() or similar.
217 * NOTES
218 * Always call SmartFreeArgs(), even if SmartReadArgs() failed! See example
219 * below.
221 * This function requires "dos.library", "icon.library" and
222 * "utility.library" to be opened by the application. Normally this
223 * already has been done by the compiler startup code.
225 * There is a not widely known feature of ReadArgs(): with templates like
226 * "FROM/M/A,TO/A", you can select the files from workbench performing the
227 * following steps:
229 * - Select the program
230 * - Select the FROM files
231 * - Select and double click the TO file
233 * This is available because ReadArgs() grabs the last string from a
234 * multi-argument FROM and uses it as the TO parameter, if none is passed
235 * explicitely.
236 * BUGS
237 * There are some known problems when used with GCC, mostly related to the
238 * fact that I never bothered creating a useable developer environment
239 * around it (and I'm not sure if this is even possible >:) ...):
241 * - Debugging output shows up in the console instead of SER:. Does
242 * debug.lib exist for gcc? (Wasn't there this strange hunk2gcc
243 * converter?)
244 * - "Read from 0" Enforcer hit in SmartReadArgs(). Couldn't figure out
245 * the exact location yet because of the asynchronous debugging output
246 * mentioned above.
248 * For someone with a reasonable experience with GCC, it should be easy to
249 * fix this.
251 * The SAS/c implementation does not have these problems.
252 * SEE ALSO
253 * SmartFreeArgs(), dos.library/ReadArgs(), icon.library/GetDiskObjectNew()
254 * EXAMPLE
255 * The main archiev comes with a "test.c" and a couple of icons to start
256 * the corresponding executable "test". Take a look at the source code and
257 * play with it.
259 * See below for a smaller code segment that expects the "dos.library",
260 * "icon.library" and "utility.library" to be open already.
261 * SOURCE
263 int main(int argc, STRPTR argv[])
265 struct SmartArgs smart_args =
266 {NULL};
267 LONG argument[2];
268 LONG error;
270 /* Obtain WBStartup; depends on your compiler environment */
271 struct WBStartup *wb_startup = NULL;
272 #if defined(__GNUC__) && !defined(__amigaos4__) && !defined(__AROS__)
273 wb_startup = _WBenchMsg;
274 #else
275 if (argc == 0)
277 wb_startup = (struct WBStartup *) argv;
279 #endif
281 smart_args.sa_Template = "FILES/M/A,VERBOSE";
282 smart_args.sa_Parameter = (IPTR *)argument;
283 smart_args.sa_FileParameter = 0;
284 smart_args.sa_Window = "CON:////My WB-Window/AUTO/CLOSE/WAIT";
286 error = SmartReadArgs(wb_startup, &smart_args);
287 if (error == 0)
289 /* do something */
291 else
293 PrintFault(error, "MyProgram");
296 SmartFreeArgs(&smart_args);
298 return ((error == 0) ? RETURN_OK : RETURN_FAIL);
300 /****************************************************************************/
301 LONG SmartReadArgs(struct WBStartup * wb_startup, struct SmartArgs * args)
303 LONG error;
305 args->sa_Flags = 0;
307 D(bug("UtilityBase = 0x%lx\n", (ULONG) UtilityBase));
308 D(bug("IconBase = 0x%lx\n", (ULONG) IconBase));
309 D(bug("WBStartup = 0x%lx\n", (ULONG) wb_startup));
311 if (wb_startup != NULL)
313 struct WBArg *wbarg = wb_startup->sm_ArgList;
314 LONG arg_counter = 0;
316 D(bug(" numArgs = %ld\n", wb_startup->sm_NumArgs));
317 while (arg_counter < wb_startup->sm_NumArgs)
319 D(bug(" name[%ld] = \"%s\"\n", arg_counter, wbarg->wa_Name));
320 wbarg += 1;
321 arg_counter += 1;
325 if (wb_startup != NULL)
327 if (!(args->sa_RDArgs = AllocDosObject(DOS_RDARGS, NULL)))
329 return (ERROR_NO_FREE_STORE);
331 else
333 args->sa_Flags |= SAF_ALLOCRDARGS;
335 if (!args->sa_Buffer)
337 args->sa_BufferSize = MAX(SA_MINIMUM_BUFFER_SIZE, args->sa_BufferSize);
338 args->sa_Buffer = AllocMem(args->sa_BufferSize, MEMF_ANY);
339 args->sa_Flags |= SAF_ALLOCBUFFER;
342 if (!args->sa_Buffer)
343 return (ERROR_NO_FREE_STORE);
344 else
346 struct DiskObject *dobj;
348 args->sa_ActualPtr = args->sa_Buffer;
349 args->sa_EndPtr = args->sa_Buffer + args->sa_BufferSize - 1;
351 if (!(dobj = smart_get_icon(args, wb_startup)))
353 return (ERROR_OBJECT_NOT_FOUND);
355 else
357 struct WBArg *wbarg = args->sa_WBArg;
358 ULONG num = args->sa_NumArgs;
360 STRPTR *tooltypes = (STRPTR *) dobj->do_ToolTypes;
361 STRPTR name;
362 STRPTR temp;
363 STRPTR ptr;
365 if (num > 1 && args->sa_FileParameter >= 0 && (temp = AllocMem(TEMPSIZE, MEMF_ANY)))
367 ULONG modes = 0;
369 get_arg_name(args, temp, TEMPSIZE, &modes);
370 fstrcpy(args, temp);
371 fstrcpy(args, " ");
373 /* no "/M" specifier in the ReadArgs() template, thus use only the first file */
374 if (modes != MODE_MULTI)
375 num = 2;
377 while (num > 1)
379 get_wbarg_name(wbarg, temp, TEMPSIZE);
380 fstrcpy(args, "\"");
381 fstrcpy(args, temp);
382 fstrcpy(args, "\" ");
383 num--;
384 wbarg++;
387 FreeMem(temp, TEMPSIZE);
390 D(bug("tooltypes=%lx\n", (ULONG) tooltypes));
391 if (tooltypes)
393 while (*tooltypes)
395 ptr = *tooltypes;
396 name = ptr;
398 /* check if this tooltype enabled and part of the
399 * template */
400 if ((*ptr != '(')
401 && is_in_template(name, args->sa_Template))
403 while (*ptr != '=' && *ptr != EOS)
404 ptr++;
406 if (*ptr == '=')
408 *ptr = EOS;
410 if (!Stricmp(name, "WINDOW"))
412 STRPTR win;
413 if ((win = AllocVec((ULONG) strlen(ptr + 1) + 1, MEMF_ANY)))
415 strcpy(win, ptr + 1);
416 args->sa_Window = win;
417 args->sa_Flags |= SAF_ALLOCWINDOW;
421 else
423 fstrcpy(args, name);
425 /* enclose the argument in "" */
426 if (*(ptr + 1) == '"')
428 fstrcpy(args, "=");
429 fstrcpy(args, ptr + 1);
431 else
433 fstrcpy(args, "=\"");
434 fstrcpy(args, ptr + 1);
435 fstrcpy(args, "\"");
438 *ptr = '=';
441 else
442 fstrcpy(args, name);
444 fstrcpy(args, " ");
446 tooltypes++;
447 } /* while (*tooltypes) */
448 } /* if (tooltypes) */
449 fstrcpy(args, "\n");
451 D(bug("final wb command line : \"%s\"\n", args->sa_Buffer));
456 args->sa_RDArgs->RDA_Source.CS_Buffer = args->sa_Buffer;
457 args->sa_RDArgs->RDA_Source.CS_Length = strlen(args->sa_Buffer);
459 args->sa_Flags |= SAF_WORKBENCH;
462 args->sa_FreeArgs = ReadArgs(args->sa_Template, args->sa_Parameter, args->sa_RDArgs);
464 if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C)
466 SetIoErr(ERROR_BREAK);
469 if ((error = IoErr()) == 0 && (wb_startup != NULL))
471 if (args->sa_Window)
473 args->sa_WindowFH = Open(args->sa_Window, MODE_NEWFILE);
474 if (args->sa_WindowFH)
476 args->sa_OldOutput = SelectOutput(args->sa_WindowFH);
477 args->sa_OldInput = SelectInput(args->sa_WindowFH);
482 return (error);
485 /****** SmartReadArgs/SmartFreeArgs ******************************************
486 * NAME
487 * SmartFreeArgs -- Free all resources allocated by SmartReadArgs().
488 * SYNOPSIS
489 * SmartFreeArgs(smart_args);
491 * void SmartFreeArgs(struct SmartArgs *);
492 * FUNCTION
493 * Free all resources allocated by a previous call to SmartReadArgs().
494 * INPUTS
495 * smart_args - Same pointer as passed to SmartReadArgs() before
496 * NOTES
497 * Always call SmartFreeArgs(), even if SmartReadArgs() failed! Take a look
498 * at the example for SmartReadArgs().
499 * SEE ALSO
500 * SmartReadArgs()
501 ****************************************************************************/
502 void SmartFreeArgs(struct SmartArgs *args)
504 /* FreeArgs() can handle a NULL pointer */
505 FreeArgs(args->sa_FreeArgs);
507 if (args->sa_Flags & SAF_ALLOCRDARGS)
508 if (args->sa_RDArgs)
509 FreeDosObject(DOS_RDARGS, args->sa_RDArgs);
511 if (args->sa_Flags & SAF_ALLOCBUFFER)
512 FreeMem(args->sa_Buffer, args->sa_BufferSize);
514 if (args->sa_WindowFH)
516 SelectOutput(args->sa_OldOutput);
517 SelectInput(args->sa_OldInput);
518 Close(args->sa_WindowFH);
521 if (args->sa_Flags & SAF_ALLOCWINDOW && args->sa_Window)
522 FreeVec(args->sa_Window);
526 /* This code was grapped from IconImage/wbarg.c/IconFromWBArg()
527 * Commodore-Amiga Example code
529 static struct DiskObject *smart_get_icon(struct SmartArgs *args, struct WBStartup *wb_startup)
531 struct DiskObject *dob = NULL;
532 struct WBArg *wbarg = wb_startup->sm_ArgList;
533 ULONG num = wb_startup->sm_NumArgs;
535 UBYTE work_name[MAXIMUM_FILENAME_LENGTH];
536 BPTR old_lock, new_lock;
538 /* Copy the WBArg contents */
539 strncpy(work_name, wbarg->wa_Name, MAXIMUM_FILENAME_LENGTH);
541 new_lock = DupLock(wbarg->wa_Lock);
542 if (new_lock != NULL)
544 D(bug("work_name : \"%s\"\n", work_name));
546 /* go to the directory where the icon resides */
547 old_lock = CurrentDir(new_lock);
549 dob = GetDiskObjectNew(work_name);
551 /* test, if the first icon is a project icon and if so, get its icon */
552 if (wb_startup->sm_NumArgs > 1)
554 BPTR new_lock2;
556 if ((new_lock2 = DupLock(wbarg[1].wa_Lock)))
558 struct DiskObject *prj;
560 CurrentDir(new_lock2);
562 UnLock(new_lock);
563 new_lock = new_lock2;
565 strncpy(work_name, wbarg[1].wa_Name, MAXIMUM_FILENAME_LENGTH);
566 D(bug("work_name2 : \"%s\"\n", work_name));
568 if ((prj = GetDiskObjectNew(work_name)))
570 if (prj->do_Type == WBPROJECT)
572 BPTR test;
574 /* if this is only an icon skip it */
575 if (!(test = Lock(work_name, SHARED_LOCK)))
577 wbarg++;
578 num--;
580 else
581 UnLock(test);
583 if (dob)
584 FreeDiskObject(dob);
586 dob = prj;
592 if (dob)
594 D(bug("dobj window : %s\n", dob->do_ToolWindow));
597 /* go back to where we used to be */
598 CurrentDir(old_lock);
600 /* release the duplicated lock */
601 UnLock(new_lock);
603 args->sa_WBArg = wbarg + 1;
604 args->sa_NumArgs = num;
607 D(bug("return (dob)\n"));
609 return (dob);
612 static void fstrcpy(struct SmartArgs *args, STRPTR string)
614 STRPTR ptr = args->sa_ActualPtr;
615 STRPTR end = args->sa_EndPtr;
617 while (ptr < end && *string)
618 *ptr++ = *string++;
620 *ptr = EOS; /* Mark end of string */
622 args->sa_ActualPtr = ptr;
625 static void get_arg_name(struct SmartArgs *args, STRPTR buffer, ULONG size, ULONG * modes)
627 ULONG num = args->sa_FileParameter;
628 STRPTR ptr = args->sa_Template;
630 *modes = 0;
632 while (num > 0)
634 while (*ptr != ',' && *ptr != EOS)
635 ptr++;
637 if (*ptr == ',')
638 ptr++;
639 num--;
642 if (*ptr != EOS)
644 while (*ptr != ',' && *ptr != '/' && *ptr != EOS && size > 0)
646 *buffer++ = *ptr++;
647 size--;
650 while (*ptr == '/')
652 ptr++;
654 if (*ptr == 'M' || *ptr == 'm')
655 *modes = MODE_MULTI;
657 ptr++;
661 *buffer = EOS;
664 static void get_wbarg_name(struct WBArg *wbarg, STRPTR buffer, ULONG size)
666 BPTR new;
668 if ((new = DupLock(wbarg->wa_Lock)))
670 if (!NameFromLock(new, buffer, size))
671 *buffer = EOS;
672 else if (!AddPart(buffer, wbarg->wa_Name, size))
673 *buffer = EOS;
675 UnLock(new);
677 else
678 *buffer = EOS;
681 /* Enable extended debugging for is_in_template() */
682 #if 0
683 #define D2(x) x
684 #else
685 #define D2(x) /* nufin */
686 #endif
687 static BOOL is_in_template(STRPTR name, STRPTR template)
689 BOOL found = FALSE;
690 STRPTR current_word = template;
691 BOOL skip_switch = FALSE;
692 size_t name_length;
694 /* Evaluate length of name part of whole tooltype */
695 name_length = 0;
696 while ((name[name_length] != EOS)
697 && (name[name_length] != '='))
699 name_length += 1;
702 D(bug("find `%s' in template `%s'\n", name, template));
703 while ((current_word[0] != '\0') && (!found))
705 STRPTR next_word = strpbrk(current_word, "/=,");
706 size_t current_word_length;
708 if (next_word == NULL)
710 next_word = current_word + strlen(current_word);
712 current_word_length = next_word - current_word;
714 if (skip_switch)
716 D2(bug(" skip (`%s', %lu)\n", current_word, current_word_length));
717 skip_switch = FALSE;
719 else
721 D2(bug(" check (`%s', %lu)\n", current_word, current_word_length));
722 if ((name_length == current_word_length)
723 && !Strnicmp(name, current_word, (LONG) name_length))
725 D(bug(" found!\n"));
726 found = TRUE;
730 current_word = next_word;
731 if (current_word[0] != '\0')
733 if (current_word[0] == '/')
735 skip_switch = TRUE;
737 current_word += 1;
741 return found;