db157468d21ed20a2f168670a8d6102dc7164120
[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;
128         }
129 }
130
131 static void
132 _O_end_standout()
133 {
134         if (_O_need_init)
135         {
136                 _O_st_init();
137         }
138         if (_O_st_ok)
139         {
140 #ifdef M_TERMCAP
141                 (void) printf("%s",_O_endline);
142 #endif 
143 #ifdef M_TERMINFO
144                 vidattr(0);
145 #endif
146         }
147         else
148         {
149                 _O_doing_ul = 0;
150         }
151 }
152
153 static void
154 _O_pchars(line,start,end)
155 char *line;
156 int start,end;
157 {
158         int cnt;
159
160         for(cnt=start;cnt < end; cnt++)
161         {
162                 if (_O_doing_ul)
163                 {
164                         (void) putchar('_');
165                         (void) putchar('\b');
166                 }
167                 (void) putchar(line[cnt]);
168         }
169 }
170
171
172 /*
173 **      convert a 0 origin token number to a 1 orgin token
174 **              number or 1 origin line number as appropriate
175 */
176 static int
177 _O_con_line(numb,flags,filenum)
178 int numb, flags,filenum;
179 {
180         if (flags & U_TOKENS)
181         {
182                 return(numb+1);
183         }
184         else
185         {
186                 /*
187                 **       check to make sure that this is a real
188                 **      line number. if not, then return 0
189                 **      on rare occasions, (i.e. insertion/deletion
190                 **      of the first token in a file) we'll get
191                 **      line numbers of -1.  the usual look-up technique
192                 **      won't work since we have no lines before than 0.
193                 */
194                 if (numb < 0)
195                         return(0);
196                 /*
197                 **      look up the line number the token and then
198                 **      add 1 to make line number 1 origin
199                 */
200                 return(L_tl2cl(filenum,numb)+1);
201         }
202 }
203
204 static char *
205 _O_convert(ptr)
206 char *ptr;
207 {
208         static char spacetext[Z_WORDLEN];
209
210         if (1 == strlen(ptr))
211         {
212                 switch (*ptr)
213                 {
214                         default:
215                                 break;
216                         case '\n' :
217                                 (void) strcpy(spacetext,"<NEWLINE>");
218                                 return(spacetext);
219                         case '\t' :
220                                 (void) strcpy(spacetext,"<TAB>");
221                                 return(spacetext);
222                         case ' ' :
223                                 (void) strcpy(spacetext,"<SPACE>");
224                                 return(spacetext);
225                 }
226                                 
227         }
228         return(ptr);
229 }
230
231 static char*
232 _O_get_text(file,index,flags)
233 int file,index,flags;
234 {
235         static char buf[Z_LINELEN*2];   /* leave lots of room for both
236                                                 the token text and the
237                                                 chatter that preceeds it */
238         char *text;
239         K_token tmp;
240
241         if (flags & U_TOKENS)
242         {
243                 tmp = K_gettoken(file,index);
244                 text = _O_convert(K_gettext(tmp));
245                 (void) sprintf(buf,"%s -- line %d, character %d\n",
246                                 text,
247                                 /*
248                                 **      add 1 to make output start at line 1 
249                                 **      and character numbers start at 1
250                                 */
251                                 L_tl2cl(file,K_getline(tmp))+1,
252                                 K_getpos(tmp)+1);
253                 return(buf);
254         }
255         else
256         {
257                 return(L_gettline(file,index));
258         }
259 }
260 #define _O_APP          1
261 #define _O_DEL          2
262 #define _O_CHA          3
263 #define _O_TYPE_E       4
264
265 static void
266 _O_do_lines(start,end,file)
267 int start,end,file;
268 {
269         int cnt;
270         int lastline = -1;
271         int nextline;
272         K_token nexttoken;
273         for (cnt=start;cnt <= end; cnt++)
274         {
275                 nexttoken = K_get_token(file,cnt);
276                 nextline = K_getline(nexttoken);
277                 if (lastline != nextline)
278                 {
279                         int lastone,lastchar;
280                         K_token lasttok;
281                         char linetext[Z_LINELEN+1];     /* leave room for
282                                                            terminator */
283                         if (0 == file)
284                         {
285                                 (void) printf("< ");
286                         }
287                         else
288                         {
289                                 (void) printf("> ");
290                         }
291
292                         /*
293                         **      put loop here if you want to print
294                         **      out any intervening lines that don't
295                         **      have any tokens on them
296                         */
297
298                         /*
299                         **      following line is necessary because
300                         **      L_gettline is a macro, and can't be passed
301                         */
302                         (void) strcpy(linetext,L_gettline(file,nextline));
303                         _O_pchars(linetext,0,K_getpos(nexttoken));
304                         _O_start_standout();
305                         /*
306                         **      look for last token on this line to be
307                         **      highlighted
308                         */
309                         for ( lastone=cnt,lasttok = K_get_token(file,lastone);
310                               (lastone<=end)&&(nextline == K_getline(lasttok));
311                                 lastone++,lasttok = K_get_token(file,lastone))
312                         {
313                         }
314                         lastone--;
315                         lasttok = K_get_token(file,lastone);
316                         lastchar = K_getpos(lasttok)
317                                         + strlen(K_gettext(lasttok));
318                         _O_pchars(linetext,K_getpos(nexttoken),lastchar);
319                         _O_end_standout();
320                         _O_pchars(linetext,lastchar,strlen(linetext));
321                         
322                         lastline = nextline;
323                 }
324         }
325 }
326
327 void
328 O_output(start,flags)
329 E_edit start;
330 int flags;
331 {
332         int type = _O_TYPE_E;   /* initialize to error state
333                                 ** this is to make sure that type is set
334                                 ** somewhere
335                                 */
336         int t_beg1, t_beg2, t_end1, t_end2; /* token numbers */
337         int first1, last1, first2, last2;
338
339         E_edit ep, behind, ahead, a, b;
340
341         /*
342         **      reverse the list of edits
343         */
344         ahead = start;
345         ep = E_NULL;
346         while (ahead != E_NULL) {
347                 /*
348                 **      set token numbers intentionally out of range
349                 **              as boilerplate
350                 */
351                 t_beg1 = t_beg2 = t_end1 = t_end2 = -1;
352                 /*
353                 **      edit script is 1 origin, all of
354                 **       our routines are zero origin
355                 */
356                 E_setl1(ahead,(E_getl1(ahead))-1);
357                 E_setl2(ahead,(E_getl2(ahead))-1);
358
359                 behind = ep;
360                 ep = ahead;
361                 ahead = E_getnext(ahead);
362                 E_setnext(ep,behind);
363         }
364
365         /*
366         **      now run down the list and collect the following information
367         **      type of change (_O_APP, _O_DEL or _O_CHA)
368         **      start and length for each file
369         */
370         while (ep != E_NULL)
371         {
372                 b = ep;
373                 /*
374                 **      operation always start here
375                 */
376                 t_beg1 = E_getl1(ep);
377                 /*
378                 **      any deletions will appear before any insertions,
379                 **      so, if the first edit is an E_INSERT, then this
380                 **      this is an _O_APP
381                 */
382                 if (E_getop(ep) == E_INSERT)
383                         type = _O_APP;
384                 else {
385                         /*
386                         **      run down the list looking for the edit
387                         **      that is not part of the current deletion
388                         */      
389                         do {
390                                 a = b;
391                                 b = E_getnext(b);
392                         } while ((b != E_NULL) &&
393                                  (E_getop(b) == E_DELETE) &&
394                                  ((E_getl1(b)) == ((E_getl1(a))+1)));
395                         /*
396                         **      if we have an insertion at the same place
397                         **      as the deletion we just scanned, then
398                         **      this is a change
399                         */
400                         if ((b != E_NULL) &&
401                                 ((E_getop(b)) == E_INSERT) &&
402                                 ((E_getl1(b))==(E_getl1(a))))
403                         {
404                                 type = _O_CHA;
405                         }
406                         else
407                         {
408                                 type = _O_DEL;
409                         }
410                         /*
411                         **      set up start and length information for
412                         **      first file
413                         */
414                         t_end1 = E_getl1(a);
415                         /*
416                         **      move pointer to beginning of insertion
417                         */
418                         ep = b;
419                         /*
420                         **      if we are showing only a deletion,
421                         **      then we're all done, so skip ahead
422                         */ 
423                         if (_O_DEL == type)
424                         {
425                                 t_beg2 = E_getl2(a);
426                                 t_end2 = -1;    /* dummy number, won't
427                                                         ever be printed */
428                                                    
429                                 goto skipit;
430                         }
431                 }
432                 t_beg2 = E_getl2(ep);
433                 t_end2 = t_beg2-1;
434                 /*
435                 **      now run down the list lookingfor the
436                 **      end of this insertion and keep count
437                 **      of the number of times we step along
438                 */
439                 do {
440                         t_end2++;
441                         ep = E_getnext(ep);
442                 } while ((ep != E_NULL) && ((E_getop(ep)) == E_INSERT) &&
443                                         ((E_getl1(ep)) == (E_getl1(b))));
444
445 skipit:;
446                 if (flags & U_TOKENS)
447                 {
448                         /*
449                         **      if we are dealing with tokens individually,
450                         **      then just print then set printing so
451                         */
452                                 first1 = t_beg1;
453                                 last1 = t_end1;
454                                 first2 = t_beg2;
455                                 last2 = t_end2;
456                 }
457                 else
458                 {
459                         /*
460                         **      we are printing differences in terms of lines
461                         **      so find the beginning and ending lines of the
462                         **      changes and print header in those terms
463                         */
464                         if ( t_beg1 >= 0)
465                                 first1 = K_getline(K_get_token(0,t_beg1));
466                         else
467                                 first1 = t_beg1;
468
469                         if ( t_end1 >= 0)
470                                 last1 = K_getline(K_get_token(0,t_end1));
471                         else
472                                 last1 = t_end1;
473
474                         if ( t_beg2 >= 0)
475                                 first2 = K_getline(K_get_token(1,t_beg2));
476                         else
477                                 first2 = t_beg2;
478
479                         if ( t_end2 >= 0)
480                                 last2 = K_getline(K_get_token(1,t_end2));
481                         else
482                                 last2 = t_end2;
483
484                 }
485                 /*
486                 **      print the header for this difference
487                 */
488                 (void) printf("%d",_O_con_line(first1,flags,0));
489                 switch (type)
490                 {
491                 case _O_APP :
492                         (void) printf("a%d",_O_con_line(first2,flags,1));
493                         if (last2 > first2)
494                         {
495                                 (void) printf(",%d",_O_con_line(last2,flags,1));
496                         }
497                         (void) printf("\n");
498                         break;
499                 case _O_DEL :
500                         if (last1 > first1)
501                         {
502                                 (void) printf(",%d",_O_con_line(last1,flags,0));
503                         }
504                         (void) printf("d%d\n",_O_con_line(first2,flags,1));
505                         break;
506                 case _O_CHA :
507                         if (last1 > first1)
508                         {
509                                 (void) printf(",%d",_O_con_line(last1,flags,0));
510                         }
511                         (void) printf("c%d",_O_con_line(first2,flags,1));
512                         if (last2 > first2)
513                         {
514                                 (void) printf(",%d",_O_con_line(last2,flags,1));
515                         }
516                         (void) printf("\n");
517                         break;
518                 default:
519                         Z_fatal("type in O_output wasn't set\n");
520                 }
521                 if (_O_DEL == type || _O_CHA == type)
522                 {
523                         if (flags & U_TOKENS)
524                         {
525                                 int cnt;
526                                 for(cnt=first1;cnt <= last1; cnt++)
527                                 {
528                 (void) printf("< %s",
529                                                         _O_get_text(0,cnt,flags));
530                                 }
531                         }
532                         else
533                         {       
534                                 _O_do_lines(t_beg1,t_end1,0);
535                         }
536                 }
537                 if (_O_CHA == type)
538                 {
539                         (void) printf("---\n");
540                 }
541                 if (_O_APP == type || _O_CHA == type)
542                 {
543                         if (flags & U_TOKENS)
544                         {
545                                 int cnt;
546                                 for(cnt=first2;cnt <= last2; cnt++)
547                                 {
548                                         (void) printf("> %s",
549                                                 _O_get_text(1,cnt,flags));
550                                 }
551                         }
552                         else
553                         {
554                                 _O_do_lines(t_beg2,t_end2,1);
555                         }
556                 }
557         }
558         O_cleanup();
559         return;
560 }