]> git.ipfire.org Git - thirdparty/sqlite.git/commitdiff
Expand "sqlite3_vfs*" JS-to-WASM function argument conversions to accept VFS names...
authorstephan <stephan@noemail.net>
Thu, 1 Dec 2022 03:55:28 +0000 (03:55 +0000)
committerstephan <stephan@noemail.net>
Thu, 1 Dec 2022 03:55:28 +0000 (03:55 +0000)
FossilOrigin-Name: e1009b16d351b23676ad7bffab0c91b373a92132eb855c9af61991b50cd237ed

ext/wasm/api/sqlite3-api-glue.js
ext/wasm/api/sqlite3-api-prologue.js
ext/wasm/common/whwasmutil.js
ext/wasm/tester1.c-pp.js
manifest
manifest.uuid

index 6c1fcae8208d8adb710d56df0d3f3c1b6e20e0db..aa0d48af56d1c8258911cb90375b41f3eebef431 100644 (file)
@@ -73,8 +73,27 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
     ('sqlite3_stmt*', aPtr)
     ('sqlite3_context*', aPtr)
     ('sqlite3_value*', aPtr)
-    ('sqlite3_vfs*', aPtr)
-    ('void*', aPtr);
+    ('void*', aPtr)
+    /**
+       `sqlite3_vfs*`:
+
+       - v is-a string: use the result of sqlite3_vfs_find(v) but
+         throw if it returns 0.
+       - v is-a capi.sqlite3_vfs: use v.pointer.
+       - Else return the same as the `'*'` argument conversion.
+    */
+    ('sqlite3_vfs*', (v)=>{
+      if('string'===typeof v){
+        const x = capi.sqlite3_vfs_find(v);
+        /* A NULL sqlite3_vfs pointer will be treated as the default
+           VFS in many contexts. We specifically do not want that
+           behavior here. */
+        if(!x) sqlite3.SQLite3Error.toss("Unknown sqlite3_vfs name:",v);
+        return x;
+      }else if(v instanceof sqlite3.capi.sqlite3_vfs) v = v.pointer;
+      return aPtr(v);
+    });
+
     wasm.xWrap.resultAdapter('sqlite3*', aPtr)
     ('sqlite3_context*', aPtr)
     ('sqlite3_stmt*', aPtr)
@@ -588,8 +607,8 @@ self.sqlite3ApiBootstrap.initializers.push(function(sqlite3){
                     'version'
                    ]){
       for(const e of Object.entries(wasm.ctype[t])){
-        // ^^^ [k,v] there triggers a buggy code transormation via one
-        // of the Emscripten-driven optimizers.
+        // ^^^ [k,v] there triggers a buggy code transformation via
+        // one of the Emscripten-driven optimizers.
         capi[e[0]] = e[1];
       }
     }
index 31cd8aa539f49a3bcfa177e8263730087d70e111..0321472423c5aaf306aa27602d9f0e85f4faf2dd 100644 (file)
@@ -185,7 +185,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     constructor(...args){
       if(1===args.length && __isInt(args[0])){
         super(__rcStr(args[0]));
-      }else if(2===args.length && 'object'===typeof args){
+      }else if(2===args.length && 'object'===typeof args[1]){
         if(__isInt(args[0])) super(__rcStr(args[0]), args[1]);
         else super(...args);
       }else{
@@ -354,7 +354,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
        message.
     */
     constructor(...args){
-      if(2===args.length && 'object'===typeof args){
+      if(2===args.length && 'object'===typeof args[1]){
         super(...args);
       }else if(args.length){
         super(args.join(' '));
@@ -748,7 +748,7 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
 
   /**
      Reports info about compile-time options using
-     sqlite_compileoption_get() and sqlite3_compileoption_used(). It
+     sqlite3_compileoption_get() and sqlite3_compileoption_used(). It
      has several distinct uses:
 
      If optName is an array then it is expected to be a list of
@@ -973,7 +973,6 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
     ["sqlite3_wasm_vfs_unlink", "int", "sqlite3_vfs*","string"]
   ];
 
-
   /**
      sqlite3.wasm.pstack (pseudo-stack) holds a special-case
      stack-style allocator intended only for use with _small_ data of
@@ -1327,6 +1326,76 @@ self.sqlite3ApiBootstrap = function sqlite3ApiBootstrap(
                                   "bytes for sqlite3_aggregate_context()")
           : 0);
   };
+
+  /**
+     Creates a file using the storage appropriate for the given
+     sqlite3_vfs.  The first argument may be a VFS name (JS string
+     only, NOT a WASM C-string), WASM-managed `sqlite3_vfs*`, or
+     a capi.sqlite3_vfs instance. Pass 0 (a NULL pointer) to use the
+     default VFS. If passed a string which does not resolve using
+     sqlite3_vfs_find(), an exception is thrown. (Note that a WASM
+     C-string is not accepted because it is impossible to
+     distinguish from a C-level `sqlite3_vfs*`.)
+
+     The second argument, the filename, must be a JS or WASM C-string.
+
+     The 3rd may either be falsy, a valid WASM memory pointer, or a
+     Uint8Array. The 4th must be the length, in bytes, of the data
+     array to copy. If the 3rd argument is a Uint8Array and the 4th is
+     not a positive integer then the 4th defaults to the array's
+     byteLength value.
+
+     If data is falsy then a file is created with dataLen bytes filled
+     with uninitialized data (whatever truncate() leaves there). If
+     data is not falsy then a file is created or truncated and it is
+     filled with the first dataLen bytes of the data source.
+
+     Throws if any arguments are invalid or if creating or writing to
+     the file fails.
+
+     Note that most VFSes do _not_ automatically create directory
+     parts of filenames, nor do all VFSes have a concept of
+     directories.  If the given filename is not valid for the given
+     VFS, an exception will be thrown. This function exists primarily
+     to assist in implementing file-upload capability, with the caveat
+     that clients must have some idea of the VFS into which they want
+     to upload and that VFS must support the operation.
+
+     VFS-specific notes:
+
+     - "memdb" and "kvvfs": results are undefined.
+
+     - "unix" and related: will use the WASM build's equivalent of the
+       POSIX I/O APIs.
+
+     - "opfs": if available, uses OPFS storage and _does_ create
+       directory parts of the filename.
+  */
+  capi.sqlite3_js_vfs_create_file = function(vfs, filename, data, dataLen){
+    let pData;
+    if(!data) pData = 0;
+    else if(wasm.isPtr(data)){
+      pData = data;
+    }else if(data instanceof Uint8Array){
+      pData = wasm.allocFromTypedArray(data);
+      if(arguments.length<4 || !util.isInt32(dataLen) || dataLen<0){
+        dataLen = data.byteLength;
+      }
+    }else{
+      SQLite3Error.toss("Invalid 3rd argument type for sqlite3_js_vfs_create_file().");
+    }
+    if(!util.isInt32(dataLen) || dataLen<0){
+      wasm.dealloc(pData);
+      SQLite3Error.toss("Invalid 4th argument for sqlite3_js_vfs_create_file().");
+    }
+    try{
+      const rc = wasm.sqlite3_wasm_vfs_create_file(vfs, filename, pData, dataLen);
+      if(rc) SQLite3Error.toss("Creation of file failed with sqlite3 result code",
+                               capi.sqlite3_js_rc_str(rc));
+    }finally{
+      wasm.dealloc(pData);
+    }
+  };
   
   if( util.isUIThread() ){
     /* Features specific to the main window thread... */
index 7e5e7981f7dcbfcb0b72d84362adf5be26608f21..0190433773c5d178c677aafcc6708e8f0833184c 100644 (file)
@@ -1313,7 +1313,7 @@ self.WhWasmUtilInstaller = function(target){
 
   const __xResultAdapterCheck =
         (t)=>xcv.result[t] || toss("Result adapter not found:",t);
-  
+
   cache.xWrap.convertArg = (t,v)=>__xArgAdapterCheck(t)(v);
   cache.xWrap.convertResult =
     (t,v)=>(null===t ? v : (t ? __xResultAdapterCheck(t)(v) : undefined));
@@ -1442,7 +1442,7 @@ self.WhWasmUtilInstaller = function(target){
      exception.
 
      Clients may map their own result and argument adapters using
-     xWrap.resultAdapter() and xWrap.argAdaptor(), noting that not all
+     xWrap.resultAdapter() and xWrap.argAdapter(), noting that not all
      type conversions are valid for both arguments _and_ result types
      as they often have different memory ownership requirements.
 
@@ -1497,7 +1497,7 @@ self.WhWasmUtilInstaller = function(target){
     };
   }/*xWrap()*/;
 
-  /** Internal impl for xWrap.resultAdapter() and argAdaptor(). */
+  /** Internal impl for xWrap.resultAdapter() and argAdapter(). */
   const __xAdapter = function(func, argc, typeName, adapter, modeName, xcvPart){
     if('string'===typeof typeName){
       if(1===argc) return xcvPart[typeName];
@@ -1575,7 +1575,7 @@ self.WhWasmUtilInstaller = function(target){
   */
   target.xWrap.argAdapter = function f(typeName, adapter){
     return __xAdapter(f, arguments.length, typeName, adapter,
-                      'argAdaptor()', xcv.arg);
+                      'argAdapter()', xcv.arg);
   };
 
   /**
@@ -1601,6 +1601,33 @@ self.WhWasmUtilInstaller = function(target){
     return target.xWrap(fname, resultType, argTypes||[]).apply(null, args||[]);
   };
 
+  /**
+     This function is ONLY exposed in the public API to facilitate
+     testing. It should not be used in application-level code, only
+     in test code.
+
+     Expects to be given (typeName, value) and returns a conversion
+     of that value as has been registered using argAdapter().
+     It throws if no adapter is found.
+
+     ACHTUNG: the adapter may require that a scopedAllocPush() is
+     active and it may allocate memory within that scope.
+  */
+  target.xWrap.testConvertArg = cache.xWrap.convertArg;
+  /**
+     This function is ONLY exposed in the public API to facilitate
+     testing. It should not be used in application-level code, only
+     in test code.
+
+     Expects to be given (typeName, value) and returns a conversion
+     of that value as has been registered using resultAdapter().
+     It throws if no adapter is found.
+
+     ACHTUNG: the adapter may allocate memory which the caller may need
+     to know how to free.
+  */
+  target.xWrap.testConvertResult = cache.xWrap.convertResult;
+
   return target;
 };
 
index 08f5480b54de5c4d218d9bd5d9e644bf6f58c357..1278e772f95b0a689daa336f4bc746cb5657c6e7 100644 (file)
@@ -1236,8 +1236,19 @@ self.sqlite3InitModule = sqlite3InitModule;
         //log("vfsList =",vfsList);
         for(const v of vfsList){
           T.assert('string' === typeof v)
-            .assert(capi.sqlite3_vfs_find(v) > 0);
+            .assert(wasm.isPtr(
+              capi.sqlite3_vfs_find(v)
+            ));
         }
+        // While we have vfsList handy, let's verify...
+        wasm.scopedAllocCall(()=>{
+          const vfsArg = (v)=>wasm.xWrap.testConvertArg('sqlite3_vfs*',v);
+          T.assert(wasm.isPtr(vfsArg(vfsList[0])));
+          const pVfs = capi.sqlite3_vfs_find(vfsList[0]);
+          const vfs = new capi.sqlite3_vfs(pVfs);
+          T.assert(wasm.isPtr(vfsArg(vfs)));
+          vfs.dispose();
+        });
       }
       /**
          Trivia: the magic db name ":memory:" does not actually use the
@@ -1784,14 +1795,15 @@ self.sqlite3InitModule = sqlite3InitModule;
 
   ////////////////////////////////////////////////////////////////////////
   T.g('OPFS (Worker thread only and only in supported browsers)',
-      (sqlite3)=>{return !!sqlite3.opfs})
+      (sqlite3)=>!!sqlite3.opfs)
     .t({
-      name: 'OPFS sanity checks',
+      name: 'OPFS db sanity checks',
       test: async function(sqlite3){
-        const filename = 'sqlite3-tester1.db';
-        const pVfs = capi.sqlite3_vfs_find('opfs');
+        const filename = this.opfsDbFile = 'sqlite3-tester1.db';
+        const pVfs = this.opfsVfs = capi.sqlite3_vfs_find('opfs');
         T.assert(pVfs);
-        const unlink = (fn=filename)=>wasm.sqlite3_wasm_vfs_unlink(pVfs,fn);
+        const unlink = this.opfsUnlink =
+              (fn=filename)=>{wasm.sqlite3_wasm_vfs_unlink(pVfs,fn)};
         unlink();
         let db = new sqlite3.oo1.OpfsDb(filename);
         try {
@@ -1808,42 +1820,76 @@ self.sqlite3InitModule = sqlite3InitModule;
           db.close();
           unlink();
         }
+      }
+    }/*OPFS db sanity checks*/)
+    .t({
+      name: 'OPFS utility APIs and sqlite3_js_vfs_create_file()',
+      test: async function(sqlite3){
+        const filename = this.opfsDbFile;
+        const pVfs = this.opfsVfs;
+        const unlink = this.opfsUnlink;
+        T.assert(filename && pVfs && !!unlink);
+        unlink();
+        // Sanity-test sqlite3_js_vfs_create_file()...
+        /**************************************************************
+           ATTENTION CLIENT-SIDE USERS: sqlite3.opfs is NOT intended
+           for client-side use. It is only for this project's own
+           internal use. Its APIs are subject to change or removal at
+           any time.
+        ***************************************************************/
+        const opfs = sqlite3.opfs;
+        const fSize = 1379;
+        let sh;
+        try{
+          T.assert(!(await opfs.entryExists(filename)));
+          capi.sqlite3_js_vfs_create_file(
+            pVfs, filename, null, fSize
+          );
+          T.assert(await opfs.entryExists(filename));
+          let fh = await opfs.rootDirectory.getFileHandle(filename);
+          sh = await fh.createSyncAccessHandle();
+          T.assert(fSize === await sh.getSize());
+          await sh.close();
+          sh = undefined;
+          unlink();
+          T.assert(!(await opfs.entryExists(filename)));
+
+          const ba = new Uint8Array([1,2,3,4,5]);
+          capi.sqlite3_js_vfs_create_file(
+            "opfs", filename, ba
+          );
+          T.assert(await opfs.entryExists(filename));
+          fh = await opfs.rootDirectory.getFileHandle(filename);
+          sh = await fh.createSyncAccessHandle();
+          T.assert(ba.byteLength === await sh.getSize());
+          await sh.close();
+          sh = undefined;
+          unlink();
 
-        if(sqlite3.opfs){
-          // Sanity-test sqlite3_wasm_vfs_create_file()...
-          const opfs = sqlite3.opfs;
-          const fSize = 1379;
-          let sh;
-          try{
-            T.assert(!(await opfs.entryExists(filename)));
-            let rc = wasm.sqlite3_wasm_vfs_create_file(
-              pVfs, filename, null, fSize
+          T.mustThrowMatching(()=>{
+            capi.sqlite3_js_vfs_create_file(
+              "no-such-vfs", filename, ba
             );
-            T.assert(0===rc)
-              .assert(await opfs.entryExists(filename));
-            const fh = await opfs.rootDirectory.getFileHandle(filename);
-            sh = await fh.createSyncAccessHandle();
-            T.assert(fSize === await sh.getSize());
-          }finally{
-            if(sh) await sh.close();
-            unlink();
-          }
-
-          // Some sanity checks of the opfs utility functions...
-          const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
-          const aDir = testDir+'/test/dir';
-          T.assert(await opfs.mkdir(aDir), "mkdir failed")
-            .assert(await opfs.mkdir(aDir), "mkdir must pass if the dir exists")
-            .assert(!(await opfs.unlink(testDir+'/test')), "delete 1 should have failed (dir not empty)")
-            .assert((await opfs.unlink(testDir+'/test/dir')), "delete 2 failed")
-            .assert(!(await opfs.unlink(testDir+'/test/dir')),
-                    "delete 2b should have failed (dir already deleted)")
-            .assert((await opfs.unlink(testDir, true)), "delete 3 failed")
-            .assert(!(await opfs.entryExists(testDir)),
-                    "entryExists(",testDir,") should have failed");
+          }, "Unknown sqlite3_vfs name: no-such-vfs");
+        }finally{
+          if(sh) await sh.close();
+          unlink();
         }
+
+        // Some sanity checks of the opfs utility functions...
+        const testDir = '/sqlite3-opfs-'+opfs.randomFilename(12);
+        const aDir = testDir+'/test/dir';
+        T.assert(await opfs.mkdir(aDir), "mkdir failed")
+          .assert(await opfs.mkdir(aDir), "mkdir must pass if the dir exists")
+          .assert(!(await opfs.unlink(testDir+'/test')), "delete 1 should have failed (dir not empty)")
+          .assert((await opfs.unlink(testDir+'/test/dir')), "delete 2 failed")
+          .assert(!(await opfs.unlink(testDir+'/test/dir')),
+                  "delete 2b should have failed (dir already deleted)")
+          .assert((await opfs.unlink(testDir, true)), "delete 3 failed")
+          .assert(!(await opfs.entryExists(testDir)),
+                  "entryExists(",testDir,") should have failed");
       }
-    }/*OPFS sanity checks*/)
+    }/*OPFS util sanity checks*/)
   ;/* end OPFS tests */
 
   ////////////////////////////////////////////////////////////////////////
index b6571cb9ecf1242a7cdcc21106d7dfed8e7764a4..06b75100f22d30183ca47a9315918330fecf7357 100644 (file)
--- a/manifest
+++ b/manifest
@@ -1,5 +1,5 @@
-C Add\sa\stestcase()\smacro\sto\sverify\sthat\sthe\scase\sof\sa\sNOT\sNULL\serror\smessage\nhitting\sthe\sstring\slength\slimit.
-D 2022-11-30T21:18:23.655
+C Expand\s"sqlite3_vfs*"\sJS-to-WASM\sfunction\sargument\sconversions\sto\saccept\sVFS\snames\s(JS\sstrings)\sand\scapi.sqlite3_vfs\sinstances.\sImplement\ssqlite3_js_vfs_create_file()\sto\sfacilitate\screation\sof\sfile-upload\sfeatures\swhich\sstore\sthe\sfile\sin\sVFS-specific\sstorage\s(where\spossible,\se.g.\s"unix"\sand\s"opfs"\sVFSes).\sCorrect\san\sargument\stype\scheck\sin\sthe\sSQLite3Error\sand\sWasmAllocError\sconstructors.
+D 2022-12-01T03:55:28.175
 F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
 F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
 F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@@ -503,9 +503,9 @@ F ext/wasm/api/post-js-footer.js cd0a8ec768501d9bd45d325ab0442037fb0e33d1f3b4f08
 F ext/wasm/api/post-js-header.js 47b6b281f39ad59fa6e8b658308cd98ea292c286a68407b35ff3ed9cfd281a62
 F ext/wasm/api/pre-js.c-pp.js b88499dc303c21fc3f55f2c364a0f814f587b60a95784303881169f9e91c1d5f
 F ext/wasm/api/sqlite3-api-cleanup.js 680d5ccfff54459db136a49b2199d9f879c8405d9c99af1dda0cc5e7c29056f4
-F ext/wasm/api/sqlite3-api-glue.js 03c40b65530d67bb2748b7380aea5fd1534500f812b76a6b401066dcd7fc4116
+F ext/wasm/api/sqlite3-api-glue.js 5468ba750e081c55ff3c572e3ae47dd415be73907ce40117fb121f37f4d0649e
 F ext/wasm/api/sqlite3-api-oo1.js 06ad2079368e16cb9f182c18cd37bdc3932536856dff4f60582d0ca5f6c491a8
-F ext/wasm/api/sqlite3-api-prologue.js e63bcd1d1942d3313bd11e76ac1fccdd3e34ba54a48b8c8296db8dd892705dbc
+F ext/wasm/api/sqlite3-api-prologue.js 04f789bab878ea67be8b18dd4292011bbfeb78ebad79eb79c591136332bd469a
 F ext/wasm/api/sqlite3-api-worker1.js e94ba98e44afccfa482874cd9acb325883ade50ed1f9f9526beb9de1711f182f
 F ext/wasm/api/sqlite3-license-version-header.js a661182fc93fc2cf212dfd0b987f8e138a3ac98f850b1112e29b5fbdaecc87c3
 F ext/wasm/api/sqlite3-opfs-async-proxy.js 9963c78bf6e5ccb5ba28e8597851bd9d980e86803b6d341cc985e586aef10c82
@@ -521,7 +521,7 @@ F ext/wasm/c-pp.c 92285f7bce67ed7b7020b40fde8ed0982c442b63dc33df9dfd4b658d4a6c07
 F ext/wasm/common/SqliteTestUtil.js d8bf97ecb0705a2299765c8fc9e11b1a5ac7f10988bbf375a6558b7ca287067b
 F ext/wasm/common/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
 F ext/wasm/common/testing.css 35889709547d89a6109ff83b25c11bbc91d8dd43aab8722e428655ca98880a06
-F ext/wasm/common/whwasmutil.js 16a592d5c304a2d268ca1c28e08a5b029a2f3cbe10af78dbc3456cfc9e3559d1
+F ext/wasm/common/whwasmutil.js dbe625a22bf0962cde1f958f2be604d27d2f97ee1b4e6ee0f19c6480aa36aeed
 F ext/wasm/demo-123-worker.html a0b58d9caef098a626a1a1db567076fca4245e8d60ba94557ede8684350a81ed
 F ext/wasm/demo-123.html 8c70a412ce386bd3796534257935eb1e3ea5c581e5d5aea0490b8232e570a508
 F ext/wasm/demo-123.js ebae30756585bca655b4ab2553ec9236a87c23ad24fc8652115dcedb06d28df6
@@ -555,7 +555,7 @@ F ext/wasm/test-opfs-vfs.html 1f2d672f3f3fce810dfd48a8d56914aba22e45c6834e262555
 F ext/wasm/test-opfs-vfs.js 44363db07b2a20e73b0eb1808de4400ca71b703af718d0fa6d962f15e73bf2ac
 F ext/wasm/tester1-worker.html 5ef353348c37cf2e4fd0b23da562d3275523e036260b510734e9a3239ba8c987
 F ext/wasm/tester1.c-pp.html 74aa9b31c75f12490653f814b53c3dd39f40cd3f70d6a53a716f4e8587107399
-F ext/wasm/tester1.c-pp.js c39594bb1a0272a08a1544277a29c1ed920b4c4877611591e7c3185744ae2431
+F ext/wasm/tester1.c-pp.js b6a8b9d8e8b070d40c140a68072bf0aa41819fe37fb5bbb9391966a3cbc154fa
 F ext/wasm/tests/opfs/concurrency/index.html 86d8ac435074d1e7007b91105f4897f368c165e8cecb6a9aa3d81f5cf5dcbe70
 F ext/wasm/tests/opfs/concurrency/test.js bfc3d7e27b207f0827f12568986b8d516a744529550b449314f5c21c9e9faf4a
 F ext/wasm/tests/opfs/concurrency/worker.js 0a8c1a3e6ebb38aabbee24f122693f1fb29d599948915c76906681bb7da1d3d2
@@ -2065,8 +2065,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
 F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
 F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
 F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P 6ee61f8cede4998f0c838d6506b058c6b09f34b3d7f30ed296100785c93f8d00
-R ca2f65a200c7997e977e891739f47e7d
-U drh
-Z 02ee85efaa582407b1d21e8a02ad86a6
+P 91f50964c10fb12d889bda7d597d8edf475d97d2d8b534b4400e0fed1d753c6a
+R e4bc31bd87c945bd19fac07fc1661145
+U stephan
+Z a323b013b8116acefa1b2ce6dcb45815
 # Remove this line to create a well-formed Fossil manifest.
index 46eb4d34cb02d84fc24fb5fd0713a9b7c222d21b..55e974a247dbcac7bb5450fef94a8a2c06e0e119 100644 (file)
@@ -1 +1 @@
-91f50964c10fb12d889bda7d597d8edf475d97d2d8b534b4400e0fed1d753c6a
\ No newline at end of file
+e1009b16d351b23676ad7bffab0c91b373a92132eb855c9af61991b50cd237ed
\ No newline at end of file