patch for new autobackup (autosave) feature

  • From: Tommy Pettersson <ptp@xxxxxxxxxxxxxx>
  • To: wilyfans@xxxxxxxxxxxxx
  • Date: Tue, 19 Oct 2004 01:40:24 +0200

Here comes a working implementation of autosave, or autobackup
as I happen to call it now.

When I was done I realized that on normal "emergency" backup,
the autosave file was removed and a backup file was created
instead.  The autosave is a superset of backup (sort of).  So I
renamed it to autobackup and made it replace the old backup.

It needs the bugfix for the etimer events to work!

There are still little things to do, so I encourage those
who are interested in autobackup to test it and tell me what
_they_ think these little things are.  :-)

(Much of the old backup code is unchanged.)
Here is a short help: 

Autobackups are stored in number-named files in $WILYBAK
or $HOME/.wilybak (same as before).

Every $WILYBAKTIME seconds (default 30) backups are created
or updated as needed.

As soon as a backup becomes unneeded, it is removed.

Backups that are still needed when a window is closed with
Del, are updated and left in the backup directory, and the
usual backup message is printed to the user.  (Things look
just like before.)

Backups that are still needed when wily crashes are also left.  :-)

The guide file is different.  The old lines with the names
of the backuped files are stored in single <num>_hdr files
together with the backup files, and the guide file contains
"<cat *_hdr" to create a fresh listing of these lines.
It would be messy to update them in place.  If you have an
old guide file in wilybak, add this command so you can update
it with new backups.


-- 
Tommy Pettersson <ptp@xxxxxxxxxxxxxx>



(patch against wily-9libs 0.13.41)

diff -rN -u diff-old/wily-9libs/wily/NOTES diff-new/wily-9libs/wily/NOTES
--- diff-old/wily-9libs/wily/NOTES      Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/NOTES      Tue Oct 19 00:00:43 2004
@@ -46,6 +46,22 @@
 has been frclear'ed, and you should not attempt to update the
 frame.
 
+TIMER EVENTS
+
+The main loop keeps a timer (gtimer) to wake it up even if
+there is no user activity, when it is time to autobackup.
+The timer event should better not be received by any eread()
+call outside the main loop.  Therefore we would stop and
+restart the timer on every turn in the main loop.  But that
+is very inefficient.  Therefore we only restart the timer
+in the main loop, if it has been stopped.  Every function
+that it going to receive timer events on its own, must first
+call suspend_gtimer().  If they do not, and try to start
+a new timer, libXg will complain at runtime.  If they, for
+some strange reason, asks for timer events without starting a
+new timer, autobackup will be delayed for an uncertain time,
+maybe infinite.
+
 NAMES
 
 A file name may be:
diff -rN -u diff-old/wily-9libs/wily/data.c diff-new/wily-9libs/wily/data.c
--- diff-old/wily-9libs/wily/data.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/data.c     Tue Oct 19 00:00:43 2004
@@ -87,6 +87,8 @@
        
        if (!strcmp(d->label,label) || !statcmp(&buf, &d->stat)) {
                if(!d->names) {
+                       data_rmbackup(d);
+                       undo_bmark(d->t);
                        tag_rmtool(d->tag, "Put");
                        undo_mark(d->t);
                }
@@ -112,6 +114,12 @@
        if(data_backup(d))
                return 1;
        
+       if (NEEDSBACKUP(d)) {
+               /* inform the user that a backup of the unsaved data is left */
+               errno = 0;
+               diag(d->backupname, "backup %s %s", mybasename(d->backupname), 
d->backupto);
+       }
+
        data_senddestroy(d);
        data_listremove(d);
        dirnames_free(d->names);
@@ -124,18 +132,18 @@
 /* Backup 'd' if necessary.  Return 0 for success. */
 int
 data_backup(Data *d) {
-       Path    fname;
-
-       if (!NEEDSBACKUP(d))
+       if (!NEEDSBUPDATE(d))
                return 0;
 
-       if( (backup_name(d->backupto, fname)) )
-               return -1;
-       if(text_write(d->t, fname))
-               return -1;
+       if (!d->backupname)
+               if ( !(d->backupname = backup_name(d->backupto)) )
+                       return -1;
 
-       errno = 0;
-       diag(fname, "backup %s %s", mybasename(fname), d->backupto);
+       if (text_write(d->t, d->backupname)) {
+               data_rmbackup(d);
+               return -1;
+       }
+       undo_bmark(d->t);
        return 0;
 }
 
@@ -165,3 +173,16 @@
        return d;
 }
 
+void
+data_rmbackup(Data *d) {
+       Path    fname;
+
+       if (!d->backupname)
+               return;
+       strcpy(fname, d->backupname);
+       (void) unlink (fname);
+       strcat(fname, "_hdr");
+       (void) unlink (fname);
+       free(d->backupname);
+       d->backupname = 0;
+}
diff -rN -u diff-old/wily-9libs/wily/data.h diff-new/wily-9libs/wily/data.h
--- diff-old/wily-9libs/wily/data.h     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/data.h     Tue Oct 19 00:00:43 2004
@@ -65,6 +65,7 @@
        Bool            has_stat;
        Stat            stat;
        char            *backupto;
+       char            *backupname;
 
        /* for object connected to some external process */
        int             fd;
@@ -76,9 +77,11 @@
 };
 
 /* A data object needs to be backed up if there is some backup file associated
- * with it, and it's Text isn't clean
+ * with it, and it's Text isn't clean.  A backup needs to be updated if the 
Text
+ * has changed since the latest backup.
  */
 #define NEEDSBACKUP(d) (d->backupto && !undo_atmark(d->t))
+#define NEEDSBUPDATE(d) (NEEDSBACKUP(d) && !undo_atbmark(d->t))
 
 void           data_setbackup(Data *, char*);
 Data * data_findid(Id );
diff -rN -u diff-old/wily-9libs/wily/file.c diff-new/wily-9libs/wily/file.c
--- diff-old/wily-9libs/wily/file.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/file.c     Tue Oct 19 00:00:43 2004
@@ -93,7 +93,7 @@
 
        undo_start(d->t);
        if(!failed)
-               undo_mark(d->t);
+               undo_mark(d->t), undo_bmark(d->t);
        
        cursorswitch(cursor);
        return failed;
@@ -114,6 +114,7 @@
        text_replace(d->t, text_all(d->t), rstring(0,0));
        undo_start(d->t);
        undo_mark(d->t);
+       undo_bmark(d->t);
        
        /* d->tag */
        tag_setlabel(d->tag, d->label);
@@ -219,6 +220,7 @@
        d->tag = text_alloc(d, false);
        d->names = 0;
        d->backupto = 0;
+       d->backupname = 0;
        d->fd = 0;
        d->emask = 0;
        d->has_stat = false;
diff -rN -u diff-old/wily-9libs/wily/mouse.c diff-new/wily-9libs/wily/mouse.c
--- diff-old/wily-9libs/wily/mouse.c    Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/mouse.c    Tue Oct 19 00:00:43 2004
@@ -176,6 +176,7 @@
        assert(ptinrect(m->xy, v->r));
        assert(v->scroll);
 
+       suspend_gtimer();
        buttons = m->buttons;
        assert(buttons);
        type = Emouse;
diff -rN -u diff-old/wily-9libs/wily/proto.h diff-new/wily-9libs/wily/proto.h
--- diff-old/wily-9libs/wily/proto.h    Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/proto.h    Tue Oct 19 00:00:43 2004
@@ -30,6 +30,7 @@
 int            data_put                (Data *d, char *path);
 int            data_del                (Data*);
 int            data_backup     (Data *d);
+void           data_rmbackup   (Data *d);
 
 /* env.c */
 void           env_init                (char **);
@@ -202,6 +203,8 @@
 void           undo_break              (Text*);
 void           undo_mark               (Text*);
 Bool           undo_atmark             (Text*);
+void           undo_bmark              (Text*);
+Bool           undo_atbmark    (Text*);
 
 /* util.c */
 void           dirnametrunc            (char*);
@@ -209,7 +212,7 @@
 void           olabel                  (char*out, char*label);
 int            statcmp                 (Stat*a, Stat*b);
 Bool           isdir                           (char*path);
-int            backup_name             (char *orig, char *back);
+char*  backup_name             (char*);
 char * columnate               (int, int, Font *, char **);
 void           noutput                 (char *context, char *base, int n);
 void           add_slash                       (char*);
@@ -281,6 +284,7 @@
 /* wily.c */
 void           addrunning              (char *cmd);
 void           rmrunning               (char *cmd);
+void           suspend_gtimer  (void);
 
 /* win.c */
 int            win_del                 (Tile*);
diff -rN -u diff-old/wily-9libs/wily/select.c diff-new/wily-9libs/wily/select.c
--- diff-old/wily-9libs/wily/select.c   Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/select.c   Tue Oct 19 00:00:43 2004
@@ -119,6 +119,7 @@
        ulong   type, timer=0;
        SelFn   fn;
        
+       suspend_gtimer();
        fn = selecting? frselectf : frselectf2;
        toggle(v, fn, p0, p1);
        v->selecting = true;
diff -rN -u diff-old/wily-9libs/wily/text.c diff-new/wily-9libs/wily/text.c
--- diff-old/wily-9libs/wily/text.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/text.c     Tue Oct 19 00:00:43 2004
@@ -184,7 +184,7 @@
        t->data = d;
        t->isbody = isbody;
        t->v = 0;               /* to be assigned later */
-       t->did = t->undone = t->mark = 0;
+       t->did = t->undone = t->mark = t->bmark = 0;
        t->undoing = NoUndo;
        t->needsbackup = false;
        return t;
diff -rN -u diff-old/wily-9libs/wily/text.h diff-new/wily-9libs/wily/text.h
--- diff-old/wily-9libs/wily/text.h     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/text.h     Tue Oct 19 00:00:43 2004
@@ -18,7 +18,7 @@
 
        ulong   pos;                    /* position for regexp search engine */
 
-       Undo    *did,*undone, *mark;
+       Undo    *did,*undone, *mark, *bmark;
        enum {NoUndo, StartUndo, MoreUndo} undoing;
 };
 #define TEXT_ISTAG(t) (        !(t)->isbody    )
diff -rN -u diff-old/wily-9libs/wily/undo.c diff-new/wily-9libs/wily/undo.c
--- diff-old/wily-9libs/wily/undo.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/undo.c     Tue Oct 19 00:00:43 2004
@@ -172,6 +172,21 @@
        return t->mark == t->did;
 }
 
+/* Remember this point in the Undo history
+ * as the latest backup point.
+ */
+void
+undo_bmark(Text*t)
+{
+       t->bmark = t->did;
+}
+
+Bool
+undo_atbmark(Text*t)
+{
+       return t->bmark == t->did;
+}
+
 /*********************************************************
        INTERNAL STUFF
 *********************************************************/
diff -rN -u diff-old/wily-9libs/wily/util.c diff-new/wily-9libs/wily/util.c
--- diff-old/wily-9libs/wily/util.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/util.c     Tue Oct 19 00:00:43 2004
@@ -93,64 +93,73 @@
        return s;
 }
 
-/* Write into 'back' the name of a file we can write to as a backup
- * for 'orig'.  Return 0 for success. */
-int
-backup_name(char *orig, char *back)
+
+/* Find an unused file name.  Return it in allocated memory.  Also create
+ * a header file corresponding to the file name returned.  The header file
+ * will contain 'orig' so we can link to the original file.  To store the
+ * "orig" links in a guide file is to much fuss since we frequently delete
+ * and recreate (with a new name) auto backup files.  Return 0 for failure.
+ */
+char *
+backup_name(char *orig)
 {
-       Path    dir, guide;
-       char    *home;
-       DIR             *dirp;
-       struct dirent   *direntp;
+       Path    path;
+       char    *home, *fname, *ret;
        FILE    *fp;
-       int     max,n;
-       int     init_guide = 0;
+       int     num;
 
        if ( !(home=getenv("WILYBAK")) ) {
                if ( !(home=getenv("HOME")) ) {
-                       return diag(0, "getenv HOME");
+                       (void) diag(0, "getenv HOME");
+                       return 0;
                }
-               sprintf(dir, "%s/.wilybak", home);
+               sprintf(path, "%s/.wilybak", home);
        } else
-               strcpy(dir, home);
+               strcpy(path, home);
 
        /* Make sure the directory exists.  Create it if necessary.
         */
-       if(access(dir, W_OK) &&  (mkdir(dir, 0700)) )
-               return diag(0, "couldn't create backup directory %s", dir);
-
-       /* Find directory entry with largest number.  We will be one
-        * greater than that.
-        */
-       max=0;
-       if(!(dirp = opendir(dir))) {
-               return diag(0, "couldn't opendir %s", dir);
+       if (access(path, W_OK) && mkdir(path, 0700)) {
+               (void) diag(0, "couldn't create backup directory %s", path);
+               return 0;
        }
-       rewinddir(dirp);        /* Workaround for FreeBSD. */
-       while ((direntp = readdir(dirp))) {
-               if ( (n=atoi(direntp->d_name)) > max)
-                       max = n;
-       }
-       closedir(dirp);
-       max++;
 
-       sprintf(back, "%s/%d", dir, max);
+       fname = path + strlen(path);
+       if (fname[-1] != '/')
+               *fname++ = '/';
 
+       /* Find unused number
+        */
+       num=0;
+       do {
+               sprintf(fname, "%d", ++num);
+       } while (!access(path, F_OK));
+       ret = strdup(path);
 
-       /* Record what is going where */
-       sprintf(guide,"%s/guide", dir);
-       if(access(guide, W_OK) < 0)
-               init_guide = 1;
-       fp = fopen(guide, "a+");
-       if(fp) {        /* if this fails, don't care all that much */
-               if(init_guide)
-                       fprintf(fp, "diff       cp      rm *\n");
-               fprintf(fp, "%3d\t%s\n", max, orig);
+       /* Write header file
+        */
+       strcat(fname, "_hdr");
+       fp = fopen(path, "w");
+       if (fp) {
+               fprintf(fp, "%3d\t%s\n", num, orig);
                fclose(fp);
        } else {
-               diag(guide, "couldn't update backup guide file");
+               (void) diag(0, "couldn't create backup header file %s", path);
+               return 0;
        }
-       return 0;
+
+       /* Write initial guide file if it does not exist
+        */
+       sprintf(fname,"guide");
+       if (access(path, W_OK)) {
+               fp = fopen(path, "w");
+               if(fp) {        /* if this fails, don't care all that much */
+                       fprintf(fp, "diff       cp      rm *\n<cat *_hdr\n");
+                       fclose(fp);
+               }
+       }
+
+       return ret;
 }
 
 void
diff -rN -u diff-old/wily-9libs/wily/wily.c diff-new/wily-9libs/wily/wily.c
--- diff-old/wily-9libs/wily/wily.c     Tue Oct 19 00:01:01 2004
+++ diff-new/wily-9libs/wily/wily.c     Tue Oct 19 00:00:43 2004
@@ -6,6 +6,7 @@
 #include "tile.h"
  
 static int     ncolumns = 2;
+static ulong   gtimer = 0;     /* used in mainloop() */
 int    tagheight;
 Tile   *wily=0;                        /* encloses the whole application */
 
@@ -198,6 +199,37 @@
        tile_reshaped(wily);
 }
 
+/* Stop the global timer.
+ * It will be automatically restarted by the main loop.
+ */
+void
+suspend_gtimer(void) {
+       if (gtimer != 0) {
+               estoptimer(gtimer);
+               gtimer = 0;
+       }
+}
+
+/* Check if it's time to autosave again, and do so if it is.
+ * Recalculate time for next autobackup, and restart the global timer.
+ */
+static void
+continue_gtimer(void) {
+       static time_t   next = 0;
+       time_t          now = time (0);
+
+       assert (gtimer == 0);
+       if (now >= next) {
+               char    *s = getenv("WILYBAKTIME");
+               int     n = s? atoi(s) : 30;
+               if (n < 10)
+                       n = 10;
+               data_backupall();
+               next = now + n;
+       }
+       gtimer = etimer (0, 1000 * (next - now));
+}
+
 /* Main event loop */
 static void
 mainloop(void)
@@ -208,7 +240,11 @@
        Point   lastp={0,0};
        View    *v=0;
 
-       while ((type = eread(~0, &e))) {
+       for (;;) {
+               if (gtimer == 0)
+                       continue_gtimer();
+                       /* gtimer must be running when we call eread() */
+               type = eread(~0, &e);
                switch(type){
                case Ekeyboard:
                        if(mouseaction) {
@@ -231,7 +267,11 @@
                        break;
 
                default:
-                       dofd(type, e.n, (char*)e.data);
+                       if (type == gtimer)
+                               suspend_gtimer();
+                               /* This will make continue_gtimer() do the 
right thing. */
+                       else
+                               dofd(type, e.n, (char*)e.data);
                        break;
                }
        }

Other related posts:

  • » patch for new autobackup (autosave) feature