OSDN Git Service

Fix issue #940: Replace slow (2)
[winmerge-jp/winmerge-jp.git] / Src / PatchHTML.cpp
1 /* HTML-format output routines for GNU DIFF.
2    Copyright (C) 1988, 89, 91, 92, 93 Free Software Foundation, Inc.
3
4 This file is part of GNU DIFF.
5
6 GNU DIFF is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU DIFF is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU DIFF; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19
20 #include "pch.h"
21 #include "PatchHTML.h"
22 #include "diff.h"
23 #include <algorithm>
24
25 static struct change *find_hunk(struct change *);
26 static void mark_ignorable(struct change *);
27 static void pr_unidiff_hunk(struct change *);
28 static void print_1_escapedhtml(const char **line);
29 static void output_1_escapedhtml(const char *text, const char *limit);
30
31 /* Print a header for a context diff, with the file names and dates.  */
32
33 void
34 print_html_header (void)
35 {
36   fprintf (outfile, 
37     "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n"
38    "<html>\n"
39    "<head>\n"
40    "<title>WinMerge File Compare Report</title>\n"
41    "</head>\n"
42    "<style type=\"text/css\">\n"
43    "<!--\n"
44    "  HTML, BODY {\n"
45    "    color: #000000;\n"
46    "    background-color: #ffffff;\n"
47    "  }\n"
48    "  \n"
49    "  table {\n"
50    "    width: 100%%;\n"
51    "    margin: 0; \n"
52    "    border: none;\n"
53    "  }\n"
54    "  A:link    { color: #0000ff; }\n"
55    "  A:visited { color: #880088; }\n"
56    "  A:active  { color: #0000ff; }\n"
57    "  \n"
58    "  \n"
59    "  /** Navigation Headers ***/\n"
60    "  .vc_navheader {\n"
61    "    background-color: #8888ff;\n"
62    "  }\n"
63    "  \n"
64    "  \n"
65    "  /*** Table Headers ***/\n"
66    "  .vc_header {\n"
67    "    text-align: left;\n"
68    "    background-color: #cccccc;\n"
69    "  }\n"
70    "  .vc_header_sort {\n"
71    "    text-align: left;\n"
72    "    background-color: #88ff88;\n"
73    "  }\n"
74    "  \n"
75    "  \n"
76    "  /*** Table Rows ***/\n"
77    "  .vc_row_even {\n"
78    "    background-color: #ffffff;\n"
79    "  }\n"
80    "  .vc_row_odd {\n"
81    "    background-color: #ccccee;\n"
82    "  }\n"
83    "  \n"
84    "  \n"
85    "  /*** Markup Summary Header ***/\n"
86    "  .vc_summary {\n"
87    "    background-color: #eeeeee;\n"
88    "  }\n"
89    "  \n"
90    "  \n"
91    "  /*** Colour Diff Styles ***/\n"
92    "  .vc_diff_header {\n"
93    "    background-color: #ffffff;\n"
94    "  }\n"
95    "  .vc_diff_chunk_header {\n"
96    "    background-color: #99cccc;\n"
97    "  }\n"
98    "  .vc_diff_chunk_extra {\n"
99    "    font-size: smaller;\n"
100    "  }\n"
101    "  .vc_diff_empty {\n"
102    "    background-color: #cccccc;\n"
103    "    font-family: monospace;\n"
104    "    font-size: smaller;\n"
105    "  }\n"
106    "  .vc_diff_add {\n"
107    "    background-color: #aaffaa;\n"
108    "    font-family: monospace;\n"
109    "    font-size: smaller;\n"
110    "  }\n"
111    "  .vc_diff_remove {\n"
112    "    background-color: #ffaaaa;\n"
113    "    font-family: monospace;\n"
114    "    font-size: smaller;\n"
115    "  }\n"
116    "  .vc_diff_change {\n"
117    "    background-color: #ffff77;\n"
118    "    font-family: monospace;\n"
119    "    font-size: smaller;\n"
120    "  }\n"
121    "  .vc_diff_change_empty {\n"
122    "    background-color: #eeee77;\n"
123    "    font-family: monospace;\n"
124    "    font-size: smaller;\n"
125    "  }\n"
126    "  .vc_diff_nochange {\n"
127    "    font-family: monospace;\n"
128    "    font-size: smaller;\n"
129    "  }\n"
130    "  \n"
131    "  \n"
132    "  /*** Query Form ***/\n"
133    "  .vc_query_form {\n"
134    "    background-color: #e6e6e6;\n"
135    "  }\n"
136    "  \n"
137    "  \n"
138    "  -->\n"
139    "</style>\n"
140    "\n"
141    "<body>\n");
142 }
143
144 void
145 print_html_terminator (void)
146 {
147   fprintf (outfile, 
148     "</body>\n"
149     "</html>\n");
150 }
151
152 void
153 print_html_diff_header (struct file_data inf[])
154 {
155   fprintf (outfile, "Left: %s<br />Right: %s<br /><br />", inf[0].name, inf[1].name);
156   fprintf (outfile, 
157     "<table cellspacing=\"0\" cellpadding=\"0\">\n"
158     "    <tr class=\"vc_diff_header\">\n");
159   
160   char ctimeBuffer[26];
161   ctime_s(ctimeBuffer, sizeof(ctimeBuffer), &inf[0].stat.st_mtime);
162   fprintf (outfile, 
163     "    <th style=\"width:50%%; vertical-align:top;\">Left: %s</th>\n", ctimeBuffer);
164   ctime_s(ctimeBuffer, sizeof(ctimeBuffer), &inf[1].stat.st_mtime);
165   fprintf (outfile, 
166     "    <th style=\"width:50%%; vertical-align:top;\">Right: %s</th>\n", ctimeBuffer);
167   fprintf (outfile, 
168     "    </tr>\n");
169 }
170
171 void
172 print_html_diff_terminator (void)
173 {
174   fprintf (outfile, "</table>\n");
175 }
176
177 /* Print an edit script in context format.  */
178
179 void
180 print_html_script (struct change *script/*, int src_codepage*/)
181 {
182   if (ignore_blank_lines_flag)
183     mark_ignorable (script);
184   else
185     {
186       struct change *e;
187       for (e = script; e; e = e->link)
188         e->ignore = 0;
189     }
190
191   print_script (script, find_hunk, pr_unidiff_hunk);
192 }
193 \f
194 /* Print a portion of an edit script in unidiff format.
195    HUNK is the beginning of the portion to be printed.
196    The end is marked by a `link' that has been nulled out.
197
198    Prints out lines from both files, and precedes each
199    line with the appropriate flag-character.  */
200
201 static void
202 pr_unidiff_hunk (struct change *hunk)
203 {
204   int first0, last0, first1, last1, show_from, show_to, i, j, k0, k1;
205   int trans_a, trans_b;
206   struct change *next;
207   FILE *out;
208
209   /* Determine range of line numbers involved in each file.  */
210
211   analyze_hunk (hunk, &first0, &last0, &first1, &last1, &show_from, &show_to, files);
212
213   if (!show_from && !show_to)
214     return;
215
216   /* Include a context's width before and after.  */
217
218   i = - files[0].prefix_lines;
219   first0 = (std::max) (first0 - context, i);
220   first1 = (std::max) (first1 - context, i);
221   last0 = (std::min) (last0 + context, files[0].valid_lines - 1);
222   last1 = (std::min) (last1 + context, files[1].valid_lines - 1);
223
224   out = outfile;
225
226   fprintf (out, "  <tr class=\"vc_diff_chunk_header\">\n");
227   fprintf (out, "    <td style=\"width:50%%;\">\n");
228   translate_range (&files[0], first0, last0, &trans_a, &trans_b);
229   fprintf (out, "      <strong>Line %d</strong>&nbsp;\n", trans_a);
230   fprintf (out, "      <span class=\"vc_diff_chunk_extra\"></span>\n");
231   fprintf (out, "    </td>\n");
232   fprintf (out, "    <td style=\"width:50%%;\">\n");
233   translate_range (&files[1], first1, last1, &trans_a, &trans_b);
234   fprintf (out, "      <strong>Line %d</strong>&nbsp;\n", trans_a);
235   fprintf (out, "      <span class=\"vc_diff_chunk_extra\"></span>\n");
236   fprintf (out, "    </td>\n");
237   fprintf (out, "  </tr>\n");
238
239   next = hunk;
240   i = first0;
241   j = first1;
242
243   while (i <= last0 || j <= last1)
244     {
245
246       /* If the line isn't a difference, output the context from file 0. */
247
248       if (next == nullptr || i < next->line0)
249         {
250           fprintf (out, "  <tr>\n");
251           fprintf (out, "    <td class=\"vc_diff_nochange\">&nbsp;");
252           print_1_escapedhtml(&files[0].linbuf[i++]);
253           fprintf (out, "</td>\n");
254           fprintf (out, "    <td class=\"vc_diff_nochange\">&nbsp;");
255           print_1_escapedhtml(&files[1].linbuf[j++]);
256           fprintf (out, "</td>\n");
257           fprintf (out, "  </tr>\n");
258         }
259       else
260         {
261           k0 = next->deleted;
262           k1 = next->inserted;
263           if (next->inserted > 0 && next->deleted > 0)
264             {
265               while (k0 > 0 || k1 > 0)
266                 {
267                   fprintf (out, "  <tr>\n");
268                   if (k0 > 0)
269                     {
270                       fprintf (out, "    <td class=\"vc_diff_change\">&nbsp;");
271                        print_1_escapedhtml(&files[0].linbuf[i++]);
272                       fprintf (out, "</td>\n");
273                     }
274                   else
275                     {
276                       fprintf (out, "    <td class=\"vc_diff_empty\">&nbsp;</td>");
277                     }
278                   if (k1 > 0)
279                     {
280                       fprintf (out, "    <td class=\"vc_diff_change\">&nbsp;");
281                       print_1_escapedhtml(&files[1].linbuf[j++]);
282                       fprintf (out, "</td>\n");
283                     }
284                   else
285                     {
286                       fprintf (out, "    <td class=\"vc_diff_empty\">&nbsp;</td>");
287                     }
288                   fprintf (out, "  </tr>\n");
289                   if (k0 > 0) k0--;
290                   if (k1 > 0) k1--;
291                 }
292             }
293           else if (next->deleted > 0 )
294             {
295               while (k0--)
296                 {
297                   fprintf (out, "  <tr>\n");
298                   fprintf (out, "    <td class=\"vc_diff_remove\">&nbsp;");
299                   print_1_escapedhtml(&files[0].linbuf[i++]);
300                   fprintf (out, "</td>\n");
301                   fprintf (out, "    <td class=\"vc_diff_empty\">&nbsp;</td>");
302                   fprintf (out, "  </tr>\n");
303                 }
304             }
305            else
306             {
307               while (k1--)
308                 {
309                   fprintf (out, "  <tr>\n");
310                   fprintf (out, "    <td class=\"vc_diff_empty\">&nbsp;</td>");
311                   fprintf (out, "    <td class=\"vc_diff_add\">&nbsp;");
312                   print_1_escapedhtml(&files[1].linbuf[j++]);
313                   fprintf (out, "</td>\n");
314                   fprintf (out, "  </tr>\n");
315                 }
316             }
317           /* We're done with this hunk, so on to the next! */
318
319           next = next->link;
320         }
321     }
322 }
323 \f
324 /* Scan a (forward-ordered) edit script for the first place that more than
325    2*CONTEXT unchanged lines appear, and return a pointer
326    to the `struct change' for the last change before those lines.  */
327
328 static struct change *
329 find_hunk (struct change *start)
330 {
331   struct change *prev;
332   int top0;
333   int thresh;
334
335   do
336     {
337       /* Compute number of first line in each file beyond this changed.  */
338       top0 = start->line0 + start->deleted;
339       int top1 = start->line1 + start->inserted;
340       prev = start;
341       start = start->link;
342       /* Threshold distance is 2*CONTEXT between two non-ignorable changes,
343          but only CONTEXT if one is ignorable.  */
344       thresh = ((prev->ignore || (start && start->ignore))
345                 ? context
346                 : 2 * context + 1);
347       /* It is not supposed to matter which file we check in the end-test.
348          If it would matter, crash.  */
349       if (start && start->line0 - top0 != start->line1 - top1)
350         abort ();
351     } while (start
352              /* Keep going if less than THRESH lines
353                 elapse before the affected line.  */
354              && start->line0 < top0 + thresh);
355
356   return prev;
357 }
358
359 /* Set the `ignore' flag properly in each change in SCRIPT.
360    It should be 1 if all the lines inserted or deleted in that change
361    are ignorable lines.  */
362
363 static void
364 mark_ignorable (struct change *script)
365 {
366   while (script != nullptr)
367     {
368       struct change *next = script->link;
369       int first0, last0, first1, last1, deletes, inserts;
370
371       /* Turn this change into a hunk: detach it from the others.  */
372       script->link = nullptr;
373
374       /* Determine whether this change is ignorable.  */
375       analyze_hunk (script, &first0, &last0, &first1, &last1, &deletes, &inserts, files);
376       /* Reconnect the chain as before.  */
377       script->link = next;
378
379       /* If the change is ignorable, mark it.  */
380       script->ignore = (char)(!deletes && !inserts);
381
382       /* Advance to the following change.  */
383       script = next;
384     }
385 }
386
387 static void
388 print_1_escapedhtml(const char **line)
389 {
390   output_1_escapedhtml(line[0], line[1]);
391 }
392
393 static void
394 output_1_escapedhtml(const char *text, const char *limit)
395 {
396   FILE *out = outfile;
397   const char *t = text;
398   int column = 0;
399   int spcolumn = -2;
400
401   while (t < limit)
402     switch (unsigned char c = *t++)
403       {
404       case '&':
405         fprintf (out, "&amp;");
406         column++;
407         break;
408
409       case '>':
410         fprintf (out, "&gt;");
411         column++;
412         break;
413
414       case '<':
415         fprintf (out, "&lt;");
416         column++;
417         break;
418
419       case ' ':
420         if (spcolumn + 1 < column)
421           putc (' ', out);
422         else
423           fprintf (out, "&nbsp;");
424         spcolumn = column;
425         column++;
426         break;
427
428       case '\"':
429         fprintf (out, "&quot;");
430         break;
431
432       case '\t':
433         {
434           unsigned spaces = TAB_WIDTH - column % TAB_WIDTH;
435           column += spaces;
436           putc (' ', out);
437           spaces--;    
438           if (spaces == 0)
439             break;
440           do
441             fprintf (out, "&nbsp;");
442           while (--spaces);
443         }
444         break;
445
446       case '\r':
447       case '\n':
448         putc (c, out);
449         column = 0;
450         break;
451
452       case '\b':
453         if (column == 0)
454           continue;
455         column--;
456         putc (c, out);
457         break;
458
459       default:
460         column++;
461         putc (c, out);
462         break;
463       }
464 }