die() {
echo "Usage: $0 [--header|--code] [spec] [output]" 1>&2
echo " [--header|--code] whether to output the header (.h) or code (.c)" 1>&2
echo " [spec] spec keyfile" 1>&2
echo " [output] output file" 1>&2
echo "" 1>&2
echo " The spec file should contain these keys:" 1>&2
echo " [prefix] lower case prefix, EX: my" 1>&2
echo " [name] lower case class name, EX: class" 1>&2
echo " [parent-prefix] prefix of parent class, EX: g" 1>&2
echo " [parent-name] name of parent class, EX: object" 1>&2
echo " [author] author name, EX: <your name>" 1>&2
echo " [include] #include for the header file (can have multiple)" 1>&2
echo " EX: <gtk/gtk.h>" 1>&2
echo " EX: \"my-other-class.h\"" 1>&2
echo " [enum] declare an enum type (can have multiple)" 1>&2
echo " EX: my fruit_type apple orange pear" 1>&2
echo " [ensure] ensure another type (can have multiple), EX: GTK_TYPE_WIDGET" 1>&2
echo " [property] property spec (can have multiple), EX: (see below)" 1>&2
echo "" 1>&2
echo " The properties are parsed with the form:" 1>&2
echo " [name] [ctype] [gtype] [spec-type] [free (optional)] [ref (optional)]" 1>&2
echo " EX: my_widget GtkWidget GTK_TYPE_WIDGET object" 1>&2
echo " EX: my_string char G_TYPE_STRING string" 1>&2
echo " EX: my_int int G_TYPE_INT int" 1>&2
echo " EX: my_ptr_array GPtrArray G_TYPE_PTR_ARRAY boxed g_ptr_array_unref g_ptr_array_ref" 1>&2
echo "" 1>&2
echo "$@, aborting!" 1>&2
exit 1
}
if [ "$#" -ne 3 ]; then
die wrong number of args
fi
OUTPUT_TYPE="$1"
SPEC_FILE="$2"
OUTPUT_FILE="$3"
if [ -z "$OUTPUT_TYPE" ] ||
[ -z "$SPEC_FILE" ] ||
[ -z "$OUTPUT_FILE" ]; then
die one or more args are empty
fi
case "$OUTPUT_TYPE" in
--header|--code) ;;
*) die arg 1 must be '--header' or '--code' ;;
esac
if ! [ -f "$SPEC_FILE" ]; then
die "$SPEC_FILE isn't a file"
fi
unset PREF
unset NAME
unset PAR_PREF
unset PAR_NAME
unset AUTHOR
unset INCLUDES
unset ENSURES
unset ENUMS
unset PROPS
while IFS= read -r line; do
[ -z "$line" ] && continue
KEY="${line%%=*}"
VAL="${line#*=}"
case "$KEY" in
prefix) PREF="$VAL" ;;
name) NAME="$VAL" ;;
parent-prefix) PAR_PREF="$VAL" ;;
parent-name) PAR_NAME="$VAL" ;;
author) AUTHOR="$VAL" ;;
include)
if [ -n "$INCLUDES" ]; then
INCLUDES="${INCLUDES}
#include ${VAL}"
else
INCLUDES="#include ${VAL}"
fi
;;
enum)
if [ -n "$ENUMS" ]; then
ENUMS="${ENUMS}
${VAL}"
else
ENUMS="$VAL"
fi
;;
ensure)
if [ -n "$ENSURES" ]; then
ENSURES="${ENSURES}
g_type_ensure (${VAL});"
else
ENSURES=" g_type_ensure (${VAL});"
fi
;;
property)
if [ -n "$PROPS" ]; then
PROPS="${PROPS}
${VAL}"
else
PROPS="$VAL"
fi
;;
*) die "unknown key '${KEY}' in ${SPEC_FILE}" ;;
esac
done < "$SPEC_FILE"
if [ -z "$PREF" ] ||
[ -z "$NAME" ] ||
[ -z "$PAR_PREF" ] ||
[ -z "$PAR_NAME" ] ||
[ -z "$AUTHOR" ] ||
[ -z "$PROPS" ]; then
die "one or more params in $SPEC_FILE are empty"
fi
case "$OUTPUT_TYPE" in
--header)
H_FILE="$OUTPUT_FILE"
C_FILE="${OUTPUT_FILE%\.h}.c"
;;
--code)
H_FILE="${OUTPUT_FILE%\.c}.h"
C_FILE="$OUTPUT_FILE"
;;
esac
to_upper() {
echo "$1" | tr '[a-z]' '[A-Z]'
}
to_pascal() {
echo "$1" | sed 's/[^_]\+/\L\u&/g' | tr -d '_'
}
to_hyphened() {
echo "$1" | tr '_' '-'
}
SNAKE="${PREF}_${NAME}"
MACRO_PREF="$(to_upper "${PREF}")"
MACRO_NAME="$(to_upper "${NAME}")"
MACRO="${MACRO_PREF}_${MACRO_NAME}"
TYPE="${MACRO_PREF}_TYPE_${MACRO_NAME}"
PASCAL="$(to_pascal "${SNAKE}")"
HYPHEN_NAME="$(to_hyphened "${NAME}")"
HYPHEN="$(to_hyphened "${PREF}")-${HYPHEN_NAME}"
PAR_SNAKE="${PAR_PREF}_${PAR_NAME}"
PAR_MACRO_PREF="$(to_upper "${PAR_PREF}")"
PAR_MACRO_NAME="$(to_upper "${PAR_NAME}")"
PAR_MACRO="${PAR_MACRO_PREF}_${PAR_MACRO_NAME}"
PAR_TYPE="${PAR_MACRO_PREF}_TYPE_${PAR_MACRO_NAME}"
PAR_PASCAL="$(to_pascal "${PAR_SNAKE}")"
PAR_HYPHEN_NAME="$(to_hyphened "${PAR_NAME}")"
PAR_HYPHEN="$(to_hyphened "${PAR_PREF}")-${PAR_HYPHEN_NAME}"
YEAR="$(date +'%Y')"
print_enums () {
HEADER="$1"
[ -z "$ENUMS" ] && return
if [ "$HEADER" = header ]; then
while IFS= read -r line; do
set -- $line
LOC_PREF="$1"
LOC_NAME="$2"
LOC_SNAKE="${LOC_PREF}_${LOC_NAME}"
LOC_SNAKE_UPPER="$(to_upper "$LOC_SNAKE")"
LOC_TYPE="$(to_upper "$LOC_PREF")_TYPE_$(to_upper "$LOC_NAME")"
LOC_PASCAL="$(to_pascal "${LOC_SNAKE}")"
shift 2
printf 'typedef enum\n{\n'
for enum in "$@"; do
LOC_ENUM_SYMBOL="${LOC_SNAKE_UPPER}_$(to_upper "$enum")"
printf ' %s,\n' "$LOC_ENUM_SYMBOL"
done
printf '} %s;\n' "$LOC_PASCAL"
printf 'GType %s_get_type (void);\n' "$LOC_SNAKE"
printf '#define %s (%s_get_type ())\n\n' "$LOC_TYPE" "$LOC_SNAKE"
done <<EOF
$ENUMS
EOF
else
while IFS= read -r line; do
set -- $line
LOC_PREF="$1"
LOC_NAME="$2"
LOC_SNAKE="${LOC_PREF}_${LOC_NAME}"
LOC_SNAKE_UPPER="$(to_upper "$LOC_SNAKE")"
LOC_TYPE="$(to_upper "$LOC_PREF")_TYPE_$(to_upper "$LOC_NAME")"
LOC_PASCAL="$(to_pascal "${LOC_SNAKE}")"
shift 2
printf 'G_DEFINE_ENUM_TYPE (\n'
printf ' %s,\n' "$LOC_PASCAL"
printf ' %s' "$LOC_SNAKE"
for enum in "$@"; do
LOC_ENUM_SYMBOL="${LOC_SNAKE_UPPER}_$(to_upper "$enum")"
LOC_ENUM_NICK="$(to_hyphened "$enum")"
printf ',\n G_DEFINE_ENUM_VALUE (%s, "%s")' "$LOC_ENUM_SYMBOL" "$LOC_ENUM_NICK"
done
printf ');\n\n'
done <<EOF
$ENUMS
EOF
fi
}
print_struct () {
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
printf ' %s ' "$LOC_CTYPE"
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*) printf '*'
esac
printf "%s;\n" "$LOC_NAME"
done <<EOF
$PROPS
EOF
}
print_prop_enums () {
printf ' PROP_0,\n\n'
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
printf ' PROP_%s,\n' "$(to_upper $LOC_NAME)"
done <<EOF
$PROPS
EOF
printf '\n LAST_PROP\n'
}
print_dispose () {
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
LOC_FREE="$5"
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*)
printf ' g_clear_pointer (&self->%s, ' "${LOC_NAME}"
if [ -n "$LOC_FREE" ]; then
printf "$LOC_FREE"
else
case "$LOC_PTYPE" in
string) printf 'g_free' ;;
*) printf 'g_object_unref' ;;
esac
fi
printf ');\n'
;;
esac
done <<EOF
$PROPS
EOF
}
print_get_property () {
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
printf ' case PROP_%s:\n' "$(to_upper $LOC_NAME)"
printf ' g_value_set_%s (value, %s_get_%s (self));\n' "$LOC_PTYPE" "$SNAKE" "$LOC_NAME"
printf ' break;\n'
done <<EOF
$PROPS
EOF
}
print_set_property () {
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
printf ' case PROP_%s:\n' "$(to_upper $LOC_NAME)"
printf ' %s_set_%s (self, g_value_get_%s (value));\n' "$SNAKE" "$LOC_NAME" "$LOC_PTYPE"
printf ' break;\n'
done <<EOF
$PROPS
EOF
}
print_init_properties () {
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
printf ' props[PROP_%s] =\n' "$(to_upper $LOC_NAME)"
printf ' g_param_spec_%s (\n' "$LOC_PTYPE"
printf ' "%s",\n' "$(to_hyphened "$LOC_NAME")"
printf ' NULL, NULL,'
case "$LOC_PTYPE" in
uchar|uint|ulong|uint64|unichar)
printf '\n 0, G_MAX%s, 0,\n' "$(to_upper "$LOC_PTYPE")"
;;
char|int|long|int64)
printf '\n G_MIN%s, G_MAX%s, 0,\n' "$(to_upper "$LOC_PTYPE")" "$(to_upper "$LOC_PTYPE")"
;;
float|double)
printf '\n G_MININT, G_MAXINT, 0.0,\n' "$(to_upper "$LOC_PTYPE")"
;;
boolean)
printf ' FALSE,\n'
;;
string)
printf ' NULL,\n'
;;
enum)
printf '\n %s, 0,\n' "$LOC_GTYPE"
;;
variant)
printf '\n G_VARIANT_TYPE_ANY, NULL,\n'
;;
*)
printf '\n %s,\n' "$LOC_GTYPE"
;;
esac
printf ' G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_EXPLICIT_NOTIFY);\n\n'
done <<EOF
$PROPS
EOF
}
print_functions () {
HEADER="$1"
printf '%s *\n%s_new (void)' "$PASCAL" "$SNAKE"
if [ "$HEADER" = header ]; then
printf ';\n\n'
else
printf '{\n return g_object_new (%s, NULL);\n}\n\n' "$TYPE"
fi
}
print_get_property_methods () {
HEADER="$1"
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
case "$LOC_PTYPE" in
string) printf 'const ' ;;
esac
printf '%s' "$LOC_CTYPE"
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*) printf ' *'
esac
printf '\n%s_get_%s (%s *self)' "$SNAKE" "$LOC_NAME" "$PASCAL"
if [ "$HEADER" = header ]; then
printf ';\n\n'
else
printf '{\n g_return_val_if_fail (%s_IS_%s (self), ' "$MACRO_PREF" "$MACRO_NAME"
case "$LOC_PTYPE" in
uchar|uint|ulong|uint64|unichar|char|int|long|int64|enum)
printf '0'
;;
float|double)
printf '0.0'
;;
boolean)
printf 'FALSE'
;;
*)
printf 'NULL'
;;
esac
printf ');\n'
printf ' return self->%s;\n' "$LOC_NAME"
printf '}\n\n'
fi
done <<EOF
$PROPS
EOF
}
print_set_property_methods () {
HEADER="$1"
TAKE="$2"
while IFS= read -r line; do
set -- $line
LOC_NAME="$1"
LOC_CTYPE="$2"
LOC_GTYPE="$3"
LOC_PTYPE="$4"
LOC_FREE="$5"
LOC_REF="$6"
if [ "$TAKE" = take ]; then
case "$LOC_PTYPE" in
string) ;;
*) continue ;;
esac
fi
if [ "$TAKE" = take ]; then
printf 'void\n%s_set_%s_take (%s *self,\n ' "$SNAKE" "$LOC_NAME" "$PASCAL"
else
printf 'void\n%s_set_%s (%s *self,\n ' "$SNAKE" "$LOC_NAME" "$PASCAL"
case "$LOC_PTYPE" in
string) printf 'const ' ;;
esac
fi
printf '%s ' "$LOC_CTYPE"
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*) printf '*' ;;
esac
printf '%s)' "$LOC_NAME"
if [ "$HEADER" = header ]; then
printf ';\n\n'
if [ "$TAKE" = take ]; then
case "$LOC_PTYPE" in
string)
printf '#define %s_set_%s_take_printf(self, ...) %s_set_%s_take (self, g_strdup_printf (__VA_ARGS__))\n\n' "$SNAKE" "$LOC_NAME" "$SNAKE" "$LOC_NAME"
;;
esac
fi
else
printf '{\n g_return_if_fail (%s_IS_%s (self));\n\n' "$MACRO_PREF" "$MACRO_NAME"
printf ' if ('
case "$LOC_PTYPE" in
boolean) printf '!!%s == !!self->%s' "$LOC_NAME" "$LOC_NAME" ;;
string)
if [ "$TAKE" = take ]; then
printf '%s != NULL && self->%s != NULL && g_strcmp0(%s, self->%s) == 0' "$LOC_NAME" "$LOC_NAME" "$LOC_NAME" "$LOC_NAME"
else
printf '%s == self->%s || (%s != NULL && self->%s != NULL && g_strcmp0(%s, self->%s) == 0)' "$LOC_NAME" "$LOC_NAME" "$LOC_NAME" "$LOC_NAME" "$LOC_NAME" "$LOC_NAME"
fi
;;
*) printf '%s == self->%s' "$LOC_NAME" "$LOC_NAME" ;;
esac
if [ "$TAKE" = take ]; then
printf '){\n '
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*)
if [ -n "$LOC_FREE" ]; then
printf "$LOC_FREE"
else
case "$LOC_PTYPE" in
string) printf 'g_free' ;;
*) printf 'g_object_unref' ;;
esac
fi
printf ' (%s);\n' "$LOC_NAME"
;;
esac
printf ' return;\n }\n\n'
else
printf ')\n return;\n\n'
fi
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double) ;;
*)
printf ' g_clear_pointer (&self->%s, ' "$LOC_NAME"
if [ -n "$LOC_FREE" ]; then
printf "$LOC_FREE"
else
case "$LOC_PTYPE" in
string) printf 'g_free' ;;
*) printf 'g_object_unref' ;;
esac
fi
printf ');\n'
printf ' if (%s != NULL)\n ' "$LOC_NAME"
;;
esac
if [ "$TAKE" = take ]; then
printf ' self->%s = %s' "$LOC_NAME" "$LOC_NAME"
else
printf ' self->%s = ' "$LOC_NAME"
if [ -n "$LOC_REF" ]; then
printf '%s (%s)' "$LOC_REF" "$LOC_NAME"
else
case "$LOC_PTYPE" in
char|uchar|boolean|int|uint|long|ulong|int64|uint64|unichar|enum|flags|float|double)
printf '%s' "$LOC_NAME"
;;
string)
printf 'g_strdup (%s)' "$LOC_NAME"
;;
*)
printf 'g_object_ref (%s)' "$LOC_NAME"
;;
esac
fi
fi
printf ';\n\n'
printf ' g_object_notify_by_pspec (G_OBJECT (self), props[PROP_%s]);\n' "$(to_upper $LOC_NAME)"
printf '}\n\n'
fi
done <<EOF
$PROPS
EOF
}
[ "$OUTPUT_TYPE" = --header ] && cat > "$H_FILE" <<EOF
/* $H_FILE
*
* Copyright $YEAR $AUTHOR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#pragma once
#include <glib-object.h>
$INCLUDES
G_BEGIN_DECLS
$(print_enums header)
#define $TYPE (${SNAKE}_get_type ())
G_DECLARE_FINAL_TYPE ($PASCAL, $SNAKE, $MACRO_PREF, $MACRO_NAME, $PAR_PASCAL)
$(print_functions header)
$(print_get_property_methods header)
$(print_set_property_methods header)
$(print_set_property_methods header take)
G_END_DECLS
/* End of $H_FILE */
EOF
[ "$OUTPUT_TYPE" = --code ] && cat > "$C_FILE" <<EOF
/* $C_FILE
*
* Copyright $YEAR $AUTHOR
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* SPDX-License-Identifier: GPL-3.0-or-later
*/
#include "$H_FILE"
$(print_enums)
struct _${PASCAL}
{
$PAR_PASCAL parent_instance;
$(print_struct)
};
G_DEFINE_FINAL_TYPE ($PASCAL, $SNAKE, $PAR_TYPE);
enum
{
$(print_prop_enums)
};
static GParamSpec *props[LAST_PROP] = { 0 };
static void
${SNAKE}_dispose (GObject *object)
{
$PASCAL *self = $MACRO (object);
$(print_dispose)
G_OBJECT_CLASS (${SNAKE}_parent_class)->dispose (object);
}
static void
${SNAKE}_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
$PASCAL *self = $MACRO (object);
switch (prop_id)
{
$(print_get_property)
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
${SNAKE}_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
$PASCAL *self = $MACRO (object);
switch (prop_id)
{
$(print_set_property)
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
${SNAKE}_class_init (${PASCAL}Class *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
$ENSURES
object_class->set_property = ${SNAKE}_set_property;
object_class->get_property = ${SNAKE}_get_property;
object_class->dispose = ${SNAKE}_dispose;
$(print_init_properties)
g_object_class_install_properties (object_class, LAST_PROP, props);
}
static void
${SNAKE}_init (${PASCAL} *self)
{
}
$(print_functions)
$(print_get_property_methods)
$(print_set_property_methods)
$(print_set_property_methods '' take)
/* End of $C_FILE */
EOF
exit 0