source: trunk/dll/pathutil.c@ 1919

Last change on this file since 1919 was 1919, checked in by Gregg Young, 17 hours ago

Fix issue with command lines that contain 2 (or more) executable extentions Ticket #536

  • Property svn:keywords set to Author Date Id Revision
File size: 13.0 KB
Line 
1
2/***********************************************************************
3
4 $Id: pathutil.c 1919 2025-11-11 16:42:02Z gyoung $
5
6 Path handling utility functions
7
8 Copyright (c) 1993-98 M. Kimes
9 Copyright (c) 2001, 2009 Steven H. Levine
10
11 05 Jan 08 SHL Move from arccnrs.c and comp.c to here
12 06 Jan 08 GKY Add NormalizeCmdLine to check program strings on entry
13 29 Feb 08 GKY Changes to enable user settable command line length
14 15 Oct 08 GKY Fix NormalizeCmdLine to check all 5 executable extensions when no extension provided;
15 use searchapath to check for existance of file types not checked by DosQAppType;
16 close DosFind.
17 28 Jun 09 GKY Added AddBackslashToPath() to remove repeatative code
18 12 Jul 09 GKY Add xDosQueryAppType and xDosAlloc... to allow FM/2 to load in high memory
19 23 Oct 10 GKY Add ForwardslashToBackslash function to streamline code
20 17 Sep 11 GKY Fix commandline quoting issues
21 01 Mar 14 JBS Ticket #524: Made "searchapath" thread-safe. Function names and signatures were changed.
22 So calls to these functions had to be changed.
23 21 Mar 14 SHL Add IsAbsolutePath
24 28 Jun 14 GKY Fix errors identified with CPPCheck;
25
26***********************************************************************/
27
28#include <stdlib.h>
29#include <string.h>
30#include <ctype.h>
31
32#define INCL_WIN
33#define INCL_DOS
34#define INCL_LONGLONG
35
36#include "fm3dll.h" // needs_quoting
37#include "notebook.h" // Data declaration(s)
38#include "init.h" // Data declaration(s)
39#include "fm3str.h"
40#include "srchpath.h" // Search*Path*ForFile
41#include "pathutil.h"
42#include "strips.h" // remove_first_occurence_of_character
43#include "valid.h" // needs_quoting
44#include "errutil.h" // Dos_Error...
45#include "strutil.h" // GetPString
46#include "wrappers.h" // xmalloc
47#include "fortify.h"
48#include "stristr.h" //stristr
49
50#ifdef PMPRINTF
51#define _PMPRINTF_ // Enable debug macros
52#include "PMPRINTF.H"
53#endif
54
55static PSZ pszSrcFile = __FILE__;
56
57PSZ ForwardslashToBackslash(PSZ pszPathName)
58{
59 CHAR *p;
60
61 p = pszPathName;
62 while (*p) {
63 if (*p == '/')
64 *p = '\\';
65 p++;
66 }
67 return pszPathName;
68}
69
70PSZ AddBackslashToPath(PSZ pszPathName)
71{
72 if (pszPathName[strlen(pszPathName) - 1] != '\\')
73 strcat(pszPathName, PCSZ_BACKSLASH);
74 return pszPathName;
75}
76
77/**
78 * Build full path name in callers buffer given directory
79 * name and filename
80 * @param pszPathName points to drive/directory if not NULL
81 * @returns pointer to full path name in caller's buffer
82 * @note OK for pszFullPathName and pszPathName to point to same buffer
83 *
84 */
85
86PSZ BldFullPathName(PSZ pszFullPathName, PCSZ pszPathName, PCSZ pszFileName)
87{
88 UINT c = pszPathName ? strlen(pszPathName) : 0;
89 if (c > 0) {
90 if (pszFullPathName != pszPathName)
91 memcpy(pszFullPathName, pszPathName, c);
92 if (pszFullPathName[c - 1] != '\\')
93 pszFullPathName[c++] = '\\';
94 }
95 strcpy(pszFullPathName + c, pszFileName);
96 return pszFullPathName;
97}
98
99/**
100 * Build quoted full path name in callers buffer given
101 * directory name and filename
102 * @param pszPathName points to drive/directory if not NULL
103 * @returns pointer to quoted path name in caller's buffer
104 */
105
106PSZ BldQuotedFullPathName(PSZ pszFullPathName, PCSZ pszPathName, PCSZ pszFileName)
107{
108 UINT c = pszPathName ? strlen(pszPathName) : 0;
109 BOOL q = needs_quoting(pszPathName) || needs_quoting(pszFileName);
110 PSZ psz = pszFullPathName;
111
112 if (q)
113 *psz++ = '"';
114 if (c > 0) {
115 memcpy(psz, pszPathName, c);
116 psz += c;
117 if (*(psz - 1) != '\\')
118 *psz++ = '\\';
119 }
120 strcpy(psz, pszFileName);
121 if (q) {
122 psz += strlen(psz);
123 *psz++ = '"';
124 *psz = 0;
125 }
126 return pszFullPathName;
127}
128
129/**
130 * Build quoted full path name in callers buffer given a filename
131 * @returns pointer to quoted file name in caller's buffer
132 */
133
134PSZ BldQuotedFileName(PSZ pszQuotedFileName, PCSZ pszFileName)
135{
136 BOOL q = needs_quoting(pszFileName);
137 PSZ psz = pszQuotedFileName;
138
139 if (q)
140 *psz++ = '"';
141 strcpy(psz, pszFileName);
142 if (q) {
143 psz += strlen(psz);
144 *psz++ = '"';
145 *psz = 0;
146 }
147 return pszQuotedFileName;
148}
149
150/**
151 * Return TRUE if absolute path name
152 * @param pszPathName points to path name
153 * @returns TRUE if absolute path, with or without drive letter
154 * @note Odd inputs return FALSE
155 *
156 */
157
158BOOL IsAbsolutePath(PCSZ pszPathName)
159{
160 return pszPathName &&
161 pszPathName[0] &&
162 ((pszPathName[0] == '\\' || pszPathName[0] == '/') ||
163 (toupper(pszPathName[0]) >= 'A' &&
164 toupper(pszPathName[0]) <= 'Z' &&
165 pszPathName[1] &&
166 pszPathName[1] == ':' &&
167 (pszPathName[2] == '\\' || pszPathName[2] == '/')));
168}
169
170/** NormalizeCmdLine
171 * Checks a command line for common errors (missing quotes, missing extension,
172 * no space between exe and args etc) Also check for the existance of the file
173 * and checks .com and .exe file headers.
174 * Command line passed as pszCmdLine_
175 * A pointer to a buffer of the size MaxComLineStrg should be supplied in
176 * pszWorkBuf. This is where the quoted etc as necessary command
177 * line string will be returned.
178 */
179
180PCSZ NormalizeCmdLine(PSZ pszWorkBuf, PSZ pszCmdLine_)
181{
182 char *szCmdLine, *szArgs;
183 char *offset = '\0', *offsetexe, *offsetcom, *offsetcmd, *offsetbtm, *offsetbat;
184 APIRET ret;
185 ULONG ulAppType;
186 char *pszChar;
187 PSZ pszNewCmdLine = pszWorkBuf;
188
189 szCmdLine = xmalloc(MaxComLineStrg, pszSrcFile, __LINE__);
190 if (!szCmdLine)
191 return pszCmdLine_; //already complained
192 szArgs = xmalloc(MaxComLineStrg, pszSrcFile, __LINE__);
193 if (!szArgs) {
194 free(szCmdLine);
195 return pszCmdLine_; //already complained
196 }
197 bstrip(pszCmdLine_);
198 memset(pszWorkBuf, 0, MaxComLineStrg);
199 strcpy(szCmdLine, pszCmdLine_);
200 if (szCmdLine[0] != '\0') {
201 offsetexe = stristr(pszCmdLine_, PCSZ_DOTEXE);
202 offsetcmd = stristr(pszCmdLine_, PCSZ_DOTCMD);
203 offsetcom = stristr(pszCmdLine_, PCSZ_DOTCOM);
204 offsetbtm = stristr(pszCmdLine_, PCSZ_DOTBTM);
205 offsetbat = stristr(pszCmdLine_, PCSZ_DOTBAT);
206 if (offsetexe)
207 offset = offsetexe;
208 if (offsetcom && !offset)
209 offset = offsetcom;
210 else if (offsetcom) {
211 if (offsetcom < offset) {
212 offset = offsetcom;
213 offsetexe = '\0';
214 }
215 }
216 if (offsetcmd && !offset)
217 offset = offsetcmd;
218 else if (offsetcmd) {
219 if (offsetcmd < offset) {
220 offset = offsetcmd;
221 offsetcom = offsetexe = '\0';
222 }
223 }
224 if (offsetbtm && !offset)
225 offset = offsetbtm;
226 else if (offsetbtm) {
227 if (offsetbtm < offset) {
228 offset = offsetbtm;
229 offsetcom = offsetexe = '\0';
230 }
231 }
232 if (offsetbat && !offset)
233 offset = offsetbat;
234 else if (offsetbat) {
235 if (offsetbat < offset) {
236 offset = offsetbat;
237 offsetcom = offsetexe = '\0';
238 }
239 }
240 if (offset) {
241 szCmdLine[offset + 4 - pszCmdLine_] = '\0';
242 strcpy(szArgs, &pszCmdLine_[offset + 4 - pszCmdLine_]);
243 while (strchr(szCmdLine, '\"'))
244 remove_first_occurence_of_character("\"", szCmdLine);
245 if ((szArgs[0] == '\"' && szArgs[1] == ' ') ||
246 (!strstr(pszCmdLine_, "\\:") && strchr(szArgs, '\"') &&
247 strchr(szArgs, '\"') == strrchr(szArgs, '\"')))
248 remove_first_occurence_of_character("\"", szArgs);
249 if (strchr(szArgs, '\"') != strrchr(szArgs, '\"'))
250 saymsg(MB_OK, HWND_DESKTOP,
251 NullStr,
252 GetPString(IDS_QUOTESINARGSTEXT),
253 pszCmdLine_);
254 if (!offsetexe && !offsetcom) {
255 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
256 ret = 0;
257 }
258 else
259 ret = xDosQueryAppType(szCmdLine, &ulAppType);
260 BldQuotedFileName(pszNewCmdLine, szCmdLine);
261 if (ret) {
262 ret = saymsg(MB_YESNO,
263 HWND_DESKTOP,
264 NullStr,
265 GetPString(IDS_PROGRAMNOTFOUNDTEXT),
266 pszCmdLine_);
267 if (ret == MBID_YES){
268 if (szArgs[0] != ' ')
269 strcat(pszNewCmdLine, " ");
270 strcat(pszNewCmdLine, szArgs);
271 }
272 else{
273 fCancelAction = TRUE;
274 pszNewCmdLine = pszCmdLine_;
275 }
276 }
277 else{
278 if (szArgs[0] != ' ')
279 strcat(pszNewCmdLine, " ");
280 strcat(pszNewCmdLine, szArgs);
281 }
282
283 }
284 // if it doesn't have an extension try it with all the standard ones and add if found
285 else if (!strchr(szCmdLine, '.') ||
286 strrchr(szCmdLine, '.' ) < strrchr(szCmdLine, '\\')) {
287 if (!strchr(szCmdLine, ' ')) {
288 // strip quotes readded by BuildQuotedFileName
289 while (strchr(szCmdLine, '\"'))
290 remove_first_occurence_of_character("\"", szCmdLine);
291 ret = xDosQueryAppType(szCmdLine, &ulAppType); // exe automatically appended
292 if (!ret)
293 strcat(szCmdLine, PCSZ_DOTEXE);
294 else {
295 strcat(szCmdLine, PCSZ_DOTCOM);
296 ret = xDosQueryAppType(szCmdLine, &ulAppType);
297 if (ret) {
298 offset = strrchr(szCmdLine, '.' );
299 *offset = 0;
300 strcat(szCmdLine, PCSZ_DOTCMD);
301 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
302 ret = 0;
303 else {
304 *offset = 0;
305 strcat(szCmdLine, PCSZ_DOTBAT);
306 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
307 ret = 0;
308 else {
309 *offset = 0;
310 strcat(szCmdLine, PCSZ_DOTBTM);
311 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
312 ret = 0;
313 }
314 }
315 }
316 }
317 }
318 else {
319 pszChar = szCmdLine;
320 while (pszChar) {
321 while (strchr(szCmdLine, '\"'))
322 remove_first_occurence_of_character("\"", szCmdLine);
323 if (*pszChar == ' ') { //test at every space for the end of the filename
324 *pszChar = '\0';
325 ret = xDosQueryAppType(szCmdLine, &ulAppType);
326 if (!ret) {
327 strcat(szCmdLine, PCSZ_DOTEXE);
328 break;
329 }
330 else {
331 strcat(szCmdLine, PCSZ_DOTCOM);
332 ret = xDosQueryAppType(szCmdLine, &ulAppType);
333 if (ret) {
334 offset = strrchr(szCmdLine, '.' );
335 *offset = 0;
336 strcat(szCmdLine, PCSZ_DOTCMD);
337 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
338 ret = 0;
339 break;
340 }
341 else {
342 *offset = 0;
343 strcat(szCmdLine, PCSZ_DOTBAT);
344 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
345 ret = 0;
346 break;
347 }
348 else {
349 *offset = 0;
350 strcat(szCmdLine, PCSZ_DOTBTM);
351 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
352 ret = 0;
353 break;
354 }
355 }
356 }
357 }
358 else
359 break;
360 }
361 }
362 strcpy(szCmdLine, pszCmdLine_);
363 pszChar++;
364 }
365 }
366 if (!ret){
367 BldQuotedFileName(pszNewCmdLine, szCmdLine);
368 strcpy(szArgs, pszCmdLine_ + strlen(szCmdLine) - 3);
369 if ((szArgs[0] == '\"' && szArgs[1] == ' ') ||
370 !strstr(pszCmdLine_, "\\:" ) ||
371 strchr(szArgs, '\"') == strrchr(szArgs, '\"'))
372 remove_first_occurence_of_character("\"", szArgs);
373 if (strchr(szArgs, '\"') != strrchr(szArgs, '\"'))
374 saymsg(MB_OK, HWND_DESKTOP,
375 NullStr,
376 GetPString(IDS_QUOTESINARGSTEXT),
377 pszCmdLine_);
378 if (szArgs[0] != ' ')
379 strcat(pszNewCmdLine, " ");
380 strcat(pszNewCmdLine, szArgs);
381 }
382 else { // fail if no extension can be found runemf2 requires one
383 ret = saymsg(MB_OK,
384 HWND_DESKTOP,
385 NullStr,
386 GetPString(IDS_PROGRAMNOTEXE2TEXT),
387 pszCmdLine_);
388 fCancelAction = TRUE;
389 pszNewCmdLine = pszCmdLine_;
390 }
391 }
392 else { // file has a nonstandard extension for executable
393 pszChar = strrchr(szCmdLine, '.');
394 while (pszChar && *pszChar !=' ') {
395 pszChar++;
396 }
397 *pszChar = '\0';
398 strcpy (szArgs, pszCmdLine_ + strlen(szCmdLine));
399 while (strchr(szCmdLine, '\"'))
400 remove_first_occurence_of_character("\"", szCmdLine);
401 if ((szArgs[0] == '\"' && szArgs[1] == ' ') ||
402 !strstr(pszCmdLine_, "\\:")||
403 strchr(szArgs, '\"') == strrchr(szArgs, '\"'))
404 remove_first_occurence_of_character("\"", szArgs);
405 if (strchr(szArgs, '\"') != strrchr(szArgs, '\"'))
406 saymsg(MB_OK, HWND_DESKTOP,
407 NullStr,
408 GetPString(IDS_QUOTESINARGSTEXT),
409 pszCmdLine_);
410 BldQuotedFileName(pszNewCmdLine, szCmdLine);
411 if (SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
412 ret = saymsg(MB_YESNO,
413 HWND_DESKTOP,
414 NullStr,
415 GetPString(IDS_PROGRAMNOTFOUNDTEXT),
416 pszCmdLine_);
417 if (ret == MBID_YES) {
418 if (szArgs[0] != ' ')
419 strcat(pszNewCmdLine, " ");
420 strcat(pszNewCmdLine, szArgs);
421 }
422 else {
423 fCancelAction = TRUE;
424 pszWorkBuf = pszCmdLine_;
425 }
426 }
427 else {
428 ret = saymsg(MB_YESNOCANCEL,
429 HWND_DESKTOP,
430 NullStr,
431 GetPString(IDS_PROGRAMNOTEXE3TEXT),
432 pszCmdLine_, pszNewCmdLine);
433 if (ret == MBID_YES){
434 if (szArgs[0] != ' ')
435 strcat(pszNewCmdLine, " ");
436 strcat(pszNewCmdLine, szArgs);
437 }
438 if (ret == MBID_CANCEL){
439 fCancelAction = TRUE;
440 pszNewCmdLine = pszCmdLine_;
441 }
442 }
443 }
444 }
445 free(szArgs);
446 free(szCmdLine);
447 return pszWorkBuf;
448}
449
450#pragma alloc_text(PATHUTIL,BldFullPathName)
451#pragma alloc_text(PATHUTIL,BldQuotedFileName)
452#pragma alloc_text(PATHUTIL,BldQuotedFullPathName)
453#pragma alloc_text(PATHUTIL,NormalizeCmdLine)
454#pragma alloc_text(PATHUTIL,IsAbsolutePath)
Note: See TracBrowser for help on using the repository browser.