Optimize the common cases (init == NULL or init == "") a bit. replace uses
[platform/upstream/glib.git] / gobject / glib-genmarshal.c
1 /* GLIB-GenMarshal - Marshaller generator for GObject library
2  * Copyright (C) 2000-2001 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 #undef G_LOG_DOMAIN
22 #define G_LOG_DOMAIN "GLib-Genmarshal"
23 #include        <glib.h>
24
25 #include        <glib/gprintf.h>
26 #include        <stdlib.h>
27 #include        <fcntl.h>
28 #include        <string.h>
29 #include        <errno.h>
30 #ifdef HAVE_UNISTD_H
31 #include        <unistd.h>
32 #endif
33 #include        <sys/types.h>
34 #include        <sys/stat.h>
35
36 #ifdef G_OS_WIN32
37 #include <io.h>
38 #endif
39
40 /* --- defines --- */
41 #define PRG_NAME        "glib-genmarshal"
42 #define PKG_NAME        "GLib"
43 #define PKG_HTTP_HOME   "http://www.gtk.org"
44
45
46 /* --- typedefs & structures --- */
47 typedef struct
48 {
49   gchar       *keyword;         /* marhaller list keyword [MY_STRING] */
50   const gchar *sig_name;        /* signature name [STRING] */
51   const gchar *ctype;           /* C type name [gchar*] */
52   const gchar *getter;          /* value getter function [g_value_get_string] */
53 } InArgument;
54 typedef struct
55 {
56   gchar       *keyword;         /* marhaller list keyword [MY_STRING] */
57   const gchar *sig_name;        /* signature name [STRING] */
58   const gchar *ctype;           /* C type name [gchar*] */
59   const gchar *setter;          /* value setter function [g_value_set_string] */
60 } OutArgument;
61 typedef struct
62 {
63   gchar       *ploc;
64   OutArgument *rarg;
65   GList       *args;    /* of type InArgument* */
66 } Signature;
67
68
69 /* --- prototypes --- */
70 static void     parse_args      (gint           *argc_p,
71                                  gchar       ***argv_p);
72 static void     print_blurb     (FILE           *bout,
73                                  gboolean        print_help);
74
75
76 /* --- variables --- */
77 static FILE          *fout = NULL;
78 static GScannerConfig scanner_config_template =
79 {
80   (
81    " \t\r"              /* "\n" is statement delimiter */
82    )                    /* cset_skip_characters */,
83   (
84    G_CSET_a_2_z
85    "_"
86    G_CSET_A_2_Z
87    )                    /* cset_identifier_first */,
88   (
89    G_CSET_a_2_z
90    "_0123456789"
91    G_CSET_A_2_Z
92    )                    /* cset_identifier_nth */,
93   ( "#\n" )             /* cpair_comment_single */,
94
95   FALSE                 /* case_sensitive */,
96
97   TRUE                  /* skip_comment_multi */,
98   TRUE                  /* skip_comment_single */,
99   TRUE                  /* scan_comment_multi */,
100   TRUE                  /* scan_identifier */,
101   FALSE                 /* scan_identifier_1char */,
102   FALSE                 /* scan_identifier_NULL */,
103   TRUE                  /* scan_symbols */,
104   FALSE                 /* scan_binary */,
105   TRUE                  /* scan_octal */,
106   TRUE                  /* scan_float */,
107   TRUE                  /* scan_hex */,
108   FALSE                 /* scan_hex_dollar */,
109   TRUE                  /* scan_string_sq */,
110   TRUE                  /* scan_string_dq */,
111   TRUE                  /* numbers_2_int */,
112   FALSE                 /* int_2_float */,
113   FALSE                 /* identifier_2_string */,
114   TRUE                  /* char_2_token */,
115   FALSE                 /* symbol_2_token */,
116   FALSE                 /* scope_0_fallback */,
117 };
118 static gchar            *std_marshaller_prefix = "g_cclosure_marshal";
119 static gchar            *marshaller_prefix = "g_cclosure_user_marshal";
120 static GHashTable       *marshallers = NULL;
121 static gboolean          gen_cheader = FALSE;
122 static gboolean          gen_cbody = FALSE;
123 static gboolean          skip_ploc = FALSE;
124 static gboolean          std_includes = TRUE;
125
126
127 /* --- functions --- */
128 static void
129 put_marshal_value_getters (void)
130 {
131   fputs ("\n", fout);
132   fputs ("#ifdef G_ENABLE_DEBUG\n", fout);
133   fputs ("#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)\n", fout);
134   fputs ("#define g_marshal_value_peek_char(v)     g_value_get_char (v)\n", fout);
135   fputs ("#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)\n", fout);
136   fputs ("#define g_marshal_value_peek_int(v)      g_value_get_int (v)\n", fout);
137   fputs ("#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)\n", fout);
138   fputs ("#define g_marshal_value_peek_long(v)     g_value_get_long (v)\n", fout);
139   fputs ("#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)\n", fout);
140   fputs ("#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)\n", fout);
141   fputs ("#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)\n", fout);
142   fputs ("#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)\n", fout);
143   fputs ("#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)\n", fout);
144   fputs ("#define g_marshal_value_peek_float(v)    g_value_get_float (v)\n", fout);
145   fputs ("#define g_marshal_value_peek_double(v)   g_value_get_double (v)\n", fout);
146   fputs ("#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)\n", fout);
147   fputs ("#define g_marshal_value_peek_param(v)    g_value_get_param (v)\n", fout);
148   fputs ("#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)\n", fout);
149   fputs ("#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)\n", fout);
150   fputs ("#define g_marshal_value_peek_object(v)   g_value_get_object (v)\n", fout);
151   fputs ("#else /* !G_ENABLE_DEBUG */\n", fout);
152   fputs ("/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.\n", fout);
153   fputs (" *          Do not access GValues directly in your code. Instead, use the\n", fout);
154   fputs (" *          g_value_get_*() functions\n", fout);
155   fputs (" */\n", fout);
156   fputs ("#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int\n", fout);
157   fputs ("#define g_marshal_value_peek_char(v)     (v)->data[0].v_int\n", fout);
158   fputs ("#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint\n", fout);
159   fputs ("#define g_marshal_value_peek_int(v)      (v)->data[0].v_int\n", fout);
160   fputs ("#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint\n", fout);
161   fputs ("#define g_marshal_value_peek_long(v)     (v)->data[0].v_long\n", fout);
162   fputs ("#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong\n", fout);
163   fputs ("#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64\n", fout);
164   fputs ("#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64\n", fout);
165   fputs ("#define g_marshal_value_peek_enum(v)     (v)->data[0].v_int\n", fout);
166   fputs ("#define g_marshal_value_peek_flags(v)    (v)->data[0].v_uint\n", fout);
167   fputs ("#define g_marshal_value_peek_float(v)    (v)->data[0].v_float\n", fout);
168   fputs ("#define g_marshal_value_peek_double(v)   (v)->data[0].v_double\n", fout);
169   fputs ("#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer\n", fout);
170   fputs ("#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer\n", fout);
171   fputs ("#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer\n", fout);
172   fputs ("#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer\n", fout);
173   fputs ("#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer\n", fout);
174   fputs ("#endif /* !G_ENABLE_DEBUG */\n", fout);
175   fputs ("\n", fout);
176 }
177
178 static gboolean
179 complete_in_arg (InArgument *iarg)
180 {
181   static const InArgument args[] = {
182     /* keyword          sig_name        ctype           getter                  */
183     { "VOID",           "VOID",         "void",         NULL,                   },
184     { "BOOLEAN",        "BOOLEAN",      "gboolean",     "g_marshal_value_peek_boolean", },
185     { "CHAR",           "CHAR",         "gchar",        "g_marshal_value_peek_char",    },
186     { "UCHAR",          "UCHAR",        "guchar",       "g_marshal_value_peek_uchar",   },
187     { "INT",            "INT",          "gint",         "g_marshal_value_peek_int",     },
188     { "UINT",           "UINT",         "guint",        "g_marshal_value_peek_uint",    },
189     { "LONG",           "LONG",         "glong",        "g_marshal_value_peek_long",    },
190     { "ULONG",          "ULONG",        "gulong",       "g_marshal_value_peek_ulong",   },
191     { "INT64",          "INT64",        "gint64",       "g_marshal_value_peek_int64",   },
192     { "UINT64",         "UINT64",       "guint64",      "g_marshal_value_peek_uint64",  },
193     { "ENUM",           "ENUM",         "gint",         "g_marshal_value_peek_enum",    },
194     { "FLAGS",          "FLAGS",        "guint",        "g_marshal_value_peek_flags",   },
195     { "FLOAT",          "FLOAT",        "gfloat",       "g_marshal_value_peek_float",   },
196     { "DOUBLE",         "DOUBLE",       "gdouble",      "g_marshal_value_peek_double",  },
197     { "STRING",         "STRING",       "gpointer",     "g_marshal_value_peek_string",  },
198     { "PARAM",          "PARAM",        "gpointer",     "g_marshal_value_peek_param",   },
199     { "BOXED",          "BOXED",        "gpointer",     "g_marshal_value_peek_boxed",   },
200     { "POINTER",        "POINTER",      "gpointer",     "g_marshal_value_peek_pointer", },
201     { "OBJECT",         "OBJECT",       "gpointer",     "g_marshal_value_peek_object",  },
202     /* deprecated: */
203     { "NONE",           "VOID",         "void",         NULL,                   },
204     { "BOOL",           "BOOLEAN",      "gboolean",     "g_marshal_value_peek_boolean", },
205   };
206   const guint n_args = sizeof (args) / sizeof (args[0]);
207   guint i;
208
209   g_return_val_if_fail (iarg != NULL, FALSE);
210
211   for (i = 0; i < n_args; i++)
212     if (strcmp (args[i].keyword, iarg->keyword) == 0)
213       {
214         iarg->sig_name = args[i].sig_name;
215         iarg->ctype = args[i].ctype;
216         iarg->getter = args[i].getter;
217
218         return TRUE;
219       }
220   return FALSE;
221 }
222
223 static gboolean
224 complete_out_arg (OutArgument *oarg)
225 {
226   static const OutArgument args[] = {
227     /* keyword          sig_name        ctype           setter                  */
228     { "VOID",           "VOID",         "void",         NULL,                                        },
229     { "BOOLEAN",        "BOOLEAN",      "gboolean",     "g_value_set_boolean",                       },
230     { "CHAR",           "CHAR",         "gchar",        "g_value_set_char",                          },
231     { "UCHAR",          "UCHAR",        "guchar",       "g_value_set_uchar",                         },
232     { "INT",            "INT",          "gint",         "g_value_set_int",                           },
233     { "UINT",           "UINT",         "guint",        "g_value_set_uint",                          },
234     { "LONG",           "LONG",         "glong",        "g_value_set_long",                          },
235     { "ULONG",          "ULONG",        "gulong",       "g_value_set_ulong",                         },
236     { "INT64",          "INT64",        "gint64",       "g_value_set_int64",                         },
237     { "UINT64",         "UINT64",       "guint64",      "g_value_set_uint64",                        },
238     { "ENUM",           "ENUM",         "gint",         "g_value_set_enum",                          },
239     { "FLAGS",          "FLAGS",        "guint",        "g_value_set_flags",                         },
240     { "FLOAT",          "FLOAT",        "gfloat",       "g_value_set_float",                         },
241     { "DOUBLE",         "DOUBLE",       "gdouble",      "g_value_set_double",                        },
242     { "STRING",         "STRING",       "gchar*",       "g_value_set_string_take_ownership",         },
243     { "PARAM",          "PARAM",        "GParamSpec*",  "g_value_set_param_take_ownership",          },
244     { "BOXED",          "BOXED",        "gpointer",     "g_value_set_boxed_take_ownership",          },
245     { "POINTER",        "POINTER",      "gpointer",     "g_value_set_pointer",                       },
246     { "OBJECT",         "OBJECT",       "GObject*",     "g_value_set_object_take_ownership",         },
247     /* deprecated: */
248     { "NONE",           "VOID",         "void",         NULL,                                        },
249     { "BOOL",           "BOOLEAN",      "gboolean",     "g_value_set_boolean",                       },
250   };
251   const guint n_args = sizeof (args) / sizeof (args[0]);
252   guint i;
253
254   g_return_val_if_fail (oarg != NULL, FALSE);
255
256   for (i = 0; i < n_args; i++)
257     if (strcmp (args[i].keyword, oarg->keyword) == 0)
258       {
259         oarg->sig_name = args[i].sig_name;
260         oarg->ctype = args[i].ctype;
261         oarg->setter = args[i].setter;
262
263         return TRUE;
264       }
265   return FALSE;
266 }
267
268 static const gchar*
269 pad (const gchar *string)
270 {
271 #define PAD_LENGTH      12
272   static gchar *buffer = NULL;
273   gint i;
274
275   g_return_val_if_fail (string != NULL, NULL);
276
277   if (!buffer)
278     buffer = g_new (gchar, PAD_LENGTH + 1);
279
280   /* paranoid check */
281   if (strlen (string) >= PAD_LENGTH)
282     {
283       g_free (buffer);
284       buffer = g_strdup_printf ("%s ", string);
285       g_warning ("overfull string (%u bytes) for padspace", (guint) strlen (string));
286
287       return buffer;
288     }
289
290   for (i = 0; i < PAD_LENGTH; i++)
291     {
292       gboolean done = *string == 0;
293
294       buffer[i] = done ? ' ' : *string++;
295     }
296   buffer[i] = 0;
297
298   return buffer;
299 }
300
301 static const gchar*
302 indent (guint n_spaces)
303 {
304   static gchar *buffer = NULL;
305   static guint blength = 0;
306
307   if (blength <= n_spaces)
308     {
309       blength = n_spaces + 1;
310       g_free (buffer);
311       buffer = g_new (gchar, blength);
312     }
313   memset (buffer, ' ', n_spaces);
314   buffer[n_spaces] = 0;
315
316   return buffer;
317 }
318
319 static void
320 generate_marshal (const gchar *signame,
321                   Signature   *sig)
322 {
323   guint ind, a;
324   GList *node;
325   gchar *tmp = g_strconcat (marshaller_prefix, "_", signame, NULL);
326   gboolean have_std_marshaller = FALSE;
327
328   /* here we have to make sure a marshaller named <marshaller_prefix>_<signame>
329    * exists. we might have put it out already, can revert to a standard marshaller
330    * provided by glib, or need to generate one.
331    */
332
333   if (g_hash_table_lookup (marshallers, tmp))
334     {
335       /* done, marshaller already generated */
336       g_free (tmp);
337       return;
338     }
339   else
340     {
341       /* need to alias/generate marshaller, register name */
342       g_hash_table_insert (marshallers, tmp, tmp);
343     }
344
345   /* can we revert to a standard marshaller? */
346   if (std_includes)
347     {
348       tmp = g_strconcat (std_marshaller_prefix, "_", signame, NULL);
349       have_std_marshaller = g_hash_table_lookup (marshallers, tmp) != NULL;
350       g_free (tmp);
351     }
352
353   if (gen_cheader && have_std_marshaller)
354     {
355       g_fprintf (fout, "#define %s_%s\t%s_%s\n", marshaller_prefix, signame, std_marshaller_prefix, signame);
356     }
357   if (gen_cheader && !have_std_marshaller)
358     {
359       ind = g_fprintf (fout, "extern void ");
360       ind += g_fprintf (fout, "%s_%s (", marshaller_prefix, signame);
361       g_fprintf (fout,   "GClosure     *closure,\n");
362       g_fprintf (fout, "%sGValue       *return_value,\n", indent (ind));
363       g_fprintf (fout, "%sguint         n_param_values,\n", indent (ind));
364       g_fprintf (fout, "%sconst GValue *param_values,\n", indent (ind));
365       g_fprintf (fout, "%sgpointer      invocation_hint,\n", indent (ind));
366       g_fprintf (fout, "%sgpointer      marshal_data);\n", indent (ind));
367     }
368   if (gen_cbody && !have_std_marshaller)
369     {
370       /* cfile marhsal header */
371       g_fprintf (fout, "void\n");
372       ind = g_fprintf (fout, "%s_%s (", marshaller_prefix, signame);
373       g_fprintf (fout,   "GClosure     *closure,\n");
374       g_fprintf (fout, "%sGValue       *return_value,\n", indent (ind));
375       g_fprintf (fout, "%sguint         n_param_values,\n", indent (ind));
376       g_fprintf (fout, "%sconst GValue *param_values,\n", indent (ind));
377       g_fprintf (fout, "%sgpointer      invocation_hint,\n", indent (ind));
378       g_fprintf (fout, "%sgpointer      marshal_data)\n", indent (ind));
379       g_fprintf (fout, "{\n");
380
381       /* cfile GMarshalFunc typedef */
382       ind = g_fprintf (fout, "  typedef %s (*GMarshalFunc_%s) (", sig->rarg->ctype, signame);
383       g_fprintf (fout, "%s data1,\n", pad ("gpointer"));
384       for (a = 1, node = sig->args; node; node = node->next)
385         {
386           InArgument *iarg = node->data;
387
388           if (iarg->getter)
389             g_fprintf (fout, "%s%s arg_%d,\n", indent (ind), pad (iarg->ctype), a++);
390         }
391       g_fprintf (fout, "%s%s data2);\n", indent (ind), pad ("gpointer"));
392
393       /* cfile marshal variables */
394       g_fprintf (fout, "  register GMarshalFunc_%s callback;\n", signame);
395       g_fprintf (fout, "  register GCClosure *cc = (GCClosure*) closure;\n");
396       g_fprintf (fout, "  register gpointer data1, data2;\n");
397       if (sig->rarg->setter)
398         g_fprintf (fout, "  %s v_return;\n", sig->rarg->ctype);
399
400       if (sig->args || sig->rarg->setter)
401         {
402           g_fprintf (fout, "\n");
403
404           if (sig->rarg->setter)
405             g_fprintf (fout, "  g_return_if_fail (return_value != NULL);\n");
406           if (sig->args)
407             {
408               for (a = 0, node = sig->args; node; node = node->next)
409                 {
410                   InArgument *iarg = node->data;
411
412                   if (iarg->getter)
413                     a++;
414                 }
415               g_fprintf (fout, "  g_return_if_fail (n_param_values == %u);\n", 1 + a);
416             }
417         }
418
419       /* cfile marshal data1, data2 and callback setup */
420       g_fprintf (fout, "\n");
421       g_fprintf (fout, "  if (G_CCLOSURE_SWAP_DATA (closure))\n    {\n");
422       g_fprintf (fout, "      data1 = closure->data;\n");
423       g_fprintf (fout, "      data2 = g_value_peek_pointer (param_values + 0);\n");
424       g_fprintf (fout, "    }\n  else\n    {\n");
425       g_fprintf (fout, "      data1 = g_value_peek_pointer (param_values + 0);\n");
426       g_fprintf (fout, "      data2 = closure->data;\n");
427       g_fprintf (fout, "    }\n");
428       g_fprintf (fout, "  callback = (GMarshalFunc_%s) (marshal_data ? marshal_data : cc->callback);\n", signame);
429
430       /* cfile marshal callback action */
431       g_fprintf (fout, "\n");
432       ind = g_fprintf (fout, " %s callback (", sig->rarg->setter ? " v_return =" : "");
433       g_fprintf (fout, "data1,\n");
434       for (a = 1, node = sig->args; node; node = node->next)
435         {
436           InArgument *iarg = node->data;
437
438           if (iarg->getter)
439             g_fprintf (fout, "%s%s (param_values + %d),\n", indent (ind), iarg->getter, a++);
440         }
441       g_fprintf (fout, "%sdata2);\n", indent (ind));
442
443       /* cfile marshal return value storage */
444       if (sig->rarg->setter)
445         {
446           g_fprintf (fout, "\n");
447           g_fprintf (fout, "  %s (return_value, v_return);\n", sig->rarg->setter);
448         }
449
450       /* cfile marshal footer */
451       g_fprintf (fout, "}\n");
452     }
453 }
454
455 static void
456 process_signature (Signature *sig)
457 {
458   gchar *pname, *sname, *tmp;
459   GList *node;
460
461   /* lookup and complete info on arguments */
462   if (!complete_out_arg (sig->rarg))
463     {
464       g_warning ("unknown type: %s", sig->rarg->keyword);
465       return;
466     }
467   for (node = sig->args; node; node = node->next)
468     {
469       InArgument *iarg = node->data;
470
471       if (!complete_in_arg (iarg))
472         {
473           g_warning ("unknown type: %s", iarg->keyword);
474           return;
475         }
476     }
477
478   /* construct requested marshaller name and technical marshaller name */
479   pname = g_strconcat (sig->rarg->keyword, "_", NULL);
480   sname = g_strconcat (sig->rarg->sig_name, "_", NULL);
481   for (node = sig->args; node; node = node->next)
482     {
483       InArgument *iarg = node->data;
484       gchar *tmp;
485
486       tmp = sname;
487       sname = g_strconcat (tmp, "_", iarg->sig_name, NULL);
488       g_free (tmp);
489       tmp = pname;
490       pname = g_strconcat (tmp, "_", iarg->keyword, NULL);
491       g_free (tmp);
492     }
493
494   /* introductionary comment */
495   g_fprintf (fout, "\n/* %s", sig->rarg->keyword);
496   for (node = sig->args; node; node = node->next)
497     {
498       InArgument *iarg = node->data;
499
500       g_fprintf (fout, "%c%s", node->prev ? ',' : ':', iarg->keyword);
501     }
502   if (!skip_ploc)
503     g_fprintf (fout, " (%s)", sig->ploc);
504   g_fprintf (fout, " */\n");
505
506   /* ensure technical marshaller exists (<marshaller_prefix>_<sname>) */
507   generate_marshal (sname, sig);
508
509   /* put out marshaller alias for requested name if required (<marshaller_prefix>_<pname>) */
510   tmp = g_strconcat (marshaller_prefix, "_", pname, NULL);
511   if (gen_cheader && !g_hash_table_lookup (marshallers, tmp))
512     {
513       g_fprintf (fout, "#define %s_%s\t%s_%s\n", marshaller_prefix, pname, marshaller_prefix, sname);
514
515       g_hash_table_insert (marshallers, tmp, tmp);
516     }
517   else
518     g_free (tmp);
519
520   g_free (pname);
521   g_free (sname);
522 }
523
524 static InArgument*
525 new_in_arg (const gchar *pname)
526 {
527   InArgument *iarg = g_new0 (InArgument, 1);
528
529   iarg->keyword = g_strdup (pname);
530
531   return iarg;
532 }
533
534 static OutArgument*
535 new_out_arg (const gchar *pname)
536 {
537   OutArgument *oarg = g_new0 (OutArgument, 1);
538
539   oarg->keyword = g_strdup (pname);
540
541   return oarg;
542 }
543
544 static guint
545 parse_line (GScanner  *scanner,
546             Signature *sig)
547 {
548   /* parse identifier for return value */
549   if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
550     return G_TOKEN_IDENTIFIER;
551   sig->rarg = new_out_arg (scanner->value.v_identifier);
552
553   /* keep a note on the location */
554   sig->ploc = g_strdup_printf ("%s:%u", scanner->input_name, scanner->line);
555
556   /* expect ':' */
557   if (g_scanner_get_next_token (scanner) != ':')
558     return ':';
559
560   /* parse first argument */
561   if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
562     return G_TOKEN_IDENTIFIER;
563   sig->args = g_list_append (sig->args, new_in_arg (scanner->value.v_identifier));
564
565   /* parse rest of argument list */
566   while (g_scanner_peek_next_token (scanner) == ',')
567     {
568       /* eat comma */
569       g_scanner_get_next_token (scanner);
570
571       /* parse arg identifier */
572       if (g_scanner_get_next_token (scanner) != G_TOKEN_IDENTIFIER)
573         return G_TOKEN_IDENTIFIER;
574       sig->args = g_list_append (sig->args, new_in_arg (scanner->value.v_identifier));
575     }
576   
577   /* expect end of line, done */
578   if (g_scanner_get_next_token (scanner) != '\n')
579     return '\n';
580   
581   /* success */
582   return G_TOKEN_NONE;
583 }
584
585 static gboolean
586 string_key_destroy (gpointer key,
587                     gpointer value,
588                     gpointer user_data)
589 {
590   g_free (key);
591
592   return TRUE;
593 }
594
595 int
596 main (int   argc,
597       char *argv[])
598 {
599   const gchar *gobject_marshallers[] = {
600 #include        "gmarshal.strings"
601   };
602   GScanner *scanner;
603   GSList *slist, *files = NULL;
604   gint i;
605   gint result = 0;
606
607   /* parse args and do fast exits */
608   parse_args (&argc, &argv);
609
610   /* list input files */
611   for (i = 1; i < argc; i++)
612     files = g_slist_prepend (files, argv[i]);
613   if (files)
614     files = g_slist_reverse (files);
615   else
616     files = g_slist_prepend (files, "/dev/stdin");
617
618   /* setup auxillary structs */
619   scanner = g_scanner_new (&scanner_config_template);
620   fout = stdout;
621   marshallers = g_hash_table_new (g_str_hash, g_str_equal);
622
623   /* add standard marshallers of the GObject library */
624   if (std_includes)
625     for (i = 0; i < sizeof (gobject_marshallers) / sizeof (gobject_marshallers[0]); i++)
626       {
627         gchar *tmp = g_strdup (gobject_marshallers[i]);
628         
629         g_hash_table_insert (marshallers, tmp, tmp);
630       }
631
632   /* put out initial heading */
633   g_fprintf (fout, "\n");
634
635   if (gen_cheader && std_includes)
636     {
637       g_fprintf (fout, "#ifndef __%s_MARSHAL_H__\n", marshaller_prefix);
638       g_fprintf (fout, "#define __%s_MARSHAL_H__\n\n", marshaller_prefix);
639     }
640
641   if ((gen_cheader || gen_cbody) && std_includes)
642     g_fprintf (fout, "#include\t<glib-object.h>\n\n");
643
644   if (gen_cheader)
645     g_fprintf (fout, "G_BEGIN_DECLS\n");
646
647   /* generate necessary preprocessor directives */
648   if (gen_cbody)
649     put_marshal_value_getters ();
650
651   /* process input files */
652   for (slist = files; slist; slist = slist->next)
653     {
654       gchar *file = slist->data;
655       gint fd = open (file, O_RDONLY);
656
657       if (fd < 0)
658         {
659           g_warning ("failed to open \"%s\": %s", file, g_strerror (errno));
660           result = 1;
661           continue;
662         }
663
664       /* set file name for error reports */
665       scanner->input_name = file;
666
667       /* parse & process file */
668       g_scanner_input_file (scanner, fd);
669       
670       /* scanning loop, we parse the input untill it's end is reached,
671        * or our sub routine came across invalid syntax
672        */
673       do
674         {
675           guint expected_token = G_TOKEN_NONE;
676
677           switch (g_scanner_peek_next_token (scanner))
678             {
679             case '\n':
680               /* eat newline and restart */
681               g_scanner_get_next_token (scanner);
682               continue;
683             case G_TOKEN_EOF:
684               /* done */
685               break;
686             default:
687               /* parse and process signatures */
688               {
689                 Signature signature = { NULL, NULL, NULL };
690                 GList *node;
691
692                 expected_token = parse_line (scanner, &signature);
693                 
694                 /* once we got a valid signature, process it */
695                 if (expected_token == G_TOKEN_NONE)
696                   process_signature (&signature);
697                 
698                 /* clean up signature contents */
699                 g_free (signature.ploc);
700                 if (signature.rarg)
701                   g_free (signature.rarg->keyword);
702                 g_free (signature.rarg);
703                 for (node = signature.args; node; node = node->next)
704                   {
705                     InArgument *iarg = node->data;
706                     
707                     g_free (iarg->keyword);
708                     g_free (iarg);
709                   }
710                 g_list_free (signature.args);
711               }
712               break;
713             }
714
715           /* bail out on errors */
716           if (expected_token != G_TOKEN_NONE)
717             {
718               g_scanner_unexp_token (scanner, expected_token, "type name", NULL, NULL, NULL, TRUE);
719               result = 1;
720               break;
721             }
722
723           g_scanner_peek_next_token (scanner);
724         }
725       while (scanner->next_token != G_TOKEN_EOF);
726
727       close (fd);
728     }
729
730   /* put out trailer */
731   if (gen_cheader)
732     {
733       g_fprintf (fout, "\nG_END_DECLS\n");
734
735       if (std_includes)
736         g_fprintf (fout, "\n#endif /* __%s_MARSHAL_H__ */\n", marshaller_prefix);
737     }
738   g_fprintf (fout, "\n");
739
740   /* clean up */
741   g_slist_free (files);
742   g_scanner_destroy (scanner);
743   g_hash_table_foreach_remove (marshallers, string_key_destroy, NULL);
744   g_hash_table_destroy (marshallers);
745
746   return result;
747 }
748
749 static void
750 parse_args (gint    *argc_p,
751             gchar ***argv_p)
752 {
753   guint argc = *argc_p;
754   gchar **argv = *argv_p;
755   guint i, e;
756   
757   for (i = 1; i < argc; i++)
758     {
759       if (strcmp ("--header", argv[i]) == 0)
760         {
761           gen_cheader = TRUE;
762           argv[i] = NULL;
763         }
764       else if (strcmp ("--body", argv[i]) == 0)
765         {
766           gen_cbody = TRUE;
767           argv[i] = NULL;
768         }
769       else if (strcmp ("--skip-source", argv[i]) == 0)
770         {
771           skip_ploc = TRUE;
772           argv[i] = NULL;
773         }
774       else if (strcmp ("--nostdinc", argv[i]) == 0)
775         {
776           std_includes = FALSE;
777           argv[i] = NULL;
778         }
779       else if (strcmp ("--stdinc", argv[i]) == 0)
780         {
781           std_includes = TRUE;
782           argv[i] = NULL;
783         }
784       else if ((strcmp ("--prefix", argv[i]) == 0) ||
785                (strncmp ("--prefix=", argv[i], 9) == 0))
786         {
787           gchar *equal = argv[i] + 8;
788
789           if (*equal == '=')
790             marshaller_prefix = g_strdup (equal + 1);
791           else if (i + 1 < argc)
792             {
793               marshaller_prefix = g_strdup (argv[i + 1]);
794               argv[i] = NULL;
795               i += 1;
796             }
797           argv[i] = NULL;
798         }
799       else if (strcmp ("-h", argv[i]) == 0 ||
800           strcmp ("--help", argv[i]) == 0)
801         {
802           print_blurb (stderr, TRUE);
803           argv[i] = NULL;
804           exit (0);
805         }
806       else if (strcmp ("-v", argv[i]) == 0 ||
807                strcmp ("--version", argv[i]) == 0)
808         {
809           print_blurb (stderr, FALSE);
810           argv[i] = NULL;
811           exit (0);
812         }
813       else if (strcmp (argv[i], "--g-fatal-warnings") == 0)
814         {
815           GLogLevelFlags fatal_mask;
816           
817           fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
818           fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
819           g_log_set_always_fatal (fatal_mask);
820           
821           argv[i] = NULL;
822         }
823     }
824   
825   e = 0;
826   for (i = 1; i < argc; i++)
827     {
828       if (e)
829         {
830           if (argv[i])
831             {
832               argv[e++] = argv[i];
833               argv[i] = NULL;
834             }
835         }
836       else if (!argv[i])
837         e = i;
838     }
839   if (e)
840     *argc_p = e;
841 }
842
843 static void
844 print_blurb (FILE    *bout,
845              gboolean print_help)
846 {
847   if (!print_help)
848     {
849       g_fprintf (bout, "%s version ", PRG_NAME);
850       g_fprintf (bout, "%u.%u.%u", GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION, GLIB_MICRO_VERSION);
851       g_fprintf (bout, "\n");
852       g_fprintf (bout, "%s comes with ABSOLUTELY NO WARRANTY.\n", PRG_NAME);
853       g_fprintf (bout, "You may redistribute copies of %s under the terms of\n", PRG_NAME);
854       g_fprintf (bout, "the GNU General Public License which can be found in the\n");
855       g_fprintf (bout, "%s source package. Sources, examples and contact\n", PKG_NAME);
856       g_fprintf (bout, "information are available at %s\n", PKG_HTTP_HOME);
857     }
858   else
859     {
860       g_fprintf (bout, "Usage: %s [options] [files...]\n", PRG_NAME);
861       g_fprintf (bout, "  --header                   generate C headers\n");
862       g_fprintf (bout, "  --body                     generate C code\n");
863       g_fprintf (bout, "  --prefix=string            specify marshaller prefix\n");
864       g_fprintf (bout, "  --skip-source              skip source location comments\n");
865       g_fprintf (bout, "  --stdinc, --nostdinc       include/use standard marshallers\n");
866       g_fprintf (bout, "  -h, --help                 show this help message\n");
867       g_fprintf (bout, "  -v, --version              print version informations\n");
868       g_fprintf (bout, "  --g-fatal-warnings         make warnings fatal (abort)\n");
869     }
870 }