Browse Source

Merge branch 'candidate-5.4.0'

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 10 years ago
parent
commit
bbdc0d37a2

+ 2 - 2
common/workunit/workunit.cpp

@@ -10218,11 +10218,11 @@ extern WORKUNIT_API void gatherLibraryNames(StringArray &names, StringArray &unr
     }
 }
 
-bool looksLikeAWuid(const char * wuid)
+bool looksLikeAWuid(const char * wuid, const char firstChar)
 {
     if (!wuid)
         return false;
-    if (wuid[0] != 'W')
+    if (wuid[0] != firstChar)
         return false;
     if (!isdigit(wuid[1]) || !isdigit(wuid[2]) || !isdigit(wuid[3]) || !isdigit(wuid[4]))
         return false;

+ 1 - 1
common/workunit/workunit.hpp

@@ -1391,7 +1391,7 @@ extern WORKUNIT_API bool isQueryManifest(const char * text);
 extern WORKUNIT_API IPropertyTree * resolveDefinitionInArchive(IPropertyTree * archive, const char * path);
 
 inline bool isLibrary(IConstWorkUnit * wu) { return wu->getApplicationValueInt("LibraryModule", "interfaceHash", 0) != 0; }
-extern WORKUNIT_API bool looksLikeAWuid(const char * wuid);
+extern WORKUNIT_API bool looksLikeAWuid(const char * wuid, const char firstChar);
 
 enum WUQueryActivationOptions
 {

+ 19 - 0
common/wuwebview/wuwebview.cpp

@@ -756,6 +756,12 @@ ILoadedDllEntry *WuWebView::loadDll(bool force)
         {
             dll.setown(queryDllServer().loadDll(dllname.str(), DllLocationAnywhere));
         }
+        catch (IException *e)
+        {
+            VStringBuffer msg("Failed to load %s", dllname.str());
+            EXCLOG(e, msg.str());
+            e->Release();
+        }
         catch(...)
         {
             DBGLOG("Failed to load %s", dllname.str());
@@ -893,6 +899,13 @@ extern WUWEBVIEW_API IWuWebView *createWuWebView(IConstWorkUnit &wu, const char
     {
         return new WuWebView(wu, target, queryname, dir, mapEspDirectories);
     }
+    catch (IException *e)
+    {
+        SCMStringBuffer wuid;
+        VStringBuffer msg("ERROR loading workunit %s shared object.", wu.getWuid(wuid).str());
+        EXCLOG(e, msg.str());
+        e->Release();
+    }
     catch (...)
     {
         DBGLOG("ERROR loading workunit %s shared object.", wu.queryWuid());
@@ -906,6 +919,12 @@ extern WUWEBVIEW_API IWuWebView *createWuWebView(const char *wuid, const char *t
     {
         return new WuWebView(wuid, target, queryname, dir, mapEspDirectories);
     }
+    catch (IException *e)
+    {
+        VStringBuffer msg("ERROR loading workunit %s shared object.", wuid);
+        EXCLOG(e, msg.str());
+        e->Release();
+    }
     catch (...)
     {
         DBGLOG("ERROR loading workunit %s shared object.", wuid);

+ 6 - 2
dali/dfu/dfuwu.cpp

@@ -3043,7 +3043,8 @@ public:
             }
         };
 
-        StringBuffer query("*");
+        StringBuffer query;
+        StringAttr namefilter("*");
         StringBuffer so;
         const char *field;
         StringBuffer sf;
@@ -3058,8 +3059,10 @@ public:
                 DFUsortfield fmt = filters[i];
                 if (fmt==DFUsf_wuid) 
                     namefilterlo.set(fv);
-                else if (fmt==DFUsf_wuidhigh) 
+                else if (fmt==DFUsf_wuidhigh)
                     namefilterhi.set(fv);
+                else if (fmt==DFUsf_wildwuid)
+                    namefilter.set(fv);
                 else if (!fv || !*fv)
                 {
                     const char* attr = getDFUSortFieldXPath(fmt);
@@ -3079,6 +3082,7 @@ public:
                 fv += strlen(fv)+1;
             }
         }
+        query.insert(0, namefilter.get());
         if (sortorder)
         {
             for (unsigned i=0;sortorder[i]!=DFUsf_term;i++)

+ 1 - 0
dali/dfu/dfuwu.hpp

@@ -118,6 +118,7 @@ enum DFUsortfield
     DFUsf_pcdone,
     DFUsf_wuidhigh,         // only for filter high of range
     DFUsf_protected,
+    DFUsf_wildwuid,
     DFUsf_term = 0,
     DFUsf_reverse = 0x100,  // for sort
     DFUsf_nocase  = 0x200,  // sort and filter

+ 18 - 12
docs/ECLLanguageReference/ECLR_mods/BltInFunc-MERGEJOIN.xml

@@ -184,17 +184,22 @@ ds2 := DATASET([{'A',2},{'B',2},{'H',2},{'I',2},{'J',2}],Rec);
 ds3 := DATASET([{'B',3},{'C',3},{'M',3},{'N',3},{'O',3}],Rec);
 ds4 := DATASET([{'A',4},{'B',4},{'R',4},{'S',4},{'T',4}],Rec);
 ds5 := DATASET([{'B',5},{'V',5},{'W',5},{'X',5},{'Y',5}],Rec);
-SetDS := [ds1,ds2,ds3,ds4,ds5];j1 := MERGEJOIN(SetDS,
-      STEPPED(LEFT.Letter=RIGHT.Letter),
-      SORTED(Letter));j2 := MERGEJOIN(SetDS,
-      STEPPED(LEFT.Letter=RIGHT.Letter),
-      SORTED(Letter),LEFT OUTER);j3 := MERGEJOIN(SetDS,
-      STEPPED(LEFT.Letter=RIGHT.Letter),
-      SORTED(Letter),LEFT ONLY);j4 := MERGEJOIN(SetDS,
-      STEPPED(LEFT.Letter=RIGHT.Letter),
-      SORTED(Letter),MOFN(3));j5 := MERGEJOIN(SetDS,
-      STEPPED(LEFT.Letter=RIGHT.Letter),
-      SORTED(Letter),MOFN(3,4));
+SetDS := [ds1,ds2,ds3,ds4,ds5];
+j1 := MERGEJOIN(SetDS,
+                STEPPED(LEFT.Letter=RIGHT.Letter),
+                SORTED(Letter));
+j2 := MERGEJOIN(SetDS,
+                STEPPED(LEFT.Letter=RIGHT.Letter),
+                SORTED(Letter),LEFT OUTER);
+j3 := MERGEJOIN(SetDS,
+                STEPPED(LEFT.Letter=RIGHT.Letter),
+                SORTED(Letter),LEFT ONLY);
+j4 := MERGEJOIN(SetDS,
+                STEPPED(LEFT.Letter=RIGHT.Letter),
+                SORTED(Letter),MOFN(3));
+j5 := MERGEJOIN(SetDS,
+                STEPPED(LEFT.Letter=RIGHT.Letter),
+                SORTED(Letter),MOFN(3,4));
 OUTPUT(j1);
 OUTPUT(j2);
 OUTPUT(j3);
@@ -202,6 +207,7 @@ OUTPUT(j4);
 OUTPUT(j5);
 </programlisting>
 
-    <para>See Also: <link linkend="MERGE">MERGE</link>, <link linkend="JOIN">JOIN</link>, <link linkend="STEPPED">STEPPED</link></para>
+    <para>See Also: <link linkend="MERGE">MERGE</link>, <link
+    linkend="JOIN">JOIN</link>, <link linkend="STEPPED">STEPPED</link></para>
   </sect2>
 </sect1>

+ 1 - 0
esp/scm/ws_fs.ecm

@@ -126,6 +126,7 @@ DFUWUSearchResponse
 
 ESPrequest GetDFUWorkunits
 {
+    [min_ver("1.12")] string Wuid;
     string Owner;
     string Cluster;
     string StateReq;

+ 59 - 0
esp/services/ws_fs/ws_fsService.cpp

@@ -843,6 +843,53 @@ bool CFileSprayEx::GetArchivedDFUWorkunits(IEspContext &context, IEspGetDFUWorku
     return true;
 }
 
+bool CFileSprayEx::getOneDFUWorkunit(IEspContext& context, const char* wuid, IEspGetDFUWorkunitsResponse& resp)
+{
+    Owned<IDFUWorkUnitFactory> factory = getDFUWorkUnitFactory();
+    Owned<IConstDFUWorkUnit> wu = factory->openWorkUnit(wuid, false);
+    if (!wu)
+        throw MakeStringException(ECLWATCH_CANNOT_OPEN_WORKUNIT, "Dfu workunit %s not found.", wuid);
+
+    Owned<IEspDFUWorkunit> resultWU = createDFUWorkunit();
+    resultWU->setID(wuid);
+    resultWU->setCommand(wu->getCommand());
+    resultWU->setIsProtected(wu->isProtected());
+
+    StringBuffer jobname, user, cluster;
+    resultWU->setJobName(wu->getJobName(jobname).str());
+    resultWU->setUser(wu->getUser(user).str());
+
+    const char* clusterName = wu->getClusterName(cluster).str();
+    if (clusterName && *clusterName)
+    {
+        Owned<IStringIterator> targets = getTargetClusters(NULL, clusterName);
+        if (!targets->first())
+            resultWU->setClusterName(clusterName);
+        else
+        {
+            SCMStringBuffer targetCluster;
+            targets->str(targetCluster);
+            resultWU->setClusterName(targetCluster.str());
+        }
+    }
+
+    IConstDFUprogress* prog = wu->queryProgress();
+    if (prog)
+    {
+        StringBuffer statemsg;
+        DFUstate state = prog->getState();
+        encodeDFUstate(state, statemsg);
+        resultWU->setState(state);
+        resultWU->setStateMessage(statemsg.str());
+        resultWU->setPercentDone(prog->getPercentDone());
+    }
+
+    IArrayOf<IEspDFUWorkunit> result;
+    result.append(*resultWU.getClear());
+    resp.setResults(result);
+    return true;
+}
+
 bool CFileSprayEx::onGetDFUWorkunits(IEspContext &context, IEspGetDFUWorkunits &req, IEspGetDFUWorkunitsResponse &resp)
 {
     try
@@ -850,6 +897,11 @@ bool CFileSprayEx::onGetDFUWorkunits(IEspContext &context, IEspGetDFUWorkunits &
         if (!context.validateFeatureAccess(DFU_WU_URL, SecAccess_Read, false))
             throw MakeStringException(ECLWATCH_DFU_WU_ACCESS_DENIED, "Access to DFU workunit is denied.");
 
+        StringBuffer wuidStr = req.getWuid();
+        const char* wuid = wuidStr.trim().str();
+        if (wuid && *wuid && looksLikeAWuid(wuid, 'D'))
+            return getOneDFUWorkunit(context, wuid, resp);
+
         double version = context.getClientVersion();
         if (version > 1.02)
         {
@@ -975,6 +1027,13 @@ bool CFileSprayEx::onGetDFUWorkunits(IEspContext &context, IEspGetDFUWorkunits &
                 filterbuf.append("");
         }
 
+        if(wuid && *wuid)
+        {
+            filters[filterCount] = DFUsf_wildwuid;
+            filterCount++;
+            filterbuf.append(wuid);
+        }
+
         if(clusterName && *clusterName)
         {
             filters[filterCount] = DFUsf_cluster;

+ 1 - 0
esp/services/ws_fs/ws_fsService.hpp

@@ -113,6 +113,7 @@ protected:
     bool ParseLogicalPath(const char * pLogicalPath, const char *group, const char* cluster, StringBuffer &folder, StringBuffer &title, StringBuffer &defaultFolder, StringBuffer &defaultReplicateFolder);
     StringBuffer& getAcceptLanguage(IEspContext& context, StringBuffer& acceptLanguage);
     void appendGroupNode(IArrayOf<IEspGroupNode>& groupNodes, const char* nodeName, const char* clusterType, bool replicateOutputs);
+    bool getOneDFUWorkunit(IEspContext& context, const char* wuid, IEspGetDFUWorkunitsResponse& resp);
 };
 
 #endif //_ESPWIZ_FileSpray_HPP__

+ 1 - 1
esp/services/ws_workunits/ws_workunitsHelpers.cpp

@@ -3084,7 +3084,7 @@ void WsWuHelpers::checkAndTrimWorkunit(const char* methodName, StringBuffer& inp
     if (isEmpty(trimmedInput))
         throw MakeStringException(ECLWATCH_INVALID_INPUT, "%s: Workunit ID not set", methodName);
 
-    if (!looksLikeAWuid(trimmedInput))
+    if (!looksLikeAWuid(trimmedInput, 'W'))
         throw MakeStringException(ECLWATCH_INVALID_INPUT, "%s: Invalid Workunit ID: %s", methodName, trimmedInput);
 
     return;

+ 7 - 7
esp/services/ws_workunits/ws_workunitsService.cpp

@@ -177,7 +177,7 @@ bool doAction(IEspContext& context, StringArray& wuids, int action, IProperties*
 
         try
         {
-            if (!looksLikeAWuid(wuid))
+            if (!looksLikeAWuid(wuid, 'W'))
                 throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Workunit ID: %s", wuid);
 
             if ((action == ActionRestore) || (action == ActionEventDeschedule))
@@ -1086,7 +1086,7 @@ bool CWsWorkunitsEx::onWURun(IEspContext &context, IEspWURunRequest &req, IEspWU
 
         if (runWuid && *runWuid)
         {
-            if (!looksLikeAWuid(runWuid))
+            if (!looksLikeAWuid(runWuid, 'W'))
                 throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Workunit ID: %s", runWuid);
 
             if (req.getCloneWorkunit())
@@ -1876,7 +1876,7 @@ void doWUQueryWithSort(IEspContext &context, IEspWUQueryRequest & req, IEspWUQue
         }
 
         const char* wuid = cw.queryWuid();
-        if (!looksLikeAWuid(wuid))
+        if (!looksLikeAWuid(wuid, 'W'))
         {
             numWUs--;
             continue;
@@ -2210,7 +2210,7 @@ bool CWsWorkunitsEx::onWUQuery(IEspContext &context, IEspWUQueryRequest & req, I
 
         if (req.getType() && strieq(req.getType(), "archived workunits"))
             doWUQueryFromArchive(context, sashaServerIp.get(), sashaServerPort, *archivedWuCache, awusCacheMinutes, req, resp);
-        else if(notEmpty(wuid) && looksLikeAWuid(wuid))
+        else if(notEmpty(wuid) && looksLikeAWuid(wuid, 'W'))
             doWUQueryBySingleWuid(context, wuid, resp);
         else if (notEmpty(req.getLogicalFile()) && req.getLogicalFileSearchType() && strieq(req.getLogicalFileSearchType(), "Created"))
             doWUQueryByFile(context, req.getLogicalFile(), resp);
@@ -2487,7 +2487,7 @@ bool CWsWorkunitsEx::onWUFile(IEspContext &context,IEspWULogFileRequest &req, IE
         const char* wuidIn = wuidStr.trim().str();
         if (wuidIn && *wuidIn)
         {
-            if (!looksLikeAWuid(wuidIn))
+            if (!looksLikeAWuid(wuidIn, 'W'))
                 throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Workunit ID");
 
             ensureWsWorkunitAccess(context, wuidIn, SecAccess_Read);
@@ -2613,7 +2613,7 @@ bool CWsWorkunitsEx::onWUResultBin(IEspContext &context,IEspWUResultBinRequest &
         const char* wuidIn = wuidStr.trim().str();
         if (wuidIn && *wuidIn)
         {
-            if (!looksLikeAWuid(wuidIn))
+            if (!looksLikeAWuid(wuidIn, 'W'))
                 throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Workunit ID: %s", wuidIn);
 
             ensureWsWorkunitAccess(context, wuidIn, SecAccess_Read);
@@ -2806,7 +2806,7 @@ bool CWsWorkunitsEx::onWUResult(IEspContext &context, IEspWUResultRequest &req,
         const char* wuid = wuidStr.trim().str();
         if (wuid && *wuid)
         {
-            if (!looksLikeAWuid(wuid))
+            if (!looksLikeAWuid(wuid, 'W'))
                 throw MakeStringException(ECLWATCH_INVALID_INPUT, "Invalid Workunit ID: %s", wuid);
 
             ensureWsWorkunitAccess(context, wuid, SecAccess_Read);

+ 4 - 0
esp/src/eclwatch/ESPWorkunit.js

@@ -48,6 +48,10 @@ define([
             if (request.Sortby && request.Sortby === "TotalClusterTime") {
                 request.Sortby = "ClusterTime";
             }
+            this.busy = true;
+        },
+        preProcessFullResponse: function (response, request, query, options) {
+            this.busy = false;
         },
         create: function (id) {
             return new Workunit({

+ 16 - 3
esp/src/eclwatch/WUQueryWidget.js

@@ -88,7 +88,9 @@ define([
             this._idleWatcher.start();
             var context = this;
             this._idleWatcherHandle = this._idleWatcher.on("idle", function () {
-                context._onRefresh();
+                if (!context.store.busy && !context.filter.exists()) {
+                    context._onRefresh();
+                }
             });
         },
 
@@ -203,6 +205,9 @@ define([
         _onFilterType: function (evt) {
             var filter = this.filter.toObject();
             this.setVisible(this.id + "ArchivedWarning", filter.Type);
+            this.setDisabled(this.id + "ECL", filter.Type);
+            this.setDisabled(this.id + "LogicalFile", filter.Type);
+            this.setDisabled(this.id + "LogicalFileSearchType", filter.Type);
         },
 
         //  Implementation  ---
@@ -212,11 +217,19 @@ define([
                 lang.mixin(retVal, {
                     StartDate: this.getISOString("FromDate", "FromTime")
                 });
+            } else if (retVal.StartDate) {
+                lang.mixin(retVal, {
+                    StartDate: registry.byId(this.id + "FromDate").attr("value").toISOString()
+                });
             }
             if (retVal.EndDate && retVal.ToTime) {
                 lang.mixin(retVal, {
                     EndDate: this.getISOString("ToDate", "ToTime")
                 });
+            } else if (retVal.EndDate) {
+                lang.mixin(retVal, {
+                    EndDate: registry.byId(this.id + "ToDate").attr("value").toISOString()
+                });
             }
             if (retVal.StartDate && retVal.EndDate) {
                 retVal["DateRB"] = "0";
@@ -378,9 +391,9 @@ define([
 
         initWorkunitsGrid: function () {
             var context = this;
-            var store = this.params.searchResults ? this.params.searchResults : new ESPWorkunit.CreateWUQueryStore();
+            this.store = this.params.searchResults ? this.params.searchResults : new ESPWorkunit.CreateWUQueryStore();
             this.workunitsGrid = new declare([ESPUtil.Grid(true, true)])({
-                store: store,
+                store: this.store,
                 query: this.getFilter(),
                 columns: {
                     col1: selector({

+ 1 - 0
esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html

@@ -14,6 +14,7 @@
                     <span data-dojo-type="dijit.ToolbarSeparator"></span>
                     <div id="${id}Filter" data-dojo-type="FilterDropDownWidget">
                         <input id="${id}Type" title="${i18n.ArchivedOnly}" name="Type" colspan="2" data-dojo-props="value:'archived workunits'" data-dojo-type="dijit.form.CheckBox" />
+                        <input id="${id}Wuid" title="${i18n.WUID}:" name="Wuid" colspan="2" data-dojo-props="trim: true, uppercase: true, placeHolder:'D20130222-171723'" data-dojo-type="dijit.form.TextBox" />
                         <input id="${id}Owner" title="${i18n.Owner}:" name="Owner" colspan="2" data-dojo-props="trim: true, placeHolder:'${i18n.JSmith}'" data-dojo-type="dijit.form.TextBox" />
                         <input id="${id}Jobname" title="${i18n.Jobname}:" name="Jobname" colspan="2" data-dojo-props="trim: true, placeHolder:'${i18n.log_analysis_1}'" data-dojo-type="dijit.form.TextBox" />
                         <input id="${id}ClusterTargetSelect" title="${i18n.Cluster}:" name="Cluster" colspan="2" data-dojo-props="trim: true, placeHolder:'${i18n.ClusterPlaceholder}'" data-dojo-type="TargetSelectWidget" />

+ 3 - 3
initfiles/bash/etc/init.d/hpcc_common.in

@@ -43,8 +43,8 @@ which_pidof(){
     PIDOF=`type --path pidof`
     if [ "${PIDOF}" == "" ]; then
         if [ -e /bin/pidof ]; then
-	    PIDOF=/bin/pidof
-	elif [ -e /sbin/pidof  ]; then
+            PIDOF=/bin/pidof
+        elif [ -e /sbin/pidof  ]; then
             PIDOF=/sbin/pidof
         elif [ -e /usr/sbin/pidof ]; then
             PIDOF=/usr/sbin/pidof
@@ -525,7 +525,7 @@ startCmd() {
     done
 
     if [ ${WAITTIME} -eq 0 ]; then
-        log_failure_msg "${compName} has timed out, but may still be starting"
+        log_timeout_msg
     fi
 
     chmod 644 ${envfile}

+ 15 - 5
initfiles/bash/etc/init.d/init-functions

@@ -169,22 +169,32 @@ log_use_fancy_output () {
 }
 
 log_success_msg () {
-    status="[  OK  ]"
+    status="[   OK    ]"
     args="$*"
     if [ "$args" != "" ]; then
         printf "\E[32m %s \n%s \033[0m \n" "${status}"  "$args "
     else
-        printf "\E[32m %s  \033[0m \n" "${status}" 
+        printf "\E[32m %s  \033[0m \n" "${status}"
     fi
-}   
+}
 
 log_failure_msg () {
-    status="[FAILED]"
+    status="[ FAILED  ]"
     args="$*"
     if [ "$args" != "" ]; then
         printf "\E[31m %s \n%s \033[0m \n" "${status}"  "$args "
     else
-        printf "\E[31m %s  \033[0m \n" "${status}"  
+        printf "\E[31m %s  \033[0m \n" "${status}"
+    fi
+}
+
+log_timeout_msg () {
+    status="[ TIMEOUT ]"
+    args="$*"
+    if [ "$args" != "" ]; then
+        printf "\E[33m %s \n%s \033[0m \n" "${status}" "$args "
+    else
+        printf "\E[33m %s \033[0m \n" "${status}"
     fi
 }
 

+ 2 - 0
plugins/cassandra/cassandraembed.cpp

@@ -441,6 +441,7 @@ static bool isInteger(const CassValueType t)
 {
     switch (t)
     {
+    case CASS_VALUE_TYPE_TIMESTAMP:
     case CASS_VALUE_TYPE_INT:
     case CASS_VALUE_TYPE_BIGINT:
     case CASS_VALUE_TYPE_COUNTER:
@@ -561,6 +562,7 @@ static __int64 getSignedResult(const RtlFieldInfo *field, const CassValue *value
         check(cass_value_get_int32(value, &output));
         return output;
     }
+    case CASS_VALUE_TYPE_TIMESTAMP:
     case CASS_VALUE_TYPE_BIGINT:
     case CASS_VALUE_TYPE_COUNTER:
     case CASS_VALUE_TYPE_VARINT:

+ 1 - 1
system/jlib/jfile.cpp

@@ -6025,7 +6025,7 @@ public:
     virtual void get(size32_t len, void * ptr)
     {
         if (len>buffer.remaining()) {
-            ERRLOG("CFileSerialStream::get read past end of stream.4(%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
+            ERRLOG("CMemoryBufferSerialStream::get read past end of stream.4(%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
             throw MakeStringException(-1,"CMemoryBufferSerialStream::get read past end of stream (%u,%u)",(unsigned)len,(unsigned)buffer.remaining());
         }
         const void * data = buffer.readDirect(len);

+ 30 - 11
system/jlib/jflz.cpp

@@ -616,6 +616,7 @@ class jlib_decl CFastLZCompressor : public CInterface, public ICompressor
     bool trailing;
     byte *outbuf;
     size32_t outlen;
+    size32_t wrmax;
 
     inline void setinmax()
     {
@@ -637,11 +638,13 @@ class jlib_decl CFastLZCompressor : public CInterface, public ICompressor
         if (trailing)
             return;
         size32_t toflush = (inlenblk==COMMITTED)?inlen:inlenblk;
+        if (toflush == 0)
+            return;
         assertex(outlen+sizeof(size32_t)*2+toflush+fastlzSlack(toflush)<=blksz);
         size32_t *cmpsize = (size32_t *)(outbuf+outlen);
         byte *out = (byte *)(cmpsize+1);
         *cmpsize = (size32_t)fastlz_compress(inbuf, (int)toflush, out, ht);
-        if (*cmpsize<=toflush) {
+        if (*cmpsize<toflush) {
             *(size32_t *)outbuf += toflush;
             outlen += *cmpsize+sizeof(size32_t);
             if (inlenblk==COMMITTED)
@@ -665,6 +668,7 @@ public:
         outlen = 0;
         outbuf = NULL;      // only set on close
         bufalloc = 0;
+        wrmax = 0;          // set at open
     }
 
     virtual ~CFastLZCompressor()
@@ -676,6 +680,7 @@ public:
 
     virtual void open(void *buf,size32_t max)
     {
+        wrmax = max;
         if (buf) {
             if (bufalloc) {
                 free(outbuf);
@@ -723,16 +728,30 @@ public:
 
     size32_t write(const void *buf,size32_t len)
     {
-        if (len+inlen>inmax) {
-            if (trailing)
-                return 0;
-            flushcommitted();
-            if (len+inlen>inmax) 
-                len = inmax-inlen;
+        // no more than wrmax per write
+        size32_t lenb = wrmax;
+        byte *b = (byte *)buf;
+        size32_t written = 0;
+        while (len)
+        {
+            if (len < lenb)
+                lenb = len;
+            if (lenb+inlen>inmax) {
+                if (trailing)
+                    return written;
+                flushcommitted();
+                if (lenb+inlen>inmax)
+                    lenb = inmax-inlen;
+            }
+            if (lenb == 0)
+                return written;
+            memcpy(inbuf+inlen,b,lenb);
+            b += lenb;
+            inlen += lenb;
+            len -= lenb;
+            written += lenb;
         }
-        memcpy(inbuf+inlen,buf,len);
-        inlen += len;
-        return len;
+        return written;
     }
 
     void *  bufptr() 
@@ -791,7 +810,7 @@ public:
     }
 
     virtual void expand(void *buf)
-{
+    {
         if (!outlen)
             return;
         if (buf) {

+ 2 - 2
tools/copyexp/copyexp.cpp

@@ -70,7 +70,7 @@ static const char *formatTime(unsigned t,StringBuffer &str)
     str.clear();
     if (t>100000)
         str.appendf("%ds",t/1000);
-    else if (t>100000)
+    else
         str.appendf("%dms",t);
     return str.str();
 
@@ -99,7 +99,7 @@ static void printStats(offset_t filesize,unsigned start,unsigned startu)
     if (elapsed<1000)
         printf("%" I64F "d bytes copied, at %.2f MB/s in %s\n",filesize,((((double)filesize)/(1024*1024))/elapsedu)*1000000,formatTimeU(elapsedu,tmp));
     else
-        printf("%" I64F "d bytes copied, at %.2f MB/s in %s\n",filesize,((((double)filesize)/(1024*1024))/elapsed)*1000,formatTime(elapsed*1000,tmp));
+        printf("%" I64F "d bytes copied, at %.2f MB/s in %s\n",filesize,((((double)filesize)/(1024*1024))/elapsed)*1000,formatTime(elapsed,tmp));
 }
 
 int copyExpanded(const char *from, const char *to, bool stats)