#define uint64_t u_int64_t
 #define uintmax_t u_intmax_t
 
+#define btime_t uint64_t
+
 #ifdef HAVE_CYGWIN
 #define socklen_t int
 #endif
 
 /* ***FIXME*** FileId_t should be uint64_t */
 typedef uint32_t FileId_t;
 typedef uint32_t DBId_t;              /* general DB id type */
+typedef uint32_t JobId_t;
 
 
 /* Job information passed to create job record and update
 
 #define MAX_DEL_LIST_LEN 1000000
 
 struct s_del_ctx {
-   uint32_t *JobId; 
+   JobId_t *JobId; 
    int num_ids;                      /* ids stored */
    int max_ids;                      /* size of array */
    int num_del;                      /* number deleted */
    }
    if (del->num_ids == del->max_ids) {
       del->max_ids = (del->max_ids * 3) / 2;
-      del->JobId = (uint32_t *)brealloc(del->JobId, sizeof(uint32_t) *
+      del->JobId = (JobId_t *)brealloc(del->JobId, sizeof(JobId_t) *
         del->max_ids);
    }
-   del->JobId[del->num_ids++] = (uint32_t)strtod(row[0], NULL);
+   del->JobId[del->num_ids++] = (JobId_t)strtod(row[0], NULL);
    return 0;
 }
 
    } else if (del.max_ids > MAX_DEL_LIST_LEN) {
       del.max_ids = MAX_DEL_LIST_LEN;
    }
-   del.JobId = (uint32_t *)malloc(sizeof(uint32_t) * del.max_ids);
+   del.JobId = (JobId_t *)malloc(sizeof(JobId_t) * del.max_ids);
    db_sql_query(mdb, mdb->cmd, delete_handler, (void *)&del);
 
    for (i=0; i < del.num_ids; i++) {
 
          scheduler.c ua_cmds.c \
          ua_dotcmds.c \
          ua_db_query.c ua_retention.c \
-         ua_input.c ua_output.c ua_run.c \
+         ua_input.c ua_output.c ua_prune.c ua_run.c \
          ua_select.c ua_server.c \
          ua_status.c verify.c
 SVROBJS = dird.o authenticate.o backup.o catreq.o dird_conf.o \
          scheduler.o ua_cmds.o \
          ua_dotcmds.o \
          ua_db_query.o ua_retention.o \
-         ua_input.o ua_output.o ua_run.o \
+         ua_input.o ua_output.o ua_prune.o ua_run.o \
          ua_select.o ua_server.o \
          ua_status.o verify.o
 
 
    {"password",    store_password, ITEM(res_dir.password), 0, ITEM_REQUIRED, 0},
    {"fdconnecttimeout", store_time,ITEM(res_dir.FDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
    {"sdconnecttimeout", store_time,ITEM(res_dir.SDConnectTimeout), 0, ITEM_DEFAULT, 60 * 30},
-
    {NULL, NULL, NULL, 0, 0, 0}
 };
 
    {"catalog",  store_res,        ITEM(res_client.catalog),  R_CATALOG, 0, 0},
    {"fileretention", store_time,  ITEM(res_client.FileRetention), 0, ITEM_DEFAULT, 60*60*24*30},
    {"jobretention",  store_time,  ITEM(res_client.JobRetention),  0, ITEM_DEFAULT, 60*60*24*365},
+   {"autoprune", store_yesno,     ITEM(res_client.AutoPrune), 1, ITEM_DEFAULT, 1},
    {NULL, NULL, NULL, 0, 0, 0} 
 };
 
    }
    switch (type) {
       case R_DIRECTOR:
-         sendit(sock, "Director: name=%s maxjobs=%d FDtimeout=%d SDtimeout=%d\n", 
+         sendit(sock, "Director: name=%s maxjobs=%d FDtimeout=%" lld " SDtimeout=%" lld "\n", 
            reshdr->name, res->res_dir.MaxConcurrentJobs, 
            res->res_dir.FDConnectTimeout,
            res->res_dir.SDConnectTimeout);
       case R_CLIENT:
          sendit(sock, "Client: name=%s address=%s FDport=%d\n",
            res->res_client.hdr.name, res->res_client.address, res->res_client.FDport);
-         sendit(sock, "JobRetention=%d FileRetention=%d\n",
-           res->res_client.JobRetention, res->res_client.FileRetention);
+         sendit(sock, "JobRetention=%" lld " FileRetention=%" lld " AutoPrune=%d\n",
+           res->res_client.JobRetention, res->res_client.FileRetention,
+           res->res_client.AutoPrune);
         if (res->res_client.catalog) {
             sendit(sock, "  --> ");
            dump_resource(-R_CATALOG, (RES *)res->res_client.catalog, sendit, sock);
       case R_POOL:
          sendit(sock, "Pool: name=%s PoolType=%s\n", res->res_pool.hdr.name,
                 res->res_pool.pool_type);
-         sendit(sock, "      use_cat=%d use_once=%d acpt_any=%d\n",
+         sendit(sock, "      use_cat=%d use_once=%d acpt_any=%d cat_files=%d\n",
                 res->res_pool.use_catalog, res->res_pool.use_volume_once,
-                res->res_pool.accept_any_volume);
-         sendit(sock, "      cat_files=%d max_vols=%d\n",
-                res->res_pool.catalog_files, res->res_pool.max_volumes);
+                res->res_pool.accept_any_volume, res->res_pool.catalog_files);
+         sendit(sock, "      max_vols=%d auto_recycle=%d VolumeRetention=%" lld "\n",
+                res->res_pool.max_volumes, res->res_pool.AutoRecycle,
+                res->res_pool.VolumeRetention);
+
          sendit(sock, "      LabelFormat=%s\n", res->res_pool.label_format?
                  res->res_pool.label_format:"NONE");
         break;
 
    char *subsys_directory;            /* SubsysDirectory */
    struct s_res_msgs *messages;
    int   MaxConcurrentJobs;
-   int   FDConnectTimeout;            /* timeout for connect in seconds */
-   int   SDConnectTimeout;            /* timeout in seconds */
+   btime_t FDConnectTimeout;          /* timeout for connect in seconds */
+   btime_t SDConnectTimeout;          /* timeout in seconds */
 };
 typedef struct s_res_dir DIRRES;
 
    RES   hdr;
 
    int   FDport;                      /* Where File daemon listens */
-   uint32_t FileRetention;            /* file retention period in seconds */
-   uint32_t JobRetention;             /* job retention period in seconds */
+   int   AutoPrune;                   /* Do automatic pruning? */
+   btime_t FileRetention;             /* file retention period in seconds */
+   btime_t JobRetention;              /* job retention period in seconds */
    char *address;
    char *password;
    struct s_res_cat    *catalog;       /* Catalog resource */
    int   RestoreJobId;                /* What -- JobId to restore */
    char *RestoreWhere;                /* Where on disk to restore -- directory */
    int   RestoreOptions;              /* How (overwrite, ..) */
-   int   MaxRunTime;                  /* max run time in seconds */
-   int   MaxStartDelay;               /* max start delay in seconds */
+   btime_t MaxRunTime;                /* max run time in seconds */
+   btime_t MaxStartDelay;             /* max start delay in seconds */
 
    struct s_res_msgs   *messages;     /* How and where to send messages */
    struct s_res_sch    *schedule;     /* When -- Automatic schedule */
    int   use_volume_once;             /* write on volume only once */
    int   accept_any_volume;           /* accept any volume */
    int   max_volumes;                 /* max number of volumes */
-   uint32_t VolumeRetention;          /* volume retention period in seconds */
+   btime_t VolumeRetention;           /* volume retention period in seconds */
    int   AutoRecycle;                 /* auto recycle */
 };
 typedef struct s_res_pool POOL;
 
    Dmsg0(100, "=====Start Job=========\n");
    jcr->start_time = now;            /* set the real start time */
    if (jcr->job->MaxStartDelay != 0 && jcr->job->MaxStartDelay <
-       (jcr->start_time - jcr->sched_time)) {
+       (btime_t)(jcr->start_time - jcr->sched_time)) {
       Jmsg(jcr, M_FATAL, 0, _("Job cancelled because max delay time exceeded.\n"));
       free_jcr(jcr);
    }
 
 extern int querycmd(UAContext *ua, char *cmd);
 extern int runcmd(UAContext *ua, char *cmd);
 extern int retentioncmd(UAContext *ua, char *cmd);
+extern int prunecmd(UAContext *ua, char *cmd);
 
 /* Forward referenced functions */
 static int addcmd(UAContext *ua, char *cmd),  createcmd(UAContext *ua, char *cmd), cancelcmd(UAContext *ua, char *cmd);
  { N_("list"),       listcmd,      _("list [pools | jobs | jobtotals | media <pool> | files job=<nn>]; from catalog")},
  { N_("messages"),   messagescmd,  _("messages")},
  { N_("mount"),      mountcmd,     _("mount <storage-name>")},
- { N_("retention"),  retentioncmd, _("retention")},
+ { N_("prune"),      prunecmd,     _("prune expired records from catalog")},
  { N_("run"),        runcmd,       _("run <job-name>")},
  { N_("setdebug"),   setdebugcmd,  _("sets debug level")},
  { N_("show"),       showcmd,      _("show (resource records) [jobs | pools | ... | all]")},
 
         }
       } else if (stat == 0) {
         if (ua.UA_sock->msglen == BNET_TERMINATE) {
+           quit = TRUE;
            break;
         }
         bnet_sig(ua.UA_sock, BNET_POLL);
 
         if (items[i].handler == store_yesno) {
            *(int *)(items[i].value) |= items[i].code;
         } else if (items[i].handler == store_pint || 
-                   items[i].handler == store_int  ||
-                   items[i].handler == store_time) {
+                   items[i].handler == store_int) {
            *(int *)(items[i].value) = items[i].default_value;
         } else if (items[i].handler == store_int64) {
            *(int64_t *)(items[i].value) = items[i].default_value;
-        } else if (items[i].handler == store_size) {
+        } else if (items[i].handler == store_size ||
+                   items[i].handler == store_time) {
            *(uint64_t *)(items[i].value) = items[i].default_value;
         }
       }
 /* Store a time period in seconds */
 void store_time(LEX *lc, struct res_items *item, int index, int pass)
 {
-   int token, i, ch, value;
+   int token, i, ch;
+   btime_t value;
    int  mod[]  = {'*', 's', 'm', 'h', 'd', 'w', 'o', 'q', 'y', 0};
    int mult[] = {1, 60, 60*60, 60*60*24, 60*60*24*7, 60*60*24*30, 
                  60*60*24*91, 60*60*24*365};
    errno = 0;
    switch (token) {
    case T_NUMBER:
-      token = (int)strtod(lc->str, NULL);
-      if (errno != 0 || token < 0) {
+      value = (btime_t)strtod(lc->str, NULL);
+      if (errno != 0 || value < 0) {
          scan_err1(lc, "expected a time period, got: %s", lc->str);
       }
-      *(int *)(item->value) = token;
+      *(btime_t *)(item->value) = value;
       break;
    case T_IDENTIFIER:
    case T_STRING:
       if (mod[i] == 0 || !is_a_number(lc->str)) {
          scan_err1(lc, "expected a time period, got: %s", lc->str);
       }
-      value = (int)strtod(lc->str, NULL);
+      value = (btime_t)strtod(lc->str, NULL);
       if (errno != 0 || value < 0) {
          scan_err1(lc, "expected a time period, got: %s", lc->str);
       }
-      *(int *)(item->value) = value * mult[i];
+      *(btime_t *)(item->value) = value * mult[i];
       break;
    default:
       scan_err1(lc, "expected a time period, got: %s", lc->str);
 
 char *          encode_mode             __PROTO((mode_t mode, char *buf));
 char *          edit_uint_with_commas   __PROTO((uint64_t val, char *buf));
 char *          add_commas              __PROTO((char *val, char *buf));
-int do_shell_expansion(char *name);
-int             is_a_number(const char *num);
+char *          edit_uint               (uint64_t val, char *buf);
+int             do_shell_expansion      (char *name);
+int             is_a_number             (const char *num);
 
 
 /*
 
    }
    if (digit_seen && (*n == 'e' || *n == 'E')
        && (ISDIGIT(n[1]) || ((n[1]=='-' || n[1] == '+') && ISDIGIT(n[2])))) {
-      n += 2;                        /* skip e- or e+ */
+      n += 2;                        /* skip e- or e+ or e digit */
       while (ISDIGIT(*n)) { n++; }
    }
    return digit_seen && *n==0;
    return add_commas(buf, buf);
 }
 
+/*
+ * Edit an integer number, the supplied buffer
+ * must be at least 27 bytes long.  The incoming number
+ * is always widened to 64 bits.
+ */
+char *edit_uint(uint64_t val, char *buf)
+{
+   sprintf(buf, "%" lld, val);
+   return buf;
+}
+
+
 /*
  * Add commas to a string, which is presumably
  * a number.  
 
 /* */
 #define VERSION "1.19"
 #define VSTRING "1"
-#define DATE    "27 April 2002"
-#define LSMDATE "27Apr02"
+#define DATE    "28 April 2002"
+#define LSMDATE "28Apr02"
 
 /* Debug flags */
 #define DEBUG 1