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

Last change on this file since 121 was 113, checked in by umoeller, 24 years ago

Misc updates.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 44.2 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_DOSSESMGR
36#define INCL_DOSERRORS
37
38#define INCL_WINPROGRAMLIST // needed for PROGDETAILS, wppgm.h
39#include <os2.h>
40
41#include <stdio.h>
42
43#include "setup.h" // code generation and debugging options
44
45#include "helpers\apps.h"
46#include "helpers\dosh.h"
47#include "helpers\prfh.h"
48#include "helpers\stringh.h"
49#include "helpers\winh.h"
50#include "helpers\xstring.h"
51
52/*
53 *@@category: Helpers\PM helpers\Application helpers
54 */
55
56/* ******************************************************************
57 *
58 * Environment helpers
59 *
60 ********************************************************************/
61
62/*
63 *@@ appParseEnvironment:
64 * this takes one of those ugly environment strings
65 * as used by DosStartSession and WinStartApp (with
66 * lots of zero-terminated strings one after another
67 * and a duplicate zero byte as a terminator) as
68 * input and splits it into an array of separate
69 * strings in pEnv.
70 *
71 * The newly allocated strings are stored in in
72 * pEnv->papszVars. The array count is stored in
73 * pEnv->cVars.
74 *
75 * Each environment variable will be copied into
76 * one newly allocated string in the array. Use
77 * appFreeEnvironment to free the memory allocated
78 * by this function.
79 *
80 * Use the following code to browse thru the array:
81 +
82 + DOSENVIRONMENT Env = {0};
83 + if (appParseEnvironment(pszEnv,
84 + &Env)
85 + == NO_ERROR)
86 + {
87 + if (Env.papszVars)
88 + {
89 + PSZ *ppszThis = Env.papszVars;
90 + for (ul = 0;
91 + ul < Env.cVars;
92 + ul++)
93 + {
94 + PSZ pszThis = *ppszThis;
95 + // pszThis now has something like PATH=C:\TEMP
96 + // ...
97 + // next environment string
98 + ppszThis++;
99 + }
100 + }
101 + appFreeEnvironment(&Env);
102 + }
103 *
104 *@@added V0.9.4 (2000-08-02) [umoeller]
105 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
106 */
107
108APIRET appParseEnvironment(const char *pcszEnv,
109 PDOSENVIRONMENT pEnv)
110{
111 APIRET arc = NO_ERROR;
112 if (!pcszEnv)
113 arc = ERROR_INVALID_PARAMETER;
114 else
115 {
116 PSZ pszVarThis = (PSZ)pcszEnv;
117 ULONG cVars = 0;
118 // count strings
119 while (*pszVarThis)
120 {
121 cVars++;
122 pszVarThis += strlen(pszVarThis) + 1;
123 }
124
125 pEnv->cVars = cVars;
126 pEnv->papszVars = 0;
127
128 if (cVars)
129 {
130 PSZ *papsz = (PSZ*)malloc(sizeof(PSZ) * cVars);
131 if (!papsz)
132 arc = ERROR_NOT_ENOUGH_MEMORY;
133 else
134 {
135 PSZ *ppszTarget = papsz;
136 memset(papsz, 0, sizeof(PSZ) * cVars);
137 pszVarThis = (PSZ)pcszEnv;
138 while (*pszVarThis)
139 {
140 *ppszTarget = strdup(pszVarThis);
141 ppszTarget++;
142 pszVarThis += strlen(pszVarThis) + 1;
143 }
144
145 pEnv->papszVars = papsz;
146 }
147 }
148 }
149
150 return (arc);
151}
152
153/*
154 *@@ appGetEnvironment:
155 * calls appParseEnvironment for the current
156 * process environment, which is retrieved from
157 * the info blocks.
158 *
159 *@@added V0.9.4 (2000-07-19) [umoeller]
160 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
161 */
162
163APIRET appGetEnvironment(PDOSENVIRONMENT pEnv)
164{
165 APIRET arc = NO_ERROR;
166 if (!pEnv)
167 arc = ERROR_INVALID_PARAMETER;
168 else
169 {
170 PTIB ptib = 0;
171 PPIB ppib = 0;
172 arc = DosGetInfoBlocks(&ptib, &ppib);
173 if (arc == NO_ERROR)
174 {
175 PSZ pszEnv = ppib->pib_pchenv;
176 if (pszEnv)
177 arc = appParseEnvironment(pszEnv, pEnv);
178 else
179 arc = ERROR_BAD_ENVIRONMENT;
180 }
181 }
182
183 return (arc);
184}
185
186/*
187 *@@ appFindEnvironmentVar:
188 * returns the PSZ* in the pEnv->papszVars array
189 * which specifies the environment variable in pszVarName.
190 *
191 * With pszVarName, you can either specify the variable
192 * name only ("VARNAME") or a full environment string
193 * ("VARNAME=BLAH"). In any case, only the variable name
194 * is compared.
195 *
196 * Returns NULL if no such variable name was found in
197 * the array.
198 *
199 *@@added V0.9.4 (2000-07-19) [umoeller]
200 *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
201 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
202 */
203
204PSZ* appFindEnvironmentVar(PDOSENVIRONMENT pEnv,
205 PSZ pszVarName)
206{
207 PSZ *ppszRet = 0;
208 if (pEnv)
209 {
210 if ((pEnv->papszVars) && (pszVarName))
211 {
212 PSZ *ppszThis = pEnv->papszVars;
213 // PSZ pszThis;
214 ULONG ul = 0;
215 ULONG ulVarNameLen = 0;
216
217 PSZ pszSearch = NULL; // receives "VARNAME="
218 PSZ pFirstEqual = strchr(pszVarName, '=');
219 if (pFirstEqual)
220 pszSearch = strhSubstr(pszVarName, pFirstEqual + 1);
221 else
222 {
223 ulVarNameLen = strlen(pszVarName);
224 pszSearch = (PSZ)malloc(ulVarNameLen + 2);
225 memcpy(pszSearch, pszVarName, ulVarNameLen);
226 *(pszSearch + ulVarNameLen) = '=';
227 *(pszSearch + ulVarNameLen + 1) = 0;
228 }
229
230 ulVarNameLen = strlen(pszSearch);
231
232 for (ul = 0;
233 ul < pEnv->cVars;
234 ul++)
235 {
236 if (strnicmp(*ppszThis, pszSearch, ulVarNameLen) == 0)
237 {
238 ppszRet = ppszThis;
239 break;
240 }
241
242 // next environment string
243 ppszThis++;
244 }
245
246 free(pszSearch); // was missing V0.9.12 (2001-05-21) [umoeller]
247 }
248 }
249
250 return (ppszRet);
251}
252
253/*
254 *@@ appSetEnvironmentVar:
255 * sets an environment variable in the specified
256 * environment, which must have been initialized
257 * using appGetEnvironment first.
258 *
259 * pszNewEnv must be a full environment string
260 * in the form "VARNAME=VALUE".
261 *
262 * If "VARNAME" has already been set to something
263 * in the string array in pEnv, that array item
264 * is replaced.
265 *
266 * OTOH, if "VARNAME" has not been set yet, a new
267 * item is added to the array, and pEnv->cVars is
268 * raised by one. In that case, fAddFirst determines
269 * whether the new array item is added to the front
270 * or the tail of the environment list.
271 *
272 *@@added V0.9.4 (2000-07-19) [umoeller]
273 *@@changed V0.9.7 (2000-12-17) [umoeller]: added fAddFirst
274 *@@changed V0.9.12 (2001-05-21) [umoeller]: fixed memory leak
275 *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed crash if !fAddFirst
276 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
277 */
278
279APIRET appSetEnvironmentVar(PDOSENVIRONMENT pEnv,
280 PSZ pszNewEnv,
281 BOOL fAddFirst)
282{
283 APIRET arc = NO_ERROR;
284 if ((!pEnv) || (!pszNewEnv))
285 arc = ERROR_INVALID_PARAMETER;
286 else
287 {
288 if (!pEnv->papszVars)
289 {
290 // no variables set yet:
291 pEnv->papszVars = (PSZ*)malloc(sizeof(PSZ));
292 pEnv->cVars = 1;
293
294 *(pEnv->papszVars) = strdup(pszNewEnv);
295 }
296 else
297 {
298 PSZ *ppszEnvLine = appFindEnvironmentVar(pEnv, pszNewEnv);
299 if (ppszEnvLine)
300 {
301 // was set already: replace
302 free(*ppszEnvLine);
303 *ppszEnvLine = strdup(pszNewEnv);
304 if (!(*ppszEnvLine))
305 arc = ERROR_NOT_ENOUGH_MEMORY;
306 }
307 else
308 {
309 // not set already:
310 PSZ *ppszNew = NULL;
311
312 // allocate new array, with one new entry
313 // fixed V0.9.12 (2001-05-26) [umoeller], this crashed
314 PSZ *papszNew = (PSZ*)malloc(sizeof(PSZ) * (pEnv->cVars + 1));
315
316 if (!papszNew)
317 arc = ERROR_NOT_ENOUGH_MEMORY;
318 else
319 {
320 if (fAddFirst)
321 {
322 // add as first entry:
323 // overwrite first entry
324 ppszNew = papszNew;
325 // copy old entries
326 memcpy(papszNew + 1, // second new entry
327 pEnv->papszVars, // first old entry
328 sizeof(PSZ) * pEnv->cVars);
329 }
330 else
331 {
332 // append at the tail:
333 // overwrite last entry
334 ppszNew = papszNew + pEnv->cVars;
335 // copy old entries
336 memcpy(papszNew, // first new entry
337 pEnv->papszVars, // first old entry
338 sizeof(PSZ) * pEnv->cVars);
339 }
340
341 if (pEnv->papszVars)
342 free(pEnv->papszVars); // was missing V0.9.12 (2001-05-21) [umoeller]
343
344 pEnv->papszVars = papszNew;
345 pEnv->cVars++;
346 *ppszNew = strdup(pszNewEnv);
347 }
348 }
349 }
350 }
351
352 return (arc);
353}
354
355/*
356 *@@ appConvertEnvironment:
357 * converts an environment initialized by appGetEnvironment
358 * to the string format required by WinStartApp and DosExecPgm,
359 * that is, one memory block is allocated in *ppszEnv and all
360 * strings in pEnv->papszVars are copied to that block. Each
361 * string is terminated with a null character; the last string
362 * is terminated with two null characters.
363 *
364 * Use free() to free the memory block allocated by this
365 * function in *ppszEnv.
366 *
367 *@@added V0.9.4 (2000-07-19) [umoeller]
368 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
369 */
370
371APIRET appConvertEnvironment(PDOSENVIRONMENT pEnv,
372 PSZ *ppszEnv, // out: environment string
373 PULONG pulSize) // out: size of block allocated in *ppszEnv; ptr can be NULL
374{
375 APIRET arc = NO_ERROR;
376 if (!pEnv)
377 arc = ERROR_INVALID_PARAMETER;
378 else
379 {
380 if (!pEnv->papszVars)
381 arc = ERROR_INVALID_PARAMETER;
382 else
383 {
384 // count memory needed for all strings
385 ULONG cbNeeded = 0,
386 ul = 0;
387 PSZ *ppszThis = pEnv->papszVars;
388
389 for (ul = 0;
390 ul < pEnv->cVars;
391 ul++)
392 {
393 cbNeeded += strlen(*ppszThis) + 1; // length of string plus null terminator
394
395 // next environment string
396 ppszThis++;
397 }
398
399 cbNeeded++; // for another null terminator
400
401 *ppszEnv = (PSZ)malloc(cbNeeded);
402 if (!(*ppszEnv))
403 arc = ERROR_NOT_ENOUGH_MEMORY;
404 else
405 {
406 PSZ pTarget = *ppszEnv;
407 if (pulSize)
408 *pulSize = cbNeeded;
409 ppszThis = pEnv->papszVars;
410
411 // now copy each string
412 for (ul = 0;
413 ul < pEnv->cVars;
414 ul++)
415 {
416 PSZ pSource = *ppszThis;
417
418 while ((*pTarget++ = *pSource++))
419 ;
420
421 // *pTarget++ = 0; // append null terminator per string
422
423 // next environment string
424 ppszThis++;
425 }
426
427 *pTarget++ = 0; // append second null terminator
428 }
429 }
430 }
431
432 return (arc);
433}
434
435/*
436 *@@ appFreeEnvironment:
437 * frees memory allocated by appGetEnvironment.
438 *
439 *@@added V0.9.4 (2000-07-19) [umoeller]
440 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from dosh2.c to apps.c
441 */
442
443APIRET appFreeEnvironment(PDOSENVIRONMENT pEnv)
444{
445 APIRET arc = NO_ERROR;
446 if (!pEnv)
447 arc = ERROR_INVALID_PARAMETER;
448 else
449 {
450 if (!pEnv->papszVars)
451 arc = ERROR_INVALID_PARAMETER;
452 else
453 {
454 PSZ *ppszThis = pEnv->papszVars;
455 PSZ pszThis;
456 ULONG ul = 0;
457
458 for (ul = 0;
459 ul < pEnv->cVars;
460 ul++)
461 {
462 pszThis = *ppszThis;
463 free(pszThis);
464 // *ppszThis = NULL;
465 // next environment string
466 ppszThis++;
467 }
468
469 free(pEnv->papszVars);
470 pEnv->cVars = 0;
471 }
472 }
473
474 return (arc);
475}
476
477/* ******************************************************************
478 *
479 * Application information
480 *
481 ********************************************************************/
482
483/*
484 *@@ appQueryAppType:
485 * returns the Control Program (Dos) and
486 * Win* PROG_* application types for the
487 * specified executable. Essentially, this
488 * is a wrapper around DosQueryAppType.
489 *
490 * pcszExecutable must be fully qualified.
491 * You can use doshFindExecutable to qualify
492 * it.
493 *
494 * This returns the APIRET of DosQueryAppType.
495 * If this is NO_ERROR; *pulDosAppType receives
496 * the app type of DosQueryAppType. In addition,
497 * *pulWinAppType is set to one of the following:
498 *
499 * -- PROG_FULLSCREEN
500 *
501 * -- PROG_PDD
502 *
503 * -- PROG_VDD
504 *
505 * -- PROG_DLL
506 *
507 * -- PROG_WINDOWEDVDM
508 *
509 * -- PROG_PM
510 *
511 * -- PROG_31_ENHSEAMLESSCOMMON
512 *
513 * -- PROG_WINDOWABLEVIO
514 *
515 *@@added V0.9.9 (2001-03-07) [umoeller]
516 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
517 *@@changed V0.9.14 (2001-08-07) [pr]: use FAPPTYP_* constants
518 */
519
520APIRET appQueryAppType(const char *pcszExecutable,
521 PULONG pulDosAppType,
522 PULONG pulWinAppType)
523{
524 APIRET arc = DosQueryAppType((PSZ)pcszExecutable, pulDosAppType);
525 if (arc == NO_ERROR)
526 {
527 ULONG _ulDosAppType = *pulDosAppType;
528
529 if (_ulDosAppType == 0)
530 *pulWinAppType = PROG_FULLSCREEN;
531 else if (_ulDosAppType & FAPPTYP_PHYSDRV) // 0x40
532 *pulWinAppType = PROG_PDD;
533 else if (_ulDosAppType & FAPPTYP_VIRTDRV) // 0x80)
534 *pulWinAppType = PROG_VDD;
535 else if ((_ulDosAppType & 0xF0) == FAPPTYP_DLL) // 0x10)
536 // DLL bit set
537 *pulWinAppType = PROG_DLL;
538 else if (_ulDosAppType & FAPPTYP_DOS) // 0x20)
539 // DOS bit set?
540 *pulWinAppType = PROG_WINDOWEDVDM;
541 else if ((_ulDosAppType & FAPPTYP_WINDOWAPI) == FAPPTYP_WINDOWAPI) // 0x0003) // "Window-API" == PM
542 *pulWinAppType = PROG_PM;
543 else if ( ((_ulDosAppType & 0xFFFF) == FAPPTYP_WINDOWSPROT31) // 0x1000) // windows program (?!?)
544 || ((_ulDosAppType & 0xFFFF) == FAPPTYP_WINDOWSPROT) // ) // windows program (?!?)
545 )
546 *pulWinAppType = PROG_31_ENHSEAMLESSCOMMON; // PROG_31_ENH;
547 // *pulWinAppType = PROG_31_ENHSEAMLESSVDM;
548 else if ((_ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_WINDOWCOMPAT) // 0x02)
549 *pulWinAppType = PROG_WINDOWABLEVIO;
550 else if ((_ulDosAppType & FAPPTYP_WINDOWAPI /* 0x03 */ ) == FAPPTYP_NOTWINDOWCOMPAT) // 0x01)
551 *pulWinAppType = PROG_FULLSCREEN;
552 }
553
554 return (arc);
555}
556
557/*
558 *@@ appDescribeAppType:
559 * returns a "PROG_*" string for the given
560 * program type. Useful for WPProgram setup
561 * strings and such.
562 *
563 *@@added V0.9.16 (2001-10-06)
564 */
565
566PCSZ appDescribeAppType(PROGCATEGORY progc) // in: from PROGDETAILS.progc
567{
568 switch (progc)
569 {
570 case PROG_DEFAULT: return "PROG_DEFAULT";
571 case PROG_FULLSCREEN: return "PROG_FULLSCREEN";
572 case PROG_WINDOWABLEVIO: return "PROG_WINDOWABLEVIO";
573 case PROG_PM: return "PROG_PM";
574 case PROG_GROUP: return "PROG_GROUP";
575 case PROG_VDM: return "PROG_VDM";
576 // same as case PROG_REAL: return "PROG_REAL";
577 case PROG_WINDOWEDVDM: return "PROG_WINDOWEDVDM";
578 case PROG_DLL: return "PROG_DLL";
579 case PROG_PDD: return "PROG_PDD";
580 case PROG_VDD: return "PROG_VDD";
581 case PROG_WINDOW_REAL: return "PROG_WINDOW_REAL";
582 case PROG_30_STD: return "PROG_30_STD";
583 // same as case PROG_WINDOW_PROT: return "PROG_WINDOW_PROT";
584 case PROG_WINDOW_AUTO: return "PROG_WINDOW_AUTO";
585 case PROG_30_STDSEAMLESSVDM: return "PROG_30_STDSEAMLESSVDM";
586 // same as case PROG_SEAMLESSVDM: return "PROG_SEAMLESSVDM";
587 case PROG_30_STDSEAMLESSCOMMON: return "PROG_30_STDSEAMLESSCOMMON";
588 // same as case PROG_SEAMLESSCOMMON: return "PROG_SEAMLESSCOMMON";
589 case PROG_31_STDSEAMLESSVDM: return "PROG_31_STDSEAMLESSVDM";
590 case PROG_31_STDSEAMLESSCOMMON: return "PROG_31_STDSEAMLESSCOMMON";
591 case PROG_31_ENHSEAMLESSVDM: return "PROG_31_ENHSEAMLESSVDM";
592 case PROG_31_ENHSEAMLESSCOMMON: return "PROG_31_ENHSEAMLESSCOMMON";
593 case PROG_31_ENH: return "PROG_31_ENH";
594 case PROG_31_STD: return "PROG_31_STD";
595
596// Warp 4 toolkit defines, whatever these were designed for...
597#ifndef PROG_DOS_GAME
598 #define PROG_DOS_GAME (PROGCATEGORY)21
599#endif
600#ifndef PROG_WIN_GAME
601 #define PROG_WIN_GAME (PROGCATEGORY)22
602#endif
603#ifndef PROG_DOS_MODE
604 #define PROG_DOS_MODE (PROGCATEGORY)23
605#endif
606
607 case PROG_DOS_GAME: return "PROG_DOS_GAME";
608 case PROG_WIN_GAME: return "PROG_WIN_GAME";
609 case PROG_DOS_MODE: return "PROG_DOS_MODE";
610 }
611
612 return NULL;
613}
614
615/*
616 *@@ appIsWindowsApp:
617 * checks the specified program category
618 * (PROGDETAILS.progt.progc) for whether
619 * it represents a Win-OS/2 application.
620 *
621 * Returns:
622 *
623 * -- 0: no windows app (it's VIO, OS/2
624 * or DOS fullscreen, or PM).
625 *
626 * -- 1: Win-OS/2 standard app.
627 *
628 * -- 2: Win-OS/2 enhanced-mode app.
629 *
630 *@@added V0.9.12 (2001-05-26) [umoeller]
631 */
632
633ULONG appIsWindowsApp(ULONG ulProgCategory)
634{
635 switch (ulProgCategory)
636 {
637 case PROG_31_ENHSEAMLESSVDM: // 17
638 case PROG_31_ENHSEAMLESSCOMMON: // 18
639 case PROG_31_ENH: // 19
640 return (2);
641
642#ifndef PROG_30_STD
643 #define PROG_30_STD (PROGCATEGORY)11
644#endif
645
646#ifndef PROG_30_STDSEAMLESSVDM
647 #define PROG_30_STDSEAMLESSVDM (PROGCATEGORY)13
648#endif
649
650 case PROG_WINDOW_REAL: // 10
651 case PROG_30_STD: // 11
652 case PROG_WINDOW_AUTO: // 12
653 case PROG_30_STDSEAMLESSVDM: // 13
654 case PROG_30_STDSEAMLESSCOMMON: // 14
655 case PROG_31_STDSEAMLESSVDM: // 15
656 case PROG_31_STDSEAMLESSCOMMON: // 16
657 case PROG_31_STD: // 20
658 return (1);
659 }
660
661 return (0);
662}
663
664/* ******************************************************************
665 *
666 * Application start
667 *
668 ********************************************************************/
669
670/*
671 *@@ CallBatchCorrectly:
672 * fixes the specified PROGDETAILS for
673 * command files in the executable part
674 * by inserting /C XXX into the parameters
675 * and setting the executable according
676 * to an environment variable.
677 *
678 *@@added V0.9.6 (2000-10-16) [umoeller]
679 *@@changed V0.9.7 (2001-01-15) [umoeller]: now using XSTRING
680 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
681 */
682
683VOID CallBatchCorrectly(PPROGDETAILS pProgDetails,
684 PXSTRING pstrParams, // in/out: modified parameters (reallocated)
685 const char *pcszEnvVar, // in: env var spec'g command proc
686 // (e.g. "OS2_SHELL"); can be NULL
687 const char *pcszDefProc) // in: def't command proc (e.g. "CMD.EXE")
688{
689 // XXX.CMD file as executable:
690 // fix args to /C XXX.CMD
691
692 PSZ pszOldParams = NULL;
693 ULONG ulOldParamsLength = pstrParams->ulLength;
694 if (ulOldParamsLength)
695 // we have parameters already:
696 // make a backup... we'll append that later
697 pszOldParams = strdup(pstrParams->psz);
698
699 // set new params to "/C filename.cmd"
700 xstrcpy(pstrParams, "/C ", 0);
701 xstrcat(pstrParams,
702 pProgDetails->pszExecutable,
703 0);
704
705 if (pszOldParams)
706 {
707 // .cmd had params:
708 // append space and old params
709 xstrcatc(pstrParams, ' ');
710 xstrcat(pstrParams,
711 pszOldParams,
712 ulOldParamsLength);
713 free(pszOldParams);
714 }
715
716 // set executable to $(OS2_SHELL)
717 pProgDetails->pszExecutable = NULL;
718 if (pcszEnvVar)
719 pProgDetails->pszExecutable = getenv(pcszEnvVar);
720 if (!pProgDetails->pszExecutable)
721 pProgDetails->pszExecutable = (PSZ)pcszDefProc;
722 // should be on PATH
723}
724
725/*
726 *@@ appQueryDefaultWin31Environment:
727 * returns the default Win-OS/2 3.1 environment
728 * from OS2.INI, which you can then merge with
729 * your process environment to be able to
730 * start Win-OS/2 sessions properly with
731 * appStartApp.
732 *
733 * Caller must free() the return value.
734 *
735 *@@added V0.9.12 (2001-05-26) [umoeller]
736 */
737
738PSZ appQueryDefaultWin31Environment(VOID)
739{
740 PSZ pszReturn = NULL;
741 ULONG ulSize = 0;
742 // get default environment (from Win-OS/2 settings object)
743 // from OS2.INI
744 PSZ pszDefEnv = prfhQueryProfileData(HINI_USER,
745 "WINOS2",
746 "PM_GlobalWindows31Settings",
747 &ulSize);
748 if (pszDefEnv)
749 {
750 if (pszReturn = (PSZ)malloc(ulSize + 2))
751 {
752 PSZ p;
753 memset(pszReturn, 0, ulSize + 2);
754 memcpy(pszReturn, pszDefEnv, ulSize);
755
756 for (p = pszReturn;
757 p < pszReturn + ulSize;
758 p++)
759 if (*p == ';')
760 *p = 0;
761
762 // okay.... now we got an OS/2-style environment
763 // with 0, 0, 00 strings
764 }
765
766 free(pszDefEnv);
767 }
768
769 return (pszReturn);
770}
771
772/*
773 *@@ appStartApp:
774 * wrapper around WinStartApp which fixes the
775 * specified PROGDETAILS to (hopefully) work
776 * work with all executable types.
777 *
778 * This fixes the executable info to support:
779 *
780 * -- starting "*" executables (command prompts
781 * for OS/2, DOS, Win-OS/2);
782 *
783 * -- starting ".CMD" and ".BAT" files as
784 * PROGDETAILS.pszExecutable.
785 *
786 * Unless it is "*", PROGDETAILS.pszExecutable must
787 * be a proper file name. The full path may be omitted
788 * if it is on the PATH, but the extension (.EXE etc.)
789 * must be given. You can use doshFindExecutable to
790 * find executables if you don't know the extension.
791 *
792 * This also handles and merges special and default
793 * environments for the app to be started. The
794 * following should be respected:
795 *
796 * -- As with WinStartApp, if PROGDETAILS.pszEnvironment
797 * is NULL, the new app inherits a default environment
798 * from the shell.
799 *
800 * -- However, if you specify an environment, you _must_
801 * specify a complete environment. This function
802 * will not merge environments. Use
803 * appSetEnvironmentVar to change environment
804 * variables in a complete environment set.
805 *
806 * -- If PROGDETAILS specifies a Win-OS/2 session
807 * and PROGDETAILS.pszEnvironment is empty,
808 * this uses the default Win-OS/2 environment.
809 * See appQueryDefaultWin31Environment.
810 *
811 * Even though this isn't clearly said in PMREF,
812 * PROGDETAILS.swpInitial is important:
813 *
814 * -- To start a session minimized, set SWP_MINIMIZE.
815 *
816 * -- To start a VIO session auto-close disabled, set
817 * the half-documented SWP_NOAUTOCLOSE flag (0x8000)
818 * This flag is now in the newer toolkit headers.
819 *
820 * In addition, this supports the following session
821 * flags with ulFlags if PROG_DEFAULT is specified:
822 *
823 * -- APP_RUN_FULLSCREEN
824 *
825 * -- APP_RUN_ENHANCED
826 *
827 * -- APP_RUN_STANDARD
828 *
829 * -- APP_RUN_SEPARATE
830 *
831 * Since this calls WinStartApp in turn, this
832 * requires a message queue on the calling thread.
833 *
834 * Note that this also does minimal checking on
835 * the specified parameters so it can return something
836 * more meaningful than FALSE like WinStartApp.
837 * As a result, you get a DOS error code now (V0.9.16).
838 *
839 * Most importantly:
840 *
841 * -- ERROR_INVALID_THREADID: not running on thread 1.
842 * Since this uses WinStartApp internally and
843 * WinStartApp completely hangs the session manager
844 * if a Win-OS/2 full-screen session is started from
845 * a thread that is NOT thread1, this will fail
846 * with an error for safety (V0.9.16).
847 *
848 * -- ERROR_INVALID_PARAMETER: pcProgDetails or
849 * phapp is NULL; or PROGDETAILS.pszExecutable is NULL.
850 *
851 * -- ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND:
852 * PROGDETAILS.pszExecutable and/or PROGDETAILS.pszStartupDir
853 * are invalid.
854 * A NULL PROGDETAILS.pszStartupDir is supported though.
855 *
856 * -- ERROR_NOT_ENOUGH_MEMORY
857 *
858 *@@added V0.9.6 (2000-10-16) [umoeller]
859 *@@changed V0.9.7 (2000-12-10) [umoeller]: PROGDETAILS.swpInitial no longer zeroed... this broke VIOs
860 *@@changed V0.9.7 (2000-12-17) [umoeller]: PROGDETAILS.pszEnvironment no longer zeroed
861 *@@changed V0.9.9 (2001-01-27) [umoeller]: crashed if PROGDETAILS.pszExecutable was NULL
862 *@@changed V0.9.12 (2001-05-26) [umoeller]: fixed PROG_DEFAULT
863 *@@changed V0.9.12 (2001-05-27) [umoeller]: moved from winh.c to apps.c
864 *@@changed V0.9.14 (2001-08-07) [pr]: removed some env. strings for Win. apps.
865 *@@changed V0.9.14 (2001-08-23) [pr]: added session type options
866 *@@changed V0.9.16 (2001-10-19) [umoeller]: added prototype to return APIRET
867 *@@changed V0.9.16 (2001-10-19) [umoeller]: added thread-1 check
868 */
869
870APIRET appStartApp(HWND hwndNotify, // in: notify window or NULLHANDLE
871 const PROGDETAILS *pcProgDetails, // in: program spec (req.)
872 ULONG ulFlags, // in: APP_RUN_* flags
873 HAPP *phapp) // out: application handle if NO_ERROR is returned
874{
875 APIRET arc = NO_ERROR;
876 PROGDETAILS ProgDetails;
877
878 if (!pcProgDetails || !phapp)
879 return (ERROR_INVALID_PARAMETER);
880
881 memcpy(&ProgDetails, pcProgDetails, sizeof(PROGDETAILS));
882 // pointers still point into old prog details buffer
883 ProgDetails.Length = sizeof(PROGDETAILS);
884 ProgDetails.progt.fbVisible = SHE_VISIBLE;
885
886 // all this only makes sense if this contains something...
887 // besides, this crashed on string comparisons V0.9.9 (2001-01-27) [umoeller]
888 if (!ProgDetails.pszExecutable)
889 arc = ERROR_INVALID_PARAMETER;
890 else if (doshMyTID() != 1) // V0.9.16 (2001-10-19) [umoeller]
891 arc = ERROR_INVALID_THREADID;
892 else
893 {
894 ULONG ulIsWinApp;
895
896 XSTRING strParamsPatched;
897 PSZ pszWinOS2Env = 0;
898
899 // memset(&ProgDetails.swpInitial, 0, sizeof(SWP));
900 // this wasn't a good idea... WPProgram stores stuff
901 // in here, such as the "minimize on startup" -> SWP_MINIMIZE
902
903 // duplicate parameters...
904 // we need this for string manipulations below...
905 if (ProgDetails.pszParameters)
906 xstrInitCopy(&strParamsPatched,
907 ProgDetails.pszParameters,
908 100);
909 else
910 // no old params:
911 xstrInit(&strParamsPatched, 100);
912
913 // _Pmpf((__FUNCTION__ ": old progc: 0x%lX", pcProgDetails->progt.progc));
914 // _Pmpf((" pszTitle: %s", (ProgDetails.pszTitle) ? ProgDetails.pszTitle : NULL));
915 // _Pmpf((" pszIcon: %s", (ProgDetails.pszIcon) ? ProgDetails.pszIcon : NULL));
916
917 // program type fixups
918 switch (ProgDetails.progt.progc) // that's a ULONG
919 {
920 case ((ULONG)-1): // we get that sometimes...
921 case PROG_DEFAULT:
922 {
923 // V0.9.12 (2001-05-26) [umoeller]
924 ULONG ulDosAppType;
925 appQueryAppType(ProgDetails.pszExecutable,
926 &ulDosAppType,
927 &ProgDetails.progt.progc);
928 }
929 break;
930 }
931
932 // set session type from option flags
933 if (ulFlags & APP_RUN_FULLSCREEN)
934 {
935 if (ProgDetails.progt.progc == PROG_WINDOWABLEVIO)
936 ProgDetails.progt.progc = PROG_FULLSCREEN;
937
938 if (ProgDetails.progt.progc == PROG_WINDOWEDVDM)
939 ProgDetails.progt.progc = PROG_VDM;
940 }
941
942 if (ulIsWinApp = appIsWindowsApp(ProgDetails.progt.progc))
943 {
944 if (ulFlags & APP_RUN_FULLSCREEN)
945 ProgDetails.progt.progc = (ulFlags & APP_RUN_ENHANCED)
946 ? PROG_31_ENH
947 : PROG_31_STD;
948 else
949 {
950 if (ulFlags & APP_RUN_STANDARD)
951 ProgDetails.progt.progc = (ulFlags & APP_RUN_SEPARATE)
952 ? PROG_31_STDSEAMLESSVDM
953 : PROG_31_STDSEAMLESSCOMMON;
954
955 if (ulFlags & APP_RUN_ENHANCED)
956 ProgDetails.progt.progc = (ulFlags & APP_RUN_SEPARATE)
957 ? PROG_31_ENHSEAMLESSVDM
958 : PROG_31_ENHSEAMLESSCOMMON;
959 }
960
961 // re-run V0.9.16 (2001-10-19) [umoeller]
962 ulIsWinApp = appIsWindowsApp(ProgDetails.progt.progc);
963 }
964
965 /*
966 * command lines fixups:
967 *
968 */
969
970 if (!strcmp(ProgDetails.pszExecutable, "*"))
971 {
972 /*
973 * "*" for command sessions:
974 *
975 */
976
977 if (ulIsWinApp == 2)
978 {
979 // enhanced Win-OS/2 session:
980 PSZ psz = NULL;
981 if (strParamsPatched.ulLength)
982 // "/3 " + existing params
983 psz = strdup(strParamsPatched.psz);
984
985 xstrcpy(&strParamsPatched, "/3 ", 0);
986
987 if (psz)
988 {
989 xstrcat(&strParamsPatched, psz, 0);
990 free(psz);
991 }
992 }
993
994 if (ulIsWinApp)
995 {
996 // cheat: WinStartApp doesn't support NULL
997 // for Win-OS2 sessions, so manually start winos2.com
998 ProgDetails.pszExecutable = "WINOS2.COM";
999 // this is a DOS app, so fix this to DOS fullscreen
1000 ProgDetails.progt.progc = PROG_VDM;
1001 }
1002 else
1003 // for all other executable types
1004 // (including OS/2 and DOS sessions),
1005 // set pszExecutable to NULL; this will
1006 // have WinStartApp start a cmd shell
1007 ProgDetails.pszExecutable = NULL;
1008
1009 } // end if (strcmp(pProgDetails->pszExecutable, "*") == 0)
1010 else
1011 {
1012 // now check if the executable is valid
1013 // V0.9.16 (2001-10-19) [umoeller]
1014 ULONG ulAttr;
1015 _Pmpf((" %d now, checking %s", arc, ProgDetails.pszExecutable));
1016 if (!(arc = doshQueryPathAttr(ProgDetails.pszExecutable,
1017 &ulAttr)))
1018 {
1019 // make sure startup dir is really a directory
1020 if (ProgDetails.pszStartupDir)
1021 {
1022 _Pmpf((" checking %s", ProgDetails.pszStartupDir));
1023 if (!(arc = doshQueryPathAttr(ProgDetails.pszStartupDir,
1024 &ulAttr)))
1025 if (!(ulAttr & FILE_DIRECTORY))
1026 arc = ERROR_PATH_NOT_FOUND;
1027 // @@todo
1028 }
1029 }
1030
1031 _Pmpf((" after checking %d", arc));
1032
1033 if (!arc)
1034 {
1035 PSZ pszExtension;
1036 switch (ProgDetails.progt.progc)
1037 {
1038 /*
1039 * .CMD files fixups
1040 *
1041 */
1042
1043 case PROG_FULLSCREEN: // OS/2 fullscreen
1044 case PROG_WINDOWABLEVIO: // OS/2 window
1045 {
1046 if ( (pszExtension = doshGetExtension(ProgDetails.pszExecutable))
1047 && (!stricmp(pszExtension, "CMD"))
1048 )
1049 {
1050 CallBatchCorrectly(&ProgDetails,
1051 &strParamsPatched,
1052 "OS2_SHELL",
1053 "CMD.EXE");
1054 }
1055 break; }
1056
1057 case PROG_VDM: // DOS fullscreen
1058 case PROG_WINDOWEDVDM: // DOS window
1059 {
1060 if ( (pszExtension = doshGetExtension(ProgDetails.pszExecutable))
1061 && (!stricmp(pszExtension, "BAT"))
1062 )
1063 {
1064 CallBatchCorrectly(&ProgDetails,
1065 &strParamsPatched,
1066 NULL,
1067 "COMMAND.COM");
1068 }
1069 break; }
1070 } // end switch (ProgDetails.progt.progc)
1071 }
1072 }
1073
1074 if (!arc)
1075 {
1076 if ( (ulIsWinApp)
1077 && ( (ProgDetails.pszEnvironment == NULL)
1078 || (!strlen(ProgDetails.pszEnvironment))
1079 )
1080 )
1081 {
1082 // this is a windoze app, and caller didn't bother
1083 // to give us an environment:
1084 // we MUST set one then, or we'll get the strangest
1085 // errors, up to system hangs. V0.9.12 (2001-05-26) [umoeller]
1086
1087 DOSENVIRONMENT Env = {0};
1088
1089 // get standard WIN-OS/2 environment
1090 PSZ pszTemp = appQueryDefaultWin31Environment();
1091
1092 if (!(arc = appParseEnvironment(pszTemp,
1093 &Env)))
1094 {
1095 // now override KBD_CTRL_BYPASS=CTRL_ESC
1096 if ( (!(arc = appSetEnvironmentVar(&Env,
1097 "KBD_CTRL_BYPASS=CTRL_ESC",
1098 FALSE))) // add last
1099 && (!(arc = appConvertEnvironment(&Env,
1100 &pszWinOS2Env, // freed at bottom
1101 NULL)))
1102 )
1103 ProgDetails.pszEnvironment = pszWinOS2Env;
1104
1105 appFreeEnvironment(&Env);
1106 }
1107
1108 free(pszTemp);
1109 }
1110
1111 if (!arc)
1112 {
1113 if (!ProgDetails.pszTitle)
1114 ProgDetails.pszTitle = ProgDetails.pszExecutable;
1115
1116 ProgDetails.pszParameters = strParamsPatched.psz;
1117
1118 /* _Pmpf(("progt.progc: %d", ProgDetails.progt.progc));
1119 _Pmpf(("progt.fbVisible: 0x%lX", ProgDetails.progt.fbVisible));
1120 _Pmpf(("progt.pszTitle: \"%s\"", (ProgDetails.pszTitle) ? ProgDetails.pszTitle : "NULL"));
1121 _Pmpf(("exec: \"%s\"", (ProgDetails.pszExecutable) ? ProgDetails.pszExecutable : "NULL"));
1122 _Pmpf(("params: \"%s\"", (ProgDetails.pszParameters) ? ProgDetails.pszParameters : "NULL"));
1123 _Pmpf(("startup: \"%s\"", (ProgDetails.pszStartupDir) ? ProgDetails.pszStartupDir : "NULL"));
1124 _Pmpf(("pszIcon: \"%s\"", (ProgDetails.pszIcon) ? ProgDetails.pszIcon : "NULL"));
1125 _Pmpf(("environment: "));
1126 {
1127 PSZ pszThis = ProgDetails.pszEnvironment;
1128 while (pszThis && *pszThis)
1129 {
1130 _Pmpf((" \"%s\"", pszThis));
1131 pszThis += strlen(pszThis) + 1;
1132 }
1133 }
1134
1135 _Pmpf(("swpInitial.fl = 0x%lX, x = %d, y = %d, cx = %d, cy = %d:",
1136 ProgDetails.swpInitial.fl,
1137 ProgDetails.swpInitial.x,
1138 ProgDetails.swpInitial.y,
1139 ProgDetails.swpInitial.cx,
1140 ProgDetails.swpInitial.cy));
1141 _Pmpf((" behind = %d, hwnd = %d, res1 = %d, res2 = %d",
1142 ProgDetails.swpInitial.hwndInsertBehind,
1143 ProgDetails.swpInitial.hwnd,
1144 ProgDetails.swpInitial.ulReserved1,
1145 ProgDetails.swpInitial.ulReserved2));
1146 */
1147
1148 /* if (WinMessageBox(HWND_DESKTOP,
1149 NULLHANDLE,
1150 (ProgDetails.pszExecutable) ? ProgDetails.pszExecutable : "NULL",
1151 "Start?",
1152 0,
1153 MB_YESNO | MB_MOVEABLE)
1154 == MBID_YES) */
1155 if (!(*phapp = WinStartApp(hwndNotify,
1156 // receives WM_APPTERMINATENOTIFY
1157 &ProgDetails,
1158 strParamsPatched.psz,
1159 NULL, // "reserved", PMREF says...
1160 SAF_INSTALLEDCMDLINE)))
1161 // we MUST use SAF_INSTALLEDCMDLINE
1162 // or no Win-OS/2 session will start...
1163 // whatever is going on here... Warp 4 FP11
1164
1165 // do not use SAF_STARTCHILDAPP, or the
1166 // app will be terminated automatically
1167 // when the WPS terminates!
1168 arc = ERROR_BAD_FORMAT;
1169 // @@todo we can probably do better
1170 // V0.9.16 (2001-10-19) [umoeller]
1171
1172 // _Pmpf((__FUNCTION__ ": got happ 0x%lX", happ));
1173 }
1174 }
1175
1176 xstrClear(&strParamsPatched);
1177 if (pszWinOS2Env)
1178 free(pszWinOS2Env);
1179 } // end if (ProgDetails.pszExecutable)
1180
1181 _Pmpf((__FUNCTION__ ": returning %d", arc));
1182
1183 return (arc);
1184}
1185
1186/*
1187 *@@ appWaitForApp:
1188 * waits for the specified application to terminate
1189 * and returns its exit code.
1190 *
1191 *@@added V0.9.9 (2001-03-07) [umoeller]
1192 */
1193
1194BOOL appWaitForApp(HWND hwndNotify, // in: notify window
1195 HAPP happ, // in: app to wait for
1196 PULONG pulExitCode) // out: exit code (ptr can be NULL)
1197{
1198 BOOL brc = FALSE;
1199
1200 if (happ)
1201 {
1202 // app started:
1203 // enter a modal message loop until we get the
1204 // WM_APPTERMINATENOTIFY for happ. Then we
1205 // know the app is done.
1206 HAB hab = WinQueryAnchorBlock(hwndNotify);
1207 QMSG qmsg;
1208 // ULONG ulXFixReturnCode = 0;
1209 while (WinGetMsg(hab, &qmsg, NULLHANDLE, 0, 0))
1210 {
1211 if ( (qmsg.msg == WM_APPTERMINATENOTIFY)
1212 && (qmsg.hwnd == hwndNotify)
1213 && (qmsg.mp1 == (MPARAM)happ)
1214 )
1215 {
1216 // xfix has terminated:
1217 // get xfix return code from mp2... this is:
1218 // -- 0: everything's OK, continue.
1219 // -- 1: handle section was rewritten, restart Desktop
1220 // now.
1221 if (pulExitCode)
1222 *pulExitCode = (ULONG)qmsg.mp2;
1223 brc = TRUE;
1224 // do not dispatch this
1225 break;
1226 }
1227
1228 WinDispatchMsg(hab, &qmsg);
1229 }
1230 }
1231
1232 return (brc);
1233}
1234
1235/*
1236 *@@ appQuickStartApp:
1237 * shortcut for simply starting an app and
1238 * waiting until it's finished.
1239 *
1240 * On errors, NULLHANDLE is returned.
1241 *
1242 * If pulReturnCode != NULL, it receives the
1243 * return code of the app.
1244 *
1245 *@@added V0.9.16 (2001-10-19) [umoeller]
1246 */
1247
1248HAPP appQuickStartApp(const char *pcszFile,
1249 ULONG ulProgType, // e.g. PROG_PM
1250 const char *pcszArgs,
1251 PULONG pulExitCode)
1252{
1253 PROGDETAILS pd = {0};
1254 HAPP happ,
1255 happReturn = NULLHANDLE;
1256 CHAR szDir[CCHMAXPATH] = "";
1257 PCSZ p;
1258 HWND hwndObject;
1259
1260 pd.Length = sizeof(pd);
1261 pd.progt.progc = ulProgType;
1262 pd.progt.fbVisible = SHE_VISIBLE;
1263 pd.pszExecutable = (PSZ)pcszFile;
1264 pd.pszParameters = (PSZ)pcszArgs;
1265 if (p = strrchr(pcszFile, '\\'))
1266 {
1267 strhncpy0(szDir,
1268 pcszFile,
1269 p - pcszFile);
1270 pd.pszStartupDir = szDir;
1271 }
1272
1273 if ( (hwndObject = winhCreateObjectWindow(WC_STATIC, NULL))
1274 && (!appStartApp(hwndObject,
1275 &pd,
1276 0,
1277 &happ))
1278 )
1279 {
1280 if (appWaitForApp(hwndObject,
1281 happ,
1282 pulExitCode))
1283 happReturn = happ;
1284 }
1285
1286 return (happReturn);
1287}
Note: See TracBrowser for help on using the repository browser.