chiark / gitweb /
fixes for cdb; it now works at least a bit
authorian <ian>
Thu, 30 Mar 2006 14:13:46 +0000 (14:13 +0000)
committerian <ian>
Thu, 30 Mar 2006 14:13:46 +0000 (14:13 +0000)
base/chiark-tcl.h
base/scriptinv.c
cdb/readonly.c
cdb/writeable.c

index f3f0922bdc1320708687bc25c23f3015f6b57979..91d76fd7cd7e907f9b51dfa1c6652f8c64bd9c5d 100644 (file)
@@ -35,21 +35,26 @@ int cht_pat_enum(Tcl_Interp*, Tcl_Obj*, const void**,
 
 /* from scriptinv.c */
 
-typedef struct { /* semi-opaque - read only, and then only where commented */
-  Tcl_Interp *ip; /* valid, non-0 and useable if set */
-  Tcl_Obj *obj; /* non-0 iff set (but only test for 0/non-0) */
-  Tcl_Obj *xargs;
-  int llength; /* after set, is llength of script + xargs */
+typedef struct { /* opaque; comments are for scriptinv.c impl'n only */
+  /* states:                Cancelled       Set                          */
+  Tcl_Interp *ipq;     /*    0               valid, non-0, useable       */
+  Tcl_Obj *script;     /*    0               valid, non-0                */
+  Tcl_Obj *xargs;      /*    0               valid, may be 0             */
+  int llen;            /*    undefined       llength of script + xargs   */
 } ScriptToInvoke;
 
-void cht_scriptinv_init(ScriptToInvoke *si);
+void cht_scriptinv_init(ScriptToInvoke *si); /* undefined -> Cancelled */
 int cht_scriptinv_set(ScriptToInvoke *si, Tcl_Interp *ip,
-                 Tcl_Obj *newscript, Tcl_Obj *xargs);
-void cht_scriptinv_cancel(ScriptToInvoke *si); /* then don't invoke */
-  /* no separate free function - just cancel */
+                     Tcl_Obj *newscript, Tcl_Obj *xargs);
+  /* Cancelled/Set -> Set (newscript!=0, ok) / Cancelled (otherwise) */
+void cht_scriptinv_cancel(ScriptToInvoke *si);
+  /* Cancelled/Set -> Cancelled.  No separate free function - just cancel. */
+#define cht_scriptinv_interp(si) ((si)->ipq)
+  /* int cht_scriptinv_interp(ScriptToInvoke *si);  returns 0 if Cancelled */
 
 int cht_scriptinv_invoke_fg(ScriptToInvoke *si, int argc,
                            Tcl_Obj *const *argv);
+  /* is a no-op if Cancelled rather than Set */
   /* if script fails, returns that error */
 
 void cht_scriptinv_invoke(ScriptToInvoke *si, int argc, Tcl_Obj *const *argv);
index 644459a0f0ec8ebd261ea39c10d399df2509dcc3..a066572c2873ecc3edff403574c8f6cc2a89f350 100644 (file)
@@ -4,13 +4,15 @@
 #include "chiark-tcl-base.h"
 
 void cht_scriptinv_init(ScriptToInvoke *si) {
-  si->obj= 0;
+  si->ipq= 0;
+  si->script= 0;
   si->xargs= 0;
 }
 
 void cht_scriptinv_cancel(ScriptToInvoke *si) {
-  if (si->obj) { Tcl_DecrRefCount(si->obj); si->obj= 0; }
+  if (si->script) { Tcl_DecrRefCount(si->script); si->script= 0; }
   if (si->xargs) { Tcl_DecrRefCount(si->xargs); si->xargs= 0; }
+  si->ipq= 0;
 }
 
 int cht_scriptinv_set(ScriptToInvoke *si, Tcl_Interp *ip,
@@ -18,19 +20,20 @@ int cht_scriptinv_set(ScriptToInvoke *si, Tcl_Interp *ip,
   int rc, xlength;
   
   cht_scriptinv_cancel(si);
+  if (!newscript) return 0;
 
-  rc= Tcl_ListObjLength(ip, newscript, &si->llength);  if (rc) return rc;
+  rc= Tcl_ListObjLength(ip, newscript, &si->llen);  if (rc) return rc;
   Tcl_IncrRefCount(newscript);
 
   if (xargs) {
     rc= Tcl_ListObjLength(ip, xargs, &xlength);  if (rc) return rc;
     Tcl_IncrRefCount(xargs);
-    si->llength += xlength;
+    si->llen += xlength;
   }
 
-  si->obj= newscript;
+  si->script= newscript;
   si->xargs= xargs;
-  si->ip= ip;
+  si->ipq= ip;
   return 0;
 }  
   
@@ -39,21 +42,22 @@ int cht_scriptinv_invoke_fg(ScriptToInvoke *si, int argc,
   Tcl_Obj *invoke=0;
   int i, rc;
 
-  assert(si->obj);
+  if (!si->ipq) return TCL_OK;
+
   for (i=0; i<argc; i++) Tcl_IncrRefCount(argv[i]);
 
-  invoke= Tcl_DuplicateObj(si->obj);
+  invoke= Tcl_DuplicateObj(si->script);
   Tcl_IncrRefCount(invoke);
 
   if (si->xargs) {
-    rc= Tcl_ListObjAppendList(si->ip, invoke, si->xargs);
+    rc= Tcl_ListObjAppendList(si->ipq, invoke, si->xargs);
     if (rc) goto x_rc;
   }
 
-  rc= Tcl_ListObjReplace(si->ip, invoke,si->llength,0, argc,argv);
+  rc= Tcl_ListObjReplace(si->ipq, invoke,si->llen,0, argc,argv);
   if (rc) goto x_rc;
 
-  rc= Tcl_EvalObjEx(si->ip,invoke,TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT);
+  rc= Tcl_EvalObjEx(si->ipq, invoke, TCL_EVAL_GLOBAL|TCL_EVAL_DIRECT);
   if (rc) goto x_rc;
 
   rc= 0;
@@ -67,5 +71,5 @@ x_rc:
 void cht_scriptinv_invoke(ScriptToInvoke *si, int argc, Tcl_Obj *const *argv) {
   int rc;
   rc= cht_scriptinv_invoke_fg(si, argc, argv);
-  if (rc) Tcl_BackgroundError(si->ip);
+  if (rc) Tcl_BackgroundError(si->ipq);
 }  
index a084fc8d2d5ef62c01c62f40b148ecae9b140dfd..4cf8e2218b1a38dfb68e31fb49aa72650f910de7 100644 (file)
@@ -19,6 +19,7 @@ int cht_do_cdb_open(ClientData cd, Tcl_Interp *ip,
   int rc, r;
 
   ro= TALLOC(sizeof(*ro));
+  ro->ix= -1;
   ro->fd= open(path, O_RDONLY);
   if (ro->fd<0) PE("open database file");
   r= cdb_init(&ro->cdb, ro->fd);
index cf9a65e11dfe0319068000d145c0f7f5bb501064..a73be821a001a894b0114920a06e4b045f1ab985 100644 (file)
@@ -165,11 +165,13 @@ static int rw_close(Tcl_Interp *ip, Rw *rw) {
   }
 
   pathbuf_free(&rw->pbsome); pathbuf_free(&rw->pbother);
-  TFREE(rw);
   return rc;
 }
 
-static void destroy_cdbrw_idtabcb(Tcl_Interp *ip, void *rw) { rw_close(0,rw); }
+static void destroy_cdbrw_idtabcb(Tcl_Interp *ip, void *rw) {
+  rw_close(0,rw);
+  TFREE(rw);
+}
 const IdDataSpec cdbtcl_rwdatabases= {
   "cdb-rwdb", "cdb-openrwdatabases-table", destroy_cdbrw_idtabcb
 };
@@ -254,7 +256,7 @@ static int readstorelogrecord(FILE *f, HashTable *ht,
   int c, rc, r;
 
   c= getc(f);
-  if (c==EOF) { if (feof(f)) return -1; return -2; }
+  if (c==EOF) { return feof(f) ? -1 : -2; }
   if (c=='\n') return -3;
   if (c!='+') return -2;
 
@@ -275,6 +277,8 @@ static int readstorelogrecord(FILE *f, HashTable *ht,
   r= fread(htv_fillptr(val), 1,vallen, f);
   if (r!=vallen) goto x2_free_keyval;
 
+  c= getc(f);  if (c!='\n') goto x2_free_keyval;
+
   rc= omitfn ? omitfn(val, ctx) : TCL_OK;
   if (rc) { assert(rc>0); TFREE(val); }
   else updatefn(ht, key, val);
@@ -297,6 +301,9 @@ static int writerecord(FILE *f, const char *key, const HashValue *val) {
   r= fwrite(val->data, 1, val->len, f);
   if (r != val->len) return -1;
 
+  r= putc('\n', f);
+  if (r==EOF) return -1;
+
   return 0;
 }
 
@@ -304,32 +311,45 @@ static int writerecord(FILE *f, const char *key, const HashValue *val) {
 
 int cht_do_cdbwr_create_empty(ClientData cd, Tcl_Interp *ip,
                              const char *pathb) {
-  static const char *const toremoves[]= {
-    ".main", ".cdb", ".log", ".tmp", 0
-  };
+  static const char *const toremoves[]= { ".cdb", ".log", ".tmp", 0 };
 
-  Pathbuf pb;
-  int lock_fd=-1, fd=-1, rc, r;
+  Pathbuf pb, pbmain;
+  int lock_fd=-1, rc, r;
+  FILE *f= 0;
   const char *const *toremove;
+  struct stat stab;
 
   pathbuf_init(&pb, pathb);
+  pathbuf_init(&pbmain, pathb);
+
   rc= acquire_lock(ip, &pb, &lock_fd);  if (rc) goto x_rc;
-  
-  fd= open(pathbuf_sfx(&pb, ".main"), O_RDWR|O_CREAT|O_EXCL, 0666);
-  if (fd <= 0) PE("create new database file");
+
+  r= lstat(pathbuf_sfx(&pbmain, ".main"), &stab);
+  if (!r) { rc= cht_staticerr(ip, "database already exists during creation",
+                             "CDB ALREADY-EXISTS");  goto x_rc; }
+  if (errno != ENOENT) PE("check for existing database .main during creation");
 
   for (toremove=toremoves; *toremove; toremove++) {
-    r= remove(*toremove);
+    r= remove(pathbuf_sfx(&pb, *toremove));
     if (r && errno != ENOENT)
       PE("delete possible spurious file during creation");
   }
   
+  f= fopen(pathbuf_sfx(&pb, ".tmp"), "w");
+  if (!f) PE("create new database .tmp");  
+  r= putc('\n', f);  if (r==EOF) PE("write sentinel to new database .tmp");
+  r= fclose(f);  f=0;  if (r) PE("close new database .tmp during creation");
+
+  r= rename(pb.buf, pbmain.buf);
+  if (r) PE("install new database .tmp as .main (finalising creation)");
+  
   rc= TCL_OK;
 
  x_rc:
-  maybe_close(fd);
+  if (f) fclose(f);
   maybe_close(lock_fd);
   pathbuf_free(&pb);
+  pathbuf_free(&pbmain);
   return rc;
 }
 
@@ -395,6 +415,7 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
   off_t logrecstart, logjunkpos;
 
   rw= TALLOC(sizeof(*rw));
+  rw->ix= -1;
   ht_setup(&rw->logincore);
   cht_scriptinv_init(&rw->on_info);
   cht_scriptinv_init(&rw->on_lexminval);
@@ -403,12 +424,11 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
   pathbuf_init(&rw->pbother, pathb);
   rw->autocompact= 1;
 
-  if (on_lexminval) {
-    rc= cht_scriptinv_set(&rw->on_lexminval, ip, on_lexminval, 0);
-    if (rc) goto x_rc;
-  } else {
-    rw->on_lexminval.llength= 0;
-  }
+  rc= cht_scriptinv_set(&rw->on_info, ip, on_info, 0);
+  if (rc) goto x_rc;
+
+  rc= cht_scriptinv_set(&rw->on_lexminval, ip, on_lexminval, 0);
+  if (rc) goto x_rc;
 
   mainfd= open(pathbuf_sfx(&rw->pbsome,".main"), O_RDONLY);
   if (mainfd<0) PE("open existing database file .main");
@@ -421,8 +441,8 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
   if (rw->cdb_fd >=0) {
     rc= cdbinit(ip, rw);  if (rc) goto x_rc;
   } else if (errno == ENOENT) {
-    if (rw->mainsz) {
-      rc= cht_staticerr(ip, ".cdb does not exist but .main is nonempty -"
+    if (rw->mainsz > 1) {
+      rc= cht_staticerr(ip, ".cdb does not exist but .main is >1byte -"
                        " .cdb must have been accidentally deleted!",
                        "CDB CDBMISSING");
       goto x_rc;
@@ -493,6 +513,7 @@ int cht_do_cdbwr_open(ClientData cd, Tcl_Interp *ip, const char *pathb,
 
  x_rc:
   rw_close(0,rw);
+  TFREE(rw);
   maybe_close(mainfd);
   return rc;
 }
@@ -508,16 +529,16 @@ int cht_do_cdbwr_open_okjunk(ClientData cd, Tcl_Interp *ip, const char *pathb,
 struct ht_forall_ctx {
   struct cdb_make cdbm;
   FILE *mainfile;
-  int lexminvall;
   long *reccount;
-  const char *lexminval;
+  int lexminvall;
+  const char *lexminval; /* may be invalid if lexminvall <= 0 */
 };
 
 /*---------- helper functions ----------*/
 
 static int expiredp(const HashValue *val, struct ht_forall_ctx *a) {
   int r, l;
-  if (!val->len) return 0;
+  if (!val->len || a->lexminvall<=0) return 0;
   l= val->len < a->lexminvall ? val->len : a->lexminvall;
   r= memcmp(val->data, a->lexminval, l);
   if (r>0) return 0;
@@ -577,7 +598,7 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
             logsz, (unsigned long)rw->mainsz);
   if (rc) goto x_rc;
 
-  if (rw->on_lexminval.llength) {
+  if (cht_scriptinv_interp(&rw->on_lexminval)) {
     rc= cht_scriptinv_invoke_fg(&rw->on_lexminval, 0,0);
     if (rc) goto x_rc;
 
@@ -591,7 +612,7 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
     rc= ht_forall(&rw->logincore, delete_ifexpired, &a);
 
   } else {
-    a.lexminval= "";
+    a.lexminvall= 0;
   }
 
   /* merge unsuperseded records from main into hash table */
@@ -604,19 +625,23 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
                          expiredp, &a,
                          ht_maybeupdate);
     if (ferror(a.mainfile)) { rc= cht_posixerr(ip, errno, "error reading"
-                         " .main during compact"); goto x_rc;
-    }
+                         " .main during compact"); goto x_rc; }
     if (r==-3) {
       break;
     } else if (r==-1 || r==-2) {
       errpos= ftello(a.mainfile);
       if (errpos<0) PE("ftello .main during report of syntax error");
-      snprintf(buf,sizeof(buf), "CDB SYNTAX MAIN %lu", (unsigned long)errpos);
+      snprintf(buf,sizeof(buf), "CDB %s MAIN %lu",
+              r==-1 ? "TRUNCATED" : "SYNTAX", (unsigned long)errpos);
       Tcl_SetObjErrorCode(ip, Tcl_NewStringObj(buf,-1));
       snprintf(buf,sizeof(buf), "%lu", (unsigned long)errpos);
       Tcl_ResetResult(ip);
-      Tcl_AppendResult(ip, "syntax error in .main during"
-                      " compact, at file position ", buf, (char*)0);
+      Tcl_AppendResult(ip,
+                      r==-1 ? "unexpected eof (truncated file)"
+                      " in .main during compact, at file position "
+                      : "syntax error"
+                      " in .main during compact, at file position ",
+                      buf, (char*)0);
       rc= TCL_ERROR;
       goto x_rc;
     } else {
@@ -657,6 +682,9 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
   r= ht_forall(&rw->logincore, addto_main, &a);
   if (r) { rc= cht_posixerr(ip, r, "error writing to new .main"
                            " during compact");  goto x_rc; }
+
+  r= putc('\n', a.mainfile);
+  if (r==EOF) PE("write trailing \n to main during compact");
   
   r= fflush(a.mainfile);  if (r) PE("fflush new main during compact");
   r= fdatasync(fileno(a.mainfile));
@@ -675,7 +703,7 @@ static int compact_core(Tcl_Interp *ip, Rw *rw, unsigned long logsz,
 
   /* done! */
   
-  rc= infocb(ip, rw, "compact-end", "main=%luby nrecs=%l",
+  rc= infocb(ip, rw, "compact-end", "main=%luby nrecs=%ld",
             (unsigned long)rw->mainsz, *a.reccount);
   if (rc) goto x_rc;
 
@@ -735,6 +763,7 @@ int cht_do_cdbwr_close(ClientData cd, Tcl_Interp *ip, void *rw_v) {
   if (rc_close) rc= rc_close;
   
   cht_tabledataid_disposing(ip, rw_v, &cdbtcl_rwdatabases);
+  TFREE(rw);
   return rc;
 }
 
@@ -749,7 +778,7 @@ static int compact_keepopen(Tcl_Interp *ip, Rw *rw, int force) {
   if (logsz < 0) return cht_posixerr(ip, errno, "ftell .log"
                                       " during compact check or force");
 
-  if (!force && logsz < rw->mainsz / 10 + 1000) return TCL_OK;
+  if (!force && logsz < rw->mainsz / 2 + 1000) return TCL_OK;
 
   rc= compact_core(ip, rw, logsz, &reccount);  if (rc) goto x_rc;
 
@@ -814,6 +843,8 @@ static int update(Tcl_Interp *ip, Rw *rw, const char *key,
   if (r) PE("write update to logfile");
 
   ht_update(&rw->logincore, key, val);
+
+  if (!rw->autocompact) return TCL_OK;
   return compact_keepopen(ip, rw, 0);
 
  x_rc: