source: trunk/third/firefox/config/makecopy.cpp @ 21695

Revision 21695, 15.7 KB checked in by rbasch, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21694, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* ***** BEGIN LICENSE BLOCK *****
3 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
4 *
5 * The contents of this file are subject to the Netscape Public License
6 * Version 1.1 (the "License"); you may not use this file except in
7 * compliance with the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/NPL/
9 *
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
14 *
15 * The Original Code is mozilla.org code.
16 *
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
21 *
22 * Contributor(s):
23 * Modified by David.Gardiner@unisa.edu.au
24 *
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the NPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the NPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40#include <windows.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <direct.h>
45#include <sys/stat.h>
46#include <sys/utime.h>
47#include <io.h>
48#include <fcntl.h>
49
50/*
51        Unicode calls are linked at run-time, so that the application can run under
52        Windows NT and 95 (which doesn't support the Unicode calls)
53
54        The following APIs are linked:
55                BackupWrite
56                CreateFileW
57                GetFullPathNameW
58*/
59
60//static const char *prog;
61
62BOOL insertHashLine = FALSE;
63BOOL trySymlink = FALSE;
64BOOL recurse = FALSE;
65
66typedef WINBASEAPI BOOL (WINAPI* LPFNBackupWrite)(HANDLE, LPBYTE, DWORD, LPDWORD, BOOL, BOOL, LPVOID *);
67typedef WINBASEAPI HANDLE (WINAPI* LPFNCreateFileW)(LPCWSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);
68typedef WINBASEAPI DWORD (WINAPI* LPFNGetFullPathNameW)(LPCWSTR, DWORD, LPWSTR, LPWSTR *);
69
70// Function pointers (used for NTFS hard links)
71LPFNBackupWrite lpfnDllBackupWrite = NULL;   
72LPFNCreateFileW lpfnDllCreateFileW = NULL;
73LPFNGetFullPathNameW lpfnDllGetFullPathNameW = NULL;
74
75// Handle to DLL
76HINSTANCE hDLL = NULL;               
77
78/*
79** Flip any "unix style slashes" into "dos style backslashes"
80*/
81inline void FlipSlashes(char *name)
82{
83    for( int i=0; name[i]; i++ ) {
84        if( name[i] == '/' ) name[i] = '\\';
85    }
86}
87
88/*
89 * Flip any "dos style backslashes" into "unix style slashes"
90 */
91inline void UnflipSlashes(char *name)
92{
93    for( int i=0; name[i]; i++ ) {
94        if( name[i] == '\\' ) name[i] = '/';
95    }
96}
97
98int MakeDir( char *path )
99{
100    char *cp, *pstr;
101    struct stat sb;
102
103    pstr = path;
104    while( cp = strchr(pstr, '\\') ) {
105        *cp = '\0';
106       
107        if( !(stat(path, &sb) == 0 && (sb.st_mode & _S_IFDIR) )) {
108            /* create the new sub-directory */
109            printf("+++ makecopy: creating directory %s\n", path);
110            if( mkdir(path) < 0 ) {
111                return -1;
112            }
113        } /* else sub-directory already exists.... */
114
115        *cp = '\\';
116        pstr = cp+1;
117    }
118
119        return 0;
120}
121
122/*
123 * Display error code and message for last error
124 */
125int ReportError()
126{       
127        LPVOID lpMsgBuf = NULL;
128
129        DWORD err = GetLastError();
130               
131        FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
132                NULL,
133                err,
134                0,
135                (LPTSTR) &lpMsgBuf,
136                0,
137                NULL);
138
139        fprintf(stderr, "%u, %s\n", err, (LPCTSTR) lpMsgBuf ) ;
140
141        LocalFree( lpMsgBuf );
142
143        return -1;
144}
145
146int ReportError(const char* msg)
147{
148        fprintf(stderr, "Error: %s\n", msg);
149        return ReportError();
150}
151
152
153/*
154        Creates an NTFS hard link of src at dest.
155        NT5 will have a CreateHardLink API which will do the same thing, but a lot simpler
156        This is based on the MSDN code sample Q153181
157
158 */
159BOOL hardSymLink(LPCSTR src, LPCSTR dest)
160{
161    WCHAR FileLink[ MAX_PATH + 1 ];
162    WCHAR FileSource[ MAX_PATH + 1 ];
163    WCHAR FileDest[ MAX_PATH + 1 ];
164    LPWSTR FilePart;
165
166    WIN32_STREAM_ID StreamId;
167    DWORD dwBytesWritten;
168    DWORD cbPathLen;
169
170    BOOL bSuccess;
171
172        // Convert src and dest to Unicode
173    if (!MultiByteToWideChar(CP_ACP, 0, src, -1, FileSource, MAX_PATH)) {
174        ReportError("Convert to WCHAR (source)");
175        return FALSE;
176    }
177
178    if (!MultiByteToWideChar(CP_ACP, 0, dest, -1, FileDest, MAX_PATH)) {
179        ReportError("Convert to WCHAR (destination)");
180        return FALSE;
181    }
182
183    //
184    // open existing file that we link to
185    //
186
187    HANDLE hFileSource = lpfnDllCreateFileW(
188        FileSource,
189        FILE_WRITE_ATTRIBUTES,
190        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
191        NULL, // sa
192        OPEN_EXISTING,
193        0,
194        NULL
195        );
196
197    if(hFileSource == INVALID_HANDLE_VALUE) {
198        ReportError("CreateFile (source)");
199        return FALSE;
200    }
201
202    //
203    // validate and sanitize supplied link path and use the result
204    // the full path MUST be Unicode for BackupWrite
205    //
206
207    cbPathLen = lpfnDllGetFullPathNameW( FileDest, MAX_PATH, FileLink, &FilePart);
208
209    if(cbPathLen == 0) {
210        ReportError("GetFullPathName");
211        return FALSE;
212    }
213
214    cbPathLen = (cbPathLen + 1) * sizeof(WCHAR); // adjust for byte count
215
216    //
217    // it might also be a good idea to verify the existence of the link,
218    // (and possibly bail), as the file specified in FileLink will be
219    // overwritten if it already exists
220    //
221
222    //
223    // prepare and write the WIN32_STREAM_ID out
224    //
225
226    LPVOID lpContext = NULL;
227
228    StreamId.dwStreamId = BACKUP_LINK;
229    StreamId.dwStreamAttributes = 0;
230    StreamId.dwStreamNameSize = 0;
231    StreamId.Size.HighPart = 0;
232    StreamId.Size.LowPart = cbPathLen;
233
234    //
235    // compute length of variable size WIN32_STREAM_ID
236    //
237
238    DWORD StreamHeaderSize = (LPBYTE)&StreamId.cStreamName - (LPBYTE)&
239        StreamId+ StreamId.dwStreamNameSize ;
240
241    bSuccess = lpfnDllBackupWrite(
242        hFileSource,
243        (LPBYTE)&StreamId,  // buffer to write
244        StreamHeaderSize,   // number of bytes to write
245        &dwBytesWritten,
246        FALSE,              // don't abort yet
247        FALSE,              // don't process security
248        &lpContext
249        );
250
251    if(bSuccess) {
252
253        //
254        // write out the buffer containing the path
255        //
256
257        bSuccess = lpfnDllBackupWrite(
258            hFileSource,
259            (LPBYTE)FileLink,   // buffer to write
260            cbPathLen,          // number of bytes to write
261            &dwBytesWritten,
262            FALSE,              // don't abort yet
263            FALSE,              // don't process security
264            &lpContext
265            );
266
267        //
268        // free context
269        //
270
271        lpfnDllBackupWrite(
272            hFileSource,
273            NULL,               // buffer to write
274            0,                  // number of bytes to write
275            &dwBytesWritten,
276            TRUE,               // abort
277            FALSE,              // don't process security
278            &lpContext
279            );
280    }
281
282    CloseHandle( hFileSource );
283
284    if(!bSuccess) {
285        ReportError("BackupWrite");
286        return FALSE;
287    }
288
289    return TRUE;
290}
291
292int CopyIfNecessary(char *oldFile, char *newFile)
293{
294    struct stat newsb;
295        struct stat oldsb;     
296
297        // Use stat to find file details
298        if (stat(oldFile, &oldsb)) {
299                return -1;
300        }
301
302    // skip directories unless recursion flag is set
303    if ( oldsb.st_mode & _S_IFDIR ) {
304        if (!recurse) {
305            printf("    Skipping directory %s\n", oldFile);
306            return 0;
307        }
308        else {
309            char *lastDir;
310            char *oldFileName; // points to where file name starts in oldFile
311            char *newFileName; // points to where file name starts in newFile
312            WIN32_FIND_DATA findFileData;
313
314            // weed out special "." and ".." directories
315            lastDir = strrchr(oldFile, '\\');
316            if ( lastDir )
317                ++lastDir;
318            else
319                lastDir = oldFile;
320            if ( strcmp( lastDir, "." ) == 0 || strcmp( lastDir, ".." ) == 0 )
321                return 0;
322
323            // find and process the contents of the directory
324            oldFileName = oldFile + strlen(oldFile);
325            strcpy(oldFileName, "\\*");
326            ++oldFileName;
327
328            newFileName = newFile + strlen(newFile);
329            strcpy(newFileName, "\\");
330            ++newFileName;
331
332            if( MakeDir(newFile) < 0 ) {
333                fprintf(stderr, "\n+++ makecopy: unable to create directory %s\n", newFile);
334                return 1;
335            }
336
337            HANDLE hFindFile = FindFirstFile(oldFile, &findFileData);
338            if (hFindFile != INVALID_HANDLE_VALUE) {
339                do {
340                    strcpy(oldFileName, findFileData.cFileName);
341                    strcpy(newFileName, findFileData.cFileName);
342                    CopyIfNecessary(oldFile, newFile);
343                } while (FindNextFile(hFindFile, &findFileData) != 0);
344            } else {
345                fprintf(stderr, "\n+++ makecopy: no such file: %s\n", oldFile);
346            }
347            FindClose(hFindFile);
348        }
349        // nothing more we can do with a directory
350        return 0;
351    }
352
353        if (!stat(newFile, &newsb)) {
354                // If file times are equal, don't copy
355                if (newsb.st_mtime == oldsb.st_mtime) {
356#if 0
357                        printf("+++ makecopy: %s is up to date\n", newFile);
358#endif
359                        return 0;
360                }
361        }
362
363
364        char   fullPathName[ MAX_PATH + 1 ];
365        LPTSTR filenamePart = NULL;
366
367        char buffer[8192];
368        DWORD bytesRead = 0;
369        DWORD bytesWritten = 0;
370
371        // find out required size
372        GetFullPathName(oldFile, MAX_PATH, fullPathName, &filenamePart);
373
374        // If we need to insert #line, the copying is a bit involved.
375        if (insertHashLine == TRUE) {
376                struct _utimbuf utim;
377
378            printf("    #Installing %s into %s\n", oldFile, newFile);
379
380                utim.actime = oldsb.st_atime;
381                utim.modtime = oldsb.st_mtime;  // modification time
382
383                HANDLE hNewFile = CreateFile(newFile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL,
384                                                CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
385                                                NULL);
386                if (hNewFile == INVALID_HANDLE_VALUE) {
387                        return ReportError("CreateFile");
388                }
389
390                HANDLE hOldFile = CreateFile(oldFile, GENERIC_READ, FILE_SHARE_READ, NULL,
391                                                        OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
392                                                        NULL);
393                if (hOldFile == INVALID_HANDLE_VALUE) {
394                        return ReportError("CreateFile");
395                }
396
397                // Insert first line.
398                sprintf(buffer, "#line 1 \"%s\"\r\n", fullPathName);
399
400                // convert to unix.
401                UnflipSlashes(buffer);
402
403                WriteFile(hNewFile, buffer, strlen(buffer), &bytesWritten, NULL);
404
405                // Copy file.
406                do {
407                        if (!ReadFile(hOldFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
408                                return ReportError("ReadFile");
409                        }
410
411                        if (!WriteFile(hNewFile, buffer, bytesRead, &bytesWritten, NULL)) {
412                                return ReportError("WriteFile");
413                        }
414
415                } while (bytesRead > 0);
416
417                CloseHandle(hNewFile);
418                CloseHandle(hOldFile);
419
420                // make copy have same time
421                _utime(newFile, &utim);
422
423        // If we don't need to do a #line, use an API to copy the file..
424        } else {
425
426                BOOL isNTFS = FALSE;
427               
428                // Find out what kind of volume this is.
429                if ( trySymlink ) {
430                        char rootPathName[MAX_PATH];
431                        char *c = strchr(fullPathName, '\\');
432
433                        if (c != NULL) {
434                TCHAR  fileSystemName[50];
435
436                                strncpy(rootPathName, fullPathName, (c - fullPathName) + 1);
437
438                                if (!GetVolumeInformation(rootPathName, NULL, 0, NULL, NULL, NULL, fileSystemName, sizeof(rootPathName))) {
439                                        return ReportError("GetVolumeInformation");
440                                }
441
442                                isNTFS = (strcmp(fileSystemName, "NTFS") == 0);
443                        }
444                }
445
446                if (isNTFS) {
447                    printf("    Symlinking %s into %s\n", oldFile, newFile);
448
449                        if (! hardSymLink(oldFile, newFile) ) {
450                                return 1;
451                        }
452                } else {
453                    printf("    Installing %s into %s\n", oldFile, newFile);
454
455                        if( ! CopyFile(oldFile, newFile, FALSE) ) {
456                                ReportError("CopyFile");
457                                return 1;
458                        }
459                }
460        }
461
462    return 0;
463}
464
465void Usage(void)
466{
467    fprintf(stderr, "makecopy: [-cisx] <file1> [file2 ... fileN] <dir-path>\n");
468    fprintf(stderr, "     -c  copy [default], cancels -s\n");
469    fprintf(stderr, "     -i  add #line directive\n");
470    fprintf(stderr, "     -r  recurse subdirectories\n");
471    fprintf(stderr, "     -s  use symlinks on NT when possible\n");
472    fprintf(stderr, "     -x  cancel -i\n");
473}
474
475
476int main( int argc, char *argv[] )
477{
478    char old_path[4096];
479    char new_path[4096];
480    char *oldFileName; // points to where file name starts in old_path
481    char *newFileName; // points to where file name starts in new_path
482    WIN32_FIND_DATA findFileData;
483    int rv = 0;
484        int i = 1;
485
486    if (argc < 3) {
487        Usage();
488        return 2;
489    }
490
491    // parse option flags
492    for ( ; *argv[i] == '-' ; ++i) {
493        char *opt = argv[i]+1;
494        for ( ; *opt; ++opt) {
495            switch (*opt) {
496            case 'c':
497                trySymlink = FALSE;
498                break;
499            case 'i':
500                insertHashLine = TRUE;
501                break;
502            case 'r':
503                recurse = TRUE;
504                break;
505            case 's':
506                trySymlink = TRUE;
507                break;
508            case 'x':
509                insertHashLine = FALSE;
510                break;
511            default:
512                Usage();
513                return 2;
514            }
515        }
516    }
517
518    if ( trySymlink ) {
519                OSVERSIONINFO osvi;
520
521        // Symlinking supported only on WinNT, not Win9x
522                // Is this Windows NT?
523                osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
524
525                if (!GetVersionEx(&osvi)) {
526                        return ReportError();
527                }
528
529                trySymlink = (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT);
530
531                if ( trySymlink ) {
532
533                        hDLL = LoadLibrary("Kernel32");
534                        if (hDLL != NULL)
535                        {
536                                lpfnDllBackupWrite = (LPFNBackupWrite)GetProcAddress(hDLL, "BackupWrite");
537                                lpfnDllCreateFileW = (LPFNCreateFileW)GetProcAddress(hDLL, "CreateFileW");
538                                lpfnDllGetFullPathNameW = (LPFNGetFullPathNameW) GetProcAddress(hDLL, "GetFullPathNameW");
539
540                                if ((!lpfnDllBackupWrite) || (!lpfnDllCreateFileW) || (!lpfnDllGetFullPathNameW))
541                                {
542                                        // handle the error
543                                        int r = ReportError("GetProcAddress");
544
545                                        FreeLibrary(hDLL);       
546                                        return r;
547                                }
548                        } else {
549                                return ReportError();
550                        }
551                }
552        }
553
554        // destination path is last argument
555        strcpy(new_path, argv[argc-1]);
556
557        // append backslash to path if not already there
558        if (new_path[strlen(new_path)] != '\\') {
559                strcat(new_path, "\\");
560        }
561
562    //sprintf(new_path, "%s\\", argv[i+1]);
563    FlipSlashes(new_path);
564    newFileName = new_path + strlen(new_path);
565
566    if( MakeDir(new_path) < 0 ) {
567        fprintf(stderr, "\n+++ makecopy: unable to create directory %s\n", new_path);
568        return 1;
569    }
570
571        // copy all named source files
572        while (i < (argc - 1)) {
573                strcpy(old_path, argv[i]);
574
575                FlipSlashes(old_path);
576                oldFileName = strrchr(old_path, '\\');
577                if (oldFileName) {
578                        oldFileName++;
579                } else {
580                        oldFileName = old_path;
581                }
582
583                HANDLE hFindFile = FindFirstFile(old_path, &findFileData);
584
585                if (hFindFile != INVALID_HANDLE_VALUE) {
586                        do {
587                                strcpy(oldFileName, findFileData.cFileName);
588                                strcpy(newFileName, findFileData.cFileName);
589                                rv = CopyIfNecessary(old_path, new_path);
590
591                        } while (FindNextFile(hFindFile, &findFileData) != 0);
592                } else {
593                        fprintf(stderr, "\n+++ makecopy: no such file: %s\n", old_path);
594                }
595
596                FindClose(hFindFile);
597                i++;
598        }
599        if ( trySymlink ) {
600                FreeLibrary(hDLL);
601        }
602    return 0;
603}
Note: See TracBrowser for help on using the repository browser.