Small fixes.
[wine] / dlls / shell32 / shellpath.c
1 /*
2  * Path Functions
3  *
4  * Many of this functions are in SHLWAPI.DLL also
5  *
6  */
7 #include <string.h>
8 #include <ctype.h>
9 #include "debugtools.h"
10 #include "winnls.h"
11 #include "winversion.h"
12 #include "winreg.h"
13 #include "crtdll.h"
14
15 #include "shlobj.h"
16 #include "shell32_main.h"
17 #include "windef.h"
18
19 DEFAULT_DEBUG_CHANNEL(shell)
20
21 /*************************************************************************
22  * PathIsRoot [SHELL32.29]
23  */
24 BOOL WINAPI PathIsRootA(LPCSTR x)
25 {       TRACE("%s\n",x);
26         if (*(x+1)==':' && *(x+2)=='\\')                /* "X:\" */
27           return 1;
28         if (*x=='\\')           /* "\" */
29           return 0;
30         if (x[0]=='\\' && x[1]=='\\')           /* UNC "\\<xx>\" */
31         { int   foundbackslash = 0;
32           x=x+2;
33           while (*x)
34           { if (*x++=='\\')
35               foundbackslash++;
36           }
37           if (foundbackslash<=1)        /* max 1 \ more ... */
38             return 1;
39         }
40         return 0;
41 }
42 BOOL WINAPI PathIsRootW(LPCWSTR x) 
43 {       TRACE("%s\n",debugstr_w(x));
44         if (*(x+1)==':' && *(x+2)=='\\')                /* "X:\" */
45           return 1;
46         if (*x == (WCHAR) '\\')         /* "\" */
47           return 0;
48         if (x[0]==(WCHAR)'\\' && x[1]==(WCHAR)'\\')     /* UNC "\\<xx>\" */
49         { int   foundbackslash = 0;
50           x=x+2;
51           while (*x) 
52           { if (*x++==(WCHAR)'\\')
53               foundbackslash++;
54           }
55           if (foundbackslash<=1)        /* max 1 \ more ... */
56             return 1;
57         }
58         return 0;
59 }
60 BOOL WINAPI PathIsRootAW(LPCVOID x) 
61 {       if (VERSION_OsIsUnicode())
62           return PathIsRootW(x);
63         return PathIsRootA(x);
64
65 }
66 /*************************************************************************
67  * PathBuildRoot [SHELL32.30]
68  */
69 LPSTR WINAPI PathBuildRootA(LPSTR root,BYTE drive) {
70   TRACE("%p %i\n",root, drive);
71         strcpy(root,"A:\\");
72         root[0]+=drive;
73         return root;
74 }
75
76 /*************************************************************************
77  * PathFindExtension [SHELL32.31]
78  *
79  * NOTES
80  *     returns pointer to last . in last pathcomponent or at \0.
81  */
82 LPCSTR WINAPI PathFindExtensionA(LPCSTR path) 
83 {       LPCSTR   lastpoint = NULL;
84         TRACE("%p %s\n",path,path);
85         while (*path) 
86         { if (*path=='\\'||*path==' ')
87             lastpoint=NULL;
88           if (*path=='.')
89             lastpoint=path;
90           path++;
91         }
92         return lastpoint?lastpoint:path;
93 }
94 LPCWSTR WINAPI PathFindExtensionW(LPCWSTR path) 
95 {       LPCWSTR   lastpoint = NULL;
96         TRACE("(%p %s)\n",path,debugstr_w(path));
97         while (*path)
98         { if (*path==(WCHAR)'\\'||*path==(WCHAR)' ')
99             lastpoint=NULL;
100           if (*path==(WCHAR)'.')
101             lastpoint=path;
102           path++;
103         }
104         return lastpoint?lastpoint:path;
105 }
106 LPCVOID WINAPI PathFindExtensionAW(LPCVOID path) 
107 {       if (VERSION_OsIsUnicode())
108           return PathFindExtensionW(path);
109         return PathFindExtensionA(path);
110
111 }
112
113 /*************************************************************************
114  * PathAddBackslash [SHELL32.32]
115  * 
116  * NOTES
117  *     append \ if there is none
118  */
119 LPSTR WINAPI PathAddBackslashA(LPSTR path)
120 {       int len;
121         TRACE("%p->%s\n",path,path);
122
123         len = strlen(path);
124         if (len && path[len-1]!='\\') 
125         { path[len]  = '\\';
126           path[len+1]= 0x00;
127           return path+len+1;
128         }
129         return path+len;
130 }
131 LPWSTR WINAPI PathAddBackslashW(LPWSTR path)
132 {       int len;
133         TRACE("%p->%s\n",path,debugstr_w(path));
134
135         len = CRTDLL_wcslen(path);
136         if (len && path[len-1]!=(WCHAR)'\\') 
137         { path[len]  = (WCHAR)'\\';
138           path[len+1]= 0x00;
139           return path+len+1;
140         }
141         return path+len;
142 }
143 LPVOID WINAPI PathAddBackslashAW(LPVOID path)
144 {       if(VERSION_OsIsUnicode())
145           return PathAddBackslashW(path);
146         return PathAddBackslashA(path);
147 }
148
149 /*************************************************************************
150  * PathRemoveBlanks [SHELL32.33]
151  * 
152  * NOTES
153  *     remove spaces from beginning and end of passed string
154  */
155 LPSTR WINAPI PathRemoveBlanksA(LPSTR str)
156 {       LPSTR x = str;
157         TRACE("%s\n",str);
158         while (*x==' ') x++;
159         if (x!=str)
160           strcpy(str,x);
161         if (!*str)
162           return str;
163         x=str+strlen(str)-1;
164         while (*x==' ')
165           x--;
166         if (*x==' ')
167           *x='\0';
168         return x;
169 }
170 LPWSTR WINAPI PathRemoveBlanksW(LPWSTR str)
171 {       LPWSTR x = str;
172         TRACE("%s\n",debugstr_w(str));
173         while (*x==' ') x++;
174         if (x!=str)
175           CRTDLL_wcscpy(str,x);
176         if (!*str)
177           return str;
178         x=str+CRTDLL_wcslen(str)-1;
179         while (*x==' ')
180           x--;
181         if (*x==' ')
182           *x='\0';
183         return x;
184 }
185 LPVOID WINAPI PathRemoveBlanksAW(LPVOID str)
186 {       if(VERSION_OsIsUnicode())
187           return PathRemoveBlanksW(str);
188         return PathRemoveBlanksA(str);
189 }
190
191
192
193 /*************************************************************************
194  * PathFindFilename [SHELL32.34]
195  * 
196  * NOTES
197  *     basename(char *fn);
198  */
199 LPCSTR WINAPI PathFindFilenameA(LPCSTR aptr)
200 {       LPCSTR aslash;
201         aslash = aptr;
202
203         TRACE("%s\n",aslash);
204         while (aptr[0]) 
205         { if (((aptr[0]=='\\') || (aptr[0]==':')) && aptr[1] && aptr[1]!='\\')
206               aslash = aptr+1;
207           aptr++;
208         }
209         return aslash;
210
211 }
212 LPCWSTR WINAPI PathFindFilenameW(LPCWSTR wptr)
213 {       LPCWSTR wslash;
214         wslash = wptr;
215
216         TRACE("%s\n",debugstr_w(wslash));
217         while (wptr[0]) 
218         { if (((wptr[0]=='\\') || (wptr[0]==':')) && wptr[1] && wptr[1]!='\\')
219             wslash = wptr+1;
220           wptr++;
221         }
222         return wslash;  
223 }
224 LPCVOID WINAPI PathFindFilenameAW(LPCVOID fn)
225 {
226         if(VERSION_OsIsUnicode())
227           return PathFindFilenameW(fn);
228         return PathFindFilenameA(fn);
229 }
230
231 /*************************************************************************
232  * PathRemoveFileSpec [SHELL32.35]
233  * 
234  * NOTES
235  *     bool getpath(char *pathname); truncates passed argument to a valid path
236  *     returns if the string was modified or not.
237  *     "\foo\xx\foo"-> "\foo\xx"
238  *     "\" -> "\"
239  *     "a:\foo" -> "a:\"
240  */
241 DWORD WINAPI PathRemoveFileSpecA(LPSTR fn) {
242         LPSTR   x,cutplace;
243   TRACE("%s\n",fn);
244         if (!fn[0])
245                 return 0;
246         x=fn;
247         cutplace = fn;
248         while (*x) {
249                 if (*x=='\\') {
250                         cutplace=x++;
251                         continue;
252                 }
253                 if (*x==':') {
254                         x++;
255                         if (*x=='\\')
256                                 cutplace=++x;
257                         continue; /* already x++ed */
258                 }
259                 x++;
260         }
261         if (!*cutplace)
262                 return 0;
263         if (cutplace==fn) {
264                 if (fn[0]=='\\') {
265                         if (!fn[1])
266                                 return 0;
267                         fn[0]='\0';
268                         return 1;
269                 }
270         }
271         *cutplace='\0';
272         return 1;
273 }
274
275 /*************************************************************************
276  * PathAppend [SHELL32.36]
277  * 
278  * NOTES
279  *     concat_paths(char*target,const char*add);
280  *     concats "target\\add" and writes them to target
281  */
282 LPSTR WINAPI PathAppendA(LPSTR x1,LPSTR x2) {
283   TRACE("%s %s\n",x1,x2);
284   while (x2[0]=='\\') x2++;
285   return PathCombineA(x1,x1,x2);
286 }
287
288 /*************************************************************************
289  * PathCombine [SHELL32.37]
290  * 
291  * NOTES
292  *  if lpszFile='.' skip it
293  *  szDest can be equal to lpszFile. Thats why we use sTemp
294  */
295 LPSTR WINAPI PathCombineA(LPSTR szDest, LPCSTR lpszDir, LPCSTR lpszFile) 
296 {       char sTemp[MAX_PATH];
297         TRACE("%p %p->%s %p->%s\n",szDest, lpszDir, lpszDir, lpszFile, lpszFile);
298         
299         
300         if (!lpszFile || !lpszFile[0] || (lpszFile[0]=='.' && !lpszFile[1]) ) 
301         { strcpy(szDest,lpszDir);
302           return szDest;
303         }
304
305         /*  if lpszFile is a complete path don't care about lpszDir */
306         if (PathIsRootA(lpszFile))
307         { strcpy(szDest,lpszFile);
308         }
309         else
310         { strcpy(sTemp,lpszDir);
311           PathAddBackslashA(sTemp);
312           strcat(sTemp,lpszFile);
313           strcpy(szDest,sTemp);
314         }
315         return szDest;
316 }
317 LPWSTR WINAPI PathCombineW(LPWSTR szDest, LPCWSTR lpszDir, LPCWSTR lpszFile) 
318 {       WCHAR sTemp[MAX_PATH];
319         TRACE("%p %p->%s %p->%s\n",szDest, lpszDir, debugstr_w(lpszDir),
320                          lpszFile, debugstr_w(lpszFile));
321         
322         
323         if (!lpszFile || !lpszFile[0] || (lpszFile[0]==(WCHAR)'.' && !lpszFile[1]) ) 
324         { CRTDLL_wcscpy(szDest,lpszDir);
325           return szDest;
326         }
327
328         /*  if lpszFile is a complete path don't care about lpszDir */
329         if (PathIsRootW(lpszFile))
330         { CRTDLL_wcscpy(szDest,lpszFile);
331         }
332         else
333         { CRTDLL_wcscpy(sTemp,lpszDir);
334           PathAddBackslashW(sTemp);
335           CRTDLL_wcscat(sTemp,lpszFile);
336           CRTDLL_wcscpy(szDest,sTemp);
337         }
338         return szDest;
339 }
340 LPVOID WINAPI PathCombineAW(LPVOID szDest, LPCVOID lpszDir, LPCVOID lpszFile) 
341 {       if (VERSION_OsIsUnicode())
342           return PathCombineW( szDest, lpszDir, lpszFile );
343         return PathCombineA( szDest, lpszDir, lpszFile );
344 }
345
346 /*************************************************************************
347  * PathIsUNC [SHELL32.39]
348  * 
349  * NOTES
350  *     PathIsUNC(char*path);
351  */
352 BOOL WINAPI PathIsUNCA(LPCSTR path) 
353 {       TRACE("%s\n",path);
354
355         if ((path[0]=='\\') && (path[1]=='\\'))
356           return TRUE;
357         return FALSE;
358 }
359 BOOL WINAPI PathIsUNCW(LPCWSTR path) 
360 {       TRACE("%s\n",debugstr_w(path));
361
362         if ((path[0]=='\\') && (path[1]=='\\'))
363           return TRUE;
364         return FALSE;
365 }
366 BOOL WINAPI PathIsUNCAW (LPCVOID path)
367 {       if (VERSION_OsIsUnicode())
368           return PathIsUNCW( path );
369         return PathIsUNCA( path );  
370 }
371 /*************************************************************************
372  *  PathIsRelativ [SHELL32.40]
373  * 
374  */
375 BOOL WINAPI PathIsRelativeA (LPCSTR path)
376 {       TRACE("path=%s\n",path);
377
378         if (path && (path[0]!='\\' && path[1]==':'))
379           return TRUE;
380         return FALSE;    
381 }
382 BOOL WINAPI PathIsRelativeW (LPCWSTR path)
383 {       TRACE("path=%s\n",debugstr_w(path));
384
385         if (path && (path[0]!='\\' && path[1]==':'))
386           return TRUE;
387         return FALSE;    
388 }
389 BOOL WINAPI PathIsRelativeAW (LPCVOID path)
390 {       if (VERSION_OsIsUnicode())
391           return PathIsRelativeW( path );
392         return PathIsRelativeA( path );  
393 }
394 /*************************************************************************
395  *  PathIsExe [SHELL32.43]
396  * 
397  */
398 BOOL WINAPI PathIsExeA (LPCSTR path)
399 {       FIXME("path=%s\n",path);
400         return FALSE;
401 }
402 BOOL WINAPI PathIsExeW (LPCWSTR path)
403 {       FIXME("path=%s\n",debugstr_w(path));
404         return FALSE;
405 }
406 BOOL WINAPI PathIsExeAW (LPCVOID path)
407 {       if (VERSION_OsIsUnicode())
408           return PathIsExeW (path);
409         return PathIsExeA(path);
410 }
411
412 /*************************************************************************
413  * PathFileExists [SHELL32.45]
414  * 
415  * NOTES
416  *     file_exists(char *fn);
417  */
418 BOOL WINAPI PathFileExistsA(LPSTR fn) {
419   TRACE("%s\n",fn);
420    if (GetFileAttributesA(fn)==-1)
421         return FALSE;
422     else
423         return TRUE;
424 }
425 /*************************************************************************
426  * PathMatchSingleMask
427  * 
428  * NOTES
429  *     internal (used by PathMatchSpec)
430  */
431 static BOOL PathMatchSingleMaskA(LPCSTR name, LPCSTR mask)
432 {
433   while (*name && *mask && *mask!=';') {
434     if (*mask=='*') {
435       do {
436         if (PathMatchSingleMaskA(name,mask+1)) return 1;  /* try substrings */
437       } while (*name++);
438       return 0;
439     }
440     if (toupper(*mask)!=toupper(*name) && *mask!='?') return 0;
441     name++;
442     mask++;
443   }
444   if (!*name) {
445     while (*mask=='*') mask++;
446     if (!*mask || *mask==';') return 1;
447   }
448   return 0;
449 }
450 static BOOL PathMatchSingleMaskW(LPCWSTR name, LPCWSTR mask)
451 {
452   while (*name && *mask && *mask!=';') {
453     if (*mask=='*') {
454       do {
455         if (PathMatchSingleMaskW(name,mask+1)) return 1;  /* try substrings */
456       } while (*name++);
457       return 0;
458     }
459     if (towupper(*mask)!=towupper(*name) && *mask!='?') return 0;
460     name++;
461     mask++;
462   }
463   if (!*name) {
464     while (*mask=='*') mask++;
465     if (!*mask || *mask==';') return 1;
466   }
467   return 0;
468 }
469 /*************************************************************************
470  * PathMatchSpec [SHELL32.46]
471  * 
472  * NOTES
473  *     used from COMDLG32
474  */
475 BOOL WINAPI PathMatchSpecA(LPCSTR name, LPCSTR mask) 
476 {
477   TRACE("%s %s\n",name,mask);
478
479   if (!lstrcmpA( mask, "*.*" )) return 1;   /* we don't require a period */
480
481   while (*mask) {
482     if (PathMatchSingleMaskA(name,mask)) return 1;    /* helper function */
483     while (*mask && *mask!=';') mask++;
484     if (*mask==';') {
485       mask++;
486       while (*mask==' ') mask++;      /*  masks may be separated by "; " */
487           }
488         }
489   return 0;
490 }
491 BOOL WINAPI PathMatchSpecW(LPCWSTR name, LPCWSTR mask) 
492 {
493   WCHAR stemp[4];
494   TRACE("%s %s\n",debugstr_w(name),debugstr_w(mask));
495
496         lstrcpyAtoW(stemp,"*.*");       
497   if (!lstrcmpW( mask, stemp )) return 1;   /* we don't require a period */
498
499   while (*mask) {
500     if (PathMatchSingleMaskW(name,mask)) return 1;    /* helper function */
501     while (*mask && *mask!=';') mask++;
502     if (*mask==';') {
503       mask++;
504       while (*mask==' ') mask++;       /* masks may be separated by "; " */
505           }
506         }
507   return 0;
508 }
509 BOOL WINAPI PathMatchSpecAW(LPVOID name, LPVOID mask) 
510 {       if (VERSION_OsIsUnicode())
511           return PathMatchSpecW( name, mask );
512         return PathMatchSpecA( name, mask );
513 }
514 /*************************************************************************
515  * PathSetDlgItemPathAW [SHELL32.48]
516  * NOTES
517  *  use PathCompactPath to make sure, the path fits into the control
518  */
519
520 BOOL WINAPI PathSetDlgItemPathA(HWND hDlg, int id, LPCSTR pszPath) 
521 {       TRACE("%x %x %s\n",hDlg, id, pszPath);
522         return SetDlgItemTextA(hDlg, id, pszPath);
523 }
524 BOOL WINAPI PathSetDlgItemPathW(HWND hDlg, int id, LPCWSTR pszPath) 
525 {       TRACE("%x %x %s\n",hDlg, id, debugstr_w(pszPath));
526         return SetDlgItemTextW(hDlg, id, pszPath);
527 }
528 BOOL WINAPI PathSetDlgItemPathAW(HWND hDlg, int id, LPCVOID pszPath) 
529 {       if (VERSION_OsIsUnicode())
530           return PathSetDlgItemPathW(hDlg, id, pszPath);
531         return PathSetDlgItemPathA(hDlg, id, pszPath);
532 }
533
534 /*************************************************************************
535  * PathQualifyAW [SHELL32.49]
536  */
537
538 BOOL WINAPI PathQualifyA(LPCSTR pszPath) 
539 {       FIXME("%s\n",pszPath);
540         return 0;
541 }
542 BOOL WINAPI PathQualifyW(LPCWSTR pszPath) 
543 {       FIXME("%s\n",debugstr_w(pszPath));
544         return 0;
545 }
546 BOOL WINAPI PathQualifyAW(LPCVOID pszPath) 
547 {       if (VERSION_OsIsUnicode())
548           return PathQualifyW(pszPath);
549         return PathQualifyA(pszPath);
550 }
551
552 /*************************************************************************
553  * PathResolve [SHELL32.51]
554  */
555 DWORD WINAPI PathResolve(LPCSTR s,DWORD x2,DWORD x3) {
556         FIXME("(%s,0x%08lx,0x%08lx),stub!\n",s,x2,x3);
557         return 0;
558 }
559
560 /*************************************************************************
561  * PathGetArgs [SHELL32.52]
562  *
563  * NOTES
564  *     look for next arg in string. handle "quoted" strings
565  *     returns pointer to argument *AFTER* the space. Or to the \0.
566  */
567 LPCSTR WINAPI PathGetArgsA(LPCSTR cmdline) 
568 {       BOOL    qflag = FALSE;
569
570         TRACE("%s\n",cmdline);
571
572         while (*cmdline) 
573         { if ((*cmdline==' ') && !qflag)
574             return cmdline+1;
575           if (*cmdline=='"')
576             qflag=!qflag;
577           cmdline++;
578         }
579         return cmdline;
580
581 }
582 LPCWSTR WINAPI PathGetArgsW(LPCWSTR cmdline) 
583 {       BOOL    qflag = FALSE;
584
585         TRACE("%s\n",debugstr_w(cmdline));
586
587         while (*cmdline) 
588         { if ((*cmdline==' ') && !qflag)
589             return cmdline+1;
590           if (*cmdline=='"')
591             qflag=!qflag;
592           cmdline++;
593         }
594         return cmdline;
595 }
596 LPCVOID WINAPI PathGetArgsAW(LPVOID cmdline) 
597 {       if (VERSION_OsIsUnicode())
598           return PathGetArgsW(cmdline);
599         return PathGetArgsA(cmdline);
600 }
601 /*************************************************************************
602  * PathQuoteSpaces [SHELL32.55]
603  * 
604  * NOTES
605  *     basename(char *fn);
606  */
607 LPSTR WINAPI PathQuoteSpacesA(LPCSTR aptr)
608 {       FIXME("%s\n",aptr);
609         return 0;
610
611 }
612 LPWSTR WINAPI PathQuoteSpacesW(LPCWSTR wptr)
613 {       FIXME("%s\n",debugstr_w(wptr));
614         return 0;       
615 }
616 LPVOID WINAPI PathQuoteSpacesAW (LPCVOID fn)
617 {       if(VERSION_OsIsUnicode())
618           return PathQuoteSpacesW(fn);
619         return PathQuoteSpacesA(fn);
620 }
621
622
623 /*************************************************************************
624  * PathUnquoteSpaces [SHELL32.56]
625  * 
626  * NOTES
627  *     unquote string (remove ")
628  */
629 VOID WINAPI PathUnquoteSpacesA(LPSTR str) 
630 {       DWORD      len = lstrlenA(str);
631         TRACE("%s\n",str);
632         if (*str!='"')
633           return;
634         if (str[len-1]!='"')
635           return;
636         str[len-1]='\0';
637         lstrcpyA(str,str+1);
638         return;
639 }
640 VOID WINAPI PathUnquoteSpacesW(LPWSTR str) 
641 {       DWORD len = CRTDLL_wcslen(str);
642
643         TRACE("%s\n",debugstr_w(str));
644
645         if (*str!='"')
646           return;
647         if (str[len-1]!='"')
648           return;
649         str[len-1]='\0';
650         CRTDLL_wcscpy(str,str+1);
651         return;
652 }
653 VOID WINAPI PathUnquoteSpacesAW(LPVOID str) 
654 {
655         if(VERSION_OsIsUnicode())
656           PathUnquoteSpacesW(str);
657         else
658           PathUnquoteSpacesA(str);
659 }
660
661
662 /*************************************************************************
663  * PathGetDriveNumber32 [SHELL32.57]
664  *
665  */
666 HRESULT WINAPI PathGetDriveNumber(LPSTR u)
667 {       FIXME("%s stub\n",debugstr_a(u));
668         return 0;
669 }
670
671 /*************************************************************************
672  * PathYetAnotherMakeUniqueName [SHELL32.75]
673  * 
674  * NOTES
675  *     exported by ordinal
676  */
677 BOOL WINAPI PathYetAnotherMakeUniqueNameA(LPDWORD x,LPDWORD y) {
678     FIXME("(%p,%p):stub.\n",x,y);
679     return TRUE;
680 }
681
682 /*************************************************************************
683  * IsLFNDrive [SHELL32.119]
684  * 
685  * NOTES
686  *     exported by ordinal Name
687  */
688 BOOL WINAPI IsLFNDriveA(LPCSTR path) {
689     DWORD       fnlen;
690
691     if (!GetVolumeInformationA(path,NULL,0,NULL,&fnlen,NULL,NULL,0))
692         return FALSE;
693     return fnlen>12;
694 }
695 /*************************************************************************
696  * PathFindOnPath [SHELL32.145]
697  */
698 BOOL WINAPI PathFindOnPathA(LPSTR sFile, LPCSTR sOtherDirs)
699 {       FIXME("%s %s\n",sFile, sOtherDirs);
700         return FALSE;
701 }
702 BOOL WINAPI PathFindOnPathW(LPWSTR sFile, LPCWSTR sOtherDirs)
703 {       FIXME("%s %s\n",debugstr_w(sFile), debugstr_w(sOtherDirs));
704         return FALSE;
705 }
706 BOOL WINAPI PathFindOnPathAW(LPVOID sFile, LPCVOID sOtherDirs)
707 {       if (VERSION_OsIsUnicode())
708           return PathFindOnPathW(sFile, sOtherDirs);
709         return PathFindOnPathA(sFile, sOtherDirs);
710 }
711
712 /*************************************************************************
713  * PathGetExtension [SHELL32.158]
714  *
715  * NOTES
716  *     exported by ordinal
717  */
718 LPCSTR WINAPI PathGetExtensionA(LPCSTR path,DWORD y,DWORD z)
719 {       TRACE("(%s,%08lx,%08lx)\n",path,y,z);
720         path = PathFindExtensionA(path);
721         return *path?(path+1):path;
722 }
723 LPCWSTR WINAPI PathGetExtensionW(LPCWSTR path,DWORD y,DWORD z)
724 {       TRACE("(%s, %08lx, %08lx)\n",debugstr_w(path),y,z);
725         path = PathFindExtensionW(path);
726         return *path?(path+1):path;
727 }
728 LPCVOID WINAPI PathGetExtensionAW(LPCVOID path,DWORD y,DWORD z) 
729 {       if (VERSION_OsIsUnicode())
730           return PathGetExtensionW(path,y,z);
731         return PathGetExtensionA(path,y,z);
732 }
733
734 /*************************************************************************
735  * PathCleanupSpec                              [SHELL32.171]
736  *
737  */
738 DWORD WINAPI PathCleanupSpecA(LPSTR x, LPSTR y)
739 {
740         FIXME("(%p %s, %p %s) stub\n",x,debugstr_a(x),y,debugstr_a(y));
741         return TRUE;
742 }
743
744 DWORD WINAPI PathCleanupSpecW(LPWSTR x, LPWSTR y)
745 {
746         FIXME("(%p %s, %p %s) stub\n",x,debugstr_w(x),y,debugstr_w(y));
747         return TRUE;
748 }
749
750 DWORD WINAPI PathCleanupSpecAW (LPVOID x, LPVOID y)
751 {
752         if (VERSION_OsIsUnicode())
753           return PathCleanupSpecW(x,y);
754         return PathCleanupSpecA(x,y);
755 }
756
757 /*************************************************************************
758  * SheGetDirW [SHELL32.281]
759  *
760  */
761 HRESULT WINAPI SheGetDirW(LPWSTR u, LPWSTR v)
762 {       FIXME("%p %p stub\n",u,v);
763         return 0;
764 }
765
766 /*************************************************************************
767  * SheChangeDirW [SHELL32.274]
768  *
769  */
770 HRESULT WINAPI SheChangeDirW(LPWSTR u)
771 {       FIXME("(%s),stub\n",debugstr_w(u));
772         return 0;
773 }
774
775 /*************************************************************************
776 *       PathProcessCommand      [SHELL32.653]
777 */
778 HRESULT WINAPI PathProcessCommandA (LPSTR lpCommand, LPSTR v, DWORD w, DWORD x)
779 {
780         FIXME("%p(%s) %p 0x%04lx 0x%04lx stub\n",
781         lpCommand, lpCommand, v, w,x );
782         lstrcpyA(v, lpCommand);
783         return 0;
784 }
785
786 HRESULT WINAPI PathProcessCommandW (LPWSTR lpCommand, LPSTR v, DWORD w, DWORD x)
787 {
788         FIXME("(%p %s, %p, 0x%04lx, 0x%04lx) stub\n",
789         lpCommand, debugstr_w(lpCommand), v, w,x );
790         return 0;
791 }
792
793 HRESULT WINAPI PathProcessCommandAW (LPVOID lpCommand, LPSTR v, DWORD w, DWORD x)
794 {
795         if (VERSION_OsIsUnicode())
796           return PathProcessCommandW(lpCommand, v, w, x);
797         return PathProcessCommandA(lpCommand, v, w, x);
798 }
799
800 /*************************************************************************
801  * SHGetSpecialFolderPath [SHELL32.175]
802  * 
803  * converts csidl to path
804  * 
805  */
806  
807 static char * szSHFolders = "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders";
808
809 BOOL WINAPI SHGetSpecialFolderPathA (
810         HWND hwndOwner,
811         LPSTR szPath,
812         DWORD csidl,
813         BOOL bCreate)
814 {
815         CHAR    szValueName[MAX_PATH], szDefaultPath[MAX_PATH];
816         HKEY    hRootKey, hKey;
817         BOOL    bRelative = TRUE;
818         DWORD   dwType, dwDisp, dwPathLen = MAX_PATH;
819
820         TRACE("0x%04x,%p,csidl=%lu,0x%04x\n", hwndOwner,szPath,csidl,bCreate);
821
822         /* build default values */
823         switch(csidl)
824         {
825           case CSIDL_APPDATA:
826             hRootKey = HKEY_CURRENT_USER;
827             strcpy (szValueName, "AppData");
828             strcpy (szDefaultPath, "AppData");
829             break;
830
831           case CSIDL_COOKIES:
832             hRootKey = HKEY_CURRENT_USER;
833             strcpy (szValueName, "Cookies");
834             strcpy(szDefaultPath, "Cookies");
835             break;
836
837           case CSIDL_DESKTOPDIRECTORY:
838             hRootKey = HKEY_CURRENT_USER;
839             strcpy(szValueName, "Desktop");
840             strcpy(szDefaultPath, "Desktop");
841             break;
842
843           case CSIDL_COMMON_DESKTOPDIRECTORY:
844             hRootKey = HKEY_LOCAL_MACHINE;
845             strcpy(szValueName, "Common Desktop");
846             strcpy(szDefaultPath, "Desktop");
847             break;
848
849           case CSIDL_FAVORITES:
850             hRootKey = HKEY_CURRENT_USER;
851             strcpy(szValueName, "Favorites");
852             strcpy(szDefaultPath, "Favorites");
853             break;
854
855           case CSIDL_FONTS:
856             hRootKey = HKEY_CURRENT_USER;
857             strcpy(szValueName, "Fonts");
858             strcpy(szDefaultPath, "Fonts");
859             break;
860
861           case CSIDL_HISTORY:
862             hRootKey = HKEY_CURRENT_USER;
863             strcpy(szValueName, "History");
864             strcpy(szDefaultPath, "History");
865             break;
866
867           case CSIDL_NETHOOD:
868             hRootKey = HKEY_CURRENT_USER;
869             strcpy(szValueName, "NetHood");
870             strcpy(szDefaultPath, "NetHood");
871             break;
872
873           case CSIDL_INTERNET_CACHE:
874             hRootKey = HKEY_CURRENT_USER;
875             strcpy(szValueName, "Cache");
876             strcpy(szDefaultPath, "Temporary Internet Files");
877             break;
878
879           case CSIDL_PERSONAL:
880             hRootKey = HKEY_CURRENT_USER;
881             strcpy(szValueName, "Personal");
882             strcpy(szDefaultPath, "My Own Files");
883             bRelative = FALSE;
884             break;
885
886           case CSIDL_PRINTHOOD:
887             hRootKey = HKEY_CURRENT_USER;
888             strcpy(szValueName, "PrintHood");
889             strcpy(szDefaultPath, "PrintHood");
890             break;
891
892           case CSIDL_PROGRAMS:
893             hRootKey = HKEY_CURRENT_USER;
894             strcpy(szValueName, "Programs");
895             strcpy(szDefaultPath, "StartMenu\\Programs");
896             break;
897
898           case CSIDL_COMMON_PROGRAMS:
899             hRootKey = HKEY_LOCAL_MACHINE;
900             strcpy(szValueName, "Common Programs");
901             strcpy(szDefaultPath, "");
902             break;
903
904           case CSIDL_RECENT:
905             hRootKey = HKEY_CURRENT_USER;
906             strcpy(szValueName, "Recent");
907             strcpy(szDefaultPath, "Recent");
908             break;
909
910           case CSIDL_SENDTO:
911             hRootKey = HKEY_CURRENT_USER;
912             strcpy(szValueName, "SendTo");
913             strcpy(szDefaultPath, "SendTo");
914             break;
915
916           case CSIDL_STARTMENU:
917             hRootKey = HKEY_CURRENT_USER;
918             strcpy(szValueName, "StartMenu");
919             strcpy(szDefaultPath, "StartMenu");
920             break;
921
922           case CSIDL_COMMON_STARTMENU:
923             hRootKey = HKEY_LOCAL_MACHINE;
924             strcpy(szValueName, "Common StartMenu");
925             strcpy(szDefaultPath, "StartMenu");
926             break;
927
928           case CSIDL_STARTUP:
929             hRootKey = HKEY_CURRENT_USER;
930             strcpy(szValueName, "Startup");
931             strcpy(szDefaultPath, "StartMenu\\Programs\\Startup");
932             break;
933
934           case CSIDL_COMMON_STARTUP:
935             hRootKey = HKEY_LOCAL_MACHINE;
936             strcpy(szValueName, "Common Startup");
937             strcpy(szDefaultPath, "StartMenu\\Programs\\Startup");
938             break;
939
940           case CSIDL_TEMPLATES:
941             hRootKey = HKEY_CURRENT_USER;
942             strcpy(szValueName, "Templates");
943             strcpy(szDefaultPath, "ShellNew");
944             break;
945
946           default:
947             ERR("folder unknown or not allowed\n");
948             return FALSE;
949         }
950
951         if (RegCreateKeyExA(hRootKey,szSHFolders,0,NULL,REG_OPTION_NON_VOLATILE,KEY_WRITE,NULL,&hKey,&dwDisp))
952         {
953           return FALSE;
954         }
955
956         if (RegQueryValueExA(hKey,szValueName,NULL,&dwType,(LPBYTE)szPath,&dwPathLen))
957         {
958           /* value not existing */
959           if (bRelative)
960           {
961             GetWindowsDirectoryA(szPath, MAX_PATH);
962             PathAddBackslashA(szPath);
963             strcat(szPath, szDefaultPath);
964           }
965           else
966           {
967             strcpy(szPath, szDefaultPath);
968           }
969           if (bCreate)
970           {
971             CreateDirectoryA(szPath,NULL);
972           }
973           RegSetValueExA(hKey,szValueName,0,REG_SZ,(LPBYTE)szPath,strlen(szPath)+1);
974         }
975         RegCloseKey(hKey);
976
977         return TRUE;
978 }
979 BOOL WINAPI SHGetSpecialFolderPathW (
980         HWND hwndOwner,
981         LPWSTR szPath,
982         DWORD csidl,
983         BOOL bCreate)
984 {
985         char szTemp[MAX_PATH];
986         
987         if (SHGetSpecialFolderPathA(hwndOwner, szTemp, csidl, bCreate))
988         {
989           lstrcpynAtoW(szPath, szTemp, MAX_PATH);
990         }
991
992         TRACE("0x%04x,%p,csidl=%lu,0x%04x\n", hwndOwner,szPath,csidl,bCreate);
993
994         return TRUE;
995 }
996 BOOL WINAPI SHGetSpecialFolderPathAW (
997         HWND hwndOwner,
998         LPVOID szPath,
999         DWORD csidl,
1000         BOOL bCreate)
1001
1002 {
1003         if (VERSION_OsIsUnicode())
1004           return SHGetSpecialFolderPathW (hwndOwner, szPath, csidl, bCreate);
1005         return SHGetSpecialFolderPathA (hwndOwner, szPath, csidl, bCreate);
1006 }
1007
1008 /* PathRemoveBackslash
1009  *
1010  * If the path ends in a backslash it is replaced by a NULL
1011  * and the address of the NULL is returned
1012  * Otherwise 
1013  * the address of the last character is returned.
1014  *
1015  */
1016
1017 LPSTR WINAPI PathRemoveBackslashA( LPSTR lpPath )
1018 {
1019         LPSTR p = lpPath;
1020         
1021         while (*lpPath) p = lpPath++;
1022         if ( *p == (CHAR)'\\') *p = (CHAR)'\0';
1023         return p;
1024 }
1025
1026 LPWSTR WINAPI PathRemoveBackslashW( LPWSTR lpPath )
1027 {
1028         LPWSTR p = lpPath;
1029         
1030         while (*lpPath); p = lpPath++;
1031         if ( *p == (WCHAR)'\\') *p = (WCHAR)'\0';
1032         return p;
1033 }
1034
1035 /*
1036    shlwapi functions that have found their way in because most of
1037    shlwapi is unimplemented and doesn't have a home.  
1038
1039    FIXME: move to a more appropriate file( when one exists )
1040 */
1041    
1042  /* SHGetValue: Gets a value from the registry */
1043
1044 DWORD WINAPI SHGetValueA(
1045     HKEY     hkey,
1046     LPCSTR   pSubKey,
1047     LPCSTR   pValue,
1048     LPDWORD  pwType,
1049     LPVOID   pvData,
1050     LPDWORD  pbData
1051     )
1052 {
1053     FIXME("(%p),stub!\n", pSubKey);
1054
1055         return ERROR_SUCCESS;  /* return success */
1056 }
1057
1058 DWORD WINAPI SHGetValueW(
1059     HKEY     hkey,
1060     LPCWSTR  pSubKey,
1061     LPCWSTR  pValue,
1062     LPDWORD  pwType,
1063     LPVOID   pvData,
1064     LPDWORD  pbData
1065     )
1066 {
1067     FIXME("(%p),stub!\n", pSubKey);
1068
1069         return ERROR_SUCCESS;  /* return success */
1070 }
1071
1072 /* gets a user-specific registry value. */
1073
1074 LONG WINAPI SHRegGetUSValueA(
1075     LPCSTR   pSubKey,
1076     LPCSTR   pValue,
1077     LPDWORD  pwType,
1078     LPVOID   pvData,
1079     LPDWORD  pbData,
1080     BOOL     fIgnoreHKCU,
1081     LPVOID   pDefaultData,
1082     DWORD    wDefaultDataSize
1083     )
1084 {
1085     FIXME("(%p),stub!\n", pSubKey);
1086
1087         return ERROR_SUCCESS;  /* return success */
1088 }
1089
1090 LONG WINAPI SHRegGetUSValueW(
1091     LPCWSTR  pSubKey,
1092     LPCWSTR  pValue,
1093     LPDWORD  pwType,
1094     LPVOID   pvData,
1095     LPDWORD  pbData,
1096     BOOL     flagIgnoreHKCU,
1097     LPVOID   pDefaultData,
1098     DWORD    wDefaultDataSize
1099     )
1100 {
1101     FIXME("(%p),stub!\n", pSubKey);
1102
1103         return ERROR_SUCCESS;  /* return success */
1104 }
1105   
1106