+2003-07-xx Version 1.31 Beta 28Jul03
+- Add sleep(1) to console when it gets a SIGTSTP signal 
+  to prevent it from using 100% of the CPU.
+- Improve description of Priorities.
+- Add a bit more documentation to jobq.c
+- Complete hash table routine htable.c htable.h
+- Change M_INFO to M_ERROR in attribs.c for Windows errors.
+
 2003-07-23 Version 1.31 Beta 22Jul03
 - Apply a patch from Nic Bellamy that clarifies the error messages 
   during recycling volumes.
 
                  Kern's ToDo List
-                   14 July 2003 
+                   28 July 2003 
 
 Documentation to do: (any release a little bit at a time)
 - Document running a test version.
   (./create_mys... ./make_my...).
 - Document all the status codes JobLevel, JobType, JobStatus.
 - Document dynamic DNS
+- On Fri, 2003-07-25 at 22:39, Thiago Lima wrote:
+    I've just noticed that bacula uses the server time to scan the
+    > clients for new files since the last backup in incremental backup.
+    >     So if server and client clocks aren't synced you may not backup all
+    > files.
+    > 
+    >     Example :
+    > 
+    >     Server time : 16:05
+    >     Client time : 16:00
+    >     runs full backup
+    > 
+    >     Server time : 16:15
+    >     Client time : 16:10 
+    >     runs incremental backup
+    > 
+    >     If there's any file created/changed between 16:00 and 16:05 (client
+    > clock) they will never be backuped.
+    >     The solution is keep all your machines clocks synced.
+    > 
+    >     Is that right? Does anybody have any comments on that? Maybe this
+    > could go to the FAQ?
+    > 
+    > 
           
 Testing to do: (painful)
 - that ALL console command line options work and are always implemented
 For 1.31 release:
 
 
+
 For 1.32:
 - Maybe remove multiple simultaneous devices code in SD.
 - On Windows with very long path names, it may be impossible to create 
   (Phil's problem).
 - Increment DB version prior to releasing.
 - Turn off FULL_DEBUG prior to releasing.
-
 
    fd_set fdset;
    struct timeval tv;
 
-   FD_ZERO(&fdset);
-   FD_SET(fd, &fdset);
    tv.tv_sec = sec;
    tv.tv_usec = 0;
    for ( ;; ) {
+      FD_ZERO(&fdset);
+      FD_SET(fd, &fdset);
       switch(select(fd + 1, &fdset, NULL, NULL, &tv)) {
-        case 0:                         /* timeout */
-           return 0;
-        case -1:
-           if (errno == EINTR || errno == EAGAIN) {
-              continue;
-           }
-           return -1;                  /* error return */
-        default:
-           return 1;
+      case 0:                        /* timeout */
+        return 0;
+      case -1:
+        if (errno == EINTR || errno == EAGAIN) {
+           continue;
+        }
+        return -1;                  /* error return */
+      default:
+        return 1;
       }
    }
 }
    }
 again:
    switch (wait_for_data(fileno(input), sec)) {
-      case 0:
-        return 0;                    /* timeout */
-      case -1: 
-        return -1;                   /* error */
-      default:
-        len = sizeof_pool_memory(sock->msg) - 1;
-        if (stop) {
-           goto again;
-        }
-        if (fgets(sock->msg, len, input) == NULL) {
-           return -1;
-        }
-        break;
+   case 0:
+      return 0;                   /* timeout */
+   case -1: 
+      return -1;                  /* error */
+   default:
+      len = sizeof_pool_memory(sock->msg) - 1;
+      if (stop) {
+        sleep(1);
+        goto again;
+      }
+      if (fgets(sock->msg, len, input) == NULL) {
+        return -1;
+      }
+      break;
    }
    strip_trailing_junk(sock->msg);
    sock->msglen = strlen(sock->msg);
 
 };
 
 /*
- * Wait until schedule time arrives before starting
+ * Wait until schedule time arrives before starting. Normally
+ *  this routine is only used for jobs started from the console
+ *  for which the user explicitly specified a start time. Otherwise
+ *  most jobs are put into the job queue only when their
+ *  scheduled time arives.
  */
 static void *sched_wait(void *arg)
 {
       }
    }
 
+   /* Ensure that at least one server looks at the queue. */
    stat = start_server(jq);
 
    if (stat == 0) {
 }
 
 /*
- *  Remove a job from the job queue
+ *  Remove a job from the job queue. Used only by cancel Console command.
  *    jq is a queue that was created with jobq_init
  *    work_item is an element of work
  *
 
 
 /*
- * Start the server thread 
+ * Start the server thread if it isn't already running
  */
 static int start_server(jobq_t *jq)
 {
               jobq_add(jq, jcr);     /* queue the job to run again */
               P(jq->mutex);
               free(je);              /* free the job entry */
-              continue;
+              continue;              /* look for another job to run */
            }
            /* 
             * Something was actually backed up, so we cannot reuse
            njcr->messages = jcr->messages;
             Dmsg0(100, "Call to run new job\n");
            V(jq->mutex);
-           run_job(njcr);
+            run_job(njcr);            /* This creates a "new" job */
            P(jq->mutex);
             Dmsg0(100, "Back from running new job.\n");
         }
             Dmsg1(100, "Set Job pri=%d\n", Priority);
         }
         /*
-         * Acquire locks
+         * Walk down the list of waiting jobs and attempt
+         *   to acquire the resources it needs.
          */
         for ( ; je;  ) {
+           /* je is current job item on the queue, jn is the next one */
            JCR *jcr = je->jcr;
            jobq_item_t *jn = (jobq_item_t *)jq->waiting_jobs->next(je);
             Dmsg3(100, "Examining Job=%d JobPri=%d want Pri=%d\n",
               je = jn;
               continue;
            }
+           /* Got all locks, now remove it from wait queue and append it
+            *   to the ready queue  
+            */
            jcr->acquired_resource_locks = true;
            jq->waiting_jobs->remove(je);
            jq->ready_jobs->append(je);
 
    ULARGE_INTEGER li;
    POOLMEM *win32_ofile;
 
+   if (!p_GetFileAttributesEx) {                                
+      return 0;
+   }
+
    if (!p || !*p) {                  /* we should have attributes */
       Dmsg2(100, "Attributes missing. of=%s ofd=%d\n", attr->ofname, ofd->fid);
       if (is_bopen(ofd)) {
                 NULL);
    Dmsg3(100, "Error in %s on file %s: ERR=%s\n", prefix, win32_ofile, msg);
    strip_trailing_junk(msg);
-   Jmsg(jcr, M_INFO, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
+   Jmsg(jcr, M_ERROR, 0, _("Error in %s file %s: ERR=%s\n"), prefix, win32_ofile, msg);
    LocalFree(msg);
 }
 
                 NULL);
    strip_trailing_junk(msg);
    if (jcr) {
-      Jmsg2(jcr, M_INFO, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
+      Jmsg2(jcr, M_ERROR, 0, _("Error in %s: ERR=%s\n"), prefix, msg);
    } else {
       MessageBox(NULL, msg, prefix, MB_OK);
    }
 
  *  htable is a hash table of items (pointers). This code is
  *    adapted and enhanced from code I wrote in 1982 for a
  *    relocatable linker.  At that time, the hash table size
- *    was fixed and a primary number, which essentially providing
- *    the hashing. In this program, the hash table can grow when
- *    it gets too full, so the table is a binary number. The
+ *    was fixed and a primary number, which essentially provides
+ *    the randomness. In this program, the hash table can grow when
+ *    it gets too full, so the table size here is a binary number. The
  *    hashing is provided using an idea from Tcl where the initial
- *    hash code is then "randomized" a simple calculation from
+ *    hash code is then "randomized" using a simple calculation from
  *    a random number generator that multiplies by a big number
+ *    (I multiply by a prime number, while Tcl did not)
  *    then shifts the results down and does the binary division
- *    by masking.  Increasing the size of the hash table is simple
- *    simply create a new larger table, walk the old table and
- *    insert each entry into the new table.
+ *    by masking.  Increasing the size of the hash table is simple.
+ *    Just create a new larger table, walk the old table and
+ *    re-hash insert each entry into the new table.
  *
  *               
  *   Kern Sibbald, July MMIII
    return;
 }
 
-htable::htable(void *item, void *link)
+htable::htable(void *item, void *link, int tsize)
 {
-   init(item, link);
+   init(item, link, tsize);
 }
 
-void htable::init(void *item, void *link)
+void htable::init(void *item, void *link, int tsize)
 {
+   int pwr;
+   tsize >>= 2;
+   for (pwr=0; tsize; pwr++) {
+      tsize >>= 1;
+   }
    loffset = (char *)link - (char *)item;
-   mask = 7;                         /* 3 bits => table size = 8 */
-   rshift = 27;                      /* start using bits 28, 29, 30 */
+   mask = ~((~0)<<pwr);              /* 3 bits => table size = 8 */
+   rshift = 30 - pwr;                /* start using bits 28, 29, 30 */
    num_items = 0;                    /* number of entries in table */
-   buckets = 8;                      /* hash table size -- power of two */
+   buckets = 1<<pwr;                 /* hash table size -- power of two */
    max_items = buckets * 4;          /* allow average 4 entries per chain */
    table = (hlink **)malloc(buckets * sizeof(hlink *));
    memset(table, 0, buckets * sizeof(hlink *));
    walk_index = 0;
 }
 
+void * htable::operator new(size_t)
+{
+   return malloc(sizeof(htable));
+}
+
+void htable::operator delete(void *tbl) 
+{
+   ((htable *)tbl)->destroy();
+   free(tbl);
+}
+
+uint32_t htable::size()
+{
+   return num_items;
+}
+
+void htable::stats() 
+{
+   int count[10];
+   int max = 0;
+   int i, j;
+   hlink *p;
+   printf("\n\nNumItems=%d\nBuckets=%d\n", num_items, buckets);
+   for (i=0; i<10; i++) {
+      count[i] = 0;
+   }
+   for (i=0; i<(int)buckets; i++) {
+      p = table[i];
+      j = 0;    
+      while (p) {
+        p = (hlink *)(p->next);
+        j++;
+      }
+      if (j > max) {
+        max = j;
+      }
+      if (j < 10) {
+        count[j]++;
+      }
+   }
+   for (i=0; i<10; i++) {
+      printf("%2d: %d\n",i, count[i]);
+   }
+   printf("max = %d\n", max);
+}
+
 void htable::grow_table()
 {
    Dmsg1(000, "Grow called old size = %d\n", buckets);
    MYJCR *save_jcr = NULL, *item;
    MYJCR *jcr = NULL;
    int count = 0;
-
+    
    jcrtbl = (htable *)malloc(sizeof(htable));
-   jcrtbl->init(jcr, &jcr->link);
+   jcrtbl->init(jcr, &jcr->link, NITEMS);
     
-   Dmsg0(000, "Insert NITEMS items 0-19\n");
+   Dmsg1(000, "Inserting %d items\n", NITEMS);
    for (int i=0; i<NITEMS; i++) {
       sprintf(mkey, "This is htable item %d", i);
       jcr = (MYJCR *)malloc(sizeof(MYJCR));
       printf("Item 10's key is: %s\n", item->key);
    }
 
+   jcrtbl->stats();
    printf("Walk the hash table:\n");
    for (MYJCR *jcr=(MYJCR *)jcrtbl->first(); jcr; jcr=(MYJCR *)jcrtbl->next() ) {
-      printf("htable item = %s\n", jcr->key);
+//    printf("htable item = %s\n", jcr->key);
       free(jcr->key);
       count++;
    }
 
  *
  */
 
-#define OFFSET(item,link) ((char *)link - (char *)item)
-
 struct hlink {
-   void *next;                       /* next hash item */
-   char *key;                        /* key this item */
-   uint32_t hash;                    /* hash for this key */
+   void *next;                        /* next hash item */
+   char *key;                         /* key this item */
+   uint32_t hash;                     /* hash for this key */
 };
 
 class htable {
-   hlink **table;                    /* hash table */
-   int loffset;                      /* link offset in item */
-   uint32_t num_items;               /* current number of items */
-   uint32_t max_items;               /* maximum items before growing */
-   uint32_t buckets;                 /* size of hash table */
-   uint32_t hash;                    /* temp storage */
-   uint32_t index;                   /* temp storage */
+   hlink **table;                     /* hash table */
+   int loffset;                       /* link offset in item */
+   uint32_t num_items;                /* current number of items */
+   uint32_t max_items;                /* maximum items before growing */
+   uint32_t buckets;                  /* size of hash table */
+   uint32_t hash;                     /* temp storage */
+   uint32_t index;                    /* temp storage */
    uint32_t mask;                     /* "remainder" mask */
-   uint32_t rshift;                  /* amount to shift down */
-   hlink *walkptr;                   /* table walk pointer */
-   uint32_t walk_index;              /* table walk index */
-   void hash_index(char *key);       /* produce hash key,index */
-   void grow_table();                /* grow the table */
+   uint32_t rshift;                   /* amount to shift down */
+   hlink *walkptr;                    /* table walk pointer */
+   uint32_t walk_index;               /* table walk index */
+   void hash_index(char *key);        /* produce hash key,index */
+   void grow_table();                 /* grow the table */
 public:
-   htable(void *item, void *link);
-   void init(void *item, void *link);
+   htable(void *item, void *link, int tsize = 31);
+   void init(void *item, void *link, int tsize = 31);
    bool  insert(char *key, void *item);
    void *lookup(char *key);
-   void *first();                    /* get first item in table */
-   void *next();                     /* get next item in table */
+   void *first();                     /* get first item in table */
+   void *next();                      /* get next item in table */
    void destroy();
+   void stats();                      /* print stats about the table */
+   uint32_t size();                   /* return size of table */
    void * operator new(size_t);
    void operator delete(void *);
 };
 
 /* */
 #define VERSION "1.31"
 #define VSTRING "1"
-#define BDATE   "22 Jul 2003"
-#define LSMDATE "22Jul03"
+#define BDATE   "28 Jul 2003"
+#define LSMDATE "28Jul03"
 
 /* Debug flags */
 #define DEBUG 1