source: trunk/dll/pathutil.c@ 1919

Last change on this file since 1919 was 1919, checked in by Gregg Young, 29 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
RevLine 
[907]1
2/***********************************************************************
3
[1323]4 $Id: pathutil.c 1919 2025-11-11 16:42:02Z gyoung $
[907]5
6 Path handling utility functions
7
8 Copyright (c) 1993-98 M. Kimes
[1394]9 Copyright (c) 2001, 2009 Steven H. Levine
[907]10
11 05 Jan 08 SHL Move from arccnrs.c and comp.c to here
[920]12 06 Jan 08 GKY Add NormalizeCmdLine to check program strings on entry
[985]13 29 Feb 08 GKY Changes to enable user settable command line length
[1699]14 15 Oct 08 GKY Fix NormalizeCmdLine to check all 5 executable extensions when no extension provided;
[1394]15 use searchapath to check for existance of file types not checked by DosQAppType;
[1761]16 close DosFind.
[1439]17 28 Jun 09 GKY Added AddBackslashToPath() to remove repeatative code
[1480]18 12 Jul 09 GKY Add xDosQueryAppType and xDosAlloc... to allow FM/2 to load in high memory
[1545]19 23 Oct 10 GKY Add ForwardslashToBackslash function to streamline code
[1632]20 17 Sep 11 GKY Fix commandline quoting issues
[1750]21 01 Mar 14 JBS Ticket #524: Made "searchapath" thread-safe. Function names and signatures were changed.
[1761]22 So calls to these functions had to be changed.
23 21 Mar 14 SHL Add IsAbsolutePath
[1782]24 28 Jun 14 GKY Fix errors identified with CPPCheck;
[907]25
26***********************************************************************/
27
[1039]28#include <stdlib.h>
[907]29#include <string.h>
[1761]30#include <ctype.h>
[907]31
32#define INCL_WIN
[920]33#define INCL_DOS
[907]34#define INCL_LONGLONG
35
[1185]36#include "fm3dll.h" // needs_quoting
[1211]37#include "notebook.h" // Data declaration(s)
38#include "init.h" // Data declaration(s)
[1185]39#include "fm3str.h"
[1750]40#include "srchpath.h" // Search*Path*ForFile
[907]41#include "pathutil.h"
[1161]42#include "strips.h" // remove_first_occurence_of_character
[1185]43#include "valid.h" // needs_quoting
[920]44#include "errutil.h" // Dos_Error...
45#include "strutil.h" // GetPString
[1185]46#include "wrappers.h" // xmalloc
[1039]47#include "fortify.h"
[1398]48#include "stristr.h" //stristr
[907]49
[1919]50#ifdef PMPRINTF
51#define _PMPRINTF_ // Enable debug macros
52#include "PMPRINTF.H"
53#endif
54
[985]55static PSZ pszSrcFile = __FILE__;
56
[1545]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
[1438]70PSZ AddBackslashToPath(PSZ pszPathName)
71{
72 if (pszPathName[strlen(pszPathName) - 1] != '\\')
73 strcat(pszPathName, PCSZ_BACKSLASH);
74 return pszPathName;
75}
76
[907]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
[1398]86PSZ BldFullPathName(PSZ pszFullPathName, PCSZ pszPathName, PCSZ pszFileName)
[907]87{
88 UINT c = pszPathName ? strlen(pszPathName) : 0;
89 if (c > 0) {
[1568]90 if (pszFullPathName != pszPathName)
91 memcpy(pszFullPathName, pszPathName, c);
[907]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
[1398]106PSZ BldQuotedFullPathName(PSZ pszFullPathName, PCSZ pszPathName, PCSZ pszFileName)
[907]107{
108 UINT c = pszPathName ? strlen(pszPathName) : 0;
[1161]109 BOOL q = needs_quoting(pszPathName) || needs_quoting(pszFileName);
[907]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
[1394]134PSZ BldQuotedFileName(PSZ pszQuotedFileName, PCSZ pszFileName)
[907]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
[1761]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
[920]170/** NormalizeCmdLine
171 * Checks a command line for common errors (missing quotes, missing extension,
[1248]172 * no space between exe and args etc) Also check for the existance of the file
173 * and checks .com and .exe file headers.
[920]174 * Command line passed as pszCmdLine_
[985]175 * A pointer to a buffer of the size MaxComLineStrg should be supplied in
[920]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{
[985]182 char *szCmdLine, *szArgs;
[920]183 char *offset = '\0', *offsetexe, *offsetcom, *offsetcmd, *offsetbtm, *offsetbat;
184 APIRET ret;
185 ULONG ulAppType;
186 char *pszChar;
187 PSZ pszNewCmdLine = pszWorkBuf;
188
[985]189 szCmdLine = xmalloc(MaxComLineStrg, pszSrcFile, __LINE__);
190 if (!szCmdLine)
191 return pszCmdLine_; //already complained
192 szArgs = xmalloc(MaxComLineStrg, pszSrcFile, __LINE__);
193 if (!szArgs) {
[1039]194 free(szCmdLine);
[985]195 return pszCmdLine_; //already complained
196 }
[920]197 bstrip(pszCmdLine_);
[985]198 memset(pszWorkBuf, 0, MaxComLineStrg);
[920]199 strcpy(szCmdLine, pszCmdLine_);
200 if (szCmdLine[0] != '\0') {
[1398]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);
[920]206 if (offsetexe)
[1919]207 offset = offsetexe;
208 if (offsetcom && !offset)
209 offset = offsetcom;
210 else if (offsetcom) {
211 if (offsetcom < offset) {
212 offset = offsetcom;
213 offsetexe = '\0';
[920]214 }
215 }
[1919]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 }
[920]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] == ' ') ||
[1631]246 (!strstr(pszCmdLine_, "\\:") && strchr(szArgs, '\"') &&
247 strchr(szArgs, '\"') == strrchr(szArgs, '\"')))
[920]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_);
[1247]254 if (!offsetexe && !offsetcom) {
[1761]255 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
256 ret = 0;
[920]257 }
258 else
[1439]259 ret = xDosQueryAppType(szCmdLine, &ulAppType);
[920]260 BldQuotedFileName(pszNewCmdLine, szCmdLine);
261 if (ret) {
[1394]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);
[920]271 }
[1394]272 else{
273 fCancelAction = TRUE;
274 pszNewCmdLine = pszCmdLine_;
275 }
[920]276 }
277 else{
[1394]278 if (szArgs[0] != ' ')
279 strcat(pszNewCmdLine, " ");
280 strcat(pszNewCmdLine, szArgs);
[920]281 }
282
283 }
[1247]284 // if it doesn't have an extension try it with all the standard ones and add if found
[1782]285 else if (!strchr(szCmdLine, '.') ||
286 strrchr(szCmdLine, '.' ) < strrchr(szCmdLine, '\\')) {
[920]287 if (!strchr(szCmdLine, ' ')) {
[1394]288 // strip quotes readded by BuildQuotedFileName
[920]289 while (strchr(szCmdLine, '\"'))
290 remove_first_occurence_of_character("\"", szCmdLine);
[1439]291 ret = xDosQueryAppType(szCmdLine, &ulAppType); // exe automatically appended
[1394]292 if (!ret)
[1398]293 strcat(szCmdLine, PCSZ_DOTEXE);
[1394]294 else {
[1398]295 strcat(szCmdLine, PCSZ_DOTCOM);
[1439]296 ret = xDosQueryAppType(szCmdLine, &ulAppType);
[1394]297 if (ret) {
298 offset = strrchr(szCmdLine, '.' );
299 *offset = 0;
[1398]300 strcat(szCmdLine, PCSZ_DOTCMD);
[1750]301 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
[1394]302 ret = 0;
303 else {
304 *offset = 0;
[1398]305 strcat(szCmdLine, PCSZ_DOTBAT);
[1750]306 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
[1394]307 ret = 0;
308 else {
309 *offset = 0;
[1398]310 strcat(szCmdLine, PCSZ_DOTBTM);
[1750]311 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL))
[1394]312 ret = 0;
313 }
314 }
315 }
316 }
[920]317 }
318 else {
319 pszChar = szCmdLine;
320 while (pszChar) {
321 while (strchr(szCmdLine, '\"'))
322 remove_first_occurence_of_character("\"", szCmdLine);
[1247]323 if (*pszChar == ' ') { //test at every space for the end of the filename
[920]324 *pszChar = '\0';
[1439]325 ret = xDosQueryAppType(szCmdLine, &ulAppType);
[1394]326 if (!ret) {
[1398]327 strcat(szCmdLine, PCSZ_DOTEXE);
[1394]328 break;
329 }
330 else {
[1398]331 strcat(szCmdLine, PCSZ_DOTCOM);
[1439]332 ret = xDosQueryAppType(szCmdLine, &ulAppType);
[1394]333 if (ret) {
334 offset = strrchr(szCmdLine, '.' );
335 *offset = 0;
[1398]336 strcat(szCmdLine, PCSZ_DOTCMD);
[1750]337 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
[1394]338 ret = 0;
339 break;
340 }
341 else {
342 *offset = 0;
[1398]343 strcat(szCmdLine, PCSZ_DOTBAT);
[1750]344 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
[1394]345 ret = 0;
346 break;
347 }
348 else {
349 *offset = 0;
[1398]350 strcat(szCmdLine, PCSZ_DOTBTM);
[1750]351 if (!SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
[1394]352 ret = 0;
353 break;
354 }
355 }
356 }
357 }
358 else
359 break;
360 }
[920]361 }
362 strcpy(szCmdLine, pszCmdLine_);
363 pszChar++;
364 }
365 }
366 if (!ret){
[1394]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);
[920]381 }
[1248]382 else { // fail if no extension can be found runemf2 requires one
[1394]383 ret = saymsg(MB_OK,
384 HWND_DESKTOP,
385 NullStr,
386 GetPString(IDS_PROGRAMNOTEXE2TEXT),
387 pszCmdLine_);
388 fCancelAction = TRUE;
389 pszNewCmdLine = pszCmdLine_;
[920]390 }
391 }
[1699]392 else { // file has a nonstandard extension for executable
[920]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);
[1750]411 if (SearchPathForFile(PCSZ_PATH, szCmdLine, NULL)) {
[920]412 ret = saymsg(MB_YESNO,
413 HWND_DESKTOP,
414 NullStr,
415 GetPString(IDS_PROGRAMNOTFOUNDTEXT),
416 pszCmdLine_);
417 if (ret == MBID_YES) {
[1394]418 if (szArgs[0] != ' ')
419 strcat(pszNewCmdLine, " ");
420 strcat(pszNewCmdLine, szArgs);
[920]421 }
422 else {
423 fCancelAction = TRUE;
424 pszWorkBuf = pszCmdLine_;
425 }
426 }
[1248]427 else {
428 ret = saymsg(MB_YESNOCANCEL,
[1394]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 }
[920]442 }
443 }
444 }
[1039]445 free(szArgs);
446 free(szCmdLine);
[920]447 return pszWorkBuf;
448}
449
[907]450#pragma alloc_text(PATHUTIL,BldFullPathName)
451#pragma alloc_text(PATHUTIL,BldQuotedFileName)
452#pragma alloc_text(PATHUTIL,BldQuotedFullPathName)
[920]453#pragma alloc_text(PATHUTIL,NormalizeCmdLine)
[1761]454#pragma alloc_text(PATHUTIL,IsAbsolutePath)
Note: See TracBrowser for help on using the repository browser.