+#endif
+ /*}}}*/
+RredMethod::State RredMethod::patchMMap(FileFd &Patch, FileFd &From, /*{{{*/
+ FileFd &out_file, Hashes *hash) const {
+#ifdef _POSIX_MAPPED_FILES
+ MMap ed_cmds(Patch, MMap::ReadOnly);
+ MMap in_file(From, MMap::ReadOnly);
+
+ unsigned long long const ed_size = ed_cmds.Size();
+ unsigned long long const in_size = in_file.Size();
+ if (ed_size == 0 || in_size == 0)
+ return MMAP_FAILED;
+
+ EdCommand* commands = 0;
+ size_t command_count = 0;
+ size_t command_alloc = 0;
+
+ const char* begin = (char*) ed_cmds.Data();
+ const char* end = begin;
+ const char* ed_end = (char*) ed_cmds.Data() + ed_size;
+
+ const char* input = (char*) in_file.Data();
+ const char* input_end = (char*) in_file.Data() + in_size;
+
+ size_t i;
+
+ /* 1. Parse entire script. It is executed in reverse order, so we cather it
+ * in the `commands' buffer first
+ */
+
+ for(;;) {
+ EdCommand cmd;
+ cmd.data_start = 0;
+ cmd.data_end = 0;
+
+ while(begin != ed_end && *begin == '\n')
+ ++begin;
+ while(end != ed_end && *end != '\n')
+ ++end;
+ if(end == ed_end && begin == end)
+ break;
+
+ /* Determine command range */
+ const char* tmp = begin;
+
+ for(;;) {
+ /* atoll is safe despite lacking NUL-termination; we know there's an
+ * alphabetic character at end[-1]
+ */
+ if(tmp == end) {
+ cmd.first_line = atol(begin);
+ cmd.last_line = cmd.first_line;
+ break;
+ }
+ if(*tmp == ',') {
+ cmd.first_line = atol(begin);
+ cmd.last_line = atol(tmp + 1);
+ break;
+ }
+ ++tmp;
+ }
+
+ // which command to execute on this line(s)?
+ switch (end[-1]) {
+ case MODE_CHANGED:
+ if (Debug == true)
+ std::clog << "Change from line " << cmd.first_line << " to " << cmd.last_line << std::endl;
+ break;
+ case MODE_ADDED:
+ if (Debug == true)
+ std::clog << "Insert after line " << cmd.first_line << std::endl;
+ break;
+ case MODE_DELETED:
+ if (Debug == true)
+ std::clog << "Delete from line " << cmd.first_line << " to " << cmd.last_line << std::endl;
+ break;
+ default:
+ _error->Error("rred: Unknown ed command '%c'. Abort.", end[-1]);
+ free(commands);
+ return ED_PARSER;
+ }
+ cmd.type = end[-1];
+
+ /* Determine the size of the inserted text, so we don't have to scan this
+ * text again later.
+ */
+ begin = end + 1;
+ end = begin;
+ cmd.data_lines = 0;
+
+ if(cmd.type == MODE_ADDED || cmd.type == MODE_CHANGED) {
+ cmd.data_start = begin - (char*) ed_cmds.Data();
+ while(end != ed_end) {
+ if(*end == '\n') {
+ if(end[-1] == '.' && end[-2] == '\n')
+ break;
+ ++cmd.data_lines;
+ }
+ ++end;
+ }
+ cmd.data_end = end - (char*) ed_cmds.Data() - 1;
+ begin = end + 1;
+ end = begin;
+ }
+ if(command_count == command_alloc) {
+ command_alloc = (command_alloc + 64) * 3 / 2;
+ EdCommand* newCommands = (EdCommand*) realloc(commands, command_alloc * sizeof(EdCommand));
+ if (newCommands == NULL) {
+ free(commands);
+ return MMAP_FAILED;
+ }
+ commands = newCommands;
+ }
+ commands[command_count++] = cmd;
+ }
+
+ struct iovec* iov = new struct iovec[IOV_COUNT];
+ size_t iov_size = 0;
+
+ size_t amount, remaining;
+ size_t line = 1;
+ EdCommand* cmd;
+
+ /* 2. Execute script. We gather writes in a `struct iov' array, and flush
+ * using writev to minimize the number of system calls. Data is read
+ * directly from the memory mappings of the input file and the script.
+ */
+
+ for(i = command_count; i-- > 0; ) {
+ cmd = &commands[i];
+ if(cmd->type == MODE_ADDED)
+ amount = cmd->first_line + 1;
+ else
+ amount = cmd->first_line;
+
+ if(line < amount) {
+ begin = input;
+ while(line != amount) {
+ input = (const char*) memchr(input, '\n', input_end - input);
+ if(!input)
+ break;
+ ++line;
+ ++input;
+ }
+
+ iov[iov_size].iov_base = (void*) begin;
+ iov[iov_size].iov_len = input - begin;
+ hash->Add((const unsigned char*) begin, input - begin);
+
+ if(++iov_size == IOV_COUNT) {
+ retry_writev(out_file.Fd(), iov, IOV_COUNT);
+ iov_size = 0;
+ }
+ }
+
+ if(cmd->type == MODE_DELETED || cmd->type == MODE_CHANGED) {
+ remaining = (cmd->last_line - cmd->first_line) + 1;
+ line += remaining;
+ while(remaining) {
+ input = (const char*) memchr(input, '\n', input_end - input);
+ if(!input)
+ break;
+ --remaining;
+ ++input;
+ }
+ }
+
+ if(cmd->type == MODE_CHANGED || cmd->type == MODE_ADDED) {
+ if(cmd->data_end != cmd->data_start) {
+ iov[iov_size].iov_base = (void*) ((char*)ed_cmds.Data() + cmd->data_start);
+ iov[iov_size].iov_len = cmd->data_end - cmd->data_start;
+ hash->Add((const unsigned char*) ((char*)ed_cmds.Data() + cmd->data_start),
+ iov[iov_size].iov_len);
+
+ if(++iov_size == IOV_COUNT) {
+ retry_writev(out_file.Fd(), iov, IOV_COUNT);
+ iov_size = 0;
+ }
+ }
+ }
+ }
+
+ if(input != input_end) {
+ iov[iov_size].iov_base = (void*) input;
+ iov[iov_size].iov_len = input_end - input;
+ hash->Add((const unsigned char*) input, input_end - input);
+ ++iov_size;
+ }
+
+ if(iov_size) {
+ retry_writev(out_file.Fd(), iov, iov_size);
+ iov_size = 0;
+ }