Don't underline diffs
[oota-llvm.git] / utils / Spiff / output.c
1 /*                        Copyright (c) 1988 Bellcore
2 **                            All Rights Reserved
3 **       Permission is granted to copy or use this program, EXCEPT that it
4 **       may not be sold for profit, the copyright notice must be reproduced
5 **       on copies, and credit should be given to Bellcore where it is due.
6 **       BELLCORE MAKES NO WARRANTY AND ACCEPTS NO LIABILITY FOR THIS PROGRAM.
7 */
8
9
10 #ifndef lint
11 static char rcsid[]= "$Header$";
12 #endif
13
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <unistd.h>
17
18 #ifdef M_TERMINFO
19 #include <curses.h>
20 #include <term.h>
21 #endif
22
23 #ifdef M_TERMCAP
24 #ifdef XENIX
25 #include <tcap.h>
26 #endif
27 #endif
28
29 #include "misc.h"
30 #include "flagdefs.h"
31 #include "edit.h"
32 #include "line.h"
33 #include "token.h"
34
35 static int _O_need_init = 1;
36 static int _O_st_ok = 0;
37 static int _O_doing_ul = 0;
38 static  char *_O_st_tmp;
39 #ifdef M_TERMCAP
40 static  char _O_startline[Z_WORDLEN];
41 static  char _O_endline[Z_WORDLEN];
42 #endif
43
44 static void
45 _O_st_init()
46 {
47         char termn[Z_WORDLEN];
48 #ifdef M_TERMCAP
49         static  char entry[1024];
50 #endif
51
52         /*
53         **      see if standard out is a terminal
54         */
55         if (!isatty(1))
56         {
57                 _O_need_init = 0;
58                 _O_st_ok = 0;
59                 return;
60         }
61
62         if (NULL == (_O_st_tmp = (char*) getenv("TERM")))
63         {
64                 Z_complain("can't find TERM entry in environment\n");
65                 _O_need_init = 0;
66                 _O_st_ok = 0;
67                 return;
68         }
69         (void) strcpy(termn,_O_st_tmp);
70
71 #ifdef M_TERMCAP
72         if (1 != tgetent(entry,termn))
73         {
74                 Z_complain("can't get TERMCAP info for terminal\n");
75                 _O_need_init = 0;
76                 _O_st_ok = 0;
77                 return;
78         }
79
80         _O_st_tmp = _O_startline;
81         _O_startline[0] = '\0';
82         tgetstr("so",&_O_st_tmp);
83
84         _O_st_tmp = _O_endline;
85         _O_endline[0] = '\0';
86         tgetstr("se",&_O_st_tmp);
87
88         _O_st_ok = (strlen(_O_startline) > 0) && (strlen(_O_endline) > 0);
89 #endif
90
91 #ifdef M_TERMINFO
92         setupterm(termn,1,&_O_st_ok);
93 #endif
94         _O_need_init = 0;
95 }
96
97 void
98 O_cleanup()
99 {
100         /*
101         **      this probably isn't necessary, but in the
102         **      name of compeleteness.
103         */
104 #ifdef M_TERMINFO
105         resetterm();
106 #endif
107 }
108
109 static void
110 _O_start_standout()
111 {
112         if (_O_need_init)
113         {
114                 _O_st_init();
115         }
116         if (_O_st_ok)
117         {
118 #ifdef M_TERMCAP
119                 (void) printf("%s",_O_startline);
120 #endif 
121 #ifdef M_TERMINFO
122                 vidattr(A_STANDOUT);
123 #endif
124         }
125         else
126         {
127                 /* _O_doing_ul = 1; */ /* disabled by brg 13-April-2004 - this
128  makes the output unreadable */
129         }
130 }
131
132 static void
133 _O_end_standout()
134 {
135         if (_O_need_init)
136         {
137                 _O_st_init();
138         }
139         if (_O_st_ok)
140         {
141 #ifdef M_TERMCAP
142                 (void) printf("%s",_O_endline);
143 #endif 
144 #ifdef M_TERMINFO
145                 vidattr(0);
146 #endif
147         }
148         else
149         {
150                 _O_doing_ul = 0;
151         }
152 }
153
154 static void
155 _O_pchars(line,start,end)
156 char *line;
157 int start,end;
158 {
159         int cnt;
160
161         for(cnt=start;cnt < end; cnt++)
162         {
163                 if (_O_doing_ul)
164                 {
165                         (void) putchar('_');
166                         (void) putchar('\b');
167                 }
168                 (void) putchar(line[cnt]);
169         }
170 }
171
172
173 /*
174 **      convert a 0 origin token number to a 1 orgin token
175 **              number or 1 origin line number as appropriate
176 */
177 static int
178 _O_con_line(numb,flags,filenum)
179 int numb, flags,filenum;
180 {
181         if (flags & U_TOKENS)
182         {
183                 return(numb+1);
184         }
185         else
186         {
187                 /*
188                 **       check to make sure that this is a real
189                 **      line number. if not, then return 0
190                 **      on rare occasions, (i.e. insertion/deletion
191                 **      of the first token in a file) we'll get
192                 **      line numbers of -1.  the usual look-up technique
193                 **      won't work since we have no lines before than 0.
194                 */
195                 if (numb < 0)
196                         return(0);
197                 /*
198                 **      look up the line number the token and then
199                 **      add 1 to make line number 1 origin
200                 */
201                 return(L_tl2cl(filenum,numb)+1);
202         }
203 }
204
205 static char *
206 _O_convert(ptr)
207 char *ptr;
208 {
209         static char spacetext[Z_WORDLEN];
210
211         if (1 == strlen(ptr))
212         {
213                 switch (*ptr)
214                 {
215                         default:
216                                 break;
217                         case '\n' :
218                                 (void) strcpy(spacetext,"<NEWLINE>");
219                                 return(spacetext);
220                         case '\t' :
221                                 (void) strcpy(spacetext,"<TAB>");
222                                 return(spacetext);
223                         case ' ' :
224                                 (void) strcpy(spacetext,"<SPACE>");
225                                 return(spacetext);
226                 }
227                                 
228         }
229         return(ptr);
230 }
231
232 static char*
233 _O_get_text(file,index,flags)
234 int file,index,flags;
235 {
236         static char buf[Z_LINELEN*2];   /* leave lots of room for both
237                                                 the token text and the
238                                                 chatter that preceeds it */
239         char *text;
240         K_token tmp;
241
242         if (flags & U_TOKENS)
243         {
244                 tmp = K_gettoken(file,index);
245                 text = _O_convert(K_gettext(tmp));
246                 (void) sprintf(buf,"%s -- line %d, character %d\n",
247                                 text,
248                                 /*
249                                 **      add 1 to make output start at line 1 
250                                 **      and character numbers start at 1
251                                 */
252                                 L_tl2cl(file,K_getline(tmp))+1,
253                                 K_getpos(tmp)+1);
254                 return(buf);
255         }
256         else
257         {
258                 return(L_gettline(file,index));
259         }
260 }
261 #define _O_APP          1
262 #define _O_DEL          2
263 #define _O_CHA          3
264 #define _O_TYPE_E       4
265
266 static void
267 _O_do_lines(start,end,file)
268 int start,end,file;
269 {
270         int cnt;
271         int lastline = -1;
272         int nextline;
273         K_token nexttoken;
274         for (cnt=start;cnt <= end; cnt++)
275         {
276                 nexttoken = K_get_token(file,cnt);
277                 nextline = K_getline(nexttoken);
278                 if (lastline != nextline)
279                 {
280                         int lastone,lastchar;
281                         K_token lasttok;
282                         char linetext[Z_LINELEN+1];     /* leave room for
283                                                            terminator */
284                         if (0 == file)
285                         {
286                                 (void) printf("< ");
287                         }
288                         else
289                         {
290                                 (void) printf("> ");
291                         }
292
293                         /*
294                         **      put loop here if you want to print
295                         **      out any intervening lines that don't
296                         **      have any tokens on them
297                         */
298
299                         /*
300                         **      following line is necessary because
301                         **      L_gettline is a macro, and can't be passed
302                         */
303                         (void) strcpy(linetext,L_gettline(file,nextline));
304                         _O_pchars(linetext,0,K_getpos(nexttoken));
305                         _O_start_standout();
306                         /*
307                         **      look for last token on this line to be
308                         **      highlighted
309                         */
310                         for ( lastone=cnt,lasttok = K_get_token(file,lastone);
311                               (lastone<=end)&&(nextline == K_getline(lasttok));
312                                 lastone++,lasttok = K_get_token(file,lastone))
313                         {
314                         }
315                         lastone--;
316                         lasttok = K_get_token(file,lastone);
317                         lastchar = K_getpos(lasttok)
318                                         + strlen(K_gettext(lasttok));
319                         _O_pchars(linetext,K_getpos(nexttoken),lastchar);
320                         _O_end_standout();
321                         _O_pchars(linetext,lastchar,strlen(linetext));
322                         
323                         lastline = nextline;
324                 }
325         }
326 }
327
328 void
329 O_output(start,flags)
330 E_edit start;
331 int flags;
332 {
333         int type = _O_TYPE_E;   /* initialize to error state
334                                 ** this is to make sure that type is set
335                                 ** somewhere
336                                 */
337         int t_beg1, t_beg2, t_end1, t_end2; /* token numbers */
338         int first1, last1, first2, last2;
339
340         E_edit ep, behind, ahead, a, b;
341
342         /*
343         **      reverse the list of edits
344         */
345         ahead = start;
346         ep = E_NULL;
347         while (ahead != E_NULL) {
348                 /*
349                 **      set token numbers intentionally out of range
350                 **              as boilerplate
351                 */
352                 t_beg1 = t_beg2 = t_end1 = t_end2 = -1;
353                 /*
354                 **      edit script is 1 origin, all of
355                 **       our routines are zero origin
356                 */
357                 E_setl1(ahead,(E_getl1(ahead))-1);
358                 E_setl2(ahead,(E_getl2(ahead))-1);
359
360                 behind = ep;
361                 ep = ahead;
362                 ahead = E_getnext(ahead);
363                 E_setnext(ep,behind);
364         }
365
366         /*
367         **      now run down the list and collect the following information
368         **      type of change (_O_APP, _O_DEL or _O_CHA)
369         **      start and length for each file
370         */
371         while (ep != E_NULL)
372         {
373                 b = ep;
374                 /*
375                 **      operation always start here
376                 */
377                 t_beg1 = E_getl1(ep);
378                 /*
379                 **      any deletions will appear before any insertions,
380                 **      so, if the first edit is an E_INSERT, then this
381                 **      this is an _O_APP
382                 */
383                 if (E_getop(ep) == E_INSERT)
384                         type = _O_APP;
385                 else {
386                         /*
387                         **      run down the list looking for the edit
388                         **      that is not part of the current deletion
389                         */      
390                         do {
391                                 a = b;
392                                 b = E_getnext(b);
393                         } while ((b != E_NULL) &&
394                                  (E_getop(b) == E_DELETE) &&
395                                  ((E_getl1(b)) == ((E_getl1(a))+1)));
396                         /*
397                         **      if we have an insertion at the same place
398                         **      as the deletion we just scanned, then
399                         **      this is a change
400                         */
401                         if ((b != E_NULL) &&
402                                 ((E_getop(b)) == E_INSERT) &&
403                                 ((E_getl1(b))==(E_getl1(a))))
404                         {
405                                 type = _O_CHA;
406                         }
407                         else
408                         {
409                                 type = _O_DEL;
410                         }
411                         /*
412                         **      set up start and length information for
413                         **      first file
414                         */
415                         t_end1 = E_getl1(a);
416                         /*
417                         **      move pointer to beginning of insertion
418                         */
419                         ep = b;
420                         /*
421                         **      if we are showing only a deletion,
422                         **      then we're all done, so skip ahead
423                         */ 
424                         if (_O_DEL == type)
425                         {
426                                 t_beg2 = E_getl2(a);
427                                 t_end2 = -1;    /* dummy number, won't
428                                                         ever be printed */
429                                                    
430                                 goto skipit;
431                         }
432                 }
433                 t_beg2 = E_getl2(ep);
434                 t_end2 = t_beg2-1;
435                 /*
436                 **      now run down the list lookingfor the
437                 **      end of this insertion and keep count
438                 **      of the number of times we step along
439                 */
440                 do {
441                         t_end2++;
442                         ep = E_getnext(ep);
443                 } while ((ep != E_NULL) && ((E_getop(ep)) == E_INSERT) &&
444                                         ((E_getl1(ep)) == (E_getl1(b))));
445
446 skipit:;
447                 if (flags & U_TOKENS)
448                 {
449                         /*
450                         **      if we are dealing with tokens individually,
451                         **      then just print then set printing so
452                         */
453                                 first1 = t_beg1;
454                                 last1 = t_end1;
455                                 first2 = t_beg2;
456                                 last2 = t_end2;
457                 }
458                 else
459                 {
460                         /*
461                         **      we are printing differences in terms of lines
462                         **      so find the beginning and ending lines of the
463                         **      changes and print header in those terms
464                         */
465                         if ( t_beg1 >= 0)
466                                 first1 = K_getline(K_get_token(0,t_beg1));
467                         else
468                                 first1 = t_beg1;
469
470                         if ( t_end1 >= 0)
471                                 last1 = K_getline(K_get_token(0,t_end1));
472                         else
473                                 last1 = t_end1;
474
475                         if ( t_beg2 >= 0)
476                                 first2 = K_getline(K_get_token(1,t_beg2));
477                         else
478                                 first2 = t_beg2;
479
480                         if ( t_end2 >= 0)
481                                 last2 = K_getline(K_get_token(1,t_end2));
482                         else
483                                 last2 = t_end2;
484
485                 }
486                 /*
487                 **      print the header for this difference
488                 */
489                 (void) printf("%d",_O_con_line(first1,flags,0));
490                 switch (type)
491                 {
492                 case _O_APP :
493                         (void) printf("a%d",_O_con_line(first2,flags,1));
494                         if (last2 > first2)
495                         {
496                                 (void) printf(",%d",_O_con_line(last2,flags,1));
497                         }
498                         (void) printf("\n");
499                         break;
500                 case _O_DEL :
501                         if (last1 > first1)
502                         {
503                                 (void) printf(",%d",_O_con_line(last1,flags,0));
504                         }
505                         (void) printf("d%d\n",_O_con_line(first2,flags,1));
506                         break;
507                 case _O_CHA :
508                         if (last1 > first1)
509                         {
510                                 (void) printf(",%d",_O_con_line(last1,flags,0));
511                         }
512                         (void) printf("c%d",_O_con_line(first2,flags,1));
513                         if (last2 > first2)
514                         {
515                                 (void) printf(",%d",_O_con_line(last2,flags,1));
516                         }
517                         (void) printf("\n");
518                         break;
519                 default:
520                         Z_fatal("type in O_output wasn't set\n");
521                 }
522                 if (_O_DEL == type || _O_CHA == type)
523                 {
524                         if (flags & U_TOKENS)
525                         {
526                                 int cnt;
527                                 for(cnt=first1;cnt <= last1; cnt++)
528                                 {
529                 (void) printf("< %s",
530                                                         _O_get_text(0,cnt,flags));
531                                 }
532                         }
533                         else
534                         {       
535                                 _O_do_lines(t_beg1,t_end1,0);
536                         }
537                 }
538                 if (_O_CHA == type)
539                 {
540                         (void) printf("---\n");
541                 }
542                 if (_O_APP == type || _O_CHA == type)
543                 {
544                         if (flags & U_TOKENS)
545                         {
546                                 int cnt;
547                                 for(cnt=first2;cnt <= last2; cnt++)
548                                 {
549                                         (void) printf("> %s",
550                                                 _O_get_text(1,cnt,flags));
551                                 }
552                         }
553                         else
554                         {
555                                 _O_do_lines(t_beg2,t_end2,1);
556                         }
557                 }
558         }
559         O_cleanup();
560         return;
561 }