Fixed another typo in string...
[gtk-doc.git] / gtkdoc-scanobj.in
blobbd8bf224bd2c0121dd06fc1f376296339c1d54c3
1 #!@PERL@ -w
2 # -*- cperl -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998  Damon Chaplin
7 # This program 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 of the License, or
10 # (at your option) any later version.
12 # This program 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.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 # This gets information about object heirarchies and signals
24 # by compiling a small C program. CFLAGS and LDFLAGS must be
25 # set appropriately before running this script.
28 use Getopt::Long;
30 push @INC, '@PACKAGE_DATA_DIR@';
31 require "gtkdoc-common.pl";
33 # Options
35 # name of documentation module
36 my $MODULE;
37 my $OUTPUT_DIR;
38 my $PRINT_VERSION;
40 my $CC;
41 my $LD;
42 my $CFLAGS;
43 my $LDFLAGS;
44 my $RUN;
46 %optctl = (module => \$MODULE,
47            types => \$TYPES_FILE,
48            nogtkinit => \$NO_GTK_INIT,
49            'output-dir' => \$OUTPUT_DIR,
50            'cc' => \$CC,
51            'ld' => \$LD,
52            'cflags' => \$CFLAGS,
53            'ldflags' => \$LDFLAGS,
54            'run' => \$RUN,
55            'version' => \$PRINT_VERSION,
56            'help' => \$PRINT_HELP);
58 GetOptions(\%optctl, "module=s", "types:s", "output-dir:s", "nogtkinit", "cc:s", "ld:s", "run:s", "cflags:s", "ldflags:s", "version", "help");
60 if ($PRINT_VERSION) {
61     print "@VERSION@\n";
62     exit 0;
65 if (!$MODULE) {
66     $PRINT_HELP = 1;
69 if ($PRINT_HELP) {
70     print <<EOF;
71 gtkdoc-scanobj version @VERSION@ - introspect gtk-objects
73 --module=MODULE_NAME          Name of the doc module being parsed
74 --types=FILE                  The name of the file to store the types in
75 --output-dir=DIRNAME          The directory where the results are stored
76 --cc=COMPILER                 The compiler to use
77 --ld=LINKER                   The linker to use
78 --run=RUN                     Command for running the scanner
79 --cflags=CFLAGS               Compiler flags
80 --ldflags=LDFLAGS             Linker flags
81 --version                     Print the version of this program
82 --help                        Print this help
83 EOF
84     exit 0;
87 $OUTPUT_DIR = $OUTPUT_DIR ? $OUTPUT_DIR : ".";
89 $TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$OUTPUT_DIR/$MODULE.types";
91 open (TYPES, $TYPES_FILE) || die "Cannot open $TYPES_FILE: $!\n";
92 open (OUTPUT, ">$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n";
94 my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals";
95 my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new";
96 my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy";
97 my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new";
98 my $old_args_filename = "$OUTPUT_DIR/$MODULE.args";
99 my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new";
101 # write a C program to scan the types
103 $includes = "";
104 @types = ();
106 for (<TYPES>) {
107     if (/^#include/) {
108         $includes .= $_;
109     } elsif (/^%/) {
110         next;
111     } elsif (/^\s*$/) {
112         next;
113     } else {
114         chomp;
115         push @types, $_;
116     }
119 $ntypes = @types + 1;
121 print OUTPUT <<EOT;
122 #include <string.h>
123 #include <stdlib.h>
124 #include <stdio.h>
125 #include <errno.h>
127 $includes
128 GtkType object_types[$ntypes];
130 GtkType *
131 get_object_types (void)
133     gint i = 0;
136 for (@types) {
137     print OUTPUT "    object_types[i++] = $_ ();\n";
140 print OUTPUT <<EOT;
141     object_types[i] = 0;
143     return object_types;
147  * This uses GTK type functions to output signal prototypes and the widget
148  * hierarchy.
149  */
151 /* The output files */
152 gchar *signals_filename = "$new_signals_filename";
153 gchar *hierarchy_filename = "$new_hierarchy_filename";
154 gchar *args_filename = "$new_args_filename";
157 static void output_signals (void);
158 static void output_widget_signals (FILE *fp,
159                                    GtkType object_type);
160 static void output_widget_signal (FILE *fp,
161                                   GtkType object_type,
162                                   gchar *object_class_name,
163                                   guint signal_id);
164 static gchar * get_type_name (GtkType type,
165                               gboolean * is_pointer);
166 static void output_widget_hierarchy (void);
167 static void output_hierarchy (FILE *fp,
168                               GtkType type,
169                               guint level);
171 static void output_args (void);
172 static void output_widget_args (FILE *fp, GtkType object_type);
175 main (int argc, char *argv[])
179   if ($NO_GTK_INIT) {
180     print OUTPUT <<EOT;
181   gtk_type_init ();
183   } else {
184     print OUTPUT <<EOT;
185   gtk_init (&argc, &argv);
187   }
189 print OUTPUT <<EOT;
190   get_object_types ();
192   output_signals ();
193   output_widget_hierarchy ();
194   output_args ();
196   return 0;
200 static void
201 output_signals (void)
203   FILE *fp;
204   gint i;
206   fp = fopen (signals_filename, "w");
207   if (fp == NULL)
208     {
209       g_warning ("Couldn't open output file: %s : %s", signals_filename, g_strerror(errno));
210       return;
211     }
213   for (i = 0; object_types[i]; i++)
214     output_widget_signals (fp, object_types[i]);
216   fclose (fp);
220 /* This outputs all the signals of one widget. */
221 static void
222 output_widget_signals (FILE *fp, GtkType object_type)
224   GtkObjectClass *class;
225   gchar *object_class_name;
226   guint sig;
228   class = gtk_type_class (object_type);
229   if (!class || class->nsignals == 0)
230     return;
232   object_class_name = gtk_type_name (object_type);
234   for (sig = 0; sig < class->nsignals; sig++)
235     {
236       if (!class->signals[sig])
237         {
238           /*g_print ("Signal slot [%u] is empty\\n", sig);*/
239           continue;
240         }
242       output_widget_signal (fp, object_type, object_class_name,
243                             class->signals[sig]);
244     }
248 /* This outputs one signal. */
249 static void
250 output_widget_signal (FILE *fp,
251                       GtkType object_type,
252                       gchar *object_name,
253                       guint signal_id)
255   GtkSignalQuery *query_info;
256   gchar *ret_type, *pos, *type_name, *arg_name, *object_arg, *object_arg_start;
257   gboolean is_pointer;
258   gchar buffer[1024];
259   guint i, param;
260   gint param_num, widget_num, event_num, callback_num;
261   gint *arg_num;
262   gchar signal_name[128];
264   /*  g_print ("Object: %s Type: %i Signal: %u\\n", object_name, object_type,
265       signal_id);*/
267   param_num = 1;
268   widget_num = event_num = callback_num = 0;
270   query_info = gtk_signal_query (signal_id);
271   if (query_info == NULL)
272     {
273       g_warning ("Couldn't query signal");
274       return;
275     }
277   /* Output the signal object type and the argument name. We assume the
278      type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and
279      convert to lower case for the argument name. */
280   pos = buffer;
281   sprintf (pos, "%s ", object_name);
282   pos += strlen (pos);
284   /* Try to come up with a sensible variable name for the first arg
285    * I chops off 2 know prefixes :/ and makes the name lowercase
286    * It should replace lowercase -> uppercase with '_'
287    * see gtkdoc-scangobject.in for better algorithm
288    */
289   if (!strncmp (object_name, "Gtk", 3))
290       object_arg = object_name + 3;
291   else if (!strncmp (object_name, "Gnome", 5))
292       object_arg = object_name + 5;
293   else
294       object_arg = object_name;
296   object_arg_start = pos;
297   sprintf (pos, "*%s\\n", object_arg);
298   pos += strlen (pos);
299   g_strdown (object_arg_start);
300   if (!strcmp (object_arg_start, "widget"))
301     widget_num++;
303   /* Convert signal name to use underscores rather than dashes '-'. */
304   strncpy (signal_name, query_info->signal_name, 127);
305   signal_name[127] = '\\0';
306   for (i = 0; signal_name[i]; i++)
307     {
308       if (signal_name[i] == '-')
309         signal_name[i] = '_';
310     }
312   /* Output the signal parameters. */
313   for (param = 0; param < query_info->nparams; param++)
314     {
315       type_name = get_type_name (query_info->params[param], &is_pointer);
317       /* Most arguments to the callback are called "arg1", "arg2", etc.
318          GtkWidgets are called "widget", "widget2", ...
319          GtkCallbacks are called "callback", "callback2", ... */
320       if (!strcmp (type_name, "GtkWidget"))
321         {
322           arg_name = "widget";
323           arg_num = &widget_num;
324         }
325       else if (!strcmp (type_name, "GtkCallback")
326                || !strcmp (type_name, "GtkCCallback"))
327         {
328           arg_name = "callback";
329           arg_num = &callback_num;
330         }
331       else
332         {
333           arg_name = "arg";
334           arg_num = &param_num;
335         }
336       sprintf (pos, "%s ", type_name);
337       pos += strlen (pos);
339       if (!arg_num || *arg_num == 0)
340         sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name);
341       else
342         sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name,
343                  *arg_num);
344           pos += strlen (pos);
346           if (arg_num)
347             *arg_num += 1;
348     }
350   /* Output the return type and function name. */
351   ret_type = get_type_name (query_info->return_val, &is_pointer);
353   fprintf (fp,
354            "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s%s</RETURNS>\\n%s</SIGNAL>\\n\\n",
355            object_name, query_info->signal_name, ret_type, is_pointer ? "*" : "", buffer);
356   g_free (query_info);
360 /* Returns the type name to use for a signal argument or return value, given
361    the GtkType from the signal info. It also sets is_pointer to TRUE if the
362    argument needs a '*' since it is a pointer. */
363 static gchar *
364 get_type_name (GtkType type, gboolean * is_pointer)
366   gchar *type_name;
368   *is_pointer = FALSE;
369   type_name = gtk_type_name (type);
371   switch (type) {
372   case GTK_TYPE_NONE:
373   case GTK_TYPE_CHAR:
374   case GTK_TYPE_UCHAR:
375   case GTK_TYPE_BOOL:
376   case GTK_TYPE_INT:
377   case GTK_TYPE_UINT:
378   case GTK_TYPE_LONG:
379   case GTK_TYPE_ULONG:
380   case GTK_TYPE_FLOAT:
381   case GTK_TYPE_DOUBLE:
382   case GTK_TYPE_POINTER:
383     /* These all have normal C type names so they are OK. */
384     return type_name;
386   case GTK_TYPE_STRING:
387     /* A GtkString is really a gchar*. */
388     *is_pointer = TRUE;
389     return "gchar";
391   case GTK_TYPE_ENUM:
392   case GTK_TYPE_FLAGS:
393     /* We use a gint for both of these. Hopefully a subtype with a decent
394        name will be registered and used instead, as GTK+ does itself. */
395     return "gint";
397   case GTK_TYPE_BOXED:
398     /* A boxed value is just an opaque pointer, I think. */
399     return "gpointer";
401   case GTK_TYPE_SIGNAL:
402   case GTK_TYPE_ARGS:
403   case GTK_TYPE_FOREIGN:
404   case GTK_TYPE_CALLBACK:
405   case GTK_TYPE_C_CALLBACK:
406     /* FIXME: These are wrong. I think they expand into more than 1 argument.
407        See the GtkArg struct in gtktypeutils.h and gtkargcollector.c.
408        Fortunately I doubt anything uses these as signal args. */
409     return "gpointer";
411   default:
412     break;
413   }
415   /* For all GtkObject subclasses we can use the class name with a "*",
416      e.g. 'GtkWidget *'. */
417   if (gtk_type_is_a (type, GTK_TYPE_OBJECT))
418     *is_pointer = TRUE;
420   return type_name;
424 /* This outputs the hierarchy of all widgets which have been initialized,
425    i.e. by calling their XXX_get_type() initialization function. */
426 static void
427 output_widget_hierarchy (void)
429   FILE *fp;
431   fp = fopen (hierarchy_filename, "w");
432   if (fp == NULL)
433     {
434       g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, g_strerror(errno));
435       return;
436     }
437   output_hierarchy (fp, GTK_TYPE_OBJECT, 0);
438   fclose (fp);
442 /* This is called recursively to output the hierarchy of a widget. */
443 static void
444 output_hierarchy (FILE *fp,
445                   GtkType type,
446                   guint level)
448   GList *list;
449   guint i;
451   if (!type)
452     return;
454   for (i = 0; i < level; i++)
455     fprintf (fp, "  ");
456   fprintf (fp, "%s\\n", gtk_type_name (type));
458   list = gtk_type_children_types (type);
460   while (list)
461     {
462       GtkType child = (GtkType) list->data;
463       output_hierarchy (fp, child, level + 1);
464       list = list->next;
465     }
469 static void
470 output_args (void)
472   FILE *fp;
473   gint i;
475   fp = fopen (args_filename, "w");
476   if (fp == NULL)
477     {
478       g_warning ("Couldn't open output file: %s : %s", args_filename, g_strerror(errno));
479       return;
480     }
482   for (i = 0; object_types[i]; i++)
483     output_widget_args (fp, object_types[i]);
485   fclose (fp);
489 static void
490 output_widget_args (FILE *fp, GtkType object_type)
492   GtkObjectClass *class;
493   gchar *object_class_name;
494   GtkArg *args;
495   guint32 *arg_flags;
496   guint n_args;
497   guint arg;
498   gchar flags[16], *pos;
500   class = gtk_type_class (object_type);
501   if (!class)
502     return;
504   object_class_name = gtk_type_name (object_type);
506   args = gtk_object_query_args (class->type, &arg_flags, &n_args);
508   for (arg = 0; arg < n_args; arg++)
509     {
510       pos = flags;
511       /* We use one-character flags for simplicity. */
512       if (arg_flags[arg] & GTK_ARG_READABLE)
513         *pos++ = 'r';
514       if (arg_flags[arg] & GTK_ARG_WRITABLE)
515         *pos++ = 'w';
516       if (arg_flags[arg] & GTK_ARG_CONSTRUCT)
517         *pos++ = 'x';
518       if (arg_flags[arg] & GTK_ARG_CONSTRUCT_ONLY)
519         *pos++ = 'X';
520       if (arg_flags[arg] & GTK_ARG_CHILD_ARG)
521         *pos++ = 'c';
522       *pos = 0;
524       fprintf (fp, "<ARG>\\n<NAME>%s</NAME>\\n<TYPE>%s</TYPE>\\n<FLAGS>%s</FLAGS>\\n</ARG>\\n\\n",
525                args[arg].name, gtk_type_name (args[arg].type), flags);
526     }
528   g_free (args);
529   g_free (arg_flags);
533 close OUTPUT;
535 # Compile and run our file
537 unless ($CC) {
538     $CC = $ENV{CC} ? $ENV{CC} : "gcc";
540 unless ($LD) {
541     $LD = $ENV{LD} ? $ENV{LD} : $CC;
543 unless ($CFLAGS) {
544     $CFLAGS = $ENV{CFLAGS} ? $ENV{CFLAGS} : "";
546 unless ($LDFLAGS) {
547     $LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : "";
549 unless ($RUN) {
550     $RUN = $ENV{RUN} ? $ENV{RUN} : "";
553 my $o_file;
554 if ($CC =~ /libtool/) {
555   $o_file  = "$MODULE-scan.lo"
556 } else {
557   $o_file = "$MODULE-scan.o"
560 # Compiling scanner
561 $command = "$CC $CFLAGS -c -o $o_file $MODULE-scan.c";
562 system("($command)") == 0 or die "Compilation of scanner failed: $!\n";
564 # Linking scanner
565 # FIXME: Can we turn off as-needed for the docs (or better fix it?)
566 #$command = "$LD -Wl,--no-as-needed $o_file $LDFLAGS -o $MODULE-scan";
567 $command = "$LD $o_file $LDFLAGS -o $MODULE-scan";
568 system("($command)") == 0 or die "Linking of scanner failed: $!\n";
570 # Running scanner $MODULE-scan ";
571 system("($RUN ./$MODULE-scan)") == 0 or die "Scan failed: $!\n";
573 if (!defined($ENV{"GTK_DOC_KEEP_INTERMEDIATE"})) {
574   unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan";
577 &UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0);
578 &UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0);
579 &UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0);