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