]> git.sur5r.net Git - bacula/bacula/commitdiff
New var.c file + implement multiple simultaneous jobs
authorKern Sibbald <kern@sibbald.com>
Wed, 18 Jun 2003 13:43:48 +0000 (13:43 +0000)
committerKern Sibbald <kern@sibbald.com>
Wed, 18 Jun 2003 13:43:48 +0000 (13:43 +0000)
git-svn-id: https://bacula.svn.sourceforge.net/svnroot/bacula/trunk@589 91ce42f0-d328-0410-95d8-f526ca767f89

25 files changed:
bacula/kernstodo
bacula/src/dird/catreq.c
bacula/src/dird/getmsg.c
bacula/src/dird/msgchan.c
bacula/src/dird/scheduler.c
bacula/src/filed/job.c
bacula/src/jcr.h
bacula/src/lib/bnet.c
bacula/src/lib/var.c [new file with mode: 0644]
bacula/src/lib/var.h [new file with mode: 0644]
bacula/src/stored/acquire.c
bacula/src/stored/append.c
bacula/src/stored/askdir.c
bacula/src/stored/bcopy.c
bacula/src/stored/bextract.c
bacula/src/stored/block.c
bacula/src/stored/bls.c
bacula/src/stored/bscan.c
bacula/src/stored/btape.c
bacula/src/stored/dev.c
bacula/src/stored/device.c
bacula/src/stored/dircmd.c
bacula/src/stored/mount.c
bacula/src/stored/protos.h
bacula/src/version.h

index 03f966506b724c25b82312ee65085dfaf1be8feb..8e1230ed5a1afa74453ffcdf0131ef28af0a16a6 100644 (file)
@@ -1,5 +1,5 @@
                  Kern's ToDo List
-                  14 June 2003 
+                  18 June 2003 
 
 Documentation to do: (any release a little bit at a time)
 - Document running a test version.
@@ -32,24 +32,16 @@ Testing to do: (painful)
 - Figure out how to use ssh or stunnel to protect Bacula communications.
 
 For 1.31 release:
-- Fix first block number after label to be zero instead of 1 (reset after label).
-- Grep for Backup OK in regression script.
-- Do NOT reuse same JobId if tape written.
 - Move JobFiles and JobBytes to SD rather than FD -- more correct.
 - Add client name to cram-md5 challenge so Director can immediately
   verify if it is the correct client.
 - Use runbeforejob to unload, then reload a volume previously used,
   then the next job run gets an error reading the drive.
-- Implement non-blocking writes and bsock->terminate in heartbeat thread,
-  or set it in status.c cancel.
-- Add restore to specific date.
 - lstat() is not going to work on Win32 for testing date.
 - Implement a Recycle command
 - Something is not right in last block of fill command.
 - Implement List Volume Job=xxx  or List scheduled volumes or
   Status Director 
-- Instrument use_count on DEVICE packets and ensure that the device is
-  being close()ed at the appropriate time.
 - Check if Incremental is working correctly when it looks for the previous Job 
   (Phil's problem).
 - Add next Volume to be used to status output.
@@ -983,3 +975,13 @@ Done: (see kernsdone for more)
 - That restoring a hard link that already exists works correctly.
   Same for soft link.
 - Make Pool resource handle Counter resources.
+- Fix first block number after label to be zero instead of 1 (reset after label).
+- Grep for Backup OK in regression script.
+- Do NOT reuse same JobId if tape written.
+- Implement non-blocking writes and bsock->terminate in heartbeat
+  thread, or set it in status.c cancel (used pthread_kill() instead of
+  non-blocking I/O.
+- Add restore to specific date.
+- Instrument use_count on DEVICE packets and ensure that the device is
+  being close()ed at the appropriate time.
+
index 6c091d90fe868fdab0b46744c9024d92629f56c5..e36bc681a448bb2b4c7c9e02a2724b727a75a180 100644 (file)
@@ -168,7 +168,7 @@ next_volume:
            edit_uint64(mr.MaxVolBytes, ed2), 
            edit_uint64(mr.VolCapacityBytes, ed3),
            mr.VolStatus, mr.Slot, mr.MaxVolJobs, mr.MaxVolFiles);
-         Dmsg1(200, "Find media: %s", bs->msg);
+         Dmsg2(100, "Find media for %s: %s", jcr->Job, bs->msg);
       } else {
          bnet_fsend(bs, "1901 No Media.\n");
       }
@@ -222,7 +222,7 @@ next_volume:
               edit_uint64(mr.MaxVolBytes, ed2), 
               edit_uint64(mr.VolCapacityBytes, ed3),
               mr.VolStatus, mr.Slot, mr.MaxVolJobs, mr.MaxVolFiles);
-            Dmsg1(200, "Vol Info: %s", bs->msg);
+            Dmsg2(100, "Vol Info for %s: %s", jcr->Job, bs->msg);
         } else { 
            /* Not suitable volume */
             bnet_fsend(bs, "1998 Volume \"%s\" %s.\n",
index 0e5eceae069241d47010c9545910289b4cb41ccf..b2309f37091e4b5e511430ad9bf2390b83fcf78c 100644 (file)
@@ -113,7 +113,7 @@ int bget_dirmsg(BSOCK *bs)
      
       /* Handle normal data */
 
-      if (B_ISDIGIT(bs->msg[0])) {     /* response? */
+      if (n > 0 && B_ISDIGIT(bs->msg[0])) {     /* response? */
         return n;                    /* yes, return it */
       }
        
index b227ffe584bea04d7b23dd9bb2c658d85098d3e5..df980e02b2b821ac6844c80ddb132e68c3fc2896 100644 (file)
@@ -183,11 +183,16 @@ int start_storage_daemon_message_thread(JCR *jcr)
 
    P(jcr->mutex);
    jcr->use_count++;                 /* mark in use by msg thread */
+   jcr->sd_msg_thread_done = false;
+   jcr->SD_msg_chan = 0;
    V(jcr->mutex);
    if ((status=pthread_create(&thid, NULL, msg_thread, (void *)jcr)) != 0) {
       Jmsg1(jcr, M_ABORT, 0, _("Cannot create message thread: %s\n"), strerror(status));
    }        
-   jcr->SD_msg_chan = thid;
+   /* Wait for thread to start */
+   while (jcr->SD_msg_chan == 0) {
+      bmicrosleep(0, 50);
+   }  
    return 1;
 }
 
@@ -218,11 +223,11 @@ static void *msg_thread(void *arg)
    uint64_t JobBytes;
    int stat;
 
+   pthread_detach(pthread_self());
+   jcr->SD_msg_chan = pthread_self();
    pthread_cleanup_push(msg_thread_cleanup, arg);
-   jcr->sd_msg_thread_done = false;
    Dmsg0(200, "msg_thread\n");
    sd = jcr->store_bsock;
-   pthread_detach(pthread_self());
 
    /* Read the Storage daemon's output.
     */
@@ -235,8 +240,8 @@ static void *msg_thread(void *arg)
       if (sscanf(sd->msg, Job_end, &Job, &JobStatus, &JobFiles,
                 &JobBytes) == 4) {
         jcr->SDJobStatus = JobStatus; /* termination status */
-        jcr->JobFiles = JobFiles;
-        jcr->JobBytes = JobBytes;
+        jcr->SDJobFiles = JobFiles;
+        jcr->SDJobBytes = JobBytes;
         break;
       }     
       if (sscanf(sd->msg, Job_status, &Job, &JobStatus) == 2) {
index 9ab02e14ef2d178c36501131975acceeea39f999..8ec070d77a0e9c34fcaed22b0be979c19df6604a 100644 (file)
@@ -118,7 +118,7 @@ JCR *wait_for_next_job(char *job_to_run)
 #ifdef xxxx_debug
       if (runtime > 0) {
         bstrftime(dt, sizeof(dt), runjobs[i].runtime);  
-         Dmsg2(000, "    %s run %s\n", dt, runjobs[i].job->hdr.name);
+         Dmsg2(100, "    %s run %s\n", dt, runjobs[i].job->hdr.name);
       }
 #endif
    }
index a5f365c51a4f1bb8bb40f555cbb4ceb6cb9e28e2..b4fdf052e331aad7dcf26a8836c8e550f6641e13 100644 (file)
@@ -894,6 +894,9 @@ int response(JCR *jcr, BSOCK *sd, char *resp, char *cmd)
         return 1;
       }
    } 
+   if (job_canceled(jcr)) {
+      return 0;                      /* if canceled avoid useless error messages */
+   }
    if (is_bnet_error(sd)) {
       Jmsg2(jcr, M_FATAL, 0, _("Comm error with SD. bad response to %s. ERR=%s\n"),
         cmd, bnet_strerror(sd));
index 1f9f0693e22a83ccbde91988565fe6469508d67e..2add1749c98c51b50e5abc9966ad52607e0911e2 100644 (file)
@@ -137,6 +137,9 @@ struct JCR {
    FILESET *fileset;                  /* FileSet resource */
    CAT *catalog;                      /* Catalog resource */
    MSGS *messages;                    /* Default message handler */
+   uint32_t SDJobFiles;               /* Number of files written, this job */
+   uint64_t SDJobBytes;               /* Number of bytes processed this job */
+   uint32_t SDErrors;                 /* Number of non-fatal errors */
    volatile int SDJobStatus;          /* Storage Job Status */
    volatile int FDJobStatus;          /* File daemon Job Status */
    B_DB *db;                          /* database pointer */
@@ -200,8 +203,8 @@ struct JCR {
    POOLMEM *media_type;               /* media type */
    POOLMEM *dev_name;                 /* device name */
    VOL_LIST *VolList;                 /* list to read */
-   long NumVolumes;                   /* number of volumes used */
-   long CurVolume;                    /* current volume number */
+   int32_t NumVolumes;                /* number of volumes used */
+   int32_t CurVolume;                 /* current volume number */
    int spool_attributes;              /* set if spooling attributes */
    int no_attributes;                 /* set if no attributes wanted */
    int label_status;                  /* device volume label status */
@@ -210,11 +213,16 @@ struct JCR {
    DEV_RECORD rec;                    /* Read/Write record */
    long Ticket;                       /* ticket for this job */
    uint32_t VolFirstIndex;            /* First file index this Volume */
+   uint32_t VolLastIndex;             /* Last file index this Volume */
    uint32_t FileIndex;                /* Current File Index */
    uint32_t EndFile;                  /* End file written */
    uint32_t StartFile;                /* Start write file */
    uint32_t StartBlock;               /* Start write block */
    uint32_t EndBlock;                 /* Ending block written */
+   bool NewVol;                       /* set when new Volume mounted */
+   bool WroteVol;                     /* set when Volume written */
+   int CurVol;                        /* Current Volume count */
+
    uint32_t FileId;                   /* Last file id inserted */
 
    /* Parmaters for Open Read Session */
index 8ee57ddffddd89a8b84897ee45116143733a9d8b..4e62c4aa9e7a2660fbadd9813137dc6f3dd12827 100644 (file)
@@ -334,7 +334,7 @@ bnet_send(BSOCK *bsock)
         bsock->b_errno = errno;
       }
       if (rc < 0) {
-        if (!bsock->suppress_error_msgs) {
+        if (!bsock->suppress_error_msgs && !bsock->timed_out) {
             Jmsg4(bsock->jcr, M_ERROR, 0, _("Write error sending to %s:%s:%d: ERR=%s\n"), 
                  bsock->who, bsock->host, bsock->port,  bnet_strerror(bsock));
         }
diff --git a/bacula/src/lib/var.c b/bacula/src/lib/var.c
new file mode 100644 (file)
index 0000000..ae777fb
--- /dev/null
@@ -0,0 +1,2719 @@
+/*
+**  OSSP var - Variable Expansion
+**  Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
+**  Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
+**
+**  This file is part of OSSP var, a variable expansion
+**  library which can be found at http://www.ossp.org/pkg/lib/var/.
+**
+**  Permission to use, copy, modify, and distribute this software for
+**  any purpose with or without fee is hereby granted, provided that
+**  the above copyright notice and this permission notice appear in all
+**  copies.
+**
+**  For disclaimer see below.
+*/
+/*
+ *  Adapted by Kern Sibbald to Bacula June 2003
+ */
+/*
+   Copyright (C) 2000-2003 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA.
+
+ */
+
+
+#include "bacula.h"
+#if defined(HAVE_PCREPOSIX)
+#  include <pcreposix.h>
+#else
+#  include <regex.h>
+#endif
+#include "var.h"
+
+/* support for OSSP ex based exception throwing */
+#ifdef WITH_EX
+#include "ex.h"
+#define VAR_RC(rv) \
+    (  (rv) != VAR_OK && (ex_catching && !ex_shielding) \
+     ? (ex_throw(var_id, NULL, (rv)), (rv)) : (rv) )
+#else
+#define VAR_RC(rv) (rv)
+#endif /* WITH_EX */
+
+#ifndef EOS
+#define EOS '\0'
+#endif
+
+/*
+**
+**  ==== INTERNAL DATA STRUCTURES ====
+**
+*/
+
+typedef char char_class_t[256]; /* 256 == 2 ^ sizeof(unsigned char)*8 */
+
+/* the external context structure */
+struct var_st {
+    var_syntax_t        syntax;
+    char_class_t        syntax_nameclass;
+    var_cb_value_t      cb_value_fct;
+    void               *cb_value_ctx;
+    var_cb_operation_t  cb_operation_fct;
+    void               *cb_operation_ctx;
+};
+
+/* the internal expansion context structure */
+struct var_parse_st {
+    struct var_parse_st *lower;
+    int                 force_expand;
+    int                 rel_lookup_flag;
+    int                 rel_lookup_cnt;
+    int                 index_this;
+};
+typedef struct var_parse_st var_parse_t;
+
+/* the default syntax configuration */
+static const var_syntax_t var_syntax_default = {
+    '\\',         /* escape */
+    '$',          /* delim_init */
+    '{',          /* delim_open */
+    '}',          /* delim_close */
+    '[',          /* index_open */
+    ']',          /* index_close */
+    '#',          /* index_mark */
+    "a-zA-Z0-9_"  /* name_chars */
+};
+
+/*
+**
+**  ==== FORMATTING FUNCTIONS ====
+**
+*/
+
+/* minimal output-independent vprintf(3) variant which supports %{c,s,d,%} only */
+static int 
+var_mvxprintf(
+    int (*output)(void *ctx, const char *buffer, int bufsize), void *ctx, 
+    const char *format, va_list ap)
+{
+    /* sufficient integer buffer: <available-bits> x log_10(2) + safety */
+    char ibuf[((sizeof(int)*8)/3)+10]; 
+    char *cp;
+    char c;
+    int d;
+    int n;
+    int bytes;
+
+    if (format == NULL || ap == NULL)
+       return -1;
+    bytes = 0;
+    while (*format != '\0') {
+        if (*format == '%') {
+           c = *(format+1);
+            if (c == '%') {
+                /* expand "%%" */
+               cp = &c;
+               n = sizeof(char);
+           }
+            else if (c == 'c') {
+                /* expand "%c" */
+               c = (char)va_arg(ap, int);
+               cp = &c;
+               n = sizeof(char);
+           }
+            else if (c == 's') {
+                /* expand "%s" */
+               if ((cp = (char *)va_arg(ap, char *)) == NULL)
+                    cp = "(null)";
+               n = strlen(cp);
+           }
+            else if (c == 'd') {
+                /* expand "%d" */
+               d = (int)va_arg(ap, int);
+                bsnprintf(ibuf, sizeof(ibuf), "%d", d); /* explicitly secure */
+               cp = ibuf;
+               n = strlen(cp);
+           }
+           else {
+                /* any other "%X" */
+               cp = (char *)format;
+               n  = 2;
+           }
+           format += 2;
+       }
+       else {
+           /* plain text */
+           cp = (char *)format;
+            if ((format = strchr(cp, '%')) == NULL)
+                format = strchr(cp, '\0');
+           n = format - cp;
+       }
+       /* perform output operation */
+       if (output != NULL)
+           if ((n = output(ctx, cp, n)) == -1)
+               break;
+       bytes += n;
+    }
+    return bytes;
+}
+
+/* output callback function context for var_mvsnprintf() */
+typedef struct {
+    char *bufptr;
+    int buflen;
+} var_mvsnprintf_cb_t;
+
+/* output callback function for var_mvsnprintf() */
+static int 
+var_mvsnprintf_cb(
+    void *_ctx, 
+    const char *buffer, int bufsize)
+{
+    var_mvsnprintf_cb_t *ctx = (var_mvsnprintf_cb_t *)_ctx;
+
+    if (bufsize > ctx->buflen)
+       return -1;
+    memcpy(ctx->bufptr, buffer, bufsize);
+    ctx->bufptr += bufsize;
+    ctx->buflen -= bufsize;
+    return bufsize;
+}
+
+/* minimal vsnprintf(3) variant which supports %{c,s,d} only */
+static int 
+var_mvsnprintf(
+    char *buffer, int bufsize, 
+    const char *format, va_list ap)
+{
+    int n;
+    var_mvsnprintf_cb_t ctx;
+
+    if (format == NULL || ap == NULL)
+       return -1;
+    if (buffer != NULL && bufsize == 0)
+       return -1;
+    if (buffer == NULL) 
+       /* just determine output length */
+       n = var_mvxprintf(NULL, NULL, format, ap);
+    else {
+       /* perform real output */
+       ctx.bufptr = buffer;
+       ctx.buflen = bufsize;
+       n = var_mvxprintf(var_mvsnprintf_cb, &ctx, format, ap);
+       if (n != -1 && ctx.buflen == 0)
+           n = -1;
+       if (n != -1)
+            *(ctx.bufptr) = '\0';
+    }
+    return n;
+}
+
+/*
+**
+**  ==== PARSE CONTEXT FUNCTIONS ====
+**
+*/
+
+static var_parse_t *
+var_parse_push(
+    var_parse_t *lower, var_parse_t *upper)
+{
+    if (upper == NULL)
+       return NULL;
+    memcpy(upper, lower, sizeof(var_parse_t));
+    upper->lower = lower;
+    return upper;
+}
+
+static var_parse_t *
+var_parse_pop(
+    var_parse_t *upper)
+{
+    if (upper == NULL)
+       return NULL;
+    return upper->lower;
+}
+
+/*
+**
+**  ==== TOKEN BUFFER FUNCTIONS ====
+**
+*/
+
+#define TOKENBUF_INITIAL_BUFSIZE 64
+
+typedef struct {
+    const char *begin;
+    const char *end;
+    int buffer_size;
+} tokenbuf_t;
+
+static void 
+tokenbuf_init(
+    tokenbuf_t *buf)
+{
+    buf->begin = NULL;
+    buf->end = NULL;
+    buf->buffer_size = 0;
+    return;
+}
+
+static int 
+tokenbuf_isundef(
+    tokenbuf_t *buf)
+{
+    if (buf->begin == NULL && buf->end == NULL)
+       return 1;
+    return 0;
+}
+
+static int 
+tokenbuf_isempty(
+    tokenbuf_t *buf)
+{
+    if (buf->begin == buf->end)
+       return 1;
+    return 0;
+}
+
+static void 
+tokenbuf_set(
+    tokenbuf_t *buf, const char *begin, const char *end, int buffer_size)
+{
+    buf->begin = begin;
+    buf->end = end;
+    buf->buffer_size = buffer_size;
+    return;
+}
+
+static void 
+tokenbuf_move(
+    tokenbuf_t *src, tokenbuf_t *dst)
+{
+    dst->begin = src->begin;
+    dst->end = src->end;
+    dst->buffer_size = src->buffer_size;
+    tokenbuf_init(src);
+    return;
+}
+
+static int 
+tokenbuf_assign(
+    tokenbuf_t *buf, const char *data, int len)
+{
+    char *p;
+
+    if ((p = (char *)malloc(len + 1)) == NULL)
+       return 0;
+    memcpy(p, data, len);
+    buf->begin = p;
+    buf->end = p + len;
+    buf->buffer_size = len + 1;
+    *((char *)(buf->end)) = EOS;
+    return 1;
+}
+
+static int 
+tokenbuf_append(
+    tokenbuf_t *output, const char *data, int len)
+{
+    char *new_buffer;
+    int new_size;
+    char *tmp;
+
+    /* Is the tokenbuffer initialized at all? If not, allocate a
+       standard-sized buffer to begin with. */
+    if (output->begin == NULL) {
+       if ((output->begin = output->end = (const char *)malloc(TOKENBUF_INITIAL_BUFSIZE)) == NULL)
+           return 0;
+       output->buffer_size = TOKENBUF_INITIAL_BUFSIZE;
+    }
+
+    /* does the token contain text, but no buffer has been allocated yet? */
+    if (output->buffer_size == 0) {
+       /* check whether data borders to output. If, we can append
+          simly by increasing the end pointer. */
+       if (output->end == data) {
+           output->end += len;
+           return 1;
+       }
+       /* ok, so copy the contents of output into an allocated buffer
+          so that we can append that way. */
+       if ((tmp = (char *)malloc(output->end - output->begin + len + 1)) == NULL)
+           return 0;
+       memcpy(tmp, output->begin, output->end - output->begin);
+       output->buffer_size = output->end - output->begin;
+       output->begin = tmp;
+       output->end = tmp + output->buffer_size;
+       output->buffer_size += len + 1;
+    }
+
+    /* does the token fit into the current buffer? If not, realloc a
+       larger buffer that fits. */
+    if ((output->buffer_size - (output->end - output->begin)) <= len) {
+       new_size = output->buffer_size;
+       do {
+           new_size *= 2;
+       } while ((new_size - (output->end - output->begin)) <= len);
+       if ((new_buffer = (char *)realloc((char *)output->begin, new_size)) == NULL)
+           return 0;
+       output->end = new_buffer + (output->end - output->begin);
+       output->begin = new_buffer;
+       output->buffer_size = new_size;
+    }
+
+    /* append the data at the end of the current buffer. */
+    if (len > 0)
+       memcpy((char *)output->end, data, len);
+    output->end += len;
+    *((char *)output->end) = EOS;
+    return 1;
+}
+
+static int 
+tokenbuf_merge(
+    tokenbuf_t *output, tokenbuf_t *input)
+{
+    return tokenbuf_append(output, input->begin, input->end - input->begin);
+}
+
+static void 
+tokenbuf_free(
+    tokenbuf_t *buf)
+{
+    if (buf->begin != NULL && buf->buffer_size > 0)
+       free((char *)buf->begin);
+    buf->begin = buf->end = NULL;
+    buf->buffer_size = 0;
+    return;
+}
+
+/*
+**
+**  ==== CHARACTER CLASS EXPANSION ====
+**
+*/
+
+static void 
+expand_range(char a, char b, char_class_t chrclass)
+{
+    do {
+       chrclass[(int)a] = 1;
+    } while (++a <= b);
+    return;
+}
+
+static var_rc_t 
+expand_character_class(const char *desc, char_class_t chrclass)
+{
+    int i;
+
+    /* clear the class array. */
+    for (i = 0; i < 256; ++i)
+       chrclass[i] = 0;
+
+    /* walk through class description and set appropriate entries in array */
+    while (*desc != EOS) {
+        if (desc[1] == '-' && desc[2] != EOS) {
+           if (desc[0] > desc[2])
+               return VAR_ERR_INCORRECT_CLASS_SPEC;
+           expand_range(desc[0], desc[2], chrclass);
+           desc += 3;
+       } else {
+           chrclass[(int) *desc] = 1;
+           desc++;
+       }
+    }
+    return VAR_OK;
+}
+
+/*
+**
+**  ==== ESCAPE SEQUENCE EXPANSION FUNCTIONS ====
+**
+*/
+
+static int 
+expand_isoct(
+    char c)
+{
+    if (c >= '0' && c <= '7')
+       return 1;
+    else
+       return 0;
+}
+
+static var_rc_t 
+expand_octal(
+    const char **src, char **dst, const char *end)
+{
+    unsigned char c;
+
+    if (end - *src < 3)
+       return VAR_ERR_INCOMPLETE_OCTAL;
+    if (   !expand_isoct(**src) 
+       || !expand_isoct((*src)[1]) 
+       || !expand_isoct((*src)[2]))
+       return VAR_ERR_INVALID_OCTAL;
+
+    c = **src - '0';
+    if (c > 3)
+       return VAR_ERR_OCTAL_TOO_LARGE;
+    c *= 8;
+    (*src)++;
+
+    c += **src - '0';
+    c *= 8;
+    (*src)++;
+
+    c += **src - '0';
+
+    **dst = (char) c;
+    (*dst)++;
+    return VAR_OK;
+}
+
+static int 
+expand_ishex(
+    char c)
+{
+    if ((c >= '0' && c <= '9') ||
+        (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+       return 1;
+    else
+       return 0;
+}
+
+static var_rc_t 
+expand_simple_hex(
+    const char **src, char **dst, const char *end)
+{
+    unsigned char c = 0;
+
+    if (end - *src < 2)
+       return VAR_ERR_INCOMPLETE_HEX;
+    if (   !expand_ishex(**src) 
+       || !expand_ishex((*src)[1]))
+       return VAR_ERR_INVALID_HEX;
+
+    if (**src >= '0' && **src <= '9')
+        c = **src - '0';
+    else if (c >= 'a' && c <= 'f')
+        c = **src - 'a' + 10;
+    else if (c >= 'A' && c <= 'F')
+        c = **src - 'A' + 10;
+
+    c = c << 4;
+    (*src)++;
+
+    if (**src >= '0' && **src <= '9')
+        c += **src - '0';
+    else if (**src >= 'a' && **src <= 'f')
+        c += **src - 'a' + 10;
+    else if (**src >= 'A' && **src <= 'F')
+        c += **src - 'A' + 10;
+
+    **dst = (char) c;
+    (*dst)++;
+    return VAR_OK;
+}
+
+static var_rc_t 
+expand_grouped_hex(
+    const char **src, char **dst, const char *end)
+{
+    var_rc_t rc;
+
+    while (*src < end && **src != '}') {
+       if ((rc = expand_simple_hex(src, dst, end)) != VAR_OK)
+           return rc;
+       (*src)++;
+    }
+    if (*src == end)
+       return VAR_ERR_INCOMPLETE_GROUPED_HEX;
+
+    return VAR_OK;
+}
+
+static var_rc_t 
+expand_hex(
+    const char **src, char **dst, const char *end)
+{
+    if (*src == end)
+       return VAR_ERR_INCOMPLETE_HEX;
+    if (**src == '{') {
+       (*src)++;
+       return expand_grouped_hex(src, dst, end);
+    } else
+       return expand_simple_hex(src, dst, end);
+}
+
+/* 
+**
+**  ==== RECURSIVE-DESCEND VARIABLE EXPANSION PARSER ====
+**
+*/
+
+/* forward declarations */
+static int parse_variable(var_t *var, var_parse_t *ctx, const char *begin, const char *end, tokenbuf_t *result);
+static int parse_numexp (var_t *var, var_parse_t *ctx, const char *begin, const char *end, int *result, int *failed);
+static int parse_name  (var_t *var, var_parse_t *ctx, const char *begin, const char *end);
+
+/* parse pattern text */
+static int 
+parse_pattern(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until '/' */
+    for (p = begin; p != end && *p != '/'; p++) {
+       if (*p == var->syntax.escape) {
+           if (p + 1 == end)
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+           p++;
+       }
+    }
+    return (p - begin);
+}
+
+/* parse substitution text */
+static int 
+parse_substext(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until delim_init or '/' */
+    for (p = begin; p != end && *p != var->syntax.delim_init && *p != '/'; p++) {
+       if (*p == var->syntax.escape) {
+           if (p + 1 == end)
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+           p++;
+       }
+    }
+    return (p - begin);
+}
+
+/* parse expression text */
+static int 
+parse_exptext(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until delim_init or delim_close or ':' */
+    for (p = begin;    p != end
+                   && *p != var->syntax.delim_init
+                   && *p != var->syntax.delim_close
+                    && *p != ':'; p++) {
+       if (*p == var->syntax.escape) {
+           if (p + 1 == end)
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+           p++;
+       }
+    }
+    return (p - begin);
+}
+
+/* parse opertion argument text */
+static int 
+parse_opargtext(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until delim_init or ')' */
+    for (p = begin; p != end && *p != var->syntax.delim_init && *p != ')'; p++) {
+       if (*p == var->syntax.escape) {
+           if (p + 1 == end)
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+           p++;
+       }
+    }
+    return (p - begin);
+}
+
+static int 
+parse_opargtext_or_variable(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p;
+    tokenbuf_t tmp;
+    int rc;
+
+    tokenbuf_init(result);
+    tokenbuf_init(&tmp);
+    p = begin;
+    if (p == end)
+       return 0;
+    do {
+       rc = parse_opargtext(var, ctx, p, end);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           if (!tokenbuf_append(result, p, rc)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+       }
+       rc = parse_variable(var, ctx, p, end, &tmp);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           p += rc;
+           if (!tokenbuf_merge(result, &tmp)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+       }
+    } while (rc > 0);
+    tokenbuf_free(&tmp);
+    return (p - begin);
+
+    error_return:
+    tokenbuf_free(&tmp);
+    tokenbuf_free(result);
+    return rc;
+}
+
+/* parse expression or variable */
+static int 
+parse_exptext_or_variable(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p = begin;
+    tokenbuf_t tmp;
+    int rc;
+
+    tokenbuf_init(result);
+    tokenbuf_init(&tmp);
+    if (begin == end)
+       return 0;
+    do {
+       /* try to parse expression text */
+       rc = parse_exptext(var, ctx, p, end);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           if (!tokenbuf_append(result, p, rc)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+       }
+
+       /* try to parse variable construct */
+       rc = parse_variable(var, ctx, p, end, &tmp);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           p += rc;
+           if (!tokenbuf_append
+               (result, tmp.begin, tmp.end - tmp.begin)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+       }
+    } while (rc > 0);
+
+    tokenbuf_free(&tmp);
+    return (p - begin);
+
+    error_return:
+    tokenbuf_free(&tmp);
+    tokenbuf_free(result);
+    return rc;
+}
+
+/* parse substitution text or variable */
+static int 
+parse_substext_or_variable(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p = begin;
+    tokenbuf_t tmp;
+    int rc;
+
+    tokenbuf_init(result);
+    tokenbuf_init(&tmp);
+    if (begin == end)
+       return 0;
+    do {
+       /* try to parse substitution text */
+       rc = parse_substext(var, ctx, p, end);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           if (!tokenbuf_append(result, p, rc)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+       }
+
+       /* try to parse substitution text */
+       rc = parse_variable(var, ctx, p, end, &tmp);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           p += rc;
+           if (!tokenbuf_append
+               (result, tmp.begin, tmp.end - tmp.begin)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+       }
+    } while (rc > 0);
+
+    tokenbuf_free(&tmp);
+    return (p - begin);
+
+    error_return:
+    tokenbuf_free(&tmp);
+    tokenbuf_free(result);
+    return rc;
+}
+
+/* parse class description */
+static int 
+parse_class_description(
+    var_t *var, var_parse_t *ctx,
+    tokenbuf_t *src, tokenbuf_t *dst)
+{
+    unsigned char c, d;
+    const char *p; 
+
+    p = src->begin;
+    while (p != src->end) {
+        if ((src->end - p) >= 3 && p[1] == '-') {
+           if (*p > p[2])
+               return VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC;
+           for (c = *p, d = p[2]; c <= d; ++c) {
+               if (!tokenbuf_append(dst, (char *)&c, 1))
+                   return VAR_ERR_OUT_OF_MEMORY;
+           }
+           p += 3;
+       } else {
+           if (!tokenbuf_append(dst, p, 1))
+               return VAR_ERR_OUT_OF_MEMORY;
+           p++;
+       }
+    }
+    return VAR_OK;
+}
+
+/* parse regex replace part */
+static int 
+parse_regex_replace(
+    var_t *var, var_parse_t *ctx,
+    const char *data, 
+    tokenbuf_t *orig,
+    regmatch_t *pmatch, 
+    tokenbuf_t *expanded)
+{
+    const char *p;
+    int i;
+
+    p = orig->begin;
+    tokenbuf_init(expanded);
+
+    while (p != orig->end) {
+        if (*p == '\\') {
+           if (orig->end - p <= 1) {
+               tokenbuf_free(expanded);
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+           }
+           p++;
+            if (*p == '\\') {
+               if (!tokenbuf_append(expanded, p, 1)) {
+                   tokenbuf_free(expanded);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               p++;
+               continue;
+           }
+           if (!isdigit((int)*p)) {
+               tokenbuf_free(expanded);
+               return VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE;
+           }
+            i = (*p - '0');
+           p++;
+           if (pmatch[i].rm_so == -1 || pmatch[i].rm_eo == -1) {
+               tokenbuf_free(expanded);
+               return VAR_ERR_SUBMATCH_OUT_OF_RANGE;
+           }
+           if (!tokenbuf_append(expanded, data + pmatch[i].rm_so,
+                                pmatch[i].rm_eo - pmatch[i].rm_so)) {
+               tokenbuf_free(expanded);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+       } else {
+           if (!tokenbuf_append(expanded, p, 1)) {
+               tokenbuf_free(expanded);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           p++;
+       }
+    }
+
+    return VAR_OK;
+}
+
+/* operation: transpose */
+static int 
+op_transpose(
+    var_t *var, var_parse_t *ctx,
+    tokenbuf_t *data, 
+    tokenbuf_t *search,
+    tokenbuf_t *replace)
+{
+    tokenbuf_t srcclass, dstclass;
+    const char *p;
+    int rc;
+    int i;
+
+    tokenbuf_init(&srcclass);
+    tokenbuf_init(&dstclass);
+    if ((rc = parse_class_description(var, ctx, search, &srcclass)) != VAR_OK)
+       goto error_return;
+    if ((rc = parse_class_description(var, ctx, replace, &dstclass)) != VAR_OK)
+       goto error_return;
+    if (srcclass.begin == srcclass.end) {
+       rc = VAR_ERR_EMPTY_TRANSPOSE_CLASS;
+       goto error_return;
+    }
+    if ((srcclass.end - srcclass.begin) != (dstclass.end - dstclass.begin)) {
+       rc = VAR_ERR_TRANSPOSE_CLASSES_MISMATCH;
+       goto error_return;
+    }
+    if (data->buffer_size == 0) {
+       tokenbuf_t tmp;
+       if (!tokenbuf_assign(&tmp, data->begin, data->end - data->begin)) {
+           rc = VAR_ERR_OUT_OF_MEMORY;
+           goto error_return;
+       }
+       tokenbuf_move(&tmp, data);
+    }
+    for (p = data->begin; p != data->end; ++p) {
+       for (i = 0; i <= (srcclass.end - srcclass.begin); ++i) {
+           if (*p == srcclass.begin[i]) {
+               *((char *)p) = dstclass.begin[i];
+               break;
+           }
+       }
+    }
+    tokenbuf_free(&srcclass);
+    tokenbuf_free(&dstclass);
+    return VAR_OK;
+
+    error_return:
+    tokenbuf_free(search);
+    tokenbuf_free(replace);
+    tokenbuf_free(&srcclass);
+    tokenbuf_free(&dstclass);
+    return rc;
+}
+
+/* operation: search & replace */
+static int 
+op_search_and_replace(
+    var_t *var, var_parse_t *ctx,
+    tokenbuf_t *data, 
+    tokenbuf_t *search,
+    tokenbuf_t *replace, 
+    tokenbuf_t *flags)
+{
+    tokenbuf_t tmp;
+    const char *p;
+    int case_insensitive = 0;
+    int multiline = 0;
+    int global = 0;
+    int no_regex = 0;
+    int rc;
+
+    if (search->begin == search->end)
+       return VAR_ERR_EMPTY_SEARCH_STRING;
+
+    for (p = flags->begin; p != flags->end; p++) {
+       switch (tolower(*p)) {
+            case 'm':
+               multiline = 1;
+               break;
+            case 'i':
+               case_insensitive = 1;
+               break;
+            case 'g':
+               global = 1;
+               break;
+            case 't':
+               no_regex = 1;
+               break;
+           default:
+               return VAR_ERR_UNKNOWN_REPLACE_FLAG;
+       }
+    }
+
+    if (no_regex) {
+       /* plain text pattern based operation */
+       tokenbuf_init(&tmp);
+       for (p = data->begin; p != data->end;) {
+           if (case_insensitive)
+               rc = strncasecmp(p, search->begin, search->end - search->begin);
+           else
+               rc = strncmp(p, search->begin, search->end - search->begin);
+           if (rc != 0) {
+               /* not matched, copy character */
+               if (!tokenbuf_append(&tmp, p, 1)) {
+                   tokenbuf_free(&tmp);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               p++;
+           } else {
+               /* matched, copy replacement string */
+               tokenbuf_merge(&tmp, replace);
+               p += (search->end - search->begin);
+               if (!global) {
+                   /* append remaining text */
+                   if (!tokenbuf_append(&tmp, p, data->end - p)) {
+                       tokenbuf_free(&tmp);
+                       return VAR_ERR_OUT_OF_MEMORY;
+                   }
+                   break;
+               }
+           }
+       }
+       tokenbuf_free(data);
+       tokenbuf_move(&tmp, data);
+    } else {
+       /* regular expression pattern based operation */
+       tokenbuf_t mydata;
+       tokenbuf_t myreplace;
+       regex_t preg;
+       regmatch_t pmatch[10];
+       int regexec_flag;
+
+       /* copy pattern and data to own buffer to make sure they are EOS-terminated */
+       if (!tokenbuf_assign(&tmp, search->begin, search->end - search->begin))
+           return VAR_ERR_OUT_OF_MEMORY;
+       if (!tokenbuf_assign(&mydata, data->begin, data->end - data->begin)) {
+           tokenbuf_free(&tmp);
+           return VAR_ERR_OUT_OF_MEMORY;
+       }
+
+       /* compile the pattern. */
+       rc = regcomp(&preg, tmp.begin, 
+                    (  REG_EXTENDED
+                     | (multiline ? REG_NEWLINE : 0)
+                     | (case_insensitive ? REG_ICASE : 0)));
+       tokenbuf_free(&tmp);
+       if (rc != 0) {
+           tokenbuf_free(&mydata);
+           return VAR_ERR_INVALID_REGEX_IN_REPLACE;
+       }
+
+       /* match the pattern and create the result string in the tmp buffer */
+        tokenbuf_append(&tmp, "", 0);
+       for (p = mydata.begin; p < mydata.end; ) {
+            if (p == mydata.begin || p[-1] == '\n')
+               regexec_flag = 0;
+           else
+               regexec_flag = REG_NOTBOL;
+           rc = regexec(&preg, p, sizeof(pmatch) / sizeof(regmatch_t), pmatch, regexec_flag);
+           if (rc != 0) {
+               /* no (more) matching */
+               tokenbuf_append(&tmp, p, mydata.end - p);
+               break;
+           }
+           else if (   multiline 
+                    && (p + pmatch[0].rm_so) == mydata.end 
+                    && (pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
+               /* special case: found empty pattern (usually /^/ or /$/ only)
+                  in multi-line at end of data (after the last newline) */
+               tokenbuf_append(&tmp, p, mydata.end - p);
+               break;
+           }
+           else {
+               /* append prolog string */
+               if (!tokenbuf_append(&tmp, p, pmatch[0].rm_so)) {
+                   regfree(&preg);
+                   tokenbuf_free(&tmp);
+                   tokenbuf_free(&mydata);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               /* create replace string */
+               rc = parse_regex_replace(var, ctx, p, replace, pmatch, &myreplace);
+               if (rc != VAR_OK) {
+                   regfree(&preg);
+                   tokenbuf_free(&tmp);
+                   tokenbuf_free(&mydata);
+                   return rc;
+               }
+               /* append replace string */
+               if (!tokenbuf_append(&tmp, myreplace.begin, myreplace.end - myreplace.begin)) {
+                   regfree(&preg);
+                   tokenbuf_free(&tmp);
+                   tokenbuf_free(&mydata);
+                   tokenbuf_free(&myreplace);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               tokenbuf_free(&myreplace);
+               /* skip now processed data */
+               p += pmatch[0].rm_eo;
+               /* if pattern matched an empty part (think about
+                  anchor-only regular expressions like /^/ or /$/) we
+                  skip the next character to make sure we do not enter
+                  an infinitive loop in matching */
+               if ((pmatch[0].rm_eo - pmatch[0].rm_so) == 0) {
+                   if (p >= mydata.end)
+                       break;
+                   if (!tokenbuf_append(&tmp, p, 1)) {
+                       regfree(&preg);
+                       tokenbuf_free(&tmp);
+                       tokenbuf_free(&mydata);
+                       return VAR_ERR_OUT_OF_MEMORY;
+                   }
+                   p++;
+               }
+               /* append prolog string and stop processing if we 
+                  do not perform the search & replace globally */
+               if (!global) {
+                   if (!tokenbuf_append(&tmp, p, mydata.end - p)) {
+                       regfree(&preg);
+                       tokenbuf_free(&tmp);
+                       tokenbuf_free(&mydata);
+                       return VAR_ERR_OUT_OF_MEMORY;
+                   }
+                   break;
+               }
+           }
+       }
+       regfree(&preg);
+       tokenbuf_free(data);
+       tokenbuf_move(&tmp, data);
+       tokenbuf_free(&mydata);
+    }
+
+    return VAR_OK;
+}
+
+/* operation: offset substring */
+static int 
+op_offset(
+    var_t *var, var_parse_t *ctx,
+    tokenbuf_t *data, 
+    int num1,
+    int num2, 
+    int isrange)
+{
+    tokenbuf_t res;
+    const char *p;
+
+    /* determine begin of result string */
+    if ((data->end - data->begin) < num1)
+       return VAR_ERR_OFFSET_OUT_OF_BOUNDS;
+    p = data->begin + num1;
+
+    /* if num2 is zero, we copy the rest from there. */
+    if (num2 == 0) {
+       if (!tokenbuf_assign(&res, p, data->end - p))
+           return VAR_ERR_OUT_OF_MEMORY;
+    } else {
+       /* ok, then use num2. */
+       if (isrange) {
+           if ((p + num2) > data->end)
+               return VAR_ERR_RANGE_OUT_OF_BOUNDS;
+           if (!tokenbuf_assign(&res, p, num2))
+               return VAR_ERR_OUT_OF_MEMORY;
+       } else {
+           if (num2 < num1)
+               return VAR_ERR_OFFSET_LOGIC;
+           if ((data->begin + num2) > data->end)
+               return VAR_ERR_RANGE_OUT_OF_BOUNDS;
+           if (!tokenbuf_assign(&res, p, num2 - num1 + 1))
+               return VAR_ERR_OUT_OF_MEMORY;
+       }
+    }
+    tokenbuf_free(data);
+    tokenbuf_move(&res, data);
+    return VAR_OK;
+}
+
+/* operation: padding */
+static int 
+op_padding(
+    var_t *var, var_parse_t *ctx,
+    tokenbuf_t *data, 
+    int width, 
+    tokenbuf_t *fill,
+    char position)
+{
+    tokenbuf_t result;
+    int i;
+
+    if (fill->begin == fill->end)
+       return VAR_ERR_EMPTY_PADDING_FILL_STRING;
+    tokenbuf_init(&result);
+    if (position == 'l') {
+       /* left padding */
+       i = width - (data->end - data->begin);
+       if (i > 0) {
+           i = i / (fill->end - fill->begin);
+           while (i > 0) {
+               if (!tokenbuf_append(data, fill->begin, fill->end - fill->begin))
+                   return VAR_ERR_OUT_OF_MEMORY;
+               i--;
+           }
+           i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
+           if (!tokenbuf_append(data, fill->begin, i))
+               return VAR_ERR_OUT_OF_MEMORY;
+       }
+    } else if (position == 'r') {
+       /* right padding */
+       i = width - (data->end - data->begin);
+       if (i > 0) {
+           i = i / (fill->end - fill->begin);
+           while (i > 0) {
+               if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
+                   tokenbuf_free(&result);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               i--;
+           }
+           i = (width - (data->end - data->begin)) % (fill->end - fill->begin);
+           if (!tokenbuf_append(&result, fill->begin, i)) {
+               tokenbuf_free(&result);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) {
+               tokenbuf_free(&result);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           /* move string from temporary buffer to data buffer */
+           tokenbuf_free(data);
+           tokenbuf_move(&result, data);
+       }
+    } else if (position == 'c') {
+       /* centered padding */
+       i = (width - (data->end - data->begin)) / 2;
+       if (i > 0) {
+           /* create the prefix */
+           i = i / (fill->end - fill->begin);
+           while (i > 0) {
+               if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
+                   tokenbuf_free(&result);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               i--;
+           }
+           i = ((width - (data->end - data->begin)) / 2)
+               % (fill->end - fill->begin);
+           if (!tokenbuf_append(&result, fill->begin, i)) {
+               tokenbuf_free(&result);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           /* append the actual data string */
+           if (!tokenbuf_append(&result, data->begin, data->end - data->begin)) {
+               tokenbuf_free(&result);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           /* append the suffix */
+           i = width - (result.end - result.begin);
+           i = i / (fill->end - fill->begin);
+           while (i > 0) {
+               if (!tokenbuf_append(&result, fill->begin, fill->end - fill->begin)) {
+                   tokenbuf_free(&result);
+                   return VAR_ERR_OUT_OF_MEMORY;
+               }
+               i--;
+           }
+           i = width - (result.end - result.begin);
+           if (!tokenbuf_append(&result, fill->begin, i)) {
+               tokenbuf_free(&result);
+               return VAR_ERR_OUT_OF_MEMORY;
+           }
+           /* move string from temporary buffer to data buffer */
+           tokenbuf_free(data);
+           tokenbuf_move(&result, data);
+       }
+    }
+    return VAR_OK;
+}
+
+/* parse an integer number ("123") */
+static int 
+parse_integer(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    int *result)
+{
+    const char *p;
+    int num;
+
+    p = begin;
+    num = 0;
+    while (isdigit(*p) && p != end) {
+       num *= 10;
+        num += (*p - '0');
+       p++;
+    }
+    if (result != NULL)
+       *result = num;
+    return (p - begin);
+}
+
+/* parse an operation (":x...") */
+static int 
+parse_operation(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *data)
+{
+    const char *p;
+    tokenbuf_t tmptokbuf;
+    tokenbuf_t search, replace, flags;
+    tokenbuf_t number1, number2;
+    int num1, num2;
+    int isrange;
+    int rc;
+    char *ptr;
+
+    /* initialization */
+    tokenbuf_init(&tmptokbuf);
+    tokenbuf_init(&search);
+    tokenbuf_init(&replace);
+    tokenbuf_init(&flags);
+    tokenbuf_init(&number1);
+    tokenbuf_init(&number2);
+    p = begin;
+    if (p == end)
+       return 0;
+
+    /* dispatch through the first operation character */
+    switch (tolower(*p)) {
+        case 'l': {
+           /* turn value to lowercase. */
+           if (data->begin != NULL) {
+               /* if the buffer does not live in an allocated buffer,
+                  we have to copy it before modifying the contents. */
+               if (data->buffer_size == 0) {
+                   if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
+                       rc = VAR_ERR_OUT_OF_MEMORY;
+                       goto error_return;
+                   }
+               }
+               /* convert value */
+               for (ptr = (char *)data->begin; ptr != data->end; ptr++)
+                   *ptr = (char)tolower((int)(*ptr));
+           }
+           p++;
+           break;
+       }
+        case 'u': {
+           /* turn value to uppercase. */
+           if (data->begin != NULL) {
+               /* if the buffer does not live in an allocated buffer,
+                  we have to copy it before modifying the contents. */
+               if (data->buffer_size == 0) {
+                   if (!tokenbuf_assign(data, data->begin, data->end - data->begin)) {
+                       rc = VAR_ERR_OUT_OF_MEMORY;
+                       goto error_return;
+                   }
+               }
+               /* convert value */
+               for (ptr = (char *)data->begin; ptr != data->end; ptr++)
+                   *ptr = (char)toupper((int)(*ptr));
+           }
+           p++;
+           break;
+       }
+        case 'o': {
+           /* cut out substring of value. */
+           p++;
+           rc = parse_integer(var, ctx, p, end, &num1);
+           if (rc == 0) {
+               rc = VAR_ERR_MISSING_START_OFFSET;
+               goto error_return;
+           }
+           else if (rc < 0)
+               goto error_return;
+           p += rc;
+            if (*p == ',') {
+               isrange = 0;
+               p++;
+            } else if (*p == '-') {
+               isrange = 1;
+               p++;
+           } else {
+               rc = VAR_ERR_INVALID_OFFSET_DELIMITER;
+               goto error_return;
+           }
+           rc = parse_integer(var, ctx, p, end, &num2);
+           p += rc;
+           if (data->begin != NULL) {
+               rc = op_offset(var, ctx, data, num1, num2, isrange);
+               if (rc < 0)
+                   goto error_return;
+           }
+           break;
+       }
+        case '#': {
+           /* determine length of the value */
+           if (data->begin != NULL) {
+               char buf[((sizeof(int)*8)/3)+10]; /* sufficient size: <#bits> x log_10(2) + safety */
+                sprintf(buf, "%d", (int)(data->end - data->begin));
+               tokenbuf_free(data);
+               if (!tokenbuf_assign(data, buf, strlen(buf))) {
+                   rc = VAR_ERR_OUT_OF_MEMORY;
+                   goto error_return;
+               }
+           }
+           p++;
+           break;
+       }
+        case '-': {
+           /* substitute parameter if data is empty */
+           p++;
+           rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
+           if (rc < 0)
+               goto error_return;
+           if (rc == 0) {
+               rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
+               goto error_return;
+           }
+           p += rc;
+           if (tokenbuf_isundef(data))
+               tokenbuf_move(&tmptokbuf, data);
+           else if (tokenbuf_isempty(data)) {
+               tokenbuf_free(data);
+               tokenbuf_move(&tmptokbuf, data);
+           }
+           break;
+       }
+        case '*': {
+           /* substitute empty string if data is not empty, parameter otherwise. */
+           p++;
+           rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
+           if (rc < 0)
+               goto error_return;
+           if (rc == 0) {
+               rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
+               goto error_return;
+           }
+           p += rc;
+           if (data->begin != NULL) {
+               if (data->begin == data->end) {
+                   tokenbuf_free(data);
+                   tokenbuf_move(&tmptokbuf, data);
+               } else {
+                   tokenbuf_free(data);
+                    data->begin = data->end = "";
+                   data->buffer_size = 0;
+               }
+           }
+           break;
+       }
+        case '+': {
+           /* substitute parameter if data is not empty. */
+           p++;
+           rc = parse_exptext_or_variable(var, ctx, p, end, &tmptokbuf);
+           if (rc < 0)
+               goto error_return;
+           if (rc == 0) {
+               rc = VAR_ERR_MISSING_PARAMETER_IN_COMMAND;
+               goto error_return;
+           }
+           p += rc;
+           if (data->begin != NULL && data->begin != data->end) {
+               tokenbuf_free(data);
+               tokenbuf_move(&tmptokbuf, data);
+           }
+           break;
+       }
+        case 's': {
+           /* search and replace. */
+           p++;
+            if (*p != '/')
+               return VAR_ERR_MALFORMATTED_REPLACE;
+           p++;
+           rc = parse_pattern(var, ctx, p, end);
+           if (rc < 0)
+               goto error_return;
+           tokenbuf_set(&search, p, p + rc, 0);
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_REPLACE;
+               goto error_return;
+           }
+           p++;
+           rc = parse_substext_or_variable(var, ctx, p, end, &replace);
+           if (rc < 0)
+               goto error_return;
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_REPLACE;
+               goto error_return;
+           }
+           p++;
+           rc = parse_exptext(var, ctx, p, end);
+           if (rc < 0)
+               goto error_return;
+           tokenbuf_set(&flags, p, p + rc, 0);
+           p += rc;
+           if (data->begin != NULL) {
+               rc = op_search_and_replace(var, ctx, data, &search, &replace, &flags);
+               if (rc < 0)
+                   goto error_return;
+           }
+           break;
+       }
+        case 'y': {
+           /* transpose characters from class A to class B. */
+           p++;
+            if (*p != '/')
+               return VAR_ERR_MALFORMATTED_TRANSPOSE;
+           p++;
+           rc = parse_substext_or_variable(var, ctx, p, end, &search);
+           if (rc < 0)
+               goto error_return;
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
+               goto error_return;
+           }
+           p++;
+           rc = parse_substext_or_variable(var, ctx, p, end, &replace);
+           if (rc < 0)
+               goto error_return;
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_TRANSPOSE;
+               goto error_return;
+           } else
+               p++;
+           if (data->begin) {
+               rc = op_transpose(var, ctx, data, &search, &replace);
+               if (rc < 0)
+                   goto error_return;
+           }
+           break;
+       }
+        case 'p': {
+           /* padding. */
+           p++;
+            if (*p != '/')
+               return VAR_ERR_MALFORMATTED_PADDING;
+           p++;
+           rc = parse_integer(var, ctx, p, end, &num1);
+           if (rc == 0) {
+               rc = VAR_ERR_MISSING_PADDING_WIDTH;
+               goto error_return;
+           }
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_PADDING;
+               goto error_return;
+           }
+           p++;
+           rc = parse_substext_or_variable(var, ctx, p, end, &replace);
+           if (rc < 0)
+               goto error_return;
+           p += rc;
+            if (*p != '/') {
+               rc = VAR_ERR_MALFORMATTED_PADDING;
+               goto error_return;
+           }
+           p++;
+            if (*p != 'l' && *p != 'c' && *p != 'r') {
+               rc = VAR_ERR_MALFORMATTED_PADDING;
+               goto error_return;
+           }
+           p++;
+           if (data->begin) {
+               rc = op_padding(var, ctx, data, num1, &replace, p[-1]);
+               if (rc < 0)
+                   goto error_return;
+           }
+           break;
+       }
+        case '%': {
+           /* operation callback function */
+           const char *op_ptr;
+           int op_len;
+           const char *arg_ptr;
+           int arg_len;
+           const char *val_ptr;
+           int val_len;
+           const char *out_ptr;
+           int out_len; 
+           int out_size;
+           tokenbuf_t args;
+
+           p++;
+           rc = parse_name(var, ctx, p, end);
+           if (rc < 0)
+               goto error_return;
+           op_ptr = p;
+           op_len = rc;
+           p += rc;
+            if (*p == '(') {
+               p++;
+               tokenbuf_init(&args);
+               rc = parse_opargtext_or_variable(var, ctx, p, end, &args);
+               if (rc < 0)
+                   goto error_return;
+               p += rc;
+               arg_ptr = args.begin;
+               arg_len = args.end - args.begin;
+                if (*p != ')') {
+                   rc = VAR_ERR_MALFORMED_OPERATION_ARGUMENTS;
+                   goto error_return;
+               }
+               p++;
+           }
+           else {
+               arg_ptr = NULL;
+               arg_len = 0;
+           }
+           val_ptr = data->begin;
+           val_len = data->end - data->begin;
+
+           if (data->begin != NULL && var->cb_operation_fct != NULL) {
+               /* call operation callback function */
+               rc = (*var->cb_operation_fct)(var, var->cb_operation_ctx,
+                                             op_ptr, op_len,
+                                             arg_ptr, arg_len,
+                                             val_ptr, val_len,
+                                             &out_ptr, &out_len, &out_size);
+               if (rc < 0)
+                   goto error_return;
+               tokenbuf_free(data);
+               tokenbuf_set(data, out_ptr, out_ptr+out_len, out_size);
+           }
+           break;
+       }
+       default:
+           return VAR_ERR_UNKNOWN_COMMAND_CHAR;
+    }
+
+    /* return successfully */
+    tokenbuf_free(&tmptokbuf);
+    tokenbuf_free(&search);
+    tokenbuf_free(&replace);
+    tokenbuf_free(&flags);
+    tokenbuf_free(&number1);
+    tokenbuf_free(&number2);
+    return (p - begin);
+
+    /* return with an error */
+    error_return:
+    tokenbuf_free(data);
+    tokenbuf_free(&tmptokbuf);
+    tokenbuf_free(&search);
+    tokenbuf_free(&replace);
+    tokenbuf_free(&flags);
+    tokenbuf_free(&number1);
+    tokenbuf_free(&number2);
+    return rc;
+}
+
+/* parse numerical expression operand */
+static int 
+parse_numexp_operand(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end, 
+    int *result, int *failed)
+{
+    const char *p;
+    tokenbuf_t tmp;
+    int rc;
+    var_parse_t myctx;
+
+    /* initialization */
+    p = begin;
+    tokenbuf_init(&tmp);
+    if (p == end)
+       return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+
+    /* parse opening numerical expression */
+    if (*p == '(') {
+       /* parse inner numerical expression */
+       rc = parse_numexp(var, ctx, ++p, end, result, failed);
+       if (rc < 0)
+           return rc;
+       p += rc;
+       if (p == end)
+           return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+       /* parse closing parenthesis */
+        if (*p != ')')
+           return VAR_ERR_UNCLOSED_BRACKET_IN_INDEX;
+       p++;
+    }
+    /* parse contained variable */
+    else if (*p == var->syntax.delim_init) {
+       /* parse variable with forced expansion */
+       ctx = var_parse_push(ctx, &myctx);
+       ctx->force_expand = 1;
+       rc = parse_variable(var, ctx, p, end, &tmp);
+       ctx = var_parse_pop(ctx);
+
+       if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
+           *failed = 1;
+           /* parse variable without forced expansion */
+           ctx = var_parse_push(ctx, &myctx);
+           ctx->force_expand = 0;
+           rc = parse_variable(var, ctx, p, end, &tmp);
+           ctx = var_parse_pop(ctx);
+           if (rc < 0)
+               return rc;
+           p += rc;
+           *result = 0;
+       }
+       else if (rc < 0)
+           return rc;
+       else {
+           p += rc;
+           /* parse remaining numerical expression */
+           rc = parse_numexp(var, ctx, tmp.begin, tmp.end, result, failed);
+           tokenbuf_free(&tmp);
+           if (rc < 0)
+               return rc;
+       }
+    }
+    /* parse relative index mark ("#") */
+    else if (  var->syntax.index_mark != EOS 
+            && *p == var->syntax.index_mark) {
+       p++;
+       *result = ctx->index_this;
+       if (ctx->rel_lookup_flag)
+           ctx->rel_lookup_cnt++;
+    }
+    /* parse plain integer number */
+    else if (isdigit(*p)) {
+       rc = parse_integer(var, ctx, p, end, result);
+       p += rc;
+    }
+    /* parse signed positive integer number */
+    else if (*p == '+') {
+       if ((end - p) > 1 && isdigit(p[1])) {
+           p++;
+           rc = parse_integer(var, ctx, p, end, result);
+           p += rc;
+       }
+       else
+           return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+    }
+    /* parse signed negative integer number */
+    else if (*p == '-') {
+       if (end - p > 1 && isdigit(p[1])) {
+           p++;
+           rc = parse_integer(var, ctx, p, end, result);
+           *result = -(*result);
+           p += rc;
+       }
+       else
+           return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+    }
+    /* else we failed to parse anything reasonable */
+    else
+       return VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+
+    return (p - begin);
+}
+
+/* parse numerical expression ("x+y") */
+static int 
+parse_numexp(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end, 
+    int *result, int *failed)
+{
+    const char *p;
+    char op;
+    int right;
+    int rc;
+
+    /* initialization */
+    p = begin;
+    if (p == end)
+       return VAR_ERR_INCOMPLETE_INDEX_SPEC;
+
+    /* parse left numerical operand */
+    rc = parse_numexp_operand(var, ctx, p, end, result, failed);
+    if (rc < 0)
+       return rc;
+    p += rc;
+
+    /* parse numerical operator */
+    while (p != end) {
+        if (*p == '+' || *p == '-') {
+           op = *p++;
+           /* recursively parse right operand (light binding) */
+           rc = parse_numexp(var, ctx, p, end, &right, failed);
+           if (rc < 0)
+               return rc;
+           p += rc;
+            if (op == '+')
+               *result = (*result + right);
+           else
+               *result = (*result - right);
+       }
+        else if (*p == '*' || *p == '/' || *p == '%') {
+           op = *p++;
+           /* recursively parse right operand (string binding) */
+           rc = parse_numexp_operand(var, ctx, p, end, &right, failed);
+           if (rc < 0)
+               return rc;
+           p += rc;
+            if (op == '*')
+               *result = (*result * right);
+            else if (op == '/') {
+               if (right == 0) {
+                   if (*failed)
+                       *result = 0;
+                   else
+                       return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
+               }
+               else
+                   *result = (*result / right);
+           }
+            else if (op == '%') {
+               if (right == 0) {
+                   if (*failed)
+                       *result = 0;
+                   else
+                       return VAR_ERR_DIVISION_BY_ZERO_IN_INDEX;
+               }
+               else
+                   *result = (*result % right);
+           }
+       }
+       else
+           break;
+    }
+
+    /* return amount of parsed input */
+    return (p - begin);
+}
+
+/* parse variable name ("abc") */
+static int 
+parse_name(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse as long as name class characters are found */
+    for (p = begin; p != end && var->syntax_nameclass[(int)(*p)]; p++)
+       ;
+    return (p - begin);
+}
+
+/* lookup a variable value through the callback function */
+static int 
+lookup_value(
+    var_t *var, var_parse_t *ctx,
+    const char *var_ptr, int  var_len, int var_inc, int var_idx,
+    const char **val_ptr, int *val_len, int *val_size)
+{
+    char buf[1];
+    int rc;
+
+    /* pass through to original callback */
+    rc = (*var->cb_value_fct)(var, var->cb_value_ctx,
+                             var_ptr, var_len, var_inc, var_idx, 
+                             val_ptr, val_len, val_size);
+
+    /* convert undefined variable into empty variable if relative
+       lookups are counted. This is the case inside an active loop
+       construct if no limits are given. There the parse_input()
+       has to proceed until all variables have undefined values. 
+       This trick here allows it to determine this case. */
+    if (ctx->rel_lookup_flag && rc == VAR_ERR_UNDEFINED_VARIABLE) {
+       ctx->rel_lookup_cnt--;
+       buf[0] = EOS;
+       *val_ptr  = buf;
+       *val_len  = 0;
+       *val_size = 0;
+       return VAR_OK;
+    }
+
+    return rc;
+}
+
+/* parse complex variable construct ("${name...}") */
+static int 
+parse_variable_complex(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p;
+    const char *data;
+    int len, buffer_size;
+    int failed = 0;
+    int rc;
+    int idx = 0;
+    int inc;   
+    tokenbuf_t name;
+    tokenbuf_t tmp;
+
+    /* initializations */
+    p = begin;
+    tokenbuf_init(&name);
+    tokenbuf_init(&tmp);
+    tokenbuf_init(result);
+
+    /* parse open delimiter */
+    if (p == end || *p != var->syntax.delim_open)
+       return 0;
+    p++;
+    if (p == end)
+       return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+
+    /* parse name of variable to expand. The name may consist of an
+       arbitrary number of variable name character and contained variable
+       constructs. */
+    do {
+       /* parse a variable name */
+       rc = parse_name(var, ctx, p, end);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           if (!tokenbuf_append(&name, p, rc)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+       }
+
+       /* parse an (embedded) variable */
+       rc = parse_variable(var, ctx, p, end, &tmp);
+       if (rc < 0)
+           goto error_return;
+       if (rc > 0) {
+           if (!tokenbuf_merge(&name, &tmp)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+       }
+    } while (rc > 0);
+
+    /* we must have the complete expanded variable name now, 
+       so make sure we really do. */
+    if (name.begin == name.end) {
+       if (ctx->force_expand) {
+           rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+           goto error_return;
+       }
+       else {
+           /* If no force_expand is requested, we have to back-off.
+               We're not sure whether our approach here is 100% correct,
+              because it _could_ have side-effects according to Peter
+              Simons, but as far as we know and tried it, it is
+              correct. But be warned -- RSE */
+           tokenbuf_set(result, begin - 1, p, 0);
+           goto goahead;
+       }
+    }
+
+    /* parse an optional index specification */
+    if (   var->syntax.index_open != EOS
+       && *p == var->syntax.index_open) {
+       p++;
+       rc = parse_numexp(var, ctx, p, end, &idx, &failed);
+       if (rc < 0)
+           goto error_return;
+       if (rc == 0) {
+           rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
+           goto error_return;
+       }
+       p += rc;
+       if (p == end) {
+           rc = VAR_ERR_INCOMPLETE_INDEX_SPEC;
+           goto error_return;
+       }
+       if (*p != var->syntax.index_close) {
+           rc = VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC;
+           goto error_return;
+       }
+       p++;
+    }
+
+    /* parse end of variable construct or start of post-operations */
+    if (p == end || (*p != var->syntax.delim_close && *p != ':' && *p != '+')) {
+       rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+       goto error_return;
+    }
+    inc = (*p == '+');                /* increment variable */
+    p++;
+
+    /* lookup the variable value now */
+    if (failed) {
+       tokenbuf_set(result, begin - 1, p, 0);
+    } else {
+       rc = lookup_value(var, ctx,
+                         name.begin, name.end-name.begin, inc, idx,
+                         &data, &len, &buffer_size);
+       if (rc == VAR_ERR_UNDEFINED_VARIABLE) {
+           tokenbuf_init(result); /* delayed handling of undefined variable */
+       } else if (rc < 0) {
+           goto error_return;
+       } else {
+           /* the preliminary result is the raw value of the variable.
+              This may be modified by the operations that may follow. */
+           tokenbuf_set(result, data, data + len, buffer_size);
+       }
+    }
+
+    /* parse optional post-operations */
+    goahead:
+    if (p[-1] == ':') {
+       tokenbuf_free(&tmp);
+       tokenbuf_init(&tmp);
+       p--;
+        while (p != end && *p == ':') {
+           p++;
+           if (!failed)
+               rc = parse_operation(var, ctx, p, end, result);
+           else
+               rc = parse_operation(var, ctx, p, end, &tmp);
+           if (rc < 0)
+               goto error_return;
+           p += rc;
+           if (failed)
+               result->end += rc;
+       }
+       if (p == end || *p != var->syntax.delim_close) {
+           rc = VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+           goto error_return;
+       }
+       p++;
+       if (failed)
+           result->end++;
+    } else if (p[-1] == '+') {
+       p++;
+    }
+
+    /* lazy handling of undefined variable */
+    if (!failed && tokenbuf_isundef(result)) {
+       if (ctx->force_expand) {
+           rc = VAR_ERR_UNDEFINED_VARIABLE;
+           goto error_return;
+       }
+       else
+           tokenbuf_set(result, begin - 1, p, 0);
+    }
+
+    /* return successfully */
+    tokenbuf_free(&name);
+    tokenbuf_free(&tmp);
+    return (p - begin);
+
+    /* return with an error */
+    error_return:
+    tokenbuf_free(&name);
+    tokenbuf_free(&tmp);
+    tokenbuf_free(result);
+    return rc;
+}
+
+/* parse variable construct ("$name" or "${name...}") */
+static int 
+parse_variable(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *result)
+{
+    const char *p;
+    const char *data;
+    int len, buffer_size;
+    int rc, rc2;
+    int inc;
+
+    /* initialization */
+    p = begin;
+    tokenbuf_init(result);
+
+    /* parse init delimiter */
+    if (p == end || *p != var->syntax.delim_init)
+       return 0;
+    p++;
+    if (p == end)
+       return VAR_ERR_INCOMPLETE_VARIABLE_SPEC;
+
+    /* parse a simple variable name. 
+       (if this fails, we're try to parse a complex variable construct) */
+    rc = parse_name(var, ctx, p, end);
+    if (rc < 0)
+       return rc;
+    if (rc > 0) {
+        inc = (p[rc] == '+');
+       rc2 = lookup_value(var, ctx, p, rc, inc, 0, &data, &len, &buffer_size);
+       if (rc2 == VAR_ERR_UNDEFINED_VARIABLE && !ctx->force_expand) {
+           tokenbuf_set(result, begin, begin + 1 + rc, 0);
+           return (1 + rc);
+       }
+       if (rc2 < 0)
+           return rc2;
+       tokenbuf_set(result, data, data + len, buffer_size);
+       return (1 + rc);
+    }
+
+    /* parse a complex variable construct (else case) */
+    rc = parse_variable_complex(var, ctx, p, end, result);
+    if (rc > 0)
+       rc++;
+    return rc;
+}
+
+/* parse loop construct limits ("[...]{b,s,e}") */
+static var_rc_t 
+parse_looplimits(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    int *start, int *step, int *stop, int *open_stop)
+{
+    const char *p;
+    int rc;
+    int failed;
+
+    /* initialization */
+    p = begin;
+
+    /* we are happy if nothing is to left to parse */
+    if (p == end)
+       return VAR_OK;
+
+    /* parse start delimiter */
+    if (*p != var->syntax.delim_open)
+       return VAR_OK;
+    p++;
+
+    /* parse loop start value */
+    failed = 0;
+    rc = parse_numexp(var, ctx, p, end, start, &failed);
+    if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
+       *start = 0; /* use default */
+    else if (rc < 0)
+       return (var_rc_t)rc;
+    else
+       p += rc;
+    if (failed)
+       return VAR_ERR_UNDEFINED_VARIABLE;
+
+    /* parse separator */
+    if (*p != ',')
+       return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
+    p++;
+
+    /* parse loop step value */
+    failed = 0;
+    rc = parse_numexp(var, ctx, p, end, step, &failed);
+    if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC)
+       *step = 1; /* use default */
+    else if (rc < 0)
+       return (var_rc_t)rc;
+    else
+       p += rc;
+    if (failed)
+       return VAR_ERR_UNDEFINED_VARIABLE;
+
+    /* parse separator */
+    if (*p != ',') {
+       /* if not found, parse end delimiter */
+       if (*p != var->syntax.delim_close)
+           return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
+       p++;
+
+       /* shift step value to stop value */
+       *stop = *step;
+       *step = 1;
+
+       /* determine whether loop end is open */
+       if (rc > 0)
+           *open_stop = 0;
+       else
+           *open_stop = 1;
+       return (var_rc_t)(p - begin);
+    }
+    p++;
+
+    /* parse loop stop value */
+    failed = 0;
+    rc = parse_numexp(var, ctx, p, end, stop, &failed);
+    if (rc == VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC) {
+       *stop = 0; /* use default */
+       *open_stop = 1;
+    }
+    else if (rc < 0)
+       return (var_rc_t)rc;
+    else {
+       *open_stop = 0;
+       p += rc;
+    }
+    if (failed)
+       return VAR_ERR_UNDEFINED_VARIABLE;
+
+    /* parse end delimiter */
+    if (*p != var->syntax.delim_close)
+       return VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS;
+    p++;
+
+    /* return amount of parsed input */
+    return (var_rc_t)(p - begin);
+}
+
+/* parse plain text */
+static int 
+parse_text(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end)
+{
+    const char *p;
+
+    /* parse until delim_init (variable construct) 
+       or index_open (loop construct) is found */
+    for (p = begin; p != end; p++) {
+       if (*p == var->syntax.escape) {
+           p++; /* skip next character */
+           if (p == end)
+               return VAR_ERR_INCOMPLETE_QUOTED_PAIR;
+       }
+       else if (*p == var->syntax.delim_init)
+           break;
+       else if (   var->syntax.index_open != EOS
+                && (   *p == var->syntax.index_open 
+                    || *p == var->syntax.index_close))
+           break;
+    }
+    return (p - begin);
+}
+
+/* expand input in general */
+static var_rc_t 
+parse_input(
+    var_t *var, var_parse_t *ctx,
+    const char *begin, const char *end,
+    tokenbuf_t *output, int recursion_level)
+{
+    const char *p;
+    int rc, rc2;
+    tokenbuf_t result;
+    int start, step, stop, open_stop;
+    int i;
+    int output_backup;
+    int rel_lookup_cnt;
+    int loop_limit_length;
+    var_parse_t myctx;
+
+    /* initialization */
+    p = begin;
+
+    do {
+       /* try to parse a loop construct */
+       if (   p != end 
+           && var->syntax.index_open != EOS 
+           && *p == var->syntax.index_open) {
+           p++;
+
+           /* loop preparation */
+           loop_limit_length = -1;
+           rel_lookup_cnt = ctx->rel_lookup_cnt;
+           open_stop = 1;
+           rc = 0;
+           start = 0;
+           step  = 1;
+           stop  = 0;
+           output_backup = 0;
+
+           /* iterate over loop construct, either as long as there is
+              (still) nothing known about the limit, or there is an open
+              (=unknown) limit stop and there are still defined variables
+              or there is a stop limit known and it is still not reached */
+           re_loop:
+           for (i = start;
+                (   (   open_stop 
+                     && (   loop_limit_length < 0 
+                         || rel_lookup_cnt > ctx->rel_lookup_cnt)) 
+                 || (   !open_stop 
+                     && i <= stop)                                );
+                i += step) {
+
+               /* remember current output end for restoring */
+               output_backup = (output->end - output->begin);
+
+               /* open temporary context for recursion */ 
+               ctx = var_parse_push(ctx, &myctx);
+               ctx->force_expand    = 1;
+               ctx->rel_lookup_flag = 1;
+               ctx->index_this      = i;
+
+               /* recursive parse input through ourself */
+               rc = parse_input(var, ctx, p, end, 
+                                output, recursion_level+1);
+
+               /* retrieve info and close temporary context */
+               rel_lookup_cnt = ctx->rel_lookup_cnt;
+               ctx = var_parse_pop(ctx);
+
+               /* error handling */
+               if (rc < 0)
+                   goto error_return;
+
+               /* make sure the loop construct is closed */
+               if (p[rc] != var->syntax.index_close) {
+                   rc = VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT;
+                   goto error_return;
+               }
+
+               /* try to parse loop construct limit specification */
+               if (loop_limit_length < 0) {
+                   rc2 = parse_looplimits(var, ctx, p+rc+1, end, 
+                                          &start, &step, &stop, &open_stop);
+                   if (rc2 < 0)
+                       goto error_return;
+                   else if (rc2 == 0)
+                       loop_limit_length = 0;
+                   else if (rc2 > 0) {
+                       loop_limit_length = rc2;
+                       /* restart loop from scratch */
+                       output->end = (output->begin + output_backup);
+                       goto re_loop;
+                   }
+               }
+           }
+
+           /* if stop value is open, restore to the output end
+              because the last iteration was just to determine the loop
+              termination and its result has to be discarded */
+           if (open_stop)
+               output->end = (output->begin + output_backup);
+
+           /* skip parsed loop construct */
+           p += rc;
+           p++;
+           p += loop_limit_length;
+
+           continue;
+       }
+
+       /* try to parse plain text */
+       rc = parse_text(var, ctx, p, end);
+       if (rc > 0) {
+           if (!tokenbuf_append(output, p, rc)) {
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           p += rc;
+           continue;
+       } else if (rc < 0)
+           goto error_return;
+
+       /* try to parse a variable construct */
+       tokenbuf_init(&result);
+       rc = parse_variable(var, ctx, p, end, &result);
+       if (rc > 0) {
+           if (!tokenbuf_merge(output, &result)) {
+               tokenbuf_free(&result);
+               rc = VAR_ERR_OUT_OF_MEMORY;
+               goto error_return;
+           }
+           tokenbuf_free(&result);
+           p += rc;
+           continue;
+       }    
+       tokenbuf_free(&result);
+       if (rc < 0)
+           goto error_return;
+
+    } while (p != end && rc > 0);
+
+    /* We do not know whether this really could happen, but because we
+       are paranoid, report an error at the outer most parsing level if
+       there is still any input. Because this would mean that we are no
+       longer able to parse the remaining input as a loop construct, a
+       text or a variable construct. This would be very strange, but
+       could perhaps happen in case of configuration errors!?... */
+    if (recursion_level == 0 && p != end) {
+       rc = VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE;
+       goto error_return;
+    }
+
+    /* return amount of parsed text */
+    return (var_rc_t)(p - begin);
+
+    /* return with an error where as a special case the output begin is
+       set to the input begin and the output end to the last input parsing
+       position. */
+    error_return:
+    tokenbuf_free(output);
+    tokenbuf_set(output, begin, p, 0);
+    return (var_rc_t)rc;
+}
+
+/* 
+**
+**  ==== APPLICATION PROGRAMMING INTERFACE (API) ====
+**
+*/
+
+/* create variable expansion context */
+var_rc_t 
+var_create(
+    var_t **pvar)
+{
+    var_t *var;
+
+    if (pvar == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    if ((var = (var_t *)malloc(sizeof(var_t))) == NULL)
+       return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
+    memset(var, 0, sizeof(var));
+    var_config(var, VAR_CONFIG_SYNTAX, &var_syntax_default);
+    *pvar = var;
+    return VAR_OK;
+}
+
+/* destroy variable expansion context */
+var_rc_t 
+var_destroy(
+    var_t *var)
+{
+    if (var == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    free(var);
+    return VAR_OK;
+}
+
+/* configure variable expansion context */
+var_rc_t 
+var_config(
+    var_t *var, 
+    var_config_t mode, 
+    ...)
+{
+    va_list ap;
+    var_rc_t rc;
+
+    if (var == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    va_start(ap, mode);
+    switch (mode) {
+       case VAR_CONFIG_SYNTAX: {
+           var_syntax_t *s;
+           s = (var_syntax_t *)va_arg(ap, void *);
+           if (s == NULL)
+               return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+           var->syntax.escape      = s->escape;
+           var->syntax.delim_init  = s->delim_init;
+           var->syntax.delim_open  = s->delim_open;
+           var->syntax.delim_close = s->delim_close;
+           var->syntax.index_open  = s->index_open;
+           var->syntax.index_close = s->index_close;
+           var->syntax.index_mark  = s->index_mark;
+           var->syntax.name_chars  = NULL; /* unused internally */
+           if ((rc = expand_character_class(s->name_chars, var->syntax_nameclass)) != VAR_OK)
+               return VAR_RC(rc);
+           if (   var->syntax_nameclass[(int)var->syntax.delim_init] 
+               || var->syntax_nameclass[(int)var->syntax.delim_open]
+               || var->syntax_nameclass[(int)var->syntax.delim_close]
+               || var->syntax_nameclass[(int)var->syntax.escape])
+               return VAR_RC(VAR_ERR_INVALID_CONFIGURATION);
+           break;
+       }
+       case VAR_CONFIG_CB_VALUE: {
+           var_cb_value_t fct;
+           void *ctx;
+           fct = (var_cb_value_t)va_arg(ap, void *);
+           ctx = (void *)va_arg(ap, void *);
+           var->cb_value_fct = fct;
+           var->cb_value_ctx = ctx;
+           break;
+       }
+       case VAR_CONFIG_CB_OPERATION: {
+           var_cb_operation_t fct;
+           void *ctx;
+           fct = (var_cb_operation_t)va_arg(ap, void *);
+           ctx = (void *)va_arg(ap, void *);
+           var->cb_operation_fct = fct;
+           var->cb_operation_ctx = ctx;
+           break;
+       }
+       default:
+           return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    }
+    va_end(ap);
+    return VAR_OK;
+}
+
+/* perform unescape operation on a buffer */
+var_rc_t 
+var_unescape(
+    var_t *var, 
+    const char *src, int srclen, 
+    char *dst, int dstlen, 
+    int all)
+{
+    const char *end;
+    var_rc_t rc;
+
+    if (var == NULL || src == NULL || dst == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+    end = src + srclen;
+    while (src < end) {
+        if (*src == '\\') {
+           if (++src == end)
+               return VAR_RC(VAR_ERR_INCOMPLETE_NAMED_CHARACTER);
+           switch (*src) {
+                case '\\':
+                   if (!all) {
+                        *dst++ = '\\';
+                   }
+                    *dst++ = '\\';
+                   break;
+                case 'n':
+                    *dst++ = '\n';
+                   break;
+                case 't':
+                    *dst++ = '\t';
+                   break;
+                case 'r':
+                    *dst++ = '\r';
+                   break;
+                case 'x':
+                   ++src;
+                   if ((rc = expand_hex(&src, &dst, end)) != VAR_OK)
+                       return VAR_RC(rc);
+                   break;
+                case '0': case '1': case '2': case '3': case '4':
+                case '5': case '6': case '7': case '8': case '9':
+                   if (   end - src >= 3 
+                       && isdigit((int)src[1]) 
+                       && isdigit((int)src[2])) {
+                       if ((rc = expand_octal(&src, &dst, end)) != 0)
+                           return VAR_RC(rc);
+                       break;
+                   }
+               default:
+                   if (!all) {
+                        *dst++ = '\\';
+                   }
+                   *dst++ = *src;
+           }
+           ++src;
+       } else
+           *dst++ = *src++;
+    }
+    *dst = EOS;
+    return VAR_OK;
+}
+
+/* perform expand operation on a buffer */
+var_rc_t 
+var_expand(
+    var_t *var, 
+    const char *src_ptr, int src_len, 
+    char **dst_ptr, int *dst_len, 
+    int force_expand)
+{
+    var_parse_t ctx;
+    tokenbuf_t output;
+    var_rc_t rc;
+
+    /* argument sanity checks */
+    if (var == NULL || src_ptr == NULL || src_len == 0 || dst_ptr == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+
+    /* prepare internal expansion context */
+    ctx.lower          = NULL;
+    ctx.force_expand   = force_expand;
+    ctx.rel_lookup_flag = 0;
+    ctx.rel_lookup_cnt = 0;
+    ctx.index_this     = 0;
+
+    /* start the parsing */
+    tokenbuf_init(&output);
+    rc = parse_input(var, &ctx, src_ptr, src_ptr+src_len, &output, 0);
+
+    /* post-processing */
+    if (rc >= 0) {
+       /* always EOS-terminate output for convinience reasons 
+          but do not count the EOS-terminator in the length */
+        if (!tokenbuf_append(&output, "\0", 1)) {
+           tokenbuf_free(&output);
+           return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
+       }
+       output.end--;
+
+       /* provide result */
+       *dst_ptr = (char *)output.begin;
+       if (dst_len != NULL)
+           *dst_len = (output.end - output.begin);
+       rc = VAR_OK;
+    }
+    else {
+       /* provide result */
+       if (dst_len != NULL)
+           *dst_len = (output.end - output.begin);
+    }
+
+    return VAR_RC(rc);
+}
+
+/* format and expand a string */
+var_rc_t 
+var_formatv(
+    var_t *var, 
+    char **dst_ptr, int force_expand, 
+    const char *fmt, va_list ap)
+{
+    var_rc_t rc;
+    va_list apbak;
+    char *cpBuf;
+    int nBuf;
+
+    /* argument sanity checks */
+    if (var == NULL || dst_ptr == NULL || fmt == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+
+    /* determine formatting buffer length */
+    apbak = ap;
+    nBuf = var_mvsnprintf(NULL, 0, fmt, ap);
+    ap = apbak;
+    if (nBuf == -1)
+       return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
+
+    /* perform formatting */
+    if ((cpBuf = (char *)malloc(nBuf+1)) == NULL)
+       return VAR_RC(VAR_ERR_OUT_OF_MEMORY);
+    nBuf = var_mvsnprintf(cpBuf, nBuf+1, fmt, ap);
+    if (nBuf == -1) {
+       free(cpBuf);
+       return VAR_RC(VAR_ERR_FORMATTING_FAILURE);
+    }
+
+    /* perform expansion */
+    if ((rc = var_expand(var, cpBuf, nBuf, dst_ptr, NULL, force_expand)) != VAR_OK) {
+       free(cpBuf);
+       return VAR_RC(rc);
+    }
+
+    /* cleanup */
+    free(cpBuf);
+
+    return VAR_OK;
+}
+
+/* format and expand a string */
+var_rc_t 
+var_format(
+    var_t *var, 
+    char **dst_ptr, int force_expand, 
+    const char *fmt, ...)
+{
+    var_rc_t rc;
+    va_list ap;
+
+    /* argument sanity checks */
+    if (var == NULL || dst_ptr == NULL || fmt == NULL)
+       return VAR_RC(VAR_ERR_INVALID_ARGUMENT);
+
+    va_start(ap, fmt);
+    rc = var_formatv(var, dst_ptr, force_expand, fmt, ap);
+    va_end(ap);
+
+    return VAR_RC(rc);
+}
+
+/* var_rc_t to string mapping table */
+static const char *var_errors[] = {
+    "everything ok",                                           /* VAR_OK = 0 */
+    "incomplete named character",                              /* VAR_ERR_INCOMPLETE_NAMED_CHARACTER */
+    "incomplete hexadecimal value",                            /* VAR_ERR_INCOMPLETE_HEX */
+    "invalid hexadecimal value",                               /* VAR_ERR_INVALID_HEX */
+    "octal value too large",                                   /* VAR_ERR_OCTAL_TOO_LARGE */
+    "invalid octal value",                                     /* VAR_ERR_INVALID_OCTAL */
+    "incomplete octal value",                                  /* VAR_ERR_INCOMPLETE_OCTAL */
+    "incomplete grouped hexadecimal value",                    /* VAR_ERR_INCOMPLETE_GROUPED_HEX */
+    "incorrect character class specification",                 /* VAR_ERR_INCORRECT_CLASS_SPEC */
+    "invalid expansion configuration",                         /* VAR_ERR_INVALID_CONFIGURATION */
+    "out of memory",                                           /* VAR_ERR_OUT_OF_MEMORY */
+    "incomplete variable specification",                       /* VAR_ERR_INCOMPLETE_VARIABLE_SPEC */
+    "undefined variable",                                      /* VAR_ERR_UNDEFINED_VARIABLE */
+    "input is neither text nor variable",                      /* VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE */
+    "unknown command character in variable",                   /* VAR_ERR_UNKNOWN_COMMAND_CHAR */
+    "malformatted search and replace operation",               /* VAR_ERR_MALFORMATTED_REPLACE */
+    "unknown flag in search and replace operation",            /* VAR_ERR_UNKNOWN_REPLACE_FLAG */
+    "invalid regex in search and replace operation",           /* VAR_ERR_INVALID_REGEX_IN_REPLACE */
+    "missing parameter in command",                            /* VAR_ERR_MISSING_PARAMETER_IN_COMMAND */
+    "empty search string in search and replace operation",     /* VAR_ERR_EMPTY_SEARCH_STRING */
+    "start offset missing in cut operation",                   /* VAR_ERR_MISSING_START_OFFSET */
+    "offsets in cut operation delimited by unknown character", /* VAR_ERR_INVALID_OFFSET_DELIMITER */
+    "range out of bounds in cut operation",                    /* VAR_ERR_RANGE_OUT_OF_BOUNDS */
+    "offset out of bounds in cut operation",                   /* VAR_ERR_OFFSET_OUT_OF_BOUNDS */
+    "logic error in cut operation",                            /* VAR_ERR_OFFSET_LOGIC */
+    "malformatted transpose operation",                        /* VAR_ERR_MALFORMATTED_TRANSPOSE */
+    "source and target class mismatch in transpose operation", /* VAR_ERR_TRANSPOSE_CLASSES_MISMATCH */
+    "empty character class in transpose operation",            /* VAR_ERR_EMPTY_TRANSPOSE_CLASS */
+    "incorrect character class in transpose operation",        /* VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC */
+    "malformatted padding operation",                          /* VAR_ERR_MALFORMATTED_PADDING */
+    "width parameter missing in padding operation",            /* VAR_ERR_MISSING_PADDING_WIDTH */
+    "fill string missing in padding operation",                /* VAR_ERR_EMPTY_PADDING_FILL_STRING */
+    "unknown quoted pair in search and replace operation",     /* VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE */
+    "sub-matching reference out of range",                     /* VAR_ERR_SUBMATCH_OUT_OF_RANGE */
+    "invalid argument",                                        /* VAR_ERR_INVALID_ARGUMENT */
+    "incomplete quoted pair",                                  /* VAR_ERR_INCOMPLETE_QUOTED_PAIR */
+    "lookup function does not support variable arrays",        /* VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED */
+    "index of array variable contains an invalid character",   /* VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC */
+    "index of array variable is incomplete",                   /* VAR_ERR_INCOMPLETE_INDEX_SPEC */
+    "bracket expression in array variable's index not closed", /* VAR_ERR_UNCLOSED_BRACKET_IN_INDEX */
+    "division by zero error in index specification",           /* VAR_ERR_DIVISION_BY_ZERO_IN_INDEX */
+    "unterminated loop construct",                             /* VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT */
+    "invalid character in loop limits",                        /* VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS */
+    "malformed operation argument list",                       /* VAR_ERR_MALFORMED_OPERATION_ARGUMENTS */
+    "undefined operation",                                     /* VAR_ERR_UNDEFINED_OPERATION */
+    "formatting failure"                                       /* VAR_ERR_FORMATTING_FAILURE */
+};
+
+/* translate a return code into its corresponding descriptive text */
+char *var_strerror(var_t *var, var_rc_t rc)
+{
+    char *str;
+    rc = (var_rc_t)(0 - rc);
+    if (rc < 0 || rc >= (int)sizeof(var_errors) / (int)sizeof(char *)) {
+        str = "unknown error";
+    } else {
+       str = (char *)var_errors[rc];
+    }
+    return str;
+}
diff --git a/bacula/src/lib/var.h b/bacula/src/lib/var.h
new file mode 100644 (file)
index 0000000..5bba30f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+**  OSSP var - Variable Expansion
+**  Copyright (c) 2001-2002 Ralf S. Engelschall <rse@engelschall.com>
+**  Copyright (c) 2001-2002 The OSSP Project (http://www.ossp.org/)
+**  Copyright (c) 2001-2002 Cable & Wireless Deutschland (http://www.cw.com/de/)
+**
+**  This file is part of OSSP var, a variable expansion
+**  library which can be found at http://www.ossp.org/pkg/lib/var/.
+**
+**  Permission to use, copy, modify, and distribute this software for
+**  any purpose with or without fee is hereby granted, provided that
+**  the above copyright notice and this permission notice appear in all
+**  copies.
+**
+**  For disclaimer see below.
+*/
+/*
+ * Modified for use with Bacula by Kern Sibbald, June 2003
+ */
+/*
+   Copyright (C) 2000-2003 Kern Sibbald and John Walker
+
+   This program is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+   General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
+   MA 02111-1307, USA.
+
+ */
+
+
+
+#ifndef __VAR_H__
+#define __VAR_H__
+
+/* Error codes */
+typedef enum {
+    VAR_ERR_CALLBACK                        = -64,
+    VAR_ERR_FORMATTING_FAILURE              = -45,
+    VAR_ERR_UNDEFINED_OPERATION             = -44,
+    VAR_ERR_MALFORMED_OPERATION_ARGUMENTS   = -43,
+    VAR_ERR_INVALID_CHAR_IN_LOOP_LIMITS     = -42,
+    VAR_ERR_UNTERMINATED_LOOP_CONSTRUCT     = -41,
+    VAR_ERR_DIVISION_BY_ZERO_IN_INDEX       = -40,
+    VAR_ERR_UNCLOSED_BRACKET_IN_INDEX       = -39,
+    VAR_ERR_INCOMPLETE_INDEX_SPEC           = -37,
+    VAR_ERR_INVALID_CHAR_IN_INDEX_SPEC      = -36,
+    VAR_ERR_ARRAY_LOOKUPS_ARE_UNSUPPORTED   = -35,
+    VAR_ERR_INCOMPLETE_QUOTED_PAIR          = -34,
+    VAR_ERR_INVALID_ARGUMENT                = -34,
+    VAR_ERR_SUBMATCH_OUT_OF_RANGE           = -33,
+    VAR_ERR_UNKNOWN_QUOTED_PAIR_IN_REPLACE  = -32,
+    VAR_ERR_EMPTY_PADDING_FILL_STRING       = -31,
+    VAR_ERR_MISSING_PADDING_WIDTH           = -30,
+    VAR_ERR_MALFORMATTED_PADDING            = -29,
+    VAR_ERR_INCORRECT_TRANSPOSE_CLASS_SPEC  = -28,
+    VAR_ERR_EMPTY_TRANSPOSE_CLASS           = -27,
+    VAR_ERR_TRANSPOSE_CLASSES_MISMATCH      = -26,
+    VAR_ERR_MALFORMATTED_TRANSPOSE          = -25,
+    VAR_ERR_OFFSET_LOGIC                    = -24,
+    VAR_ERR_OFFSET_OUT_OF_BOUNDS            = -23,
+    VAR_ERR_RANGE_OUT_OF_BOUNDS             = -22,
+    VAR_ERR_INVALID_OFFSET_DELIMITER        = -21,
+    VAR_ERR_MISSING_START_OFFSET            = -20,
+    VAR_ERR_EMPTY_SEARCH_STRING             = -19,
+    VAR_ERR_MISSING_PARAMETER_IN_COMMAND    = -18,
+    VAR_ERR_INVALID_REGEX_IN_REPLACE        = -17,
+    VAR_ERR_UNKNOWN_REPLACE_FLAG            = -16,
+    VAR_ERR_MALFORMATTED_REPLACE            = -15,
+    VAR_ERR_UNKNOWN_COMMAND_CHAR            = -14,
+    VAR_ERR_INPUT_ISNT_TEXT_NOR_VARIABLE    = -13,
+    VAR_ERR_UNDEFINED_VARIABLE              = -12,
+    VAR_ERR_INCOMPLETE_VARIABLE_SPEC        = -11,
+    VAR_ERR_OUT_OF_MEMORY                   = -10,
+    VAR_ERR_INVALID_CONFIGURATION           =  -9,
+    VAR_ERR_INCORRECT_CLASS_SPEC            =  -8,
+    VAR_ERR_INCOMPLETE_GROUPED_HEX          =  -7,
+    VAR_ERR_INCOMPLETE_OCTAL                =  -6,
+    VAR_ERR_INVALID_OCTAL                   =  -5,
+    VAR_ERR_OCTAL_TOO_LARGE                 =  -4,
+    VAR_ERR_INVALID_HEX                     =  -3,
+    VAR_ERR_INCOMPLETE_HEX                  =  -2,
+    VAR_ERR_INCOMPLETE_NAMED_CHARACTER      =  -1,
+    VAR_OK                                  =   0
+} var_rc_t;
+
+struct var_st;
+typedef struct var_st var_t;
+
+typedef enum {
+    VAR_CONFIG_SYNTAX,
+    VAR_CONFIG_CB_VALUE,
+    VAR_CONFIG_CB_OPERATION
+} var_config_t;
+
+typedef struct {
+    char  escape;       /* default: '\' */
+    char  delim_init;   /* default: '$' */
+    char  delim_open;   /* default: '{' */
+    char  delim_close;  /* default: '}' */
+    char  index_open;   /* default: '[' */
+    char  index_close;  /* default: ']' */
+    char  index_mark;   /* default: '#' */
+    char *name_chars;   /* default: "a-zA-Z0-9_" */
+} var_syntax_t;
+
+typedef var_rc_t (*var_cb_value_t)(
+    var_t *var, void *ctx,
+    const char  *var_ptr, int     var_len, int var_inc, int var_idx,
+    const char **val_ptr, int    *val_len, int    *val_size
+);
+
+typedef var_rc_t (*var_cb_operation_t)(
+    var_t *var, void *ctx,
+    const char  *op_ptr,  int    op_len,
+    const char  *arg_ptr, int    arg_len,
+    const char  *val_ptr, int    val_len,
+    const char **out_ptr, int    *out_len, int *out_size
+);
+
+
+var_rc_t  var_create    (var_t **var);
+var_rc_t  var_destroy   (var_t  *var);
+var_rc_t  var_config    (var_t  *var, var_config_t mode, ...);
+var_rc_t  var_unescape  (var_t  *var, const char *src_ptr, int src_len, char  *dst_ptr, int dst_len, int all);
+var_rc_t  var_expand    (var_t  *var, const char *src_ptr, int src_len, char **dst_ptr, int *dst_len, int force_expand);
+var_rc_t  var_formatv   (var_t  *var, char **dst_ptr, int force_expand, const char *fmt, va_list ap);
+var_rc_t  var_format    (var_t  *var, char **dst_ptr, int force_expand, const char *fmt, ...);
+char     *var_strerror  (var_t  *var, var_rc_t rc);            
+
+#endif /* __VAR_H__ */
index 98ac1eac9fc518d83971253ed62f5c9d23479042..5ea0884d76843b228d87d93868d93e94c95a4403 100644 (file)
@@ -108,7 +108,7 @@ int acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
 default_path:
         tape_previously_mounted = 1;
          Dmsg0(200, "dir_get_volume_info\n");
-        if (!dir_get_volume_info(jcr, 0)) { 
+        if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_READ)) { 
             Jmsg1(jcr, M_WARNING, 0, "%s", jcr->errmsg);
         }
         /* Call autochanger only once unless ask_sysop called */
@@ -186,7 +186,7 @@ DEVICE * acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
        *    dir_find_next_appendable_volume
        */
       pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName);
-      if (!dir_get_volume_info(jcr, 1) &&
+      if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE) &&
          !(dir_find_next_appendable_volume(jcr) &&
            strcmp(dev->VolHdr.VolName, jcr->VolumeName) == 0)) { /* wrong tape mounted */
         if (dev->num_writers != 0) {
@@ -274,14 +274,6 @@ int release_device(JCR *jcr, DEVICE *dev)
 
    } else if (dev->num_writers > 0) {
       dev->num_writers--;
-      if (dev->state & ST_TAPE) {
-        jcr->EndBlock = dev->EndBlock;
-        jcr->EndFile  = dev->EndFile;
-         Dmsg2(200, "Release device: EndFile=%u EndBlock=%u\n", jcr->EndFile, jcr->EndBlock);
-      } else {
-        jcr->EndBlock = (uint32_t)dev->file_addr;
-        jcr->EndFile = (uint32_t)(dev->file_addr >> 32);
-      }
       Dmsg1(100, "There are %d writers in release_device\n", dev->num_writers);
       if (dev->num_writers == 0) {
         /* If we have fully acquired the tape */
index dd81af9e25b6b6d8755d62c4a521e3f6a1211268..bac65e28619e260714945681b67886302ef07a9c 100644 (file)
@@ -66,6 +66,7 @@ int do_append_data(JCR *jcr)
    Dmsg1(20, "Begin append device=%s\n", dev_name(dev));
 
    block = new_block(dev);
+   memset(&rec, 0, sizeof(rec));
 
    /* 
     * Acquire output device for writing.  Note, after acquiring a
@@ -89,7 +90,6 @@ int do_append_data(JCR *jcr)
       ok = FALSE;
    }
 
-   memset(&rec, 0, sizeof(rec));
 
    /* 
     * Get Data from File daemon, write to device.  To clarify what is
@@ -109,7 +109,7 @@ int do_append_data(JCR *jcr)
     *  and 3. for the MD5 if any.
     */
    jcr->VolFirstIndex = 0;
-   time(&jcr->run_time);             /* start counting time for rates */
+   jcr->run_time = time(NULL);             /* start counting time for rates */
    for (last_file_index = 0; ok && !job_canceled(jcr); ) {
       char info[100];
 
@@ -129,7 +129,6 @@ int do_append_data(JCR *jcr)
         ok = FALSE;
         break;
       }
-      ds->msg[ds->msglen] = 0;
       if (sscanf(ds->msg, "%ld %ld %100s", &file_index, &stream, info) != 3) {
          Jmsg1(jcr, M_FATAL, 0, _("Malformed data header from FD: %s\n"), ds->msg);
         ok = FALSE;
@@ -148,6 +147,7 @@ int do_append_data(JCR *jcr)
         if (jcr->VolFirstIndex == 0) {
            jcr->VolFirstIndex = file_index;
         }
+        jcr->VolLastIndex = file_index;
         last_file_index = file_index;
       }
       
@@ -236,9 +236,9 @@ int do_append_data(JCR *jcr)
         set_jcr_job_status(jcr, JS_ErrorTerminated);
         ok = FALSE;
       }
-      /* Write out final block of this session */
+      /* Flush out final partial block of this session */
       if (!write_block_to_device(jcr, dev, block)) {
-         Pmsg0(000, _("Set ok=FALSE after write_block_to_device.\n"));
+         Dmsg0(100, _("Set ok=FALSE after write_block_to_device.\n"));
         set_jcr_job_status(jcr, JS_ErrorTerminated);
         ok = FALSE;
       }
index 781ee94941f0e4d8d8de2c71d3ae8c3b9aa809e3..bad70ce0af27e13e4956c1d9e40ae0067de15a6a 100644 (file)
@@ -111,14 +111,15 @@ static int do_request_volume_info(JCR *jcr)
  *
  *         Volume information returned in jcr
  */
-int dir_get_volume_info(JCR *jcr, int writing)
+int dir_get_volume_info(JCR *jcr, enum get_vol_info_rw writing)
 {
     BSOCK *dir = jcr->dir_bsock;
 
     bstrncpy(jcr->VolCatInfo.VolCatName, jcr->VolumeName, sizeof(jcr->VolCatInfo.VolCatName));
     Dmsg1(200, "dir_get_volume_info=%s\n", jcr->VolCatInfo.VolCatName);
     bash_spaces(jcr->VolCatInfo.VolCatName);
-    bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName, writing);
+    bnet_fsend(dir, Get_Vol_Info, jcr->Job, jcr->VolCatInfo.VolCatName, 
+       writing==GET_VOL_INFO_FOR_WRITE?1:0);
     return do_request_volume_info(jcr);
 }
 
@@ -188,12 +189,12 @@ int dir_create_jobmedia_record(JCR *jcr)
 {
    BSOCK *dir = jcr->dir_bsock;
 
-   if (jcr->VolFirstIndex == 0) {
+   if (!jcr->WroteVol) {
       return 1;                      /* nothing written to tape */
    }
 
    bnet_fsend(dir, Create_job_media, jcr->Job, 
-      jcr->VolFirstIndex, jcr->JobFiles,
+      jcr->VolFirstIndex, jcr->VolLastIndex,
       jcr->StartFile, jcr->EndFile,
       jcr->StartBlock, jcr->EndBlock);
    Dmsg1(100, "create_jobmedia(): %s", dir->msg);
@@ -271,7 +272,7 @@ int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev)
       if (job_canceled(jcr)) {
          Mmsg(&dev->errmsg, _("Job %s canceled while waiting for mount on Storage Device \"%s\".\n"), 
              jcr->Job, jcr->dev_name);
-         Jmsg(jcr, M_FATAL, 0, "%s", dev->errmsg);
+         Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
         return 0;
       }
       if (dir_find_next_appendable_volume(jcr)) {    /* get suggested volume */
@@ -342,7 +343,7 @@ Please use the \"label\"  command to create a new Volume for:\n\
       num_wait = 0;
       /* If no VolumeName, and cannot get one, try again */
       if (jcr->VolumeName[0] == 0 && 
-         !dir_find_next_appendable_volume(jcr)) {
+         !dir_find_next_appendable_volume(jcr) && !job_canceled(jcr)) {
         Jmsg(jcr, M_MOUNT, 0, _(
 "Someone woke me up, but I cannot find any appendable\n\
 volumes for Job=%s.\n"), jcr->Job);
index 11b55215323e45df140c5d724807d349bd05577b..f0f985eaf7f51b279cedc634f81704033c18d6a4 100644 (file)
@@ -37,8 +37,8 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
 /* Global variables */
 static DEVICE *in_dev = NULL;
 static DEVICE *out_dev = NULL;
-static JCR *in_jcr;                   /* input jcr */
-static JCR *out_jcr;                  /* output jcr */
+static JCR *in_jcr;                    /* input jcr */
+static JCR *out_jcr;                   /* output jcr */
 static BSR *bsr = NULL;
 static char *wd = "/tmp";
 static int list_records = 0;
@@ -78,42 +78,42 @@ int main (int argc, char *argv[])
    while ((ch = getopt(argc, argv, "b:c:d:mn:p:rsu:vV:w:?")) != -1) {
       switch (ch) {
          case 'b':
-           bsr = parse_bsr(NULL, optarg);
-           break;
+            bsr = parse_bsr(NULL, optarg);
+            break;
 
          case 'c':                    /* specify config file */
-           if (configfile != NULL) {
-              free(configfile);
-           }
-           configfile = bstrdup(optarg);
-           break;
+            if (configfile != NULL) {
+               free(configfile);
+            }
+            configfile = bstrdup(optarg);
+            break;
 
          case 'd':                    /* debug level */
-           debug_level = atoi(optarg);
-           if (debug_level <= 0)
-              debug_level = 1; 
-           break;
+            debug_level = atoi(optarg);
+            if (debug_level <= 0)
+               debug_level = 1; 
+            break;
 
          case 'v':
-           verbose++;
-           break;
+            verbose++;
+            break;
 
          case 'i':                    /* input Volume name */
-           iVolumeName = optarg;
-           break;
+            iVolumeName = optarg;
+            break;
 
          case 'o':                    /* output Volume name */
-           oVolumeName = optarg;
-           break;
+            oVolumeName = optarg;
+            break;
 
 
          case 'w':
-           wd = optarg;
-           break;
+            wd = optarg;
+            break;
 
          case '?':
-        default:
-           usage();
+         default:
+            usage();
 
       }  
    }
@@ -135,7 +135,7 @@ int main (int argc, char *argv[])
 
    /* Setup and acquire input device for reading */
    in_jcr = setup_jcr("bcopy", argv[0], bsr, iVolumeName);
-   in_dev = setup_to_access_device(in_jcr, 1);  /* read device */
+   in_dev = setup_to_access_device(in_jcr, 1);   /* read device */
    if (!in_dev) { 
       exit(1);
    }
@@ -144,7 +144,7 @@ int main (int argc, char *argv[])
    out_jcr = setup_jcr("bcopy", argv[1], bsr, oVolumeName);
    out_dev = setup_to_access_device(out_jcr, 0);   /* no acquire */  
    if (!out_dev) { 
-      exit(1);     
+      exit(1);      
    }
    /* For we must now acquire the device for writing */
    out_block = new_block(out_dev);
@@ -183,8 +183,8 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re
 {
    if (list_records) {
       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
-           rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, 
-           rec->Stream, rec->data_len);
+            rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, 
+            rec->Stream, rec->data_len);
    }
    /* 
     * Check for Start or End of Session Record 
@@ -193,44 +193,44 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re
    if (rec->FileIndex < 0) {
 
       if (verbose > 1) {
-        dump_label_record(dev, rec, 1);
+         dump_label_record(dev, rec, 1);
       }
       switch (rec->FileIndex) {
-        case PRE_LABEL:
+         case PRE_LABEL:
             Pmsg0(000, "Volume is prelabeled. This volume cannot be copied.\n");
-           return;
-        case VOL_LABEL:
+            return;
+         case VOL_LABEL:
             Pmsg0(000, "Volume label not copied.\n");
-           return;
-        case SOS_LABEL:
-           jobs++;
-           break;
-        case EOS_LABEL:
-           while (!write_record_to_block(out_block, rec)) {
+            return;
+         case SOS_LABEL:
+            jobs++;
+            break;
+         case EOS_LABEL:
+            while (!write_record_to_block(out_block, rec)) {
                Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
-                         rec->remainder);
-              if (!write_block_to_device(out_jcr, out_dev, out_block)) {
+                          rec->remainder);
+               if (!write_block_to_device(out_jcr, out_dev, out_block)) {
                   Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
-                    dev_name(out_dev), strerror_dev(out_dev));
+                     dev_name(out_dev), strerror_dev(out_dev));
                   Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
-                       strerror_dev(out_dev));
-              }
-           }
-           if (!write_block_to_device(out_jcr, out_dev, out_block)) {
+                        strerror_dev(out_dev));
+               }
+            }
+            if (!write_block_to_device(out_jcr, out_dev, out_block)) {
                Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
-                 dev_name(out_dev), strerror_dev(out_dev));
+                  dev_name(out_dev), strerror_dev(out_dev));
                Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
-                    strerror_dev(out_dev));
-           }
-           break;
-        case EOM_LABEL:
+                     strerror_dev(out_dev));
+            }
+            break;
+         case EOM_LABEL:
             Pmsg0(000, "EOM label not copied.\n");
-           return;
-        case EOT_LABEL:              /* end of all tapes */
+            return;
+         case EOT_LABEL:              /* end of all tapes */
             Pmsg0(000, "EOT label not copied.\n");
-           return;
-        default:
-           break;
+            return;
+         default:
+            break;
       }
    }
 
@@ -238,13 +238,13 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re
    records++;
    while (!write_record_to_block(out_block, rec)) {
       Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec->data_len,
-                rec->remainder);
+                 rec->remainder);
       if (!write_block_to_device(out_jcr, out_dev, out_block)) {
          Dmsg2(90, "Got write_block_to_dev error on device %s. %s\n",
-           dev_name(out_dev), strerror_dev(out_dev));
+            dev_name(out_dev), strerror_dev(out_dev));
          Jmsg(out_jcr, M_FATAL, 0, _("Cannot fixup device error. %s\n"),
-              strerror_dev(out_dev));
-        break;
+               strerror_dev(out_dev));
+         break;
       }
    }
    return;
@@ -252,19 +252,19 @@ static void record_cb(JCR *in_jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *re
 
 
 /* Dummies to replace askdir.c */
-int    dir_get_volume_info(JCR *jcr, int writing) { return 1;}
-int    dir_find_next_appendable_volume(JCR *jcr) { return 1;}
-int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
-int    dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int    dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
-int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
-int    dir_send_job_status(JCR *jcr) {return 1;}
+int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw  writing) { return 1;}
+int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int     dir_send_job_status(JCR *jcr) {return 1;}
 
 
 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
 {
    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
       in_jcr->VolumeName, dev_name(dev));
-   getchar();  
+   getchar();   
    return 1;
 }
index ba9f4443bc5395a89fe5ceeb04128f1580f230d6..fea67c21f487be59e7ebc91eac78a44329bf56af 100644 (file)
@@ -60,9 +60,9 @@ static int prog_name_msg = 0;
 static int win32_data_msg = 0;
 static char *VolumeName = NULL;
 
-static char *wbuf;                   /* write buffer address */
-static uint32_t wsize;               /* write size */
-static uint64_t fileAddr = 0;        /* file write address */
+static char *wbuf;                    /* write buffer address */
+static uint32_t wsize;                /* write size */
+static uint64_t fileAddr = 0;         /* file write address */
 
 #define CONFIG_FILE "bacula-sd.conf"
 char *configfile;
@@ -93,7 +93,7 @@ int main (int argc, char *argv[])
 
    working_directory = "/tmp";
    my_name_is(argc, argv, "bextract");
-   init_msg(NULL, NULL);             /* setup message handler */
+   init_msg(NULL, NULL);              /* setup message handler */
 
    memset(ff, 0, sizeof(FF_PKT));
    init_include_exclude_files(ff);
@@ -102,59 +102,59 @@ int main (int argc, char *argv[])
    while ((ch = getopt(argc, argv, "b:c:d:e:i:?")) != -1) {
       switch (ch) {
       case 'b':                    /* bootstrap file */
-        bsr = parse_bsr(NULL, optarg);
-//      dump_bsr(bsr);
-        break;
+         bsr = parse_bsr(NULL, optarg);
+//       dump_bsr(bsr);
+         break;
 
       case 'c':                    /* specify config file */
-        if (configfile != NULL) {
-           free(configfile);
-        }
-        configfile = bstrdup(optarg);
-        break;
+         if (configfile != NULL) {
+            free(configfile);
+         }
+         configfile = bstrdup(optarg);
+         break;
 
       case 'd':                    /* debug level */
-        debug_level = atoi(optarg);
-        if (debug_level <= 0)
-           debug_level = 1; 
-        break;
+         debug_level = atoi(optarg);
+         if (debug_level <= 0)
+            debug_level = 1; 
+         break;
 
       case 'e':                    /* exclude list */
          if ((fd = fopen(optarg, "r")) == NULL) {
             Pmsg2(0, "Could not open exclude file: %s, ERR=%s\n",
-              optarg, strerror(errno));
-           exit(1);
-        }
-        while (fgets(line, sizeof(line), fd) != NULL) {
-           strip_trailing_junk(line);
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
             Dmsg1(900, "add_exclude %s\n", line);
-           add_fname_to_exclude_list(ff, line);
-        }
-        fclose(fd);
-        break;
+            add_fname_to_exclude_list(ff, line);
+         }
+         fclose(fd);
+         break;
 
       case 'i':                    /* include list */
          if ((fd = fopen(optarg, "r")) == NULL) {
             Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
-              optarg, strerror(errno));
-           exit(1);
-        }
-        while (fgets(line, sizeof(line), fd) != NULL) {
-           strip_trailing_junk(line);
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
             Dmsg1(900, "add_include %s\n", line);
-           add_fname_to_include_list(ff, 0, line);
-        }
-        fclose(fd);
-        got_inc = TRUE;
-        break;
+            add_fname_to_include_list(ff, 0, line);
+         }
+         fclose(fd);
+         got_inc = TRUE;
+         break;
 
       case 'V':                    /* Volume name */
-        VolumeName = optarg;
-        break;
+         VolumeName = optarg;
+         break;
 
       case '?':
       default:
-        usage();
+         usage();
 
       } /* end switch */
    } /* end while */
@@ -172,7 +172,7 @@ int main (int argc, char *argv[])
 
    parse_config(configfile);
 
-   if (!got_inc) {                           /* If no include file, */
+   if (!got_inc) {                            /* If no include file, */
       add_fname_to_include_list(ff, 0, "/");  /*   include everything */
    }
 
@@ -184,11 +184,11 @@ int main (int argc, char *argv[])
    }
    if (prog_name_msg) {
       Pmsg1(000, "%d Program Name and/or Program Data Stream records ignored.\n",
-        prog_name_msg);
+         prog_name_msg);
    }
    if (win32_data_msg) {
       Pmsg1(000, "%d Win32 data or Win32 gzip data stream records. Ignored.\n",
-        win32_data_msg);
+         win32_data_msg);
    }
    return 0;
 }
@@ -205,7 +205,7 @@ static void do_extract(char *devname)
    /* Make sure where directory exists and that it is a directory */
    if (stat(where, &statp) < 0) {
       Emsg2(M_ERROR_TERM, 0, "Cannot stat %s. It must exist. ERR=%s\n",
-        where, strerror(errno));
+         where, strerror(errno));
    }
    if (!S_ISDIR(statp.st_mode)) {
       Emsg1(M_ERROR_TERM, 0, "%s must be a directory.\n", where);
@@ -254,11 +254,11 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
        * close the output file.
        */
       if (extract) {
-        if (!is_bopen(&bfd)) {
+         if (!is_bopen(&bfd)) {
             Emsg0(M_ERROR, 0, _("Logic error output file should be open but is not.\n"));
-        }
-        extract = FALSE;
-        set_attributes(jcr, attr, &bfd);
+         }
+         extract = FALSE;
+         set_attributes(jcr, attr, &bfd);
       }
 
       if (!unpack_attributes_record(jcr, rec->Stream, rec->data, attr)) {
@@ -267,46 +267,46 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
 
       if (attr->file_index != rec->FileIndex) {
          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
-           rec->FileIndex, attr->file_index);
+            rec->FileIndex, attr->file_index);
       }
-        
+         
       if (file_is_included(ff, attr->fname) && !file_is_excluded(ff, attr->fname)) {
-        uint32_t LinkFI;
-
-        decode_stat(attr->attr, &attr->statp, &LinkFI);
-
-        build_attr_output_fnames(jcr, attr);
-
-        extract = FALSE;
-        stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);   
-        switch (stat) {
-        case CF_ERROR:
-        case CF_SKIP:
-           break;
-        case CF_EXTRACT:
-           extract = TRUE;
-           print_ls_output(jcr, attr);
-           num_files++;
-           fileAddr = 0;
-           break;
-        case CF_CREATED:
-           set_attributes(jcr, attr, &bfd);
-           print_ls_output(jcr, attr);
-           num_files++;
-           fileAddr = 0;
-           break;
-        }  
+         uint32_t LinkFI;
+
+         decode_stat(attr->attr, &attr->statp, &LinkFI);
+
+         build_attr_output_fnames(jcr, attr);
+
+         extract = FALSE;
+         stat = create_file(jcr, attr, &bfd, REPLACE_ALWAYS);   
+         switch (stat) {
+         case CF_ERROR:
+         case CF_SKIP:
+            break;
+         case CF_EXTRACT:
+            extract = TRUE;
+            print_ls_output(jcr, attr);
+            num_files++;
+            fileAddr = 0;
+            break;
+         case CF_CREATED:
+            set_attributes(jcr, attr, &bfd);
+            print_ls_output(jcr, attr);
+            num_files++;
+            fileAddr = 0;
+            break;
+         }  
       }
 
    /* Windows Backup data stream */
    case STREAM_WIN32_DATA:  
       if (!is_win32_backup()) {
-        if (!non_support_data) {
+         if (!non_support_data) {
             Jmsg(jcr, M_ERROR, 0, _("Win32 backup data not supported on this Client.\n"));
-        }
-        extract = FALSE;
-        non_support_data++;
-        return;
+         }
+         extract = FALSE;
+         non_support_data++;
+         return;
       }
       goto extract_data;
    
@@ -317,42 +317,42 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
 
 extract_data:
       if (extract) {
-        if (rec->Stream == STREAM_SPARSE_DATA) {
-           ser_declare;
-           uint64_t faddr;
-           wbuf = rec->data + SPARSE_FADDR_SIZE;
-           wsize = rec->data_len - SPARSE_FADDR_SIZE;
-           ser_begin(rec->data, SPARSE_FADDR_SIZE);
-           unser_uint64(faddr);
-           if (fileAddr != faddr) {
-              fileAddr = faddr;
-              if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
+         if (rec->Stream == STREAM_SPARSE_DATA) {
+            ser_declare;
+            uint64_t faddr;
+            wbuf = rec->data + SPARSE_FADDR_SIZE;
+            wsize = rec->data_len - SPARSE_FADDR_SIZE;
+            ser_begin(rec->data, SPARSE_FADDR_SIZE);
+            unser_uint64(faddr);
+            if (fileAddr != faddr) {
+               fileAddr = faddr;
+               if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
                   Emsg2(M_ERROR_TERM, 0, _("Seek error on %s: %s\n"), 
-                    attr->ofname, strerror(errno));
-              }
-           }
-        } else {
-           wbuf = rec->data;
-           wsize = rec->data_len;
-        }
-        total += wsize;
+                     attr->ofname, strerror(errno));
+               }
+            }
+         } else {
+            wbuf = rec->data;
+            wsize = rec->data_len;
+         }
+         total += wsize;
          Dmsg2(8, "Write %u bytes, total=%u\n", wsize, total);
-        if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
+         if ((uint32_t)bwrite(&bfd, wbuf, wsize) != wsize) {
             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), 
-              attr->ofname, strerror(errno));
-        }
-        fileAddr += wsize;
+               attr->ofname, strerror(errno));
+         }
+         fileAddr += wsize;
       }
 
    /* Windows Backup GZIP data stream */
    case STREAM_WIN32_GZIP_DATA:  
       if (!is_win32_backup()) {
-        if (!non_support_attr) {
+         if (!non_support_attr) {
             Jmsg(jcr, M_ERROR, 0, _("Win32 GZIP backup data not supported on this Client.\n"));
-        }
-        extract = FALSE;
-        non_support_attr++;
-        return;
+         }
+         extract = FALSE;
+         non_support_attr++;
+         return;
       }
       /* Fall through desired */
 
@@ -361,49 +361,49 @@ extract_data:
    case STREAM_SPARSE_GZIP_DATA: 
 #ifdef HAVE_LIBZ
       if (extract) {
-        uLongf compress_len;
-        int stat;
-
-        if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
-           ser_declare;
-           uint64_t faddr;
-           wbuf = rec->data + SPARSE_FADDR_SIZE;
-           wsize = rec->data_len - SPARSE_FADDR_SIZE;
-           ser_begin(rec->data, SPARSE_FADDR_SIZE);
-           unser_uint64(faddr);
-           if (fileAddr != faddr) {
-              fileAddr = faddr;
-              if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
+         uLongf compress_len;
+         int stat;
+
+         if (rec->Stream == STREAM_SPARSE_GZIP_DATA) {
+            ser_declare;
+            uint64_t faddr;
+            wbuf = rec->data + SPARSE_FADDR_SIZE;
+            wsize = rec->data_len - SPARSE_FADDR_SIZE;
+            ser_begin(rec->data, SPARSE_FADDR_SIZE);
+            unser_uint64(faddr);
+            if (fileAddr != faddr) {
+               fileAddr = faddr;
+               if (blseek(&bfd, (off_t)fileAddr, SEEK_SET) < 0) {
                   Emsg2(M_ERROR, 0, _("Seek error on %s: %s\n"), 
-                    attr->ofname, strerror(errno));
-              }
-           }
-        } else {
-           wbuf = rec->data;
-           wsize = rec->data_len;
-        }
-        compress_len = compress_buf_size;
-        if ((stat=uncompress((Bytef *)compress_buf, &compress_len, 
-              (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
+                     attr->ofname, strerror(errno));
+               }
+            }
+         } else {
+            wbuf = rec->data;
+            wsize = rec->data_len;
+         }
+         compress_len = compress_buf_size;
+         if ((stat=uncompress((Bytef *)compress_buf, &compress_len, 
+               (const Bytef *)wbuf, (uLong)wsize) != Z_OK)) {
             Emsg1(M_ERROR_TERM, 0, _("Uncompression error. ERR=%d\n"), stat);
-        }
+         }
 
          Dmsg2(100, "Write uncompressed %d bytes, total before write=%d\n", compress_len, total);
-        if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
+         if ((uLongf)bwrite(&bfd, compress_buf, (size_t)compress_len) != compress_len) {
             Pmsg0(0, "===Write error===\n");
             Emsg2(M_ERROR_TERM, 0, _("Write error on %s: %s\n"), 
-              attr->ofname, strerror(errno));
-        }
-        total += compress_len;
-        fileAddr += compress_len;
+               attr->ofname, strerror(errno));
+         }
+         total += compress_len;
+         fileAddr += compress_len;
          Dmsg2(100, "Compress len=%d uncompressed=%d\n", rec->data_len,
-           compress_len);
+            compress_len);
       }
 #else
       if (extract) {
          Emsg0(M_ERROR, 0, "GZIP data stream found, but GZIP not configured!\n");
-        extract = FALSE;
-        return;
+         extract = FALSE;
+         return;
       }
 #endif
 
@@ -415,21 +415,21 @@ extract_data:
    case STREAM_PROGRAM_DATA:
       if (!prog_name_msg) {
          Pmsg0(000, "Got Program Name or Data Stream. Ignored.\n");
-        prog_name_msg++;
+         prog_name_msg++;
       }
       break;
 
    default:
       /* If extracting, wierd stream (not 1 or 2), close output file anyway */
       if (extract) {
-        if (!is_bopen(&bfd)) {
+         if (!is_bopen(&bfd)) {
             Emsg0(M_ERROR, 0, "Logic error output file should be open but is not.\n");
-        }
-        extract = FALSE;
-        set_attributes(jcr, attr, &bfd);
+         }
+         extract = FALSE;
+         set_attributes(jcr, attr, &bfd);
       }
       Jmsg(jcr, M_ERROR, 0, _("Unknown stream=%d ignored. This shouldn't happen!\n"), 
-        rec->Stream);
+         rec->Stream);
       break;
       
    } /* end switch */
@@ -439,19 +439,19 @@ extract_data:
 
 
 /* Dummies to replace askdir.c */
-int    dir_get_volume_info(JCR *jcr, int writing) { return 1;}
-int    dir_find_next_appendable_volume(JCR *jcr) { return 1;}
-int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
-int    dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int    dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
-int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
-int    dir_send_job_status(JCR *jcr) {return 1;}
+int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw  writing) { return 1;}
+int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int     dir_send_job_status(JCR *jcr) {return 1;}
 
 
 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
 {
    fprintf(stderr, "Mount Volume %s on device %s and press return when ready: ",
       jcr->VolumeName, dev_name(dev));
-   getchar();  
+   getchar();   
    return 1;
 }
index ed20383c1152f4a93856b828ed989540e08558a7..4f49ded7e054589d6f4e8341749f1b822909ab7b 100644 (file)
@@ -276,9 +276,27 @@ int write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
 {
    int stat = 1;
    lock_device(dev);
+
+   /*
+    * If a new volume has been mounted since our last write
+    *  Create a JobMedia record for the previous volume written,
+    *  and set new parameters to write this volume   
+    */
+   if (jcr->NewVol) {
+      /* Create a jobmedia record for this job */
+      if (!dir_create_jobmedia_record(jcr)) {
+         Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
+           jcr->VolCatInfo.VolCatName, jcr->Job);
+        unlock_device(dev);
+        return 0;
+      }
+      set_new_volume_parameters(jcr, dev);
+   }
+
    if (!write_block_to_dev(jcr, dev, block)) {
        stat = fixup_device_block_write_error(jcr, dev, block);
    }
+
    unlock_device(dev);
    return stat;
 }
@@ -455,6 +473,9 @@ int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
 #endif
       return 0;
    }
+
+   /* Do housekeeping */
+
    dev->VolCatInfo.VolCatBytes += block->binbuf;
    dev->VolCatInfo.VolCatBlocks++;   
    dev->file_addr += wlen;
@@ -463,6 +484,16 @@ int write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    dev->block_num++;
    block->BlockNumber++;
 
+   /* Update jcr values */
+   if (dev->state & ST_TAPE) {
+      jcr->EndBlock = dev->EndBlock;
+      jcr->EndFile  = dev->EndFile;
+   } else {
+      jcr->EndBlock = (uint32_t)dev->file_addr;
+      jcr->EndFile = (uint32_t)(dev->file_addr >> 32);
+   }
+   jcr->WroteVol = true;
+
    Dmsg2(190, "write_block: wrote block %d bytes=%d\n", dev->block_num,
       wlen);
    empty_block(block);
index 4a72d543903f5abce353b678e2d87280796b8226..f32a9bb30e382f14aed5d9018da7d8acdce0f3c0 100644 (file)
@@ -91,7 +91,7 @@ int main (int argc, char *argv[])
 
    working_directory = "/tmp";
    my_name_is(argc, argv, "bls");
-   init_msg(NULL, NULL);             /* initialize message handler */
+   init_msg(NULL, NULL);              /* initialize message handler */
 
    memset(&ff, 0, sizeof(ff));
    init_include_exclude_files(&ff);
@@ -99,73 +99,73 @@ int main (int argc, char *argv[])
    while ((ch = getopt(argc, argv, "b:c:d:e:i:jkLtvV:?")) != -1) {
       switch (ch) {
       case 'b':
-        bsrName = optarg;
-        break;
+         bsrName = optarg;
+         break;
 
       case 'c':                    /* specify config file */
-        if (configfile != NULL) {
-           free(configfile);
-        }
-        configfile = bstrdup(optarg);
-        break;
+         if (configfile != NULL) {
+            free(configfile);
+         }
+         configfile = bstrdup(optarg);
+         break;
 
       case 'd':                    /* debug level */
-        debug_level = atoi(optarg);
-        if (debug_level <= 0)
-           debug_level = 1; 
-        break;
+         debug_level = atoi(optarg);
+         if (debug_level <= 0)
+            debug_level = 1; 
+         break;
 
       case 'e':                    /* exclude list */
          if ((fd = fopen(optarg, "r")) == NULL) {
             Pmsg2(0, _("Could not open exclude file: %s, ERR=%s\n"),
-              optarg, strerror(errno));
-           exit(1);
-        }
-        while (fgets(line, sizeof(line), fd) != NULL) {
-           strip_trailing_junk(line);
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
             Dmsg1(100, "add_exclude %s\n", line);
-           add_fname_to_exclude_list(&ff, line);
-        }
-        fclose(fd);
-        break;
+            add_fname_to_exclude_list(&ff, line);
+         }
+         fclose(fd);
+         break;
 
       case 'i':                    /* include list */
          if ((fd = fopen(optarg, "r")) == NULL) {
             Pmsg2(0, "Could not open include file: %s, ERR=%s\n",
-              optarg, strerror(errno));
-           exit(1);
-        }
-        while (fgets(line, sizeof(line), fd) != NULL) {
-           strip_trailing_junk(line);
+               optarg, strerror(errno));
+            exit(1);
+         }
+         while (fgets(line, sizeof(line), fd) != NULL) {
+            strip_trailing_junk(line);
             Dmsg1(100, "add_include %s\n", line);
-           add_fname_to_include_list(&ff, 0, line);
-        }
-        fclose(fd);
-        break;
+            add_fname_to_include_list(&ff, 0, line);
+         }
+         fclose(fd);
+         break;
 
       case 'j':
-        list_jobs = TRUE;
-        break;
+         list_jobs = TRUE;
+         break;
 
       case 'k':
-        list_blocks = TRUE;
-        break;
+         list_blocks = TRUE;
+         break;
 
       case 'L':
-        dump_label = TRUE;
-        break;
+         dump_label = TRUE;
+         break;
 
       case 'v':
-        verbose++;
-        break;
+         verbose++;
+         break;
 
       case 'V':                    /* Volume name */
-        VolumeName = optarg;
-        break;
+         VolumeName = optarg;
+         break;
 
       case '?':
       default:
-        usage();
+         usage();
 
       } /* end switch */
    } /* end while */
@@ -189,12 +189,12 @@ int main (int argc, char *argv[])
 
    for (i=0; i < argc; i++) {
       if (bsrName) {
-        bsr = parse_bsr(NULL, bsrName);
+         bsr = parse_bsr(NULL, bsrName);
       }
       jcr = setup_jcr("bls", argv[i], bsr, VolumeName);
       dev = setup_to_access_device(jcr, 1);   /* acquire for read */
       if (!dev) {
-        exit(1);
+         exit(1);
       }
       rec = new_record();
       block = new_block(dev);
@@ -206,15 +206,15 @@ int main (int argc, char *argv[])
       if (dev->VolHdr.PrevVolName[0] != 0) { /* second volume */
          Pmsg1(0, "\n\
 Warning, this Volume is a continuation of Volume %s\n",
-               dev->VolHdr.PrevVolName);
+                dev->VolHdr.PrevVolName);
       }
 
       if (list_blocks) {
-        do_blocks(argv[i]);
+         do_blocks(argv[i]);
       } else if (list_jobs) {
-        do_jobs(argv[i]);
+         do_jobs(argv[i]);
       } else {
-        do_ls(argv[i]);
+         do_ls(argv[i]);
       }
       do_close(jcr);
    }
@@ -246,45 +246,45 @@ static void do_blocks(char *infname)
    for ( ;; ) {
       if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
          Dmsg1(100, "!read_block(): ERR=%s\n", strerror_dev(dev));
-        if (dev->state & ST_EOT) {
-           if (!mount_next_read_volume(jcr, dev, block)) {
+         if (dev->state & ST_EOT) {
+            if (!mount_next_read_volume(jcr, dev, block)) {
                Jmsg(jcr, M_INFO, 0, _("Got EOM at file %u on device %s, Volume \"%s\"\n"), 
-                 dev->file, dev_name(dev), jcr->VolumeName);
-              break;
-           }
-           /* Read and discard Volume label */
-           DEV_RECORD *record;
-           record = new_record();
-           read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK);
-           read_record_from_block(block, record);
-           get_session_record(dev, record, &sessrec);
-           free_record(record);
+                  dev->file, dev_name(dev), jcr->VolumeName);
+               break;
+            }
+            /* Read and discard Volume label */
+            DEV_RECORD *record;
+            record = new_record();
+            read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK);
+            read_record_from_block(block, record);
+            get_session_record(dev, record, &sessrec);
+            free_record(record);
             Jmsg(jcr, M_INFO, 0, _("Mounted Volume \"%s\".\n"), jcr->VolumeName);
-           
-        } else if (dev->state & ST_EOF) {
+            
+         } else if (dev->state & ST_EOF) {
             Jmsg(jcr, M_INFO, 0, _("Got EOF at file %u on device %s, Volume \"%s\"\n"), 
-              dev->file, dev_name(dev), jcr->VolumeName);
+               dev->file, dev_name(dev), jcr->VolumeName);
             Dmsg0(20, "read_record got eof. try again\n");
-           continue;
-        } else if (dev->state & ST_SHORT) {
+            continue;
+         } else if (dev->state & ST_SHORT) {
             Jmsg(jcr, M_INFO, 0, "%s", dev->errmsg);
-           continue;
-        } else {
-           /* I/O error */
-           display_tape_error_status(jcr, dev);
-           break;
-        }
+            continue;
+         } else {
+            /* I/O error */
+            display_tape_error_status(jcr, dev);
+            break;
+         }
       }
       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
-        block->BlockNumber, block->block_len, block->BlockVer,
-        block->VolSessionId, block->VolSessionTime);
+         block->BlockNumber, block->block_len, block->BlockVer,
+         block->VolSessionId, block->VolSessionTime);
       if (verbose == 1) {
-        read_record_from_block(block, rec);
+         read_record_from_block(block, rec);
          Pmsg7(-1, "Block: %u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
-             block->BlockNumber, block->block_len,
-             FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
-             stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
-        rec->remainder = 0;
+              block->BlockNumber, block->block_len,
+              FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
+              stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
+         rec->remainder = 0;
       } else if (verbose > 1) {
          dump_block(block, "");
       } else {
@@ -343,14 +343,14 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
 
       if (attr->file_index != rec->FileIndex) {
          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
-           rec->FileIndex, attr->file_index);
+            rec->FileIndex, attr->file_index);
       }
 
       decode_stat(attr->attr, &attr->statp, &LinkFI);
 
       if (file_is_included(&ff, attr->fname) && !file_is_excluded(&ff, attr->fname)) {
-        print_ls_output(jcr, attr);
-        num_files++;
+         print_ls_output(jcr, attr);
+         num_files++;
       }
    }
    return;
@@ -364,44 +364,44 @@ static void get_session_record(DEVICE *dev, DEV_RECORD *rec, SESSION_LABEL *sess
    switch (rec->FileIndex) {
       case PRE_LABEL:
          rtype = "Fresh Volume Label";   
-        break;
+         break;
       case VOL_LABEL:
          rtype = "Volume Label";
-        unser_volume_label(dev, rec);
-        break;
+         unser_volume_label(dev, rec);
+         break;
       case SOS_LABEL:
          rtype = "Begin Session";
-        unser_session_label(sessrec, rec);
-        break;
+         unser_session_label(sessrec, rec);
+         break;
       case EOS_LABEL:
          rtype = "End Session";
-        break;
+         break;
       case EOM_LABEL:
          rtype = "End of Medium";
-        break;
+         break;
       default:
          rtype = "Unknown";
-        break;
+         break;
    }
    Dmsg5(10, "%s Record: VolSessionId=%d VolSessionTime=%d JobId=%d DataLen=%d\n",
-        rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
+         rtype, rec->VolSessionId, rec->VolSessionTime, rec->Stream, rec->data_len);
 }
 
 
 /* Dummies to replace askdir.c */
-int    dir_get_volume_info(JCR *jcr, int writing) { return 1;}
-int    dir_find_next_appendable_volume(JCR *jcr) { return 1;}
-int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
-int    dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int    dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
-int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
-int    dir_send_job_status(JCR *jcr) {return 1;}
+int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw  writing) { return 1;}
+int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int     dir_send_job_status(JCR *jcr) {return 1;}
 
 
 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
 {
    fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
       jcr->VolumeName, dev_name(dev));
-   getchar();  
+   getchar();   
    return 1;
 }
index e01911f95e7a9a21b318013e8811b5c0f3b343ec..ceeebe3de144293ac9e64f565ac4276a473c7ae3 100644 (file)
 static void do_scan(void);
 static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec);
 static int  create_file_attributes_record(B_DB *db, JCR *mjcr, 
-                              char *fname, char *lname, int type,
-                              char *ap, DEV_RECORD *rec);
+                               char *fname, char *lname, int type,
+                               char *ap, DEV_RECORD *rec);
 static int  create_media_record(B_DB *db, MEDIA_DBR *mr, VOLUME_LABEL *vl);
 static int  update_media_record(B_DB *db, MEDIA_DBR *mr);
 static int  create_pool_record(B_DB *db, POOL_DBR *pr);
 static JCR *create_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *label, DEV_RECORD *rec);
 static int  update_job_record(B_DB *db, JOB_DBR *mr, SESSION_LABEL *elabel, 
-                             DEV_RECORD *rec);
+                              DEV_RECORD *rec);
 static int  create_client_record(B_DB *db, CLIENT_DBR *cr);
 static int  create_fileset_record(B_DB *db, FILESET_DBR *fsr);
 static int  create_jobmedia_record(B_DB *db, JCR *jcr);
@@ -65,7 +65,7 @@ int win32_client = 0;
 /* Local variables */
 static DEVICE *dev = NULL;
 static B_DB *db;
-static JCR *bjcr;                    /* jcr for bscan */
+static JCR *bjcr;                     /* jcr for bscan */
 static BSR *bsr = NULL;
 static MEDIA_DBR mr;
 static POOL_DBR pr;
@@ -128,61 +128,61 @@ int main (int argc, char *argv[])
    while ((ch = getopt(argc, argv, "b:c:d:mn:p:rsu:vVw:?")) != -1) {
       switch (ch) {
       case 'b':
-        bsr = parse_bsr(NULL, optarg);
-        break;
+         bsr = parse_bsr(NULL, optarg);
+         break;
 
       case 'c':                    /* specify config file */
-        if (configfile != NULL) {
-           free(configfile);
-        }
-        configfile = bstrdup(optarg);
-        break;
+         if (configfile != NULL) {
+            free(configfile);
+         }
+         configfile = bstrdup(optarg);
+         break;
 
       case 'd':                    /* debug level */
-        debug_level = atoi(optarg);
-        if (debug_level <= 0)
-           debug_level = 1; 
-        break;
+         debug_level = atoi(optarg);
+         if (debug_level <= 0)
+            debug_level = 1; 
+         break;
 
       case 'm':
-        update_vol_info = 1;
-        break;
+         update_vol_info = 1;
+         break;
 
       case 'n':
-        db_name = optarg;
-        break;
+         db_name = optarg;
+         break;
 
       case 'u':
-        db_user = optarg;
-        break;
+         db_user = optarg;
+         break;
 
       case 'p':
-        db_password = optarg;
-        break;
+         db_password = optarg;
+         break;
 
       case 'r':
-        list_records = 1;
-        break;
+         list_records = 1;
+         break;
 
       case 's':
-        update_db = 1;
-        break;
+         update_db = 1;
+         break;
 
       case 'v':
-        verbose++;
-        break;
+         verbose++;
+         break;
 
       case 'V':                    /* Volume name */
-        VolumeName = optarg;
-        break;
+         VolumeName = optarg;
+         break;
 
       case 'w':
-        wd = optarg;
-        break;
+         wd = optarg;
+         break;
 
       case '?':
       default:
-        usage();
+         usage();
 
       }  
    }
@@ -204,7 +204,7 @@ int main (int argc, char *argv[])
    if (!me) {
       UnlockRes();
       Emsg1(M_ERROR_TERM, 0, _("No Storage resource defined in %s. Cannot continue.\n"), 
-        configfile);
+         configfile);
    }
    UnlockRes();
    /* Check if -w option given, otherwise use resource for working directory */
@@ -212,7 +212,7 @@ int main (int argc, char *argv[])
       working_directory = wd;
    } else if (!me->working_directory) {
       Emsg1(M_ERROR_TERM, 0, _("No Working Directory defined in %s. Cannot continue.\n"),
-        configfile);
+         configfile);
    } else {
       working_directory = me->working_directory;
    }
@@ -220,11 +220,11 @@ int main (int argc, char *argv[])
    /* Check that working directory is good */
    if (stat(working_directory, &stat_buf) != 0) {
       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s not found. Cannot continue.\n"),
-        working_directory);
+         working_directory);
    }
    if (!S_ISDIR(stat_buf.st_mode)) {
       Emsg1(M_ERROR_TERM, 0, _("Working Directory: %s is not a directory. Cannot continue.\n"),
-        working_directory);
+         working_directory);
    }
 
    bjcr = setup_jcr("bscan", argv[0], bsr, VolumeName);
@@ -263,15 +263,15 @@ static int bscan_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
          Pmsg1(000, _("Create JobMedia for Job %s\n"), mjcr->Job);
       }
       if (dev->state & ST_TAPE) {
-        mjcr->EndBlock = dev->EndBlock;
-        mjcr->EndFile = dev->EndFile;
+         mjcr->EndBlock = dev->EndBlock;
+         mjcr->EndFile = dev->EndFile;
       } else {
-        mjcr->EndBlock = (uint32_t)dev->file_addr;
-        mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32);
+         mjcr->EndBlock = (uint32_t)dev->file_addr;
+         mjcr->StartBlock = (uint32_t)(dev->file_addr >> 32);
       }
       if (!create_jobmedia_record(db, mjcr)) {
          Pmsg2(000, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
-           dev->VolCatInfo.VolCatName, mjcr->Job);
+            dev->VolCatInfo.VolCatName, mjcr->Job);
       }
    }  
    /* Now let common read routine get up next tape. Note,
@@ -284,7 +284,7 @@ static int bscan_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    return stat;
 }
 
-static void do_scan()            
+static void do_scan()             
 {
    attr = new_attr();
 
@@ -315,8 +315,8 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
    }
    if (list_records) {
       Pmsg5(000, _("Record: SessId=%u SessTim=%u FileIndex=%d Stream=%d len=%u\n"),
-           rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, 
-           rec->Stream, rec->data_len);
+            rec->VolSessionId, rec->VolSessionTime, rec->FileIndex, 
+            rec->Stream, rec->data_len);
    }
    /* 
     * Check for Start or End of Session Record 
@@ -326,210 +326,210 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
       int save_update_db = update_db;
 
       if (verbose > 1) {
-        dump_label_record(dev, rec, 1);
+         dump_label_record(dev, rec, 1);
       }
       switch (rec->FileIndex) {
       case PRE_LABEL:
          Pmsg0(000, _("Volume is prelabeled. This tape cannot be scanned.\n"));
-        return;
-        break;
+         return;
+         break;
 
       case VOL_LABEL:
-        unser_volume_label(dev, rec);
-        /* Check Pool info */
-        strcpy(pr.Name, dev->VolHdr.PoolName);
-        strcpy(pr.PoolType, dev->VolHdr.PoolType);
-        if (db_get_pool_record(bjcr, db, &pr)) {
-           if (verbose) {
+         unser_volume_label(dev, rec);
+         /* Check Pool info */
+         strcpy(pr.Name, dev->VolHdr.PoolName);
+         strcpy(pr.PoolType, dev->VolHdr.PoolType);
+         if (db_get_pool_record(bjcr, db, &pr)) {
+            if (verbose) {
                Pmsg1(000, _("Pool record for %s found in DB.\n"), pr.Name);
-           }
-        } else {
-           if (!update_db) {
+            }
+         } else {
+            if (!update_db) {
                Pmsg1(000, _("VOL_LABEL: Pool record not found for Pool: %s\n"),
-                 pr.Name);
-           }
-           create_pool_record(db, &pr);
-        }
-        if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
+                  pr.Name);
+            }
+            create_pool_record(db, &pr);
+         }
+         if (strcmp(pr.PoolType, dev->VolHdr.PoolType) != 0) {
             Pmsg2(000, _("VOL_LABEL: PoolType mismatch. DB=%s Vol=%s\n"),
-              pr.PoolType, dev->VolHdr.PoolType);
-           return;
-        } else if (verbose) {
+               pr.PoolType, dev->VolHdr.PoolType);
+            return;
+         } else if (verbose) {
             Pmsg1(000, _("Pool type \"%s\" is OK.\n"), pr.PoolType);
-        }
-
-        /* Check Media Info */
-        memset(&mr, 0, sizeof(mr));
-        strcpy(mr.VolumeName, dev->VolHdr.VolName);
-        mr.PoolId = pr.PoolId;
-        if (db_get_media_record(bjcr, db, &mr)) {
-           if (verbose) {
+         }
+
+         /* Check Media Info */
+         memset(&mr, 0, sizeof(mr));
+         strcpy(mr.VolumeName, dev->VolHdr.VolName);
+         mr.PoolId = pr.PoolId;
+         if (db_get_media_record(bjcr, db, &mr)) {
+            if (verbose) {
                Pmsg1(000, _("Media record for %s found in DB.\n"), mr.VolumeName);
-           }
-           /* Clear out some volume statistics that will be updated */
-           mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
-           mr.VolBytes = rec->data_len + 20;
-        } else {
-           if (!update_db) {
+            }
+            /* Clear out some volume statistics that will be updated */
+            mr.VolJobs = mr.VolFiles = mr.VolBlocks = 0;
+            mr.VolBytes = rec->data_len + 20;
+         } else {
+            if (!update_db) {
                Pmsg1(000, _("VOL_LABEL: Media record not found for Volume: %s\n"),
-                 mr.VolumeName);
-           }
-           strcpy(mr.MediaType, dev->VolHdr.MediaType);
-           create_media_record(db, &mr, &dev->VolHdr);
-        }
-        if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
+                  mr.VolumeName);
+            }
+            strcpy(mr.MediaType, dev->VolHdr.MediaType);
+            create_media_record(db, &mr, &dev->VolHdr);
+         }
+         if (strcmp(mr.MediaType, dev->VolHdr.MediaType) != 0) {
             Pmsg2(000, _("VOL_LABEL: MediaType mismatch. DB=%s Vol=%s\n"),
-              mr.MediaType, dev->VolHdr.MediaType);
-           return;
-        } else if (verbose) {
+               mr.MediaType, dev->VolHdr.MediaType);
+            return;
+         } else if (verbose) {
             Pmsg1(000, _("Media type \"%s\" is OK.\n"), mr.MediaType);
-        }
-        /* Reset some JCR variables */
-        for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
-           mjcr->VolFirstIndex = mjcr->FileIndex = 0;
-           mjcr->StartBlock = mjcr->EndBlock = 0;
-           mjcr->StartFile = mjcr->EndFile = 0;
-        }
+         }
+         /* Reset some JCR variables */
+         for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
+            mjcr->VolFirstIndex = mjcr->FileIndex = 0;
+            mjcr->StartBlock = mjcr->EndBlock = 0;
+            mjcr->StartFile = mjcr->EndFile = 0;
+         }
 
          Pmsg1(000, _("VOL_LABEL: OK for Volume: %s\n"), mr.VolumeName);
-        break;
+         break;
 
       case SOS_LABEL:
-        mr.VolJobs++;
-        if (ignored_msgs > 0) {
+         mr.VolJobs++;
+         if (ignored_msgs > 0) {
             Pmsg1(000, _("%d \"errors\" ignored before first Start of Session record.\n"), 
-                 ignored_msgs);
-           ignored_msgs = 0;
-        }
-        unser_session_label(&label, rec);
-        memset(&jr, 0, sizeof(jr));
-        jr.JobId = label.JobId;
-        if (db_get_job_record(bjcr, db, &jr)) {
-           /* Job record already exists in DB */
+                  ignored_msgs);
+            ignored_msgs = 0;
+         }
+         unser_session_label(&label, rec);
+         memset(&jr, 0, sizeof(jr));
+         jr.JobId = label.JobId;
+         if (db_get_job_record(bjcr, db, &jr)) {
+            /* Job record already exists in DB */
             update_db = 0;  /* don't change db in create_job_record */
-           if (verbose) {
+            if (verbose) {
                Pmsg1(000, _("SOS_LABEL: Found Job record for JobId: %d\n"), jr.JobId);
-           }
-        } else {
-           /* Must create a Job record in DB */
-           if (!update_db) {
+            }
+         } else {
+            /* Must create a Job record in DB */
+            if (!update_db) {
                Pmsg1(000, _("SOS_LABEL: Job record not found for JobId: %d\n"),
-                 jr.JobId);
-           }
-        }
-        /* Create Client record if not already there */
-           strcpy(cr.Name, label.ClientName);
-           create_client_record(db, &cr);
-           jr.ClientId = cr.ClientId;
+                  jr.JobId);
+            }
+         }
+         /* Create Client record if not already there */
+            strcpy(cr.Name, label.ClientName);
+            create_client_record(db, &cr);
+            jr.ClientId = cr.ClientId;
 
          /* process label, if Job record exists don't update db */
-        mjcr = create_job_record(db, &jr, &label, rec);
-        update_db = save_update_db;
-
-        jr.PoolId = pr.PoolId;
-        /* Set start positions into JCR */
-        if (dev->state & ST_TAPE) {
-           mjcr->StartBlock = dev->block_num;
-           mjcr->StartFile = dev->file;
-        } else {
-           mjcr->StartBlock = (uint32_t)dev->file_addr;
-           mjcr->StartFile = (uint32_t)(dev->file_addr >> 32);
-        }
-        mjcr->start_time = jr.StartTime;
-        mjcr->JobLevel = jr.Level;
-
-        mjcr->client_name = get_pool_memory(PM_FNAME);
-        pm_strcpy(&mjcr->client_name, label.ClientName);
-        mjcr->pool_type = get_pool_memory(PM_FNAME);
-        pm_strcpy(&mjcr->pool_type, label.PoolType);
-        mjcr->fileset_name = get_pool_memory(PM_FNAME);
-        pm_strcpy(&mjcr->fileset_name, label.FileSetName);
-        mjcr->pool_name = get_pool_memory(PM_FNAME);
-        pm_strcpy(&mjcr->pool_name, label.PoolName);
-
-        if (rec->VolSessionId != jr.VolSessionId) {
+         mjcr = create_job_record(db, &jr, &label, rec);
+         update_db = save_update_db;
+
+         jr.PoolId = pr.PoolId;
+         /* Set start positions into JCR */
+         if (dev->state & ST_TAPE) {
+            mjcr->StartBlock = dev->block_num;
+            mjcr->StartFile = dev->file;
+         } else {
+            mjcr->StartBlock = (uint32_t)dev->file_addr;
+            mjcr->StartFile = (uint32_t)(dev->file_addr >> 32);
+         }
+         mjcr->start_time = jr.StartTime;
+         mjcr->JobLevel = jr.Level;
+
+         mjcr->client_name = get_pool_memory(PM_FNAME);
+         pm_strcpy(&mjcr->client_name, label.ClientName);
+         mjcr->pool_type = get_pool_memory(PM_FNAME);
+         pm_strcpy(&mjcr->pool_type, label.PoolType);
+         mjcr->fileset_name = get_pool_memory(PM_FNAME);
+         pm_strcpy(&mjcr->fileset_name, label.FileSetName);
+         mjcr->pool_name = get_pool_memory(PM_FNAME);
+         pm_strcpy(&mjcr->pool_name, label.PoolName);
+
+         if (rec->VolSessionId != jr.VolSessionId) {
             Pmsg3(000, _("SOS_LABEL: VolSessId mismatch for JobId=%u. DB=%d Vol=%d\n"),
-              jr.JobId,
-              jr.VolSessionId, rec->VolSessionId);
-           return;
-        }
-        if (rec->VolSessionTime != jr.VolSessionTime) {
+               jr.JobId,
+               jr.VolSessionId, rec->VolSessionId);
+            return;
+         }
+         if (rec->VolSessionTime != jr.VolSessionTime) {
             Pmsg3(000, _("SOS_LABEL: VolSessTime mismatch for JobId=%u. DB=%d Vol=%d\n"),
-              jr.JobId,
-              jr.VolSessionTime, rec->VolSessionTime);
-           return;
-        }
-        if (jr.PoolId != pr.PoolId) {
+               jr.JobId,
+               jr.VolSessionTime, rec->VolSessionTime);
+            return;
+         }
+         if (jr.PoolId != pr.PoolId) {
             Pmsg3(000, _("SOS_LABEL: PoolId mismatch for JobId=%u. DB=%d Vol=%d\n"),
-              jr.JobId,
-              jr.PoolId, pr.PoolId);
-           return;
-        }
-        break;
+               jr.JobId,
+               jr.PoolId, pr.PoolId);
+            return;
+         }
+         break;
 
       case EOS_LABEL:
-        unser_session_label(&elabel, rec);
+         unser_session_label(&elabel, rec);
 
-        /* Create FileSet record */
-        strcpy(fsr.FileSet, label.FileSetName);
-        strcpy(fsr.MD5, label.FileSetMD5);
-        create_fileset_record(db, &fsr);
-        jr.FileSetId = fsr.FileSetId;
+         /* Create FileSet record */
+         strcpy(fsr.FileSet, label.FileSetName);
+         strcpy(fsr.MD5, label.FileSetMD5);
+         create_fileset_record(db, &fsr);
+         jr.FileSetId = fsr.FileSetId;
 
-        mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
-        if (!mjcr) {
+         mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
+         if (!mjcr) {
             Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
-                 rec->VolSessionId, rec->VolSessionTime);
-           break;
-        }
+                  rec->VolSessionId, rec->VolSessionTime);
+            break;
+         }
 
-        /* Do the final update to the Job record */
-        update_job_record(db, &jr, &elabel, rec);
+         /* Do the final update to the Job record */
+         update_job_record(db, &jr, &elabel, rec);
 
-        mjcr->end_time = jr.EndTime;
-        mjcr->JobStatus = JS_Terminated;
+         mjcr->end_time = jr.EndTime;
+         mjcr->JobStatus = JS_Terminated;
 
-        /* Create JobMedia record */
-        create_jobmedia_record(db, mjcr);
-        detach_jcr_from_device(dev, mjcr);
-        free_jcr(mjcr);
+         /* Create JobMedia record */
+         create_jobmedia_record(db, mjcr);
+         detach_jcr_from_device(dev, mjcr);
+         free_jcr(mjcr);
 
-        break;
+         break;
 
       case EOM_LABEL:
-        break;
-
-      case EOT_LABEL:             /* end of all tapes */
-        /* 
-         * Wiffle through all jobs still open and close
-         *   them.
-         */
-        if (update_db) {
-           for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
-              jr.JobId = mjcr->JobId;
-              jr.JobStatus = JS_ErrorTerminated;
-              jr.JobFiles = mjcr->JobFiles;
-              jr.JobBytes = mjcr->JobBytes;
-              jr.VolSessionId = mjcr->VolSessionId;
-              jr.VolSessionTime = mjcr->VolSessionTime;
-              jr.JobTDate = (utime_t)mjcr->start_time;
-              jr.ClientId = mjcr->ClientId;
-              free_jcr(mjcr);
-              if (!db_update_job_end_record(bjcr, db, &jr)) {
+         break;
+
+      case EOT_LABEL:              /* end of all tapes */
+         /* 
+          * Wiffle through all jobs still open and close
+          *   them.
+          */
+         if (update_db) {
+            for (mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
+               jr.JobId = mjcr->JobId;
+               jr.JobStatus = JS_ErrorTerminated;
+               jr.JobFiles = mjcr->JobFiles;
+               jr.JobBytes = mjcr->JobBytes;
+               jr.VolSessionId = mjcr->VolSessionId;
+               jr.VolSessionTime = mjcr->VolSessionTime;
+               jr.JobTDate = (utime_t)mjcr->start_time;
+               jr.ClientId = mjcr->ClientId;
+               free_jcr(mjcr);
+               if (!db_update_job_end_record(bjcr, db, &jr)) {
                   Pmsg1(0, _("Could not update job record. ERR=%s\n"), db_strerror(db));
-              }
-           }
-        }
-        mr.VolFiles = rec->File;
-        mr.VolBlocks = rec->Block;
-        mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
-        mr.VolMounts++;
-        update_media_record(db, &mr);
+               }
+            }
+         }
+         mr.VolFiles = rec->File;
+         mr.VolBlocks = rec->Block;
+         mr.VolBytes += mr.VolBlocks * WRITE_BLKHDR_LENGTH; /* approx. */
+         mr.VolMounts++;
+         update_media_record(db, &mr);
          Pmsg3(0, _("End of all Volumes. VolFiles=%u VolBlocks=%u VolBytes=%s\n"), mr.VolFiles,
-                   mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
-        break;
+                    mr.VolBlocks, edit_uint64_with_commas(mr.VolBytes, ec1));
+         break;
       default:
-        break;
+         break;
       } /* end switch */
       return;
    }
@@ -546,33 +546,33 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
 
       if (attr->file_index != rec->FileIndex) {
          Emsg2(M_ERROR_TERM, 0, _("Record header file index %ld not equal record index %ld\n"),
-           rec->FileIndex, attr->file_index);
+            rec->FileIndex, attr->file_index);
       }
        
       if (verbose > 1) {
-        uint32_t LinkFI;
-        decode_stat(attr->attr, &attr->statp, &LinkFI);
-        print_ls_output(bjcr, attr);
+         uint32_t LinkFI;
+         decode_stat(attr->attr, &attr->statp, &LinkFI);
+         print_ls_output(bjcr, attr);
       }
       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
       if (!mjcr) {
-        if (mr.VolJobs > 0) {
+         if (mr.VolJobs > 0) {
             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Attributes record.\n"),
-                        rec->VolSessionId, rec->VolSessionTime);
-        } else {
-           ignored_msgs++;
-        }
-        return;
+                         rec->VolSessionId, rec->VolSessionTime);
+         } else {
+            ignored_msgs++;
+         }
+         return;
       }
       fr.JobId = mjcr->JobId;
       fr.FileId = 0;
       if (db_get_file_attributes_record(bjcr, db, attr->fname, &fr)) {
-        if (verbose > 1) {
+         if (verbose > 1) {
             Pmsg1(000, _("File record already exists for: %s\n"), attr->fname);
-        }
+         }
       } else {
-        create_file_attributes_record(db, mjcr, attr->fname, attr->lname, 
-           attr->type, attr->attr, rec);
+         create_file_attributes_record(db, mjcr, attr->fname, attr->lname, 
+            attr->type, attr->attr, rec);
       }
       free_jcr(mjcr);
       break;
@@ -583,66 +583,66 @@ static void record_cb(JCR *bjcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
    case STREAM_SPARSE_DATA:
       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
       if (!mjcr) {
-        if (mr.VolJobs > 0) {
+         if (mr.VolJobs > 0) {
             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for File Data record.\n"),
-                        rec->VolSessionId, rec->VolSessionTime);
-        } else {
-           ignored_msgs++;
-        }
-        return;
+                         rec->VolSessionId, rec->VolSessionTime);
+         } else {
+            ignored_msgs++;
+         }
+         return;
       }
       mjcr->JobBytes += rec->data_len;
       if (rec->Stream == STREAM_SPARSE_DATA) {
-        mjcr->JobBytes -= sizeof(uint64_t);
+         mjcr->JobBytes -= sizeof(uint64_t);
       }
-        
-      free_jcr(mjcr);                /* done using JCR */
+         
+      free_jcr(mjcr);                 /* done using JCR */
       break;
 
    case STREAM_GZIP_DATA:
       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
       if (!mjcr) {
-        if (mr.VolJobs > 0) {
+         if (mr.VolJobs > 0) {
             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for GZIP Data record.\n"),
-                        rec->VolSessionId, rec->VolSessionTime);
-        } else {
-           ignored_msgs++;
-        }
-        return;
+                         rec->VolSessionId, rec->VolSessionTime);
+         } else {
+            ignored_msgs++;
+         }
+         return;
       }
       mjcr->JobBytes += rec->data_len; /* No correct, we should expand it */
-      free_jcr(mjcr);                /* done using JCR */
+      free_jcr(mjcr);                 /* done using JCR */
       break;
 
    case STREAM_SPARSE_GZIP_DATA:
       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
       if (!mjcr) {
-        if (mr.VolJobs > 0) {
+         if (mr.VolJobs > 0) {
             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Sparse GZIP Data record.\n"),
-                        rec->VolSessionId, rec->VolSessionTime);
-        } else {
-           ignored_msgs++;
-        }
-        return;
+                         rec->VolSessionId, rec->VolSessionTime);
+         } else {
+            ignored_msgs++;
+         }
+         return;
       }
       mjcr->JobBytes += rec->data_len - sizeof(uint64_t); /* No correct, we should expand it */
-      free_jcr(mjcr);                /* done using JCR */
+      free_jcr(mjcr);                 /* done using JCR */
       break;
 
    /* Win32 GZIP stream */
    case STREAM_WIN32_GZIP_DATA:
       mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
       if (!mjcr) {
-        if (mr.VolJobs > 0) {
+         if (mr.VolJobs > 0) {
             Pmsg2(000, _("Could not find Job SessId=%d SessTime=%d for Win32 GZIP Data record.\n"),
-                        rec->VolSessionId, rec->VolSessionTime);
-        } else {
-           ignored_msgs++;
-        }
-        return;
+                         rec->VolSessionId, rec->VolSessionTime);
+         } else {
+            ignored_msgs++;
+         }
+         return;
       }
       mjcr->JobBytes += rec->data_len;
-      free_jcr(mjcr);                /* done using JCR */
+      free_jcr(mjcr);                 /* done using JCR */
       break;
 
    case STREAM_MD5_SIGNATURE:
@@ -710,8 +710,8 @@ static void dird_free_jcr(JCR *jcr)
  *   record, and then create the attributes record.
  */
 static int create_file_attributes_record(B_DB *db, JCR *mjcr,
-                              char *fname, char *lname, int type,
-                              char *ap, DEV_RECORD *rec)
+                               char *fname, char *lname, int type,
+                               char *ap, DEV_RECORD *rec)
 {
 
    ar.fname = fname;
@@ -866,8 +866,8 @@ static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
    } else {
       if (!db_create_fileset_record(bjcr, db, fsr)) {
          Pmsg2(0, _("Could not create FileSet record \"%s\". ERR=%s\n"), 
-           fsr->FileSet, db_strerror(db));
-        return 0;
+            fsr->FileSet, db_strerror(db));
+         return 0;
       }
       if (verbose) {
          Pmsg1(000, _("Created FileSet record \"%s\"\n"), fsr->FileSet);
@@ -882,7 +882,7 @@ static int create_fileset_record(B_DB *db, FILESET_DBR *fsr)
  *  begins running.
  */
 static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label, 
-                            DEV_RECORD *rec)
+                             DEV_RECORD *rec)
 {
    JCR *mjcr;
    struct date_time dt;
@@ -927,8 +927,8 @@ static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
       return mjcr;
    }
    Pmsg2(000, _("Created new JobId=%u record for original JobId=%u\n"), jr->JobId, 
-        label->JobId);
-   mjcr->JobId = jr->JobId;          /* set new JobId */
+         label->JobId);
+   mjcr->JobId = jr->JobId;           /* set new JobId */
    return mjcr;
 }
 
@@ -937,7 +937,7 @@ static JCR *create_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *label,
  *  at Job termination time.
  */
 static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
-                             DEV_RECORD *rec)
+                              DEV_RECORD *rec)
 {
    struct date_time dt;
    struct tm tm;
@@ -946,7 +946,7 @@ static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
    mjcr = get_jcr_by_session(rec->VolSessionId, rec->VolSessionTime);
    if (!mjcr) {
       Pmsg2(000, _("Could not find SessId=%d SessTime=%d for EOS record.\n"),
-                  rec->VolSessionId, rec->VolSessionTime);
+                   rec->VolSessionId, rec->VolSessionTime);
       return 0;
    }
    if (elabel->VerNum >= 11) {
@@ -992,18 +992,18 @@ static int update_job_record(B_DB *db, JOB_DBR *jr, SESSION_LABEL *elabel,
       switch (mjcr->JobStatus) {
       case JS_Terminated:
          term_msg = _("Backup OK");
-        break;
+         break;
       case JS_FatalError:
       case JS_ErrorTerminated:
          term_msg = _("*** Backup Error ***");
-        break;
+         break;
       case JS_Canceled:
          term_msg = _("Backup Canceled");
-        break;
+         break;
       default:
-        term_msg = term_code;
+         term_msg = term_code;
          sprintf(term_code, _("Job Termination code: %d"), mjcr->JobStatus);
-        break;
+         break;
       }
       bstrftime(sdt, sizeof(sdt), mjcr->start_time);
       bstrftime(edt, sizeof(edt), mjcr->end_time);
@@ -1021,20 +1021,20 @@ Volume Session Id:      %d\n\
 Volume Session Time:    %d\n\
 Last Volume Bytes:      %s\n\
 Termination:            %s\n\n"),
-       edt,
-       mjcr->JobId,
-       mjcr->Job,
-       mjcr->fileset_name,
-       job_level_to_str(mjcr->JobLevel),
-       mjcr->client_name,
-       sdt,
-       edt,
-       edit_uint64_with_commas(mjcr->JobFiles, ec1),
-       edit_uint64_with_commas(mjcr->JobBytes, ec2),
-       mjcr->VolSessionId,
-       mjcr->VolSessionTime,
-       edit_uint64_with_commas(mr.VolBytes, ec3),
-       term_msg);
+        edt,
+        mjcr->JobId,
+        mjcr->Job,
+        mjcr->fileset_name,
+        job_level_to_str(mjcr->JobLevel),
+        mjcr->client_name,
+        sdt,
+        edt,
+        edit_uint64_with_commas(mjcr->JobFiles, ec1),
+        edit_uint64_with_commas(mjcr->JobBytes, ec2),
+        mjcr->VolSessionId,
+        mjcr->VolSessionTime,
+        edit_uint64_with_commas(mr.VolBytes, ec3),
+        term_msg);
    }
    free_jcr(mjcr);
    return 1;
@@ -1073,7 +1073,7 @@ static int create_jobmedia_record(B_DB *db, JCR *mjcr)
    }
    if (verbose) {
       Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"), 
-               jmr.JobId, jmr.MediaId);
+                jmr.JobId, jmr.MediaId);
    }
    return 1;
 }
@@ -1089,9 +1089,9 @@ static int update_SIG_record(B_DB *db, char *SIGbuf, DEV_RECORD *rec, int type)
    if (!mjcr) {
       if (mr.VolJobs > 0) {
          Pmsg2(000, _("Could not find SessId=%d SessTime=%d for MD5/SHA1 record.\n"),
-                     rec->VolSessionId, rec->VolSessionTime);
+                      rec->VolSessionId, rec->VolSessionTime);
       } else {
-        ignored_msgs++;
+         ignored_msgs++;
       }
       return 0;
    }
@@ -1122,7 +1122,7 @@ static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
    JCR *jobjcr;
    /*
     * Transfer as much as possible to the Job JCR. Most important is
-    *  the JobId and the ClientId.
+    *   the JobId and the ClientId.
     */
    jobjcr = new_jcr(sizeof(JCR), dird_free_jcr);
    jobjcr->JobType = jr->Type;
@@ -1140,19 +1140,19 @@ static JCR *create_jcr(JOB_DBR *jr, DEV_RECORD *rec, uint32_t JobId)
 }
 
 /* Dummies to replace askdir.c */
-int    dir_get_volume_info(JCR *jcr, int writing) { return 1;}
-int    dir_find_next_appendable_volume(JCR *jcr) { return 1;}
-int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
-int    dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int    dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
-int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
-int    dir_send_job_status(JCR *jcr) {return 1;}
+int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw  writing) { return 1;}
+int     dir_find_next_appendable_volume(JCR *jcr) { return 1;}
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev) { return 1; }
+int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int     dir_send_job_status(JCR *jcr) {return 1;}
 
 
 int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
 {
    fprintf(stderr, _("Mount Volume %s on device %s and press return when ready: "),
       jcr->VolumeName, dev_name(dev));
-   getchar();  
+   getchar();   
    return 1;
 }
index ff325d8cb0614aa2ab39be529412b0e634d128a3..ee630be1e8df92bb90d4e06b4fc82e36feab298d 100644 (file)
@@ -49,7 +49,7 @@ char VolName[MAX_NAME_LENGTH];
 DEVICE *dev = NULL;
 DEVRES *device = NULL;
 
-           
+            
 /* Forward referenced subroutines */
 static void do_tape_cmds();
 static void helpcmd();
@@ -111,7 +111,7 @@ int get_cmd(char *prompt);
 
 /*********************************************************************
  *
- *        Main Bacula Pool Creation Program
+ *         Main Bacula Pool Creation Program
  *
  */
 int main(int argc, char *argv[])
@@ -122,7 +122,7 @@ int main(int argc, char *argv[])
    /* Sanity checks */
    if (TAPE_BSIZE % DEV_BSIZE != 0 || TAPE_BSIZE / DEV_BSIZE == 0) {
       Emsg2(M_ABORT, 0, "Tape block size (%d) not multiple of system size (%d)\n",
-        TAPE_BSIZE, DEV_BSIZE);
+         TAPE_BSIZE, DEV_BSIZE);
    }
    if (TAPE_BSIZE != (1 << (ffs(TAPE_BSIZE)-1))) {
       Emsg1(M_ABORT, 0, "Tape block size (%d) is not a power of 2\n", TAPE_BSIZE);
@@ -137,36 +137,36 @@ int main(int argc, char *argv[])
    while ((ch = getopt(argc, argv, "b:c:d:sv?")) != -1) {
       switch (ch) {
          case 'b':                    /* bootstrap file */
-           bsr = parse_bsr(NULL, optarg);
-//         dump_bsr(bsr);
-           break;
+            bsr = parse_bsr(NULL, optarg);
+//          dump_bsr(bsr);
+            break;
 
          case 'c':                    /* specify config file */
-           if (configfile != NULL) {
-              free(configfile);
-           }
-           configfile = bstrdup(optarg);
-           break;
+            if (configfile != NULL) {
+               free(configfile);
+            }
+            configfile = bstrdup(optarg);
+            break;
 
          case 'd':                    /* set debug level */
-           debug_level = atoi(optarg);
-           if (debug_level <= 0) {
-              debug_level = 1; 
-           }
-           break;
+            debug_level = atoi(optarg);
+            if (debug_level <= 0) {
+               debug_level = 1; 
+            }
+            break;
 
          case 's':
-           signals = FALSE;
-           break;
+            signals = FALSE;
+            break;
 
          case 'v':
-           verbose++;
-           break;
+            verbose++;
+            break;
 
          case '?':
-        default:
-           helpcmd();
-           exit(0);
+         default:
+            helpcmd();
+            exit(0);
 
       }  
    }
@@ -210,9 +210,9 @@ int main(int argc, char *argv[])
       Dmsg0(129, "Opening device.\n");
       if (open_dev(dev, jcr->VolumeName, READ_WRITE) < 0) {
          Emsg1(M_FATAL, 0, _("dev open failed: %s\n"), dev->errmsg);
-        unlock_device(dev);
-        free_block(block);
-        goto terminate;
+         unlock_device(dev);
+         free_block(block);
+         goto terminate;
       }
    }
    Dmsg1(129, "open_dev %s OK\n", dev_name(dev));
@@ -258,7 +258,7 @@ static void terminate_btape(int stat)
    }
 
    term_msg();
-   close_memory_pool();              /* free memory in pool */
+   close_memory_pool();               /* free memory in pool */
 
    sm_dump(False);
    exit(stat);
@@ -280,11 +280,11 @@ static void labelcmd()
    LockRes();
    for (device=NULL; (device=(DEVRES *)GetNextRes(R_DEVICE, (RES *)device)); ) {
       if (strcmp(device->device_name, dev->dev_name) == 0) {
-        jcr->device = device;        /* Arggg a bit of duplication here */
-        device->dev = dev;
-        dev->device = device;
-        found = 1;
-        break;
+         jcr->device = device;        /* Arggg a bit of duplication here */
+         device->dev = dev;
+         dev->device = device;
+         found = 1;
+         break;
       }
    } 
    UnlockRes();
@@ -297,10 +297,10 @@ static void labelcmd()
       strcpy(cmd, VolumeName);
    } else {
       if (!get_cmd("Enter Volume Name: ")) {
-        return;
+         return;
       }
    }
-        
+         
    if (!(dev->state & ST_OPENED)) {
       if (!open_device(dev)) {
          Pmsg1(0, "Device open failed. ERR=%s\n", strerror_dev(dev));
@@ -310,7 +310,7 @@ static void labelcmd()
 }
 
 /*
- * Read the tape label  
+ * Read the tape label   
  */
 static void readlabelcmd()
 {
@@ -323,28 +323,28 @@ static void readlabelcmd()
    switch (stat) {
       case VOL_NO_LABEL:
          Pmsg0(0, "Volume has no label.\n");
-        break;
+         break;
       case VOL_OK:
          Pmsg0(0, "Volume label read correctly.\n");
-        break;
+         break;
       case VOL_IO_ERROR:
          Pmsg1(0, "I/O error on device: ERR=%s", strerror_dev(dev));
-        break;
+         break;
       case VOL_NAME_ERROR:
          Pmsg0(0, "Volume name error\n");
-        break;
+         break;
       case VOL_CREATE_ERROR:
          Pmsg1(0, "Error creating label. ERR=%s", strerror_dev(dev));
-        break;
+         break;
       case VOL_VERSION_ERROR:
          Pmsg0(0, "Volume version error.\n");
-        break;
+         break;
       case VOL_LABEL_ERROR:
          Pmsg0(0, "Bad Volume label type.\n");
-        break;
+         break;
       default:
          Pmsg0(0, "Unknown error.\n");
-        break;
+         break;
    }
 
    debug_level = 20;
@@ -404,7 +404,7 @@ static void weofcmd()
 }
 
 
-/* Go to the end of the medium -- raw command  
+/* Go to the end of the medium -- raw command   
  * The idea was orginally that the end of the Bacula
  * medium would be flagged differently. This is not
  * currently the case. So, this is identical to the
@@ -534,11 +534,11 @@ static void rectestcmd()
       rec->data_len = i;
       sm_check(__FILE__, __LINE__, False);
       if (write_record_to_block(block, rec)) {
-        empty_block(block);
-        blkno++;
+         empty_block(block);
+         blkno++;
          Pmsg2(0, "Block %d i=%d\n", blkno, i);
       } else {
-        break;
+         break;
       }
       sm_check(__FILE__, __LINE__, False);
    }
@@ -638,7 +638,7 @@ static int re_read_block_test()
    for (int i=0; i<len; i++) {
       if (rec->data[i] != 3) {
          Pmsg0(0, _("Bad data in record. Test failed!\n"));
-        goto bail_out;
+         goto bail_out;
       }
    }
    Pmsg0(0, _("\nBlock re-read correct. Test succeeded!\n"));
@@ -675,14 +675,14 @@ static int append_test()
 "             and three records in file 2\n\n"));
    rewindcmd();
    wrcmd();
-   weofcmd();     /* end file 0 */
+   weofcmd();      /* end file 0 */
    wrcmd();
    wrcmd();
-   weofcmd();     /* end file 1 */
+   weofcmd();      /* end file 1 */
    wrcmd();
    wrcmd();
    wrcmd();
-   weofcmd();    /* end file 2 */
+   weofcmd();     /* end file 2 */
    rewindcmd();
    Pmsg0(0, _("Now moving to end of medium.\n"));
    eodcmd();
@@ -720,39 +720,39 @@ static void testcmd()
    int stat;
 
    stat = append_test();
-   if (stat == 1) {                  /* OK get out */
+   if (stat == 1) {                   /* OK get out */
       goto all_done;
    }
-   if (stat == -1) {                 /* first test failed */
+   if (stat == -1) {                  /* first test failed */
       if (dev_cap(dev, CAP_EOM)) {
          Pmsg0(-1, "\nAppend test failed. Attempting again.\n"
                    "Setting \"Hardware End of Medium = no\" and retrying append test.\n\n");
-        dev->capabilities &= ~CAP_EOM; /* turn off eom */
-        stat = append_test();
-        if (stat == 1) {
+         dev->capabilities &= ~CAP_EOM; /* turn off eom */
+         stat = append_test();
+         if (stat == 1) {
             Pmsg0(-1, "\n\nIt looks like the test worked this time, please add:\n\n"
                      "    Hardware End of Medium = No\n\n"
                      "to your Device resource in the Storage conf file.\n");
-           goto all_done;
-        }
-        if (stat == -1) {
+            goto all_done;
+         }
+         if (stat == -1) {
             Pmsg0(-1, "\n\nThat appears not to have corrected the problem.\n");
-           goto all_done;
-        }
-        /* Wrong count after append */
-        if (stat == -2) {
+            goto all_done;
+         }
+         /* Wrong count after append */
+         if (stat == -2) {
             Pmsg0(-1, "\n\nIt looks like the append failed. Attempting again.\n"
                      "Setting \"BSF at EOM = yes\" and retrying append test.\n");
-           dev->capabilities |= CAP_BSFATEOM; /* backspace on eom */
-           stat = append_test();
-           if (stat == 1) {
+            dev->capabilities |= CAP_BSFATEOM; /* backspace on eom */
+            stat = append_test();
+            if (stat == 1) {
                Pmsg0(-1, "\n\nIt looks like the test worked this time, please add:\n\n"
                      "    Hardware End of Medium = No\n"
                      "    BSR at EOM = yes\n\n"
                      "to your Device resource in the Storage conf file.\n");
-              goto all_done;
-           }
-        }
+               goto all_done;
+            }
+         }
 
          Pmsg0(-1, "\n!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
                "Unable to correct the problem. You MUST fix this\n"
@@ -909,45 +909,45 @@ static void scancmd()
    Pmsg1(0, _("Starting scan at file %u\n"), dev->file);
    for (;;) {
       if ((stat = read(dev->fd, buf, sizeof(buf))) < 0) {
-        clrerror_dev(dev, -1);
+         clrerror_dev(dev, -1);
          Mmsg2(&dev->errmsg, "read error on %s. ERR=%s.\n",
-           dev->dev_name, strerror(dev->dev_errno));
+            dev->dev_name, strerror(dev->dev_errno));
          Pmsg2(0, "Bad status from read %d. ERR=%s\n", stat, strerror_dev(dev));
-        if (blocks > 0)
+         if (blocks > 0)
             printf("%d block%s of %d bytes in file %d\n",        
                     blocks, blocks>1?"s":"", block_size, dev->file);
-        return;
+         return;
       }
       Dmsg1(200, "read status = %d\n", stat);
 /*    sleep(1); */
       if (stat != block_size) {
-        update_pos_dev(dev);
-        if (blocks > 0) {
+         update_pos_dev(dev);
+         if (blocks > 0) {
             printf("%d block%s of %d bytes in file %d\n", 
                  blocks, blocks>1?"s":"", block_size, dev->file);
-           blocks = 0;
-        }
-        block_size = stat;
+            blocks = 0;
+         }
+         block_size = stat;
       }
-      if (stat == 0) {               /* EOF */
-        update_pos_dev(dev);
+      if (stat == 0) {                /* EOF */
+         update_pos_dev(dev);
          printf("End of File mark.\n");
-        /* Two reads of zero means end of tape */
-        if (dev->state & ST_EOF)
-           dev->state |= ST_EOT;
-        else {
-           dev->state |= ST_EOF;
-           dev->file++;
-        }
-        if (dev->state & ST_EOT) {
+         /* Two reads of zero means end of tape */
+         if (dev->state & ST_EOF)
+            dev->state |= ST_EOT;
+         else {
+            dev->state |= ST_EOF;
+            dev->file++;
+         }
+         if (dev->state & ST_EOT) {
             printf("End of tape\n");
-           break;
-        }
-      } else {                       /* Got data */
-        dev->state &= ~ST_EOF;
-        blocks++;
-        tot_blocks++;
-        bytes += stat;
+            break;
+         }
+      } else {                        /* Got data */
+         dev->state &= ~ST_EOF;
+         blocks++;
+         tot_blocks++;
+         bytes += stat;
       }
    }
    update_pos_dev(dev);
@@ -980,58 +980,58 @@ static void scan_blocks()
    for (;;) {
       if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
          Dmsg1(100, "!read_block(): ERR=%s\n", strerror_dev(dev));
-        if (dev->state & ST_EOT) {
-           if (blocks > 0) {
+         if (dev->state & ST_EOT) {
+            if (blocks > 0) {
                printf("%d block%s of %d bytes in file %d\n", 
                     blocks, blocks>1?"s":"", block_size, dev->file);
-              blocks = 0;
-           }
-           goto bail_out;
-        }
-        if (dev->state & ST_EOF) {
-           if (blocks > 0) {
+               blocks = 0;
+            }
+            goto bail_out;
+         }
+         if (dev->state & ST_EOF) {
+            if (blocks > 0) {
                printf("%d block%s of %d bytes in file %d\n",        
                        blocks, blocks>1?"s":"", block_size, dev->file);
-              blocks = 0;
-           }
+               blocks = 0;
+            }
             printf(_("End of File mark.\n"));
-           continue;
-        }
-        if (dev->state & ST_SHORT) {
-           if (blocks > 0) {
+            continue;
+         }
+         if (dev->state & ST_SHORT) {
+            if (blocks > 0) {
                printf("%d block%s of %d bytes in file %d\n",        
                        blocks, blocks>1?"s":"", block_size, dev->file);
-              blocks = 0;
-           }
+               blocks = 0;
+            }
             printf(_("Short block read.\n"));
-           continue;
-        }
+            continue;
+         }
          printf(_("Error reading block. ERR=%s\n"), strerror_dev(dev));
-        goto bail_out;
+         goto bail_out;
       }
       if (block->block_len != block_size) {
-        if (blocks > 0) {
+         if (blocks > 0) {
             printf("%d block%s of %d bytes in file %d\n",        
                     blocks, blocks>1?"s":"", block_size, dev->file);
-           blocks = 0;
-        }
-        block_size = block->block_len;
+            blocks = 0;
+         }
+         block_size = block->block_len;
       }
       blocks++;
       tot_blocks++;
       bytes += block->block_len;
       Dmsg5(100, "Blk=%u blen=%u bVer=%d SessId=%u SessTim=%u\n",
-        block->BlockNumber, block->block_len, block->BlockVer,
-        block->VolSessionId, block->VolSessionTime);
+         block->BlockNumber, block->block_len, block->BlockVer,
+         block->VolSessionId, block->VolSessionTime);
       if (verbose == 1) {
-        DEV_RECORD *rec = new_record();
-        read_record_from_block(block, rec);
+         DEV_RECORD *rec = new_record();
+         read_record_from_block(block, rec);
          Pmsg7(-1, "Block: %u blen=%u First rec FI=%s SessId=%u SessTim=%u Strm=%s rlen=%d\n",
-             block->BlockNumber, block->block_len,
-             FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
-             stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
-        rec->remainder = 0;
-        free_record(rec);
+              block->BlockNumber, block->block_len,
+              FI_to_ascii(rec->FileIndex), rec->VolSessionId, rec->VolSessionTime,
+              stream_to_ascii(rec->Stream, rec->FileIndex), rec->data_len);
+         rec->remainder = 0;
+         free_record(rec);
       } else if (verbose > 1) {
          dump_block(block, "");
       }
@@ -1066,7 +1066,7 @@ static void statcmd()
 /* 
  * First we label the tape, then we fill
  *  it with data get a new tape and write a few blocks.
- */                           
+ */                            
 static void fillcmd()
 {
    DEV_RECORD rec;
@@ -1115,8 +1115,8 @@ This may take a long time -- hours! ...\n\n");
 
    /* 
     * Acquire output device for writing.  Note, after acquiring a
-    *  device, we MUST release it, which is done at the end of this
-    *  subroutine.
+    *   device, we MUST release it, which is done at the end of this
+    *   subroutine.
     */
    Dmsg0(100, "just before acquire_device\n");
    if (!(dev=acquire_device_for_append(jcr, dev, block))) {
@@ -1132,7 +1132,7 @@ This may take a long time -- hours! ...\n\n");
    if (!write_session_label(jcr, block, SOS_LABEL)) {
       set_jcr_job_status(jcr, JS_ErrorTerminated);
       Jmsg1(jcr, M_FATAL, 0, _("Write session label failed. ERR=%s\n"),
-        strerror_dev(dev));
+         strerror_dev(dev));
       ok = FALSE;
    }
    Pmsg0(-1, "Wrote Start Of Session label.\n");
@@ -1147,7 +1147,7 @@ This may take a long time -- hours! ...\n\n");
     * Generate data as if from File daemon, write to device   
     */
    jcr->VolFirstIndex = 0;
-   time(&jcr->run_time);             /* start counting time for rates */
+   time(&jcr->run_time);              /* start counting time for rates */
    Pmsg0(-1, "Begin writing records to first tape ...\n");
    for (file_index = 0; ok && !job_canceled(jcr); ) {
       rec.VolSessionId = jcr->VolSessionId;
@@ -1161,81 +1161,81 @@ This may take a long time -- hours! ...\n\n");
        */
       uint64_t *lp = (uint64_t *)rec.data;
       for (uint32_t i=0; i < (rec.data_len-sizeof(uint64_t))/sizeof(uint64_t); i++) {
-        *lp++ = ~file_index;
+         *lp++ = ~file_index;
       }
 
       Dmsg4(250, "before writ_rec FI=%d SessId=%d Strm=%s len=%d\n",
-        rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream, rec.FileIndex), 
-        rec.data_len);
+         rec.FileIndex, rec.VolSessionId, stream_to_ascii(rec.Stream, rec.FileIndex), 
+         rec.data_len);
        
       while (!write_record_to_block(block, &rec)) {
-        /*
-         * When we get here we have just filled a block
-         */
+         /*
+          * When we get here we have just filled a block
+          */
          Dmsg2(150, "!write_record_to_block data_len=%d rem=%d\n", rec.data_len,
-                   rec.remainder);
-
-        /* Write block to tape */
-        if (!flush_block(block, 1)) {
-           return;
-        }
-
-        /* Every 5000 blocks (approx 322MB) report where we are.
-         */
-        if ((block->BlockNumber % 5000) == 0) {
-           now = time(NULL);
-           now -= jcr->run_time;
-           if (now <= 0) {
-              now = 1;
-           }
-           kbs = (double)dev->VolCatInfo.VolCatBytes / (1000.0 * (double)now);
+                    rec.remainder);
+
+         /* Write block to tape */
+         if (!flush_block(block, 1)) {
+            return;
+         }
+
+         /* Every 5000 blocks (approx 322MB) report where we are.
+          */
+         if ((block->BlockNumber % 5000) == 0) {
+            now = time(NULL);
+            now -= jcr->run_time;
+            if (now <= 0) {
+               now = 1;
+            }
+            kbs = (double)dev->VolCatInfo.VolCatBytes / (1000.0 * (double)now);
             Pmsg3(-1, "Wrote block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
-              edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs);
-        }
-        /* Every 15000 blocks (approx 1GB) write an EOF.
-         */
-        if ((block->BlockNumber % 15000) == 0) {
+               edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), (float)kbs);
+         }
+         /* Every 15000 blocks (approx 1GB) write an EOF.
+          */
+         if ((block->BlockNumber % 15000) == 0) {
             Pmsg0(-1, "Flush block, write EOF\n");
-           flush_block(block, 0);
-           weof_dev(dev, 1);
-        }
-
-        /* Get out after writing 10 blocks to the second tape */
-        if (++BlockNumber > 10 && stop != 0) {      /* get out */
-           break;    
-        }
+            flush_block(block, 0);
+            weof_dev(dev, 1);
+         }
+
+         /* Get out after writing 10 blocks to the second tape */
+         if (++BlockNumber > 10 && stop != 0) {      /* get out */
+            break;    
+         }
       }
       if (!ok) {
          Pmsg0(000, _("Not OK\n"));
-        break;
+         break;
       }
       jcr->JobBytes += rec.data_len;   /* increment bytes this job */
       Dmsg4(190, "write_record FI=%s SessId=%d Strm=%s len=%d\n",
-        FI_to_ascii(rec.FileIndex), rec.VolSessionId, 
-        stream_to_ascii(rec.Stream, rec.FileIndex), rec.data_len);
+         FI_to_ascii(rec.FileIndex), rec.VolSessionId, 
+         stream_to_ascii(rec.Stream, rec.FileIndex), rec.data_len);
 
       /* Get out after writing 10 blocks to the second tape */
-      if (BlockNumber > 10 && stop != 0) {     /* get out */
+      if (BlockNumber > 10 && stop != 0) {      /* get out */
          Pmsg0(-1, "Done writing ...\n");
-        break;    
+         break;    
       }
    }
    if (stop > 0) {
       Dmsg0(100, "Write_end_session_label()\n");
       /* Create Job status for end of session label */
       if (!job_canceled(jcr) && ok) {
-        set_jcr_job_status(jcr, JS_Terminated);
+         set_jcr_job_status(jcr, JS_Terminated);
       } else if (!ok) {
-        set_jcr_job_status(jcr, JS_ErrorTerminated);
+         set_jcr_job_status(jcr, JS_ErrorTerminated);
       }
       if (!write_session_label(jcr, block, EOS_LABEL)) {
          Pmsg1(000, _("Error writting end session label. ERR=%s\n"), strerror_dev(dev));
-        ok = FALSE;
+         ok = FALSE;
       }
       /* Write out final block of this session */
       if (!write_block_to_device(jcr, dev, block)) {
          Pmsg0(-1, _("Set ok=FALSE after write_block_to_device.\n"));
-        ok = FALSE;
+         ok = FALSE;
       }
       Pmsg0(-1, "Wrote End Of Session label.\n");
    }
@@ -1278,7 +1278,7 @@ static void unfillcmd()
    if (!simple) {
       /* Close device so user can use autochanger if desired */
       if (dev_cap(dev, CAP_OFFLINEUNMOUNT)) {
-        offline_dev(dev);
+         offline_dev(dev);
       }
       force_close_dev(dev);
       get_cmd(_("Mount first tape. Press enter when ready: ")); 
@@ -1291,11 +1291,11 @@ static void unfillcmd()
       dev->state &= ~ST_READ;
       if (!acquire_device_for_read(jcr, dev, block)) {
          Pmsg1(-1, "%s", dev->errmsg);
-        return;
+         return;
       }
    }
 
-   time(&jcr->run_time);             /* start counting time for rates */
+   time(&jcr->run_time);              /* start counting time for rates */
    stop = 0;
    file_index = 0;
    if (!simple) {
@@ -1309,27 +1309,27 @@ static void unfillcmd()
       Pmsg0(000, "Rewinding tape ...\n");
       if (!rewind_dev(dev)) {
          Pmsg1(-1, _("Error rewinding: ERR=%s\n"), strerror_dev(dev));
-        goto bail_out;
+         goto bail_out;
       }
       if (last_file > 0) {
          Pmsg1(000, "Forward spacing to last file=%u\n", last_file);
-        if (!fsf_dev(dev, last_file)) {
+         if (!fsf_dev(dev, last_file)) {
             Pmsg1(-1, _("Error in FSF: ERR=%s\n"), strerror_dev(dev));
-           goto bail_out;
-        }
+            goto bail_out;
+         }
       }
       Pmsg1(-1, _("Forward space to file %u complete. Reading blocks ...\n"), 
-           last_file);
+            last_file);
       Pmsg1(-1, _("Now reading to block %u.\n"), last_block_num);
       for (uint32_t i=0; i <= last_block_num; i++) {
-        if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
+         if (!read_block_from_device(jcr, dev, block, NO_BLOCK_NUMBER_CHECK)) {
             Pmsg1(-1, _("Error reading blocks: ERR=%s\n"), strerror_dev(dev));
             Pmsg2(-1, _("Wanted block %u error at block %u\n"), last_block_num, i);
-           goto bail_out;
-        }
-        if (i > 0 && i % 1000 == 0) {
+            goto bail_out;
+         }
+         if (i > 0 && i % 1000 == 0) {
             Pmsg1(-1, _("At block %u\n"), i);
-        }
+         }
       }
       if (last_block) {
          dump_block(last_block, _("Last block written"));
@@ -1353,69 +1353,69 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
 {
    SESSION_LABEL label;
 
-   if (stop > 1 && !dumped) {        /* on second tape */
+   if (stop > 1 && !dumped) {         /* on second tape */
       dumped = 1;
       dump_block(block, "First block on second tape");
       Pmsg4(-1, "Blk: FileIndex=%d: block=%u size=%d vol=%s\n", 
-          rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName);
+           rec->FileIndex, block->BlockNumber, block->block_len, dev->VolHdr.VolName);
       Pmsg6(-1, "   Rec: VId=%d VT=%d FI=%s Strm=%s len=%d state=%x\n",
-          rec->VolSessionId, rec->VolSessionTime, 
-          FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex),
-          rec->data_len, rec->state);
+           rec->VolSessionId, rec->VolSessionTime, 
+           FI_to_ascii(rec->FileIndex), stream_to_ascii(rec->Stream, rec->FileIndex),
+           rec->data_len, rec->state);
    }
    if (rec->FileIndex < 0) {
       if (verbose > 1) {
-        dump_label_record(dev, rec, 1);
+         dump_label_record(dev, rec, 1);
       }
       switch (rec->FileIndex) {
       case PRE_LABEL:
          Pmsg0(-1, "Volume is prelabeled. This tape cannot be scanned.\n");
-        return;
+         return;
       case VOL_LABEL:
-        unser_volume_label(dev, rec);
+         unser_volume_label(dev, rec);
          Pmsg3(-1, "VOL_LABEL: block=%u size=%d vol=%s\n", block->BlockNumber, 
-           block->block_len, dev->VolHdr.VolName);
-        stop++;
-        break;
+            block->block_len, dev->VolHdr.VolName);
+         stop++;
+         break;
       case SOS_LABEL:
-        unser_session_label(&label, rec);
+         unser_session_label(&label, rec);
          Pmsg1(-1, "SOS_LABEL: JobId=%u\n", label.JobId);
-        break;
+         break;
       case EOS_LABEL:
-        unser_session_label(&label, rec);
+         unser_session_label(&label, rec);
          Pmsg2(-1, "EOS_LABEL: block=%u JobId=%u\n", block->BlockNumber, 
-           label.JobId);
-        break;
+            label.JobId);
+         break;
       case EOM_LABEL:
          Pmsg0(-1, "EOM_LABEL:\n");
-        break;
-      case EOT_LABEL:             /* end of all tapes */
-        char ec1[50];
-
-        if (LastBlock != block->BlockNumber) {
-           VolBytes += block->block_len;
-        }
-        LastBlock = block->BlockNumber;
-        now = time(NULL);
-        now -= jcr->run_time;
-        if (now <= 0) {
-           now = 1;
-        }
-        kbs = (double)VolBytes / (1000 * now);
+         break;
+      case EOT_LABEL:              /* end of all tapes */
+         char ec1[50];
+
+         if (LastBlock != block->BlockNumber) {
+            VolBytes += block->block_len;
+         }
+         LastBlock = block->BlockNumber;
+         now = time(NULL);
+         now -= jcr->run_time;
+         if (now <= 0) {
+            now = 1;
+         }
+         kbs = (double)VolBytes / (1000 * now);
          Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
-                 edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+                  edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
 
          Pmsg0(000, "End of all tapes.\n");
 
-        break;
+         break;
       default:
-        break;
+         break;
       }
       return;
    }
    if (++file_index != rec->FileIndex) {
       Pmsg3(000, "Incorrect FileIndex in Block %u. Got %d, expected %d.\n", 
-        block->BlockNumber, rec->FileIndex, file_index);
+         block->BlockNumber, rec->FileIndex, file_index);
    }
    /*
     * Now check that the right data is in the record.
@@ -1425,8 +1425,8 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
    for (uint32_t i=0; i < (REC_SIZE-sizeof(uint64_t))/sizeof(uint64_t); i++) {
       if (*lp++ != val) {
          Pmsg2(000, "Record %d contains bad data in Block %u.\n",
-           file_index, block->BlockNumber);
-        break;
+            file_index, block->BlockNumber);
+         break;
       }
    }
 
@@ -1438,11 +1438,11 @@ static void record_cb(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, DEV_RECORD *rec)
       now = time(NULL);
       now -= jcr->run_time;
       if (now <= 0) {
-        now = 1;
+         now = 1;
       }
       kbs = (double)VolBytes / (1000 * now);
       Pmsg3(000, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
-              edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+               edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
    }
    LastBlock = block->BlockNumber;
    if (end_of_tape) {
@@ -1475,44 +1475,44 @@ static int flush_block(DEV_BLOCK *block, int dump)
    this_file = dev->file;
    this_block_num = dev->block_num;
    if (!write_block_to_dev(jcr, dev, block)) {
-      Pmsg0(000, strerror_dev(dev));           
+      Pmsg0(000, strerror_dev(dev));            
       Pmsg3(000, "Block not written: FileIndex=%u Block=%u Size=%u\n", 
-        (unsigned)file_index, block->BlockNumber, block->block_len);
+         (unsigned)file_index, block->BlockNumber, block->block_len);
       Pmsg2(000, "last_block_num=%u this_block_num=%d\n", last_block_num,
-        this_block_num);
+         this_block_num);
       if (dump) {
          dump_block(block, "Block not written");
       }
       if (stop == 0) {
-        eot_block = block->BlockNumber;
-        eot_block_len = block->block_len;
-        eot_FileIndex = file_index;
+         eot_block = block->BlockNumber;
+         eot_block_len = block->block_len;
+         eot_FileIndex = file_index;
       }
       now = time(NULL);
       now -= jcr->run_time;
       if (now <= 0) {
-        now = 1;
+         now = 1;
       }
       kbs = (double)dev->VolCatInfo.VolCatBytes / (1000 * now);
       vol_size = dev->VolCatInfo.VolCatBytes;
       Pmsg2(000, "End of tape. VolumeCapacity=%s. Write rate = %.1f KB/s\n", 
-        edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs);
+         edit_uint64_with_commas(dev->VolCatInfo.VolCatBytes, ec1), kbs);
 
       if (simple) {
-        stop = -1;                   /* stop, but do simplified test */
+         stop = -1;                   /* stop, but do simplified test */
       } else {
-        /* Full test in progress */
-        if (!fixup_device_block_write_error(jcr, dev, block)) {
+         /* Full test in progress */
+         if (!fixup_device_block_write_error(jcr, dev, block)) {
             Pmsg1(000, _("Cannot fixup device error. %s\n"), strerror_dev(dev));
-           ok = FALSE;
-           unlock_device(dev);
-           return 0;
-        }
-        stop = 1;                                                     
-        BlockNumber = 0;             /* start counting for second tape */
+            ok = FALSE;
+            unlock_device(dev);
+            return 0;
+         }
+         stop = 1;                                                     
+         BlockNumber = 0;             /* start counting for second tape */
       }
       unlock_device(dev);
-      return 1;                      /* end of tape reached */
+      return 1;                       /* end of tape reached */
    }
 
    /*
@@ -1534,7 +1534,7 @@ static int flush_block(DEV_BLOCK *block, int dump)
 /* 
  * First we label the tape, then we fill
  *  it with data get a new tape and write a few blocks.
- */                           
+ */                            
 static void qfillcmd()
 {
    DEV_BLOCK *block;
@@ -1567,11 +1567,11 @@ static void qfillcmd()
       }
       if (!write_record_to_block(block, rec)) {
          Pmsg0(0, _("Error writing record to block.\n")); 
-        goto bail_out;
+         goto bail_out;
       }
       if (!write_block_to_dev(jcr, dev, block)) {
          Pmsg0(0, _("Error writing block to device.\n")); 
-        goto bail_out;
+         goto bail_out;
       }
    }
    printf("\n");
@@ -1612,10 +1612,10 @@ static void rawfill_cmd()
       *p = block_num;
       stat = write(dev->fd, block->buf, block->buf_len);
       if (stat == (int)block->buf_len) {
-        if ((block_num++ % 100) == 0) {
+         if ((block_num++ % 100) == 0) {
             printf("+");
-        }
-        continue;
+         }
+         continue;
       }
       break;
    }
@@ -1656,7 +1656,7 @@ static struct cmdstruct commands[] = {
  {"wr",         wrcmd,        "write a single Bacula block"}, 
  {"rr",         rrcmd,        "read a single record"},
  {"qfill",      qfillcmd,     "quick fill command"},
-            };
+             };
 #define comsize (sizeof(commands)/sizeof(struct cmdstruct))
 
 static void
@@ -1669,15 +1669,15 @@ do_tape_cmds()
       sm_check(__FILE__, __LINE__, False);
       found = 0;
       for (i=0; i<comsize; i++)       /* search for command */
-        if (fstrsch(cmd,  commands[i].key)) {
-           (*commands[i].func)();    /* go execute command */
-           found = 1;
-           break;
-        }
+         if (fstrsch(cmd,  commands[i].key)) {
+            (*commands[i].func)();    /* go execute command */
+            found = 1;
+            break;
+         }
       if (!found)
          Pmsg1(0, _("%s is an illegal command\n"), cmd);
       if (quit)
-        break;
+         break;
    }
 }
 
@@ -1705,7 +1705,7 @@ static void usage()
 
 }
 
-/*     
+/*      
  * Get next input command from terminal.  This
  * routine is REALLY primitive, and should be enhanced
  * to have correct backspacing, etc.
@@ -1723,14 +1723,14 @@ get_cmd(char *prompt)
    cmd[i] = 0;
    while ((ch = fgetc(stdin)) != EOF) { 
       if (ch == '\n') {
-        strip_trailing_junk(cmd);
-        return 1;
+         strip_trailing_junk(cmd);
+         return 1;
       } else if (ch == 4 || ch == 0xd3 || ch == 0x8) {
-        if (i > 0)
-           cmd[--i] = 0;
-        continue;
+         if (i > 0)
+            cmd[--i] = 0;
+         continue;
       } 
-        
+         
       cmd[i++] = ch;
       cmd[i] = 0;
    }
@@ -1739,15 +1739,15 @@ get_cmd(char *prompt)
 }
 
 /* Dummies to replace askdir.c */
-int    dir_get_volume_info(JCR *jcr, int writing) { return 1;}
-int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
-int    dir_create_jobmedia_record(JCR *jcr) { return 1; }
-int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
-int    dir_send_job_status(JCR *jcr) {return 1;}
+int     dir_get_volume_info(JCR *jcr, enum get_vol_info_rw  writing) { return 1;}
+int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel) { return 1; }
+int     dir_create_jobmedia_record(JCR *jcr) { return 1; }
+int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec) { return 1;}
+int     dir_send_job_status(JCR *jcr) {return 1;}
 
 
 
-int    dir_find_next_appendable_volume(JCR *jcr) 
+int     dir_find_next_appendable_volume(JCR *jcr) 
 { 
    return 1; 
 }
@@ -1762,7 +1762,7 @@ int dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev)
    Pmsg1(-1, "%s", dev->errmsg);           /* print reason */
    fprintf(stderr, "Mount Volume \"%s\" on device %s and press return when ready: ",
       jcr->VolumeName, dev_name(dev));
-   getchar();  
+   getchar();   
    return 1;
 }
 
@@ -1775,7 +1775,7 @@ int dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev)
    force_close_dev(dev);
    fprintf(stderr, "Mount next Volume on device %s and press return when ready: ",
       dev_name(dev));
-   getchar();  
+   getchar();   
    set_volume_name("TestVolume2", 2);
    labelcmd();
    VolumeName = NULL;
@@ -1801,7 +1801,7 @@ static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    }
    kbs = (double)VolBytes / (1000.0 * (double)now);
    Pmsg3(-1, "Read block=%u, VolBytes=%s rate=%.1f KB/s\n", block->BlockNumber,
-           edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
+            edit_uint64_with_commas(VolBytes, ec1), (float)kbs);
 
    if (strcmp(jcr->VolumeName, "TestVolume2") == 0) {
       end_of_tape = 1;
@@ -1818,7 +1818,7 @@ static int my_mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
       Pmsg2(0, "Cannot open Dev=%s, Vol=%s\n", dev_name(dev), jcr->VolumeName);
       return 0;
    }
-   return 1;                      /* next volume mounted */
+   return 1;                       /* next volume mounted */
 }
 
 static void set_volume_name(char *VolName, int volnum) 
index de8d15eb4b1eb20ce3ee3c7b1bd7a0bea64c686b..1994e23c1c290cb880029acf8cda4180615473bf 100644 (file)
@@ -270,7 +270,7 @@ open_dev(DEVICE *dev, char *VolName, int mode)
       if (dev->fd >= 0) {
         dev->dev_errno = 0;
         dev->state |= ST_OPENED;
-        dev->use_count++;
+        dev->use_count = 1;
         update_pos_dev(dev);             /* update position */
       }
       /* Stop any open() timer we started */
@@ -312,7 +312,7 @@ open_dev(DEVICE *dev, char *VolName, int mode)
       } else {
         dev->dev_errno = 0;
         dev->state |= ST_OPENED;
-        dev->use_count++;
+        dev->use_count = 1;
         update_pos_dev(dev);                /* update position */
       }
       Dmsg1(29, "open_dev: disk fd=%d opened\n", dev->fd);
@@ -1075,7 +1075,7 @@ int flush_dev(DEVICE *dev)
 static void do_close(DEVICE *dev)
 {
 
-   Dmsg0(29, "really close_dev\n");
+   Dmsg1(29, "really close_dev %s\n", dev->dev_name);
    close(dev->fd);
    /* Clean up device packet so it can be reused */
    dev->fd = -1;
@@ -1089,6 +1089,7 @@ static void do_close(DEVICE *dev)
       stop_thread_timer(dev->tid);
       dev->tid = 0;
    }
+   dev->use_count = 0;
 }
 
 /* 
@@ -1104,8 +1105,10 @@ close_dev(DEVICE *dev)
    }
    if (dev->fd >= 0 && dev->use_count == 1) {
       do_close(dev);
+   } else if (dev->use_count > 0) {
+      dev->use_count--;
    }
-   dev->use_count--;
+          
 #ifdef FULL_DEBUG
    ASSERT(dev->use_count >= 0);
 #endif
@@ -1121,9 +1124,9 @@ void force_close_dev(DEVICE *dev)
       Emsg0(M_FATAL, 0, dev->errmsg);
       return;
    }
-   Dmsg0(29, "really close_dev\n");
+   Dmsg1(29, "Force close_dev %s\n", dev->dev_name);
    do_close(dev);
-   dev->use_count--;
+
 #ifdef FULL_DEBUG
    ASSERT(dev->use_count >= 0);
 #endif
index 1d4e26411c624e3031ebac60110922cc5eaea47a..c927ee692151f44f4bc817e2e39b77ae90e93a37 100644 (file)
@@ -61,6 +61,13 @@ extern int debug_level;
  * medium condition or worse, and error condition.
  * Attempt to "recover" by obtaining a new Volume.
  *
+ * Here are a few things to know:
+ *  jcr->VolCatInfo contains the info on the "current" tape for this job.
+ *  dev->VolCatInfo contains the info on the tape in the drive.
+ *    The tape in the drive could have changed several times since 
+ *    the last time the job used it (jcr->VolCatInfo).
+ *  jcr->VolumeName is the name of the current/desired tape in the drive.
+ *
  * We enter with device locked, and 
  *     exit with device locked.
  *
@@ -90,36 +97,21 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    /* Unlock, but leave BLOCKED */
    unlock_device(dev);
 
-   /* 
-    * Walk through all attached jcrs creating a jobmedia_record()
-    */
-   Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
-   for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
-      if (mjcr->JobId == 0) {
-        continue;                 /* ignore console */
-      }
-      Dmsg1(100, "create JobMedia for Job %s\n", mjcr->Job);
-      if (dev->state & ST_TAPE) {
-        mjcr->EndBlock = dev->EndBlock;
-        mjcr->EndFile  = dev->EndFile;
-         Dmsg2(200, "Fixup EndFile=%u EndBlock=%u\n", mjcr->EndFile, mjcr->EndBlock);
-      } else {
-        mjcr->EndBlock = (uint32_t)dev->file_addr;
-        mjcr->EndFile = (uint32_t)(dev->file_addr >> 32);
-      }
-      if (!dir_create_jobmedia_record(mjcr)) {
-         Jmsg(mjcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=%s Job=%s\n"),
-           dev->VolCatInfo.VolCatName, mjcr->Job);
-        P(dev->mutex);
-        unblock_device(dev);
-        return 0;
-      }
-      mjcr->VolFirstIndex = 0;     /* prevent writing jobmedia second time */
+   /* Create a jobmedia record for this job */
+   if (!dir_create_jobmedia_record(jcr)) {
+       Jmsg(jcr, M_ERROR, 0, _("Could not create JobMedia record for Volume=\"%s\" Job=%s\n"),
+           jcr->VolCatInfo.VolCatName, jcr->Job);
+       P(dev->mutex);
+       unblock_device(dev);
+       return 0;
    }
 
    strcpy(dev->VolCatInfo.VolCatStatus, "Full");
    Dmsg2(200, "Call update_vol_info Stat=%s Vol=%s\n", 
       dev->VolCatInfo.VolCatStatus, dev->VolCatInfo.VolCatName);
+   dev->VolCatInfo.VolCatFiles = dev->file;   /* set number of files */
+   /* *****FIXME**** this needs to be done elsewhere */
+   dev->VolCatInfo.VolCatJobs++;             /* increment number of jobs */
    if (!dir_update_volume_info(jcr, &dev->VolCatInfo, 0)) {    /* send Volume info to Director */
       P(dev->mutex);
       unblock_device(dev);
@@ -165,24 +157,27 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    }
    free_block(label_blk);
 
-
-   /* Update start block/file for overflow block */
-   jcr->NumVolumes++;
+   /* 
+    * Walk through all attached jcrs indicating the volume has changed  
+    */
+   Dmsg1(100, "Walk attached jcrs. Volume=%s\n", dev->VolCatInfo.VolCatName);
    for (JCR *mjcr=NULL; (mjcr=next_attached_jcr(dev, mjcr)); ) {
-      /* Set new start/end positions */
-      if (dev->state & ST_TAPE) {
-        mjcr->StartBlock = dev->block_num;
-        mjcr->StartFile = dev->file;
-      } else {
-        mjcr->StartBlock = (uint32_t)dev->file_addr;
-        mjcr->StartFile  = (uint32_t)(dev->file_addr >> 32);
+      if (mjcr->JobId == 0) {
+        continue;                 /* ignore console */
+      }
+      mjcr->NewVol = true;
+      if (jcr != mjcr) {
+        pm_strcpy(&mjcr->VolumeName, jcr->VolumeName);  /* get a copy of the new volume */
       }
-      /* Set first FirstIndex for new Volume */
-      mjcr->VolFirstIndex = mjcr->JobFiles;
-      mjcr->run_time += time(NULL) - wait_time; /* correct run time */
    }
 
-   /* Write overflow block to tape */
+   /* Clear NewVol now because dir_get_volume_info() already done */
+   jcr->NewVol = false;
+   set_new_volume_parameters(jcr, dev);
+
+   jcr->run_time += time(NULL) - wait_time; /* correct run time for mount wait */
+
+   /* Write overflow block to device */
    Dmsg0(190, "Write overflow block to dev\n");
    if (!write_block_to_dev(jcr, dev, block)) {
       Pmsg1(0, "write_block_to_device overflow block failed. ERR=%s",
@@ -195,6 +190,27 @@ int fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
    return 1;                               /* device locked */
 }
 
+void set_new_volume_parameters(JCR *jcr, DEVICE *dev) 
+{
+   if (jcr->NewVol && !dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) {
+      Jmsg1(jcr, M_ERROR, 0, "%s", jcr->errmsg);
+   }
+   /* Set new start/end positions */
+   if (dev->state & ST_TAPE) {
+      jcr->StartBlock = dev->block_num;
+      jcr->StartFile = dev->file;
+   } else {
+      jcr->StartBlock = (uint32_t)dev->file_addr;
+      jcr->StartFile  = (uint32_t)(dev->file_addr >> 32);
+   }
+   /* Reset indicies */
+   jcr->VolFirstIndex = 0;
+   jcr->VolLastIndex = 0;
+   jcr->NumVolumes++;
+   jcr->NewVol = false;
+   jcr->WroteVol = false;
+}
+
 
 /*
  *   Open the device. Expect dev to already be initialized.  
index c6b127862f7aa8ea6bf508d2936d5d77a1c4bbf8..68d6b4d038981c42b275ceb02a18e93959d59721 100644 (file)
@@ -402,7 +402,7 @@ static void label_volume_if_ok(JCR *jcr, DEVICE *dev, char *oldname,
          bnet_fsend(dir, _("3912 Failed to label Volume: ERR=%s\n"), strerror_dev(dev));
         break;
       }
-      strcpy(jcr->VolumeName, newname);
+      pm_strcpy(&jcr->VolumeName, newname);
       bnet_fsend(dir, _("3000 OK label. Volume=%s Device=%s\n"), 
         newname, dev->dev_name);
       break;
index 2ff25f96f145203e766f8ade6827dccc56b67487..4efe9b3641801ad8e439757945296458c4d40856 100644 (file)
@@ -175,7 +175,7 @@ read_volume:
         memcpy(&VolCatInfo, &jcr->VolCatInfo, sizeof(jcr->VolCatInfo));
         /* Check if this is a valid Volume in the pool */
         pm_strcpy(&jcr->VolumeName, dev->VolHdr.VolName);                         
-        if (!dir_get_volume_info(jcr, 1)) {
+        if (!dir_get_volume_info(jcr, GET_VOL_INFO_FOR_WRITE)) {
             Mmsg(&jcr->errmsg, _("Director wanted Volume \"%s\".\n"
                  "    Current Volume \"%s\" not acceptable because:\n"
                  "    %s"),
@@ -363,9 +363,14 @@ int mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block)
 
 void release_volume(JCR *jcr, DEVICE *dev)
 {
+   /********FIXME******* if WroteVol, must write JobMedia record */
    /* 
     * First erase all memory of the current volume   
     */
+
+   if (jcr->WroteVol) {
+      Jmsg0(jcr, M_ERROR, 0, "Hey!!!!! WriteVol non-zero !!!!!\n");
+   }
    dev->block_num = dev->file = 0;
    dev->EndBlock = dev->EndFile = 0;
    memset(&dev->VolCatInfo, 0, sizeof(dev->VolCatInfo));
index 8ef17a840abc8546b04578ab4bcb935a7eb9cb6f..644a37494aba328ce38fa155006d96528f690191 100644 (file)
 uint32_t new_VolSessionId();
 
 /* From acquire.c */
-DEVICE  *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int      acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int      release_device(JCR *jcr, DEVICE *dev);
+DEVICE *acquire_device_for_append(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int     acquire_device_for_read(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int     release_device(JCR *jcr, DEVICE *dev);
 
 /* From askdir.c */
-int     dir_get_volume_info(JCR *jcr, int writing);
-int     dir_find_next_appendable_volume(JCR *jcr);
-int     dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
-int     dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
-int     dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
-int     dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
-int     dir_send_job_status(JCR *jcr);
-int     dir_create_jobmedia_record(JCR *jcr);
+enum get_vol_info_rw {
+   GET_VOL_INFO_FOR_WRITE,
+   GET_VOL_INFO_FOR_READ
+};
+int    dir_get_volume_info(JCR *jcr, enum get_vol_info_rw);
+int    dir_find_next_appendable_volume(JCR *jcr);
+int    dir_update_volume_info(JCR *jcr, VOLUME_CAT_INFO *vol, int relabel);
+int    dir_ask_sysop_to_mount_next_volume(JCR *jcr, DEVICE *dev);
+int    dir_ask_sysop_to_mount_volume(JCR *jcr, DEVICE *dev);
+int    dir_update_file_attributes(JCR *jcr, DEV_RECORD *rec);
+int    dir_send_job_status(JCR *jcr);
+int    dir_create_jobmedia_record(JCR *jcr);
 
 /* authenticate.c */
-int     authenticate_director(JCR *jcr);
-int     authenticate_filed(JCR *jcr);
+int    authenticate_director(JCR *jcr);
+int    authenticate_filed(JCR *jcr);
 
 /* From block.c */
-void    dump_block(DEV_BLOCK *b, char *msg);
+void   dump_block(DEV_BLOCK *b, char *msg);
 DEV_BLOCK *new_block(DEVICE *dev);
-void    init_block_write(DEV_BLOCK *block);
-void    empty_block(DEV_BLOCK *block);
-void    free_block(DEV_BLOCK *block);
-int     write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-int     write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void   init_block_write(DEV_BLOCK *block);
+void   empty_block(DEV_BLOCK *block);
+void   free_block(DEV_BLOCK *block);
+int    write_block_to_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int    write_block_to_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
 
 #define CHECK_BLOCK_NUMBERS    true
 #define NO_BLOCK_NUMBER_CHECK  false
-int     read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
-int     read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int    read_block_from_device(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
+int    read_block_from_dev(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, bool check_block_numbers);
 
 /* From butil.c -- utilities for SD tool programs */
-void    print_ls_output(char *fname, char *link, int type, struct stat *statp);
+void   print_ls_output(char *fname, char *link, int type, struct stat *statp);
 JCR    *setup_jcr(char *name, char *device, BSR *bsr, char *VolumeName);
 DEVICE *setup_to_access_device(JCR *jcr, int read_access);
-void    display_tape_error_status(JCR *jcr, DEVICE *dev);
+void   display_tape_error_status(JCR *jcr, DEVICE *dev);
 DEVRES *find_device_res(char *device_name, int read_access);
 
 
 /* From dev.c */
-DEVICE  *init_dev(DEVICE *dev, DEVRES *device);
-int      open_dev(DEVICE *dev, char *VolName, int mode);
-void     close_dev(DEVICE *dev);
-void     force_close_dev(DEVICE *dev);
-int      truncate_dev(DEVICE *dev);
-void     term_dev(DEVICE *dev);
-char *   strerror_dev(DEVICE *dev);
-void     clrerror_dev(DEVICE *dev, int func);
-int      update_pos_dev(DEVICE *dev);
-int      rewind_dev(DEVICE *dev);
-int      load_dev(DEVICE *dev);
-int      offline_dev(DEVICE *dev);
-int      flush_dev(DEVICE *dev);
-int      weof_dev(DEVICE *dev, int num);
-int      write_block(DEVICE *dev);
-int      write_dev(DEVICE *dev, char *buf, size_t len);
-int      read_dev(DEVICE *dev, char *buf, size_t len);
-int      status_dev(DEVICE *dev, uint32_t *status);
-int      eod_dev(DEVICE *dev);
-int      fsf_dev(DEVICE *dev, int num);
-int      fsr_dev(DEVICE *dev, int num);
-int      bsf_dev(DEVICE *dev, int num);
-int      bsr_dev(DEVICE *dev, int num);
-void     attach_jcr_to_device(DEVICE *dev, JCR *jcr);
-void     detach_jcr_from_device(DEVICE *dev, JCR *jcr);
-JCR     *next_attached_jcr(DEVICE *dev, JCR *jcr);
-int     dev_can_write(DEVICE *dev);
-int     offline_or_rewind_dev(DEVICE *dev);
+DEVICE *init_dev(DEVICE *dev, DEVRES *device);
+int     open_dev(DEVICE *dev, char *VolName, int mode);
+void    close_dev(DEVICE *dev);
+void    force_close_dev(DEVICE *dev);
+int     truncate_dev(DEVICE *dev);
+void    term_dev(DEVICE *dev);
+char *  strerror_dev(DEVICE *dev);
+void    clrerror_dev(DEVICE *dev, int func);
+int     update_pos_dev(DEVICE *dev);
+int     rewind_dev(DEVICE *dev);
+int     load_dev(DEVICE *dev);
+int     offline_dev(DEVICE *dev);
+int     flush_dev(DEVICE *dev);
+int     weof_dev(DEVICE *dev, int num);
+int     write_block(DEVICE *dev);
+int     write_dev(DEVICE *dev, char *buf, size_t len);
+int     read_dev(DEVICE *dev, char *buf, size_t len);
+int     status_dev(DEVICE *dev, uint32_t *status);
+int     eod_dev(DEVICE *dev);
+int     fsf_dev(DEVICE *dev, int num);
+int     fsr_dev(DEVICE *dev, int num);
+int     bsf_dev(DEVICE *dev, int num);
+int     bsr_dev(DEVICE *dev, int num);
+void    attach_jcr_to_device(DEVICE *dev, JCR *jcr);
+void    detach_jcr_from_device(DEVICE *dev, JCR *jcr);
+JCR    *next_attached_jcr(DEVICE *dev, JCR *jcr);
+int    dev_can_write(DEVICE *dev);
+int    offline_or_rewind_dev(DEVICE *dev);
 
 
 /* Get info about device */
-char *   dev_name(DEVICE *dev);
-char *   dev_vol_name(DEVICE *dev);
+char *  dev_name(DEVICE *dev);
+char *  dev_vol_name(DEVICE *dev);
 uint32_t dev_block(DEVICE *dev);
 uint32_t dev_file(DEVICE *dev);
-int      dev_is_tape(DEVICE *dev);
+int     dev_is_tape(DEVICE *dev);
 
 /* From device.c */
-int      open_device(DEVICE *dev);
-int      fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+int     open_device(DEVICE *dev);
+int     fixup_device_block_write_error(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
 void _lock_device(char *file, int line, DEVICE *dev);
 void _unlock_device(char *file, int line, DEVICE *dev);
 void _block_device(char *file, int line, DEVICE *dev, int state);
 void _unblock_device(char *file, int line, DEVICE *dev);
 void _steal_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold, int state);
 void _give_back_device_lock(char *file, int line, DEVICE *dev, bsteal_lock_t *hold);
+void set_new_volume_parameters(JCR *jcr, DEVICE *dev);
 
 /* From dircmd.c */
-void     *connection_request(void *arg); 
+void    *connection_request(void *arg); 
 
 
 /* From fd_cmds.c */
-void     run_job(JCR *jcr);
+void    run_job(JCR *jcr);
 
 /* From job.c */
-void     stored_free_jcr(JCR *jcr);
-void     connection_from_filed(void *arg);     
-void     handle_filed_connection(BSOCK *fd, char *job_name);
+void    stored_free_jcr(JCR *jcr);
+void    connection_from_filed(void *arg);     
+void    handle_filed_connection(BSOCK *fd, char *job_name);
 
 /* From label.c */
-int      read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void     create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
-void     create_volume_label(DEVICE *dev, char *VolName);
-int      write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
-int      write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
-int      write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void     dump_volume_label(DEVICE *dev);
-void     dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
-int      unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
-int      unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
+int     read_dev_volume_label(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void    create_session_label(JCR *jcr, DEV_RECORD *rec, int label);
+void    create_volume_label(DEVICE *dev, char *VolName);
+int     write_volume_label_to_dev(JCR *jcr, DEVRES *device, char *VolName, char *PoolName);
+int     write_session_label(JCR *jcr, DEV_BLOCK *block, int label);
+int     write_volume_label_to_block(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void    dump_volume_label(DEVICE *dev);
+void    dump_label_record(DEVICE *dev, DEV_RECORD *rec, int verbose);
+int     unser_volume_label(DEVICE *dev, DEV_RECORD *rec);
+int     unser_session_label(SESSION_LABEL *label, DEV_RECORD *rec);
 
 /* From match_bsr.c */
 int match_bsr(BSR *bsr, DEV_RECORD *rec, VOLUME_LABEL *volrec, 
-              SESSION_LABEL *sesrec);
+             SESSION_LABEL *sesrec);
 
 /* From mount.c */
-int      mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
-int      mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
-void     release_volume(JCR *jcr, DEVICE *dev);
+int     mount_next_write_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block, int release);
+int     mount_next_read_volume(JCR *jcr, DEVICE *dev, DEV_BLOCK *block);
+void    release_volume(JCR *jcr, DEVICE *dev);
 
 /* From autochanger.c */
-int      autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
-int      autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
-void     invalidate_slot_in_catalog(JCR *jcr);
+int     autoload_device(JCR *jcr, DEVICE *dev, int writing, BSOCK *dir);
+int     autochanger_list(JCR *jcr, DEVICE *dev, BSOCK *dir);
+void    invalidate_slot_in_catalog(JCR *jcr);
 
 
 /* From parse_bsr.c */
@@ -167,11 +172,11 @@ extern void create_vol_list(JCR *jcr);
 /* From record.c */
 char   *FI_to_ascii(int fi);
 char   *stream_to_ascii(int stream, int fi);
-int     write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int     can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
-int     read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec); 
+int    write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int    can_write_record_to_block(DEV_BLOCK *block, DEV_RECORD *rec);
+int    read_record_from_block(DEV_BLOCK *block, DEV_RECORD *rec); 
 DEV_RECORD *new_record();
-void    free_record(DEV_RECORD *rec);
+void   free_record(DEV_RECORD *rec);
 
 /* From read_record.c */
 int read_records(JCR *jcr,  DEVICE *dev, 
index e71542c8893d93e2e1df5624d7d6c3f43c451369..1f79bbf30e55842d220a6021ae9dc33ae1428e3c 100644 (file)
@@ -1,8 +1,8 @@
 /* */
 #define VERSION "1.31"
 #define VSTRING "1"
-#define BDATE   "16 Jun 2003"
-#define LSMDATE "16Jun03"
+#define BDATE   "18 Jun 2003"
+#define LSMDATE "18Jun03"
 
 /* Debug flags */
 #define DEBUG 1