#include "config.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "librsync.h"
#include "job.h"
#include "netint.h"
#include "stream.h"
#include "command.h"
#include "prototab.h"
#include "trace.h"
static rs_result rs_patch_s_cmdbyte(rs_job_t *);
static rs_result rs_patch_s_params(rs_job_t *);
static rs_result rs_patch_s_run(rs_job_t *);
static rs_result rs_patch_s_literal(rs_job_t *);
static rs_result rs_patch_s_copy(rs_job_t *);
static rs_result rs_patch_s_copying(rs_job_t *);
static rs_result rs_patch_s_cmdbyte(rs_job_t *job)
{
rs_result result;
if ((result = rs_suck_byte(job, &job->op)) != RS_DONE)
return result;
job->cmd = &rs_prototab[job->op];
rs_trace("got command %#04x (%s), len_1=" FMT_SIZE "", job->op,
rs_op_kind_name(job->cmd->kind), job->cmd->len_1);
if (job->cmd->len_1)
job->statefn = rs_patch_s_params;
else {
job->param1 = job->cmd->immediate;
job->statefn = rs_patch_s_run;
}
return RS_RUNNING;
}
static rs_result rs_patch_s_params(rs_job_t *job)
{
rs_result result;
int len = job->cmd->len_1 + job->cmd->len_2;
void *p;
assert(len);
result = rs_scoop_readahead(job, len, &p);
if (result != RS_DONE)
return result;
result = rs_suck_netint(job, &job->param1, job->cmd->len_1);
assert(result == RS_DONE);
if (job->cmd->len_2) {
result = rs_suck_netint(job, &job->param2, job->cmd->len_2);
assert(result == RS_DONE);
}
job->statefn = rs_patch_s_run;
return RS_RUNNING;
}
static rs_result rs_patch_s_run(rs_job_t *job)
{
rs_trace("running command %#04x", job->op);
switch (job->cmd->kind) {
case RS_KIND_LITERAL:
job->statefn = rs_patch_s_literal;
return RS_RUNNING;
case RS_KIND_END:
return RS_DONE;
case RS_KIND_COPY:
job->statefn = rs_patch_s_copy;
return RS_RUNNING;
default:
rs_error("bogus command %#04x", job->op);
return RS_CORRUPT;
}
}
static rs_result rs_patch_s_literal(rs_job_t *job)
{
rs_long_t len = job->param1;
rs_trace("LITERAL(len=" FMT_LONG ")", len);
if (len < 0) {
rs_error("invalid length=" FMT_LONG " on LITERAL command", len);
return RS_CORRUPT;
}
job->stats.lit_cmds++;
job->stats.lit_bytes += len;
job->stats.lit_cmdbytes += 1 + job->cmd->len_1;
rs_tube_copy(job, len);
job->statefn = rs_patch_s_cmdbyte;
return RS_RUNNING;
}
static rs_result rs_patch_s_copy(rs_job_t *job)
{
rs_long_t where, len;
rs_stats_t *stats;
where = job->param1;
len = job->param2;
rs_trace("COPY(where=" FMT_LONG ", len=" FMT_LONG ")", where, len);
if (len < 0) {
rs_error("invalid length=" FMT_LONG " on COPY command", len);
return RS_CORRUPT;
}
if (where < 0) {
rs_error("invalid where=" FMT_LONG " on COPY command", where);
return RS_CORRUPT;
}
job->basis_pos = where;
job->basis_len = len;
stats = &job->stats;
stats->copy_cmds++;
stats->copy_bytes += len;
stats->copy_cmdbytes += 1 + job->cmd->len_1 + job->cmd->len_2;
job->statefn = rs_patch_s_copying;
return RS_RUNNING;
}
static rs_result rs_patch_s_copying(rs_job_t *job)
{
rs_result result;
size_t desired_len, len;
void *ptr;
rs_buffers_t *buffs = job->stream;
desired_len = len =
(buffs->avail_out < job->basis_len) ? buffs->avail_out : job->basis_len;
if (!len)
return RS_BLOCKED;
rs_trace("copy " FMT_SIZE " bytes from basis at offset " FMT_LONG "", len,
job->basis_pos);
ptr = buffs->next_out;
result = (job->copy_cb) (job->copy_arg, job->basis_pos, &len, &ptr);
if (result != RS_DONE)
return result;
else
rs_trace("copy callback returned %s", rs_strerror(result));
rs_trace("got " FMT_SIZE " bytes back from basis callback", len);
if (len > desired_len) {
rs_trace("warning: copy_cb returned more than the requested length.");
len = desired_len;
}
if (ptr != buffs->next_out)
memcpy(buffs->next_out, ptr, len);
buffs->next_out += len;
buffs->avail_out -= len;
job->basis_pos += len;
job->basis_len -= len;
if (!job->basis_len) {
job->statefn = rs_patch_s_cmdbyte;
}
return RS_RUNNING;
}
static rs_result rs_patch_s_header(rs_job_t *job)
{
int v;
rs_result result;
if ((result = rs_suck_n4(job, &v)) != RS_DONE)
return result;
if (v != RS_DELTA_MAGIC) {
rs_error("got magic number %#x rather than expected value %#x", v,
RS_DELTA_MAGIC);
return RS_BAD_MAGIC;
} else
rs_trace("got patch magic %#x", v);
job->statefn = rs_patch_s_cmdbyte;
return RS_RUNNING;
}
rs_job_t *rs_patch_begin(rs_copy_cb * copy_cb, void *copy_arg)
{
rs_job_t *job = rs_job_new("patch", rs_patch_s_header);
job->copy_cb = copy_cb;
job->copy_arg = copy_arg;
rs_mdfour_begin(&job->output_md4);
return job;
}