added newly added gobject/ headers.
[platform/upstream/glib.git] / gobject / glib-genmarshal.c
1 /* GLIB-GenMarshal - Marshaller generator for GObject library
2  * Copyright (C) 2000 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19 #include        <glib-object.h>
20
21 #include        <stdio.h>
22 #include        <fcntl.h>
23 #include        <string.h>
24 #include        <errno.h>
25 #include        <unistd.h>
26 #include        <sys/types.h>
27 #include        <sys/stat.h>
28
29
30 /* --- defines --- */
31 #define PRG_NAME        "glib-genmarshal"
32 #define PKG_NAME        "GLib"
33 #define PKG_HTTP_HOME   "http://www.gtk.org"
34
35
36 /* --- typedefs & structures --- */
37 typedef struct _Argument  Argument;
38 typedef struct _Signature Signature;
39 struct _Argument
40 {
41   gchar       *pname;           /* parsed name */
42   const gchar *sname;           /* signature name */
43   const gchar *func;            /* functional extension */
44   const gchar *cname;           /* C name */
45 };
46 struct _Signature
47 {
48   gchar    *ploc;
49   Argument *rarg;
50   GList    *args;       /* of type Argument* */
51 };
52
53
54 /* --- prototypes --- */
55 static void     parse_args      (gint           *argc_p,
56                                  gchar       ***argv_p);
57 static void     print_blurb     (FILE           *bout,
58                                  gboolean        print_help);
59
60
61 /* --- variables --- */
62 static FILE          *fout = NULL;
63 static GScannerConfig scanner_config_template =
64 {
65   (
66    " \t\r"              /* "\n" is statement delimiter */
67    )                    /* cset_skip_characters */,
68   (
69    G_CSET_a_2_z
70    "_"
71    G_CSET_A_2_Z
72    )                    /* cset_identifier_first */,
73   (
74    G_CSET_a_2_z
75    "_0123456789"
76    G_CSET_A_2_Z
77    )                    /* cset_identifier_nth */,
78   ( "#\n" )             /* cpair_comment_single */,
79
80   FALSE                 /* case_sensitive */,
81
82   TRUE                  /* skip_comment_multi */,
83   TRUE                  /* skip_comment_single */,
84   TRUE                  /* scan_comment_multi */,
85   TRUE                  /* scan_identifier */,
86   FALSE                 /* scan_identifier_1char */,
87   FALSE                 /* scan_identifier_NULL */,
88   TRUE                  /* scan_symbols */,
89   FALSE                 /* scan_binary */,
90   TRUE                  /* scan_octal */,
91   TRUE                  /* scan_float */,
92   TRUE                  /* scan_hex */,
93   FALSE                 /* scan_hex_dollar */,
94   TRUE                  /* scan_string_sq */,
95   TRUE                  /* scan_string_dq */,
96   TRUE                  /* numbers_2_int */,
97   FALSE                 /* int_2_float */,
98   FALSE                 /* identifier_2_string */,
99   TRUE                  /* char_2_token */,
100   FALSE                 /* symbol_2_token */,
101   FALSE                 /* scope_0_fallback */,
102 };
103 static gchar            *marshaller_prefix = "g_cclosure_marshal";
104 static GHashTable       *marshallers = NULL;
105 static gboolean          gen_cheader = FALSE;
106 static gboolean          gen_cbody = FALSE;
107 static gboolean          skip_ploc = FALSE;
108
109
110 /* --- functions --- */
111 static gboolean
112 complete_arg (Argument *arg,
113               gboolean  is_return)
114 {
115   static const Argument inout_arguments[] = {
116     /* pname,           sname,          func,                   cname           */
117     { "VOID",           "VOID",         NULL,                   "void",         },
118     { "BOOLEAN",        "BOOLEAN",      "boolean",              "gboolean",     },
119     { "CHAR",           "CHAR",         "char",                 "gchar",        },
120     { "UCHAR",          "UCHAR",        "uchar",                "guchar",       },
121     { "INT",            "INT",          "int",                  "gint",         },
122     { "UINT",           "UINT",         "uint",                 "guint",        },
123     { "LONG",           "LONG",         "long",                 "glong",        },
124     { "ULONG",          "ULONG",        "ulong",                "gulong",       },
125     { "ENUM",           "ENUM",         "enum",                 "gint",         },
126     { "FLAGS",          "FLAGS",        "flags",                "guint",        },
127     { "FLOAT",          "FLOAT",        "float",                "gfloat",       },
128     { "DOUBLE",         "DOUBLE",       "double",               "gdouble",      },
129     /* deprecated: */
130     { "NONE",           "VOID",         NULL,                   "void",         },
131     { "BOOL",           "BOOLEAN",      "boolean",              "gboolean",     },
132   };
133   static const Argument in_arguments[] = {
134     { "STRING",         "POINTER",      "as_pointer",           "gpointer",     },
135     { "BOXED",          "POINTER",      "as_pointer",           "gpointer",     },
136     { "POINTER",        "POINTER",      "as_pointer",           "gpointer",     },
137     { "OBJECT",         "POINTER",      "as_pointer",           "gpointer",     },
138   };
139   static const Argument out_arguments[] = {
140     { "STRING",         "STRING",       "string",               "gchar*",       },
141     { "BOXED",          "BOXED",        "boxed",                "gpointer",     },
142     { "POINTER",        "POINTER",      "pointer",              "gpointer",     },
143     { "OBJECT",         "OBJECT",       "object",               "GObject*",     },
144   };
145   const guint n_inout_arguments = sizeof (inout_arguments) / sizeof (inout_arguments[0]);
146   const guint n_out_arguments = sizeof (out_arguments) / sizeof (out_arguments[0]);
147   const guint n_in_arguments = sizeof (in_arguments) / sizeof (in_arguments[0]);
148   const Argument *arguments;
149   guint i, n_arguments;
150
151   g_return_val_if_fail (arg != NULL, FALSE);
152
153   arguments = inout_arguments;
154   n_arguments = n_inout_arguments;
155   for (i = 0; i < n_arguments; i++)
156     if (strcmp (arguments[i].pname, arg->pname) == 0)
157       {
158         arg->sname = arguments[i].sname;
159         arg->func = arguments[i].func;
160         arg->cname = arguments[i].cname;
161
162         return TRUE;
163       }
164   arguments = is_return ? out_arguments : in_arguments;
165   n_arguments = is_return ? n_out_arguments : n_in_arguments;
166   for (i = 0; i < n_arguments; i++)
167     if (strcmp (arguments[i].pname, arg->pname) == 0)
168       {
169         arg->sname = arguments[i].sname;
170         arg->func = arguments[i].func;
171         arg->cname = arguments[i].cname;
172
173         return TRUE;
174       }
175
176   return FALSE;
177 }
178
179 static const gchar*
180 pad (const gchar *string)
181 {
182 #define PAD_LENGTH      12
183   static gchar *buffer = NULL;
184   gint i;
185
186   g_return_val_if_fail (string != NULL, NULL);
187
188   if (!buffer)
189     buffer = g_new (gchar, PAD_LENGTH + 1);
190
191   /* paranoid check */
192   if (strlen (string) >= PAD_LENGTH)
193     {
194       g_free (buffer);
195       buffer = g_strdup_printf ("%s ", string);
196       g_warning ("overfull string (%u bytes) for padspace", strlen (string));
197
198       return buffer;
199     }
200
201   for (i = 0; i < PAD_LENGTH; i++)
202     {
203       gboolean done = *string == 0;
204
205       buffer[i] = done ? ' ' : *string++;
206     }
207   buffer[i] = 0;
208
209   return buffer;
210 }
211
212 static const gchar*
213 indent (guint n_spaces)
214 {
215   static gchar *buffer;
216   static guint blength = 0;
217
218   if (blength <= n_spaces)
219     {
220       blength = n_spaces + 1;
221       g_free (buffer);
222       buffer = g_new (gchar, blength);
223     }
224   memset (buffer, ' ', n_spaces);
225   buffer[n_spaces] = 0;
226
227   return buffer;
228 }
229
230 static void
231 generate_marshal (const gchar *signame,
232                   Signature   *sig)
233 {
234   guint ind, a;
235   GList *node;
236
237   if (g_hash_table_lookup (marshallers, signame))
238     return;
239   else
240     {
241       gchar *tmp = g_strdup (signame);
242
243       g_hash_table_insert (marshallers, tmp, tmp);
244     }
245   
246   if (gen_cheader)
247     {
248       ind = fprintf (fout, "extern void ");
249       ind += fprintf (fout, "%s_%s (", marshaller_prefix, signame);
250       fprintf (fout,   "GClosure     *closure,\n");
251       fprintf (fout, "%sguint         invocation_hint,\n", indent (ind));
252       fprintf (fout, "%sGValue       *return_value,\n", indent (ind));
253       fprintf (fout, "%sguint         n_param_values,\n", indent (ind));
254       fprintf (fout, "%sconst GValue *param_values,\n", indent (ind));
255       fprintf (fout, "%sgpointer      marshal_data);\n", indent (ind));
256     }
257   if (gen_cbody)
258     {
259       /* cfile marhsal header */
260       fprintf (fout, "void\n");
261       ind = fprintf (fout, "%s_%s (", marshaller_prefix, signame);
262       fprintf (fout,   "GClosure     *closure,\n");
263       fprintf (fout, "%sguint         invocation_hint,\n", indent (ind));
264       fprintf (fout, "%sGValue       *return_value,\n", indent (ind));
265       fprintf (fout, "%sguint         n_param_values,\n", indent (ind));
266       fprintf (fout, "%sconst GValue *param_values,\n", indent (ind));
267       fprintf (fout, "%sgpointer      marshal_data)\n", indent (ind));
268       fprintf (fout, "{\n");
269
270       /* cfile GSignalFunc typedef */
271       ind = fprintf (fout, "  typedef %s (*GSignalFunc_%s) (", sig->rarg->cname, signame);
272       fprintf (fout, "%s data1,\n", pad ("gpointer"));
273       for (a = 1, node = sig->args; node; node = node->next)
274         {
275           Argument *arg = node->data;
276
277           if (arg->func)
278             fprintf (fout, "%s%s arg_%d,\n", indent (ind), pad (arg->cname), a++);
279         }
280       fprintf (fout, "%s%s data2);\n", indent (ind), pad ("gpointer"));
281
282       /* cfile marshal variables */
283       fprintf (fout, "  register GSignalFunc_%s callback;\n", signame);
284       fprintf (fout, "  register GCClosure *cc = (GCClosure*) closure;\n");
285       fprintf (fout, "  register gpointer data1, data2;\n");
286       if (sig->rarg->func)
287         fprintf (fout, "  %s v_return;\n", sig->rarg->cname);
288
289       if (sig->args || sig->rarg->func)
290         {
291           fprintf (fout, "\n");
292
293           if (sig->rarg->func)
294             fprintf (fout, "  g_return_if_fail (return_value != NULL);\n");
295           if (sig->args)
296             {
297               for (a = 0, node = sig->args; node; node = node->next)
298                 {
299                   Argument *arg = node->data;
300
301                   if (arg->func)
302                     a++;
303                 }
304               fprintf (fout, "  g_return_if_fail (n_param_values >= %u);\n", 1 + a);
305             }
306         }
307
308       /* cfile marshal data1, data2 and callback setup */
309       fprintf (fout, "\n");
310       fprintf (fout, "  if (G_CCLOSURE_SWAP_DATA (closure))\n    {\n");
311       fprintf (fout, "      data1 = closure->data;\n");
312       fprintf (fout, "      data2 = g_value_get_as_pointer (param_values + 0);\n");
313       fprintf (fout, "    }\n  else\n    {\n");
314       fprintf (fout, "      data1 = g_value_get_as_pointer (param_values + 0);\n");
315       fprintf (fout, "      data2 = closure->data;\n");
316       fprintf (fout, "    }\n");
317       fprintf (fout, "  callback = (GSignalFunc_%s) (marshal_data ? marshal_data : cc->callback);\n", signame);
318
319       /* cfile marshal callback action */
320       fprintf (fout, "\n");
321       ind = fprintf (fout, " %s callback (", sig->rarg->func ? " v_return =" : "");
322       fprintf (fout, "data1,\n");
323       for (a = 1, node = sig->args; node; node = node->next)
324         {
325           Argument *arg = node->data;
326
327           if (arg->func)
328             fprintf (fout, "%sg_value_get_%s (param_values + %d),\n", indent (ind), arg->func, a++);
329         }
330       fprintf (fout, "%sdata2);\n", indent (ind));
331
332       /* cfile marshal return value storage */
333       if (sig->rarg->func)
334         {
335           fprintf (fout, "\n");
336           fprintf (fout, "  g_value_set_%s (return_value, v_return);\n", sig->rarg->func);
337         }
338
339       /* cfile marshal footer */
340       fprintf (fout, "}\n");
341     }
342 }
343
344 static void
345 process_signature (Signature *sig)
346 {
347   gchar *pname, *sname;
348   GList *node;
349
350   /* lookup and complete info on arguments */
351   if (!complete_arg (sig->rarg, TRUE))
352     {
353       g_warning ("unknown type: %s", sig->rarg->pname);
354       return;
355     }
356   for (node = sig->args; node; node = node->next)
357     {
358       Argument *arg = node->data;
359
360       if (!complete_arg (arg, FALSE))
361         {
362           g_warning ("unknown type: %s", arg->pname);
363           return;
364         }
365     }
366
367   /* construct requested marshaller name and technical marshaller name */
368   pname = g_strconcat (sig->rarg->pname, "_", NULL);
369   sname = g_strconcat (sig->rarg->sname, "_", NULL);
370   for (node = sig->args; node; node = node->next)
371     {
372       Argument *arg = node->data;
373       gchar *tmp;
374
375       tmp = sname;
376       sname = g_strconcat (tmp, "_", arg->sname, NULL);
377       g_free (tmp);
378       tmp = pname;
379       pname = g_strconcat (tmp, "_", arg->pname, NULL);
380       g_free (tmp);
381     }
382
383   /* introductionary comment */
384   fprintf (fout, "\n/* %s", sig->rarg->pname);
385   for (node = sig->args; node; node = node->next)
386     {
387       Argument *arg = node->data;
388
389       fprintf (fout, "%c%s", node->prev ? ',' : ':', arg->pname);
390     }
391   if (!skip_ploc)
392     fprintf (fout, " (%s)", sig->ploc);
393   fprintf (fout, " */\n");
394
395   /* generate signature marshaller */
396   generate_marshal (sname, sig);
397
398   /* put out marshaler alias if required */
399   if (gen_cheader && !g_hash_table_lookup (marshallers, pname))
400     {
401       gchar *tmp = g_strdup (pname);
402
403       fprintf (fout, "#define %s_%s\t%s_%s\n", marshaller_prefix, pname, marshaller_prefix, sname);
404
405       g_hash_table_insert (marshallers, tmp, tmp);
406     }
407
408   g_free (pname);
409   g_free (sname);
410 }
411
412 static Argument*
413 new_arg (const gchar *pname)
414 {
415   Argument *arg = g_new0 (Argument, 1);
416
417   arg->pname = g_strdup (pname);
418
419   return arg;
420 }
421
422 static guint
423 parse_line (GScanner  *scanner,
424             Signature *sig)
425 {
426   /* parse identifier for return value */
427   if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
428     return G_TOKEN_IDENTIFIER;
429   sig->rarg = new_arg (scanner->value.v_identifier);
430
431   /* keep a note on the location */
432   sig->ploc = g_strdup_printf ("%s:%u", scanner->input_name, scanner->line);
433
434   /* expect ':' */
435   if (g_scanner_get_next_token (scanner) != ':')
436     return ':';
437
438   /* parse first argument */
439   if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
440     return G_TOKEN_IDENTIFIER;
441   sig->args = g_list_append (sig->args, new_arg (scanner->value.v_identifier));
442
443   /* parse rest of argument list */
444   while (g_scanner_peek_next_token (scanner) == ',')
445     {
446       /* eat comma */
447       g_scanner_get_next_token (scanner);
448
449       /* parse arg identifier */
450       if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
451         return G_TOKEN_IDENTIFIER;
452       sig->args = g_list_append (sig->args, new_arg (scanner->value.v_identifier));
453     }
454   
455   /* expect end of line, done */
456   if (g_scanner_get_next_token (scanner) != '\n')
457     return '\n';
458   
459   /* success */
460   return G_TOKEN_NONE;
461 }
462
463 static gboolean
464 string_key_destroy (gpointer key,
465                     gpointer value,
466                     gpointer user_data)
467 {
468   g_free (key);
469
470   return TRUE;
471 }
472
473 int
474 main (int   argc,
475       char *argv[])
476 {
477   GScanner *scanner;
478   GSList *slist, *files = NULL;
479   gint i;
480
481   /* parse args and do fast exits */
482   parse_args (&argc, &argv);
483
484   /* list input files */
485   for (i = 1; i < argc; i++)
486     files = g_slist_prepend (files, argv[i]);
487   if (files)
488     files = g_slist_reverse (files);
489   else
490     files = g_slist_prepend (files, "/dev/stdin");
491
492   /* setup auxillary structs */
493   scanner = g_scanner_new (&scanner_config_template);
494   fout = stdout;
495   marshallers = g_hash_table_new (g_str_hash, g_str_equal);
496
497   /* process input files */
498   for (slist = files; slist; slist = slist->next)
499     {
500       gchar *file = slist->data;
501       gint fd = open (file, O_RDONLY);
502
503       if (fd < 0)
504         {
505           g_warning ("failed to open \"%s\": %s", file, g_strerror (errno));
506           continue;
507         }
508
509       /* set file name for error reports */
510       scanner->input_name = file;
511
512       /* parse & process file */
513       g_scanner_input_file (scanner, fd);
514       
515       /* scanning loop, we parse the input untill it's end is reached,
516        * or our sub routine came across invalid syntax
517        */
518       do
519         {
520           guint expected_token = G_TOKEN_NONE;
521
522           switch (g_scanner_peek_next_token (scanner))
523             {
524             case '\n':
525               /* eat newline and restart */
526               g_scanner_get_next_token (scanner);
527               continue;
528             case G_TOKEN_EOF:
529               /* done */
530               break;
531             default:
532               /* parse and process signatures */
533               {
534                 Signature signature = { NULL, NULL, NULL };
535                 GList *node;
536
537                 expected_token = parse_line (scanner, &signature);
538                 
539                 /* once we got a valid signature, process it */
540                 if (expected_token == G_TOKEN_NONE)
541                   process_signature (&signature);
542                 
543                 /* clean up signature contents */
544                 g_free (signature.ploc);
545                 if (signature.rarg)
546                   g_free (signature.rarg->pname);
547                 g_free (signature.rarg);
548                 for (node = signature.args; node; node = node->next)
549                   {
550                     Argument *arg = node->data;
551                     
552                     g_free (arg->pname);
553                     g_free (arg);
554                   }
555                 g_list_free (signature.args);
556               }
557               break;
558             }
559
560           /* bail out on errors */
561           if (expected_token != G_TOKEN_NONE)
562             {
563               g_scanner_unexp_token (scanner, expected_token, "type name", NULL, NULL, NULL, TRUE);
564               break;
565             }
566
567           g_scanner_peek_next_token (scanner);
568         }
569       while (scanner->next_token != G_TOKEN_EOF);
570
571       close (fd);
572     }
573
574   /* clean up */
575   g_slist_free (files);
576   g_scanner_destroy (scanner);
577   g_hash_table_foreach_remove (marshallers, string_key_destroy, NULL);
578   g_hash_table_destroy (marshallers);
579
580   return 0;
581 }
582
583 static void
584 parse_args (gint    *argc_p,
585             gchar ***argv_p)
586 {
587   guint argc = *argc_p;
588   gchar **argv = *argv_p;
589   guint i, e;
590   
591   for (i = 1; i < argc; i++)
592     {
593       if (strcmp ("--header", argv[i]) == 0)
594         {
595           gen_cheader = TRUE;
596           argv[i] = NULL;
597         }
598       else if (strcmp ("--body", argv[i]) == 0)
599         {
600           gen_cbody = TRUE;
601           argv[i] = NULL;
602         }
603       else if (strcmp ("--skip-source", argv[i]) == 0)
604         {
605           skip_ploc = TRUE;
606           argv[i] = NULL;
607         }
608       else if ((strcmp ("--prefix", argv[i]) == 0) ||
609                (strncmp ("--prefix=", argv[i], 9) == 0))
610         {
611           gchar *equal = argv[i] + 8;
612
613           if (*equal == '=')
614             marshaller_prefix = g_strdup (equal + 1);
615           else if (i + 1 < argc)
616             {
617               marshaller_prefix = g_strdup (argv[i + 1]);
618               argv[i] = NULL;
619               i += 1;
620             }
621           argv[i] = NULL;
622         }
623       else if (strcmp ("-h", argv[i]) == 0 ||
624           strcmp ("--help", argv[i]) == 0)
625         {
626           print_blurb (stderr, TRUE);
627           argv[i] = NULL;
628           exit (0);
629         }
630       else if (strcmp ("-v", argv[i]) == 0 ||
631                strcmp ("--version", argv[i]) == 0)
632         {
633           print_blurb (stderr, FALSE);
634           argv[i] = NULL;
635           exit (0);
636         }
637       else if (strcmp (argv[i], "--g-fatal-warnings") == 0)
638         {
639           GLogLevelFlags fatal_mask;
640           
641           fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
642           fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
643           g_log_set_always_fatal (fatal_mask);
644           
645           argv[i] = NULL;
646         }
647     }
648   
649   e = 0;
650   for (i = 1; i < argc; i++)
651     {
652       if (e)
653         {
654           if (argv[i])
655             {
656               argv[e++] = argv[i];
657               argv[i] = NULL;
658             }
659         }
660       else if (!argv[i])
661         e = i;
662     }
663   if (e)
664     *argc_p = e;
665 }
666
667 static void
668 print_blurb (FILE    *bout,
669              gboolean print_help)
670 {
671   if (!print_help)
672     {
673       fprintf (bout, "%s version ", PRG_NAME);
674       fprintf (bout, "%u.%u.%u", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
675       fprintf (bout, "\n");
676       fprintf (bout, "%s comes with ABSOLUTELY NO WARRANTY.\n", PRG_NAME);
677       fprintf (bout, "You may redistribute copies of %s under the terms of\n", PRG_NAME);
678       fprintf (bout, "the GNU General Public License which can be found in the\n");
679       fprintf (bout, "%s source package. Sources, examples and contact\n", PKG_NAME);
680       fprintf (bout, "information are available at %s\n", PKG_HTTP_HOME);
681     }
682   else
683     {
684       fprintf (bout, "Usage: %s [options] [files...]\n", PRG_NAME);
685       fprintf (bout, "  --header                        generate C headers\n");
686       fprintf (bout, "  --body                          generate C code\n");
687       fprintf (bout, "  --prefix=string                 specify marshaller prefix\n");
688       fprintf (bout, "  --skip-source                   skip source location comments\n");
689       fprintf (bout, "  -h, --help                      show this help message\n");
690       fprintf (bout, "  -v, --version                   print version informations\n");
691       fprintf (bout, "  --g-fatal-warnings              make warnings fatal (abort)\n");
692     }
693 }