/[jscoverage]/trunk/js/config/nsinstall_win.c
ViewVC logotype

Contents of /trunk/js/config/nsinstall_win.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 510 - (show annotations)
Mon Jan 11 05:17:00 2010 UTC (9 years, 7 months ago) by siliconforks
File MIME type: text/plain
File size: 21016 byte(s)
Fixes for building on Windows.

1 /*
2 * The nsinstall command for Win32
3 *
4 * Our gmake makefiles use the nsinstall command to create the
5 * object directories or installing headers and libs. This code was originally
6 * taken from shmsdos.c
7 */
8
9 #include <direct.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <windows.h>
14 #pragma hdrstop
15
16 /*
17 * sh_FileFcn --
18 *
19 * A function that operates on a file. The pathname is either
20 * absolute or relative to the current directory, and contains
21 * no wildcard characters such as * and ?. Additional arguments
22 * can be passed to the function via the arg pointer.
23 */
24
25 typedef BOOL (*sh_FileFcn)(
26 wchar_t *pathName,
27 WIN32_FIND_DATA *fileData,
28 void *arg);
29
30 static int shellCp (wchar_t **pArgv);
31 static int shellNsinstall (wchar_t **pArgv);
32 static int shellMkdir (wchar_t **pArgv);
33 static BOOL sh_EnumerateFiles(const wchar_t *pattern, const wchar_t *where,
34 sh_FileFcn fileFcn, void *arg, int *nFiles);
35 static const char *sh_GetLastErrorMessage(void);
36 static BOOL sh_DoCopy(wchar_t *srcFileName, DWORD srcFileAttributes,
37 wchar_t *dstFileName, DWORD dstFileAttributes,
38 int force, int recursive);
39
40 #define LONGPATH_PREFIX L"\\\\?\\"
41 #define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
42 #define STR_LEN(a) (ARRAY_LEN(a) - 1)
43
44 /* changes all forward slashes in token to backslashes */
45 void changeForwardSlashesToBackSlashes ( wchar_t *arg )
46 {
47 if ( arg == NULL )
48 return;
49
50 while ( *arg ) {
51 if ( *arg == '/' )
52 *arg = '\\';
53 arg++;
54 }
55 }
56
57 #ifdef __MINGW32__
58 /*
59 http://article.gmane.org/gmane.comp.gnu.mingw.user/22962
60 http://msdn.microsoft.com/en-us/library/bb776391%28VS.85%29.aspx
61 */
62 int main(int argc, char ** argv)
63 {
64 LPWSTR *szArglist;
65 int nArgs;
66 int i;
67 int result;
68
69 szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
70 if (NULL == szArglist) {
71 wprintf(L"CommandLineToArgvW failed\n");
72 return 1;
73 }
74
75 /* terminate with NULL */
76 LPWSTR * wargv = malloc((nArgs + 1) * sizeof(LPWSTR));
77 if (wargv == NULL) {
78 printf("malloc failed\n");
79 return 1;
80 }
81 for (i = 0; i < nArgs; ++i) {
82 wargv[i] = szArglist[i];
83 }
84 wargv[nArgs] = NULL;
85 LocalFree(szArglist);
86
87 result = wmain(nArgs, wargv);
88
89 free(wargv);
90
91 return result;
92 }
93 #endif
94
95 int wmain(int argc, wchar_t *argv[ ])
96 {
97 return shellNsinstall ( argv + 1 );
98 }
99
100 static int
101 shellNsinstall (wchar_t **pArgv)
102 {
103 int retVal = 0; /* exit status */
104 int dirOnly = 0; /* 1 if and only if -D is specified */
105 wchar_t **pSrc;
106 wchar_t **pDst;
107
108 /*
109 * Process the command-line options. We ignore the
110 * options except for -D. Some options, such as -m,
111 * are followed by an argument. We need to skip the
112 * argument too.
113 */
114 while ( *pArgv && **pArgv == '-' ) {
115 wchar_t c = (*pArgv)[1]; /* The char after '-' */
116
117 if ( c == 'D' ) {
118 dirOnly = 1;
119 } else if ( c == 'm' ) {
120 pArgv++; /* skip the next argument */
121 }
122 pArgv++;
123 }
124
125 if ( !dirOnly ) {
126 /* There are files to install. Get source files */
127 if ( *pArgv ) {
128 pSrc = pArgv++;
129 } else {
130 fprintf( stderr, "nsinstall: not enough arguments\n");
131 return 3;
132 }
133 }
134
135 /* Get to last token to find destination directory */
136 if ( *pArgv ) {
137 pDst = pArgv++;
138 if ( dirOnly && *pArgv ) {
139 fprintf( stderr, "nsinstall: too many arguments with -D\n");
140 return 3;
141 }
142 } else {
143 fprintf( stderr, "nsinstall: not enough arguments\n");
144 return 3;
145 }
146 while ( *pArgv )
147 pDst = pArgv++;
148
149 retVal = shellMkdir ( pDst );
150 if ( retVal )
151 return retVal;
152 if ( !dirOnly )
153 retVal = shellCp ( pSrc );
154 return retVal;
155 }
156
157 static int
158 shellMkdir (wchar_t **pArgv)
159 {
160 int retVal = 0; /* assume valid return */
161 wchar_t *arg;
162 wchar_t *pArg;
163 wchar_t path[_MAX_PATH];
164 wchar_t tmpPath[_MAX_PATH];
165 wchar_t *pTmpPath = tmpPath;
166
167 /* All the options are simply ignored in this implementation */
168 while ( *pArgv && **pArgv == '-' ) {
169 if ( (*pArgv)[1] == 'm' ) {
170 pArgv++; /* skip the next argument (mode) */
171 }
172 pArgv++;
173 }
174
175 while ( *pArgv ) {
176 arg = *pArgv;
177 changeForwardSlashesToBackSlashes ( arg );
178 pArg = arg;
179 pTmpPath = tmpPath;
180 while ( 1 ) {
181 /* create part of path */
182 while ( *pArg ) {
183 *pTmpPath++ = *pArg++;
184 if ( *pArg == '\\' )
185 break;
186 }
187 *pTmpPath = '\0';
188
189 /* check if directory already exists */
190 _wgetcwd ( path, _MAX_PATH );
191 if ( _wchdir ( tmpPath ) != -1 ) {
192 _wchdir ( path );
193 } else {
194 if ( _wmkdir ( tmpPath ) == -1 ) {
195 printf ( "%ls: ", tmpPath );
196 perror ( "Could not create the directory" );
197 retVal = 3;
198 break;
199 }
200 }
201 if ( *pArg == '\0' ) /* complete path? */
202 break;
203 /* loop for next directory */
204 }
205
206 pArgv++;
207 }
208 return retVal;
209 }
210
211 static const char *
212 sh_GetLastErrorMessage()
213 {
214 static char buf[128];
215
216 FormatMessageA(
217 FORMAT_MESSAGE_FROM_SYSTEM,
218 NULL,
219 GetLastError(),
220 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* default language */
221 buf,
222 sizeof(buf),
223 NULL
224 );
225 return buf;
226 }
227
228 /*
229 * struct sh_FileData --
230 *
231 * A pointer to the sh_FileData structure is passed into sh_RecordFileData,
232 * which will fill in the fields.
233 */
234
235 struct sh_FileData {
236 wchar_t pathName[_MAX_PATH];
237 DWORD dwFileAttributes;
238 };
239
240 /*
241 * sh_RecordFileData --
242 *
243 * Record the pathname and attributes of the file in
244 * the sh_FileData structure pointed to by arg.
245 *
246 * Always return TRUE (successful completion).
247 *
248 * This function is intended to be passed into sh_EnumerateFiles
249 * to see if a certain pattern expands to exactly one file/directory,
250 * and if so, record its pathname and attributes.
251 */
252
253 static BOOL
254 sh_RecordFileData(wchar_t *pathName, WIN32_FIND_DATA *findData, void *arg)
255 {
256 struct sh_FileData *fData = (struct sh_FileData *) arg;
257
258 wcscpy(fData->pathName, pathName);
259 fData->dwFileAttributes = findData->dwFileAttributes;
260 return TRUE;
261 }
262
263 static BOOL
264 sh_DoCopy(wchar_t *srcFileName,
265 DWORD srcFileAttributes,
266 wchar_t *dstFileName,
267 DWORD dstFileAttributes,
268 int force,
269 int recursive
270 )
271 {
272 if (dstFileAttributes != 0xFFFFFFFF) {
273 if ((dstFileAttributes & FILE_ATTRIBUTE_READONLY) && force) {
274 dstFileAttributes &= ~FILE_ATTRIBUTE_READONLY;
275 SetFileAttributes(dstFileName, dstFileAttributes);
276 }
277 }
278
279 if (srcFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
280 fprintf(stderr, "nsinstall: %ls is a directory\n",
281 srcFileName);
282 return FALSE;
283 } else {
284 DWORD r;
285 wchar_t longSrc[1004] = LONGPATH_PREFIX;
286 wchar_t longDst[1004] = LONGPATH_PREFIX;
287 r = GetFullPathName(srcFileName, 1000, longSrc + STR_LEN(LONGPATH_PREFIX), NULL);
288 if (!r) {
289 fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n",
290 srcFileName, sh_GetLastErrorMessage());
291 return FALSE;
292 }
293 r = GetFullPathName(dstFileName, 1000, longDst + ARRAY_LEN(LONGPATH_PREFIX) - 1, NULL);
294 if (!r) {
295 fprintf(stderr, "nsinstall: couldn't get full path of %ls: %s\n",
296 dstFileName, sh_GetLastErrorMessage());
297 return FALSE;
298 }
299
300 if (!CopyFile(longSrc, longDst, FALSE)) {
301 fprintf(stderr, "nsinstall: cannot copy %ls to %ls: %s\n",
302 srcFileName, dstFileName, sh_GetLastErrorMessage());
303 return FALSE;
304 }
305 }
306 return TRUE;
307 }
308
309 /*
310 * struct sh_CpCmdArg --
311 *
312 * A pointer to the sh_CpCmdArg structure is passed into sh_CpFileCmd.
313 * The sh_CpCmdArg contains information about the cp command, and
314 * provide a buffer for constructing the destination file name.
315 */
316
317 struct sh_CpCmdArg {
318 int force; /* -f option, ok to overwrite an existing
319 * read-only destination file */
320 int recursive; /* -r or -R option, recursively copy
321 * directories. Note: this field is not used
322 * by nsinstall and should always be 0. */
323 wchar_t *dstFileName; /* a buffer for constructing the destination
324 * file name */
325 wchar_t *dstFileNameMarker; /* points to where in the dstFileName buffer
326 * we should write the file component of the
327 * destination file */
328 };
329
330 /*
331 * sh_CpFileCmd --
332 *
333 * Copy a file to the destination directory
334 *
335 * This function is intended to be passed into sh_EnumerateFiles to
336 * copy all the files specified by the pattern to the destination
337 * directory.
338 *
339 * Return TRUE if the file is successfully copied, and FALSE otherwise.
340 */
341
342 static BOOL
343 sh_CpFileCmd(wchar_t *pathName, WIN32_FIND_DATA *findData, void *cpArg)
344 {
345 BOOL retVal = TRUE;
346 struct sh_CpCmdArg *arg = (struct sh_CpCmdArg *) cpArg;
347
348 wcscpy(arg->dstFileNameMarker, findData->cFileName);
349 return sh_DoCopy(pathName, findData->dwFileAttributes,
350 arg->dstFileName, GetFileAttributes(arg->dstFileName),
351 arg->force, arg->recursive);
352 }
353
354 static int
355 shellCp (wchar_t **pArgv)
356 {
357 int retVal = 0;
358 wchar_t **pSrc;
359 wchar_t **pDst;
360 struct sh_CpCmdArg arg;
361 struct sh_FileData dstData;
362 int dstIsDir = 0;
363 int n;
364
365 arg.force = 0;
366 arg.recursive = 0;
367 arg.dstFileName = dstData.pathName;
368 arg.dstFileNameMarker = 0;
369
370 while (*pArgv && **pArgv == '-') {
371 wchar_t *p = *pArgv;
372
373 while (*(++p)) {
374 if (*p == 'f') {
375 arg.force = 1;
376 }
377 }
378 pArgv++;
379 }
380
381 /* the first source file */
382 if (*pArgv) {
383 pSrc = pArgv++;
384 } else {
385 fprintf(stderr, "nsinstall: not enough arguments\n");
386 return 3;
387 }
388
389 /* get to the last token to find destination */
390 if (*pArgv) {
391 pDst = pArgv++;
392 } else {
393 fprintf(stderr, "nsinstall: not enough arguments\n");
394 return 3;
395 }
396 while (*pArgv) {
397 pDst = pArgv++;
398 }
399
400 /*
401 * The destination pattern must unambiguously expand to exactly
402 * one file or directory.
403 */
404
405 changeForwardSlashesToBackSlashes(*pDst);
406 sh_EnumerateFiles(*pDst, *pDst, sh_RecordFileData, &dstData, &n);
407 assert(n >= 0);
408 if (n == 1) {
409 /*
410 * Is the destination a file or directory?
411 */
412
413 if (dstData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
414 dstIsDir = 1;
415 }
416 } else if (n > 1) {
417 fprintf(stderr, "nsinstall: %ls: ambiguous destination file "
418 "or directory\n", *pDst);
419 return 3;
420 } else {
421 /*
422 * n == 0, meaning that destination file or directory does
423 * not exist. In this case the destination file directory
424 * name must be fully specified.
425 */
426
427 wchar_t *p;
428
429 for (p = *pDst; *p; p++) {
430 if (*p == '*' || *p == '?') {
431 fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
432 *pDst);
433 return 3;
434 }
435 }
436
437 /*
438 * Do not include the trailing \, if any, unless it is a root
439 * directory (\ or X:\).
440 */
441
442 if (p > *pDst && p[-1] == '\\' && p != *pDst + 1 && p[-2] != ':') {
443 p[-1] = '\0';
444 }
445 wcscpy(dstData.pathName, *pDst);
446 dstData.dwFileAttributes = 0xFFFFFFFF;
447 }
448
449 /*
450 * If there are two or more source files, the destination has
451 * to be a directory.
452 */
453
454 if (pDst - pSrc > 1 && !dstIsDir) {
455 fprintf(stderr, "nsinstall: cannot copy more than"
456 " one file to the same destination file\n");
457 return 3;
458 }
459
460 if (dstIsDir) {
461 arg.dstFileNameMarker = arg.dstFileName + wcslen(arg.dstFileName);
462
463 /*
464 * Now arg.dstFileNameMarker is pointing to the null byte at the
465 * end of string. We want to make sure that there is a \ at the
466 * end of string, and arg.dstFileNameMarker should point right
467 * after that \.
468 */
469
470 if (arg.dstFileNameMarker[-1] != '\\') {
471 *(arg.dstFileNameMarker++) = '\\';
472 }
473 }
474
475 if (!dstIsDir) {
476 struct sh_FileData srcData;
477
478 assert(pDst - pSrc == 1);
479 changeForwardSlashesToBackSlashes(*pSrc);
480 sh_EnumerateFiles(*pSrc, *pSrc, sh_RecordFileData, &srcData, &n);
481 if (n == 0) {
482 fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
483 *pSrc);
484 retVal = 3;
485 } else if (n > 1) {
486 fprintf(stderr, "nsinstall: cannot copy more than one file or "
487 "directory to the same destination\n");
488 retVal = 3;
489 } else {
490 assert(n == 1);
491 if (sh_DoCopy(srcData.pathName, srcData.dwFileAttributes,
492 dstData.pathName, dstData.dwFileAttributes,
493 arg.force, arg.recursive) == FALSE) {
494 retVal = 3;
495 }
496 }
497 return retVal;
498 }
499
500 for ( ; *pSrc != *pDst; pSrc++) {
501 BOOL rv;
502
503 changeForwardSlashesToBackSlashes(*pSrc);
504 rv = sh_EnumerateFiles(*pSrc, *pSrc, sh_CpFileCmd, &arg, &n);
505 if (rv == FALSE) {
506 retVal = 3;
507 } else {
508 if (n == 0) {
509 fprintf(stderr, "nsinstall: %ls: No such file or directory\n",
510 *pSrc);
511 retVal = 3;
512 }
513 }
514 }
515
516 return retVal;
517 }
518
519 /*
520 * sh_EnumerateFiles --
521 *
522 * Enumerate all the files in the specified pattern, which is a pathname
523 * containing possibly wildcard characters such as * and ?. fileFcn
524 * is called on each file, passing the expanded file name, a pointer
525 * to the file's WIN32_FILE_DATA, and the arg pointer.
526 *
527 * It is assumed that there are no wildcard characters before the
528 * character pointed to by 'where'.
529 *
530 * On return, *nFiles stores the number of files enumerated. *nFiles is
531 * set to this number whether sh_EnumerateFiles or 'fileFcn' succeeds
532 * or not.
533 *
534 * Return TRUE if the files are successfully enumerated and all
535 * 'fileFcn' invocations succeeded. Return FALSE if something went
536 * wrong.
537 */
538
539 static BOOL sh_EnumerateFiles(
540 const wchar_t *pattern,
541 const wchar_t *where,
542 sh_FileFcn fileFcn,
543 void *arg,
544 int *nFiles
545 )
546 {
547 WIN32_FIND_DATA fileData;
548 HANDLE hSearch;
549 const wchar_t *src;
550 wchar_t *dst;
551 wchar_t fileName[_MAX_PATH];
552 wchar_t *fileNameMarker = fileName;
553 wchar_t *oldFileNameMarker;
554 BOOL hasWildcard = FALSE;
555 BOOL retVal = TRUE;
556 BOOL patternEndsInDotStar = FALSE;
557 BOOL patternEndsInDot = FALSE; /* a special case of
558 * patternEndsInDotStar */
559 int numDotsInPattern;
560 int len;
561
562 /*
563 * Windows expands patterns ending in ".", ".*", ".**", etc.
564 * differently from the glob expansion on Unix. For example,
565 * both "foo." and "foo.*" match "foo", and "*.*" matches
566 * everything, including filenames with no dots. So we need
567 * to throw away extra files returned by the FindNextFile()
568 * function. We require that a matched filename have at least
569 * the number of dots in the pattern.
570 */
571 len = wcslen(pattern);
572 if (len >= 2) {
573 /* Start from the end of pattern and go backward */
574 const wchar_t *p = &pattern[len - 1];
575
576 /* We can have zero or more *'s */
577 while (p >= pattern && *p == '*') {
578 p--;
579 }
580 if (p >= pattern && *p == '.') {
581 patternEndsInDotStar = TRUE;
582 if (p == &pattern[len - 1]) {
583 patternEndsInDot = TRUE;
584 }
585 p--;
586 numDotsInPattern = 1;
587 while (p >= pattern && *p != '\\') {
588 if (*p == '.') {
589 numDotsInPattern++;
590 }
591 p--;
592 }
593 }
594 }
595
596 *nFiles = 0;
597
598 /*
599 * Copy pattern to fileName, but only up to and not including
600 * the first \ after the first wildcard letter.
601 *
602 * Make fileNameMarker point to one of the following:
603 * - the start of fileName, if fileName does not contain any \.
604 * - right after the \ before the first wildcard letter, if there is
605 * a wildcard character.
606 * - right after the last \, if there is no wildcard character.
607 */
608
609 dst = fileName;
610 src = pattern;
611 while (src < where) {
612 if (*src == '\\') {
613 oldFileNameMarker = fileNameMarker;
614 fileNameMarker = dst + 1;
615 }
616 *(dst++) = *(src++);
617 }
618
619 while (*src && *src != '*' && *src != '?') {
620 if (*src == '\\') {
621 oldFileNameMarker = fileNameMarker;
622 fileNameMarker = dst + 1;
623 }
624 *(dst++) = *(src++);
625 }
626
627 if (*src) {
628 /*
629 * Must have seen the first wildcard letter
630 */
631
632 hasWildcard = TRUE;
633 while (*src && *src != '\\') {
634 *(dst++) = *(src++);
635 }
636 }
637
638 /* Now src points to either null or \ */
639
640 assert(*src == '\0' || *src == '\\');
641 assert(hasWildcard || *src == '\0');
642 *dst = '\0';
643
644 /*
645 * If the pattern does not contain any wildcard characters, then
646 * we don't need to go the FindFirstFile route.
647 */
648
649 if (!hasWildcard) {
650 /*
651 * See if it is the root directory, \, or X:\.
652 */
653
654 assert(!wcscmp(fileName, pattern));
655 assert(wcslen(fileName) >= 1);
656 if (dst[-1] == '\\' && (dst == fileName + 1 || dst[-2] == ':')) {
657 fileData.cFileName[0] = '\0';
658 } else {
659 /*
660 * Do not include the trailing \, if any
661 */
662
663 if (dst[-1] == '\\') {
664 assert(*fileNameMarker == '\0');
665 dst[-1] = '\0';
666 fileNameMarker = oldFileNameMarker;
667 }
668 wcscpy(fileData.cFileName, fileNameMarker);
669 }
670 fileData.dwFileAttributes = GetFileAttributes(fileName);
671 if (fileData.dwFileAttributes == 0xFFFFFFFF) {
672 return TRUE;
673 }
674 *nFiles = 1;
675 return (*fileFcn)(fileName, &fileData, arg);
676 }
677
678 hSearch = FindFirstFile(fileName, &fileData);
679 if (hSearch == INVALID_HANDLE_VALUE) {
680 return retVal;
681 }
682
683 do {
684 if (!wcscmp(fileData.cFileName, L".")
685 || !wcscmp(fileData.cFileName, L"..")) {
686 /*
687 * Skip over . and ..
688 */
689
690 continue;
691 }
692
693 if (patternEndsInDotStar) {
694 int nDots = 0;
695 wchar_t *p = fileData.cFileName;
696 while (*p) {
697 if (*p == '.') {
698 nDots++;
699 }
700 p++;
701 }
702 /* Now p points to the null byte at the end of file name */
703 if (patternEndsInDot && (p == fileData.cFileName
704 || p[-1] != '.')) {
705 /*
706 * File name does not end in dot. Skip this file.
707 * Note: windows file name probably cannot end in dot,
708 * but we do this check anyway.
709 */
710 continue;
711 }
712 if (nDots < numDotsInPattern) {
713 /*
714 * Not enough dots in file name. Must be an extra
715 * file in matching .* pattern. Skip this file.
716 */
717 continue;
718 }
719 }
720
721 wcscpy(fileNameMarker, fileData.cFileName);
722 if (*src && *(src + 1)) {
723 /*
724 * More to go. Recurse.
725 */
726
727 int n;
728
729 assert(*src == '\\');
730 where = fileName + wcslen(fileName);
731 wcscat(fileName, src);
732 sh_EnumerateFiles(fileName, where, fileFcn, arg, &n);
733 *nFiles += n;
734 } else {
735 assert(wcschr(fileName, '*') == NULL);
736 assert(wcschr(fileName, '?') == NULL);
737 (*nFiles)++;
738 if ((*fileFcn)(fileName, &fileData, arg) == FALSE) {
739 retVal = FALSE;
740 }
741 }
742 } while (FindNextFile(hSearch, &fileData));
743
744 FindClose(hSearch);
745 return retVal;
746 }

  ViewVC Help
Powered by ViewVC 1.1.24