Added MooConfig

master
Yevgen Muntyan 2006-04-08 03:45:37 -05:00
parent 8dc62a9365
commit b1a2531961
5 changed files with 700 additions and 383 deletions

View File

@ -10,7 +10,7 @@ Makefile\.in$
aclocal\.m4
autom4te.*
compile
config.*
^config.*
depcomp
install-sh
ltmain\.sh

View File

@ -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 \

587
moo/mooutils/mooconfig.c Normal file
View File

@ -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;
}

54
moo/mooutils/mooconfig.h Normal file
View File

@ -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__ */

View File

@ -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);
}