Index: src/blob.c ================================================================== --- src/blob.c +++ src/blob.c @@ -130,11 +130,11 @@ } /* ** A reallocation function that assumes that aData came from malloc(). ** This function attempts to resize the buffer of the blob to hold -** newSize bytes. +** newSize bytes. ** ** No attempt is made to recover from an out-of-memory error. ** If an OOM error occurs, an error message is printed on stderr ** and the program exits. */ @@ -368,11 +368,11 @@ ((B)->nUsed==sizeof(S)-1 && memcmp((B)->aData,S,sizeof(S)-1)==0) #endif /* -** Attempt to resize a blob so that its internal buffer is +** Attempt to resize a blob so that its internal buffer is ** nByte in size. The blob is truncated if necessary. */ void blob_resize(Blob *pBlob, unsigned int newSize){ pBlob->xRealloc(pBlob, newSize+1); pBlob->nUsed = newSize; @@ -453,11 +453,11 @@ int blob_tell(Blob *p){ return p->iCursor; } /* -** Extract a single line of text from pFrom beginning at the current +** Extract a single line of text from pFrom beginning at the current ** cursor location and use that line of text to initialize pTo. ** pTo will include the terminating \n. Return the number of bytes ** in the line including the \n at the end. 0 is returned at ** end-of-file. ** @@ -669,11 +669,11 @@ void blob_vappendf(Blob *pBlob, const char *zFormat, va_list ap){ vxprintf(pBlob, zFormat, ap); } /* -** Initalize a blob to the data on an input channel. Return +** Initalize a blob to the data on an input channel. Return ** the number of bytes read into the blob. Any prior content ** of the blob is discarded, not freed. */ int blob_read_from_channel(Blob *pBlob, FILE *in, int nToRead){ size_t n; @@ -767,22 +767,16 @@ int blob_write_to_file(Blob *pBlob, const char *zFilename){ FILE *out; int wrote; if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ - int n; + int n = blob_size(pBlob); #if defined(_WIN32) - if( _isatty(fileno(stdout)) ){ - char *z; - z = fossil_utf8_to_console(blob_str(pBlob)); - n = strlen(z); - fwrite(z, 1, n, stdout); - free(z); + if( fossil_utf8_to_console(blob_buffer(pBlob), n, 0) >= 0 ){ return n; } #endif - n = blob_size(pBlob); fwrite(blob_buffer(pBlob), 1, n, stdout); return n; }else{ int i, nName; char *zName, zBuf[1000]; @@ -833,12 +827,12 @@ return wrote; } /* ** Compress a blob pIn. Store the result in pOut. It is ok for pIn and -** pOut to be the same blob. -** +** pOut to be the same blob. +** ** pOut must either be the same as pIn or else uninitialized. */ void blob_compress(Blob *pIn, Blob *pOut){ unsigned int nIn = blob_size(pIn); unsigned int nOut = 13 + nIn + (nIn+999)/1000; @@ -871,13 +865,13 @@ blob_compress(&f, &f); blob_write_to_file(&f, g.argv[3]); } /* -** Compress the concatenation of a blobs pIn1 and pIn2. Store the result -** in pOut. -** +** Compress the concatenation of a blobs pIn1 and pIn2. Store the result +** in pOut. +** ** pOut must be either uninitialized or must be the same as either pIn1 or ** pIn2. */ void blob_compress2(Blob *pIn1, Blob *pIn2, Blob *pOut){ unsigned int nIn = blob_size(pIn1) + blob_size(pIn2); @@ -944,11 +938,11 @@ inBuf = (unsigned char*)blob_buffer(pIn); nOut = (inBuf[0]<<24) + (inBuf[1]<<16) + (inBuf[2]<<8) + inBuf[3]; blob_zero(&temp); blob_resize(&temp, nOut+1); nOut2 = (long int)nOut; - rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2, + rc = uncompress((unsigned char*)blob_buffer(&temp), &nOut2, &inBuf[4], nIn - 4); if( rc!=Z_OK ){ blob_reset(&temp); return 1; } @@ -1062,11 +1056,11 @@ ** ** Returns the number of bytes read/copied, which may be less than ** nLen (if end-of-blob is encountered). ** ** Updates pIn's cursor. -** +** ** Returns 0 if pIn contains no data. */ unsigned int blob_read(Blob *pIn, void * pDest, unsigned int nLen ){ if( !pIn->aData || (pIn->iCursor >= pIn->nUsed) ){ return 0; Index: src/file.c ================================================================== --- src/file.c +++ src/file.c @@ -114,11 +114,11 @@ /* ** Same as file_size(), but takes into account symlinks. */ i64 file_wd_size(const char *zFilename){ - return getStat(zFilename, 1) ? -1 : fileStat.st_size; + return getStat(zFilename, 1) ? -1 : fileStat.st_size; } /* ** Return the modification time for a file. Return -1 if the file ** does not exist. If zFilename is NULL return the size of the most @@ -134,11 +134,11 @@ i64 file_wd_mtime(const char *zFilename){ return getStat(zFilename, 1) ? -1 : fileStat.st_mtime; } /* -** Return TRUE if the named file is an ordinary file or symlink +** Return TRUE if the named file is an ordinary file or symlink ** and symlinks are allowed. ** Return false for directories, devices, fifos, etc. */ int file_wd_isfile_or_link(const char *zFilename){ return getStat(zFilename, 1) ? 0 : S_ISREG(fileStat.st_mode) || @@ -191,14 +191,14 @@ } } if( zName!=zBuf ) free(zName); if( symlink(zTargetFile, zName)!=0 ){ - fossil_fatal_recursive("unable to create symlink \"%s\"", zName); + fossil_fatal_recursive("unable to create symlink \"%s\"", zName); } }else -#endif +#endif { Blob content; blob_set(&content, zTargetFile); blob_write_to_file(&content, zLinkFile); blob_reset(&content); @@ -230,11 +230,11 @@ if( S_ISREG(fileStat.st_mode) && ((S_IXUSR)&fileStat.st_mode)!=0 ) return PERM_EXE; else return PERM_REG; #else - if( S_ISREG(fileStat.st_mode) && + if( S_ISREG(fileStat.st_mode) && ((S_IXUSR|S_IXGRP|S_IXOTH)&fileStat.st_mode)!=0 ) return PERM_EXE; else if( g.allowSymlinks && S_ISLNK(fileStat.st_mode) ) return PERM_LNK; else @@ -405,11 +405,11 @@ #endif } /* ** Create the directory named in the argument, if it does not already -** exist. If forceFlag is 1, delete any prior non-directory object +** exist. If forceFlag is 1, delete any prior non-directory object ** with the same name. ** ** Return the number of errors. */ int file_mkdir(const char *zName, int forceFlag){ @@ -719,11 +719,11 @@ } } return 1; } -/* +/* ** Return a pointer to the first character in a pathname past the ** drive letter. This routine is a no-op on unix. */ char *file_without_drive_letter(char *zIn){ #ifdef _WIN32 @@ -947,19 +947,19 @@ 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; } - /* Check that the output buffer is large enough for the temporary file + /* Check that the output buffer is large enough for the temporary file ** name. If it is not, return SQLITE_ERROR. */ if( (strlen(zDir) + 17) >= (size_t)nBuf ){ fossil_fatal("insufficient space for temporary filename"); } @@ -1035,21 +1035,21 @@ ** Since everything is always UTF8 on unix, these routines are no-ops ** there. */ /* -** Translate MBCS to UTF8. Return a pointer to the translated text. +** 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. */ char *fossil_mbcs_to_utf8(const char *zMbcs){ #ifdef _WIN32 extern char *sqlite3_win32_mbcs_to_utf8(const char*); return sqlite3_win32_mbcs_to_utf8(zMbcs); #else return (char*)zMbcs; /* No-op on unix */ -#endif +#endif } /* ** Translate Unicode to UTF8. Return a pointer to the translated text. ** Call fossil_mbcs_free() to deallocate any memory used to store the @@ -1078,11 +1078,11 @@ #ifdef _WIN32 extern char *sqlite3_win32_utf8_to_mbcs(const char*); return sqlite3_win32_utf8_to_mbcs(zUtf8); #else return (char*)zUtf8; /* No-op on unix */ -#endif +#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 @@ -1116,48 +1116,71 @@ #endif return zValue; } /* -** Translate UTF8 to MBCS for display on the console. Return a pointer to the -** translated text.. Call fossil_mbcs_free() to deallocate any memory -** used to store the returned pointer when done. +** Display UTF8 on the console. Return the number of +** Characters written. If stdout or stderr is redirected +** to a file, -1 is returned and nothing is written +** to the console. */ -char *fossil_utf8_to_console(const char *zUtf8){ +int fossil_utf8_to_console(const char *zUtf8, int nByte, int toStdErr){ #ifdef _WIN32 - int nChar, nByte; - WCHAR *zUnicode; /* Unicode version of zUtf8 */ + int nChar; + wchar_t *zUnicode; /* Unicode version of zUtf8 */ +#ifdef UNICODE + DWORD dummy; +#else char *zConsole; /* Console version of zUtf8 */ int codepage; /* Console code page */ +#endif + + static int istty[2] = { -1, -1 }; + if( istty[toStdErr] == -1 ){ + istty[toStdErr] = _isatty(toStdErr + 1) != 0; + } + if( !istty[toStdErr] ){ + /* stdout/stderr is not a console. */ + return -1; + } - nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, NULL, 0); - zUnicode = malloc( nChar*sizeof(zUnicode[0]) ); + nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, nByte, NULL, 0); + zUnicode = malloc( (nChar + 1) *sizeof(zUnicode[0]) ); if( zUnicode==0 ){ return 0; } - nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); + nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, nByte, zUnicode, nChar); if( nChar==0 ){ free(zUnicode); return 0; } + zUnicode[nChar] = '\0'; +#ifdef UNICODE + WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE - toStdErr), zUnicode, nChar, &dummy, 0); +#else /* !UNICODE */ codepage = GetConsoleCP(); - nByte = WideCharToMultiByte(codepage, 0, zUnicode, -1, 0, 0, 0, 0); - zConsole = malloc( nByte ); + nByte = WideCharToMultiByte(codepage, 0, zUnicode, nChar, 0, 0, 0, 0); + zConsole = malloc( nByte + 1); if( zConsole==0 ){ free(zUnicode); return 0; } - nByte = WideCharToMultiByte(codepage, 0, zUnicode, -1, zConsole, nByte, 0, 0); + nByte = WideCharToMultiByte(codepage, 0, zUnicode, nChar, zConsole, nByte, 0, 0); + zConsole[nByte] = '\0'; free(zUnicode); if( nByte == 0 ){ free(zConsole); zConsole = 0; + return 0; } - return zConsole; + fwrite(zConsole, 1, nByte, toStdErr ? stderr : stdout); + fflush(toStdErr ? stderr : stdout); +#endif /* UNICODE */ + return nChar; #else - return (char*)zUtf8; /* No-op on unix */ -#endif + return -1; /* No-op on unix */ +#endif } /* ** Translate MBCS to UTF8. Return a pointer. Call fossil_mbcs_free() ** to deallocate any memory used to store the returned pointer when done. @@ -1166,11 +1189,11 @@ #ifdef _WIN32 extern void sqlite3_free(void*); sqlite3_free(zOld); #else /* No-op on unix */ -#endif +#endif } /* ** Like fopen() but always takes a UTF8 argument. */ Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -157,11 +157,11 @@ char *urlPasswd; /* Password for http: */ char *urlCanonical; /* Canonical representation of the URL */ char *urlProxyAuth; /* Proxy-Authorizer: string */ char *urlFossil; /* The path of the ?fossil=path suffix on ssh: */ int dontKeepUrl; /* Do not persist the URL */ - + const char *zLogin; /* Login name. "" if not logged in. */ const char *zSSLIdentity; /* Value of --ssl-identity option, filename of SSL client identity */ int useLocalauth; /* No login required if from 127.0.0.1 */ int noPswd; /* Logged in without password (on 127.0.0.1) */ int userUid; /* Integer user id */ @@ -168,11 +168,11 @@ /* Information used to populate the RCVFROM table */ int rcvid; /* The rcvid. 0 if not yet defined. */ char *zIpAddr; /* The remote IP address */ char *zNonce; /* The nonce used for login */ - + /* permissions used by the server */ struct FossilUserPerms perm; #ifdef FOSSIL_ENABLE_TCL /* all Tcl related context necessary for integration */ @@ -195,11 +195,11 @@ const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */ char *azAuxParam[MX_AUX]; /* Param of each aux() or option() value */ const char *azAuxVal[MX_AUX]; /* Value of each aux() or option() value */ const char **azAuxOpt[MX_AUX]; /* Options of each option() value */ int anAuxCols[MX_AUX]; /* Number of columns for option() values */ - + int allowSymlinks; /* Cached "allow-symlinks" option */ #ifdef FOSSIL_ENABLE_JSON struct FossilJsonBits { int isJsonMode; /* True if running in JSON mode, else @@ -262,11 +262,11 @@ #endif Global g; /* -** The table of web pages supported by this application is generated +** The table of web pages supported by this application is generated ** automatically by the "mkindex" program and written into a file ** named "page_index.h". We include that file here to get access ** to the table. */ #include "page_index.h" @@ -330,29 +330,146 @@ if(g.db){ db_close(0); } } +#if defined(_WIN32) /* -** Convert all arguments from mbcs to UTF-8. Then +** Parse the command-line arguments passed to windows. We do this +** ourselves to work around bugs in the command-line parsing of MinGW. +** It is possible (in theory) to only use this routine when compiling +** with MinGW and to use built-in command-line parsing for MSVC and +** MinGW-64. However, the code is here, it is efficient, and works, and +** by using it in all cases we do a better job of testing it. If you suspect +** a bug in this code, test your theory by invoking "fossil test-echo". +** +** This routine is copied from TCL with some reformatting. +** The original comment text follows: +** +** Parse the Windows command line string into argc/argv. Done here +** because we don't trust the builtin argument parser in crt0. Windows +** applications are responsible for breaking their command line into +** arguments. +** +** 2N backslashes + quote -> N backslashes + begin quoted string +** 2N + 1 backslashes + quote -> literal +** N backslashes + non-quote -> literal +** quote + quote in a quoted string -> single quote +** quote + quote not in quoted string -> empty string +** quote -> begin quoted string +** +** Results: +** Fills argcPtr with the number of arguments and argvPtr with the array +** of arguments. +*/ +#include <tchar.h> +#define tchar_isspace(X) ((X)==TEXT(' ') || (X)==TEXT('\t')) +static void parse_windows_command_line( + int *argcPtr, /* Filled with number of argument strings. */ + void *argvPtr /* Filled with argument strings (malloc'd). */ +){ + TCHAR *cmdLine, *p, *arg, *argSpace; + TCHAR **argv; + int argc, size, inquote, copy, slashes; + + cmdLine = GetCommandLine(); + + /* + ** Precompute an overly pessimistic guess at the number of arguments in + ** the command line by counting non-space spans. + */ + size = 2; + for(p=cmdLine; *p!=TEXT('\0'); p++){ + if( tchar_isspace(*p) ){ + size++; + while( tchar_isspace(*p) ){ + p++; + } + if( *p==TEXT('\0') ){ + break; + } + } + } + + argSpace = fossil_malloc(size * sizeof(char*) + + (_tcslen(cmdLine) * sizeof(TCHAR)) + sizeof(TCHAR)); + argv = (TCHAR**)argSpace; + argSpace += size*(sizeof(char*)/sizeof(TCHAR)); + size--; + + p = cmdLine; + for(argc=0; argc<size; argc++){ + argv[argc] = arg = argSpace; + while( tchar_isspace(*p) ){ + p++; + } + if (*p == TEXT('\0')) { + break; + } + inquote = 0; + slashes = 0; + while(1){ + copy = 1; + while( *p==TEXT('\\') ){ + slashes++; + p++; + } + if( *p==TEXT('"') ){ + if( (slashes&1)==0 ){ + copy = 0; + if( inquote && p[1]==TEXT('"') ){ + p++; + copy = 1; + }else{ + inquote = !inquote; + } + } + slashes >>= 1; + } + while( slashes ){ + *arg = TEXT('\\'); + arg++; + slashes--; + } + if( *p==TEXT('\0') || (!inquote && tchar_isspace(*p)) ){ + break; + } + if( copy!=0 ){ + *arg = *p; + arg++; + } + p++; + } + *arg = '\0'; + argSpace = arg + 1; + } + argv[argc] = NULL; + *argcPtr = argc; + *((TCHAR ***)argvPtr) = argv; +} +#endif /* defined(_WIN32) */ + + +/* +** Convert all arguments from mbcs (or unicode) to UTF-8. Then ** search g.argv for arguments "--args FILENAME". If found, then ** (1) remove the two arguments from g.argv ** (2) Read the file FILENAME ** (3) Use the contents of FILE to replace the two removed arguments: ** (a) Ignore blank lines in the file ** (b) Each non-empty line of the file is an argument, except ** (c) If the line begins with "-" and contains a space, it is broken ** into two arguments at the space. */ -static void expand_args_option(int argc, char **argv){ +static void expand_args_option(int argc, void *argv){ Blob file = empty_blob; /* Content of the file */ Blob line = empty_blob; /* One line of the file */ unsigned int nLine; /* Number of lines in the file*/ unsigned int i, j, k; /* Loop counters */ int n; /* Number of bytes in one line */ - char *z; /* General use string pointer */ - char **newArgv; /* New expanded g.argv under construction */ + char *z; /* General use string pointer */ + char **newArgv; /* New expanded g.argv under construction */ char const * zFileName; /* input file name */ FILE * zInFile; /* input FILE */ int foundBom = -1; /* -1= not searched yet, 0 = no; 1=yes */ #ifdef _WIN32 wchar_t buf[MAX_PATH]; @@ -359,13 +476,18 @@ #endif g.argc = argc; g.argv = argv; #ifdef _WIN32 + parse_windows_command_line(&g.argc, &g.argv); GetModuleFileNameW(NULL, buf, MAX_PATH); g.argv[0] = fossil_unicode_to_utf8(buf); +#ifdef UNICODE + for(i=1; i<g.argc; i++) g.argv[i] = fossil_unicode_to_utf8(g.argv[i]); +#else for(i=1; i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]); +#endif #endif for(i=1; i<g.argc-1; i++){ z = g.argv[i]; if( z[0]!='-' ) continue; z++; @@ -390,21 +512,21 @@ } z = blob_str(&file); for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++; newArgv = fossil_malloc( sizeof(char*)*(g.argc + nLine*2) ); for(j=0; j<i; j++) newArgv[j] = g.argv[j]; - + blob_rewind(&file); while( (n = blob_line(&file, &line))>0 ){ if( n<=1 ) continue; z = blob_buffer(&line); z[n-1] = 0; if (foundBom == -1) { static const char bom[] = { 0xEF, 0xBB, 0xBF }; foundBom = memcmp(z, bom, 3)==0; if( foundBom ) { - z += 3; n -= 3; + z += 3; n -= 3; } } if((n>1) && ('\r'==z[n-2])){ if(n==2) continue /*empty line*/; z[n-2] = 0; @@ -430,11 +552,16 @@ } /* ** This procedure runs first. */ -int main(int argc, char **argv){ +#if defined(_WIN32) && defined(UNICODE) +int wmain(int argc, wchar_t **argv) +#else +int main(int argc, char **argv) +#endif +{ const char *zCmdName = "unknown"; int idx; int rc; sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0); @@ -726,12 +853,12 @@ #else /* On unix, evaluate the command directly. */ if( g.fSystemTrace ) fprintf(stderr, "SYSTEM: %s\n", zOrigCmd); rc = system(zOrigCmd); -#endif - return rc; +#endif + return rc; } /* ** Turn off any NL to CRNL translation on the stream given as an ** argument. This is a no-op on unix but is necessary on windows. @@ -1194,11 +1321,11 @@ ** * Environment variables are set up according to the CGI standard. ** ** If the repository is known, it has already been opened. If unknown, ** then g.zRepositoryName holds the directory that contains the repository ** and the actual repository is taken from the first element of PATH_INFO. -** +** ** Process the webpage specified by the PATH_INFO or REQUEST_URI ** environment variable. */ static void process_one_web_page(const char *zNotFound){ const char *zPathInfo; @@ -1267,11 +1394,11 @@ cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]); zPathInfo += i; cgi_replace_parameter("SCRIPT_NAME", zNewScript); db_open_repository(zRepo); if( g.fHttpTrace ){ - fprintf(stderr, + fprintf(stderr, "# repository: [%s]\n" "# new PATH_INFO = [%s]\n" "# new SCRIPT_NAME = [%s]\n", zRepo, zPathInfo, zNewScript); } @@ -1282,11 +1409,11 @@ */ if( g.zContentType && memcmp(g.zContentType, "application/x-fossil", 20)==0 ){ zPathInfo = "/xfer"; } set_base_url(); - if( zPathInfo==0 || zPathInfo[0]==0 + if( zPathInfo==0 || zPathInfo[0]==0 || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){ #ifdef FOSSIL_ENABLE_JSON if(g.json.isJsonMode){ json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1); fossil_exit(0); @@ -1312,11 +1439,11 @@ ** that accepts the login credentials of the current repository. A ** subrepository is identified by a CONFIG table entry "subrepo:NAME" ** where NAME is the first component of the path. The value of the ** the CONFIG entries is the string "USER:FILENAME" where USER is the ** USER name to log in as in the subrepository and FILENAME is the - ** repository filename. + ** repository filename. */ zAltRepo = db_text(0, "SELECT value FROM config WHERE name='subrepo:%q'", g.zPath); if( zAltRepo ){ int nHost; @@ -1514,11 +1641,11 @@ /* If the CGI program contains one or more lines of the form ** ** redirect: repository-filename http://hostname/path/%s ** -** then control jumps here. Search each repository for an artifact ID +** then control jumps here. Search each repository for an artifact ID ** that matches the "name" CGI parameter and for the first match, ** redirect to the corresponding URL with the "name" CGI parameter ** inserted. Paint an error page if no match is found. ** ** If there is a line of the form: @@ -1530,11 +1657,11 @@ */ void redirect_web_page(int nRedirect, char **azRedirect){ int i; /* Loop counter */ const char *zNotFound = 0; /* Not found URL */ const char *zName = P("name"); - set_base_url(); + set_base_url(); if( zName==0 ){ zName = P("SCRIPT_NAME"); if( zName && zName[0]=='/' ) zName++; } if( zName && validate16(zName, strlen(zName)) ){ @@ -1603,11 +1730,11 @@ ** ** Usage: %fossil http REPOSITORY ?OPTIONS? ** ** Handle a single HTTP request appearing on stdin. The resulting webpage ** is delivered on stdout. This method is used to launch an HTTP request -** handler from inetd, for example. The argument is the name of the +** handler from inetd, for example. The argument is the name of the ** repository. ** ** If REPOSITORY is a directory that contains one or more repositories ** with names of the form "*.fossil" then the first element of the URL ** pathname selects among the various repositories. If the pathname does @@ -1742,11 +1869,11 @@ ** See also: cgi, http, winsrv */ void cmd_webserver(void){ int iPort, mxPort; /* Range of TCP ports allowed */ const char *zPort; /* Value of the --port option */ - char *zBrowser; /* Name of web browser program */ + const char *zBrowser; /* Name of web browser program */ char *zBrowserCmd = 0; /* Command to launch the web browser */ int isUiCmd; /* True if command is "ui", not "server' */ const char *zNotFound; /* The --notfound option or NULL */ int flags = 0; /* Server flags */ @@ -1779,11 +1906,11 @@ /* Unix implementation */ if( isUiCmd ){ #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__) zBrowser = db_get("web-browser", 0); if( zBrowser==0 ){ - static char *azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; + static const char *const azBrowserProg[] = { "xdg-open", "gnome-open", "firefox" }; int i; zBrowser = "echo"; for(i=0; i<sizeof(azBrowserProg)/sizeof(azBrowserProg[0]); i++){ if( binaryOnPath(azBrowserProg[i]) ){ zBrowser = azBrowserProg[i]; Index: src/makemake.tcl ================================================================== --- src/makemake.tcl +++ src/makemake.tcl @@ -473,10 +473,15 @@ # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif + +# Fix buggy MinGW command line parsing +ifdef MINGW_BROKEN_MAINARGS +TCC += -DMINGW_BROKEN_MAINARGS +endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -243,11 +243,11 @@ blob_append(pBlob,"%",1); count++; break; } /* Find out what flags are present */ - flag_leftjustify = flag_plussign = flag_blanksign = + flag_leftjustify = flag_plussign = flag_blanksign = flag_alternateform = flag_altform2 = flag_zeropad = 0; done = 0; do{ switch( c ){ case '-': flag_leftjustify = 1; break; @@ -814,25 +814,16 @@ ** if the output is going to the screen. If output is redirected into ** a file, no translation occurs. No translation ever occurs on unix. */ void fossil_puts(const char *z, int toStdErr){ #if defined(_WIN32) - static int once = 1; - static int istty[2]; - char *zToFree = 0; - if( once ){ - istty[0] = _isatty(fileno(stdout)); - istty[1] = _isatty(fileno(stderr)); - once = 0; + if( fossil_utf8_to_console(z, strlen(z), toStdErr) >= 0 ){ + return; } +#endif assert( toStdErr==0 || toStdErr==1 ); - if( istty[toStdErr] ) z = zToFree = fossil_utf8_to_console(z); fwrite(z, 1, strlen(z), toStdErr ? stderr : stdout); - free(zToFree); -#else - fwrite(z, 1, strlen(z), toStdErr ? stderr : stdout); -#endif fflush(toStdErr ? stderr : stdout); } /* ** Write output for user consumption. If g.cgiOutput is enabled, then @@ -863,11 +854,11 @@ return -1; }else if( zB==0 ){ return +1; }else{ int a, b; - do{ + do{ a = *zA++; b = *zB++; }while( a==b && a!=0 ); return ((unsigned char)a) - (unsigned char)b; } @@ -878,11 +869,11 @@ return -1; }else if( zB==0 ){ return +1; }else if( nByte>0 ){ int a, b; - do{ + do{ a = *zA++; b = *zB++; }while( a==b && a!=0 && (--nByte)>0 ); return ((unsigned char)a) - (unsigned char)b; }else{ Index: src/winhttp.c ================================================================== --- src/winhttp.c +++ src/winhttp.c @@ -109,11 +109,11 @@ wanted -= got; } fclose(out); out = 0; sqlite3_snprintf(sizeof(zCmd), zCmd, "\"%s\" http \"%s\" %s %s %s --nossl%s", - fossil_nameofexe(), g.zRepositoryName, zRequestFName, zReplyFName, + fossil_nameofexe(), g.zRepositoryName, zRequestFName, zReplyFName, inet_ntoa(p->addr.sin_addr), p->zOptions ); fossil_system(zCmd); in = fossil_fopen(zReplyFName, "rb"); if( in ){ @@ -129,10 +129,15 @@ file_delete(zRequestFName); file_delete(zReplyFName); free(p); } +#if !defined(UNICODE) +# define fossil_unicode_to_utf8 fossil_mbcs_to_utf8 +# define fossil_utf8_to_unicode fossil_utf8_to_mbcs +#endif + /* ** Start a listening socket and process incoming HTTP requests on ** that socket. */ void win32_http_server( @@ -146,11 +151,11 @@ SOCKET s = INVALID_SOCKET; SOCKADDR_IN addr; int idCnt = 0; int iPort = mnPort; Blob options; - char zTmpPath[MAX_PATH]; + TCHAR zTmpPath[MAX_PATH]; if( zStopper ) file_delete(zStopper); blob_zero(&options); if( zNotFound ){ blob_appendf(&options, " --notfound %s", zNotFound); @@ -194,11 +199,11 @@ } } if( !GetTempPath(MAX_PATH, zTmpPath) ){ fossil_fatal("unable to get path to the temporary directory."); } - zTempPrefix = mprintf("%sfossil_server_P%d_", fossil_mbcs_to_utf8(zTmpPath), iPort); + zTempPrefix = mprintf("%sfossil_server_P%d_", fossil_unicode_to_utf8(zTmpPath), iPort); fossil_print("Listening for HTTP requests on TCP port %d\n", iPort); if( zBrowser ){ zBrowser = mprintf(zBrowser, iPort); fossil_print("Launch webbrowser: %s\n", zBrowser); fossil_system(zBrowser); @@ -214,11 +219,11 @@ int len = sizeof(client_addr); int wsaError; client = accept(s, (struct sockaddr*)&client_addr, &len); if( client==INVALID_SOCKET ){ - /* If the service control handler has closed the listener socket, + /* If the service control handler has closed the listener socket, ** cleanup and return, otherwise report a fatal error. */ wsaError = WSAGetLastError(); if( (wsaError==WSAEINTR) || (wsaError==WSAENOTSOCK) ){ WSACleanup(); return; @@ -249,11 +254,11 @@ struct HttpService { int port; /* Port on which the http server should run */ const char *zNotFound; /* The --notfound option, or NULL */ int flags; /* One or more HTTP_SERVER_ flags */ int isRunningAsService; /* Are we running as a service ? */ - const char *zServiceName; /* Name of the service */ + const TCHAR *zServiceName;/* Name of the service */ SOCKET s; /* Socket on which the http server listens */ }; /* ** Variables used for running as windows service. @@ -298,11 +303,11 @@ 0, NULL ); } if( nMsg ){ - zMsg = fossil_mbcs_to_utf8(tmp); + zMsg = fossil_unicode_to_utf8(tmp); }else{ fossil_fatal("unable to get system error message."); } if( tmp ){ LocalFree((HLOCAL) tmp); @@ -326,11 +331,11 @@ } ssStatus.dwCurrentState = dwCurrentState; ssStatus.dwWin32ExitCode = dwWin32ExitCode; ssStatus.dwWaitHint = dwWaitHint; - if( (dwCurrentState==SERVICE_RUNNING) || + if( (dwCurrentState==SERVICE_RUNNING) || (dwCurrentState==SERVICE_STOPPED) ){ ssStatus.dwCheckPoint = 0; }else{ ssStatus.dwCheckPoint++; } @@ -384,11 +389,11 @@ if( argc>0 ){ hsData.zServiceName = argv[0]; } /* Register the service control handler function */ - sshStatusHandle = RegisterServiceCtrlHandler("", win32_http_service_ctrl); + sshStatusHandle = RegisterServiceCtrlHandler(TEXT(""), win32_http_service_ctrl); if( !sshStatusHandle ){ win32_report_service_status(SERVICE_STOPPED, NO_ERROR, 0); return; } @@ -429,12 +434,12 @@ const char *zNotFound, /* The --notfound option, or NULL */ int flags /* One or more HTTP_SERVER_ flags */ ){ /* Define the service table. */ SERVICE_TABLE_ENTRY ServiceTable[] = - {{"", (LPSERVICE_MAIN_FUNCTION)win32_http_service_main}, {NULL, NULL}}; - + {{TEXT(""), (LPSERVICE_MAIN_FUNCTION)win32_http_service_main}, {NULL, NULL}}; + /* Initialize the HttpService structure. */ hsData.port = nPort; hsData.zNotFound = zNotFound; hsData.flags = flags; @@ -447,11 +452,12 @@ } } return 0; } -/* +#ifdef _WIN32 +/* dupe ifdef needed for mkindex ** COMMAND: winsrv* ** Usage: fossil winsrv METHOD ?SERVICE-NAME? ?OPTIONS? ** ** Where METHOD is one of: create delete show start stop. ** @@ -459,11 +465,11 @@ ** (for example) Fossil to be running in the background when no user ** is logged in. ** ** In the following description of the methods, "Fossil-DSCM" will be ** used as the default SERVICE-NAME: -** +** ** fossil winsrv create ?SERVICE-NAME? ?OPTIONS? ** ** Creates a service. Available options include: ** ** -D|--display DISPLAY-NAME @@ -565,11 +571,11 @@ if( strncmp(zMethod, "create", n)==0 ){ SC_HANDLE hScm; SC_HANDLE hSvc; SERVICE_DESCRIPTION - svcDescr = {"Fossil - Distributed Software Configuration Management"}; + svcDescr = {TEXT("Fossil - Distributed Software Configuration Management")}; char *zErrFmt = "unable to create service '%s': %s"; DWORD dwStartType = SERVICE_DEMAND_START; const char *zDisplay = find_option("display", "D", 1); const char *zStart = find_option("start", "S", 1); const char *zUsername = find_option("username", "U", 1); @@ -624,22 +630,22 @@ /* Create the service. */ hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); hSvc = CreateService( hScm, /* Handle to the SCM */ - fossil_utf8_to_mbcs(zSvcName), /* Name of the service */ - fossil_utf8_to_mbcs(zDisplay), /* Display name */ + fossil_utf8_to_unicode(zSvcName), /* Name of the service */ + fossil_utf8_to_unicode(zDisplay), /* Display name */ SERVICE_ALL_ACCESS, /* Desired access */ SERVICE_WIN32_OWN_PROCESS, /* Service type */ dwStartType, /* Start type */ SERVICE_ERROR_NORMAL, /* Error control */ - fossil_utf8_to_mbcs(blob_str(&binPath)), /* Binary path */ + fossil_utf8_to_unicode(blob_str(&binPath)), /* Binary path */ NULL, /* Load ordering group */ NULL, /* Tag value */ NULL, /* Service dependencies */ - fossil_utf8_to_mbcs(zUsername), /* Service account */ - fossil_utf8_to_mbcs(zPassword) /* Account password */ + fossil_utf8_to_unicode(zUsername), /* Service account */ + fossil_utf8_to_unicode(zPassword) /* Account password */ ); if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); /* Set the service description. */ ChangeServiceConfig2(hSvc, SERVICE_CONFIG_DESCRIPTION, &svcDescr); fossil_print("Service '%s' successfully created.\n", zSvcName); @@ -658,11 +664,11 @@ }else if( g.argc>4 ){ fossil_fatal("to much arguments for delete method."); } hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); - hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + hSvc = OpenService(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_STOPPED ){ fossil_print("Stopping service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ @@ -695,28 +701,28 @@ SERVICE_STATUS sstat; LPQUERY_SERVICE_CONFIG pSvcConfig; LPSERVICE_DESCRIPTION pSvcDescr; BOOL bStatus; DWORD nRequired; - char *zErrFmt = "unable to show service '%s': %s"; - static const char *zSvcTypes[] = { + const char *zErrFmt = "unable to show service '%s': %s"; + static const char *const zSvcTypes[] = { "Driver service", "File system driver service", "Service runs in its own process", "Service shares a process with other services", "Service can interact with the desktop" }; const char *zSvcType = ""; - static char *zSvcStartTypes[] = { + static const char *const zSvcStartTypes[] = { "Started by the system loader", "Started by the IoInitSystem function", "Started automatically by the service control manager", "Started manually", "Service cannot be started" }; const char *zSvcStartType = ""; - static const char *zSvcStates[] = { + static const char *const zSvcStates[] = { "Stopped", "Starting", "Stopping", "Running", "Continue pending", "Pause pending", "Paused" }; const char *zSvcState = ""; @@ -726,11 +732,11 @@ }else if( g.argc>4 ){ fossil_fatal("to much arguments for show method."); } hScm = OpenSCManager(NULL, NULL, GENERIC_READ); if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); - hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), GENERIC_READ); + hSvc = OpenService(hScm, fossil_utf8_to_unicode(zSvcName), GENERIC_READ); if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); /* Get the service configuration */ bStatus = QueryServiceConfig(hSvc, NULL, 0, &nRequired); if( !bStatus && GetLastError()!=ERROR_INSUFFICIENT_BUFFER ){ fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); @@ -778,19 +784,19 @@ case SERVICE_PAUSED: zSvcState = zSvcStates[6]; break; } /* Print service information to terminal */ fossil_print("Service name .......: %s\n", zSvcName); fossil_print("Display name .......: %s\n", - fossil_mbcs_to_utf8(pSvcConfig->lpDisplayName)); + fossil_unicode_to_utf8(pSvcConfig->lpDisplayName)); fossil_print("Service description : %s\n", - fossil_mbcs_to_utf8(pSvcDescr->lpDescription)); + fossil_unicode_to_utf8(pSvcDescr->lpDescription)); fossil_print("Service type .......: %s.\n", zSvcType); fossil_print("Service start type .: %s.\n", zSvcStartType); fossil_print("Binary path name ...: %s\n", - fossil_mbcs_to_utf8(pSvcConfig->lpBinaryPathName)); + fossil_unicode_to_utf8(pSvcConfig->lpBinaryPathName)); fossil_print("Service username ...: %s\n", - fossil_mbcs_to_utf8(pSvcConfig->lpServiceStartName)); + fossil_unicode_to_utf8(pSvcConfig->lpServiceStartName)); fossil_print("Current state ......: %s.\n", zSvcState); /* Cleanup */ fossil_free(pSvcConfig); fossil_free(pSvcDescr); CloseServiceHandle(hSvc); @@ -808,11 +814,11 @@ }else if( g.argc>4 ){ fossil_fatal("to much arguments for start method."); } hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); - hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + hSvc = OpenService(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_RUNNING ){ fossil_print("Starting service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_START_PENDING ){ @@ -844,11 +850,11 @@ }else if( g.argc>4 ){ fossil_fatal("to much arguments for stop method."); } hScm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if( !hScm ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); - hSvc = OpenService(hScm, fossil_utf8_to_mbcs(zSvcName), SERVICE_ALL_ACCESS); + hSvc = OpenService(hScm, fossil_utf8_to_unicode(zSvcName), SERVICE_ALL_ACCESS); if( !hSvc ) fossil_fatal(zErrFmt, zSvcName, win32_get_last_errmsg()); QueryServiceStatus(hSvc, &sstat); if( sstat.dwCurrentState!=SERVICE_STOPPED ){ fossil_print("Stopping service '%s'", zSvcName); if( sstat.dwCurrentState!=SERVICE_STOP_PENDING ){ @@ -872,7 +878,8 @@ fossil_fatal("METHOD should be one of:" " create delete show start stop"); } return; } +#endif /* _WIN32 */ #endif /* _WIN32 -- This code is for win32 only */ ADDED test/cmdline.test Index: test/cmdline.test ================================================================== --- test/cmdline.test +++ test/cmdline.test @@ -0,0 +1,30 @@ +# +# Copyright (c) 2011 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".) +# +# This program is distributed in the hope that it will be useful, +# but without any warranty; without even the implied warranty of +# merchantability or fitness for a particular purpose. +# +# Author contact information: +# drh@hwaci.com +# http://www.hwaci.com/drh/ +# +############################################################################ +# +# Test command line parsing +# + +proc cmd-line {testname args} { + set i 1 + foreach {cmdline result} $args { + fossil test-echo {*}$cmdline + test cmd-line-$testname.$i {[lrange [split $::RESULT \n] 2 end]=="\{argv\[2\] = \[$result\]\}"} + incr i + } +} +cmd-line 100 abc abc {"abc"} abc +cmd-line 101 * {*} *.* {*.*} Index: win/Makefile.mingw ================================================================== --- win/Makefile.mingw +++ win/Makefile.mingw @@ -145,10 +145,15 @@ # With JSON support ifdef FOSSIL_ENABLE_JSON TCC += -DFOSSIL_ENABLE_JSON=1 RCC += -DFOSSIL_ENABLE_JSON=1 endif + +# Fix buggy MinGW command line parsing +ifdef MINGW_BROKEN_MAINARGS +TCC += -DMINGW_BROKEN_MAINARGS +endif #### We add the -static option here so that we can build a static # executable that will run in a chroot jail. # LIB = -static Index: win/Makefile.msc ================================================================== --- win/Makefile.msc +++ win/Makefile.msc @@ -32,11 +32,11 @@ ZLIBDIR = $(MSCDIR)\extra\lib ZLIB = zlib.lib INCL = -I. -I$(SRCDIR) -I$B\win\include -I$(MSCDIR)\extra\include -I$(ZINCDIR) -CFLAGS = -nologo -MT -O2 +CFLAGS = -nologo -MT -O2 -DUNICODE -D_UNICODE BCC = $(CC) $(CFLAGS) TCC = $(CC) -c $(CFLAGS) $(MSCDEF) $(SSL) $(INCL) LIBS = $(ZLIB) ws2_32.lib advapi32.lib $(SSLLIB) LIBDIR = -LIBPATH:$(MSCDIR)\extra\lib -LIBPATH:$(ZLIBDIR)