/* * generator.c: dictionary generator * * Copyright (c) 2013 - 2020, Přemysl Eric Janouch * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * */ #include #include #include #include #include #include "stardict.h" #include "stardict-private.h" #include "generator.h" /// Creates an output stream for a path plus suffix. static GFileOutputStream * replace_file_by_suffix (const gchar *base, const gchar *suffix, GError **error) { gchar *full_path = g_strconcat (base, suffix, NULL); GFile *file = g_file_new_for_path (full_path); g_free (full_path); GFileOutputStream *stream = g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); g_object_unref (file); return stream; } /// Creates a Stardict dictionary generator for the specified basename. Generator * generator_new (const gchar *base, GError **error) { Generator *self = g_malloc0 (sizeof *self); self->info = g_malloc0 (sizeof *self->info); self->info->path = g_strconcat (base, ".ifo", NULL); self->dict_stream = replace_file_by_suffix (base, ".dict", error); if (!self->dict_stream) goto error_dict; self->idx_stream = replace_file_by_suffix (base, ".idx", error); if (!self->idx_stream) goto error_idx; self->dict_data = g_data_output_stream_new (G_OUTPUT_STREAM (self->dict_stream)); g_data_output_stream_set_byte_order (self->dict_data, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); self->idx_data = g_data_output_stream_new (G_OUTPUT_STREAM (self->idx_stream)); g_data_output_stream_set_byte_order (self->idx_data, G_DATA_STREAM_BYTE_ORDER_BIG_ENDIAN); return self; error_idx: g_object_unref (self->dict_stream); error_dict: stardict_info_free (self->info); g_free (self); return NULL; } /// Finishes the dictionary and writes the .ifo file. gboolean generator_finish (Generator *self, GError **error) { GString *ifo_contents = g_string_new ("StarDict's dict ifo file\n"); if (self->info->version == SD_VERSION_3_0_0) g_string_append (ifo_contents, "version=3.0.0\n"); else g_string_append (ifo_contents, "version=2.4.2\n"); self->info->idx_filesize = g_seekable_tell (G_SEEKABLE (self->idx_stream)); self->info->idx_offset_bits = 32; if (!g_output_stream_close (G_OUTPUT_STREAM (self->dict_stream), NULL, error) || !g_output_stream_close (G_OUTPUT_STREAM (self->idx_stream), NULL, error)) return FALSE; guint i; for (i = 0; i < _stardict_ifo_keys_length; i++) { const struct stardict_ifo_key *key = &_stardict_ifo_keys[i]; if (key->type == IFO_STRING) { const gchar *value = G_STRUCT_MEMBER (const gchar *, self->info, key->offset); if (value) g_string_append_printf (ifo_contents, "%s=%s\n", key->name, value); } else { if (self->info->version == SD_VERSION_2_4_2 && !strcmp (key->name, "idxoffsetbits")) continue; gulong value = G_STRUCT_MEMBER (gulong, self->info, key->offset); if (value) g_string_append_printf (ifo_contents, "%s=%lu\n", key->name, value); } } gboolean success = g_file_set_contents (self->info->path, ifo_contents->str, -1, error); g_string_free (ifo_contents, TRUE); return success; } /// Start writing a dictionary entry. void generator_begin_entry (Generator *self) { self->entry_mark = g_seekable_tell (G_SEEKABLE (self->dict_stream)); } /// Write the data type of an entry field, when there's no sametypesequence. gboolean generator_write_type (Generator *self, gchar type, GError **error) { return g_data_output_stream_put_byte (self->dict_data, type, NULL, error); } /// Write a raw binary field. gboolean generator_write_raw (Generator *self, gpointer data, gsize data_size, gboolean mark_end, GError **error) { gsize written; if ((mark_end && !g_data_output_stream_put_uint32 (self->dict_data, data_size, NULL, error)) || !g_output_stream_write_all (G_OUTPUT_STREAM (self->dict_stream), data, data_size, &written, NULL, error)) return FALSE; return TRUE; } /// Write a text string. gboolean generator_write_string (Generator *self, const gchar *s, gboolean mark_end, GError **error) { if (!g_data_output_stream_put_string (self->dict_data, s, NULL, error) || (mark_end && !g_data_output_stream_put_byte (self->dict_data, '\0', NULL, error))) return FALSE; return TRUE; } /// Write a list of data fields back to a dictionary. The list has to be /// acceptable for the generated dictionary's sametypesequence (or lack of). gboolean generator_write_fields (Generator *self, const GList *fields, GError **error) { gboolean sts = self->info->same_type_sequence != NULL; while (fields) { StardictEntryField *field = fields->data; if (!sts && !generator_write_type (self, field->type, error)) return FALSE; gboolean mark_end = !sts || fields->next != NULL; if (g_ascii_islower (field->type)) { if (!generator_write_string (self, field->data, mark_end, error)) return FALSE; } else if (!generator_write_raw (self, field->data, field->data_size, mark_end, error)) return FALSE; fields = fields->next; } return TRUE; } /// Finishes the current entry and writes it into the index. gboolean generator_finish_entry (Generator *self, const gchar *word, GError **error) { if (!g_data_output_stream_put_string (self->idx_data, word, NULL, error) || !g_data_output_stream_put_byte (self->idx_data, '\0', NULL, error) || !g_data_output_stream_put_uint32 (self->idx_data, self->entry_mark, NULL, error) || !g_data_output_stream_put_uint32 (self->idx_data, g_seekable_tell (G_SEEKABLE (self->dict_stream)) - self->entry_mark, NULL, error)) return FALSE; self->info->word_count++; return TRUE; } /// Destroys the generator object, freeing up system resources. void generator_free (Generator *self) { stardict_info_free (self->info); g_object_unref (self->dict_data); g_object_unref (self->idx_data); g_object_unref (self->dict_stream); g_object_unref (self->idx_stream); }