Please find as attached file another patch which: 1. First test for a real terminal before spawning the pager command. 2. No more paginate json command.
I see that most of you complain about this proposed feature. It was only a proposition, if it is not accepted I won't care. However, it would be possible to make both world happy: 1. Those who don't like this feature simply don't set a pager-command setting. 2. Those who want to use it set a pager-command setting. And, IMHO adding or removing a '|' at the end of command names is not a maintenance nightmare. Note : provided patch can be applied against latest version of the trunk ([a0ce33c90b] at the time of this writing).
Index: src/allrepo.c ================================================================== --- src/allrepo.c +++ src/allrepo.c @@ -418,15 +418,15 @@ if( showLabel ){ int len = (int)strlen(zFilename); int nStar = 80 - (len + 15); if( nStar<2 ) nStar = 1; fossil_print("%.13c %s %.*c\n", '*', zFilename, nStar, '*'); - fflush(stdout); + fflush(our_stdout); } if( !quiet || dryRunFlag ){ fossil_print("%s\n", zSyscmd); - fflush(stdout); + fflush(our_stdout); } rc = dryRunFlag ? 0 : fossil_system(zSyscmd); free(zSyscmd); free(zQFilename); if( stopOnError && rc ){ Index: src/blob.c ================================================================== --- src/blob.c +++ src/blob.c @@ -859,17 +859,17 @@ if( zFilename[0]==0 || (zFilename[0]=='-' && zFilename[1]==0) ){ blob_is_init(pBlob); #if defined(_WIN32) nWrote = fossil_utf8_to_console(blob_buffer(pBlob), blob_size(pBlob), 0); if( nWrote>=0 ) return nWrote; - fflush(stdout); - _setmode(_fileno(stdout), _O_BINARY); + fflush(our_stdout); + _setmode(_fileno(our_stdout), _O_BINARY); #endif - nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), stdout); + nWrote = fwrite(blob_buffer(pBlob), 1, blob_size(pBlob), our_stdout); #if defined(_WIN32) - fflush(stdout); - _setmode(_fileno(stdout), _O_TEXT); + fflush(our_stdout); + _setmode(_fileno(our_stdout), _O_TEXT); #endif }else{ file_mkfolder(zFilename, 1, 0); out = fossil_fopen(zFilename, "wb"); if( out==0 ){ Index: src/branch.c ================================================================== --- src/branch.c +++ src/branch.c @@ -261,11 +261,11 @@ ); } /* -** COMMAND: branch +** COMMAND: branch| ** ** Usage: %fossil branch SUBCOMMAND ... ?OPTIONS? ** ** Run various subcommands to manage branches of the open repository or ** of the repository identified by the -R or --repository option. Index: src/bundle.c ================================================================== --- src/bundle.c +++ src/bundle.c @@ -716,11 +716,11 @@ } db_end_transaction(0); } /* -** COMMAND: bundle +** COMMAND: bundle| ** ** Usage: %fossil bundle SUBCOMMAND ARGS... ** ** fossil bundle append BUNDLE FILE... ** Index: src/checkin.c ================================================================== --- src/checkin.c +++ src/checkin.c @@ -350,12 +350,12 @@ if( relPathOption ){ relativePaths = 1; } return relativePaths; } /* -** COMMAND: changes -** COMMAND: status +** COMMAND: changes| +** COMMAND: status| ** ** Usage: %fossil changes|status ?OPTIONS? ?PATHS ...? ** ** Report the change status of files in the current checkout. If one or ** more PATHS are specified, only changes among the named files and @@ -644,11 +644,11 @@ } db_finalize(&q); } /* -** COMMAND: ls +** COMMAND: ls| ** ** Usage: %fossil ls ?OPTIONS? ?PATHS ...? ** ** List all files in the current checkout. If PATHS is included, only the ** named files (or their children if directories) are shown. @@ -802,11 +802,11 @@ } db_finalize(&q); } /* -** COMMAND: extras +** COMMAND: extras| ** ** Usage: %fossil extras ?OPTIONS? ?PATH1 ...? ** ** Print a list of all files in the source tree that are not part of the ** current checkout. See also the "clean" command. If paths are specified, @@ -872,11 +872,11 @@ } blob_reset(&report); } /* -** COMMAND: clean +** COMMAND: clean| ** ** Usage: %fossil clean ?OPTIONS? ?PATH ...? ** ** Delete all "extra" files in the source tree. "Extra" files are files ** that are not officially part of the checkout. If one or more PATH Index: src/clone.c ================================================================== --- src/clone.c +++ src/clone.c @@ -209,14 +209,14 @@ db_open_repository(g.argv[3]); } db_begin_transaction(); fossil_print("Rebuilding repository meta-data...\n"); rebuild_db(0, 1, 0); - fossil_print("Extra delta compression... "); fflush(stdout); + fossil_print("Extra delta compression... "); fflush(our_stdout); extra_deltification(); db_end_transaction(0); - fossil_print("\nVacuuming the database... "); fflush(stdout); + fossil_print("\nVacuuming the database... "); fflush(our_stdout); if( db_int(0, "PRAGMA page_count")>1000 && db_int(0, "PRAGMA page_size")<8192 ){ db_multi_exec("PRAGMA page_size=8192;"); } db_multi_exec("VACUUM"); Index: src/content.c ================================================================== --- src/content.c +++ src/content.c @@ -308,11 +308,11 @@ } return rc; } /* -** COMMAND: artifact* +** COMMAND: artifact*| ** ** Usage: %fossil artifact ARTIFACT-ID ?OUTPUT-FILENAME? ?OPTIONS? ** ** Extract an artifact by its artifact hash and write the results on ** standard output, or if the optional 4th argument is given, in @@ -921,11 +921,11 @@ const char *zUuid = db_column_text(&q, 1); int nUuid = db_column_bytes(&q, 1); int size = db_column_int(&q, 2); n1++; fossil_print(" %d/%d\r", n1, total); - fflush(stdout); + fflush(our_stdout); if( size<0 ){ fossil_print("skip phantom %d %s\n", rid, zUuid); continue; /* Ignore phantoms */ } content_get(rid, &content); Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -2775,10 +2775,11 @@ { "max-upload", 0, 25, 0, 0, "250000" }, { "mtime-changes", 0, 0, 0, 0, "on" }, #if FOSSIL_ENABLE_LEGACY_MV_RM { "mv-rm-files", 0, 0, 0, 0, "off" }, #endif + { "pager-command", 0, 40, 0, 0, "" }, { "pgp-command", 0, 40, 0, 0, "gpg --clearsign -o " }, { "proxy", 0, 32, 0, 0, "off" }, { "relative-paths", 0, 0, 0, 0, "on" }, { "repo-cksum", 0, 0, 0, 0, "on" }, { "self-register", 0, 0, 0, 0, "off" }, @@ -3000,10 +3001,15 @@ ** support), the "mv" and "rename" commands will also move ** the associated files within the checkout -AND- the "rm" ** and "delete" commands will also remove the associated ** files from within the checkout. Default: off. ** +** +** pager-command Pager command to run when some commands outputs don't +** fit on a single screen. PAGER environment variable has +** precedence over this setting. +** ** pgp-command Command used to clear-sign manifests at check-in. ** The default is "gpg --clearsign -o ". ** ** proxy URL of the HTTP proxy. If undefined or "off" then ** the "http_proxy" environment variable is consulted. Index: src/descendants.c ================================================================== --- src/descendants.c +++ src/descendants.c @@ -265,11 +265,11 @@ rid, N ); } /* -** COMMAND: descendants* +** COMMAND: descendants*| ** ** Usage: %fossil descendants ?CHECKIN? ?OPTIONS? ** ** Find all leaf descendants of the check-in specified or if the argument ** is omitted, of the check-in currently checked out. @@ -317,11 +317,11 @@ print_timeline(&q, 0, width, 0); db_finalize(&q); } /* -** COMMAND: leaves* +** COMMAND: leaves*| ** ** Usage: %fossil leaves ?OPTIONS? ** ** Find leaves of all branches. By default show only open leaves. ** The -a|--all flag causes all leaves (closed and open) to be shown. Index: src/diff.c ================================================================== --- src/diff.c +++ src/diff.c @@ -2459,13 +2459,13 @@ @ </pre> style_footer(); } /* -** COMMAND: annotate -** COMMAND: blame -** COMMAND: praise +** COMMAND: annotate| +** COMMAND: blame| +** COMMAND: praise| ** ** Usage: %fossil (annotate|blame|praise) ?OPTIONS? FILENAME ** ** Output the text of a file with markings to show when each line of ** the file was last modified. The "annotate" command shows line numbers Index: src/diffcmd.c ================================================================== --- src/diffcmd.c +++ src/diffcmd.c @@ -784,11 +784,11 @@ if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0); return zBinGlob; } /* -** COMMAND: diff +** COMMAND: diff| ** COMMAND: gdiff ** ** Usage: %fossil diff|gdiff ?OPTIONS? ?FILE1? ?FILE2 ...? ** ** Show the difference between the current version of each of the FILEs Index: src/dispatch.c ================================================================== --- src/dispatch.c +++ src/dispatch.c @@ -44,15 +44,16 @@ #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ +#define CMDFLAG_PAGINATE 0x0020 /* A command which can be paginated */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0018 /* Match anything */ -#define CMDFLAG_PREFIX 0x0020 /* Prefix match is ok */ +#define CMDFLAG_PREFIX 0x0040 /* Prefix match is ok */ #endif /* INTERFACE */ /* ** The page_index.h file contains the definition for aCommand[] - an array Index: src/finfo.c ================================================================== --- src/finfo.c +++ src/finfo.c @@ -19,11 +19,11 @@ */ #include "config.h" #include "finfo.h" /* -** COMMAND: finfo +** COMMAND: finfo| ** ** Usage: %fossil finfo ?OPTIONS? FILENAME ** ** Print the complete change history for a single file going backwards ** in time. The default mode is -l. @@ -234,11 +234,11 @@ blob_reset(&fname); } } /* -** COMMAND: cat +** COMMAND: cat| ** ** Usage: %fossil cat FILENAME ... ?OPTIONS? ** ** Print on standard output the content of one or more files as they exist ** in the repository. The version currently checked out is shown by default. Index: src/http_transport.c ================================================================== --- src/http_transport.c +++ src/http_transport.c @@ -315,11 +315,11 @@ }else if( pUrlData->isFile ){ got = fread(zBuf, 1, N, transport.pFile); }else{ got = socket_receive(0, zBuf, N); } - /* printf("received %d of %d bytes\n", got, N); fflush(stdout); */ + /* printf("received %d of %d bytes\n", got, N); fflush(our_stdout); */ if( transport.pLog ){ fwrite(zBuf, 1, got, transport.pLog); fflush(transport.pLog); } return got; @@ -334,16 +334,16 @@ int nByte = 0; /* Bytes of content received */ onHand = transport.nUsed - transport.iCursor; if( g.fSshTrace){ printf("Reading %d bytes with %d on hand... ", N, onHand); - fflush(stdout); + fflush(our_stdout); } if( onHand>0 ){ int toMove = onHand; if( toMove>N ) toMove = N; - /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(stdout); */ + /* printf("bytes on hand: %d of %d\n", toMove, N); fflush(our_stdout); */ memcpy(zBuf, &transport.pBuf[transport.iCursor], toMove); transport.iCursor += toMove; if( transport.iCursor>=transport.nUsed ){ transport.nUsed = 0; transport.iCursor = 0; @@ -456,10 +456,10 @@ ** Close SSH transport. */ void transport_ssh_close(void){ if( sshPid ){ /*printf("Closing SSH tunnel: ");*/ - fflush(stdout); + fflush(our_stdout); pclose2(sshIn, sshOut, sshPid); sshPid = 0; } } Index: src/import.c ================================================================== --- src/import.c +++ src/import.c @@ -586,11 +586,11 @@ }else if( strncmp(zLine, "progress ", 9)==0 ){ gg.xFinish(); trim_newline(&zLine[9]); fossil_print("%s\n", &zLine[9]); - fflush(stdout); + fflush(our_stdout); }else if( strncmp(zLine, "data ", 5)==0 ){ fossil_free(gg.aData); gg.aData = 0; gg.nData = atoi(&zLine[5]); if( gg.nData ){ @@ -1872,11 +1872,11 @@ rebuild_db(0, 1, !incrFlag); verify_cancel(); db_end_transaction(0); } if( !omitVacuum ){ - fossil_print("Vacuuming..."); fflush(stdout); + fossil_print("Vacuuming..."); fflush(our_stdout); db_multi_exec("VACUUM"); } fossil_print(" ok\n"); if( !incrFlag ){ fossil_print("project-id: %s\n", db_get("project-code", 0)); Index: src/json.c ================================================================== --- src/json.c +++ src/json.c @@ -604,15 +604,15 @@ if( g.json.jsonp ){ cgi_append_content(")",1); } }else{/*CLI mode*/ if( g.json.jsonp ){ - fprintf(stdout,"%s(",g.json.jsonp); + fprintf(our_stdout,"%s(",g.json.jsonp); } - cson_output_FILE( pResponse, stdout, &g.json.outOpt ); + cson_output_FILE( pResponse, our_stdout, &g.json.outOpt ); if( g.json.jsonp ){ - fwrite(")\n", 2, 1, stdout); + fwrite(")\n", 2, 1, our_stdout); } } } /* Index: src/json_user.c ================================================================== --- src/json_user.c +++ src/json_user.c @@ -350,11 +350,11 @@ #else /* need name for login group support :/ */ blob_append_sql(&sql, " WHERE login=%Q", zName); #endif #if 0 puts(blob_str(&sql)); - cson_output_FILE( cson_object_value(pUser), stdout, NULL ); + cson_output_FILE( cson_object_value(pUser), our_stdout, NULL ); #endif db_prepare(&q, "%s", blob_sql_text(&sql)); db_exec(&q); db_finalize(&q); #if TRY_LOGIN_GROUP Index: src/main.c ================================================================== --- src/main.c +++ src/main.c @@ -22,10 +22,11 @@ #include "config.h" #if defined(_WIN32) # include <windows.h> #endif #include "main.h" +#include "popen.h" #include <string.h> #include <time.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> @@ -298,10 +299,13 @@ #endif Global g; +static int pagerPid = 0; /* Process id of pager subprocess */ +extern FILE *our_stdout; + /* ** atexit() handler which frees up "some" of the resources ** used by fossil. */ static void fossil_atexit(void) { @@ -347,10 +351,15 @@ if( g.interp ){ Th_DeleteInterp(g.interp); g.interp = 0; } assert( Th_GetOutstandingMalloc()==0 ); } + + if( pagerPid>0 ){ + /* Close connection with pager process */ + pclose2(0, our_stdout, pagerPid); + } } /* ** Convert all arguments from mbcs (or unicode) to UTF-8. Then ** search g.argv for arguments "--args FILENAME". If found, then @@ -553,10 +562,12 @@ #endif { const char *zCmdName = "unknown"; const CmdOrPage *pCmd = 0; int rc; + char *pager; + our_stdout = stdout; if( sqlite3_libversion_number()<3014000 ){ fossil_fatal("Unsuitable SQLite version %s, must be at least 3.14.0", sqlite3_libversion()); } sqlite3_config(SQLITE_CONFIG_MULTITHREAD); @@ -755,10 +766,25 @@ rc = TH_OK; } if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){ if( rc==TH_OK || rc==TH_RETURN ){ #endif + + if( !g.isHTTP && (pCmd->eCmdFlags & CMDFLAG_PAGINATE) && is_tty(stdout) ){ + pager = fossil_getenv("PAGER"); + if( pager==0 ){ + db_find_and_open_repository(0, 0); + pager = db_get("pager-command", 0); + } + if( pager != 0 && strcmp(pager, "")!=0 ){ + /* Launch pager as a subprocess and pipe our output to its input */ + if( popen2(pager, 0, &our_stdout, &pagerPid) != 0 ){ + our_stdout = stdout; + } + } + } + pCmd->xFunc(); #ifdef FOSSIL_ENABLE_TH1_HOOKS } if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){ Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags); Index: src/merge3.c ================================================================== --- src/merge3.c +++ src/merge3.c @@ -492,11 +492,11 @@ azSubst[0] = "%baseline"; azSubst[1] = zPivot; azSubst[2] = "%original"; azSubst[3] = zOrig; azSubst[4] = "%merge"; azSubst[5] = zOther; azSubst[6] = "%output"; azSubst[7] = zOut; zCmd = string_subst(zGMerge, 8, azSubst); - printf("%s\n", zCmd); fflush(stdout); + printf("%s\n", zCmd); fflush(our_stdout); fossil_system(zCmd); if( file_wd_size(zOut)>=0 ){ blob_read_from_file(pOut, zOut); file_delete(zPivot); file_delete(zOrig); Index: src/mkindex.c ================================================================== --- src/mkindex.c +++ src/mkindex.c @@ -42,15 +42,21 @@ ** ** Commands are 1st-tier by default. If the command name begins with ** "test-" or if the command name has a "test" argument, then it becomes ** a test command. If the command name has a "2nd-tier" argument or ends ** with a "*" character, it is second tier. Examples: +** If the command name has a "paginate" argument or ends with +** a "|" character, its output can be sent through a pager. Examples: ** ** COMMAND: abcde* ** COMMAND: fghij 2nd-tier ** COMMAND: test-xyzzy ** COMMAND: xyzzy test +** COMMAND: paginated1| +** COMMAND: paginated2 paginate +** COMMAND: paginatedand2ndtier1*| +** COMMAND: paginatedand2ndtier2 2nd-tier paginate ** ** New arguments may be added in future releases that set additional ** bits in the eCmdFlags field. ** ** Additional lines of comment after the COMMAND: or WEBPAGE: become @@ -73,10 +79,11 @@ #define CMDFLAG_1ST_TIER 0x0001 /* Most important commands */ #define CMDFLAG_2ND_TIER 0x0002 /* Obscure and seldom used commands */ #define CMDFLAG_TEST 0x0004 /* Commands for testing only */ #define CMDFLAG_WEBPAGE 0x0008 /* Web pages */ #define CMDFLAG_COMMAND 0x0010 /* A command */ +#define CMDFLAG_PAGINATE 0x0020 /* A command which can be paginated */ /**************************************************************************/ /* ** Each entry looks like this: */ @@ -191,18 +198,30 @@ aEntry[nUsed].zFunc = 0; if( (eType & CMDFLAG_COMMAND)!=0 ){ if( strncmp(&zLine[i], "test-", 5)==0 ){ /* Commands that start with "test-" are test-commands */ aEntry[nUsed].eType |= CMDFLAG_TEST; - }else if( zLine[i+j-1]=='*' ){ - /* If the command name ends in '*', remove the '*' from the name - ** but move the command into the second tier */ - aEntry[nUsed].zPath[j-1] = 0; - aEntry[nUsed].eType |= CMDFLAG_2ND_TIER; }else{ - /* Otherwise, this is a first-tier command */ aEntry[nUsed].eType |= CMDFLAG_1ST_TIER; + while( zLine[i+j-1]=='*' || zLine[i+j-1]=='|' ){ + if( zLine[i+j-1]=='*' ){ + /* If the command name ends in '*', + ** move the command into the second tier */ + aEntry[nUsed].eType &= ~CMDFLAG_1ST_TIER; + aEntry[nUsed].eType |= CMDFLAG_2ND_TIER; + }else if( zLine[i+j-1]=='|' ){ + /* If the command name ends in '|', + ** consider its output can be sent to a pager */ + aEntry[nUsed].eType |= CMDFLAG_PAGINATE; + } + aEntry[nUsed].zPath[j-1] = 0; + /* replace just processed '*' or '|' by a blank character so that + * it is no more part of the command name + */ + zLine[i+j-1] = ' '; + j--; + } } } /* Process additional flags that might follow the command name */ while( zLine[i+j]!=0 ){ @@ -217,10 +236,12 @@ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_TEST); aEntry[nUsed].eType |= CMDFLAG_2ND_TIER; }else if( j==4 && strncmp(&zLine[i], "test", j)==0 ){ aEntry[nUsed].eType &= ~(CMDFLAG_1ST_TIER|CMDFLAG_2ND_TIER); aEntry[nUsed].eType |= CMDFLAG_TEST; + }else if( j==8 && strncmp(&zLine[i], "paginate", j)==0 ){ + aEntry[nUsed].eType |= CMDFLAG_PAGINATE; }else{ fprintf(stderr, "%s:%d: unknown option: '%.*s'\n", zFile, nLine, j, &zLine[i]); nErr++; } Index: src/popen.c ================================================================== --- src/popen.c +++ src/popen.c @@ -21,15 +21,16 @@ #include "popen.h" #ifdef _WIN32 #include <windows.h> #include <fcntl.h> +#include <string.h> /* ** Print a fatal error and quit. */ static void win32_fatal_error(const char *zMsg){ - fossil_fatal("%s", zMsg); + fossil_fatal("%s : %d", zMsg, GetLastError()); } #else #include <signal.h> #include <sys/wait.h> #endif @@ -116,11 +117,13 @@ ** Create a child process running shell command "zCmd". *ppOut is ** a FILE that becomes the standard input of the child process. ** (The caller writes to *ppOut in order to send text to the child.) ** *ppIn is stdout from the child process. (The caller ** reads from *ppIn in order to receive input from the child.) -** Note that *ppIn is an unbuffered file descriptor, not a FILE. +** Notes concerning *ppIn: +** 1)It can be NULL if output of child process needs not be captured. +** 2)It is an unbuffered file descriptor, not a FILE. ** The process ID of the child is written into *pChildPid. ** ** Return the number of errors. */ int popen2(const char *zCmd, int *pfdIn, FILE **ppOut, int *pChildPid){ @@ -145,19 +148,23 @@ SetHandleInformation( hStdinWr, HANDLE_FLAG_INHERIT, FALSE); win32_create_child_process(fossil_utf8_to_unicode(zCmd), hStdinRd, hStdoutWr, hStderr,&childPid); *pChildPid = childPid; - *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0); + if( pfdIn ){ + *pfdIn = _open_osfhandle(PTR_TO_INT(hStdoutRd), 0); + } fd = _open_osfhandle(PTR_TO_INT(hStdinWr), 0); *ppOut = _fdopen(fd, "w"); CloseHandle(hStdinRd); CloseHandle(hStdoutWr); return 0; #else int pin[2], pout[2]; - *pfdIn = 0; + if( pfdIn ){ + *pfdIn = 0; + } *ppOut = 0; *pChildPid = 0; if( pipe(pin)<0 ){ return 1; @@ -184,21 +191,25 @@ close(0); fd = dup(pout[0]); if( fd!=0 ) nErr++; close(pout[0]); close(pout[1]); - close(1); - fd = dup(pin[1]); - if( fd!=1 ) nErr++; - close(pin[0]); - close(pin[1]); + if( pfdIn ){ + close(1); + fd = dup(pin[1]); + if( fd!=1 ) nErr++; + close(pin[0]); + close(pin[1]); + } execl("/bin/sh", "/bin/sh", "-c", zCmd, (char*)0); return 1; }else{ /* This is the parent process */ close(pin[1]); - *pfdIn = pin[0]; + if( pfdIn ){ + *pfdIn = pin[0]; + } close(pout[0]); *ppOut = fdopen(pout[1], "w"); return 0; } #endif @@ -205,17 +216,20 @@ } /* ** Close the connection to a child process previously created using ** popen2(). +** fdIn must be >0. Otherwise there won't be any attempt to close it. */ void pclose2(int fdIn, FILE *pOut, int childPid){ #ifdef _WIN32 /* Not implemented, yet */ close(fdIn); fclose(pOut); #else - close(fdIn); + if( fdIn>0 ){ + close(fdIn); + } fclose(pOut); - while( waitpid(0, 0, WNOHANG)>0 ) {} + while( waitpid(0, 0, 0)>0 ) {} #endif } Index: src/printf.c ================================================================== --- src/printf.c +++ src/printf.c @@ -870,10 +870,17 @@ ** a line. ** was a \n */ static int stdoutAtBOL = 1; +/* The stream used as standard output one +** Set to: +** 1)Conventional stdout if no pager is used. +** 2)Input stream of the pager otherwise. +*/ +FILE *our_stdout = 0; + /* ** Write to standard output or standard error. ** ** On windows, transform the output into the current terminal encoding ** if the output is going to the screen. If output is redirected into @@ -881,11 +888,11 @@ ** properly process line-endings, make sure to switch the mode back to ** text when done. ** No translation ever occurs on unix. */ void fossil_puts(const char *z, int toStdErr){ - FILE* out = (toStdErr ? stderr : stdout); + FILE* out = (toStdErr ? stderr : our_stdout); int n = (int)strlen(z); if( n==0 ) return; assert( toStdErr==0 || toStdErr==1 ); if( toStdErr==0 ) stdoutAtBOL = (z[n-1]=='\n'); #if defined(_WIN32) Index: src/rebuild.c ================================================================== --- src/rebuild.c +++ src/rebuild.c @@ -186,11 +186,11 @@ */ static void percent_complete(int permill){ static int lastOutput = -1; if( permill>lastOutput ){ fossil_print(" %d.%d%% complete...\r", permill/10, permill%10); - fflush(stdout); + fflush(our_stdout); lastOutput = permill; } } @@ -638,11 +638,11 @@ errCnt ); db_end_transaction(1); }else{ if( runCompress ){ - fossil_print("Extra delta compression... "); fflush(stdout); + fossil_print("Extra delta compression... "); fflush(our_stdout); extra_deltification(); runVacuum = 1; } if( omitVerify ) verify_cancel(); db_end_transaction(0); @@ -657,16 +657,16 @@ db_multi_exec("DROP TABLE IF EXISTS sqlite_stat1;" "DROP TABLE IF EXISTS sqlite_stat3;" "DROP TABLE IF EXISTS sqlite_stat4;"); } if( runAnalyze ){ - fossil_print("Analyzing the database... "); fflush(stdout); + fossil_print("Analyzing the database... "); fflush(our_stdout); db_multi_exec("ANALYZE;"); fossil_print("done\n"); } if( runVacuum ){ - fossil_print("Vacuuming the database... "); fflush(stdout); + fossil_print("Vacuuming the database... "); fflush(our_stdout); db_multi_exec("VACUUM"); fossil_print("done\n"); } if( activateWal ){ db_multi_exec("PRAGMA journal_mode=WAL;"); @@ -934,11 +934,11 @@ } content_put(&aContent); blob_reset(&path); blob_reset(&aContent); fossil_print("\r%d", ++nFileRead); - fflush(stdout); + fflush(our_stdout); } free(zSubpath); } closedir(d); }else { @@ -1068,11 +1068,11 @@ bag_init(&bagDone); ttyOutput = 1; processCnt = 0; if (!g.fQuiet) { fossil_print("0 (0%%)...\r"); - fflush(stdout); + fflush(our_stdout); } totalSize = db_int(0, "SELECT count(*) FROM blob"); db_prepare(&s, "SELECT rid, size FROM blob /*scan*/" " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)" Index: src/search.c ================================================================== --- src/search.c +++ src/search.c @@ -552,11 +552,11 @@ } /* ** Testing the search function. ** -** COMMAND: search* +** COMMAND: search*| ** ** Usage: %fossil search [-all|-a] [-limit|-n #] [-width|-W #] pattern... ** ** Search for timeline entries matching all words provided on the ** command line. Whole-word matches scope more highly than partial @@ -1674,11 +1674,11 @@ /* ** Construct, prepopulate, and then update the full-text index. */ void search_rebuild_index(void){ fossil_print("rebuilding the search index..."); - fflush(stdout); + fflush(our_stdout); search_create_index(); search_fill_index(); search_update_index(search_restrict(SRCH_ALL)); fossil_print(" done\n"); } Index: src/tag.c ================================================================== --- src/tag.c +++ src/tag.c @@ -363,11 +363,11 @@ assert( blob_is_reset(&ctrl) ); manifest_to_disk(rid); } /* -** COMMAND: tag +** COMMAND: tag| ** ** Usage: %fossil tag SUBCOMMAND ... ** ** Run various subcommands to control tags and properties. ** Index: src/timeline.c ================================================================== --- src/timeline.c +++ src/timeline.c @@ -2310,11 +2310,11 @@ static int fossil_is_julianday(const char *zDate){ return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd WHERE jd IS NOT NULL)", zDate); } /* -** COMMAND: timeline +** COMMAND: timeline| ** ** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS? ** ** Print a summary of activity going backwards in date and time ** specified or from the current date and time if no arguments Index: src/tkt.c ================================================================== --- src/tkt.c +++ src/tkt.c @@ -1048,11 +1048,11 @@ } @ </ol> } /* -** COMMAND: ticket* +** COMMAND: ticket*| ** ** Usage: %fossil ticket SUBCOMMAND ... ** ** Run various subcommands to control tickets ** Index: src/undo.c ================================================================== --- src/undo.c +++ src/undo.c @@ -425,12 +425,12 @@ fossil_print("Rolling back prior filesystem changes...\n"); undo_all_filesystem(0); } /* -** COMMAND: undo -** COMMAND: redo* +** COMMAND: undo| +** COMMAND: redo*| ** ** Usage: %fossil undo ?OPTIONS? ?FILENAME...? ** or: %fossil redo ?OPTIONS? ?FILENAME...? ** ** Undo the changes to the working checkout caused by the most recent Index: src/unversioned.c ================================================================== --- src/unversioned.c +++ src/unversioned.c @@ -201,12 +201,12 @@ } return 0; } /* -** COMMAND: uv* -** COMMAND: unversioned +** COMMAND: uv*| +** COMMAND: unversioned| ** ** Usage: %fossil unversioned SUBCOMMAND ARGS... ** or: %fossil uv SUBCOMMAND ARGS.. ** ** Unversioned files (UV-files) are artifacts that are synced and are available Index: src/util.c ================================================================== --- src/util.c +++ src/util.c @@ -23,18 +23,20 @@ /* ** For the fossil_timer_xxx() family of functions... */ #ifdef _WIN32 # include <windows.h> +# include <io.h> #else # include <sys/time.h> # include <sys/resource.h> # include <unistd.h> # include <fcntl.h> # include <errno.h> #endif +# include <stdio.h> /* ** Exit. Take care to close the database first. */ NORETURN void fossil_exit(int rc){ @@ -378,10 +380,21 @@ return 1; #else return fcntl(fd, F_GETFL)!=(-1) || errno!=EBADF; #endif } + +/* +** Return TRUE if stream is a terminal. +*/ +int is_tty(FILE *stream){ +#ifdef _WIN32 + return (stream!=0) && _isatty(_fileno(stream)); +#else + return (stream!=0) && isatty(fileno(stream)); +#endif +} /* ** Returns TRUE if zSym is exactly UUID_SIZE bytes long and contains ** only lower-case ASCII hexadecimal values. */ Index: src/wiki.c ================================================================== --- src/wiki.c +++ src/wiki.c @@ -1154,11 +1154,11 @@ } return rid; } /* -** COMMAND: wiki* +** COMMAND: wiki*| ** ** Usage: %fossil wiki (export|create|commit|list) WikiName ** ** Run various subcommands to work with wiki entries or tech notes. **
_______________________________________________ fossil-users mailing list fossil-users@lists.fossil-scm.org http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users