#help_index "Cmd Line (Typically)" #define DS_USE_FILE1 0 #define DS_USE_FILE2 1 #define DS_REMAINDER_1 2 #define DS_REMAINDER_2 3 #define DS_ABORT_FILE 4 I64 PopUpDiffMenu() { I64 i; CDoc *doc = DocNew; DocPrint(doc, "$CM+LX,2,2$$LTRED$FILE1$FG$ = DST.$CM+LX,24,0$$LTGREEN$FILE2$FG$ = SRC." "$CM+LX,2,4$$RED$$BT,\"USE FILE1\",LE=DS_USE_FILE1$" "$CM+LX,24,0$$GREEN$$BT,\"USE FILE2\",LE=DS_USE_FILE2$" "$CM+LX,2,4$$RED$$BT,\"REMAINDER ALL FILE1\",LE=DS_REMAINDER_1$" "$CM+LX,24,0$$GREEN$$BT,\"REMAINDER ALL FILE2\",LE=DS_REMAINDER_2$" "$CM+LX,2,4$$FG$$BT,\"ABORT FILE\",LE=DS_ABORT_FILE$" "$CM+LX,24,0$$FG$$BT,\"ABORT ALL FILES\",LE=DOCM_CANCEL$\n"); i = PopUpMenu(doc); DocDel(doc); return i; } I64 DiffEntriesCompare(CDocEntry *doc_e1, CDocEntry *doc_e2) { return StrCompare(doc_e1->tag, doc_e2->tag); } #define DF_MODIFIED 0x01 #define DF_DONT_MODIFIED 0x02 #define DF_REMAINDER_ALL_FILE1 0x04 #define DF_REMAINDER_ALL_FILE2 0x08 #define DF_ABORT_FILE 0x10 #define DF_ABORT_ALL_FILES 0x20 #define DF_NO_MORE_PROMPTS_THIS_FILE 0x40 #define DF_KEEP_FLAGS 0x80 // tells Diff NOT to &= with abort-all flag at call start U0 DiffSel(CDoc *doc, I64 *_df_flags, I64 j1_lo, I64 j1_hi, I64 j2_lo, I64 j2_hi, I64 count1, I64 count2, CDocEntry **doc_unsorted1, CDocEntry **doc_unsorted2) { CDocEntry *doc_e, *doc_e1, *doc_e2; Bool use_file1; I64 i, old_flags; CDoc *cur_l; if (!(*_df_flags & (DF_ABORT_FILE | DF_ABORT_ALL_FILES))) { "$PURPLE$"; if (0 <= j1_lo < count1) "%d,", doc_unsorted1[j1_lo]->y + 1; else if (0 <= j1_hi - 1 < count1) "%d,", doc_unsorted1[j1_hi - 1]->y + 1; else "***,"; if (0 <= j2_lo < count2) "%d", doc_unsorted2[j2_lo]->y + 1; else if (0 <= j2_hi - 1 < count2) "%d", doc_unsorted2[j2_hi - 1]->y + 1; else "***"; "---------------------$RED$\n"; if (j1_lo <= 0) i = 0; else i = j1_lo - 1; while (i < j1_hi) { if (cur_l = DocPut) { old_flags = cur_l->flags & DOCF_PLAIN_TEXT; cur_l->flags |= DOCF_PLAIN_TEXT; } "%s", doc_unsorted1[i++]->tag; if (cur_l) cur_l->flags = cur_l->flags & ~DOCF_PLAIN_TEXT | old_flags; '\n'; } "$GREEN$"; if (j2_lo <= 0) i = 0; else i = j2_lo - 1; while (i < j2_hi) { if (cur_l = DocPut) { old_flags = cur_l->flags & DOCF_PLAIN_TEXT; cur_l->flags |= DOCF_PLAIN_TEXT; } "%s", doc_unsorted2[i++]->tag; if (cur_l) cur_l->flags = cur_l->flags & ~DOCF_PLAIN_TEXT | old_flags; '\n'; } "$FG$"; use_file1 = TRUE; if (!(*_df_flags & DF_NO_MORE_PROMPTS_THIS_FILE)) { switch (PopUpDiffMenu) { case DS_USE_FILE1: break; case DS_USE_FILE2: use_file1 = FALSE; break; case DS_REMAINDER_1: *_df_flags = *_df_flags & ~DF_REMAINDER_ALL_FILE2 | DF_REMAINDER_ALL_FILE1 | DF_NO_MORE_PROMPTS_THIS_FILE; break; case DS_REMAINDER_2: *_df_flags = *_df_flags & ~DF_REMAINDER_ALL_FILE1 | DF_REMAINDER_ALL_FILE2 | DF_NO_MORE_PROMPTS_THIS_FILE; break; case DS_ABORT_FILE: *_df_flags |= DF_DONT_MODIFIED | DF_ABORT_FILE | DF_NO_MORE_PROMPTS_THIS_FILE; break; default: *_df_flags |= DF_DONT_MODIFIED | DF_ABORT_ALL_FILES | DF_NO_MORE_PROMPTS_THIS_FILE; } } if (*_df_flags & DF_REMAINDER_ALL_FILE2 && !(*_df_flags & (DF_DONT_MODIFIED | DF_REMAINDER_ALL_FILE1))) use_file1 = FALSE; if (!use_file1) { *_df_flags |= DF_MODIFIED; doc_e1 = doc_unsorted1[j1_lo]->last; if (j1_lo < j1_hi) { doc_e = doc_unsorted1[j1_lo]; while (doc_e != doc_unsorted1[j1_hi]) { doc_e2 = doc_e->next; DocEntryDel(doc, doc_e); doc_e = doc_e2; } } if (j2_lo < j2_hi) { doc_e = doc_unsorted2[j2_lo]; while (doc_e != doc_unsorted2[j2_hi]) { doc_e2 = DocEntryCopy(doc, doc_e); QueueInsert(doc_e2, doc_e1); doc_e1 = doc_e2; doc_e = doc_e->next; } } } } } Bool DiffSub(CDoc *doc, I64 *_df_flags, I64 j1_lo, I64 j1_hi, I64 j2_lo, I64 j2_hi, I64 count1, I64 count2, CDocEntry **doc_sorted1, CDocEntry **doc_sorted2, CDocEntry **doc_unsorted1, CDocEntry **doc_unsorted2) { I64 i, i1 = 0, i2 = 0, i2b, j1, j2, n, best_j1, best_j2, best_score = 0, score; Bool res = FALSE; if (j1_lo >= j1_hi || j2_lo >= j2_hi) { if (j1_lo < j1_hi || j2_lo < j2_hi) { DiffSel(doc, _df_flags, j1_lo, j1_hi, j2_lo, j2_hi, count1, count2, doc_unsorted1, doc_unsorted2); return TRUE; } else return FALSE; } //Locate longest matching str in intervals while (i1 < count1 && i2 < count2) { if (!(j1_lo <= doc_sorted1[i1]->user_data < j1_hi)) //user_data is the new y i1++; else if (!(j2_lo <= doc_sorted2[i2]->user_data < j2_hi)) //user_data is new y i2++; else { if (!CheckPtr(doc_sorted1[i1]) || !CheckPtr(doc_sorted2[i2]) || !CheckPtr(doc_sorted1[i1]->tag) || !CheckPtr(doc_sorted2[i2]->tag)) { RawPrint(200, "DiffSub PANIC DEBUG FIXME: %X %X %X %X\n", doc_sorted1[i1], doc_sorted2[i2], doc_sorted1[i1]->tag, doc_sorted2[i2]->tag); i = 1; // ??? } else i = StrCompare(doc_sorted1[i1]->tag, doc_sorted2[i2]->tag); if (i > 0) i2++; else if (i < 0) i1++; else { i2b = i2; while (!StrCompare(doc_sorted1[i1]->tag, doc_sorted2[i2]->tag)) { if (j2_lo <= doc_sorted2[i2]->user_data < j2_hi) {//user_data is the new y score = 0; j1 = doc_sorted1[i1]->user_data; //user_data is the new y j2 = doc_sorted2[i2]->user_data; //user_data is the new y n = j1_hi - j1; if (j2_hi - j2 < n) n = j2_hi - j2; while (score < n) { if (!StrCompare(doc_unsorted1[j1 + score]->tag, doc_unsorted2[j2 + score]->tag)) score++; else break; } if (score > best_score) { best_score = score; best_j1 = j1; best_j2 = j2; } } i2++; if (i2 >= count2) break; } i2 = i2b; i1++; } } } if (!best_score) { DiffSel(doc, _df_flags, j1_lo, j1_hi, j2_lo, j2_hi, count1, count2, doc_unsorted1, doc_unsorted2); return TRUE; } else { if (DiffSub(doc, _df_flags, j1_lo, best_j1, j2_lo, best_j2, count1, count2, doc_sorted1, doc_sorted2, doc_unsorted1, doc_unsorted2)) res = TRUE; if (DiffSub(doc, _df_flags, best_j1 + best_score, j1_hi, best_j2 + best_score, j2_hi, count1, count2, doc_sorted1, doc_sorted2, doc_unsorted1, doc_unsorted2)) res = TRUE; return res; } } Bool DiffBins(CDoc *doc1, CDoc *doc2) { CDocBin *tmpb1 = doc1->bin_head.next, *tmpb2 = doc2->bin_head.next; if (tmpb1->last->last->num != tmpb2->last->last->num) return TRUE; while (tmpb1 != &doc1->bin_head) { if (tmpb1->size != tmpb2->size || MemCompare(tmpb1->data, tmpb2->data, tmpb1->size)) return TRUE; tmpb1 = tmpb1->next; tmpb2 = tmpb2->next; } return FALSE; } public Bool Diff(U8 *dst_file, U8 *src_file, I64 *_df_flags=NULL) {//Report differences between two files and merge differences //from src_file to dst_file. Don't use _df_flags arg. (Used by Merge().) CDoc *doc1 = DocRead(dst_file, DOCF_PLAIN_TEXT_TABS | DOCF_NO_CURSOR), *doc2 = DocRead(src_file, DOCF_PLAIN_TEXT_TABS | DOCF_NO_CURSOR); CDocEntry *doc_e, **doc_sorted1, **doc_sorted2, **doc_unsorted1, **doc_unsorted2; I64 i, count1 = 0, count2 = 0,df_flags; Bool res = FALSE; if (_df_flags) df_flags = *_df_flags; else df_flags = 0; if (!(df_flags & DF_KEEP_FLAGS)) df_flags &= DF_ABORT_ALL_FILES; doc_e = doc1->head.next; while (doc_e != doc1) { if (doc_e->type_u8 == DOCT_TEXT) doc_e->user_data = count1++; //user_data is the new y doc_e = doc_e->next; } doc_e = doc2->head.next; while (doc_e != doc2) { if (doc_e->type_u8 == DOCT_TEXT) doc_e->user_data = count2++; //user_data is the new y doc_e = doc_e->next; } doc_sorted1 = MAlloc(count1 * sizeof(CDocEntry *)); doc_unsorted1 = MAlloc((count1 + 1) * sizeof(CDocEntry *)); i = 0; doc_e = doc1->head.next; while (doc_e != doc1) { if (doc_e->type_u8 == DOCT_TEXT) { doc_sorted1[i] = doc_e; doc_unsorted1[i++] = doc_e; } doc_e = doc_e->next; } doc_unsorted1[i] = doc1; QuickSortI64(doc_sorted1, count1, &DiffEntriesCompare); doc_sorted2 = MAlloc(count2 * sizeof(CDocEntry *)); doc_unsorted2 = MAlloc((count2 + 1) * sizeof(CDocEntry *)); i = 0; doc_e = doc2->head.next; while (doc_e != doc2) { if (doc_e->type_u8 == DOCT_TEXT) { doc_sorted2[i] = doc_e; doc_unsorted2[i++] = doc_e; } doc_e = doc_e->next; } doc_unsorted2[i] = doc2; QuickSortI64(doc_sorted2, count2, &DiffEntriesCompare); res = DiffSub(doc1, &df_flags, 0, count1, 0, count2, count1, count2, doc_sorted1, doc_sorted2, doc_unsorted1, doc_unsorted2); if (df_flags & DF_MODIFIED && !(df_flags & DF_DONT_MODIFIED)) DocWrite(doc1); if (DiffBins(doc1, doc2)) { "$RED$Bin Data is Different$FG$\n"; res = TRUE; } DocDel(doc1); DocDel(doc2); Free(doc_sorted1); Free(doc_sorted2); Free(doc_unsorted1); Free(doc_unsorted2); if (_df_flags) *_df_flags = df_flags; return res; }