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.
- 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.
- 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.
+
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");
}
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",
/* Handle normal data */
- if (B_ISDIGIT(bs->msg[0])) { /* response? */
+ if (n > 0 && B_ISDIGIT(bs->msg[0])) { /* response? */
return n; /* yes, return it */
}
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;
}
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.
*/
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) {
#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
}
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));
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 */
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 */
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 */
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));
}
--- /dev/null
+/*
+** 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;
+}
--- /dev/null
+/*
+** 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__ */
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 */
* 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) {
} 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 */
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
ok = FALSE;
}
- memset(&rec, 0, sizeof(rec));
/*
* Get Data from File daemon, write to device. To clarify what is
* 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];
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;
if (jcr->VolFirstIndex == 0) {
jcr->VolFirstIndex = file_index;
}
+ jcr->VolLastIndex = file_index;
last_file_index = file_index;
}
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;
}
*
* 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);
}
{
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);
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 */
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);
/* 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;
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();
}
}
/* 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);
}
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);
{
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
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;
}
}
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;
/* 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;
}
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;
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);
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 */
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 */
}
}
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;
}
/* 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);
* 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)) {
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;
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 */
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
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 */
/* 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;
}
{
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;
}
#endif
return 0;
}
+
+ /* Do housekeeping */
+
dev->VolCatInfo.VolCatBytes += block->binbuf;
dev->VolCatInfo.VolCatBlocks++;
dev->file_addr += wlen;
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);
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);
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 */
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);
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);
}
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 {
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;
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;
}
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);
/* 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;
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();
}
}
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 */
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;
}
/* 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);
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,
return stat;
}
-static void do_scan()
+static void do_scan()
{
attr = new_attr();
}
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
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;
}
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;
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:
* 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;
} 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);
* 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;
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;
}
* 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;
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) {
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);
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;
}
if (verbose) {
Pmsg2(000, _("Created JobMedia record JobId %d, MediaId %d\n"),
- jmr.JobId, jmr.MediaId);
+ jmr.JobId, jmr.MediaId);
}
return 1;
}
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;
}
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;
}
/* 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;
}
DEVICE *dev = NULL;
DEVRES *device = NULL;
-
+
/* Forward referenced subroutines */
static void do_tape_cmds();
static void helpcmd();
/*********************************************************************
*
- * Main Bacula Pool Creation Program
+ * Main Bacula Pool Creation Program
*
*/
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);
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);
}
}
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));
}
term_msg();
- close_memory_pool(); /* free memory in pool */
+ close_memory_pool(); /* free memory in pool */
sm_dump(False);
exit(stat);
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();
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));
}
/*
- * Read the tape label
+ * Read the tape label
*/
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;
}
-/* 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
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);
}
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"));
" 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();
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"
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);
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, "");
}
/*
* 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;
/*
* 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))) {
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");
* 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;
*/
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");
}
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: "));
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) {
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"));
{
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.
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;
}
}
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) {
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 */
}
/*
/*
* 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;
}
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");
*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;
}
{"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
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;
}
}
}
-/*
+/*
* Get next input command from terminal. This
* routine is REALLY primitive, and should be enhanced
* to have correct backspacing, etc.
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;
}
}
/* 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;
}
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;
}
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;
}
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;
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)
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 */
} 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);
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;
stop_thread_timer(dev->tid);
dev->tid = 0;
}
+ dev->use_count = 0;
}
/*
}
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
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
* 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.
*
/* 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);
}
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",
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.
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;
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"),
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));
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 */
/* 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,
/* */
#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