Index: src/add.c ================================================================== --- src/add.c +++ src/add.c @@ -241,11 +241,11 @@ for(i=2; i<g.argc; i++){ char *zName; int isDir; Blob fullName; - file_canonical_name(g.argv[i], &fullName); + file_canonical_name(g.argv[i], &fullName, 0); zName = blob_str(&fullName); isDir = file_wd_isdir(zName); if( isDir==1 ){ vfile_scan(&fullName, nRoot-1, includeDotFiles, pIgnore); }else if( isDir==0 ){ Index: src/allrepo.c ================================================================== --- src/allrepo.c +++ src/allrepo.c @@ -168,11 +168,11 @@ }else if( !file_is_canonical(zFilename) ){ Blob cname; char *zRepo = mprintf("repo:%s", zFilename); db_unset(zRepo, 1); free(zRepo); - file_canonical_name(zFilename, &cname); + file_canonical_name(zFilename, &cname, 0); zRepo = mprintf("repo:%s", blob_str(&cname)); db_set(zRepo, "1", 1); free(zRepo); } } Index: src/blob.c ================================================================== --- src/blob.c +++ src/blob.c @@ -90,10 +90,13 @@ int fossil_isupper(char c){ return c>='A' && c<='Z'; } int fossil_isdigit(char c){ return c>='0' && c<='9'; } int fossil_tolower(char c){ return fossil_isupper(c) ? c - 'A' + 'a' : c; } +int fossil_toupper(char c){ + return fossil_islower(c) ? c - 'a' + 'A' : c; +} int fossil_isalpha(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z'); } int fossil_isalnum(char c){ return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9'); @@ -791,11 +794,11 @@ zName = mprintf("%s", zFilename); }else{ zName = zBuf; memcpy(zName, zFilename, nName+1); } - nName = file_simplify_name(zName, nName); + nName = file_simplify_name(zName, nName, 0); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; #if defined(_WIN32) /* Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -55,11 +55,11 @@ int isChnged = db_column_int(&q,2); int isNew = db_column_int(&q,3)==0; int isRenamed = db_column_int(&q,4); char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); if( cwdRelative ){ - file_relative_name(zFullName, &rewrittenPathname); + file_relative_name(zFullName, &rewrittenPathname, 0); zDisplayName = blob_str(&rewrittenPathname); if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ zDisplayName += 2; /* no unnecessary ./ prefix */ } } @@ -313,11 +313,11 @@ blob_zero(&rewrittenPathname); while( db_step(&q)==SQLITE_ROW ){ zDisplayName = zPathname = db_column_text(&q, 0); if( cwdRelative ) { char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); - file_relative_name(zFullName, &rewrittenPathname); + file_relative_name(zFullName, &rewrittenPathname, 0); free(zFullName); zDisplayName = blob_str(&rewrittenPathname); if( zDisplayName[0]=='.' && zDisplayName[1]=='/' ){ zDisplayName += 2; /* no unnecessary ./ prefix */ } @@ -826,11 +826,11 @@ if( lastNl>1000 ) return; /* Binary if any line longer than 1000 */ } } if( nCrNl ){ char c; - file_relative_name(zFilename, &fname); + file_relative_name(zFilename, &fname, 0); blob_zero(&ans); zMsg = mprintf( "%s contains CR/NL line endings; commit anyhow (yes/no/all)?", blob_str(&fname)); prompt_user(zMsg, &ans); Index: src/clone.c ================================================================== --- src/clone.c +++ src/clone.c @@ -150,11 +150,11 @@ db_set("last-sync-url", g.argv[2], 0); if( g.zSSLIdentity!=0 ){ /* If the --ssl-identity option was specified, store it as a setting */ Blob fn; blob_zero(&fn); - file_canonical_name(g.zSSLIdentity, &fn); + file_canonical_name(g.zSSLIdentity, &fn, 0); db_set("ssl-identity", blob_str(&fn), 0); blob_reset(&fn); } db_multi_exec( "REPLACE INTO config(name,value,mtime)" Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -1035,11 +1035,11 @@ } if( db_open_local()==0 ){ fossil_fatal("not in a local checkout"); return; } - file_canonical_name(g.argv[2], &repo); + file_canonical_name(g.argv[2], &repo, 0); zRepo = blob_str(&repo); if( file_access(zRepo, 0) ){ fossil_fatal("no such file: %s", zRepo); } db_open_or_attach(zRepo, "test_repo"); @@ -1698,23 +1698,26 @@ Blob full; if( zName==0 ){ if( !g.localOpen ) return; zName = db_repository_filename(); } - file_canonical_name(zName, &full); + file_canonical_name(zName, &full, 0); db_swap_connections(); db_multi_exec( "INSERT OR IGNORE INTO global_config(name,value)" "VALUES('repo:%q',1)", blob_str(&full) ); if( g.localOpen && g.zLocalRoot && g.zLocalRoot[0] ){ + Blob localRoot; + file_canonical_name(g.zLocalRoot, &localRoot, 1); db_multi_exec( "REPLACE INTO global_config(name, value)" "VALUES('ckout:%q','%q');", - g.zLocalRoot, blob_str(&full) + blob_str(&localRoot), blob_str(&full) ); + blob_reset(&localRoot); } db_swap_connections(); blob_reset(&full); } @@ -1749,11 +1752,11 @@ usage("REPOSITORY-FILENAME ?VERSION?"); } if( !allowNested && db_open_local() ){ fossil_panic("already within an open tree rooted at %s", g.zLocalRoot); } - file_canonical_name(g.argv[2], &path); + file_canonical_name(g.argv[2], &path, 0); db_open_repository(blob_str(&path)); db_init_database("./_FOSSIL_", zLocalSchema, (char*)0); db_delete_on_failure("./_FOSSIL_"); db_open_local(); db_lset("repository", g.argv[2]); Index: src/diffcmd.c ================================================================== --- src/diffcmd.c +++ src/diffcmd.c @@ -19,10 +19,19 @@ */ #include "config.h" #include "diffcmd.h" #include <assert.h> +/* +** Use the right null device for the platform. +*/ +#if defined(_WIN32) +# define NULL_DEVICE "NUL" +#else +# define NULL_DEVICE "/dev/null" +#endif + /* ** Print the "Index:" message that patches wants to see at the top of a diff. */ void diff_print_index(const char *zFile, int diffFlags){ if( (diffFlags & (DIFF_SIDEBYSIDE|DIFF_BRIEF))==0 ){ @@ -76,11 +85,11 @@ const char *zName2; /* Name of zFile2 for display */ /* Read content of zFile2 into memory */ blob_zero(&file2); if( file_wd_size(zFile2)<0 ){ - zName2 = "/dev/null"; + zName2 = NULL_DEVICE; }else{ if( file_wd_islink(zFile2) ){ blob_read_link(&file2, zFile2); }else{ blob_read_from_file(&file2, zFile2); @@ -283,11 +292,11 @@ char *zFullName = mprintf("%s%s", g.zLocalRoot, zPathname); char *zToFree = zFullName; int showDiff = 1; if( isDeleted ){ fossil_print("DELETED %s\n", zPathname); - if( !asNewFile ){ showDiff = 0; zFullName = "/dev/null"; } + if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; } }else if( file_access(zFullName, 0) ){ fossil_print("MISSING %s\n", zPathname); if( !asNewFile ){ showDiff = 0; } }else if( isNew ){ fossil_print("ADDED %s\n", zPathname); Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -28,10 +28,17 @@ #include <unistd.h> #include <string.h> #include <errno.h> #include "file.h" +/* +** On Windows, include the Platform SDK header file. +*/ +#ifdef _WIN32 +# include <windows.h> +#endif + /* ** The file status information from the most recent stat() call. ** ** Use _stati64 rather than stat on windows, in order to handle files ** larger than 2GB. @@ -168,11 +175,11 @@ zName = mprintf("%s", zLinkFile); }else{ zName = zBuf; memcpy(zName, zLinkFile, nName+1); } - nName = file_simplify_name(zName, nName); + nName = file_simplify_name(zName, nName, 0); for(i=1; i<nName; i++){ if( zName[i]=='/' ){ zName[i] = 0; if( file_mkdir(zName, 1) ){ fossil_fatal_recursive("unable to create directory %s", zName); @@ -259,11 +266,11 @@ int file_isdir(const char *zFilename){ int rc; if( zFilename ){ char *zFN = mprintf("%s", zFilename); - file_simplify_name(zFN, -1); + file_simplify_name(zFN, -1, 0); rc = getStat(zFN, 0); free(zFN); }else{ rc = getStat(0, 0); } @@ -276,11 +283,11 @@ int file_wd_isdir(const char *zFilename){ int rc; if( zFilename ){ char *zFN = mprintf("%s", zFilename); - file_simplify_name(zFN, -1); + file_simplify_name(zFN, -1, 0); rc = getStat(zFN, 1); free(zFN); }else{ rc = getStat(0, 1); } @@ -314,11 +321,11 @@ fossil_free(z); z = mprintf("%s-%s-%d", zBase, zSuffix, cnt++); } if( relFlag ){ Blob x; - file_relative_name(z, &x); + file_relative_name(z, &x, 0); fossil_free(z); z = blob_str(&x); } return z; } @@ -473,12 +480,14 @@ ** * removing any trailing and duplicate / ** * removing /./ ** * removing /A/../ ** ** Changes are made in-place. Return the new name length. +** If the slash parameter is non-zero, the trailing slash, if any, +** is retained. */ -int file_simplify_name(char *z, int n){ +int file_simplify_name(char *z, int n, int slash){ int i, j; if( n<0 ) n = strlen(z); /* On windows convert all \ characters to / */ #if defined(_WIN32) @@ -486,11 +495,13 @@ if( z[i]=='\\' ) z[i] = '/'; } #endif /* Removing trailing "/" characters */ - while( n>1 && z[n-1]=='/' ){ n--; } + if ( !slash ){ + while( n>1 && z[n-1]=='/' ){ n--; } + } /* Remove duplicate '/' characters. Except, two // at the beginning ** of a pathname is allowed since this is important on windows. */ for(i=j=1; i<n; i++){ z[j++] = z[i]; @@ -540,11 +551,11 @@ int i; char *z; for(i=2; i<g.argc; i++){ z = mprintf("%s", g.argv[i]); fossil_print("[%s] -> ", z); - file_simplify_name(z, -1); + file_simplify_name(z, -1, 0); fossil_print("[%s]\n", z); fossil_free(z); } } @@ -606,22 +617,45 @@ ** Compute a canonical pathname for a file or directory. ** Make the name absolute if it is relative. ** Remove redundant / characters ** Remove all /./ path elements. ** Convert /A/../ to just / +** If the slash parameter is non-zero, the trailing slash, if any, +** is retained. */ -void file_canonical_name(const char *zOrigName, Blob *pOut){ +void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){ if( file_is_absolute_path(zOrigName) ){ +#if defined(_WIN32) + char *zOut; +#endif blob_set(pOut, zOrigName); blob_materialize(pOut); +#if defined(_WIN32) + /* + ** On Windows, normalize the drive letter to upper case. + */ + zOut = blob_str(pOut); + if( fossil_isalpha(zOut[0]) && zOut[1]==':' ){ + zOut[0] = fossil_toupper(zOut[0]); + } +#endif }else{ char zPwd[2000]; file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName)); +#if defined(_WIN32) + /* + ** On Windows, normalize the drive letter to upper case. + */ + if( fossil_isalpha(zPwd[0]) && zPwd[1]==':' ){ + zPwd[0] = fossil_toupper(zPwd[0]); + } +#endif blob_zero(pOut); blob_appendf(pOut, "%//%/", zPwd, zOrigName); } - blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); + blob_resize(pOut, file_simplify_name(blob_buffer(pOut), + blob_size(pOut), slash)); } /* ** COMMAND: test-canonical-name ** Usage: %fossil test-canonical-name FILENAME... @@ -634,11 +668,11 @@ Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ char zBuf[100]; const char *zName = g.argv[i]; - file_canonical_name(zName, &x); + file_canonical_name(zName, &x, 0); fossil_print("[%s] -> [%s]\n", zName, blob_buffer(&x)); blob_reset(&x); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_size(zName)); fossil_print(" file_size = %s\n", zBuf); sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", file_wd_mtime(zName)); @@ -688,16 +722,18 @@ return zIn; } /* ** Compute a pathname for a file or directory that is relative -** to the current directory. +** to the current directory. If the slash parameter is non-zero, +** the trailing slash, if any, is retained. */ -void file_relative_name(const char *zOrigName, Blob *pOut){ +void file_relative_name(const char *zOrigName, Blob *pOut, int slash){ char *zPath; blob_set(pOut, zOrigName); - blob_resize(pOut, file_simplify_name(blob_buffer(pOut), blob_size(pOut))); + blob_resize(pOut, file_simplify_name(blob_buffer(pOut), + blob_size(pOut), slash)); zPath = file_without_drive_letter(blob_buffer(pOut)); if( zPath[0]=='/' ){ int i, j; Blob tmp; char *zPwd; @@ -753,11 +789,11 @@ void cmd_test_relative_name(void){ int i; Blob x; blob_zero(&x); for(i=2; i<g.argc; i++){ - file_relative_name(g.argv[i], &x); + file_relative_name(g.argv[i], &x, 0); fossil_print("%s\n", blob_buffer(&x)); blob_reset(&x); } } @@ -768,37 +804,46 @@ ** false, then simply return 0. ** ** The root of the tree is defined by the g.zLocalRoot variable. */ int file_tree_name(const char *zOrigName, Blob *pOut, int errFatal){ - int n; + Blob localRoot; + int nLocalRoot; + char *zLocalRoot; Blob full; int nFull; char *zFull; blob_zero(pOut); db_must_be_within_tree(); - file_canonical_name(zOrigName, &full); - n = strlen(g.zLocalRoot); - assert( n>0 && g.zLocalRoot[n-1]=='/' ); + file_canonical_name(g.zLocalRoot, &localRoot, 1); + nLocalRoot = blob_size(&localRoot); + zLocalRoot = blob_buffer(&localRoot); + assert( nLocalRoot>0 && zLocalRoot[nLocalRoot-1]=='/' ); + file_canonical_name(zOrigName, &full, 0); nFull = blob_size(&full); zFull = blob_buffer(&full); /* Special case. zOrigName refers to g.zLocalRoot directory. */ - if( nFull==n-1 && memcmp(g.zLocalRoot, zFull, nFull)==0 ){ + if( nFull==nLocalRoot-1 && memcmp(zLocalRoot, zFull, nFull)==0 ){ blob_append(pOut, ".", 1); + blob_reset(&localRoot); + blob_reset(&full); return 1; } - if( nFull<=n || memcmp(g.zLocalRoot, zFull, n) ){ + if( nFull<=nLocalRoot || memcmp(zLocalRoot, zFull, nLocalRoot) ){ + blob_reset(&localRoot); blob_reset(&full); if( errFatal ){ fossil_fatal("file outside of checkout tree: %s", zOrigName); } return 0; } - blob_append(pOut, &zFull[n], nFull-n); + blob_append(pOut, &zFull[nLocalRoot], nFull-nLocalRoot); + blob_reset(&localRoot); + blob_reset(&full); return 1; } /* ** COMMAND: test-tree-name @@ -861,25 +906,44 @@ /* ** Construct a random temporary filename into zBuf[]. */ void file_tempname(int nBuf, char *zBuf){ static const char *azDirs[] = { +#if defined(_WIN32) + 0, /* GetTempPath */ + 0, /* TEMP */ + 0, /* TMP */ +#else "/var/tmp", "/usr/tmp", "/tmp", "/temp", +#endif ".", }; static const unsigned char zChars[] = "abcdefghijklmnopqrstuvwxyz" "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789"; unsigned int i, j; const char *zDir = "."; int cnt = 0; + +#if defined(_WIN32) + char zTmpPath[MAX_PATH]; + + if( GetTempPath(sizeof(zTmpPath), zTmpPath) ){ + azDirs[0] = zTmpPath; + } + + azDirs[1] = fossil_getenv("TEMP"); + azDirs[2] = fossil_getenv("TMP"); +#endif + for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); i++){ + if( azDirs[i]==0 ) continue; if( !file_isdir(azDirs[i]) ) continue; zDir = azDirs[i]; break; } @@ -898,10 +962,15 @@ for(i=0; i<15; i++, j++){ zBuf[j] = (char)zChars[ ((unsigned char)zBuf[j])%(sizeof(zChars)-1) ]; } zBuf[j] = 0; }while( file_size(zBuf)>=0 ); + +#if defined(_WIN32) + fossil_mbcs_free((char *)azDirs[1]); + fossil_mbcs_free((char *)azDirs[2]); +#endif } /* ** Return true if a file named zName exists and has identical content @@ -930,13 +999,10 @@ /************************************************************************** ** The following routines translate between MBCS and UTF8 on windows. ** Since everything is always UTF8 on unix, these routines are no-ops ** there. */ -#ifdef _WIN32 -# include <windows.h> -#endif /* ** Translate MBCS 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. Index: src/login.c ================================================================== --- src/login.c +++ src/login.c @@ -1404,17 +1404,17 @@ *pzErrMsg = 0; /* Default to no errors */ zSelf = db_name("repository"); /* Get the full pathname of the other repository */ - file_canonical_name(zRepo, &fullName); + file_canonical_name(zRepo, &fullName, 0); zRepo = mprintf(blob_str(&fullName)); blob_reset(&fullName); /* Get the full pathname for our repository. Also the project code ** and project name for ourself. */ - file_canonical_name(g.zRepositoryName, &fullName); + file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = mprintf(blob_str(&fullName)); blob_reset(&fullName); zSelfProjCode = db_get("project-code", "unknown"); zSelfLabel = db_get("project-name", 0); if( zSelfLabel==0 ){ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -1112,11 +1112,11 @@ int i; struct stat sStat; Blob dir; char *zDir; - file_canonical_name(zRepo, &dir); + file_canonical_name(zRepo, &dir, 0); zDir = blob_str(&dir); if( file_isdir(zDir)==1 ){ if( chdir(zDir) || chroot(zDir) || chdir("/") ){ fossil_fatal("unable to chroot into %s", zDir); } @@ -1288,11 +1288,11 @@ zUser = "nobody"; } if( g.zLogin==0 ) zUser = "nobody"; if( zAltRepo[0]!='/' ){ zAltRepo = mprintf("%s/../%s", g.zRepositoryName, zAltRepo); - file_simplify_name(zAltRepo, -1); + file_simplify_name(zAltRepo, -1, 0); } db_close(1); db_open_repository(zAltRepo); login_as_user(zUser); g.perm.Password = 0; @@ -1550,11 +1550,11 @@ static void find_server_repository(int disallowDir){ if( g.argc<3 ){ db_must_be_within_tree(); }else if( !disallowDir && file_isdir(g.argv[2])==1 ){ g.zRepositoryName = mprintf("%s", g.argv[2]); - file_simplify_name(g.zRepositoryName, -1); + file_simplify_name(g.zRepositoryName, -1, 0); }else{ db_open_repository(g.argv[2]); } } Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -469,11 +469,11 @@ # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else -LIB += -lws2_32 +LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # Index: src/setup.c ================================================================== --- src/setup.c +++ src/setup.c @@ -951,11 +951,11 @@ login_check_credentials(); if( !g.perm.Setup ){ login_needed(); } - file_canonical_name(g.zRepositoryName, &fullName); + file_canonical_name(g.zRepositoryName, &fullName, 0); zSelfRepo = mprintf(blob_str(&fullName)); blob_reset(&fullName); if( P("join")!=0 ){ login_group_join(zRepo, zLogin, zPw, zNewName, &zErrMsg); }else if( P("leave") ){ Index: src/url.c ================================================================== --- src/url.c +++ src/url.c @@ -187,11 +187,11 @@ fossil_panic("unknown repository: %s", zUrl); } if( g.urlIsFile ){ Blob cfile; dehttpize(zFile); - file_canonical_name(zFile, &cfile); + file_canonical_name(zFile, &cfile, 0); free(zFile); g.urlProtocol = "file"; g.urlPath = ""; g.urlName = mprintf("%b", &cfile); g.urlCanonical = mprintf("file://%T", g.urlName); Index: src/winhttp.c ================================================================== --- src/winhttp.c +++ src/winhttp.c @@ -596,11 +596,11 @@ zRepository = find_option("repository", "R", 1); if( !zRepository ){ db_must_be_within_tree(); }else if( file_isdir(zRepository)==1 ){ g.zRepositoryName = mprintf("%s", zRepository); - file_simplify_name(g.zRepositoryName, -1); + file_simplify_name(g.zRepositoryName, -1, 0); }else{ db_open_repository(zRepository); } db_close(0); verify_all_options(); Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -150,11 +150,11 @@ # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else -LIB += -lws2_32 +LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. # Index: win/Makefile.mingw.mistachkin ================================================================== --- win/Makefile.mingw.mistachkin +++ win/Makefile.mingw.mistachkin @@ -35,11 +35,11 @@ # FOSSIL_ENABLE_SSL = 1 #### Enable scripting support via Tcl/Tk # -FOSSIL_ENABLE_TCL = 1 +# FOSSIL_ENABLE_TCL = 1 #### Use the Tcl source directory instead of the install directory? # This is useful when Tcl has been compiled statically with MinGW. # FOSSIL_TCL_SOURCE = 1 @@ -150,11 +150,11 @@ # or linking with it will not work (exact reason unknown). # ifdef FOSSIL_ENABLE_TCL LIB += -lnetapi32 -lkernel32 -luser32 -ladvapi32 -lws2_32 else -LIB += -lws2_32 +LIB += -lkernel32 -lws2_32 endif #### Tcl shell for use in running the fossil test suite. This is only # used for testing. #