Added MooConfig
parent
8dc62a9365
commit
b1a2531961
|
@ -10,7 +10,7 @@ Makefile\.in$
|
|||
aclocal\.m4
|
||||
autom4te.*
|
||||
compile
|
||||
config.*
|
||||
^config.*
|
||||
depcomp
|
||||
install-sh
|
||||
ltmain\.sh
|
||||
|
|
|
@ -16,6 +16,7 @@ mooutils_include_headers = \
|
|||
$(mooutils)/mooclosure.h \
|
||||
$(mooutils)/moocmd.h \
|
||||
$(mooutils)/moocombo.h \
|
||||
$(mooutils)/mooconfig.h \
|
||||
$(mooutils)/mooentry.h \
|
||||
$(mooutils)/moofiltermgr.h \
|
||||
$(mooutils)/mooglade.h \
|
||||
|
@ -57,6 +58,7 @@ mooutils_sources = \
|
|||
$(mooutils)/moocombo.c \
|
||||
$(mooutils)/moocompat.c \
|
||||
$(mooutils)/moocompat.h \
|
||||
$(mooutils)/mooconfig.c \
|
||||
$(mooutils)/moodialogs.c \
|
||||
$(mooutils)/moodialogs.h \
|
||||
$(mooutils)/mooentry.c \
|
||||
|
|
|
@ -0,0 +1,587 @@
|
|||
/*
|
||||
* mooconfig.c
|
||||
*
|
||||
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* See COPYING file that comes with this distribution.
|
||||
*/
|
||||
|
||||
#include "mooutils/mooconfig.h"
|
||||
#include <string.h>
|
||||
|
||||
|
||||
struct _MooConfig {
|
||||
GPtrArray *items;
|
||||
};
|
||||
|
||||
struct _MooConfigItem {
|
||||
GHashTable *dict;
|
||||
char *content;
|
||||
guint start;
|
||||
guint end;
|
||||
};
|
||||
|
||||
#define NTH_ITEM(cfg,n) ((MooConfigItem*) (cfg)->items->pdata[n])
|
||||
|
||||
static MooConfigItem *moo_config_item_new (void);
|
||||
static void moo_config_item_free (MooConfigItem *item);
|
||||
|
||||
|
||||
MooConfig *
|
||||
moo_config_new (void)
|
||||
{
|
||||
MooConfig *config = g_new0 (MooConfig, 1);
|
||||
config->items = g_ptr_array_new ();
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
moo_config_free (MooConfig *config)
|
||||
{
|
||||
if (config)
|
||||
{
|
||||
g_ptr_array_foreach (config->items,
|
||||
(GFunc) moo_config_item_free,
|
||||
NULL);
|
||||
g_ptr_array_free (config->items, TRUE);
|
||||
g_free (config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
guint
|
||||
moo_config_n_items (MooConfig *config)
|
||||
{
|
||||
g_return_val_if_fail (config != NULL, 0);
|
||||
return config->items->len;
|
||||
}
|
||||
|
||||
|
||||
MooConfigItem *
|
||||
moo_config_nth_item (MooConfig *config,
|
||||
guint n)
|
||||
{
|
||||
g_return_val_if_fail (config != NULL, NULL);
|
||||
g_return_val_if_fail (n < config->items->len, NULL);
|
||||
return NTH_ITEM (config, n);
|
||||
}
|
||||
|
||||
|
||||
MooConfigItem *
|
||||
moo_config_new_item (MooConfig *config)
|
||||
{
|
||||
MooConfigItem *item;
|
||||
|
||||
g_return_val_if_fail (config != NULL, NULL);
|
||||
|
||||
item = moo_config_item_new ();
|
||||
g_ptr_array_add (config->items, item);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
static MooConfigItem *
|
||||
moo_config_item_new (void)
|
||||
{
|
||||
MooConfigItem *item = g_new0 (MooConfigItem, 1);
|
||||
item->dict = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
return item;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
moo_config_item_free (MooConfigItem *item)
|
||||
{
|
||||
if (item)
|
||||
{
|
||||
g_hash_table_destroy (item->dict);
|
||||
g_free (item->content);
|
||||
g_free (item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static char *
|
||||
normalize_key (const char *string)
|
||||
{
|
||||
char *norm = g_ascii_strdown (string, -1);
|
||||
return g_strdelimit (norm, "_", '-');
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
moo_config_item_get_value (MooConfigItem *item,
|
||||
const char *key)
|
||||
{
|
||||
char *norm;
|
||||
const char *value;
|
||||
|
||||
g_return_val_if_fail (item != NULL, NULL);
|
||||
g_return_val_if_fail (key != NULL, NULL);
|
||||
|
||||
norm = normalize_key (key);
|
||||
value = g_hash_table_lookup (item->dict, norm);
|
||||
|
||||
g_free (norm);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
moo_config_item_set_value (MooConfigItem *item,
|
||||
const char *key,
|
||||
const char *value)
|
||||
{
|
||||
char *norm;
|
||||
|
||||
g_return_if_fail (item != NULL);
|
||||
g_return_if_fail (key != NULL);
|
||||
|
||||
norm = normalize_key (key);
|
||||
|
||||
if (value)
|
||||
{
|
||||
g_hash_table_insert (item->dict, norm, g_strdup (value));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_remove (item->dict, norm);
|
||||
g_free (norm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
moo_config_item_get_content (MooConfigItem *item)
|
||||
{
|
||||
g_return_val_if_fail (item != NULL, NULL);
|
||||
return item->content;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
moo_config_item_set_content (MooConfigItem *item,
|
||||
const char *content)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
g_return_if_fail (item != NULL);
|
||||
|
||||
tmp = item->content;
|
||||
item->content = g_strdup (content);
|
||||
g_free (tmp);
|
||||
}
|
||||
|
||||
|
||||
/*********************************************************************/
|
||||
/* Parsing
|
||||
*/
|
||||
|
||||
inline static const char *
|
||||
find_line_term (const char *string,
|
||||
guint len,
|
||||
guint *term_len)
|
||||
{
|
||||
while (len)
|
||||
{
|
||||
switch (string[0])
|
||||
{
|
||||
case '\r':
|
||||
if (len > 1 && string[1] == '\n')
|
||||
*term_len = 2;
|
||||
else
|
||||
*term_len = 1;
|
||||
return string;
|
||||
|
||||
case '\n':
|
||||
*term_len = 1;
|
||||
return string;
|
||||
|
||||
default:
|
||||
len--;
|
||||
string++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
inline static char *
|
||||
normalize_indent (const char *line,
|
||||
guint len)
|
||||
{
|
||||
char *norm;
|
||||
guint n_tabs;
|
||||
|
||||
if (!len || line[0] != '\t')
|
||||
return g_strndup (line, len);
|
||||
|
||||
for (n_tabs = 0; n_tabs < len && line[n_tabs] == '\t'; ++n_tabs) ;
|
||||
|
||||
norm = g_new (char, len - n_tabs + n_tabs * 8 + 1);
|
||||
norm[len - n_tabs + n_tabs * 8] = 0;
|
||||
|
||||
memset (norm, n_tabs * 8, ' ');
|
||||
|
||||
if (n_tabs < len)
|
||||
memcpy (norm + n_tabs * 8, line + n_tabs, len - n_tabs);
|
||||
|
||||
return norm;
|
||||
}
|
||||
|
||||
static char **
|
||||
splitlines (const char *string,
|
||||
guint len,
|
||||
guint *n_lines_p)
|
||||
{
|
||||
GSList *list = NULL;
|
||||
char **lines;
|
||||
guint n_lines = 0, i;
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
while (len)
|
||||
{
|
||||
guint term_len = 0;
|
||||
const char *term = find_line_term (string, len, &term_len);
|
||||
|
||||
n_lines++;
|
||||
|
||||
if (term)
|
||||
{
|
||||
list = g_slist_prepend (list, normalize_indent (string, term - string));
|
||||
len -= (term - string + term_len);
|
||||
string = term + term_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = g_slist_prepend (list, normalize_indent (string, len));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list = g_slist_reverse (list);
|
||||
lines = g_new (char*, n_lines + 1);
|
||||
lines[n_lines] = NULL;
|
||||
i = 0;
|
||||
|
||||
while (list)
|
||||
{
|
||||
lines[i++] = list->data;
|
||||
list = g_slist_delete_link (list, list);
|
||||
}
|
||||
|
||||
*n_lines_p = n_lines;
|
||||
return lines;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
line_is_blank_or_comment (const char *line)
|
||||
{
|
||||
if (!line[0] || line[0] == '#')
|
||||
return TRUE;
|
||||
|
||||
while (*line)
|
||||
{
|
||||
if (*line != ' ' && *line != '\t')
|
||||
return FALSE;
|
||||
line++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
line_is_blank (const char *line)
|
||||
{
|
||||
while (line[0])
|
||||
{
|
||||
if (line[0] != ' ' && line[0] != '\t')
|
||||
return FALSE;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static guint
|
||||
line_indent (const char *line)
|
||||
{
|
||||
guint indent = 0;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
switch (*line++)
|
||||
{
|
||||
case ' ':
|
||||
indent += 1;
|
||||
break;
|
||||
|
||||
case '\t':
|
||||
indent += 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
return indent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline static gboolean
|
||||
line_is_comment (const char *line)
|
||||
{
|
||||
return line[0] == '#';
|
||||
}
|
||||
|
||||
|
||||
inline static gboolean
|
||||
line_is_indented (const char *line)
|
||||
{
|
||||
return line[0] == ' ';
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
parse_header_line (char **lines,
|
||||
guint n,
|
||||
MooConfigItem **item)
|
||||
{
|
||||
char *line = lines[n];
|
||||
char *value = NULL, *key = NULL;
|
||||
|
||||
value = strchr (line, ':');
|
||||
|
||||
if (!value)
|
||||
{
|
||||
g_warning ("%s: value missing on line %d", G_STRLOC, n+1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
key = g_strstrip (g_strndup (line, value - line));
|
||||
value = g_strstrip (g_strdup (value + 1));
|
||||
|
||||
if (!key[0])
|
||||
{
|
||||
g_warning ("%s: empty key on line %d", G_STRLOC, n+1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!value[0])
|
||||
{
|
||||
g_warning ("%s: value missing on line %d", G_STRLOC, n+1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!*item)
|
||||
*item = moo_config_item_new ();
|
||||
|
||||
if (moo_config_item_get_value (*item, key))
|
||||
{
|
||||
g_warning ("%s: duplicated key '%s' on line %d", G_STRLOC, key, n+1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
moo_config_item_set_value (*item, key, value);
|
||||
g_free (value);
|
||||
g_free (key);
|
||||
return TRUE;
|
||||
|
||||
error:
|
||||
g_free (value);
|
||||
g_free (key);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
||||
static MooConfig *
|
||||
parse_lines (char **lines,
|
||||
guint n_lines)
|
||||
{
|
||||
MooConfig *config;
|
||||
MooConfigItem *item;
|
||||
GPtrArray *content;
|
||||
guint start;
|
||||
GSList *items;
|
||||
|
||||
content = g_ptr_array_new ();
|
||||
start = 0;
|
||||
items = NULL;
|
||||
item = NULL;
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
guint content_start;
|
||||
|
||||
while (start < n_lines && line_is_blank_or_comment (lines[start]))
|
||||
start++;
|
||||
|
||||
if (start == n_lines)
|
||||
break;
|
||||
|
||||
if (line_is_indented (lines[start]))
|
||||
{
|
||||
g_warning ("line %d", start+1);
|
||||
goto error;
|
||||
}
|
||||
|
||||
content_start = start;
|
||||
|
||||
while (content_start < n_lines)
|
||||
{
|
||||
const char *line = lines[content_start];
|
||||
|
||||
if (line_is_blank (line) || line_is_indented (line))
|
||||
break;
|
||||
|
||||
if (line_is_comment (lines[content_start]))
|
||||
{
|
||||
content_start++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parse_header_line (lines, content_start, &item))
|
||||
goto error;
|
||||
|
||||
content_start++;
|
||||
}
|
||||
|
||||
g_assert (item != NULL);
|
||||
start = content_start;
|
||||
|
||||
if (line_is_indented (lines[start]) && !line_is_blank (lines[start]))
|
||||
{
|
||||
while (start < n_lines)
|
||||
{
|
||||
if (line_is_blank_or_comment (lines[start]))
|
||||
{
|
||||
start++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!line_is_indented (lines[start]))
|
||||
break;
|
||||
|
||||
g_ptr_array_add (content, lines[start]);
|
||||
start++;
|
||||
}
|
||||
}
|
||||
|
||||
if (content->len)
|
||||
{
|
||||
guint indent, i;
|
||||
GString *str;
|
||||
|
||||
indent = line_indent (content->pdata[0]);
|
||||
|
||||
for (i = 1; i < content->len; ++i)
|
||||
{
|
||||
guint indent_here = line_indent (content->pdata[i]);
|
||||
indent = MIN (indent_here, indent);
|
||||
}
|
||||
|
||||
if (!indent)
|
||||
{
|
||||
g_critical ("%s: oops", G_STRLOC);
|
||||
goto error;
|
||||
}
|
||||
|
||||
str = g_string_new (NULL);
|
||||
|
||||
for (i = 0; i < content->len; ++i)
|
||||
{
|
||||
char *line = content->pdata[i];
|
||||
g_string_append (str, line + indent);
|
||||
g_string_append_c (str, '\n');
|
||||
}
|
||||
|
||||
item->content = str->str;
|
||||
g_string_free (str, FALSE);
|
||||
g_ptr_array_set_size (content, 0);
|
||||
}
|
||||
|
||||
items = g_slist_prepend (items, item);
|
||||
item = NULL;
|
||||
}
|
||||
|
||||
config = moo_config_new ();
|
||||
items = g_slist_reverse (items);
|
||||
|
||||
while (items)
|
||||
{
|
||||
g_ptr_array_add (config->items, items->data);
|
||||
items = items->next;
|
||||
}
|
||||
|
||||
g_slist_free (items);
|
||||
g_ptr_array_free (content, TRUE);
|
||||
|
||||
return config;
|
||||
|
||||
error:
|
||||
moo_config_item_free (item);
|
||||
g_ptr_array_free (content, TRUE);
|
||||
g_slist_foreach (items, (GFunc) moo_config_item_free, NULL);
|
||||
g_slist_free (items);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
MooConfig *
|
||||
moo_config_parse (const char *string,
|
||||
int len)
|
||||
{
|
||||
char **lines;
|
||||
guint n_lines;
|
||||
MooConfig *config;
|
||||
|
||||
g_return_val_if_fail (string != NULL, NULL);
|
||||
|
||||
if (len < 0)
|
||||
len = strlen (string);
|
||||
|
||||
lines = splitlines (string, len, &n_lines);
|
||||
config = parse_lines (lines, n_lines);
|
||||
|
||||
g_strfreev (lines);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
||||
MooConfig *
|
||||
moo_config_parse_file (const char *path)
|
||||
{
|
||||
MooConfig *config;
|
||||
GMappedFile *file;
|
||||
GError *error = NULL;
|
||||
|
||||
g_return_val_if_fail (path != NULL, NULL);
|
||||
|
||||
file = g_mapped_file_new (path, FALSE, &error);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
g_warning ("%s: %s", G_STRLOC, error->message);
|
||||
g_error_free (error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
config = moo_config_parse (g_mapped_file_get_contents (file),
|
||||
g_mapped_file_get_length (file));
|
||||
|
||||
g_mapped_file_free (file);
|
||||
return config;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* mooconfig.h
|
||||
*
|
||||
* Copyright (C) 2004-2006 by Yevgen Muntyan <muntyan@math.tamu.edu>
|
||||
*
|
||||
* 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 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* See COPYING file that comes with this distribution.
|
||||
*/
|
||||
|
||||
#ifndef __MOO_CONFIG_H__
|
||||
#define __MOO_CONFIG_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef struct _MooConfig MooConfig;
|
||||
typedef struct _MooConfigItem MooConfigItem;
|
||||
|
||||
|
||||
MooConfig *moo_config_new (void);
|
||||
void moo_config_free (MooConfig *config);
|
||||
|
||||
MooConfig *moo_config_parse (const char *string,
|
||||
int len);
|
||||
MooConfig *moo_config_parse_file (const char *filename);
|
||||
char *moo_config_format (MooConfig *config);
|
||||
gboolean moo_config_save (MooConfig *config,
|
||||
const char *file,
|
||||
GError **error);
|
||||
|
||||
guint moo_config_n_items (MooConfig *config);
|
||||
MooConfigItem *moo_config_nth_item (MooConfig *config,
|
||||
guint n);
|
||||
MooConfigItem *moo_config_new_item (MooConfig *config);
|
||||
|
||||
const char *moo_config_item_get_value (MooConfigItem *item,
|
||||
const char *key);
|
||||
void moo_config_item_set_value (MooConfigItem *item,
|
||||
const char *key,
|
||||
const char *value);
|
||||
const char *moo_config_item_get_content (MooConfigItem *item);
|
||||
void moo_config_item_set_content (MooConfigItem *item,
|
||||
const char *content);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __MOO_CONFIG_H__ */
|
|
@ -14,20 +14,11 @@
|
|||
#include "mooutils/moouseractions.h"
|
||||
#include "mooscript/mooscript-parser.h"
|
||||
#include "mooutils/moocompat.h"
|
||||
#include "mooutils/mooconfig.h"
|
||||
#include <string.h>
|
||||
|
||||
typedef MooUserActionCtxFunc CtxFunc;
|
||||
|
||||
typedef struct {
|
||||
const char *filename;
|
||||
GSList *actions;
|
||||
|
||||
char *action;
|
||||
char *label;
|
||||
char *accel;
|
||||
GString *code;
|
||||
} Parser;
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
char *label;
|
||||
|
@ -49,7 +40,7 @@ typedef struct {
|
|||
|
||||
|
||||
GType _moo_user_action_get_type (void) G_GNUC_CONST;
|
||||
static void parser_create_actions (Parser *parser);
|
||||
|
||||
|
||||
G_DEFINE_TYPE (MooUserAction, _moo_user_action, MOO_TYPE_ACTION)
|
||||
|
||||
|
@ -117,259 +108,25 @@ action_new (const char *name,
|
|||
}
|
||||
|
||||
|
||||
static void
|
||||
parser_init (Parser *parser,
|
||||
const char *filename)
|
||||
static MooAction *
|
||||
create_action (MooWindow *window,
|
||||
Action *data)
|
||||
{
|
||||
memset (parser, 0, sizeof (Parser));
|
||||
parser->filename = filename;
|
||||
parser->code = g_string_new (NULL);
|
||||
}
|
||||
MooUserAction *action;
|
||||
|
||||
g_return_val_if_fail (data != NULL, NULL);
|
||||
|
||||
static gboolean
|
||||
is_blank_or_comment (const char *line)
|
||||
{
|
||||
char c;
|
||||
action = g_object_new (_moo_user_action_get_type (),
|
||||
"name", data->name,
|
||||
"accel", data->accel,
|
||||
"label", data->label,
|
||||
NULL);
|
||||
|
||||
while ((c = *line))
|
||||
{
|
||||
if (!c || c == '\r' || c == '#')
|
||||
return TRUE;
|
||||
action->window = window;
|
||||
action->script = ms_node_ref (data->script);
|
||||
action->ctx_func = data->ctx_func;
|
||||
|
||||
if (c != ' ' && c != '\t')
|
||||
return FALSE;
|
||||
|
||||
line++;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parser_cleanup (Parser *parser)
|
||||
{
|
||||
// g_slist_foreach (parser->actions, (GFunc) action_free, NULL);
|
||||
g_slist_free (parser->actions);
|
||||
g_string_free (parser->code, TRUE);
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
starts_action (const char *line)
|
||||
{
|
||||
return !g_ascii_strncasecmp (line, "action:", strlen ("action:"));
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
is_header_line (const char *line)
|
||||
{
|
||||
return !g_ascii_strncasecmp (line, "action:", strlen ("action:")) ||
|
||||
!g_ascii_strncasecmp (line, "label:", strlen ("label:")) ||
|
||||
!g_ascii_strncasecmp (line, "accel:", strlen ("accel:"));
|
||||
}
|
||||
|
||||
|
||||
static gboolean
|
||||
parse_header_line (Parser *parser,
|
||||
char *line,
|
||||
guint line_no)
|
||||
{
|
||||
char *colon, *value, *comment;
|
||||
|
||||
colon = strchr (line, ':');
|
||||
g_assert (colon != NULL);
|
||||
*colon = 0;
|
||||
|
||||
value = colon + 1;
|
||||
comment = strchr (value, '#');
|
||||
if (comment)
|
||||
*comment = 0;
|
||||
g_strstrip (value);
|
||||
|
||||
if (!value[0])
|
||||
{
|
||||
g_warning ("file %s, line %d: empty key value",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!g_ascii_strcasecmp (line, "action"))
|
||||
{
|
||||
if (parser->action)
|
||||
{
|
||||
g_warning ("file %s, line %d: duplicated action field",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
parser->action = value;
|
||||
// g_print ("action %s\n", value);
|
||||
}
|
||||
else if (!g_ascii_strcasecmp (line, "label"))
|
||||
{
|
||||
if (!parser->action)
|
||||
{
|
||||
g_warning ("file %s, line %d: missing action field",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
else if (parser->label)
|
||||
{
|
||||
g_warning ("file %s, line %d: duplicated label field",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
parser->label = value;
|
||||
// g_print ("in action %s: label %s\n", parser->action, value);
|
||||
}
|
||||
else if (!g_ascii_strcasecmp (line, "accel"))
|
||||
{
|
||||
if (!parser->action)
|
||||
{
|
||||
g_warning ("file %s, line %d: missing action field",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
else if (parser->accel)
|
||||
{
|
||||
g_warning ("file %s, line %d: duplicated accel field",
|
||||
parser->filename, line_no);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
parser->accel = value;
|
||||
// g_print ("in action %s: accel %s\n", parser->action, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_return_val_if_reached (FALSE);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parser_add_code (Parser *parser,
|
||||
char *line)
|
||||
{
|
||||
if (parser->code->len)
|
||||
g_string_append_c (parser->code, '\n');
|
||||
g_string_append (parser->code, line);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parser_end_code (Parser *parser,
|
||||
CtxFunc func)
|
||||
{
|
||||
Action *action;
|
||||
|
||||
if (!parser->code->len)
|
||||
{
|
||||
g_warning ("file %s: missing code for action '%s'",
|
||||
parser->filename, parser->action);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// g_print ("in action %s\n-----------------\n%s\n----------------\n",
|
||||
// parser->action, parser->code->str);
|
||||
|
||||
action = action_new (parser->action, parser->label,
|
||||
parser->accel, parser->code->str,
|
||||
func);
|
||||
|
||||
if (!action)
|
||||
goto out;
|
||||
|
||||
parser->actions = g_slist_prepend (parser->actions, action);
|
||||
|
||||
out:
|
||||
parser->action = NULL;
|
||||
parser->label = NULL;
|
||||
parser->accel = NULL;
|
||||
g_string_truncate (parser->code, 0);
|
||||
}
|
||||
|
||||
|
||||
inline static const char *
|
||||
find_line_term (const char *string,
|
||||
guint len,
|
||||
guint *term_len)
|
||||
{
|
||||
while (len)
|
||||
{
|
||||
switch (string[0])
|
||||
{
|
||||
case '\r':
|
||||
if (len > 1 && string[1] == '\n')
|
||||
*term_len = 2;
|
||||
else
|
||||
*term_len = 1;
|
||||
return string;
|
||||
|
||||
case '\n':
|
||||
*term_len = 1;
|
||||
return string;
|
||||
|
||||
default:
|
||||
len--;
|
||||
string++;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char **
|
||||
splitlines (const char *string,
|
||||
guint len,
|
||||
guint *n_lines_p)
|
||||
{
|
||||
GSList *list = NULL;
|
||||
char **lines;
|
||||
guint n_lines = 0, i;
|
||||
|
||||
if (!len)
|
||||
return NULL;
|
||||
|
||||
while (len)
|
||||
{
|
||||
guint term_len = 0;
|
||||
const char *term = find_line_term (string, len, &term_len);
|
||||
|
||||
n_lines++;
|
||||
|
||||
if (term)
|
||||
{
|
||||
list = g_slist_prepend (list, g_strndup (string, term - string));
|
||||
len -= (term - string + term_len);
|
||||
string = term + term_len;
|
||||
}
|
||||
else
|
||||
{
|
||||
list = g_slist_prepend (list, g_strndup (string, len));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
list = g_slist_reverse (list);
|
||||
lines = g_new (char*, n_lines + 1);
|
||||
lines[n_lines] = NULL;
|
||||
i = 0;
|
||||
|
||||
while (list)
|
||||
{
|
||||
lines[i++] = list->data;
|
||||
list = g_slist_delete_link (list, list);
|
||||
}
|
||||
|
||||
*n_lines_p = n_lines;
|
||||
return lines;
|
||||
return MOO_ACTION (action);
|
||||
}
|
||||
|
||||
|
||||
|
@ -377,97 +134,56 @@ void
|
|||
moo_parse_user_actions (const char *filename,
|
||||
MooUserActionCtxFunc ctx_func)
|
||||
{
|
||||
GMappedFile *file;
|
||||
GError *error = NULL;
|
||||
Parser parser;
|
||||
char **lines;
|
||||
guint n_lines = 0, i;
|
||||
guint n_items, i;
|
||||
MooConfig *config;
|
||||
MooWindowClass *klass;
|
||||
|
||||
g_return_if_fail (filename != NULL);
|
||||
g_return_if_fail (ctx_func != NULL);
|
||||
|
||||
file = g_mapped_file_new (filename, FALSE, &error);
|
||||
config = moo_config_parse_file (filename);
|
||||
|
||||
if (!file)
|
||||
{
|
||||
g_warning ("%s: could not open file %s", G_STRLOC, filename);
|
||||
g_warning ("%s: %s", G_STRLOC, error->message);
|
||||
g_error_free (error);
|
||||
if (!config)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_mapped_file_get_length (file))
|
||||
n_items = moo_config_n_items (config);
|
||||
klass = g_type_class_ref (MOO_TYPE_WINDOW);
|
||||
|
||||
for (i = 0; i < n_items; ++i)
|
||||
{
|
||||
g_mapped_file_free (file);
|
||||
return;
|
||||
Action *action;
|
||||
const char *name, *label, *accel, *code;
|
||||
MooConfigItem *item = moo_config_nth_item (config, i);
|
||||
|
||||
name = moo_config_item_get_value (item, "action");
|
||||
label = moo_config_item_get_value (item, "label");
|
||||
accel = moo_config_item_get_value (item, "accel");
|
||||
code = moo_config_item_get_content (item);
|
||||
|
||||
if (!name)
|
||||
{
|
||||
g_warning ("%s: action name missing", G_STRLOC);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!label)
|
||||
label = name;
|
||||
|
||||
if (!code)
|
||||
{
|
||||
g_warning ("%s: code missing", G_STRLOC);
|
||||
continue;
|
||||
}
|
||||
|
||||
action = action_new (name, label, accel, code, ctx_func);
|
||||
|
||||
if (action)
|
||||
moo_window_class_new_action_custom (klass, action->name,
|
||||
(MooWindowActionFunc) create_action,
|
||||
action, (GDestroyNotify) action_free);
|
||||
}
|
||||
|
||||
lines = splitlines (g_mapped_file_get_contents (file),
|
||||
g_mapped_file_get_length (file),
|
||||
&n_lines);
|
||||
g_mapped_file_free (file);
|
||||
|
||||
parser_init (&parser, filename);
|
||||
i = 0;
|
||||
|
||||
while (i < n_lines)
|
||||
{
|
||||
while (i < n_lines && is_blank_or_comment (lines[i]))
|
||||
i++;
|
||||
|
||||
if (i == n_lines)
|
||||
break;
|
||||
|
||||
if (!starts_action (lines[i]))
|
||||
{
|
||||
g_warning ("file %s, line %d: invalid text '%s'",
|
||||
filename, i, lines[i]);
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
if (is_header_line (lines[i]))
|
||||
{
|
||||
if (!parse_header_line (&parser, lines[i], i))
|
||||
goto out;
|
||||
|
||||
}
|
||||
else if (!is_blank_or_comment (lines[i]))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (++i == n_lines)
|
||||
{
|
||||
g_warning ("file %s: missing code in action '%s'",
|
||||
filename, parser.action);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
while (i < n_lines)
|
||||
{
|
||||
if ((!lines[i][0] || lines[i][0] == '\r') &&
|
||||
(i == n_lines - 1 || starts_action (lines[i+1])))
|
||||
{
|
||||
parser_end_code (&parser, ctx_func);
|
||||
break;
|
||||
}
|
||||
|
||||
parser_add_code (&parser, lines[i++]);
|
||||
}
|
||||
|
||||
if (i == n_lines)
|
||||
parser_end_code (&parser, ctx_func);
|
||||
}
|
||||
|
||||
parser.actions = g_slist_reverse (parser.actions);
|
||||
parser_create_actions (&parser);
|
||||
|
||||
out:
|
||||
parser_cleanup (&parser);
|
||||
g_strfreev (lines);
|
||||
g_type_class_unref (klass);
|
||||
}
|
||||
|
||||
|
||||
|
@ -511,45 +227,3 @@ _moo_user_action_class_init (MooUserActionClass *klass)
|
|||
G_OBJECT_CLASS(klass)->finalize = moo_user_action_finalize;
|
||||
MOO_ACTION_CLASS(klass)->activate = moo_user_action_activate;
|
||||
}
|
||||
|
||||
|
||||
static MooAction *
|
||||
create_action (MooWindow *window,
|
||||
Action *data)
|
||||
{
|
||||
MooUserAction *action;
|
||||
|
||||
g_return_val_if_fail (data != NULL, NULL);
|
||||
|
||||
action = g_object_new (_moo_user_action_get_type (),
|
||||
"name", data->name,
|
||||
"accel", data->accel,
|
||||
"label", data->label,
|
||||
NULL);
|
||||
|
||||
action->window = window;
|
||||
action->script = ms_node_ref (data->script);
|
||||
action->ctx_func = data->ctx_func;
|
||||
|
||||
return MOO_ACTION (action);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
parser_create_actions (Parser *parser)
|
||||
{
|
||||
GSList *l;
|
||||
MooWindowClass *klass;
|
||||
|
||||
klass = g_type_class_ref (MOO_TYPE_WINDOW);
|
||||
|
||||
for (l = parser->actions; l != NULL; l = l->next)
|
||||
{
|
||||
Action *action = l->data;
|
||||
moo_window_class_new_action_custom (klass, action->name,
|
||||
(MooWindowActionFunc) create_action,
|
||||
action, (GDestroyNotify) action_free);
|
||||
}
|
||||
|
||||
g_type_class_unref (klass);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue