source: trunk/kitgen/8.x/blt/win/nmakehlp.c

Last change on this file was 175, checked in by demin, 12 years ago

initial commit

File size: 18.3 KB
Line 
1/*
2 * ----------------------------------------------------------------------------
3 * nmakehlp.c --
4 *
5 * This is used to fix limitations within nmake and the environment.
6 *
7 * Copyright (c) 2002 by David Gravereaux.
8 * Copyright (c) 2006 by Pat Thoyts
9 *
10 * See the file "license.terms" for information on usage and redistribution of
11 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
12 *
13 * ----------------------------------------------------------------------------
14 * RCS: @(#) $Id: nmakehlp.c,v 1.7 2008/06/18 11:01:42 patthoyts Exp $
15 * ----------------------------------------------------------------------------
16 */
17
18#define _CRT_SECURE_NO_DEPRECATE
19#include <windows.h>
20#define NO_SHLWAPI_GDI
21#define NO_SHLWAPI_STREAM
22#define NO_SHLWAPI_REG
23#include <shlwapi.h>
24#pragma comment (lib, "user32.lib")
25#pragma comment (lib, "kernel32.lib")
26#pragma comment (lib, "shlwapi.lib")
27#include <stdio.h>
28#include <math.h>
29
30/*
31 * This library is required for x64 builds with _some_ versions of MSVC
32 */
33#if defined(_M_IA64) || defined(_M_AMD64)
34#if _MSC_VER >= 1400 && _MSC_VER < 1500
35#pragma comment(lib, "bufferoverflowU")
36#endif
37#endif
38
39/* ISO hack for dumb VC++ */
40#ifdef _MSC_VER
41#define snprintf _snprintf
42#endif
43
44
45
46/* protos */
47
48int CheckForCompilerFeature(const char *option);
49int CheckForLinkerFeature(const char *option);
50int IsIn(const char *string, const char *substring);
51int GrepForDefine(const char *file, const char *string);
52int SubstituteFile(const char *substs, const char *filename);
53int QualifyPath(const char *path);
54const char * GetVersionFromFile(const char *filename, const char *match);
55DWORD WINAPI ReadFromPipe(LPVOID args);
56
57/* globals */
58
59#define CHUNK 25
60#define STATICBUFFERSIZE 1000
61typedef struct {
62 HANDLE pipe;
63 char buffer[STATICBUFFERSIZE];
64} pipeinfo;
65
66pipeinfo Out = {INVALID_HANDLE_VALUE, '\0'};
67pipeinfo Err = {INVALID_HANDLE_VALUE, '\0'};
68
69
70/*
71 * exitcodes: 0 == no, 1 == yes, 2 == error
72 */
73
74int
75main(
76 int argc,
77 char *argv[])
78{
79 char msg[300];
80 DWORD dwWritten;
81 int chars;
82
83 /*
84 * Make sure children (cl.exe and link.exe) are kept quiet.
85 */
86
87 SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
88
89 /*
90 * Make sure the compiler and linker aren't effected by the outside world.
91 */
92
93 SetEnvironmentVariable("CL", "");
94 SetEnvironmentVariable("LINK", "");
95
96 if (argc > 1 && *argv[1] == '-') {
97 switch (*(argv[1]+1)) {
98 case 'c':
99 if (argc != 3) {
100 chars = snprintf(msg, sizeof(msg) - 1,
101 "usage: %s -c <compiler option>\n"
102 "Tests for whether cl.exe supports an option\n"
103 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
104 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
105 &dwWritten, NULL);
106 return 2;
107 }
108 return CheckForCompilerFeature(argv[2]);
109 case 'l':
110 if (argc != 3) {
111 chars = snprintf(msg, sizeof(msg) - 1,
112 "usage: %s -l <linker option>\n"
113 "Tests for whether link.exe supports an option\n"
114 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
115 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
116 &dwWritten, NULL);
117 return 2;
118 }
119 return CheckForLinkerFeature(argv[2]);
120 case 'f':
121 if (argc == 2) {
122 chars = snprintf(msg, sizeof(msg) - 1,
123 "usage: %s -f <string> <substring>\n"
124 "Find a substring within another\n"
125 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
126 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
127 &dwWritten, NULL);
128 return 2;
129 } else if (argc == 3) {
130 /*
131 * If the string is blank, there is no match.
132 */
133
134 return 0;
135 } else {
136 return IsIn(argv[2], argv[3]);
137 }
138 case 'g':
139 if (argc == 2) {
140 chars = snprintf(msg, sizeof(msg) - 1,
141 "usage: %s -g <file> <string>\n"
142 "grep for a #define\n"
143 "exitcodes: integer of the found string (no decimals)\n",
144 argv[0]);
145 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
146 &dwWritten, NULL);
147 return 2;
148 }
149 return GrepForDefine(argv[2], argv[3]);
150 case 's':
151 if (argc == 2) {
152 chars = snprintf(msg, sizeof(msg) - 1,
153 "usage: %s -s <substitutions file> <file>\n"
154 "Perform a set of string map type substutitions on a file\n"
155 "exitcodes: 0\n",
156 argv[0]);
157 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
158 &dwWritten, NULL);
159 return 2;
160 }
161 return SubstituteFile(argv[2], argv[3]);
162 case 'V':
163 if (argc != 4) {
164 chars = snprintf(msg, sizeof(msg) - 1,
165 "usage: %s -V filename matchstring\n"
166 "Extract a version from a file:\n"
167 "eg: pkgIndex.tcl \"package ifneeded http\"",
168 argv[0]);
169 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
170 &dwWritten, NULL);
171 return 0;
172 }
173 printf("%s\n", GetVersionFromFile(argv[2], argv[3]));
174 return 0;
175 case 'Q':
176 if (argc != 3) {
177 chars = snprintf(msg, sizeof(msg) - 1,
178 "usage: %s -q path\n"
179 "Emit the fully qualified path\n"
180 "exitcodes: 0 == no, 1 == yes, 2 == error\n", argv[0]);
181 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars,
182 &dwWritten, NULL);
183 return 2;
184 }
185 return QualifyPath(argv[2]);
186 }
187 }
188 chars = snprintf(msg, sizeof(msg) - 1,
189 "usage: %s -c|-l|-f|-g|-V|-s|-Q ...\n"
190 "This is a little helper app to equalize shell differences between WinNT and\n"
191 "Win9x and get nmake.exe to accomplish its job.\n",
192 argv[0]);
193 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg, chars, &dwWritten, NULL);
194 return 2;
195}
196
197
198int
199CheckForCompilerFeature(
200 const char *option)
201{
202 STARTUPINFO si;
203 PROCESS_INFORMATION pi;
204 SECURITY_ATTRIBUTES sa;
205 DWORD threadID;
206 char msg[300];
207 BOOL ok;
208 HANDLE hProcess, h, pipeThreads[2];
209 char cmdline[100];
210
211 hProcess = GetCurrentProcess();
212
213 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
214 ZeroMemory(&si, sizeof(STARTUPINFO));
215 si.cb = sizeof(STARTUPINFO);
216 si.dwFlags = STARTF_USESTDHANDLES;
217 si.hStdInput = INVALID_HANDLE_VALUE;
218
219 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
220 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
221 sa.lpSecurityDescriptor = NULL;
222 sa.bInheritHandle = FALSE;
223
224 /*
225 * Create a non-inheritible pipe.
226 */
227
228 CreatePipe(&Out.pipe, &h, &sa, 0);
229
230 /*
231 * Dupe the write side, make it inheritible, and close the original.
232 */
233
234 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
235 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
236
237 /*
238 * Same as above, but for the error side.
239 */
240
241 CreatePipe(&Err.pipe, &h, &sa, 0);
242 DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
243 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
244
245 /*
246 * Base command line.
247 */
248
249 lstrcpy(cmdline, "cl.exe -nologo -c -TC -Zs -X -Fp.\\_junk.pch ");
250
251 /*
252 * Append our option for testing
253 */
254
255 lstrcat(cmdline, option);
256
257 /*
258 * Filename to compile, which exists, but is nothing and empty.
259 */
260
261 lstrcat(cmdline, " .\\nul");
262
263 ok = CreateProcess(
264 NULL, /* Module name. */
265 cmdline, /* Command line. */
266 NULL, /* Process handle not inheritable. */
267 NULL, /* Thread handle not inheritable. */
268 TRUE, /* yes, inherit handles. */
269 DETACHED_PROCESS, /* No console for you. */
270 NULL, /* Use parent's environment block. */
271 NULL, /* Use parent's starting directory. */
272 &si, /* Pointer to STARTUPINFO structure. */
273 &pi); /* Pointer to PROCESS_INFORMATION structure. */
274
275 if (!ok) {
276 DWORD err = GetLastError();
277 int chars = snprintf(msg, sizeof(msg) - 1,
278 "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
279
280 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
281 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
282 (300-chars), 0);
283 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL);
284 return 2;
285 }
286
287 /*
288 * Close our references to the write handles that have now been inherited.
289 */
290
291 CloseHandle(si.hStdOutput);
292 CloseHandle(si.hStdError);
293
294 WaitForInputIdle(pi.hProcess, 5000);
295 CloseHandle(pi.hThread);
296
297 /*
298 * Start the pipe reader threads.
299 */
300
301 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
302 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
303
304 /*
305 * Block waiting for the process to end.
306 */
307
308 WaitForSingleObject(pi.hProcess, INFINITE);
309 CloseHandle(pi.hProcess);
310
311 /*
312 * Wait for our pipe to get done reading, should it be a little slow.
313 */
314
315 WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
316 CloseHandle(pipeThreads[0]);
317 CloseHandle(pipeThreads[1]);
318
319 /*
320 * Look for the commandline warning code in both streams.
321 * - in MSVC 6 & 7 we get D4002, in MSVC 8 we get D9002.
322 */
323
324 return !(strstr(Out.buffer, "D4002") != NULL
325 || strstr(Err.buffer, "D4002") != NULL
326 || strstr(Out.buffer, "D9002") != NULL
327 || strstr(Err.buffer, "D9002") != NULL
328 || strstr(Out.buffer, "D2021") != NULL
329 || strstr(Err.buffer, "D2021") != NULL);
330}
331
332
333int
334CheckForLinkerFeature(
335 const char *option)
336{
337 STARTUPINFO si;
338 PROCESS_INFORMATION pi;
339 SECURITY_ATTRIBUTES sa;
340 DWORD threadID;
341 char msg[300];
342 BOOL ok;
343 HANDLE hProcess, h, pipeThreads[2];
344 char cmdline[100];
345
346 hProcess = GetCurrentProcess();
347
348 ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
349 ZeroMemory(&si, sizeof(STARTUPINFO));
350 si.cb = sizeof(STARTUPINFO);
351 si.dwFlags = STARTF_USESTDHANDLES;
352 si.hStdInput = INVALID_HANDLE_VALUE;
353
354 ZeroMemory(&sa, sizeof(SECURITY_ATTRIBUTES));
355 sa.nLength = sizeof(SECURITY_ATTRIBUTES);
356 sa.lpSecurityDescriptor = NULL;
357 sa.bInheritHandle = TRUE;
358
359 /*
360 * Create a non-inheritible pipe.
361 */
362
363 CreatePipe(&Out.pipe, &h, &sa, 0);
364
365 /*
366 * Dupe the write side, make it inheritible, and close the original.
367 */
368
369 DuplicateHandle(hProcess, h, hProcess, &si.hStdOutput, 0, TRUE,
370 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
371
372 /*
373 * Same as above, but for the error side.
374 */
375
376 CreatePipe(&Err.pipe, &h, &sa, 0);
377 DuplicateHandle(hProcess, h, hProcess, &si.hStdError, 0, TRUE,
378 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
379
380 /*
381 * Base command line.
382 */
383
384 lstrcpy(cmdline, "link.exe -nologo ");
385
386 /*
387 * Append our option for testing.
388 */
389
390 lstrcat(cmdline, option);
391
392 ok = CreateProcess(
393 NULL, /* Module name. */
394 cmdline, /* Command line. */
395 NULL, /* Process handle not inheritable. */
396 NULL, /* Thread handle not inheritable. */
397 TRUE, /* yes, inherit handles. */
398 DETACHED_PROCESS, /* No console for you. */
399 NULL, /* Use parent's environment block. */
400 NULL, /* Use parent's starting directory. */
401 &si, /* Pointer to STARTUPINFO structure. */
402 &pi); /* Pointer to PROCESS_INFORMATION structure. */
403
404 if (!ok) {
405 DWORD err = GetLastError();
406 int chars = snprintf(msg, sizeof(msg) - 1,
407 "Tried to launch: \"%s\", but got error [%u]: ", cmdline, err);
408
409 FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS|
410 FORMAT_MESSAGE_MAX_WIDTH_MASK, 0L, err, 0, (LPVOID)&msg[chars],
411 (300-chars), 0);
412 WriteFile(GetStdHandle(STD_ERROR_HANDLE), msg,lstrlen(msg), &err,NULL);
413 return 2;
414 }
415
416 /*
417 * Close our references to the write handles that have now been inherited.
418 */
419
420 CloseHandle(si.hStdOutput);
421 CloseHandle(si.hStdError);
422
423 WaitForInputIdle(pi.hProcess, 5000);
424 CloseHandle(pi.hThread);
425
426 /*
427 * Start the pipe reader threads.
428 */
429
430 pipeThreads[0] = CreateThread(NULL, 0, ReadFromPipe, &Out, 0, &threadID);
431 pipeThreads[1] = CreateThread(NULL, 0, ReadFromPipe, &Err, 0, &threadID);
432
433 /*
434 * Block waiting for the process to end.
435 */
436
437 WaitForSingleObject(pi.hProcess, INFINITE);
438 CloseHandle(pi.hProcess);
439
440 /*
441 * Wait for our pipe to get done reading, should it be a little slow.
442 */
443
444 WaitForMultipleObjects(2, pipeThreads, TRUE, 500);
445 CloseHandle(pipeThreads[0]);
446 CloseHandle(pipeThreads[1]);
447
448 /*
449 * Look for the commandline warning code in the stderr stream.
450 */
451
452 return !(strstr(Out.buffer, "LNK1117") != NULL ||
453 strstr(Err.buffer, "LNK1117") != NULL ||
454 strstr(Out.buffer, "LNK4044") != NULL ||
455 strstr(Err.buffer, "LNK4044") != NULL);
456}
457
458
459DWORD WINAPI
460ReadFromPipe(
461 LPVOID args)
462{
463 pipeinfo *pi = (pipeinfo *) args;
464 char *lastBuf = pi->buffer;
465 DWORD dwRead;
466 BOOL ok;
467
468 again:
469 if (lastBuf - pi->buffer + CHUNK > STATICBUFFERSIZE) {
470 CloseHandle(pi->pipe);
471 return (DWORD)-1;
472 }
473 ok = ReadFile(pi->pipe, lastBuf, CHUNK, &dwRead, 0L);
474 if (!ok || dwRead == 0) {
475 CloseHandle(pi->pipe);
476 return 0;
477 }
478 lastBuf += dwRead;
479 goto again;
480
481 return 0; /* makes the compiler happy */
482}
483
484
485int
486IsIn(
487 const char *string,
488 const char *substring)
489{
490 return (strstr(string, substring) != NULL);
491}
492
493
494/*
495 * Find a specified #define by name.
496 *
497 * If the line is '#define TCL_VERSION "8.5"', it returns 85 as the result.
498 */
499
500int
501GrepForDefine(
502 const char *file,
503 const char *string)
504{
505 char s1[51], s2[51], s3[51];
506 FILE *f = fopen(file, "rt");
507
508 if (f == NULL) {
509 return 0;
510 }
511
512 do {
513 int r = fscanf(f, "%50s", s1);
514
515 if (r == 1 && !strcmp(s1, "#define")) {
516 /*
517 * Get next two words.
518 */
519
520 r = fscanf(f, "%50s %50s", s2, s3);
521 if (r != 2) {
522 continue;
523 }
524
525 /*
526 * Is the first word what we're looking for?
527 */
528
529 if (!strcmp(s2, string)) {
530 double d1;
531
532 fclose(f);
533
534 /*
535 * Add 1 past first double quote char. "8.5"
536 */
537
538 d1 = atof(s3 + 1); /* 8.5 */
539 while (floor(d1) != d1) {
540 d1 *= 10.0;
541 }
542 return ((int) d1); /* 85 */
543 }
544 }
545 } while (!feof(f));
546
547 fclose(f);
548 return 0;
549}
550
551
552/*
553 * GetVersionFromFile --
554 * Looks for a match string in a file and then returns the version
555 * following the match where a version is anything acceptable to
556 * package provide or package ifneeded.
557 */
558
559const char *
560GetVersionFromFile(
561 const char *filename,
562 const char *match)
563{
564 size_t cbBuffer = 100;
565 static char szBuffer[100];
566 char *szResult = NULL;
567 FILE *fp = fopen(filename, "rt");
568
569 if (fp != NULL) {
570 /*
571 * Read data until we see our match string.
572 */
573
574 while (fgets(szBuffer, cbBuffer, fp) != NULL) {
575 LPSTR p, q;
576
577 p = strstr(szBuffer, match);
578 if (p != NULL) {
579 /*
580 * Skip to first digit.
581 */
582
583 while (*p && !isdigit(*p)) {
584 ++p;
585 }
586
587 /*
588 * Find ending whitespace.
589 */
590
591 q = p;
592 while (*q && (isalnum(*q) || *q == '.')) {
593 ++q;
594 }
595
596 memcpy(szBuffer, p, q - p);
597 szBuffer[q-p] = 0;
598 szResult = szBuffer;
599 break;
600 }
601 }
602 fclose(fp);
603 }
604 return szResult;
605}
606
607
608/*
609 * List helpers for the SubstituteFile function
610 */
611
612typedef struct list_item_t {
613 struct list_item_t *nextPtr;
614 char * key;
615 char * value;
616} list_item_t;
617
618/* insert a list item into the list (list may be null) */
619static list_item_t *
620list_insert(list_item_t **listPtrPtr, const char *key, const char *value)
621{
622 list_item_t *itemPtr = malloc(sizeof(list_item_t));
623 if (itemPtr) {
624 itemPtr->key = strdup(key);
625 itemPtr->value = strdup(value);
626 itemPtr->nextPtr = NULL;
627
628 while(*listPtrPtr) {
629 listPtrPtr = &(*listPtrPtr)->nextPtr;
630 }
631 *listPtrPtr = itemPtr;
632 }
633 return itemPtr;
634}
635
636static void
637list_free(list_item_t **listPtrPtr)
638{
639 list_item_t *tmpPtr, *listPtr = *listPtrPtr;
640 while (listPtr) {
641 tmpPtr = listPtr;
642 listPtr = listPtr->nextPtr;
643 free(tmpPtr->key);
644 free(tmpPtr->value);
645 free(tmpPtr);
646 }
647}
648
649
650/*
651 * SubstituteFile --
652 * As windows doesn't provide anything useful like sed and it's unreliable
653 * to use the tclsh you are building against (consider x-platform builds -
654 * eg compiling AMD64 target from IX86) we provide a simple substitution
655 * option here to handle autoconf style substitutions.
656 * The substitution file is whitespace and line delimited. The file should
657 * consist of lines matching the regular expression:
658 * \s*\S+\s+\S*$
659 *
660 * Usage is something like:
661 * nmakehlp -S << $** > $@
662 * @PACKAGE_NAME@ $(PACKAGE_NAME)
663 * @PACKAGE_VERSION@ $(PACKAGE_VERSION)
664 * <<
665 */
666
667int
668SubstituteFile(
669 const char *substitutions,
670 const char *filename)
671{
672 size_t cbBuffer = 1024;
673 static char szBuffer[1024], szCopy[1024];
674 char *szResult = NULL;
675 list_item_t *substPtr = NULL;
676 FILE *fp, *sp;
677
678 fp = fopen(filename, "rt");
679 if (fp != NULL) {
680
681 /*
682 * Build a list of substutitions from the first filename
683 */
684
685 sp = fopen(substitutions, "rt");
686 if (sp != NULL) {
687 while (fgets(szBuffer, cbBuffer, sp) != NULL) {
688 char *ks, *ke, *vs, *ve;
689 ks = szBuffer;
690 while (ks && *ks && isspace(*ks)) ++ks;
691 ke = ks;
692 while (ke && *ke && !isspace(*ke)) ++ke;
693 vs = ke;
694 while (vs && *vs && isspace(*vs)) ++vs;
695 ve = vs;
696 while (ve && *ve && !(*ve == '\r' || *ve == '\n')) ++ve;
697 *ke = 0, *ve = 0;
698 list_insert(&substPtr, ks, vs);
699 }
700 fclose(sp);
701 }
702
703 /* debug: dump the list */
704#ifdef _DEBUG
705 {
706 int n = 0;
707 list_item_t *p = NULL;
708 for (p = substPtr; p != NULL; p = p->nextPtr, ++n) {
709 fprintf(stderr, "% 3d '%s' => '%s'\n", n, p->key, p->value);
710 }
711 }
712#endif
713
714 /*
715 * Run the substitutions over each line of the input
716 */
717
718 while (fgets(szBuffer, cbBuffer, fp) != NULL) {
719 list_item_t *p = NULL;
720 for (p = substPtr; p != NULL; p = p->nextPtr) {
721 char *m = strstr(szBuffer, p->key);
722 if (m) {
723 char *cp, *op, *sp;
724 cp = szCopy;
725 op = szBuffer;
726 while (op != m) *cp++ = *op++;
727 sp = p->value;
728 while (sp && *sp) *cp++ = *sp++;
729 op += strlen(p->key);
730 while (*op) *cp++ = *op++;
731 *cp = 0;
732 memcpy(szBuffer, szCopy, sizeof(szCopy));
733 }
734 }
735 printf(szBuffer);
736 }
737
738 list_free(&substPtr);
739 }
740 fclose(fp);
741 return 0;
742}
743
744
745/*
746 * QualifyPath --
747 *
748 * This composes the current working directory with a provided path
749 * and returns the fully qualified and normalized path.
750 * Mostly needed to setup paths for testing.
751 */
752
753int
754QualifyPath(
755 const char *szPath)
756{
757 char szCwd[MAX_PATH + 1];
758 char szTmp[MAX_PATH + 1];
759 char *p;
760 GetCurrentDirectory(MAX_PATH, szCwd);
761 while ((p = strchr(szPath, '/')) && *p)
762 *p = '\\';
763 PathCombine(szTmp, szCwd, szPath);
764 PathCanonicalize(szCwd, szTmp);
765 printf("%s\n", szCwd);
766 return 0;
767}
768
769/*
770 * Local variables:
771 * mode: c
772 * c-basic-offset: 4
773 * fill-column: 78
774 * indent-tabs-mode: t
775 * tab-width: 8
776 * End:
777 */
Note: See TracBrowser for help on using the repository browser.