source: trunk/src/helpers/apps.c@ 152

Last change on this file since 152 was 152, checked in by umoeller, 23 years ago

Sources as of 0.9.18.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 69.4 KB
Line 
1
2/*
3 *@@sourcefile apps.c:
4 * contains program helpers (environments, application start).
5 *
6 * This file is new with V0.9.12 and contains functions
7 * previously in winh.c and dosh2.c.
8 *
9 * Note: Version numbering in this file relates to XWorkplace version
10 * numbering.
11 *
12 *@@header "helpers\apps.h"
13 *@@added V0.9.12 (2001-05-26) [umoeller]
14 */
15
16/*
17 * Copyright (C) 1997-2001 Ulrich M”ller.
18 * This file is part of the "XWorkplace helpers" source package.
19 * This is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published
21 * by the Free Software Foundation, in version 2 as it comes in the
22 * "COPYING" file of the XWorkplace main distribution.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 */
28
29#define OS2EMX_PLAIN_CHAR
30 // this is needed for "os2emx.h"; if this is defined,
31 // emx will define PSZ as _signed_ char, otherwise
32 // as unsigned char
33
34#define INCL_DOSPROCESS
35#define INCL_DOSEXCEPTIONS
36#define INCL_DOSMODULEMGR
37#define INCL_DOSSESMGR
38#define INCL_DOSERRORS
39
40#define INCL_WINPROGRAMLIST // needed for PROGDETAILS, wppgm.h
41#define INCL_WINERRORS
42#define INCL_SHLERRORS
43#include <os2.h>
44
45#include <stdio.h>
46#include <setjmp.h> // needed for except.h
47#include <assert.h> // needed for except.h
48
49#include "setup.h" // code generation and debugging options
50
51#include "helpers\apps.h"
52#include "helpers\dosh.h"
53#include "helpers\except.h" // exception handling
54#include "helpers\prfh.h"
55#include "helpers\standards.h" // some standard macros
56#include "helpers\stringh.h"
57#include "helpers\winh.h"
58#include "helpers\xstring.h"
59
60/*
61 *@@category: Helpers\PM helpers\Application helpers
62 */
63
64/* ******************************************************************
65 *
66 * Environment helpers
67 *
68 ********************************************************************/
69
70/*
71 *@@ appQueryEnvironmentLen:
72 * returns the total length of the passed in environment
73 * string buffer, including the terminating two null bytes.
74 *
75 *@@added V0.9.16 (2002-01-09) [umoeller]
76 */
77
78ULONG appQueryEnvironmentLen(PCSZ pcszEnvironment)
79{
80 ULONG cbEnvironment = 0;
81 if (pcszEnvironment)
82 {
83 PCSZ pVarThis = pcszEnvironment;
84 // go thru the environment strings; last one has two null bytes
85 while (*pVarThis)
86 {
87 ULONG ulLenThis = strlen(pVarThis) + 1;
88 cbEnvironment += ulLenThis;
89 pVarThis += ulLenThis;
90 }
91
92 cbEnvironment++; // last null byte
93 }
94
95 return (cbEnvironment);
96}
97
98/*
99 *@@ appParseEnvironment:
100 * this takes one of those ugly environment strings
101 * as used by DosStartSession and WinStartApp (with
102 * lots of zero-terminated strings one after another
103 * and a duplicate zero byte as a terminator) as
104 * input and splits it into an array of separate
105 * strings in pEnv.
106 *
107 * The newly allocated strings are stored in in
108 * pEnv->papszVars. The array count is stored in
109 * pEnv->cVars.
110 *
111 * Each environment variable will be copied into
112 * one newly allocated string in the array. Use
113 * appFreeEnvironment to free the memory allocated
114 * by this function.
115 *
116 * Use the following code to browse thru the array:
117 +
118 + DOSENVIRONMENT Env = {0};
119 + if (appParseEnvironment(pszEnv,
120 + &Env)
121 + == NO_ERROR)
122 + {
123 + if (Env.papszVars)
124 + {
125 + PSZ *ppszThis = Env.papszVars;
126 + for (ul = 0;
127 + ul < Env.cVars;
128 + ul++)
129 + {
130 + PSZ pszThis = *ppszThis;
131 + // pszThis now has something like PATH=C:\TEMP
132 + // ...
133 + // next environment string
134 + ppszThis++;
135 + }
136 + }
137 + appFreeEnvironment(&Env);
138 + }
139 *
140 *@@added V0.9.4 (2000-08-02) [umoeller]
141 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
142 */
143
144APIRET appParseEnvironment(const char *pcszEnv,
145 PDOSENVIRONMENT pEnv) // out: new environment
146{
147 APIRET arc = NO_ERROR;
148 if (!pcszEnv)
149 arc = ERROR_INVALID_PARAMETER;
150 else
151 {
152 PSZ pszVarThis = (PSZ)pcszEnv;
153 ULONG cVars = 0;
154 // count strings
155 while (*pszVarThis)
156 {
157 cVars++;
158 pszVarThis += strlen(pszVarThis) + 1;
159 }
160
161 pEnv->cVars = 0;
162 pEnv->papszVars = 0;
163
164 if (cVars)
165 {
166 ULONG cbArray = sizeof(PSZ) * cVars;
167 PSZ *papsz;
168 if (!(papsz = (PSZ*)malloc(cbArray)))
169 arc = ERROR_NOT_ENOUGH_MEMORY;
170 else
171 {
172 PSZ *ppszTarget = papsz;
173 memset(papsz, 0, cbArray);
174 pszVarThis = (PSZ)pcszEnv;
175 while (*pszVarThis)
176 {
177 ULONG ulThisLen;
178 if (!(*ppszTarget = strhdup(pszVarThis, &ulThisLen)))
179 {
180 arc = ERROR_NOT_ENOUGH_MEMORY;
181 break;
182 }
183 (pEnv->cVars)++;
184 ppszTarget++;
185 pszVarThis += ulThisLen + 1;
186 }
187
188 pEnv->papszVars = papsz;
189 }
190 }
191 }
192
193 return (arc);
194}
195
196/*
197 *@@ appGetEnvironment:
198 * calls appParseEnvironment for the current
199 * process environment, which is retrieved from
200 * the info blocks.
201 *
202 * Returns:
203 *
204 * -- NO_ERROR:
205 *
206 * -- ERROR_INVALID_PARAMETER
207 *
208 * -- ERROR_BAD_ENVIRONMENT: no environment found in
209 * info blocks.
210 *
211 *@@added V0.9.4 (2000-07-19) [umoeller]
212 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
213 */
214
215APIRET appGetEnvironment(PDOSENVIRONMENT pEnv)
216{
217 APIRET arc = NO_ERROR;
218 if (!pEnv)
219 arc = ERROR_INVALID_PARAMETER;
220 else
221 {
222 PTIB ptib = 0;
223 PPIB ppib = 0;
224 arc = DosGetInfoBlocks(&ptib, &ppib);
225 if (arc == NO_ERROR)
226 {
227 PSZ pszEnv;
228 if (pszEnv = ppib->pib_pchenv)
229 arc = appParseEnvironment(pszEnv, pEnv);
230 else
231 arc = ERROR_BAD_ENVIRONMENT;
232 }
233 }
234
235 return (arc);
236}
237
238/*
239 *@@ appFindEnvironmentVar:
240 * returns the PSZ* in the pEnv->papszVars array
241 * which specifies the environment variable in pszVarName.
242 *
243 * With pszVarName, you can either specify the variable
244 * name only ("VARNAME") or a full environment string
245 * ("VARNAME=BLAH"). In any case, only the variable name
246 * is compared.
247 *
248 * Returns NULL if no such variable name was found in
249 * the array.
250 *
251 *@@added V0.9.4 (2000-07-19) [umoeller]
252 *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
253 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
254 *@@changed V0.9.16 (2002-01-01) [umoeller]: removed extra heap allocation
255 */
256
257PSZ* appFindEnvironmentVar(PDOSENVIRONMENT pEnv,
258 PSZ pszVarName)
259{
260 PSZ *ppszRet = 0;
261
262 if ( (pEnv)
263 && (pEnv->papszVars)
264 && (pszVarName)
265 )
266 {
267 ULONG ul = 0;
268 ULONG ulVarNameLen = 0;
269
270 PSZ pFirstEqual;
271 // rewrote all the following for speed V0.9.16 (2002-01-01) [umoeller]
272 if (pFirstEqual = strchr(pszVarName, '='))
273 // VAR=VALUE
274 // ^ pFirstEqual
275 ulVarNameLen = pFirstEqual - pszVarName;
276 else
277 ulVarNameLen = strlen(pszVarName);
278
279 for (ul = 0;
280 ul < pEnv->cVars;
281 ul++)
282 {
283 PSZ pszThis = pEnv->papszVars[ul];
284 if (pFirstEqual = strchr(pszThis, '='))
285 {
286 ULONG ulLenThis = pFirstEqual - pszThis;
287 if ( (ulLenThis == ulVarNameLen)
288 && (!memicmp(pszThis,
289 pszVarName,
290 ulVarNameLen))
291 )
292 {
293 ppszRet = &pEnv->papszVars[ul];
294 break;
295 }
296 }
297 }
298 }
299
300 return (ppszRet);
301}
302
303/*
304 *@@ appSetEnvironmentVar:
305 * sets an environment variable in the specified
306 * environment, which must have been initialized
307 * using appGetEnvironment first.
308 *
309 * pszNewEnv must be a full environment string
310 * in the form "VARNAME=VALUE".
311 *
312 * If "VARNAME" has already been set to something
313 * in the string array in pEnv, that array item
314 * is replaced.
315 *
316 * OTOH, if "VARNAME" has not been set yet, a new
317 * item is added to the array, and pEnv->cVars is
318 * raised by one. In that case, fAddFirst determines
319 * whether the new array item is added to the front
320 * or the tail of the environment list.
321 *
322 *@@added V0.9.4 (2000-07-19) [umoeller]
323 *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
324 *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
325 *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed crash if !fAddFirst
326 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
327 */
328
329APIRET appSetEnvironmentVar(PDOSENVIRONMENT pEnv,
330 PSZ pszNewEnv,
331 BOOL fAddFirst)
332{
333 APIRET arc = NO_ERROR;
334 if ((!pEnv) || (!pszNewEnv))
335 arc = ERROR_INVALID_PARAMETER;
336 else
337 {
338 if (!pEnv->papszVars)
339 {
340 // no variables set yet:
341 pEnv->papszVars = (PSZ*)malloc(sizeof(PSZ));
342 pEnv->cVars = 1;
343
344 *(pEnv->papszVars) = strdup(pszNewEnv);
345 }
346 else
347 {
348 PSZ *ppszEnvLine;
349 if (ppszEnvLine = appFindEnvironmentVar(pEnv, pszNewEnv))
350 // was set already: replace
351 arc = strhStore(ppszEnvLine,
352 pszNewEnv,
353 NULL);
354 else
355 {
356 // not set already:
357 PSZ *ppszNew = NULL;
358
359 // allocate new array, with one new entry
360 // fixed V0.9.12 (2001-05-26) [umoeller], this crashed
361 PSZ *papszNew;
362
363 if (!(papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1))))
364 arc = ERROR_NOT_ENOUGH_MEMORY;
365 else
366 {
367 if (fAddFirst)
368 {
369 // add as first entry:
370 // overwrite first entry
371 ppszNew = papszNew;
372 // copy old entries
373 memcpy(papszNew + 1, // second new entry
374 pEnv->papszVars, // first old entry
375 sizeof(PSZ) * pEnv->cVars);
376 }
377 else
378 {
379 // append at the tail:
380 // overwrite last entry
381 ppszNew = papszNew + pEnv->cVars;
382 // copy old entries
383 memcpy(papszNew, // first new entry
384 pEnv->papszVars, // first old entry
385 sizeof(PSZ) * pEnv->cVars);
386 }
387
388 free(pEnv->papszVars); // was missing V0.9.12 (2001-05-21) [umoeller]
389 pEnv->papszVars = papszNew;
390 pEnv->cVars++;
391 *ppszNew = strdup(pszNewEnv);
392 }
393 }
394 }
395 }
396
397 return (arc);
398}
399
400/*
401 *@@ appConvertEnvironment:
402 * converts an environment initialized by appGetEnvironment
403 * to the string format required by WinStartApp and DosExecPgm,
404 * that is, one memory block is allocated in *ppszEnv and all
405 * strings in pEnv->papszVars are copied to that block. Each
406 * string is terminated with a null character; the last string
407 * is terminated with two null characters.
408 *
409 * Use free() to free the memory block allocated by this
410 * function in *ppszEnv.
411 *
412 *@@added V0.9.4 (2000-07-19) [umoeller]
413 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
414 */
415
416APIRET appConvertEnvironment(PDOSENVIRONMENT pEnv,
417 PSZ *ppszEnv, // out: environment string
418 PULONG pulSize) // out: size of block allocated in *ppszEnv; ptr can be NULL
419{
420 APIRET arc = NO_ERROR;
421 if ( (!pEnv)
422 || (!pEnv->papszVars)
423 )
424 arc = ERROR_INVALID_PARAMETER;
425 else
426 {
427 // count memory needed for all strings
428 ULONG cbNeeded = 0,
429 ul = 0;
430 PSZ *ppszThis = pEnv->papszVars;
431
432 for (ul = 0;
433 ul < pEnv->cVars;
434 ul++)
435 {
436 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
437
438 // next environment string
439 ppszThis++;
440 }
441
442 cbNeeded++; // for another null terminator
443
444 if (!(*ppszEnv = (PSZ)malloc(cbNeeded)))
445 arc = ERROR_NOT_ENOUGH_MEMORY;
446 else
447 {
448 PSZ pTarget = *ppszEnv;
449 if (pulSize)
450 *pulSize = cbNeeded;
451 ppszThis = pEnv->papszVars;
452
453 // now copy each string
454 for (ul = 0;
455 ul < pEnv->cVars;
456 ul++)
457 {
458 PSZ pSource = *ppszThis;
459
460 while ((*pTarget++ = *pSource++))
461 ;
462
463 // *pTarget++ = 0; // append null terminator per string
464
465 // next environment string
466 ppszThis++;
467 }
468
469 *pTarget++ = 0; // append second null terminator
470 }
471 }
472
473 return (arc);
474}
475
476/*
477 *@@ appFreeEnvironment:
478 * frees memory allocated by appGetEnvironment.
479 *
480 *@@added V0.9.4 (2000-07-19) [umoeller]
481 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
482 */
483
484APIRET appFreeEnvironment(PDOSENVIRONMENT pEnv)
485{
486 APIRET arc = NO_ERROR;
487 if ( (!pEnv)
488 || (!pEnv->papszVars)
489 )
490 arc = ERROR_INVALID_PARAMETER;
491 else
492 {
493 PSZ *ppszThis = pEnv->papszVars;
494 PSZ pszThis;
495 ULONG ul = 0;
496
497 for (ul = 0;
498 ul < pEnv->cVars;
499 ul++)
500 {
501 pszThis = *ppszThis;
502 free(pszThis);
503 // *ppszThis = NULL;
504 // next environment string
505 ppszThis++;
506 }
507
508 free(pEnv->papszVars);
509 pEnv->cVars = 0;
510 }
511
512 return (arc);
513}
514
515/* ******************************************************************
516 *
517 * Application information
518 *
519 ********************************************************************/
520
521/*
522 *@@ appQueryAppType:
523 * returns the Control Program (Dos) and
524 * Win* PROG_* application types for the
525 * specified executable. Essentially, this
526 * is a wrapper around DosQueryAppType.
527 *
528 * pcszExecutable must be fully qualified.
529 * You can use doshFindExecutable to qualify
530 * it.
531 *
532 * This returns the APIRET of DosQueryAppType.
533 * If this is NO_ERROR; *pulDosAppType receives
534 * the app type of DosQueryAppType. In addition,
535 * *pulWinAppType is set to one of the following:
536 *
537 * -- PROG_FULLSCREEN
538 *
539 * -- PROG_PDD
540 *
541 * -- PROG_VDD
542 *
543 * -- PROG_DLL
544 *
545 * -- PROG_WINDOWEDVDM
546 *
547 * -- PROG_PM
548 *
549 * -- PROG_31_ENHSEAMLESSCOMMON
550 *
551 * -- PROG_WINDOWABLEVIO
552 *
553 * -- PROG_DEFAULT
554 *
555 *@@added V0.9.9 (2001-03-07) [umoeller]
556 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
557 *@@changed V0.9.14 (2001-08-07) [pr]: use FAPPTYP_* constants
558 *@@changed V0.9.16 (2001-12-08) [umoeller]: added checks for batch files, other optimizations
559 */
560
561APIRET appQueryAppType(const char *pcszExecutable,
562 PULONG pulDosAppType, // out: DOS app type
563 PULONG pulWinAppType) // out: PROG_* app type
564{
565 APIRET arc;
566
567/*
568 #define FAPPTYP_NOTSPEC 0x0000
569 #define FAPPTYP_NOTWINDOWCOMPAT 0x0001
570 #define FAPPTYP_WINDOWCOMPAT 0x0002
571 #define FAPPTYP_WINDOWAPI 0x0003
572 #define FAPPTYP_BOUND 0x0008
573 #define FAPPTYP_DLL 0x0010
574 #define FAPPTYP_DOS 0x0020
575 #define FAPPTYP_PHYSDRV 0x0040 // physical device driver
576 #define FAPPTYP_VIRTDRV 0x0080 // virtual device driver
577 #define FAPPTYP_PROTDLL 0x0100 // 'protected memory' dll
578 #define FAPPTYP_WINDOWSREAL 0x0200 // Windows real mode app
579 #define FAPPTYP_WINDOWSPROT 0x0400 // Windows protect mode app
580 #define FAPPTYP_WINDOWSPROT31 0x1000 // Windows 3.1 protect mode app
581 #define FAPPTYP_32BIT 0x4000
582*/
583
584 ULONG ulWinAppType = PROG_DEFAULT;
585
586 if (!(arc = DosQueryAppType((PSZ)pcszExecutable, pulDosAppType)))
587 {
588 // clear the 32-bit flag
589 // V0.9.16 (2001-12-08) [umoeller]
590 ULONG ulDosAppType = (*pulDosAppType) & ~FAPPTYP_32BIT,
591 ulLoAppType = ulDosAppType & 0xFFFF;
592
593 if (ulDosAppType & FAPPTYP_PHYSDRV) // 0x40
594 ulWinAppType = PROG_PDD;
595 else if (ulDosAppType & FAPPTYP_VIRTDRV) // 0x80
596 ulWinAppType = PROG_VDD;
597 else if ((ulDosAppType & 0xF0) == FAPPTYP_DLL) // 0x10
598 // DLL bit set
599 ulWinAppType = PROG_DLL;
600 else if (ulDosAppType & FAPPTYP_DOS) // 0x20
601 // DOS bit set?
602 ulWinAppType = PROG_WINDOWEDVDM;
603 else if ((ulDosAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) // 0x0003)
604 // "Window-API" == PM
605 ulWinAppType = PROG_PM;
606 else if (ulLoAppType == FAPPTYP_WINDOWSREAL)
607 ulWinAppType = PROG_31_ENHSEAMLESSCOMMON; // @@todo really?
608 else if ( (ulLoAppType == FAPPTYP_WINDOWSPROT31) // 0x1000) // windows program (?!?)
609 || (ulLoAppType == FAPPTYP_WINDOWSPROT) // ) // windows program (?!?)
610 )
611 ulWinAppType = PROG_31_ENHSEAMLESSCOMMON; // PROG_31_ENH;
612 else if ((ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_WINDOWCOMPAT) // 0x02)
613 ulWinAppType = PROG_WINDOWABLEVIO;
614 else if ((ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_NOTWINDOWCOMPAT) // 0x01)
615 ulWinAppType = PROG_FULLSCREEN;
616 }
617
618 if (ulWinAppType == PROG_DEFAULT)
619 {
620 // added checks for batch files V0.9.16 (2001-12-08) [umoeller]
621 PCSZ pcszExt;
622 if (pcszExt = doshGetExtension(pcszExecutable))
623 {
624 if (!stricmp(pcszExt, "BAT"))
625 {
626 ulWinAppType = PROG_WINDOWEDVDM;
627 arc = NO_ERROR;
628 }
629 else if (!stricmp(pcszExt, "CMD"))
630 {
631 ulWinAppType = PROG_WINDOWABLEVIO;
632 arc = NO_ERROR;
633 }
634 }
635 }
636
637 *pulWinAppType = ulWinAppType;
638
639 return (arc);
640}
641
642/*
643 *@@ PROGTYPESTRING:
644 *
645 *@@added V0.9.16 (2002-01-13) [umoeller]
646 */
647
648typedef struct _PROGTYPESTRING
649{
650 PROGCATEGORY progc;
651 PCSZ pcsz;
652} PROGTYPESTRING, *PPROGTYPESTRING;
653
654PROGTYPESTRING G_aProgTypes[] =
655 {
656 PROG_DEFAULT, "PROG_DEFAULT",
657 PROG_FULLSCREEN, "PROG_FULLSCREEN",
658 PROG_WINDOWABLEVIO, "PROG_WINDOWABLEVIO",
659 PROG_PM, "PROG_PM",
660 PROG_GROUP, "PROG_GROUP",
661 PROG_VDM, "PROG_VDM",
662 // same as PROG_REAL, "PROG_REAL",
663 PROG_WINDOWEDVDM, "PROG_WINDOWEDVDM",
664 PROG_DLL, "PROG_DLL",
665 PROG_PDD, "PROG_PDD",
666 PROG_VDD, "PROG_VDD",
667 PROG_WINDOW_REAL, "PROG_WINDOW_REAL",
668 PROG_30_STD, "PROG_30_STD",
669 // same as PROG_WINDOW_PROT, "PROG_WINDOW_PROT",
670 PROG_WINDOW_AUTO, "PROG_WINDOW_AUTO",
671 PROG_30_STDSEAMLESSVDM, "PROG_30_STDSEAMLESSVDM",
672 // same as PROG_SEAMLESSVDM, "PROG_SEAMLESSVDM",
673 PROG_30_STDSEAMLESSCOMMON, "PROG_30_STDSEAMLESSCOMMON",
674 // same as PROG_SEAMLESSCOMMON, "PROG_SEAMLESSCOMMON",
675 PROG_31_STDSEAMLESSVDM, "PROG_31_STDSEAMLESSVDM",
676 PROG_31_STDSEAMLESSCOMMON, "PROG_31_STDSEAMLESSCOMMON",
677 PROG_31_ENHSEAMLESSVDM, "PROG_31_ENHSEAMLESSVDM",
678 PROG_31_ENHSEAMLESSCOMMON, "PROG_31_ENHSEAMLESSCOMMON",
679 PROG_31_ENH, "PROG_31_ENH",
680 PROG_31_STD, "PROG_31_STD",
681
682// Warp 4 toolkit defines, whatever these were designed for...
683#ifndef PROG_DOS_GAME
684 #define PROG_DOS_GAME (PROGCATEGORY)21
685#endif
686#ifndef PROG_WIN_GAME
687 #define PROG_WIN_GAME (PROGCATEGORY)22
688#endif
689#ifndef PROG_DOS_MODE
690 #define PROG_DOS_MODE (PROGCATEGORY)23
691#endif
692
693 PROG_DOS_GAME, "PROG_DOS_GAME",
694 PROG_WIN_GAME, "PROG_WIN_GAME",
695 PROG_DOS_MODE, "PROG_DOS_MODE",
696
697 // added this V0.9.16 (2001-12-08) [umoeller]
698 PROG_WIN32, "PROG_WIN32"
699 };
700
701/*
702 *@@ appDescribeAppType:
703 * returns a "PROG_*" string for the given
704 * program type. Useful for WPProgram setup
705 * strings and such.
706 *
707 *@@added V0.9.16 (2001-10-06)
708 */
709
710PCSZ appDescribeAppType(PROGCATEGORY progc) // in: from PROGDETAILS.progc
711{
712 ULONG ul;
713 for (ul = 0;
714 ul < ARRAYITEMCOUNT(G_aProgTypes);
715 ul++)
716 {
717 if (G_aProgTypes[ul].progc == progc)
718 return (G_aProgTypes[ul].pcsz);
719 }
720
721 return NULL;
722}
723
724/*
725 *@@ appIsWindowsApp:
726 * checks the specified program category
727 * (PROGDETAILS.progt.progc) for whether
728 * it represents a Win-OS/2 application.
729 *
730 * Returns:
731 *
732 * -- 0: no windows app (it's VIO, OS/2
733 * or DOS fullscreen, or PM).
734 *
735 * -- 1: Win-OS/2 standard app.
736 *
737 * -- 2: Win-OS/2 enhanced-mode app.
738 *
739 *@@added V0.9.12 (2001-05-26) [umoeller]
740 */
741
742ULONG appIsWindowsApp(ULONG ulProgCategory)
743{
744 switch (ulProgCategory)
745 {
746 case PROG_31_ENHSEAMLESSVDM: // 17
747 case PROG_31_ENHSEAMLESSCOMMON: // 18
748 case PROG_31_ENH: // 19
749 return (2);
750
751#ifndef PROG_30_STD
752 #define PROG_30_STD (PROGCATEGORY)11
753#endif
754
755#ifndef PROG_30_STDSEAMLESSVDM
756 #define PROG_30_STDSEAMLESSVDM (PROGCATEGORY)13
757#endif
758
759 case PROG_WINDOW_REAL: // 10
760 case PROG_30_STD: // 11
761 case PROG_WINDOW_AUTO: // 12
762 case PROG_30_STDSEAMLESSVDM: // 13
763 case PROG_30_STDSEAMLESSCOMMON: // 14
764 case PROG_31_STDSEAMLESSVDM: // 15
765 case PROG_31_STDSEAMLESSCOMMON: // 16
766 case PROG_31_STD: // 20
767 return (1);
768 }
769
770 return (0);
771}
772
773/* ******************************************************************
774 *
775 * Application start
776 *
777 ********************************************************************/
778
779/*
780 *@@ CallBatchCorrectly:
781 * fixes the specified PROGDETAILS for
782 * command files in the executable part
783 * by inserting /C XXX into the parameters
784 * and setting the executable according
785 * to an environment variable.
786 *
787 *@@added V0.9.6 (2000-10-16) [umoeller]
788 *@@changed V0.9.7 (2001-01-15) [umoeller]: now using XSTRING
789 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
790 */
791
792static VOID CallBatchCorrectly(PPROGDETAILS pProgDetails,
793 PXSTRING pstrParams, // in/out: modified parameters (reallocated)
794 const char *pcszEnvVar, // in: env var spec'g command proc
795 // (e.g. "OS2_SHELL"); can be NULL
796 const char *pcszDefProc) // in: def't command proc (e.g. "CMD.EXE")
797{
798 // XXX.CMD file as executable:
799 // fix args to /C XXX.CMD
800
801 PSZ pszOldParams = NULL;
802 ULONG ulOldParamsLength = pstrParams->ulLength;
803 if (ulOldParamsLength)
804 // we have parameters already:
805 // make a backup... we'll append that later
806 pszOldParams = strdup(pstrParams->psz);
807
808 // set new params to "/C filename.cmd"
809 xstrcpy(pstrParams, "/C ", 0);
810 xstrcat(pstrParams,
811 pProgDetails->pszExecutable,
812 0);
813
814 if (pszOldParams)
815 {
816 // .cmd had params:
817 // append space and old params
818 xstrcatc(pstrParams, ' ');
819 xstrcat(pstrParams,
820 pszOldParams,
821 ulOldParamsLength);
822 free(pszOldParams);
823 }
824
825 // set executable to $(OS2_SHELL)
826 pProgDetails->pszExecutable = NULL;
827 if (pcszEnvVar)
828 pProgDetails->pszExecutable = getenv(pcszEnvVar);
829 if (!pProgDetails->pszExecutable)
830 pProgDetails->pszExecutable = (PSZ)pcszDefProc;
831 // should be on PATH
832}
833
834/*
835 *@@ appQueryDefaultWin31Environment:
836 * returns the default Win-OS/2 3.1 environment
837 * from OS2.INI, which you can then merge with
838 * your process environment to be able to
839 * start Win-OS/2 sessions properly with
840 * appStartApp.
841 *
842 * Caller must free() the return value.
843 *
844 *@@added V0.9.12 (2001-05-26) [umoeller]
845 */
846
847PSZ appQueryDefaultWin31Environment(VOID)
848{
849 PSZ pszReturn = NULL;
850 ULONG ulSize = 0;
851 // get default environment (from Win-OS/2 settings object)
852 // from OS2.INI
853 PSZ pszDefEnv = prfhQueryProfileData(HINI_USER,
854 "WINOS2",
855 "PM_GlobalWindows31Settings",
856 &ulSize);
857 if (pszDefEnv)
858 {
859 if (pszReturn = (PSZ)malloc(ulSize + 2))
860 {
861 PSZ p;
862 memset(pszReturn, 0, ulSize + 2);
863 memcpy(pszReturn, pszDefEnv, ulSize);
864
865 for (p = pszReturn;
866 p < pszReturn + ulSize;
867 p++)
868 if (*p == ';')
869 *p = 0;
870
871 // okay.... now we got an OS/2-style environment
872 // with 0, 0, 00 strings
873 }
874
875 free(pszDefEnv);
876 }
877
878 return (pszReturn);
879}
880
881#ifdef _PMPRINTF_
882
883static void DumpMemoryBlock(PBYTE pb, // in: start address
884 ULONG ulSize, // in: size of block
885 ULONG ulIndent) // in: how many spaces to put
886 // before each output line
887{
888 TRY_QUIET(excpt1)
889 {
890 PBYTE pbCurrent = pb; // current byte
891 ULONG ulCount = 0,
892 ulCharsInLine = 0; // if this grows > 7, a new line is started
893 CHAR szTemp[1000];
894 CHAR szLine[400] = "",
895 szAscii[30] = " "; // ASCII representation; filled for every line
896 PSZ pszLine = szLine,
897 pszAscii = szAscii;
898
899 for (pbCurrent = pb;
900 ulCount < ulSize;
901 pbCurrent++, ulCount++)
902 {
903 if (ulCharsInLine == 0)
904 {
905 memset(szLine, ' ', ulIndent);
906 pszLine += ulIndent;
907 }
908 pszLine += sprintf(pszLine, "%02lX ", (ULONG)*pbCurrent);
909
910 if ( (*pbCurrent > 31) && (*pbCurrent < 127) )
911 // printable character:
912 *pszAscii = *pbCurrent;
913 else
914 *pszAscii = '.';
915 pszAscii++;
916
917 ulCharsInLine++;
918 if ( (ulCharsInLine > 7) // 8 bytes added?
919 || (ulCount == ulSize-1) // end of buffer reached?
920 )
921 {
922 // if we haven't had eight bytes yet,
923 // fill buffer up to eight bytes with spaces
924 ULONG ul2;
925 for (ul2 = ulCharsInLine;
926 ul2 < 8;
927 ul2++)
928 pszLine += sprintf(pszLine, " ");
929
930 sprintf(szTemp, "%04lX: %s %ss",
931 (ulCount & 0xFFFFFFF8), // offset in hex
932 szLine, // bytes string
933 szAscii); // ASCII string
934
935 _Pmpf(("%s", szTemp));
936
937 // restart line buffer
938 pszLine = szLine;
939
940 // clear ASCII buffer
941 strcpy(szAscii, " ");
942 pszAscii = szAscii;
943
944 // reset line counter
945 ulCharsInLine = 0;
946 }
947 }
948
949 }
950 CATCH(excpt1)
951 {
952 _Pmpf(("Crash in " __FUNCTION__ ));
953 } END_CATCH();
954}
955
956#endif
957
958/*
959 *@@ appFixProgDetails:
960 * extracted code from appStartApp to fix the
961 * given PROGDETAILS data to support the typical
962 * WPS stuff.
963 *
964 * This is now used by XWP's progOpenProgram
965 * directly as a temporary fix for all the
966 * session hangs.
967 *
968 * The caller is responsible for cleaning out
969 * the given three memory buffers.
970 *
971 *@@added V0.9.18 (2002-03-27) [umoeller]
972 */
973
974APIRET appFixProgDetails(PPROGDETAILS pDetails, // out: fixed program spec (req.)
975 const PROGDETAILS *pcProgDetails, // in: program spec (req.)
976 ULONG ulFlags, // in: APP_RUN_* flags or 0
977 PXSTRING pstrExecutablePatched, // in: init'ed XSTRING, out: to be freed
978 PXSTRING pstrParamsPatched, // in: init'ed XSTRING, out: to be freed
979 PSZ *ppszWinOS2Env) // in: ptr to NULL, out: memory to be freed
980{
981 APIRET arc = NO_ERROR;
982
983 memcpy(pDetails, pcProgDetails, sizeof(PROGDETAILS));
984 // pointers still point into old prog details buffer
985 pDetails->Length = sizeof(PROGDETAILS);
986 pDetails->progt.fbVisible = SHE_VISIBLE;
987
988 // all this only makes sense if this contains something...
989 // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
990 if ( (!pDetails->pszExecutable)
991 || (!(*(pDetails->pszExecutable)))
992 )
993 arc = ERROR_INVALID_PARAMETER;
994 else
995 {
996 ULONG ulIsWinApp;
997
998 // memset(&pDetails->swpInitial, 0, sizeof(SWP));
999 // this wasn't a good idea... WPProgram stores stuff
1000 // in here, such as the "minimize on startup" -> SWP_MINIMIZE
1001
1002 // duplicate parameters...
1003 // we need this for string manipulations below...
1004 if ( (pDetails->pszParameters)
1005 && (*pDetails->pszParameters) // V0.9.18
1006 )
1007 xstrcpy(pstrParamsPatched,
1008 pDetails->pszParameters,
1009 0);
1010
1011 #ifdef DEBUG_PROGRAMSTART
1012 _Pmpf((__FUNCTION__ ": old progc: 0x%lX", pcProgDetails->progt.progc));
1013 _Pmpf((" pszTitle: %s", (pDetails->pszTitle) ? pDetails->pszTitle : NULL));
1014 _Pmpf((" pszIcon: %s", (pDetails->pszIcon) ? pDetails->pszIcon : NULL));
1015 #endif
1016
1017 // program type fixups
1018 switch (pDetails->progt.progc) // that's a ULONG
1019 {
1020 case ((ULONG)-1): // we get that sometimes...
1021 case PROG_DEFAULT:
1022 {
1023 // V0.9.12 (2001-05-26) [umoeller]
1024 ULONG ulDosAppType;
1025 appQueryAppType(pDetails->pszExecutable,
1026 &ulDosAppType,
1027 &pDetails->progt.progc);
1028 }
1029 break;
1030 }
1031
1032 // set session type from option flags
1033 if (ulFlags & APP_RUN_FULLSCREEN)
1034 {
1035 if (pDetails->progt.progc == PROG_WINDOWABLEVIO)
1036 pDetails->progt.progc = PROG_FULLSCREEN;
1037 else if (pDetails->progt.progc == PROG_WINDOWEDVDM)
1038 pDetails->progt.progc = PROG_VDM;
1039 }
1040
1041 if (ulIsWinApp = appIsWindowsApp(pDetails->progt.progc))
1042 {
1043 if (ulFlags & APP_RUN_FULLSCREEN)
1044 pDetails->progt.progc = (ulFlags & APP_RUN_ENHANCED)
1045 ? PROG_31_ENH
1046 : PROG_31_STD;
1047 else
1048 {
1049 if (ulFlags & APP_RUN_STANDARD)
1050 pDetails->progt.progc = (ulFlags & APP_RUN_SEPARATE)
1051 ? PROG_31_STDSEAMLESSVDM
1052 : PROG_31_STDSEAMLESSCOMMON;
1053 else if (ulFlags & APP_RUN_ENHANCED)
1054 pDetails->progt.progc = (ulFlags & APP_RUN_SEPARATE)
1055 ? PROG_31_ENHSEAMLESSVDM
1056 : PROG_31_ENHSEAMLESSCOMMON;
1057 }
1058
1059 // re-run V0.9.16 (2001-10-19) [umoeller]
1060 ulIsWinApp = appIsWindowsApp(pDetails->progt.progc);
1061 }
1062
1063 if (!arc)
1064 {
1065 /*
1066 * command lines fixups:
1067 *
1068 */
1069
1070 if (!strcmp(pDetails->pszExecutable, "*"))
1071 {
1072 /*
1073 * "*" for command sessions:
1074 *
1075 */
1076
1077 if (ulIsWinApp == 2)
1078 {
1079 // enhanced Win-OS/2 session:
1080 PSZ psz = NULL;
1081 if (pstrParamsPatched->ulLength)
1082 // "/3 " + existing params
1083 psz = strdup(pstrParamsPatched->psz);
1084
1085 xstrcpy(pstrParamsPatched, "/3 ", 0);
1086
1087 if (psz)
1088 {
1089 xstrcat(pstrParamsPatched, psz, 0);
1090 free(psz);
1091 }
1092 }
1093
1094 if (ulIsWinApp)
1095 {
1096 // cheat: WinStartApp doesn't support NULL
1097 // for Win-OS2 sessions, so manually start winos2.com
1098 pDetails->pszExecutable = "WINOS2.COM";
1099 // this is a DOS app, so fix this to DOS fullscreen
1100 pDetails->progt.progc = PROG_VDM;
1101 }
1102 else
1103 // for all other executable types
1104 // (including OS/2 and DOS sessions),
1105 // set pszExecutable to NULL; this will
1106 // have WinStartApp start a cmd shell
1107 pDetails->pszExecutable = NULL;
1108
1109 } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
1110 else
1111 {
1112 // check if the executable is fully qualified; if so,
1113 // check if the executable file exists
1114 if ( (pDetails->pszExecutable[1] == ':')
1115 && (strchr(pDetails->pszExecutable, '\\'))
1116 )
1117 {
1118 ULONG ulAttr;
1119 if (!(arc = doshQueryPathAttr(pDetails->pszExecutable,
1120 &ulAttr)))
1121 {
1122 // make sure startup dir is really a directory
1123 if (pDetails->pszStartupDir)
1124 {
1125 // it is valid to specify a startup dir of "C:"
1126 if ( (strlen(pDetails->pszStartupDir) > 2)
1127 && (!(arc = doshQueryPathAttr(pDetails->pszStartupDir,
1128 &ulAttr)))
1129 && (!(ulAttr & FILE_DIRECTORY))
1130 )
1131 arc = ERROR_PATH_NOT_FOUND;
1132 }
1133 }
1134 }
1135 else
1136 {
1137 // _not_ fully qualified: look it up on the PATH then
1138 // V0.9.16 (2001-12-06) [umoeller]
1139 CHAR szFQExecutable[CCHMAXPATH];
1140 if (!(arc = doshSearchPath("PATH",
1141 pDetails->pszExecutable,
1142 szFQExecutable,
1143 sizeof(szFQExecutable))))
1144 {
1145 // alright, found it:
1146 xstrcpy(pstrExecutablePatched, szFQExecutable, 0);
1147 pDetails->pszExecutable = pstrExecutablePatched->psz;
1148 }
1149 }
1150
1151 if (!arc)
1152 {
1153 PSZ pszExtension;
1154 switch (pDetails->progt.progc)
1155 {
1156 /*
1157 * .CMD files fixups
1158 *
1159 */
1160
1161 case PROG_FULLSCREEN: // OS/2 fullscreen
1162 case PROG_WINDOWABLEVIO: // OS/2 window
1163 {
1164 if ( (pszExtension = doshGetExtension(pDetails->pszExecutable))
1165 && (!stricmp(pszExtension, "CMD"))
1166 )
1167 {
1168 CallBatchCorrectly(pDetails,
1169 pstrParamsPatched,
1170 "OS2_SHELL",
1171 "CMD.EXE");
1172 }
1173 }
1174 break;
1175
1176 case PROG_VDM: // DOS fullscreen
1177 case PROG_WINDOWEDVDM: // DOS window
1178 {
1179 if ( (pszExtension = doshGetExtension(pDetails->pszExecutable))
1180 && (!stricmp(pszExtension, "BAT"))
1181 )
1182 {
1183 CallBatchCorrectly(pDetails,
1184 pstrParamsPatched,
1185 NULL,
1186 "COMMAND.COM");
1187 }
1188 }
1189 break;
1190 } // end switch (pDetails->progt.progc)
1191 }
1192 }
1193 }
1194
1195 if (!arc)
1196 {
1197 if ( (ulIsWinApp)
1198 && ( (pDetails->pszEnvironment == NULL)
1199 || (!strlen(pDetails->pszEnvironment))
1200 )
1201 )
1202 {
1203 // this is a windoze app, and caller didn't bother
1204 // to give us an environment:
1205 // we MUST set one then, or we'll get the strangest
1206 // errors, up to system hangs. V0.9.12 (2001-05-26) [umoeller]
1207
1208 DOSENVIRONMENT Env = {0};
1209
1210 // get standard WIN-OS/2 environment
1211 PSZ pszTemp = appQueryDefaultWin31Environment();
1212
1213 if (!(arc = appParseEnvironment(pszTemp,
1214 &Env)))
1215 {
1216 // now override KBD_CTRL_BYPASS=CTRL_ESC
1217 if ( (!(arc = appSetEnvironmentVar(&Env,
1218 "KBD_CTRL_BYPASS=CTRL_ESC",
1219 FALSE))) // add last
1220 && (!(arc = appConvertEnvironment(&Env,
1221 ppszWinOS2Env, // freed at bottom
1222 NULL)))
1223 )
1224 pDetails->pszEnvironment = *ppszWinOS2Env;
1225
1226 appFreeEnvironment(&Env);
1227 }
1228
1229 free(pszTemp);
1230 }
1231
1232 if (!arc)
1233 {
1234 if (!pDetails->pszTitle)
1235 pDetails->pszTitle = pDetails->pszExecutable;
1236
1237 // make sure params have a leading space
1238 // V0.9.18 (2002-03-27) [umoeller]
1239 if (pstrParamsPatched->ulLength)
1240 {
1241 if (pstrParamsPatched->psz[0] != ' ')
1242 {
1243 XSTRING str2;
1244 xstrInit(&str2, 0);
1245 xstrcpy(&str2, " ", 1);
1246 xstrcats(&str2, pstrParamsPatched);
1247 xstrcpys(pstrParamsPatched, &str2);
1248 xstrClear(&str2);
1249 // we really need xstrInsert or something
1250 }
1251 pDetails->pszParameters = pstrParamsPatched->psz;
1252 }
1253 else
1254 pDetails->pszParameters = "";
1255
1256 if (!pDetails->pszIcon)
1257 pDetails->pszIcon = "";
1258
1259 if (!pDetails->pszStartupDir)
1260 pDetails->pszStartupDir = "";
1261
1262 }
1263 }
1264 }
1265
1266 return (arc);
1267}
1268
1269/*
1270 *@@ CallDosStartSession:
1271 *
1272 *@@added V0.9.18 (2002-03-27) [umoeller]
1273 */
1274
1275static APIRET CallDosStartSession(HAPP *phapp,
1276 const PROGDETAILS *pNewProgDetails, // in: program spec (req.)
1277 ULONG cbFailingName,
1278 PSZ pszFailingName)
1279{
1280 APIRET arc = NO_ERROR;
1281
1282 BOOL fCrit = FALSE,
1283 fResetDir = FALSE;
1284 CHAR szCurrentDir[CCHMAXPATH];
1285
1286 ULONG sid,
1287 pid;
1288 STARTDATA SData;
1289 SData.Length = sizeof(STARTDATA);
1290 SData.Related = SSF_RELATED_INDEPENDENT; // SSF_RELATED_CHILD;
1291 // per default, try to start this in the foreground
1292 SData.FgBg = SSF_FGBG_FORE;
1293 SData.TraceOpt = SSF_TRACEOPT_NONE;
1294
1295 SData.PgmTitle = pNewProgDetails->pszTitle;
1296 SData.PgmName = pNewProgDetails->pszExecutable;
1297 SData.PgmInputs = pNewProgDetails->pszParameters;
1298
1299 SData.TermQ = NULL;
1300 SData.Environment = pNewProgDetails->pszEnvironment;
1301 SData.InheritOpt = SSF_INHERTOPT_PARENT; // ignored
1302
1303 switch (pNewProgDetails->progt.progc)
1304 {
1305 case PROG_FULLSCREEN:
1306 SData.SessionType = SSF_TYPE_FULLSCREEN;
1307 break;
1308
1309 case PROG_WINDOWABLEVIO:
1310 SData.SessionType = SSF_TYPE_WINDOWABLEVIO;
1311 break;
1312
1313 case PROG_PM:
1314 SData.SessionType = SSF_TYPE_PM;
1315 SData.FgBg = SSF_FGBG_BACK; // otherwise we get ERROR_SMG_START_IN_BACKGROUND
1316 break;
1317
1318 case PROG_VDM:
1319 SData.SessionType = SSF_TYPE_VDM;
1320 break;
1321
1322 case PROG_WINDOWEDVDM:
1323 SData.SessionType = SSF_TYPE_WINDOWEDVDM;
1324 break;
1325
1326 default:
1327 SData.SessionType = SSF_TYPE_DEFAULT;
1328 }
1329
1330 SData.IconFile = 0;
1331 SData.PgmHandle = 0;
1332
1333 SData.PgmControl = 0;
1334
1335 if (pNewProgDetails->progt.fbVisible == SHE_VISIBLE)
1336 SData.PgmControl |= SSF_CONTROL_VISIBLE;
1337
1338 if (pNewProgDetails->swpInitial.fl & SWP_HIDE)
1339 SData.PgmControl |= SSF_CONTROL_INVISIBLE;
1340
1341 if (pNewProgDetails->swpInitial.fl & SWP_MAXIMIZE)
1342 SData.PgmControl |= SSF_CONTROL_MAXIMIZE;
1343 if (pNewProgDetails->swpInitial.fl & SWP_MINIMIZE)
1344 {
1345 SData.PgmControl |= SSF_CONTROL_MINIMIZE;
1346 // use background then
1347 SData.FgBg = SSF_FGBG_BACK;
1348 }
1349 if (pNewProgDetails->swpInitial.fl & SWP_MOVE)
1350 SData.PgmControl |= SSF_CONTROL_SETPOS;
1351 if (pNewProgDetails->swpInitial.fl & SWP_NOAUTOCLOSE)
1352 SData.PgmControl |= SSF_CONTROL_NOAUTOCLOSE;
1353
1354 SData.InitXPos = pNewProgDetails->swpInitial.x;
1355 SData.InitYPos = pNewProgDetails->swpInitial.y;
1356 SData.InitXSize = pNewProgDetails->swpInitial.cx;
1357 SData.InitYSize = pNewProgDetails->swpInitial.cy;
1358
1359 SData.Reserved = 0;
1360 SData.ObjectBuffer = pszFailingName;
1361 SData.ObjectBuffLen = cbFailingName;
1362
1363 // now, if a required module cannot be found,
1364 // DosStartSession still returns ERROR_FILE_NOT_FOUND
1365 // (2), but pszFailingName will be set to something
1366 // meaningful... so set it to a null string first
1367 // and we can then check if it has changed
1368 if (pszFailingName)
1369 *pszFailingName = '\0';
1370
1371 TRY_QUIET(excpt1)
1372 {
1373 if ( (pNewProgDetails->pszStartupDir)
1374 && (pNewProgDetails->pszStartupDir[0])
1375 )
1376 {
1377 fCrit = !DosEnterCritSec();
1378 if ( (!(arc = doshQueryCurrentDir(szCurrentDir)))
1379 && (!(arc = doshSetCurrentDir(pNewProgDetails->pszStartupDir)))
1380 )
1381 fResetDir = TRUE;
1382 }
1383
1384 if ( (!arc)
1385 && (!(arc = DosStartSession(&SData, &sid, &pid)))
1386 )
1387 {
1388 // app started:
1389 // compose HAPP from that
1390 *phapp = sid;
1391 }
1392 else if (pszFailingName && *pszFailingName)
1393 // DosStartSession has set this to something
1394 // other than NULL: then use error code 1804,
1395 // as cmd.exe does
1396 arc = 1804;
1397 }
1398 CATCH(excpt1)
1399 {
1400 arc = ERROR_PROTECTION_VIOLATION;
1401 } END_CATCH();
1402
1403 if (fResetDir)
1404 doshSetCurrentDir(szCurrentDir);
1405
1406 if (fCrit)
1407 DosExitCritSec();
1408
1409 #ifdef DEBUG_PROGRAMSTART
1410 _Pmpf((" DosStartSession returned %d, pszFailingName: \"%s\"",
1411 arc, pszFailingName));
1412 #endif
1413
1414 return (arc);
1415}
1416
1417/*
1418 *@@ CallWinStartApp:
1419 * wrapper around WinStartApp which copies all the
1420 * parameters into a contiguous block of tiled memory.
1421 *
1422 * This might fix some of the problems with truncated
1423 * environments we were having because apparently the
1424 * WinStartApp thunking to 16-bit doesn't always work.
1425 *
1426 *@@added V0.9.18 (2002-02-13) [umoeller]
1427 *@@changed V0.9.18 (2002-03-27) [umoeller]: made failing modules work
1428 */
1429
1430static APIRET CallWinStartApp(HAPP *phapp, // out: application handle if NO_ERROR is returned
1431 HWND hwndNotify, // in: notify window or NULLHANDLE
1432 const PROGDETAILS *pcProgDetails, // in: program spec (req.)
1433 ULONG cbFailingName,
1434 PSZ pszFailingName)
1435{
1436 ULONG cb,
1437 cbTitle,
1438 cbExecutable,
1439 cbParameters,
1440 cbStartupDir,
1441 cbIcon,
1442 cbEnvironment;
1443
1444 APIRET arc = NO_ERROR;
1445
1446 /*
1447 if (WinMessageBox(HWND_DESKTOP,
1448 NULLHANDLE,
1449 (ProgDetails.pszExecutable) ? ProgDetails.pszExecutable : "NULL",
1450 "Start?",
1451 0,
1452 MB_YESNO | MB_MOVEABLE)
1453 != MBID_YES)
1454 return (ERROR_INTERRUPT);
1455 */
1456
1457 // allocate a chunk of tiled memory from OS/2 to make sure
1458 // this is aligned on a 64K memory (backed up by a 16-bit
1459 // LDT selector)
1460 cb = sizeof(PROGDETAILS);
1461 if (cbTitle = strhSize(pcProgDetails->pszTitle))
1462 cb += cbTitle;
1463 else
1464 return (ERROR_INVALID_PARAMETER);
1465 _Pmpf((__FUNCTION__ ": cbTitle is %d", cbTitle));
1466
1467 if (cbExecutable = strhSize(pcProgDetails->pszExecutable))
1468 cb += cbExecutable;
1469 else
1470 return (ERROR_INVALID_PARAMETER);
1471 _Pmpf((" cbExecutable is %d", cbExecutable));
1472
1473 if (cbParameters = strhSize(pcProgDetails->pszParameters))
1474 cb += cbParameters;
1475 else
1476 return (ERROR_INVALID_PARAMETER);
1477 _Pmpf((" cbParameters is %d", cbExecutable));
1478
1479 if (cbStartupDir = strhSize(pcProgDetails->pszStartupDir))
1480 cb += cbStartupDir;
1481 else
1482 return (ERROR_INVALID_PARAMETER);
1483 _Pmpf((" cbStartupDir is %d", cbStartupDir));
1484
1485 if (cbIcon = strhSize(pcProgDetails->pszIcon))
1486 cb += cbIcon;
1487 else
1488 return (ERROR_INVALID_PARAMETER);
1489 _Pmpf((" cbIcon is %d", cbIcon));
1490
1491 if (cbEnvironment = appQueryEnvironmentLen(pcProgDetails->pszEnvironment))
1492 cb += cbEnvironment;
1493 _Pmpf((" cbEnvironment is %d", cbEnvironment));
1494
1495 _Pmpf((" cbTotal is %d", cb));
1496
1497 if (cb > 60000) // to be on the safe side
1498 arc = ERROR_BAD_ENVIRONMENT; // 10;
1499 else
1500 {
1501 PPROGDETAILS pNewProgDetails;
1502 if (!(arc = DosAllocMem((PVOID*)&pNewProgDetails,
1503 cb,
1504 PAG_COMMIT | OBJ_TILE | PAG_READ | PAG_WRITE)))
1505 {
1506 // alright, copy stuff
1507 memset(pNewProgDetails, 0, sizeof(PROGDETAILS));
1508
1509 pNewProgDetails->Length = sizeof(PROGDETAILS);
1510
1511 if (!(pNewProgDetails->progt.progc = pcProgDetails->progt.progc))
1512 arc = ERROR_BAD_FORMAT;
1513 // this should never happen because we check
1514 // for this in appStartApp
1515 else
1516 {
1517 PBYTE pThis;
1518
1519 pNewProgDetails->progt.fbVisible = pcProgDetails->progt.fbVisible;
1520 memcpy(&pNewProgDetails->swpInitial, &pcProgDetails->swpInitial, sizeof(SWP));
1521
1522 // start copying into buffer right after PROGDETAILS
1523 pThis = (PBYTE)(pNewProgDetails + 1);
1524
1525 #define COPY(id) if (cb ## id) { \
1526 memcpy(pThis, pcProgDetails->psz ## id, cb ## id); \
1527 pNewProgDetails->psz ## id = pThis; \
1528 pThis += cb ## id; }
1529
1530 COPY(Title);
1531 COPY(Executable);
1532 COPY(Parameters);
1533 COPY(StartupDir);
1534 COPY(Icon);
1535 COPY(Environment);
1536
1537 #ifdef DEBUG_PROGRAMSTART
1538 _Pmpf((__FUNCTION__ ": progt.progc: %d", pNewProgDetails->progt.progc));
1539 _Pmpf((" progt.fbVisible: 0x%lX", pNewProgDetails->progt.fbVisible));
1540 _Pmpf((" progt.pszTitle: \"%s\"", (pNewProgDetails->pszTitle) ? pNewProgDetails->pszTitle : "NULL"));
1541 _Pmpf((" exec: \"%s\"", (pNewProgDetails->pszExecutable) ? pNewProgDetails->pszExecutable : "NULL"));
1542 _Pmpf((" params: \"%s\"", (pNewProgDetails->pszParameters) ? pNewProgDetails->pszParameters : "NULL"));
1543 _Pmpf((" startup: \"%s\"", (pNewProgDetails->pszStartupDir) ? pNewProgDetails->pszStartupDir : "NULL"));
1544 _Pmpf((" pszIcon: \"%s\"", (pNewProgDetails->pszIcon) ? pNewProgDetails->pszIcon : "NULL"));
1545 _Pmpf((" environment: "));
1546 {
1547 PSZ pszThis = pNewProgDetails->pszEnvironment;
1548 while (pszThis && *pszThis)
1549 {
1550 _Pmpf((" \"%s\"", pszThis));
1551 pszThis += strlen(pszThis) + 1;
1552 }
1553 }
1554
1555 _Pmpf((" swpInitial.fl = 0x%lX, x = %d, y = %d, cx = %d, cy = %d:",
1556 pNewProgDetails->swpInitial.fl,
1557 pNewProgDetails->swpInitial.x,
1558 pNewProgDetails->swpInitial.y,
1559 pNewProgDetails->swpInitial.cx,
1560 pNewProgDetails->swpInitial.cy));
1561 _Pmpf((" behind = %d, hwnd = %d, res1 = %d, res2 = %d",
1562 pNewProgDetails->swpInitial.hwndInsertBehind,
1563 pNewProgDetails->swpInitial.hwnd,
1564 pNewProgDetails->swpInitial.ulReserved1,
1565 pNewProgDetails->swpInitial.ulReserved2));
1566
1567 /* DumpMemoryBlock((PBYTE)pNewProgDetails,
1568 cb,
1569 8); */
1570
1571 #endif
1572
1573 /* if (!(appIsWindowsApp(pNewProgDetails->progt.progc)))
1574 arc = CallDosStartSession(phapp,
1575 pNewProgDetails,
1576 cbFailingName,
1577 pszFailingName);
1578 else */
1579 // windoze app: use WinStartApp
1580
1581 if (!(*phapp = WinStartApp(hwndNotify,
1582 // receives WM_APPTERMINATENOTIFY
1583 pNewProgDetails,
1584 pNewProgDetails->pszParameters,
1585 NULL, // "reserved", PMREF says...
1586 SAF_INSTALLEDCMDLINE)))
1587 // we MUST use SAF_INSTALLEDCMDLINE
1588 // or no Win-OS/2 session will start...
1589 // whatever is going on here... Warp 4 FP11
1590
1591 // do not use SAF_STARTCHILDAPP, or the
1592 // app will be terminated automatically
1593 // when the WPS terminates!
1594 {
1595 // cannot start app:
1596 PERRINFO pei;
1597
1598 #ifdef DEBUG_PROGRAMSTART
1599 _Pmpf((__FUNCTION__ ": WinStartApp failed"));
1600 #endif
1601
1602 // unfortunately WinStartApp doesn't
1603 // return meaningful codes like DosStartSession, so
1604 // try to see what happened
1605
1606 if (pei = WinGetErrorInfo(0))
1607 {
1608 #ifdef DEBUG_PROGRAMSTART
1609 _Pmpf((" WinGetErrorInfo returned 0x%lX, errorid 0x%lX, %d",
1610 pei,
1611 pei->idError,
1612 ERRORIDERROR(pei->idError)));
1613 #endif
1614
1615 switch (ERRORIDERROR(pei->idError))
1616 {
1617 case PMERR_DOS_ERROR: // (0x1200)
1618 {
1619 /*
1620 PUSHORT pausMsgOfs = (PUSHORT)(((PBYTE)pei) + pei->offaoffszMsg);
1621 PULONG pulData = (PULONG)(((PBYTE)pei) + pei->offBinaryData);
1622 PSZ pszMsg = (PSZ)(((PBYTE)pei) + *pausMsgOfs);
1623
1624 CHAR szMsg[1000];
1625 sprintf(szMsg, "cDetail: %d\nmsg: %s\n*pul: %d",
1626 pei->cDetailLevel,
1627 pszMsg,
1628 *(pulData - 1));
1629
1630 WinMessageBox(HWND_DESKTOP,
1631 NULLHANDLE,
1632 szMsg,
1633 "Error",
1634 0,
1635 MB_OK | MB_MOVEABLE);
1636
1637 // Very helpful. The message is "UNK 1200 E",
1638 // where I assume "UNK" means "unknown", which is
1639 // exactly what I was trying to find out. Oh my.
1640 // And cDetailLevel is always 1, which isn't terribly
1641 // helpful either. V0.9.18 (2002-03-27) [umoeller]
1642 // WHO THE &%õ$ CREATED THESE APIS?
1643
1644 */
1645
1646 // this is probably the case where the module
1647 // couldn't be loaded, so try DosStartSession
1648 // to get a meaningful return code... note that
1649 // this cannot handle hwndNotify then
1650 arc = CallDosStartSession(phapp,
1651 pNewProgDetails,
1652 cbFailingName,
1653 pszFailingName);
1654 }
1655 break;
1656
1657 case PMERR_INVALID_APPL: // (0x1530)
1658 // Attempted to start an application whose type is not
1659 // recognized by OS/2.
1660 // This we get also if the executable doesn't exist...
1661 // V0.9.18 (2002-03-27) [umoeller]
1662 // arc = ERROR_INVALID_EXE_SIGNATURE;
1663 arc = ERROR_FILE_NOT_FOUND;
1664 break;
1665
1666 case PMERR_INVALID_PARAMETERS: // (0x1208)
1667 // An application parameter value is invalid for
1668 // its converted PM type. For example: a 4-byte
1669 // value outside the range -32 768 to +32 767 cannot be
1670 // converted to a SHORT, and a negative number cannot
1671 // be converted to a ULONG or USHORT.
1672 arc = ERROR_INVALID_DATA;
1673 break;
1674
1675 case PMERR_STARTED_IN_BACKGROUND: // (0x1532)
1676 // The application started a new session in the
1677 // background.
1678 arc = ERROR_SMG_START_IN_BACKGROUND;
1679 break;
1680
1681 case PMERR_INVALID_WINDOW: // (0x1206)
1682 // The window specified with a Window List call
1683 // is not a valid frame window.
1684
1685 default:
1686 arc = ERROR_BAD_FORMAT;
1687 break;
1688 }
1689
1690 WinFreeErrorInfo(pei);
1691 }
1692 }
1693 }
1694
1695 DosFreeMem(pNewProgDetails);
1696 }
1697 }
1698
1699 return (arc);
1700}
1701
1702/*
1703 *@@ appStartApp:
1704 * wrapper around WinStartApp which fixes the
1705 * specified PROGDETAILS to (hopefully) work
1706 * work with all executable types.
1707 *
1708 * This fixes the executable info to support:
1709 *
1710 * -- starting "*" executables (command prompts
1711 * for OS/2, DOS, Win-OS/2);
1712 *
1713 * -- starting ".CMD" and ".BAT" files as
1714 * PROGDETAILS.pszExecutable;
1715 *
1716 * -- starting apps which are not fully qualified
1717 * and therefore assumed to be on the PATH.
1718 *
1719 * Unless it is "*", PROGDETAILS.pszExecutable must
1720 * be a proper file name. The full path may be omitted
1721 * if it is on the PATH, but the extension (.EXE etc.)
1722 * must be given. You can use doshFindExecutable to
1723 * find executables if you don't know the extension.
1724 *
1725 * This also handles and merges special and default
1726 * environments for the app to be started. The
1727 * following should be respected:
1728 *
1729 * -- As with WinStartApp, if PROGDETAILS.pszEnvironment
1730 * is NULL, the new app inherits a default environment
1731 * from the shell.
1732 *
1733 * -- However, if you specify an environment, you _must_
1734 * specify a complete environment. This function
1735 * will not merge environments. Use
1736 * appSetEnvironmentVar to change environment
1737 * variables in a complete environment set.
1738 *
1739 * -- If PROGDETAILS specifies a Win-OS/2 session
1740 * and PROGDETAILS.pszEnvironment is empty,
1741 * this uses the default Win-OS/2 environment.
1742 * See appQueryDefaultWin31Environment.
1743 *
1744 * Even though this isn't clearly said in PMREF,
1745 * PROGDETAILS.swpInitial is important:
1746 *
1747 * -- To start a session minimized, set SWP_MINIMIZE.
1748 *
1749 * -- To start a VIO session with auto-close disabled,
1750 * set the half-documented SWP_NOAUTOCLOSE flag (0x8000)
1751 * This flag is now in the newer toolkit headers.
1752 *
1753 * In addition, this supports the following session
1754 * flags with ulFlags if PROG_DEFAULT is specified:
1755 *
1756 * -- APP_RUN_FULLSCREEN
1757 *
1758 * -- APP_RUN_ENHANCED
1759 *
1760 * -- APP_RUN_STANDARD
1761 *
1762 * -- APP_RUN_SEPARATE
1763 *
1764 * Since this calls WinStartApp in turn, this
1765 * requires a message queue on the calling thread.
1766 *
1767 * Note that this also does minimal checking on
1768 * the specified parameters so it can return something
1769 * more meaningful than FALSE like WinStartApp.
1770 * As a result, you get a DOS error code now (V0.9.16).
1771 *
1772 * Most importantly:
1773 *
1774 * -- ERROR_INVALID_THREADID: not running on thread 1.
1775 * See remarks below.
1776 *
1777 * -- ERROR_INVALID_PARAMETER: pcProgDetails or
1778 * phapp is NULL; or PROGDETAILS.pszExecutable is NULL.
1779 *
1780 * -- ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND:
1781 * PROGDETAILS.pszExecutable and/or PROGDETAILS.pszStartupDir
1782 * are invalid.
1783 * A NULL PROGDETAILS.pszStartupDir is supported though.
1784 *
1785 * -- ERROR_NOT_ENOUGH_MEMORY
1786 *
1787 * <B>About enforcing thread 1</B>
1788 *
1789 * OK, after long, long debugging hours, I have found
1790 * that WinStartApp hangs the system in the following
1791 * cases hard:
1792 *
1793 * -- If a Win-OS/2 session is started and WinStartApp
1794 * is _not_ on thread 1. For this reason, we check
1795 * if the caller is trying to start a Win-OS/2
1796 * session and return ERROR_INVALID_THREADID if
1797 * this is not running on thread 1.
1798 *
1799 * -- By contrast, WinStartApp hangs the system with
1800 * VIO sessions if we do the XWorkplace tricky of
1801 * redirecting WPProgram::wpOpen to thread 1
1802 * via WinPostMsg to the kernel thread-1 object
1803 * window. This also happens with DosStartSession,
1804 * so I suspect the bug is somewhere deeper in
1805 * SESMGR or wherever. So we no longer return
1806 * ERROR_INVALID_THREADID for non-Win-OS/2 sessions
1807 * (V0.9.18).
1808 *
1809 *@@added V0.9.6 (2000-10-16) [umoeller]
1810 *@@changed V0.9.7 (2000-12-10) [umoeller]: PROGDETAILS.swpInitial no longer zeroed... this broke VIOs
1811 *@@changed V0.9.7 (2000-12-17) [umoeller]: PROGDETAILS.pszEnvironment no longer zeroed
1812 *@@changed V0.9.9 (2001-01-27) [umoeller]: crashed if PROGDETAILS.pszExecutable was NULL
1813 *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed PROG_DEFAULT
1814 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
1815 *@@changed V0.9.14 (2001-08-07) [pr]: removed some env. strings for Win. apps.
1816 *@@changed V0.9.14 (2001-08-23) [pr]: added session type options
1817 *@@changed V0.9.16 (2001-10-19) [umoeller]: added prototype to return APIRET
1818 *@@changed V0.9.16 (2001-10-19) [umoeller]: added thread-1 check
1819 *@@changed V0.9.16 (2001-12-06) [umoeller]: now using doshSearchPath for finding pszExecutable if not qualified
1820 *@@changed V0.9.16 (2002-01-04) [umoeller]: removed error report if startup directory was drive letter only
1821 *@@changed V0.9.16 (2002-01-04) [umoeller]: added more detailed error reports and *FailingName params
1822 *@@changed V0.9.18 (2002-02-13) [umoeller]: added CallWinStartApp to fix possible memory problems
1823 *@@changed V0.9.18 (2002-03-27) [umoeller]: no longer returning ERROR_INVALID_THREADID, except for Win-OS/2 sessions
1824 *@@changed V0.9.18 (2002-03-27) [umoeller]: extracted appFixProgDetails
1825 */
1826
1827APIRET appStartApp(HWND hwndNotify, // in: notify window or NULLHANDLE
1828 const PROGDETAILS *pcProgDetails, // in: program spec (req.)
1829 ULONG ulFlags, // in: APP_RUN_* flags or 0
1830 HAPP *phapp, // out: application handle if NO_ERROR is returned
1831 ULONG cbFailingName,
1832 PSZ pszFailingName)
1833{
1834 APIRET arc;
1835 PROGDETAILS ProgDetails;
1836 XSTRING strExecutablePatched,
1837 strParamsPatched;
1838 PSZ pszWinOS2Env = 0;
1839
1840 if (pszFailingName)
1841 *pszFailingName = '\0';
1842
1843 if (!pcProgDetails || !phapp)
1844 return (ERROR_INVALID_PARAMETER);
1845
1846 xstrInit(&strExecutablePatched, 0);
1847 xstrInit(&strParamsPatched, 0);
1848
1849 if (!(arc = appFixProgDetails(&ProgDetails,
1850 pcProgDetails,
1851 ulFlags,
1852 &strExecutablePatched,
1853 &strParamsPatched,
1854 &pszWinOS2Env)))
1855 {
1856 if (pszFailingName)
1857 strhncpy0(pszFailingName, ProgDetails.pszExecutable, cbFailingName);
1858
1859 if ( (appIsWindowsApp(ProgDetails.progt.progc))
1860 && (doshMyTID() != 1) // V0.9.16 (2001-10-19) [umoeller]
1861 )
1862 arc = ERROR_INVALID_THREADID;
1863 else
1864 arc = CallWinStartApp(phapp,
1865 hwndNotify,
1866 &ProgDetails,
1867 cbFailingName,
1868 pszFailingName);
1869 } // end if (ProgDetails.pszExecutable)
1870
1871 xstrClear(&strParamsPatched);
1872 xstrClear(&strExecutablePatched);
1873
1874 if (pszWinOS2Env)
1875 free(pszWinOS2Env);
1876
1877 #ifdef DEBUG_PROGRAMSTART
1878 _Pmpf((__FUNCTION__ ": returning %d", arc));
1879 #endif
1880
1881 return (arc);
1882}
1883
1884/*
1885 *@@ appWaitForApp:
1886 * waits for the specified application to terminate
1887 * and returns its exit code.
1888 *
1889 *@@added V0.9.9 (2001-03-07) [umoeller]
1890 */
1891
1892BOOL appWaitForApp(HWND hwndNotify, // in: notify window
1893 HAPP happ, // in: app to wait for
1894 PULONG pulExitCode) // out: exit code (ptr can be NULL)
1895{
1896 BOOL brc = FALSE;
1897
1898 if (happ)
1899 {
1900 // app started:
1901 // enter a modal message loop until we get the
1902 // WM_APPTERMINATENOTIFY for happ. Then we
1903 // know the app is done.
1904 HAB hab = WinQueryAnchorBlock(hwndNotify);
1905 QMSG qmsg;
1906 // ULONG ulXFixReturnCode = 0;
1907 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
1908 {
1909 if ( (qmsg.msg == WM_APPTERMINATENOTIFY)
1910 && (qmsg.hwnd == hwndNotify)
1911 && (qmsg.mp1 == (MPARAM)happ)
1912 )
1913 {
1914 // xfix has terminated:
1915 // get xfix return code from mp2... this is:
1916 // -- 0: everything's OK, continue.
1917 // -- 1: handle section was rewritten, restart Desktop
1918 // now.
1919 if (pulExitCode)
1920 *pulExitCode = (ULONG)qmsg.mp2;
1921 brc = TRUE;
1922 // do not dispatch this
1923 break;
1924 }
1925
1926 WinDispatchMsg(hab, &qmsg);
1927 }
1928 }
1929
1930 return (brc);
1931}
1932
1933/*
1934 *@@ appQuickStartApp:
1935 * shortcut for simply starting an app and
1936 * waiting until it's finished.
1937 *
1938 * On errors, NULLHANDLE is returned.
1939 *
1940 * If pulReturnCode != NULL, it receives the
1941 * return code of the app.
1942 *
1943 *@@added V0.9.16 (2001-10-19) [umoeller]
1944 */
1945
1946HAPP appQuickStartApp(const char *pcszFile,
1947 ULONG ulProgType, // e.g. PROG_PM
1948 const char *pcszArgs,
1949 PULONG pulExitCode)
1950{
1951 PROGDETAILS pd = {0};
1952 HAPP happ,
1953 happReturn = NULLHANDLE;
1954 CHAR szDir[CCHMAXPATH] = "";
1955 PCSZ p;
1956 HWND hwndObject;
1957
1958 pd.Length = sizeof(pd);
1959 pd.progt.progc = ulProgType;
1960 pd.progt.fbVisible = SHE_VISIBLE;
1961 pd.pszExecutable = (PSZ)pcszFile;
1962 pd.pszParameters = (PSZ)pcszArgs;
1963 if (p = strrchr(pcszFile, '\\'))
1964 {
1965 strhncpy0(szDir,
1966 pcszFile,
1967 p - pcszFile);
1968 pd.pszStartupDir = szDir;
1969 }
1970
1971 if ( (hwndObject = winhCreateObjectWindow(WC_STATIC, NULL))
1972 && (!appStartApp(hwndObject,
1973 &pd,
1974 0,
1975 &happ,
1976 0,
1977 NULL))
1978 )
1979 {
1980 if (appWaitForApp(hwndObject,
1981 happ,
1982 pulExitCode))
1983 happReturn = happ;
1984 }
1985
1986 return (happReturn);
1987}
Note: See TracBrowser for help on using the repository browser.