Index: src/export.c ================================================================== --- src/export.c +++ src/export.c @@ -1,7 +1,7 @@ /* -** Copyright (c) 2010 D. Richard Hipp +** Copyright © 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) @@ -136,11 +136,11 @@ if( markfile_in!=0 ){ Stmt qb,qc; char line[100]; FILE *f; - f = fopen(markfile_in, "r"); + f = fossil_fopen(markfile_in, "r"); if( f==0 ){ fossil_panic("cannot open %s for reading", markfile_in); } db_prepare(&qb, "INSERT OR IGNORE INTO oldblob VALUES (:rid)"); db_prepare(&qc, "INSERT OR IGNORE INTO oldcommit VALUES (:rid)"); @@ -325,11 +325,11 @@ db_finalize(&q); bag_clear(&vers); if( markfile_out!=0 ){ FILE *f; - f = fopen(markfile_out, "w"); + f = fossil_fopen(markfile_out, "w"); if( f == 0 ){ fossil_panic("cannot open %s for writing", markfile_out); } db_prepare(&q, "SELECT rid FROM oldblob"); while( db_step(&q)==SQLITE_ROW ){ Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -1,7 +1,7 @@ /* -** Copyright (c) 2006 D. Richard Hipp +** Copyright © 2006 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) @@ -32,10 +32,11 @@ /* ** On Windows, include the Platform SDK header file. */ #ifdef _WIN32 +# include # include #endif /* ** The file status information from the most recent stat() call. @@ -68,12 +69,12 @@ }else{ return stat(zFilename, buf); } #else int rc = 0; - char *zMbcs = fossil_utf8_to_mbcs(zFilename); - rc = stat(zMbcs, buf); + wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename); + rc = _wstati64(zMbcs, buf); fossil_mbcs_free(zMbcs); return rc; #endif } @@ -298,13 +299,17 @@ /* ** Wrapper around the access() system call. */ int file_access(const char *zFilename, int flags){ - char *zMbcs = fossil_utf8_to_mbcs(zFilename); - int rc = access(zMbcs, flags); +#ifdef _WIN32 + wchar_t *zMbcs = fossil_utf8_to_unicode(zFilename); + int rc = _waccess(zMbcs, flags); fossil_mbcs_free(zMbcs); +#else + int rc = access(zFilename, flags); +#endif return rc; } /* ** Find an unused filename similar to zBase with zSuffix appended. @@ -389,13 +394,17 @@ /* ** Delete a file. */ void file_delete(const char *zFilename){ - char *z = fossil_utf8_to_mbcs(zFilename); - unlink(z); +#ifdef _WIN32 + wchar_t *z = fossil_utf8_to_unicode(zFilename); + _wunlink(z); fossil_mbcs_free(z); +#else + unlink(zFilename); +#endif } /* ** Create the directory named in the argument, if it does not already ** exist. If forceFlag is 1, delete any prior non-directory object @@ -410,12 +419,12 @@ file_delete(zName); } if( rc!=1 ){ #if defined(_WIN32) int rc; - char *zMbcs = fossil_utf8_to_mbcs(zName); - rc = mkdir(zMbcs); + wchar_t *zMbcs = fossil_utf8_to_unicode(zName); + rc = _wmkdir(zMbcs); fossil_mbcs_free(zMbcs); return rc; #else return mkdir(zName, 0755); #endif @@ -561,24 +570,24 @@ } /* ** Get the current working directory. ** -** On windows, the name is converted from MBCS to UTF8 and all '\\' +** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** unix. */ void file_getcwd(char *zBuf, int nBuf){ #ifdef _WIN32 char *zPwdUtf8; int nPwd; int i; - char zPwd[2000]; - if( getcwd(zPwd, sizeof(zPwd)-1)==0 ){ + wchar_t zPwd[2000]; + if( _wgetcwd(zPwd, sizeof(zPwd)/sizeof(zPwd[0])-1)==0 ){ fossil_fatal("cannot find the current working directory."); } - zPwdUtf8 = fossil_mbcs_to_utf8(zPwd); + zPwdUtf8 = fossil_unicode_to_utf8(zPwd); nPwd = strlen(zPwdUtf8); if( nPwd > nBuf-1 ){ fossil_fatal("pwd too big: max %d\n", nBuf-1); } for(i=0; zPwdUtf8[i]; i++) if( zPwdUtf8[i]=='\\' ) zPwdUtf8[i] = '/'; @@ -928,14 +937,14 @@ unsigned int i, j; const char *zDir = "."; int cnt = 0; #if defined(_WIN32) - char zTmpPath[MAX_PATH]; + wchar_t zTmpPath[MAX_PATH]; - if( GetTempPath(sizeof(zTmpPath), zTmpPath) ){ - azDirs[0] = zTmpPath; + if( GetTempPathW(MAX_PATH, zTmpPath) ){ + azDirs[0] = fossil_unicode_to_utf8(zTmpPath); } azDirs[1] = fossil_getenv("TEMP"); azDirs[2] = fossil_getenv("TMP"); #endif @@ -994,10 +1003,34 @@ rc = blob_compare(&onDisk, pContent); blob_reset(&onDisk); return rc==0; } +/* +** Portable unicode implementation of opendir() +*/ +#if INTERFACE + +#if defined(_WIN32) +# include +# define FOSSIL_DIR _WDIR +# define fossil_dirent _wdirent +# define fossil_opendir _wopendir +# define fossil_readdir _wreaddir +# define fossil_closedir _wclosedir +#else +# include +# define FOSSIL_DIR DIR +# define fossil_dirent dirent +# define fossil_opendir opendir +# define fossil_readdir readdir +# define fossil_closedir closedir +#endif + +#endif /* INTERFACE */ + + /************************************************************************** ** The following routines translate between MBCS and UTF8 on windows. ** Since everything is always UTF8 on unix, these routines are no-ops ** there. @@ -1014,10 +1047,29 @@ return sqlite3_win32_mbcs_to_utf8(zMbcs); #else return (char*)zMbcs; /* No-op on unix */ #endif } + +/* +** Translate Unicode to UTF8. Return a pointer to the translated text. +** Call fossil_mbcs_free() to deallocate any memory used to store the +** returned pointer when done. +*/ +char *fossil_unicode_to_utf8(const void *zUnicode){ +#ifdef _WIN32 + int nByte = WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, 0, 0, 0, 0); + char *zUtf = sqlite3_malloc( nByte ); + if( zUtf==0 ){ + return 0; + } + WideCharToMultiByte(CP_UTF8, 0, zUnicode, -1, zUtf, nByte, 0, 0); + return zUtf; +#else + return (char *)zUnicode; /* No-op on unix */ +#endif +} /* ** Translate UTF8 to MBCS for use in system calls. Return a pointer to the ** translated text.. Call fossil_mbcs_free() to deallocate any memory ** used to store the returned pointer when done. @@ -1028,18 +1080,41 @@ return sqlite3_win32_utf8_to_mbcs(zUtf8); #else return (char*)zUtf8; /* No-op on unix */ #endif } + +/* +** Translate UTF8 to unicode for use in system calls. Return a pointer to the +** translated text.. Call fossil_mbcs_free() to deallocate any memory +** used to store the returned pointer when done. +*/ +void *fossil_utf8_to_unicode(const char *zUtf8){ +#ifdef _WIN32 + int nByte = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); + wchar_t *zUnicode = sqlite3_malloc( nByte * 2 ); + if( zUnicode==0 ){ + return 0; + } + MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nByte); + return zUnicode; +#else + return (void *)zUtf8; /* No-op on unix */ +#endif +} /* ** Return the value of an environment variable as UTF8. */ char *fossil_getenv(const char *zName){ - char *zValue = getenv(zName); #ifdef _WIN32 - if( zValue ) zValue = fossil_mbcs_to_utf8(zValue); + wchar_t *uName = fossil_utf8_to_unicode(zName); + void *zValue = _wgetenv(uName); + fossil_mbcs_free(uName); + if( zValue ) zValue = fossil_unicode_to_utf8(zValue); +#else + char *zValue = getenv(zName); #endif return zValue; } /* @@ -1085,11 +1160,11 @@ /* ** Translate MBCS to UTF8. Return a pointer. Call fossil_mbcs_free() ** to deallocate any memory used to store the returned pointer when done. */ -void fossil_mbcs_free(char *zOld){ +void fossil_mbcs_free(void *zOld){ #ifdef _WIN32 extern void sqlite3_free(void*); sqlite3_free(zOld); #else /* No-op on unix */ @@ -1098,10 +1173,16 @@ /* ** Like fopen() but always takes a UTF8 argument. */ FILE *fossil_fopen(const char *zName, const char *zMode){ - char *zMbcs = fossil_utf8_to_mbcs(zName); - FILE *f = fopen(zMbcs, zMode); - fossil_mbcs_free(zMbcs); +#ifdef _WIN32 + wchar_t *uMode = fossil_utf8_to_unicode(zMode); + wchar_t *uName = fossil_utf8_to_unicode(zName); + FILE *f = _wfopen(uName, uMode); + fossil_mbcs_free(uName); + fossil_mbcs_free(uMode); +#else + FILE *f = fopen(zName, zMode); +#endif return f; } Index: src/import.c ================================================================== --- src/import.c +++ src/import.c @@ -1,7 +1,7 @@ /* -** Copyright (c) 2010 D. Richard Hipp +** Copyright © 2010 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) @@ -732,11 +732,11 @@ verify_all_options(); if( g.argc!=3 && g.argc!=4 ){ usage("REPOSITORY-NAME"); } if( g.argc==4 ){ - pIn = fopen(g.argv[3], "rb"); + pIn = fossil_fopen(g.argv[3], "rb"); }else{ pIn = stdin; fossil_binary_mode(pIn); } if( !incrFlag ){ Index: src/merge.c ================================================================== --- src/merge.c +++ src/merge.c @@ -90,19 +90,22 @@ ** show what would have happened. ** ** --case-sensitive BOOL Overwrite the case-sensitive setting. If false, ** files whose names differ only in case are taken ** to be the same file. +** +** --force | -f Force the merge even if it would be a no-op. */ void merge_cmd(void){ int vid; /* Current version "V" */ int mid; /* Version we are merging from "M" */ int pid; /* The pivot version - most recent common ancestor P */ int detailFlag; /* True if the --detail option is present */ int pickFlag; /* True if the --cherrypick option is present */ int backoutFlag; /* True if the --backout option is present */ int nochangeFlag; /* True if the --nochange or -n option is present */ + int forceFlag; /* True if the --force or -f option is present */ const char *zBinGlob; /* The value of --binary */ const char *zPivot; /* The value of --baseline */ int debugFlag; /* True if --debug is present */ int nChng; /* Number of file name changes */ int *aChng; /* An array of file name changes */ @@ -125,10 +128,11 @@ pickFlag = find_option("cherrypick",0,0)!=0; backoutFlag = find_option("backout",0,0)!=0; debugFlag = find_option("debug",0,0)!=0; zBinGlob = find_option("binary",0,1); nochangeFlag = find_option("nochange","n",0)!=0; + forceFlag = find_option("force","f",0)!=0; zPivot = find_option("baseline",0,1); capture_case_sensitive_option(); if( g.argc!=3 ){ usage("VERSION"); } @@ -176,12 +180,13 @@ mid = t; } if( !is_a_version(pid) ){ fossil_fatal("not a version: record #%d", pid); } - if( mid==pid ){ - fossil_print("This merge is a no-op.\n"); + if( !forceFlag && (mid==pid || vid==pid || mid==vid) ){ + fossil_print("Merge skipped because it is a no-op. " + " Use --force to override.\n"); return; } if( detailFlag ){ print_checkin_description(mid, 12, "merge-from:"); print_checkin_description(pid, 12, "baseline:"); Index: src/rebuild.c ================================================================== --- src/rebuild.c +++ src/rebuild.c @@ -1,7 +1,7 @@ /* -** Copyright (c) 2007 D. Richard Hipp +** Copyright © 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) @@ -18,11 +18,10 @@ ** This file contains code used to rebuild the database. */ #include "config.h" #include "rebuild.h" #include -#include #include /* ** Make changes to the stable part of the schema (the part that is not ** simply deleted and reconstructed on a rebuild) to bring the schema @@ -818,28 +817,28 @@ /* ** Recursively read all files from the directory zPath and install ** every file read as a new artifact in the repository. */ void recon_read_dir(char *zPath){ - DIR *d; - struct dirent *pEntry; + FOSSIL_DIR *d; + struct fossil_dirent *pEntry; Blob aContent; /* content of the just read artifact */ static int nFileRead = 0; - char *zMbcsPath; + void *zUnicodePath; char *zUtf8Name; - zMbcsPath = fossil_utf8_to_mbcs(zPath); - d = opendir(zMbcsPath); + zUnicodePath = fossil_utf8_to_unicode(zPath); + d = fossil_opendir(zUnicodePath); if( d ){ - while( (pEntry=readdir(d))!=0 ){ + while( (pEntry=fossil_readdir(d))!=0 ){ Blob path; char *zSubpath; if( pEntry->d_name[0]=='.' ){ continue; } - zUtf8Name = fossil_mbcs_to_utf8(pEntry->d_name); + zUtf8Name = fossil_unicode_to_utf8(pEntry->d_name); zSubpath = mprintf("%s/%s", zPath, zUtf8Name); fossil_mbcs_free(zUtf8Name); if( file_isdir(zSubpath)==1 ){ recon_read_dir(zSubpath); } @@ -854,16 +853,16 @@ blob_reset(&aContent); free(zSubpath); fossil_print("\r%d", ++nFileRead); fflush(stdout); } - closedir(d); + fossil_closedir(d); }else { fossil_panic("encountered error %d while trying to open \"%s\".", errno, g.argv[3]); } - fossil_mbcs_free(zMbcsPath); + fossil_mbcs_free(zUnicodePath); } /* ** COMMAND: reconstruct* ** Index: src/vfile.c ================================================================== --- src/vfile.c +++ src/vfile.c @@ -1,7 +1,7 @@ /* -** Copyright (c) 2007 D. Richard Hipp +** Copyright © 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) @@ -19,15 +19,10 @@ */ #include "config.h" #include "vfile.h" #include #include -#if defined(__DMC__) -#include "dirent.h" -#else -#include -#endif /* ** The input is guaranteed to be a 40-character well-formed UUID. ** Find its rid. */ @@ -381,18 +376,18 @@ ** Any files or directories that match the glob pattern pIgnore are ** excluded from the scan. Name matching occurs after the first ** nPrefix characters are elided from the filename. */ void vfile_scan(Blob *pPath, int nPrefix, int allFlag, Glob *pIgnore){ - DIR *d; + FOSSIL_DIR *d; int origSize; const char *zDir; - struct dirent *pEntry; + struct fossil_dirent *pEntry; int skipAll = 0; static Stmt ins; static int depth = 0; - char *zMbcs; + void *zMbcs; origSize = blob_size(pPath); if( pIgnore ){ blob_appendf(pPath, "/"); if( glob_match(pIgnore, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1; @@ -407,22 +402,22 @@ ); } depth++; zDir = blob_str(pPath); - zMbcs = fossil_utf8_to_mbcs(zDir); - d = opendir(zMbcs); + zMbcs = fossil_utf8_to_unicode(zDir); + d = fossil_opendir(zMbcs); if( d ){ - while( (pEntry=readdir(d))!=0 ){ + while( (pEntry=fossil_readdir(d))!=0 ){ char *zPath; char *zUtf8; if( pEntry->d_name[0]=='.' ){ if( !allFlag ) continue; if( pEntry->d_name[1]==0 ) continue; if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue; } - zUtf8 = fossil_mbcs_to_utf8(pEntry->d_name); + zUtf8 = fossil_unicode_to_utf8(pEntry->d_name); blob_appendf(pPath, "/%s", zUtf8); fossil_mbcs_free(zUtf8); zPath = blob_str(pPath); if( glob_match(pIgnore, &zPath[nPrefix+1]) ){ /* do nothing */ @@ -435,11 +430,11 @@ db_step(&ins); db_reset(&ins); } blob_resize(pPath, origSize); } - closedir(d); + fossil_closedir(d); } fossil_mbcs_free(zMbcs); depth--; if( depth==0 ){ Index: win/include/dirent.h ================================================================== --- win/include/dirent.h +++ win/include/dirent.h @@ -61,71 +61,70 @@ #include #include #include -typedef struct dirent -{ - char d_name[MAX_PATH + 1]; /* current dir entry (multi-byte char string) */ - WIN32_FIND_DATAA data; /* file attributes */ -} dirent; - - -typedef struct DIR -{ - dirent current; /* Current directory entry */ +typedef struct _wdirent +{ + wchar_t d_name[MAX_PATH + 1]; /* current dir entry (unicode char string) */ + WIN32_FIND_DATAW data; /* file attributes */ +} _wdirent; + + +typedef struct _WDIR +{ + _wdirent current; /* Current directory entry */ int cached; /* Indicates un-processed entry in memory */ HANDLE search_handle; /* File search handle */ - char patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */ -} DIR; + wchar_t patt[MAX_PATH + 3]; /* search pattern (3 = pattern + "\\*\0") */ +} _WDIR; /* Forward declarations */ -static DIR *opendir (const char *dirname); -static struct dirent *readdir (DIR *dirp); -static int closedir (DIR *dirp); -static void rewinddir(DIR* dirp); +static _WDIR *_wopendir (const wchar_t *dirname); +static struct _wdirent *_wreaddir (_WDIR *dirp); +static int _wclosedir (_WDIR *dirp); /* Use the new safe string functions introduced in Visual Studio 2005 */ #if defined(_MSC_VER) && _MSC_VER >= 1400 -# define STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE) +# define STRNCPY(dest,src,size) wcsncpy_s((dest),(size),(src),_TRUNCATE) #else -# define STRNCPY(dest,src,size) strncpy((dest),(src),(size)) +# define STRNCPY(dest,src,size) wcsncpy((dest),(src),(size)) #endif /***************************************************************************** * Open directory stream DIRNAME for read and return a pointer to the * internal working area that is used to retrieve individual directory * entries. */ -static DIR *opendir(const char *dirname) +static _WDIR *_wopendir(const wchar_t *dirname) { - DIR *dirp; + _WDIR *dirp; assert (dirname != NULL); - assert (strlen (dirname) < MAX_PATH); + assert (wcslen (dirname) < MAX_PATH); - /* construct new DIR structure */ - dirp = (DIR*) malloc (sizeof (struct DIR)); + /* construct new _WDIR structure */ + dirp = (_WDIR*) malloc (sizeof (struct _WDIR)); if (dirp != NULL) { - char *p; + wchar_t *p; /* take directory name... */ STRNCPY (dirp->patt, dirname, sizeof(dirp->patt)); dirp->patt[MAX_PATH] = '\0'; /* ... and append search pattern to it */ - p = strchr (dirp->patt, '\0'); + p = wcschr (dirp->patt, '\0'); if (dirp->patt < p && *(p-1) != '\\' && *(p-1) != ':') { *p++ = '\\'; } *p++ = '*'; *p = '\0'; /* open stream and retrieve first file */ - dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data); + dirp->search_handle = FindFirstFileW (dirp->patt, &dirp->current.data); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* invalid search pattern? */ free (dirp); return NULL; } @@ -143,11 +142,11 @@ * containing the name of the entry in d_name field. Individual directory * entries returned by this very function include regular files, * sub-directories, pseudo-directories "." and "..", but also volume labels, * hidden files and system files may be returned. */ -static struct dirent *readdir(DIR *dirp) +static struct _wdirent *_wreaddir(_WDIR *dirp) { assert (dirp != NULL); if (dirp->search_handle == INVALID_HANDLE_VALUE) { /* directory stream was opened/rewound incorrectly or ended normally */ @@ -158,11 +157,11 @@ if (dirp->cached != 0) { /* a valid directory entry already in memory */ dirp->cached = 0; } else { /* read next directory entry from disk */ - if (FindNextFileA (dirp->search_handle, &dirp->current.data) == FALSE) { + if (FindNextFileW (dirp->search_handle, &dirp->current.data) == FALSE) { /* the very last file has been processed or an error occured */ FindClose (dirp->search_handle); dirp->search_handle = INVALID_HANDLE_VALUE; return NULL; } @@ -181,11 +180,11 @@ /***************************************************************************** * Close directory stream opened by opendir() function. Close of the * directory stream invalidates the DIR structure as well as any previously * read directory entry. */ -static int closedir(DIR *dirp) +static int _wclosedir(_WDIR *dirp) { assert (dirp != NULL); /* release search handle */ if (dirp->search_handle != INVALID_HANDLE_VALUE) { @@ -195,36 +194,7 @@ /* release directory handle */ free (dirp); return 0; } - - -/***************************************************************************** - * Resets the position of the directory stream to which dirp refers to the - * beginning of the directory. It also causes the directory stream to refer - * to the current state of the corresponding directory, as a call to opendir() - * would have done. If dirp does not refer to a directory stream, the effect - * is undefined. - */ -static void rewinddir(DIR* dirp) -{ - /* release search handle */ - if (dirp->search_handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->search_handle); - dirp->search_handle = INVALID_HANDLE_VALUE; - } - - /* open new search handle and retrieve first file */ - dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->current.data); - if (dirp->search_handle == INVALID_HANDLE_VALUE) { - /* invalid search pattern? */ - free (dirp); - return; - } - - /* there is an un-processed directory entry in memory now */ - dirp->cached = 1; -} - #endif /*DIRENT_H*/