// // aegis - project change supervisor // Copyright (C) 2001-2008, 2011, 2012, 2014 Peter Miller // // 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 3 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, see . // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static string_ty * dir_and_base(string_ty *dir, string_ty *other) { const char *base; base = strrchr(other->str_text, '/'); if (base) ++base; else base = other->str_text; return str_format("%s/%s", dir->str_text, base); } string_ty * change_history_encode(change::pointer cp, fstate_src_ty *src, int *unlink_p) { pconf_ty *pconf_data; string_ty *ofn1; string_ty *ofn2; string_ty *filename; static string_ty *b64_dir; static string_ty *qp_dir; static string_ty *nenc_dir; string_ty *ofn3; trace(("change_history_encode(cp = %p, src->file_name = \"%s\")\n{\n", cp.get(), src->file_name->str_text)); *unlink_p = 1; if (!src->edit) { src->edit = (history_version_ty *)history_version_type.alloc(); } src->edit->encoding = history_version_encoding_none; pconf_data = change_pconf_get(cp, 1); bool min_qp_enc = false; switch (pconf_data->history_content_limitation) { case pconf_history_content_limitation_binary_capable: if (src->uuid) { // // See comment below. Some history tools need basename of // the two files to be the same. If there is a UUID they // will definitely not match. // if (!nenc_dir) { nenc_dir = os_edit_filename(0); change_become(cp); os_mkdir(nenc_dir, 0755); undo_rmdir_bg(nenc_dir); commit_rmdir_tree_errok(nenc_dir); change_become_undo(cp); } string_ty *relname = project_history_uuid_translate(src); ofn3 = dir_and_base(nenc_dir, relname); str_free(relname); filename = cp->file_path(src->file_name); change_become(cp); os_symlink_or_copy(filename, ofn3); change_become_undo(cp); str_free(filename); trace(("return \"%s\"\n", ofn3->str_text)); trace(("}\n")); return ofn3; } *unlink_p = 0; trace(("}\n")); return cp->file_path(src->file_name); case pconf_history_content_limitation_international_text: min_qp_enc = true; break; case pconf_history_content_limitation_ascii_text: break; } // // Some history tools are rather dumb, and require the last // pathname portion of the input file and the history file // to match. E.g. blah/fred and blahblah/fred,v for RCS. // And, no, the GNU versions aren't any smarter. // // To accomplish this for encoded files, we have to make a // directory to plonk them into, one for each encoding we want // to compare. Sigh. // change_become(cp); if (!b64_dir) { b64_dir = os_edit_filename(0); os_mkdir(b64_dir, 0755); undo_rmdir_bg(b64_dir); commit_rmdir_tree_errok(b64_dir); } if (!qp_dir) { qp_dir = os_edit_filename(0); os_mkdir(qp_dir, 0755); undo_rmdir_bg(qp_dir); commit_rmdir_tree_errok(qp_dir); } if (!nenc_dir) { nenc_dir = os_edit_filename(0); os_mkdir(nenc_dir, 0755); undo_rmdir_bg(nenc_dir); commit_rmdir_tree_errok(nenc_dir); } change_become_undo(cp); filename = cp->file_path(src->file_name); if (src->uuid) { string_ty *relname; relname = project_history_uuid_translate(src); ofn1 = dir_and_base(qp_dir, relname); ofn2 = dir_and_base(b64_dir, relname); ofn3 = dir_and_base(nenc_dir, relname); str_free(relname); } else { ofn1 = dir_and_base(qp_dir, filename); ofn2 = dir_and_base(b64_dir, filename); ofn3 = str_copy(filename); } change_become(cp); if (src->uuid) { assert(!str_equal(filename, ofn3)); os_symlink_or_copy(filename, ofn3); } input::pointer ip = input_file::open(nstring(filename)); str_free(filename); output::pointer op; { output::pointer op1 = output_file::text_open(ofn1); output::pointer op1x = output_filter_quoted_printable::create(op1, min_qp_enc); output::pointer op2 = output_file::text_open(ofn2); output::pointer op2x = output_filter_base64::create(op2); op = output_tee::create(op1x, op2x); } bool last_was_newline = true; bool ascii_yuck = false; bool intl_yuck = false; for (;;) { char buffer[1 << 14]; int nbytes = ip->read(buffer, sizeof(buffer)); if (nbytes == 0) break; op->write(buffer, nbytes); char *pos = buffer; char *end = buffer + nbytes; while (pos < end) { unsigned char c = *pos++; // C locale switch (c) { case 0: intl_yuck = true; ascii_yuck = true; break; case ' ': case '\f': case '\n': case '\r': case '\t': break; default: if (!isprint(c)) ascii_yuck = true; break; } } last_was_newline = (buffer[nbytes - 1] == '\n'); } if (!last_was_newline) { ascii_yuck = true; intl_yuck = true; } ip.reset(); op.reset(); change_become_undo(cp); bool encoding_required = true; switch (pconf_data->history_content_limitation) { case pconf_history_content_limitation_binary_capable: // unreachable encoding_required = false; break; case pconf_history_content_limitation_ascii_text: encoding_required = ascii_yuck; break; case pconf_history_content_limitation_international_text: encoding_required = intl_yuck; break; } if (!encoding_required) { change_become(cp); os_unlink(ofn1); str_free(ofn1); os_unlink(ofn2); str_free(ofn2); change_become_undo(cp); *unlink_p = !str_equal(ofn3, filename); trace(("}\n")); return ofn3; } // // Encoding is required. // Use the smallest encoding of the file. // change_become(cp); if (src->uuid) os_unlink(ofn3); str_free(ofn3); off_t size1 = os_file_size(ofn1); off_t size2 = os_file_size(ofn2); if (size1 <= size2) { src->edit->encoding = history_version_encoding_quoted_printable; filename = ofn1; os_unlink(ofn2); str_free(ofn2); } else { src->edit->encoding = history_version_encoding_base64; os_unlink(ofn1); str_free(ofn1); filename = ofn2; } change_become_undo(cp); trace(("}\n")); return filename; } // vim: set ts=8 sw=4 et :