fc2d1078ac2f021e87f631811aaa10575a8467b9
[gcc.git] / gcc / cp / xref.c
1 /* Code for handling XREF output from GNU C++.
2 Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
3 Contributed by Michael Tiemann (tiemann@cygnus.com)
4
5 This file is part of GNU CC.
6
7 GNU CC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU CC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
21
22
23 #include "config.h"
24 #include "tree.h"
25 #include <stdio.h>
26 #include "cp-tree.h"
27 #include "input.h"
28
29 #include <ctype.h>
30
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34
35 #ifdef HAVE_UNISTD_H
36 #include <unistd.h>
37 #endif
38
39 extern char *getpwd PROTO((void));
40
41 #ifdef HAVE_STRING_H
42 #include <string.h>
43 #else
44 extern char *index ();
45 extern char *rindex ();
46 #endif
47
48 /* The character(s) used to join a directory specification (obtained with
49 getwd or equivalent) with a non-absolute file name. */
50
51 #ifndef FILE_NAME_JOINER
52 #define FILE_NAME_JOINER "/"
53 #endif
54
55 /* Nonzero if NAME as a file name is absolute. */
56 #ifndef FILE_NAME_ABSOLUTE_P
57 #define FILE_NAME_ABSOLUTE_P(NAME) (NAME[0] == '/')
58 #endif
59
60 /* For cross referencing. */
61
62 int flag_gnu_xref;
63
64 /************************************************************************/
65 /* */
66 /* Common definitions */
67 /* */
68 /************************************************************************/
69
70 #ifndef TRUE
71 #define TRUE 1
72 #endif
73 #ifndef FALSE
74 #define FALSE 0
75 #endif
76
77 #define PALLOC(typ) ((typ *) calloc(1,sizeof(typ)))
78
79
80 /* Return a malloc'd copy of STR. */
81 #define SALLOC(str) \
82 ((char *) ((str) == NULL ? NULL \
83 : (char *) strcpy ((char *) malloc (strlen ((str)) + 1), (str))))
84 #define SFREE(str) (str != NULL && (free(str),0))
85
86 #define STREQL(s1,s2) (strcmp((s1),(s2)) == 0)
87 #define STRNEQ(s1,s2) (strcmp((s1),(s2)) != 0)
88 #define STRLSS(s1,s2) (strcmp((s1),(s2)) < 0)
89 #define STRLEQ(s1,s2) (strcmp((s1),(s2)) <= 0)
90 #define STRGTR(s1,s2) (strcmp((s1),(s2)) > 0)
91 #define STRGEQ(s1,s2) (strcmp((s1),(s2)) >= 0)
92
93 /************************************************************************/
94 /* */
95 /* Type definitions */
96 /* */
97 /************************************************************************/
98
99
100 typedef struct _XREF_FILE * XREF_FILE;
101 typedef struct _XREF_SCOPE * XREF_SCOPE;
102
103 typedef struct _XREF_FILE
104 {
105 char *name;
106 char *outname;
107 XREF_FILE next;
108 } XREF_FILE_INFO;
109
110 typedef struct _XREF_SCOPE
111 {
112 int gid;
113 int lid;
114 XREF_FILE file;
115 int start;
116 XREF_SCOPE outer;
117 } XREF_SCOPE_INFO;
118
119 /************************************************************************/
120 /* */
121 /* Local storage */
122 /* */
123 /************************************************************************/
124
125 static char doing_xref = 0;
126 static FILE * xref_file = NULL;
127 static char xref_name[1024];
128 static XREF_FILE all_files = NULL;
129 static char * wd_name = NULL;
130 static XREF_SCOPE cur_scope = NULL;
131 static int scope_ctr = 0;
132 static XREF_FILE last_file = NULL;
133 static tree last_fndecl = NULL;
134
135 /************************************************************************/
136 /* */
137 /* Forward definitions */
138 /* */
139 /************************************************************************/
140 static void gen_assign PROTO((XREF_FILE, tree));
141 static XREF_FILE find_file PROTO((char *));
142 static char * filename PROTO((XREF_FILE));
143 static char * fctname PROTO((tree));
144 static char * declname PROTO((tree));
145 static void simplify_type PROTO((char *));
146 static char * fixname PROTO((char *, char *));
147 static void open_xref_file PROTO((char *));
148
149 /* Start cross referencing. FILE is the name of the file we xref. */
150
151 void
152 GNU_xref_begin (file)
153 char *file;
154 {
155 doing_xref = 1;
156
157 if (file != NULL && STRNEQ (file,"-"))
158 {
159 open_xref_file(file);
160 GNU_xref_file(file);
161 }
162 }
163
164 /* Finish cross-referencing. ERRCNT is the number of errors
165 we encountered. */
166
167 void
168 GNU_xref_end (ect)
169 int ect;
170 {
171 XREF_FILE xf;
172
173 if (!doing_xref) return;
174
175 xf = find_file (input_filename);
176 if (xf == NULL) return;
177
178 while (cur_scope != NULL)
179 GNU_xref_end_scope(cur_scope->gid,0,0,0);
180
181 doing_xref = 0;
182
183 if (xref_file == NULL) return;
184
185 fclose (xref_file);
186
187 xref_file = NULL;
188 all_files = NULL;
189
190 if (ect > 0) unlink (xref_name);
191 }
192
193 /* Write out xref for file named NAME. */
194
195 void
196 GNU_xref_file (name)
197 char *name;
198 {
199 XREF_FILE xf;
200
201 if (!doing_xref || name == NULL) return;
202
203 if (xref_file == NULL)
204 {
205 open_xref_file (name);
206 if (!doing_xref) return;
207 }
208
209 if (all_files == NULL)
210 fprintf(xref_file,"SCP * 0 0 0 0 RESET\n");
211
212 xf = find_file (name);
213 if (xf != NULL) return;
214
215 xf = PALLOC (XREF_FILE_INFO);
216 xf->name = SALLOC (name);
217 xf->next = all_files;
218 all_files = xf;
219
220 if (wd_name == NULL)
221 wd_name = getpwd ();
222
223 if (FILE_NAME_ABSOLUTE_P (name) || ! wd_name)
224 xf->outname = xf->name;
225 else
226 {
227 char *nmbuf
228 = (char *) malloc (strlen (wd_name) + strlen (FILE_NAME_JOINER)
229 + strlen (name) + 1);
230 sprintf (nmbuf, "%s%s%s", wd_name, FILE_NAME_JOINER, name);
231 name = nmbuf;
232 xf->outname = nmbuf;
233 }
234
235 fprintf (xref_file, "FIL %s %s 0\n", name, wd_name);
236
237 filename (xf);
238 fctname (NULL);
239 }
240
241 /* Start a scope identified at level ID. */
242
243 void
244 GNU_xref_start_scope (id)
245 HOST_WIDE_INT id;
246 {
247 XREF_SCOPE xs;
248 XREF_FILE xf;
249
250 if (!doing_xref) return;
251 xf = find_file (input_filename);
252
253 xs = PALLOC (XREF_SCOPE_INFO);
254 xs->file = xf;
255 xs->start = lineno;
256 if (xs->start <= 0) xs->start = 1;
257 xs->gid = id;
258 xs->lid = ++scope_ctr;
259 xs->outer = cur_scope;
260 cur_scope = xs;
261 }
262
263 /* Finish a scope at level ID.
264 INID is ???
265 PRM is ???
266 KEEP is nonzero iff this scope is retained (nonzero if it's
267 a compiler-generated invisible scope).
268 TRNS is ??? */
269
270 void
271 GNU_xref_end_scope (id,inid,prm,keep)
272 HOST_WIDE_INT id;
273 HOST_WIDE_INT inid;
274 int prm,keep;
275 {
276 XREF_FILE xf;
277 XREF_SCOPE xs,lxs,oxs;
278 char *stype;
279
280 if (!doing_xref) return;
281 xf = find_file (input_filename);
282 if (xf == NULL) return;
283
284 lxs = NULL;
285 for (xs = cur_scope; xs != NULL; xs = xs->outer)
286 {
287 if (xs->gid == id) break;
288 lxs = xs;
289 }
290 if (xs == NULL) return;
291
292 if (inid != 0) {
293 for (oxs = cur_scope; oxs != NULL; oxs = oxs->outer) {
294 if (oxs->gid == inid) break;
295 }
296 if (oxs == NULL) return;
297 inid = oxs->lid;
298 }
299
300 if (prm == 2) stype = "SUE";
301 else if (prm != 0) stype = "ARGS";
302 else if (keep == 2 || inid != 0) stype = "INTERN";
303 else stype = "EXTERN";
304
305 fprintf (xref_file,"SCP %s %d %d %d %d %s\n",
306 filename (xf), xs->start, lineno,xs->lid, inid, stype);
307
308 if (lxs == NULL) cur_scope = xs->outer;
309 else lxs->outer = xs->outer;
310
311 free (xs);
312 }
313
314 /* Output a reference to NAME in FNDECL. */
315
316 void
317 GNU_xref_ref (fndecl,name)
318 tree fndecl;
319 char *name;
320 {
321 XREF_FILE xf;
322
323 if (!doing_xref) return;
324 xf = find_file (input_filename);
325 if (xf == NULL) return;
326
327 fprintf (xref_file, "REF %s %d %s %s\n",
328 filename (xf), lineno, fctname (fndecl), name);
329 }
330
331 /* Output a reference to DECL in FNDECL. */
332
333 void
334 GNU_xref_decl (fndecl,decl)
335 tree fndecl;
336 tree decl;
337 {
338 XREF_FILE xf,xf1;
339 char *cls;
340 char *name;
341 char buf[10240];
342 int uselin;
343
344 if (!doing_xref) return;
345 xf = find_file (input_filename);
346 if (xf == NULL) return;
347
348 uselin = FALSE;
349
350 if (TREE_CODE (decl) == TYPE_DECL) cls = "TYPEDEF";
351 else if (TREE_CODE (decl) == FIELD_DECL) cls = "FIELD";
352 else if (TREE_CODE (decl) == VAR_DECL)
353 {
354 if (fndecl == NULL && TREE_STATIC(decl)
355 && TREE_READONLY(decl) && DECL_INITIAL(decl) != 0
356 && !TREE_PUBLIC(decl) && !DECL_EXTERNAL(decl)
357 && DECL_MODE(decl) != BLKmode) cls = "CONST";
358 else if (DECL_EXTERNAL(decl)) cls = "EXTERN";
359 else if (TREE_PUBLIC(decl)) cls = "EXTDEF";
360 else if (TREE_STATIC(decl)) cls = "STATIC";
361 else if (DECL_REGISTER(decl)) cls = "REGISTER";
362 else cls = "AUTO";
363 }
364 else if (TREE_CODE (decl) == PARM_DECL) cls = "PARAM";
365 else if (TREE_CODE (decl) == FIELD_DECL) cls = "FIELD";
366 else if (TREE_CODE (decl) == CONST_DECL) cls = "CONST";
367 else if (TREE_CODE (decl) == FUNCTION_DECL)
368 {
369 if (DECL_EXTERNAL (decl)) cls = "EXTERN";
370 else if (TREE_PUBLIC (decl)) cls = "EFUNCTION";
371 else cls = "SFUNCTION";
372 }
373 else if (TREE_CODE (decl) == LABEL_DECL) cls = "LABEL";
374 else if (TREE_CODE (decl) == UNION_TYPE)
375 {
376 cls = "UNIONID";
377 decl = TYPE_NAME (decl);
378 uselin = TRUE;
379 }
380 else if (TREE_CODE (decl) == RECORD_TYPE)
381 {
382 if (CLASSTYPE_DECLARED_CLASS (decl)) cls = "CLASSID";
383 else if (IS_SIGNATURE (decl)) cls = "SIGNATUREID";
384 else cls = "STRUCTID";
385 decl = TYPE_NAME (decl);
386 uselin = TRUE;
387 }
388 else if (TREE_CODE (decl) == ENUMERAL_TYPE)
389 {
390 cls = "ENUMID";
391 decl = TYPE_NAME (decl);
392 uselin = TRUE;
393 }
394 else if (TREE_CODE (decl) == TEMPLATE_DECL)
395 {
396 if (TREE_CODE (DECL_RESULT (decl)) == TYPE_DECL)
397 cls = "CLASSTEMP";
398 else if (TREE_CODE (DECL_RESULT (decl)) == FUNCTION_DECL)
399 cls = "FUNCTEMP";
400 else if (TREE_CODE (DECL_RESULT (decl)) == VAR_DECL)
401 cls = "VARTEMP";
402 else
403 my_friendly_abort (358);
404 uselin = TRUE;
405 }
406 else cls = "UNKNOWN";
407
408 if (decl == NULL || DECL_NAME (decl) == NULL) return;
409
410 if (uselin && decl->decl.linenum > 0 && decl->decl.filename != NULL)
411 {
412 xf1 = find_file (decl->decl.filename);
413 if (xf1 != NULL)
414 {
415 lineno = decl->decl.linenum;
416 xf = xf1;
417 }
418 }
419
420 if (DECL_ASSEMBLER_NAME (decl))
421 name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
422 else
423 name = IDENTIFIER_POINTER (DECL_NAME (decl));
424
425 strcpy (buf, type_as_string (TREE_TYPE (decl), 0));
426 simplify_type (buf);
427
428 fprintf (xref_file, "DCL %s %d %s %d %s %s %s\n",
429 filename(xf), lineno, name,
430 (cur_scope != NULL ? cur_scope->lid : 0),
431 cls, fctname(fndecl), buf);
432
433 if (STREQL (cls, "STRUCTID") || STREQL (cls, "UNIONID")
434 || STREQL (cls, "SIGNATUREID"))
435 {
436 cls = "CLASSID";
437 fprintf (xref_file, "DCL %s %d %s %d %s %s %s\n",
438 filename(xf), lineno,name,
439 (cur_scope != NULL ? cur_scope->lid : 0),
440 cls, fctname(fndecl), buf);
441 }
442 }
443
444 /* Output a reference to a call to NAME in FNDECL. */
445
446 void
447 GNU_xref_call (fndecl, name)
448 tree fndecl;
449 char *name;
450 {
451 XREF_FILE xf;
452 char buf[1024];
453 char *s;
454
455 if (!doing_xref) return;
456 xf = find_file (input_filename);
457 if (xf == NULL) return;
458 name = fixname (name, buf);
459
460 for (s = name; *s != 0; ++s)
461 if (*s == '_' && s[1] == '_') break;
462 if (*s != 0) GNU_xref_ref (fndecl, name);
463
464 fprintf (xref_file, "CAL %s %d %s %s\n",
465 filename (xf), lineno, name, fctname (fndecl));
466 }
467
468 /* Output cross-reference info about FNDECL. If non-NULL,
469 ARGS are the arguments for the function (i.e., before the FUNCTION_DECL
470 has been fully built). */
471
472 void
473 GNU_xref_function (fndecl, args)
474 tree fndecl;
475 tree args;
476 {
477 XREF_FILE xf;
478 int ct;
479 char buf[1024];
480
481 if (!doing_xref) return;
482 xf = find_file (input_filename);
483 if (xf == NULL) return;
484
485 ct = 0;
486 buf[0] = 0;
487 if (args == NULL) args = DECL_ARGUMENTS (fndecl);
488
489 GNU_xref_decl (NULL, fndecl);
490
491 for ( ; args != NULL; args = TREE_CHAIN (args))
492 {
493 GNU_xref_decl (fndecl,args);
494 if (ct != 0) strcat (buf,",");
495 strcat (buf, declname (args));
496 ++ct;
497 }
498
499 fprintf (xref_file, "PRC %s %d %s %d %d %s\n",
500 filename(xf), lineno, declname(fndecl),
501 (cur_scope != NULL ? cur_scope->lid : 0),
502 ct, buf);
503 }
504
505 /* Output cross-reference info about an assignment to NAME. */
506
507 void
508 GNU_xref_assign(name)
509 tree name;
510 {
511 XREF_FILE xf;
512
513 if (!doing_xref) return;
514 xf = find_file(input_filename);
515 if (xf == NULL) return;
516
517 gen_assign(xf, name);
518 }
519
520 static void
521 gen_assign(xf, name)
522 XREF_FILE xf;
523 tree name;
524 {
525 char *s;
526
527 s = NULL;
528
529 switch (TREE_CODE (name))
530 {
531 case IDENTIFIER_NODE :
532 s = IDENTIFIER_POINTER(name);
533 break;
534 case VAR_DECL :
535 s = declname(name);
536 break;
537 case COMPONENT_REF :
538 gen_assign(xf, TREE_OPERAND(name, 0));
539 gen_assign(xf, TREE_OPERAND(name, 1));
540 break;
541 case INDIRECT_REF :
542 case OFFSET_REF :
543 case ARRAY_REF :
544 case BUFFER_REF :
545 gen_assign(xf, TREE_OPERAND(name, 0));
546 break;
547 case COMPOUND_EXPR :
548 gen_assign(xf, TREE_OPERAND(name, 1));
549 break;
550 default :
551 break;
552 }
553
554 if (s != NULL)
555 fprintf(xref_file, "ASG %s %d %s\n", filename(xf), lineno, s);
556 }
557
558 /* Output cross-reference info about a class hierarchy.
559 CLS is the class type of interest. BASE is a baseclass
560 for CLS. PUB and VIRT give the access info about
561 the class derivation. FRND is nonzero iff BASE is a friend
562 of CLS.
563
564 ??? Needs to handle nested classes. */
565
566 void
567 GNU_xref_hier(cls, base, pub, virt, frnd)
568 char *cls;
569 char *base;
570 int pub;
571 int virt;
572 int frnd;
573 {
574 XREF_FILE xf;
575
576 if (!doing_xref) return;
577 xf = find_file(input_filename);
578 if (xf == NULL) return;
579
580 fprintf(xref_file, "HIE %s %d %s %s %d %d %d\n",
581 filename(xf), lineno, cls, base, pub, virt, frnd);
582 }
583
584 /* Output cross-reference info about class members. CLS
585 is the containing type; FLD is the class member. */
586
587 void
588 GNU_xref_member(cls, fld)
589 tree cls;
590 tree fld;
591 {
592 XREF_FILE xf;
593 char *prot;
594 int confg, pure;
595 char *d;
596 #ifdef XREF_SHORT_MEMBER_NAMES
597 int i;
598 #endif
599 char buf[1024], bufa[1024];
600
601 if (!doing_xref) return;
602 xf = find_file(fld->decl.filename);
603 if (xf == NULL) return;
604
605 if (TREE_PRIVATE (fld)) prot = "PRIVATE";
606 else if (TREE_PROTECTED(fld)) prot = "PROTECTED";
607 else prot = "PUBLIC";
608
609 confg = 0;
610 if (TREE_CODE (fld) == FUNCTION_DECL && DECL_CONST_MEMFUNC_P(fld))
611 confg = 1;
612 else if (TREE_CODE (fld) == CONST_DECL)
613 confg = 1;
614
615 pure = 0;
616 if (TREE_CODE (fld) == FUNCTION_DECL && DECL_ABSTRACT_VIRTUAL_P(fld))
617 pure = 1;
618
619 d = IDENTIFIER_POINTER(cls);
620 sprintf(buf, "%d%s", strlen(d), d);
621 #ifdef XREF_SHORT_MEMBER_NAMES
622 i = strlen(buf);
623 #endif
624 strcpy(bufa, declname(fld));
625
626 #ifdef XREF_SHORT_MEMBER_NAMES
627 for (p = &bufa[1]; *p != 0; ++p)
628 {
629 if (p[0] == '_' && p[1] == '_' && p[2] >= '0' && p[2] <= '9') {
630 if (strncmp(&p[2], buf, i) == 0) *p = 0;
631 break;
632 }
633 else if (p[0] == '_' && p[1] == '_' && p[2] == 'C' && p[3] >= '0' && p[3] <= '9') {
634 if (strncmp(&p[3], buf, i) == 0) *p = 0;
635 break;
636 }
637 }
638 #endif
639
640 fprintf(xref_file, "MEM %s %d %s %s %s %d %d %d %d %d %d %d\n",
641 filename(xf), fld->decl.linenum, d, bufa, prot,
642 (TREE_CODE (fld) == FUNCTION_DECL ? 0 : 1),
643 (DECL_INLINE (fld) ? 1 : 0),
644 (DECL_LANG_SPECIFIC(fld) && DECL_FRIEND_P(fld) ? 1 : 0),
645 (DECL_VINDEX(fld) ? 1 : 0),
646 (TREE_STATIC(fld) ? 1 : 0),
647 pure, confg);
648 }
649
650 /* Find file entry given name. */
651
652 static XREF_FILE
653 find_file(name)
654 char *name;
655 {
656 XREF_FILE xf;
657
658 for (xf = all_files; xf != NULL; xf = xf->next) {
659 if (STREQL(name, xf->name)) break;
660 }
661
662 return xf;
663 }
664
665 /* Return filename for output purposes. */
666
667 static char *
668 filename(xf)
669 XREF_FILE xf;
670 {
671 if (xf == NULL) {
672 last_file = NULL;
673 return "*";
674 }
675
676 if (last_file == xf) return "*";
677
678 last_file = xf;
679
680 return xf->outname;
681 }
682
683 /* Return function name for output purposes. */
684
685 static char *
686 fctname(fndecl)
687 tree fndecl;
688 {
689 static char fctbuf[1024];
690 char *s;
691
692 if (fndecl == NULL && last_fndecl == NULL) return "*";
693
694 if (fndecl == NULL)
695 {
696 last_fndecl = NULL;
697 return "*TOP*";
698 }
699
700 if (fndecl == last_fndecl) return "*";
701
702 last_fndecl = fndecl;
703
704 s = declname(fndecl);
705 s = fixname(s, fctbuf);
706
707 return s;
708 }
709
710 /* Return decl name for output purposes. */
711
712 static char *
713 declname(dcl)
714 tree dcl;
715 {
716 if (DECL_NAME (dcl) == NULL) return "?";
717
718 if (DECL_ASSEMBLER_NAME (dcl))
719 return IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (dcl));
720 else
721 return IDENTIFIER_POINTER (DECL_NAME (dcl));
722 }
723
724 /* Simplify a type string by removing unneeded parenthesis. */
725
726 static void
727 simplify_type(typ)
728 char *typ;
729 {
730 char *s;
731 int lvl, i;
732
733 i = strlen(typ);
734 while (i > 0 && isspace(typ[i-1])) typ[--i] = 0;
735
736 if (i > 7 && STREQL(&typ[i-5], "const"))
737 {
738 typ[i-5] = 0;
739 i -= 5;
740 }
741
742 if (typ[i-1] != ')') return;
743
744 s = &typ[i-2];
745 lvl = 1;
746 while (*s != 0) {
747 if (*s == ')') ++lvl;
748 else if (*s == '(')
749 {
750 --lvl;
751 if (lvl == 0)
752 {
753 s[1] = ')';
754 s[2] = 0;
755 break;
756 }
757 }
758 --s;
759 }
760
761 if (*s != 0 && s[-1] == ')')
762 {
763 --s;
764 --s;
765 if (*s == '(') s[2] = 0;
766 else if (*s == ':') {
767 while (*s != '(') --s;
768 s[1] = ')';
769 s[2] = 0;
770 }
771 }
772 }
773
774 /* Fixup a function name (take care of embedded spaces). */
775
776 static char *
777 fixname(nam, buf)
778 char *nam;
779 char *buf;
780 {
781 char *s, *t;
782 int fg;
783
784 s = nam;
785 t = buf;
786 fg = 0;
787
788 while (*s != 0)
789 {
790 if (*s == ' ')
791 {
792 *t++ = '\36';
793 ++fg;
794 }
795 else *t++ = *s;
796 ++s;
797 }
798 *t = 0;
799
800 if (fg == 0) return nam;
801
802 return buf;
803 }
804
805 /* Open file for xreffing. */
806
807 static void
808 open_xref_file(file)
809 char *file;
810 {
811 char *s, *t;
812
813 #ifdef XREF_FILE_NAME
814 XREF_FILE_NAME (xref_name, file);
815 #else
816 s = rindex (file, '/');
817 if (s == NULL)
818 sprintf (xref_name, ".%s.gxref", file);
819 else
820 {
821 ++s;
822 strcpy (xref_name, file);
823 t = rindex (xref_name, '/');
824 ++t;
825 *t++ = '.';
826 strcpy (t, s);
827 strcat (t, ".gxref");
828 }
829 #endif /* no XREF_FILE_NAME */
830
831 xref_file = fopen(xref_name, "w");
832
833 if (xref_file == NULL)
834 {
835 error("Can't create cross-reference file `%s'", xref_name);
836 doing_xref = 0;
837 }
838 }