summaryrefslogtreecommitdiff
path: root/gcc/genhooks.c
blob: a9eecd79a9287d21bb36f31025d7045812d40fb7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/* Process target.def to create initialization macros definition in
   target-hooks-def.h and documentation in target-hooks.texi.
   Copyright (C) 2009, 2010 Free Software Foundation, Inc.

This file is part of GCC.

GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3.  If not see
<http://www.gnu.org/licenses/>.  */
#include "bconfig.h"
#include "system.h"
#include <string.h>
#include "hashtab.h"
#include "errors.h"

struct hook_desc { const char *doc, *type, *name, *param, *init; };
static struct hook_desc hook_array[] = {
#define HOOK_VECTOR_1(NAME, FRAGMENT) \
  { 0, 0, #NAME, 0, 0 },
#define DEFHOOKPOD(NAME, DOC, TYPE, INIT) \
  { DOC, #TYPE, HOOK_PREFIX #NAME, 0, #INIT },
#define DEFHOOK(NAME, DOC, TYPE, PARAMS, INIT) \
  { DOC, #TYPE, HOOK_PREFIX #NAME, #PARAMS, #INIT },
#define DEFHOOK_UNDOC(NAME, DOC, TYPE, PARAMS, INIT) \
  { "*", #TYPE, HOOK_PREFIX #NAME, #PARAMS, #INIT },
#include "target.def"
#undef DEFHOOK
};

/* For each @Fcode in the the first paragraph of the documentation string DOC,
   print an @findex directive.  HOOK_NAME is the name of the hook this bit of
   documentation pertains to.  */
static void
emit_findices (const char *doc, const char *hook_name)
{
  const char *end = strstr (doc, "\n\n");
  const char *fcode;

  while ((fcode = strstr (doc, "@Fcode{")) && (!end || fcode < end))
    {
      fcode += strlen ("@Fcode{");
      doc = strchr (fcode, '}');
      if (!doc)
	fatal ("Malformed @Fcode for hook %s\n", hook_name);
      printf ("@findex %.*s\n", (int) (doc - fcode), fcode);
      doc = fcode;
    }
}

/* Return an upper-case copy of IN.  */
static char *
upstrdup (const char *in)
{
  char *p, *ret = xstrdup (in);
  for (p = ret; *p; p++)
    *p = TOUPPER (*p);
  return ret;
}

/* Struct for 'start hooks' which start a sequence of consecutive hooks
   that are defined in target.def and to be documented in tm.texi.  */
struct s_hook
{
  char *name;
  int pos;
};

static hashval_t
s_hook_hash (const void *p)
{
  const struct s_hook *s_hook = (const struct s_hook *)p;
  return htab_hash_string (s_hook->name);
}

static int
s_hook_eq_p (const void *p1, const void *p2)
{
  return (strcmp (((const struct s_hook *) p1)->name, 
		  ((const struct s_hook *) p2)->name) == 0);
}

/* Read the documentation file with name IN_FNAME, perform substitutions
   to incorporate informtion from hook_array, and emit the result on stdout.
   Hooks defined with DEFHOOK / DEFHOOKPOD are emitted at the place of a
   matching @hook in the input file; if there is no matching @hook, the
   hook is emitted after the hook that precedes it in target.def .
   Usually, the emitted hook documentation starts with the hook
   signature, followed by the string from the doc field.
   The documentation is bracketed in @deftypefn / @deftypevr and a matching
   @end.
   While emitting the doc field, @Fcode is translated to @code, and an
   @findex entry is added to the affected paragraph.
   If the doc field starts with '*', the leading '*' is stripped, and the doc
   field is otherwise emitted unaltered; no function signature/
   @deftypefn/deftypevr/@end is emitted.
   In particular, a doc field of "*" means not to emit any ocumentation for
   this target.def / hook_array entry at all (there might be documentation
   for this hook in the file named IN_FNAME, though).
   A doc field of 0 is used to append the hook signature after the previous
   hook's signture, so that one description can be used for a group of hooks.
   When the doc field is "", @deftypefn/@deftypevr and the hook signature
   is emitted, but not the matching @end.  This allows all the free-form
   documentation to be placed in IN_FNAME, to work around GPL/GFDL
   licensing incompatibility issues.  */
static void
emit_documentation (const char *in_fname)
{
  int i, j;
  char buf[1000];
  htab_t start_hooks = htab_create (99, s_hook_hash, s_hook_eq_p, (htab_del) 0);
  FILE *f;
  bool found_start = false;

  /* Enter all the start hooks in start_hooks.  */
  f = fopen (in_fname, "r");
  if (!f)
    {
      perror ("");
      fatal ("Couldn't open input file");
    }
  while (fscanf (f, "%*[^@]"), buf[0] = '\0',
	 fscanf (f, "@%5[^ \n]", buf) != EOF)
    {
      void **p;
      struct s_hook *shp;

      if (strcmp (buf, "hook") != 0)
	continue;
      buf[0] = '\0';
      fscanf (f, "%999s", buf);
      shp = XNEW (struct s_hook);
      shp->name = upstrdup (buf);
      shp->pos = -1;
      p = htab_find_slot (start_hooks, shp, INSERT);
      if (*p != HTAB_EMPTY_ENTRY)
	fatal ("Duplicate placement for hook %s\n", shp->name);
      *(struct s_hook **) p = shp;
    }
  fclose (f);
  /* For each hook in hook_array, if it is a start hook, store its position.  */
  for (i = 0; i < (int) (sizeof hook_array / sizeof hook_array[0]); i++)
    {
      struct s_hook sh, *shp;
      void *p;

      if (!hook_array[i].doc || strcmp (hook_array[i].doc, "*") == 0)
	continue;
      sh.name = upstrdup (hook_array[i].name);
      p = htab_find (start_hooks, &sh);
      if (p)
	{
	  shp = (struct s_hook *) p;
	  if (shp->pos >= 0)
	    fatal ("Duplicate hook %s\n", sh.name);
	  shp->pos = i;
	  found_start = true;
	}
      else if (!found_start)
	fatal ("No place specified to document hook %s\n", sh.name);
      free (sh.name);
    }
  /* Copy input file to stdout, substituting @hook directives with the
     corresponding hook documentation sequences.  */
  f = fopen (in_fname, "r");
  if (!f)
    {
      perror ("");
      fatal ("Couldn't open input file");
    }
  for (;;)
    {
      struct s_hook sh, *shp;
      int c = getc (f);
      char *name;

      if (c == EOF)
	break;
      if (c != '@')
	{
	  putchar (c);
	  continue;
	}
      buf[0] = '\0';
      fscanf (f, "%5[^ \n]", buf);
      if (strcmp (buf, "hook") != 0)
	{
	  printf ("@%s", buf);
	  continue;
	}
      fscanf (f, "%999s", buf);
      sh.name = name = upstrdup (buf);
      shp = (struct s_hook *) htab_find (start_hooks, &sh);
      if (!shp || shp->pos < 0)
	fatal ("No documentation for hook %s\n", sh.name);
      i = shp->pos;
      do
	{
	  const char *q, *e;
	  const char *deftype;
	  const char *doc, *fcode, *p_end;

	  /* A leading '*' means to output the documentation string without
	     further processing.  */
	  if (*hook_array[i].doc == '*')
	    printf ("%s", hook_array[i].doc + 1);
	  else
	    {
	      if (i != shp->pos)
		printf ("\n\n");
	      emit_findices (hook_array[i].doc, name);

	      /* Print header.  Function-valued hooks have a parameter list, 
		 unlike POD-valued ones.  */
	      deftype = hook_array[i].param ? "deftypefn" : "deftypevr";
	      printf ("@%s {Target Hook} ", deftype);
	      if (strchr (hook_array[i].type, ' '))
		printf ("{%s}", hook_array[i].type);
	      else
		printf ("%s", hook_array[i].type);
	      printf (" %s", name);
	      if (hook_array[i].param)
		{
		  /* Print the parameter list, with the parameter names
		     enclosed in @var{}.  */
		  printf (" ");
		  for (q = hook_array[i].param; (e = strpbrk (q, " *,)"));
		       q = e + 1)
		    /* Type names like 'int' are followed by a space, sometimes
		       also by '*'.  'void' should appear only in "(void)".  */
		    if (*e == ' ' || *e == '*' || *q == '(')
		      printf ("%.*s", (int) (e - q + 1), q);
		    else
		      printf ("@var{%.*s}%c", (int) (e - q), q, *e);
		}
	      /* POD-valued hooks sometimes come in groups with common
		 documentation.*/
	      for (j = i + 1;
		   j < (int) (sizeof hook_array / sizeof hook_array[0])
		   && hook_array[j].doc == 0 && hook_array[j].type; j++)
		{
		  char *namex = upstrdup (hook_array[j].name);

		  printf ("\n@%sx {Target Hook} {%s} %s",
			  deftype, hook_array[j].type, namex);
		}
	      if (hook_array[i].doc[0])
		{
		  printf ("\n");
		  /* Print each documentation paragraph in turn.  */
		  for (doc = hook_array[i].doc; *doc; doc = p_end)
		    {
		      /* Find paragraph end.  */
		      p_end = strstr (doc, "\n\n");
		      p_end = (p_end ? p_end + 2 : doc + strlen (doc));
		      /* Print paragraph, emitting @Fcode as @code.  */
		      for (; (fcode = strstr (doc, "@Fcode{")) && fcode < p_end;
			   doc = fcode + 2)
			printf ("%.*s@", (int) (fcode - doc), doc);
		      printf ("%.*s", (int) (p_end - doc), doc);
		      /* Emit function indices for next paragraph.  */
		      emit_findices (p_end, name);
		    }
		  printf ("\n@end %s", deftype);
		}
	    }
	  if (++i >= (int) (sizeof hook_array / sizeof hook_array[0])
	      || !hook_array[i].doc)
	    break;
	  free (name);
	  sh.name = name = upstrdup (hook_array[i].name);
	}
      while (!htab_find (start_hooks, &sh));
      free (name);
    }
}

/* Emit #defines to stdout (this will be redirected to generate
   target-hook-def.h) which set target hooks initializer macros
   to their default values.  */
static void
emit_init_macros (void)
{
  int i;
  const int MAX_NEST = 2;
  int print_nest, nest = 0;

  for (print_nest = 0; print_nest <= MAX_NEST; print_nest++)
    {
      for (i = 0; i < (int) (sizeof hook_array / sizeof hook_array[0]); i++)
	{
	  char *name = upstrdup (hook_array[i].name);

	  if (!hook_array[i].type)
	    {
	      if (*name)
		{
		  if (nest && nest == print_nest)
		    printf ("    %s, \\\n", name);
		  nest++;
		  if (nest > MAX_NEST)
		    fatal ("Unexpected nesting of %s\n", name);
		  if (nest == print_nest)
		    printf ("\n#define %s \\\n  { \\\n", name);
		}
	      else
		{
		  if (nest == print_nest)
		    printf ("  }\n");
		  nest--;
		}
	      continue;
	    }
	  if (0 == print_nest)
	    {
	      /* Output default definitions of target hooks.  */
	      printf ("#ifndef %s\n#define %s %s\n#endif\n",
		      name, name, hook_array[i].init);
	    }
	  if (nest == print_nest)
	    printf ("    %s, \\\n", name);
	}
    }
}

int
main (int argc, char **argv)
{
  if (argc >= 2)
    emit_documentation (argv[1]);
  else
    emit_init_macros ();
  return 0;
}