bool bs_parser_parse(bs_parser_t *parser, const char *path, const char *framework_path, bs_parse_options_t options, bs_parse_callback_t callback, void *context, char **error) { xmlTextReaderPtr reader; bs_element_function_t *func; bs_element_class_t *klass; bs_element_method_t *method; unsigned int i; #define MAX_ARGS 128 bs_element_arg_t args[MAX_ARGS]; bs_element_arg_t fptr_args[MAX_ARGS]; char *protocol_name = NULL; int func_ptr_arg_depth; bs_element_function_pointer_t *func_ptr; bool success; CFStringRef cf_path; bool nested_func_ptr; unsigned int version_number = 0; if (callback == NULL) return false; /* check if the given framework path has not been loaded already */ cf_path = CFStringCreateWithFileSystemRepresentation(kCFAllocatorMalloc, path); CFMakeCollectable(cf_path); for (unsigned i = 0, count = CFArrayGetCount(parser->loaded_paths); i < count; i++) { CFStringRef s = CFArrayGetValueAtIndex(parser->loaded_paths, i); if (CFStringCompare(cf_path, s, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { /* already loaded */ return true; } } CFArrayAppendValue(parser->loaded_paths, cf_path); //printf("parsing %s\n", path); #define BAIL(fmt, args...) \ do { \ if (error != NULL) { \ char buf[1024]; \ snprintf(buf, sizeof buf, \ "%s:%ld - "fmt, path, \ xmlGetLineNo(xmlTextReaderCurrentNode(reader)), \ ##args); \ *error = strdup(buf); \ } \ success = false; \ goto bails; \ } \ while (0) #if __LP64__ # define CHECK_TYPE_ATTRIBUTE(var) CHECK_ATTRIBUTE(var, "type") #else # define CHECK_TYPE_ATTRIBUTE(var) \ if (var == NULL && get_type64_attribute(reader) != NULL) { \ break; \ } \ CHECK_ATTRIBUTE(var, "type") #endif #define CHECK_ATTRIBUTE_CAN_BE_EMPTY(a, name) \ CHECK_ATTRIBUTE0(a, name, true) #define CHECK_ATTRIBUTE(a, name) \ CHECK_ATTRIBUTE0(a, name, false) #define CHECK_ATTRIBUTE0(a, name, can_be_empty) \ do { \ if (a == NULL) \ BAIL("expected attribute `%s' for element `%s'", \ name, xmlTextReaderConstName(reader)); \ if (!can_be_empty && *a == '\0') { \ free(a); \ BAIL("empty attribute `%s' for element `%s'", \ name, xmlTextReaderConstName(reader)); \ } \ } while (0) \ reader = xmlNewTextReaderFilename(path); if (reader == NULL) BAIL("cannot create XML text reader for file at path `%s'", path); func = NULL; func_ptr = NULL; func_ptr_arg_depth = -1; nested_func_ptr = false; klass = NULL; method = NULL; protocol_name = NULL; while (true) { const char *name; unsigned int namelen; int node_type = -1; bool eof = false; struct bs_xml_atom *atom; void *bs_element; bs_element_type_t bs_element_type = 0; do { int retval = xmlTextReaderRead(reader); if (retval == 0) { eof = true; break; } else if (retval < 0) BAIL("parsing error: %d", retval); node_type = xmlTextReaderNodeType(reader); } while (node_type != XML_READER_TYPE_ELEMENT && node_type != XML_READER_TYPE_END_ELEMENT); if (eof) break; name = (const char *)xmlTextReaderConstName(reader); namelen = strlen(name); bs_element = NULL; atom = bs_xml_element(name, namelen); if (atom == NULL) { // TODO: we should include the "signatures" string into the gperf // function. if (version_number == 0 && strcmp(name, "signatures") == 0) { char *str = get_attribute(reader, "version"); if (str != NULL) { char *p = strchr(str, '.'); if (p != NULL) { *p = '\0'; int major = atoi(str); int minor = atoi(&p[1]); assert(major < 10 && minor < 10); version_number = (major * 10) + minor; parser->version_number = version_number; } free(str); } } continue; } if (nested_func_ptr) { // FIXME: elements nesting function_pointers aren't supported yet by the // parser, so we just ignore them. if (node_type == XML_READER_TYPE_END_ELEMENT && (atom->val == BS_XML_FUNCTION || atom->val == BS_XML_METHOD)) { nested_func_ptr = false; } continue; } if (node_type == XML_READER_TYPE_ELEMENT) { switch (atom->val) { case BS_XML_DEPENDS_ON: { char *depends_on_path; char bs_path[PATH_MAX]; bool bs_path_found; depends_on_path = get_attribute(reader, "path"); CHECK_ATTRIBUTE(depends_on_path, "path"); //printf("depends of %s\n", depends_on_path); bs_path_found = bs_find_path(depends_on_path, bs_path, sizeof bs_path); if (bs_path_found) { if (!bs_parser_parse(parser, bs_path, depends_on_path, options, callback, context, error)) { free(depends_on_path); return false; } } free(depends_on_path); break; } case BS_XML_CONSTANT: { bs_element_constant_t *bs_const; char *const_name; char *const_type; const_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(const_name, "name"); const_type = get_type_attribute(reader); CHECK_TYPE_ATTRIBUTE(const_type); bs_const = (bs_element_constant_t *) malloc(sizeof(bs_element_constant_t)); ASSERT_ALLOC(bs_const); bs_const->name = const_name; bs_const->type = const_type; bs_const->ignore = false; bs_const->suggestion = NULL; bs_const->magic_cookie = get_boolean_attribute(reader, "magic_cookie", false); bs_element = bs_const; bs_element_type = BS_ELEMENT_CONSTANT; break; } case BS_XML_STRING_CONSTANT: { bs_element_string_constant_t *bs_strconst; char *strconst_name; char *strconst_value; strconst_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(strconst_name, "name"); strconst_value = get_attribute(reader, "value"); CHECK_ATTRIBUTE_CAN_BE_EMPTY(strconst_value, "value"); bs_strconst = (bs_element_string_constant_t *) malloc(sizeof(bs_element_string_constant_t)); ASSERT_ALLOC(bs_strconst); bs_strconst->name = strconst_name; bs_strconst->value = strconst_value; bs_strconst->nsstring = get_boolean_attribute(reader, "nsstring", false); bs_element = bs_strconst; bs_element_type = BS_ELEMENT_STRING_CONSTANT; break; } case BS_XML_ENUM: { char *enum_name; char *enum_value; enum_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(enum_name, "name"); #if __LP64__ enum_value = get_attribute(reader, "value64"); if (enum_value == NULL) #endif enum_value = get_attribute(reader, "value"); #if BYTE_ORDER == BIG_ENDIAN # define BYTE_ORDER_VALUE_ATTR_NAME "be_value" #else # define BYTE_ORDER_VALUE_ATTR_NAME "le_value" #endif if (enum_value == NULL) enum_value = get_attribute(reader, BYTE_ORDER_VALUE_ATTR_NAME); if (enum_value != NULL) { bs_element_enum_t *bs_enum; bs_enum = (bs_element_enum_t *)malloc(sizeof(bs_element_enum_t)); ASSERT_ALLOC(bs_enum); bs_enum->name = enum_name; bs_enum->value = enum_value; bs_enum->ignore = get_boolean_attribute(reader, "ignore", false); bs_enum->suggestion = get_attribute(reader, "suggestion"); bs_element = bs_enum; bs_element_type = BS_ELEMENT_ENUM; } break; } case BS_XML_STRUCT: { bs_element_struct_t *bs_struct; char *struct_decorated_type; char *struct_name; char type[MAX_ENCODE_LEN]; bs_element_struct_field_t fields[128]; int field_count; struct_decorated_type = get_type_attribute(reader); CHECK_TYPE_ATTRIBUTE(struct_decorated_type); struct_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(struct_name, "name"); if (!undecorate_struct_type(struct_decorated_type, type, sizeof type, fields, 128, &field_count)) { BAIL("Can't handle structure '%s' with type '%s'", struct_name, struct_decorated_type); } free(struct_decorated_type); bs_struct = (bs_element_struct_t *)malloc(sizeof(bs_element_struct_t)); ASSERT_ALLOC(bs_struct); bs_struct->name = struct_name; bs_struct->type = strdup(type); bs_struct->fields = (bs_element_struct_field_t *)malloc( sizeof(bs_element_struct_field_t) * field_count); ASSERT_ALLOC(bs_struct->fields); memcpy(bs_struct->fields, fields, sizeof(bs_element_struct_field_t) * field_count); bs_struct->fields_count = field_count; bs_struct->opaque = get_boolean_attribute(reader, "opaque", false); bs_element = bs_struct; bs_element_type = BS_ELEMENT_STRUCT; break; } case BS_XML_OPAQUE: { bs_element_opaque_t *bs_opaque; char *opaque_name; char *opaque_type; opaque_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(opaque_name, "name"); opaque_type = get_type_attribute(reader); CHECK_TYPE_ATTRIBUTE(opaque_type); bs_opaque = (bs_element_opaque_t *)malloc(sizeof(bs_element_opaque_t)); ASSERT_ALLOC(bs_opaque); bs_opaque->name = opaque_name; bs_opaque->type = opaque_type; bs_element = bs_opaque; bs_element_type = BS_ELEMENT_OPAQUE; break; } case BS_XML_CFTYPE: { bs_element_cftype_t *bs_cftype; char *cftype_name; char *cftype_type; cftype_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(cftype_name, "name"); cftype_type = get_type_attribute(reader); CHECK_TYPE_ATTRIBUTE(cftype_type); bs_cftype = (bs_element_cftype_t *)malloc(sizeof(bs_element_cftype_t)); ASSERT_ALLOC(bs_cftype); bs_cftype->name = cftype_name; bs_cftype->type = cftype_type; #if 1 /* the type_id field isn't used in MacRuby */ bs_cftype->type_id = 0; #else char *cftype_gettypeid_func_name; cftype_gettypeid_func_name = get_attribute(reader, "gettypeid_func"); if (cftype_gettypeid_func_name != NULL) { void *sym; sym = dlsym(RTLD_DEFAULT, cftype_gettypeid_func_name); if (sym == NULL) { BAIL("cannot locate gettypeid_func function `%s'", cftype_gettypeid_func_name); } else { CFTypeID (*cb)(void) = sym; bs_cftype->type_id = (*cb)(); } } else { bs_cftype->type_id = 0; } #endif bs_cftype->tollfree = get_attribute(reader, "tollfree"); bs_element = bs_cftype; bs_element_type = BS_ELEMENT_CFTYPE; break; } case BS_XML_INFORMAL_PROTOCOL: { if (protocol_name != NULL) free(protocol_name); protocol_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(protocol_name, "name"); break; } case BS_XML_FUNCTION: { char *func_name; func_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(func_name, "name"); func = (bs_element_function_t *)malloc(sizeof(bs_element_function_t)); ASSERT_ALLOC(func); func->name = func_name; func->variadic = get_boolean_attribute(reader, "variadic", false); func->args_count = 0; func->args = NULL; func->retval = NULL; if (xmlTextReaderIsEmptyElement(reader)) { bs_element = func; bs_element_type = BS_ELEMENT_FUNCTION; func = NULL; } break; } case BS_XML_FUNCTION_ALIAS: { bs_element_function_alias_t *bs_func_alias; char *alias_name; char *alias_original; alias_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(alias_name, "name"); alias_original = get_attribute(reader, "original"); CHECK_ATTRIBUTE(alias_original, "original"); bs_func_alias = (bs_element_function_alias_t *)malloc( sizeof(bs_element_function_alias_t)); ASSERT_ALLOC(bs_func_alias); bs_func_alias->name = alias_name; bs_func_alias->original = alias_original; bs_element = bs_func_alias; bs_element_type = BS_ELEMENT_FUNCTION_ALIAS; break; } case BS_XML_CLASS: { char *class_name; class_name = get_attribute(reader, "name"); CHECK_ATTRIBUTE(class_name, "name"); klass = (bs_element_class_t *)malloc(sizeof(bs_element_class_t)); ASSERT_ALLOC(klass); klass->name = class_name; klass->class_methods = klass->instance_methods = NULL; klass->class_methods_count = klass->instance_methods_count = 0; break; } case BS_XML_ARG: { if (func != NULL || method != NULL || func_ptr != NULL) { bs_element_arg_t *bs_arg; unsigned *argc; argc = func_ptr != NULL ? &func_ptr->args_count : func != NULL ? &func->args_count : &method->args_count; if (*argc >= MAX_ARGS) { if (func_ptr != NULL) BAIL("maximum number of arguments (%d) reached " \ "for function pointer", MAX_ARGS); else if (func != NULL) BAIL("maximum number of arguments (%d) reached " \ "for function '%s'", MAX_ARGS, func->name); else BAIL("maximum number of arguments (%d) reached " \ "for method '%s'", MAX_ARGS, (char *)method->name); } bs_element_arg_t *args_from = (func_ptr == NULL ? args : fptr_args); bs_arg = &args_from[(*argc)++]; if (method != NULL && func_ptr == NULL) { char *index = get_attribute(reader, "index"); CHECK_ATTRIBUTE(index, "index"); bs_arg->index = strtol(index, NULL, 10); free(index); } else { bs_arg->index = -1; } get_type_modifier_attribute(reader, &bs_arg->type_modifier); #if __LP64__ bs_arg->sel_of_type = get_attribute(reader, "sel_of_type64"); if (bs_arg->sel_of_type == NULL) #endif bs_arg->sel_of_type = get_attribute(reader, "sel_of_type"); bs_arg->printf_format = get_boolean_attribute(reader, "printf_format", false); bs_arg->null_accepted = get_boolean_attribute(reader, "null_accepted", true); get_c_ary_type_attribute(reader, &bs_arg->carray_type, &bs_arg->carray_type_value); bs_arg->type = get_type_attribute(reader); if (get_boolean_attribute(reader, "function_pointer", false)) { if (func_ptr != NULL) { func_ptr = NULL; nested_func_ptr = true; break; } bs_arg->function_pointer = (bs_element_function_pointer_t *) calloc(1, sizeof(bs_element_function_pointer_t)); ASSERT_ALLOC(bs_arg->function_pointer); func_ptr = bs_arg->function_pointer; func_ptr_arg_depth = xmlTextReaderDepth(reader); } else { bs_arg->function_pointer = NULL; } } else { BAIL("argument defined outside of a " \ "function/method/function_pointer"); } break; } case BS_XML_RETVAL: { if (func != NULL || method != NULL || func_ptr != NULL) { bs_element_retval_t *bs_retval; if (func_ptr != NULL) { if (func_ptr->retval != NULL) BAIL("function pointer return value defined more than once"); } else if (func != NULL) { if (func->retval != NULL) BAIL("function '%s' return value defined more than once", func->name); } else if (method != NULL) { if (method->retval != NULL) BAIL("method '%s' return value defined more than once", (char *)method->name); } bs_retval = (bs_element_retval_t *)malloc(sizeof(bs_element_retval_t)); ASSERT_ALLOC(bs_retval); get_c_ary_type_attribute(reader, &bs_retval->carray_type, &bs_retval->carray_type_value); bs_retval->type = get_type_attribute(reader); if (bs_retval->type != NULL) bs_retval->already_retained = get_boolean_attribute(reader, "already_retained", false); if (func_ptr != NULL) { if (bs_retval->type != NULL) { func_ptr->retval = bs_retval; } else { free(bs_retval); BAIL("function pointer return value defined without type"); } } else if (func != NULL) { if (bs_retval->type != NULL) { func->retval = bs_retval; } else { free(bs_retval); #if !defined(__LP64__) if (get_type64_attribute(reader) != NULL) { // The function has no 32-bit return value type and we // run in 32-bit mode. We just ignore it. func = NULL; break; } #endif BAIL("function '%s' return value defined without type", func->name); } } else { method->retval = bs_retval; } if (get_boolean_attribute(reader, "function_pointer", false)) { if (func_ptr != NULL) { func_ptr = NULL; nested_func_ptr = true; break; } bs_retval->function_pointer = (bs_element_function_pointer_t *) calloc(1, sizeof(bs_element_function_pointer_t)); ASSERT_ALLOC(bs_retval->function_pointer); func_ptr = bs_retval->function_pointer; func_ptr_arg_depth = xmlTextReaderDepth(reader); } else { bs_retval->function_pointer = NULL; } } else { BAIL("return value defined outside a function/method"); } break; } case BS_XML_METHOD: { if (protocol_name != NULL) { bs_element_informal_protocol_method_t *bs_informal_method; char *selector; char *method_type; selector = get_attribute(reader, "selector"); CHECK_ATTRIBUTE(selector, "selector"); method_type = get_type_attribute(reader); CHECK_TYPE_ATTRIBUTE(method_type); bs_informal_method = (bs_element_informal_protocol_method_t *) malloc(sizeof(bs_element_informal_protocol_method_t)); ASSERT_ALLOC(bs_informal_method); bs_informal_method->name = sel_registerName(selector); free(selector); bs_informal_method->class_method = get_boolean_attribute(reader, "class_method", false); bs_informal_method->type = method_type; bs_informal_method->protocol_name = strdup(protocol_name); bs_element = bs_informal_method; bs_element_type = BS_ELEMENT_INFORMAL_PROTOCOL_METHOD; } else if (klass != NULL) { char *selector; selector = get_attribute(reader, "selector"); CHECK_ATTRIBUTE(selector, "selector"); method = (bs_element_method_t *)malloc(sizeof(bs_element_method_t)); ASSERT_ALLOC(method); method->name = sel_registerName(selector); free(selector); method->class_method = get_boolean_attribute(reader, "class_method", false); method->variadic = get_boolean_attribute(reader, "variadic", false); method->ignore = get_boolean_attribute(reader, "ignore", false); method->suggestion = get_attribute(reader, "suggestion"); method->args_count = 0; method->args = NULL; method->retval = NULL; if (xmlTextReaderIsEmptyElement(reader)) { goto index_method; } } else { BAIL("method defined outside a class or informal protocol"); } break; } } } else if (node_type == XML_READER_TYPE_END_ELEMENT) { switch (atom->val) { case BS_XML_INFORMAL_PROTOCOL: { protocol_name = NULL; break; } case BS_XML_RETVAL: case BS_XML_ARG: { if (func_ptr != NULL && func_ptr_arg_depth == xmlTextReaderDepth(reader)) { bs_element_retval_t *retval = NULL; bs_element_arg_t *arg = NULL; unsigned args_count; if (atom->val == BS_XML_RETVAL) { retval = func != NULL ? func->retval : method->retval; } else { args_count = func != NULL ? func->args_count : method->args_count; arg = &args[args_count - 1]; } // Determine if we deal with a block or a function pointer. const char *old_type = (retval ? retval->type : arg->type); const char lambda_type = *old_type == '@' ? _MR_C_LAMBDA_BLOCK : _MR_C_LAMBDA_FUNCPTR; char tmp_type[1025]; // 3 less to fit <, type and > char new_type[1028]; // Function ptr return type strlcpy(tmp_type, func_ptr->retval->type, sizeof(tmp_type)); // Function ptr args for (i = 0; i < func_ptr->args_count; i++) { strlcat(tmp_type, fptr_args[i].type, sizeof(tmp_type)); } // Clear the final type string memset(new_type, 0, sizeof(new_type)); // Append the function pointer type snprintf(new_type, sizeof(new_type), "%c%c%s%c", _MR_C_LAMBDA_B, lambda_type, tmp_type, _MR_C_LAMBDA_E); // Free the old values if (retval) { free(retval->type); retval->type = strdup(new_type); } else { free(arg->type); arg->type = strdup(new_type); } if (func_ptr->args_count > 0) { size_t len; len = sizeof(bs_element_arg_t) * func_ptr->args_count; func_ptr->args = (bs_element_arg_t *)malloc(len); ASSERT_ALLOC(func_ptr->args); memcpy(func_ptr->args, fptr_args, len); } else { func_ptr->args = NULL; } func_ptr = NULL; func_ptr_arg_depth = -1; } break; } case BS_XML_FUNCTION: { if (func == NULL) { break; } for (i = 0; i < func->args_count; i++) { if (args[i].type == NULL) BAIL("function '%s' argument #%d type not provided", func->name, i); } if (func->args_count > 0) { size_t len; len = sizeof(bs_element_arg_t) * func->args_count; func->args = (bs_element_arg_t *)malloc(len); ASSERT_ALLOC(func->args); memcpy(func->args, args, len); } bs_element = func; bs_element_type = BS_ELEMENT_FUNCTION; func = NULL; break; } case BS_XML_METHOD: { bs_element_method_t *methods; unsigned *methods_count; if (method->args_count > 0) { size_t len; len = sizeof(bs_element_arg_t) * method->args_count; method->args = (bs_element_arg_t *)malloc(len); ASSERT_ALLOC(method->args); memcpy(method->args, args, len); } index_method: methods = method->class_method ? klass->class_methods : klass->instance_methods; methods_count = method->class_method ? &klass->class_methods_count : &klass->instance_methods_count; if (methods == NULL) { methods = (bs_element_method_t *)malloc( sizeof(bs_element_method_t) * (*methods_count + 1)); } else { methods = (bs_element_method_t *)realloc(methods, sizeof(bs_element_method_t) * (*methods_count + 1)); } ASSERT_ALLOC(methods); // methods[*methods_count] = method; // FIXME this is inefficient memcpy(&methods[*methods_count], method, sizeof(bs_element_method_t)); (*methods_count)++; if (method->class_method) klass->class_methods = methods; else klass->instance_methods = methods; free(method); method = NULL; break; } case BS_XML_CLASS: { bs_element = klass; bs_element_type = BS_ELEMENT_CLASS; klass = NULL; break; } } } if (bs_element != NULL) (*callback)(parser, path, bs_element_type, bs_element, context); } success = true; bails: if (protocol_name != NULL) free(protocol_name); xmlFreeTextReader(reader); if (!success) { for (unsigned i = 0, count = CFArrayGetCount(parser->loaded_paths); i < count; i++) { CFStringRef s = CFArrayGetValueAtIndex(parser->loaded_paths, i); if (CFStringCompare(cf_path, s, kCFCompareCaseInsensitive) == kCFCompareEqualTo) { CFArrayRemoveValueAtIndex(parser->loaded_paths, i); break; } } } if (success && options == BS_PARSE_OPTIONS_LOAD_DYLIBS && framework_path != NULL) { char buf[PATH_MAX]; if (_bs_find_path(framework_path, buf, sizeof buf, "dylib")) { if (dlopen(buf, RTLD_LAZY) == NULL) { if (error != NULL) { *error = dlerror(); } success = false; } } } return success; }
static gboolean span_parse_func (MarkupData *md, OpenTag *tag, const gchar **names, const gchar **values, GMarkupParseContext *context, GError **error) { int line_number, char_number; int i; const char *family = NULL; const char *size = NULL; const char *style = NULL; const char *weight = NULL; const char *variant = NULL; const char *stretch = NULL; const char *desc = NULL; const char *foreground = NULL; const char *background = NULL; const char *underline = NULL; const char *underline_color = NULL; const char *strikethrough = NULL; const char *strikethrough_color = NULL; const char *rise = NULL; const char *letter_spacing = NULL; const char *lang = NULL; const char *fallback = NULL; const char *gravity = NULL; const char *gravity_hint = NULL; g_markup_parse_context_get_position (context, &line_number, &char_number); #define CHECK_DUPLICATE(var) G_STMT_START{ \ if ((var) != NULL) { \ g_set_error (error, G_MARKUP_ERROR, \ G_MARKUP_ERROR_INVALID_CONTENT, \ _("Attribute '%s' occurs twice on <span> tag " \ "on line %d char %d, may only occur once"), \ names[i], line_number, char_number); \ return FALSE; \ }}G_STMT_END #define CHECK_ATTRIBUTE2(var, name) \ if (attr_strcmp (names[i], (name)) == 0) { \ CHECK_DUPLICATE (var); \ (var) = values[i]; \ found = TRUE; \ break; \ } #define CHECK_ATTRIBUTE(var) CHECK_ATTRIBUTE2 (var, G_STRINGIFY (var)) i = 0; while (names[i]) { gboolean found = FALSE; switch (names[i][0]) { case 'f': CHECK_ATTRIBUTE (fallback); CHECK_ATTRIBUTE2(desc, "font"); CHECK_ATTRIBUTE2(desc, "font_desc"); CHECK_ATTRIBUTE2(family, "face"); CHECK_ATTRIBUTE2(family, "font_family"); CHECK_ATTRIBUTE2(size, "font_size"); CHECK_ATTRIBUTE2(stretch, "font_stretch"); CHECK_ATTRIBUTE2(style, "font_style"); CHECK_ATTRIBUTE2(variant, "font_variant"); CHECK_ATTRIBUTE2(weight, "font_weight"); CHECK_ATTRIBUTE (foreground); CHECK_ATTRIBUTE2 (foreground, "fgcolor"); break; case 's': CHECK_ATTRIBUTE (size); CHECK_ATTRIBUTE (stretch); CHECK_ATTRIBUTE (strikethrough); CHECK_ATTRIBUTE (strikethrough_color); CHECK_ATTRIBUTE (style); break; case 'g': CHECK_ATTRIBUTE (gravity); CHECK_ATTRIBUTE (gravity_hint); break; case 'l': CHECK_ATTRIBUTE (lang); CHECK_ATTRIBUTE (letter_spacing); break; case 'u': CHECK_ATTRIBUTE (underline); CHECK_ATTRIBUTE (underline_color); break; default: CHECK_ATTRIBUTE (background); CHECK_ATTRIBUTE2 (background, "bgcolor"); CHECK_ATTRIBUTE2(foreground, "color"); CHECK_ATTRIBUTE (rise); CHECK_ATTRIBUTE (variant); CHECK_ATTRIBUTE (weight); break; } if (!found) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, _("Attribute '%s' is not allowed on the <span> tag " "on line %d char %d"), names[i], line_number, char_number); return FALSE; } ++i; } /* Parse desc first, then modify it with other font-related attributes. */ if (G_UNLIKELY (desc)) { PangoFontDescription *parsed; parsed = pango_font_description_from_string (desc); if (parsed) { add_attribute (tag, pango_attr_font_desc_new (parsed)); if (tag) open_tag_set_absolute_font_size (tag, pango_font_description_get_size (parsed)); pango_font_description_free (parsed); } } if (G_UNLIKELY (family)) { add_attribute (tag, pango_attr_family_new (family)); } if (G_UNLIKELY (size)) { if (g_ascii_isdigit (*size)) { const char *end; gint n; /* cap size from the top at an arbitrary 2048 */ #define MAX_SIZE (2048 * PANGO_SCALE) if ((end = size, !pango_scan_int (&end, &n)) || *end != '\0' || n < 0 || n > MAX_SIZE) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("Value of 'size' attribute on <span> tag on line %d " "could not be parsed; should be an integer less than %d, or a " "string such as 'small', not '%s'"), line_number, MAX_SIZE+1, size); goto error; } add_attribute (tag, pango_attr_size_new (n)); if (tag) open_tag_set_absolute_font_size (tag, n); } else if (strcmp (size, "smaller") == 0) { if (tag) { tag->scale_level_delta -= 1; tag->scale_level -= 1; } } else if (strcmp (size, "larger") == 0) { if (tag) { tag->scale_level_delta += 1; tag->scale_level += 1; } } else if (parse_absolute_size (tag, size)) ; /* nothing */ else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("Value of 'size' attribute on <span> tag on line %d " "could not be parsed; should be an integer, or a " "string such as 'small', not '%s'"), line_number, size); goto error; } } if (G_UNLIKELY (style)) { PangoStyle pango_style; if (pango_parse_style (style, &pango_style, FALSE)) add_attribute (tag, pango_attr_style_new (pango_style)); else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("'%s' is not a valid value for the 'style' attribute " "on <span> tag, line %d; valid values are " "'normal', 'oblique', 'italic'"), style, line_number); goto error; } } if (G_UNLIKELY (weight)) { PangoWeight pango_weight; if (pango_parse_weight (weight, &pango_weight, FALSE)) add_attribute (tag, pango_attr_weight_new (pango_weight)); else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("'%s' is not a valid value for the 'weight' " "attribute on <span> tag, line %d; valid " "values are for example 'light', 'ultrabold' or a number"), weight, line_number); goto error; } } if (G_UNLIKELY (variant)) { PangoVariant pango_variant; if (pango_parse_variant (variant, &pango_variant, FALSE)) add_attribute (tag, pango_attr_variant_new (pango_variant)); else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("'%s' is not a valid value for the 'variant' " "attribute on <span> tag, line %d; valid values are " "'normal', 'smallcaps'"), variant, line_number); goto error; } } if (G_UNLIKELY (stretch)) { PangoStretch pango_stretch; if (pango_parse_stretch (stretch, &pango_stretch, FALSE)) add_attribute (tag, pango_attr_stretch_new (pango_stretch)); else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, _("'%s' is not a valid value for the 'stretch' " "attribute on <span> tag, line %d; valid " "values are for example 'condensed', " "'ultraexpanded', 'normal'"), stretch, line_number); goto error; } } if (G_UNLIKELY (foreground)) { PangoColor color; if (!span_parse_color ("foreground", foreground, &color, line_number, error)) goto error; add_attribute (tag, pango_attr_foreground_new (color.red, color.green, color.blue)); } if (G_UNLIKELY (background)) { PangoColor color; if (!span_parse_color ("background", background, &color, line_number, error)) goto error; add_attribute (tag, pango_attr_background_new (color.red, color.green, color.blue)); } if (G_UNLIKELY (underline)) { PangoUnderline ul = PANGO_UNDERLINE_NONE; if (!span_parse_enum ("underline", underline, PANGO_TYPE_UNDERLINE, &ul, line_number, error)) goto error; add_attribute (tag, pango_attr_underline_new (ul)); } if (G_UNLIKELY (underline_color)) { PangoColor color; if (!span_parse_color ("underline_color", underline_color, &color, line_number, error)) goto error; add_attribute (tag, pango_attr_underline_color_new (color.red, color.green, color.blue)); } if (G_UNLIKELY (gravity)) { PangoGravity gr = PANGO_GRAVITY_SOUTH; if (!span_parse_enum ("gravity", gravity, PANGO_TYPE_GRAVITY, &gr, line_number, error)) goto error; add_attribute (tag, pango_attr_gravity_new (gr)); } if (G_UNLIKELY (gravity_hint)) { PangoGravityHint hint = PANGO_GRAVITY_HINT_NATURAL; if (!span_parse_enum ("gravity_hint", gravity_hint, PANGO_TYPE_GRAVITY_HINT, &hint, line_number, error)) goto error; add_attribute (tag, pango_attr_gravity_hint_new (hint)); } if (G_UNLIKELY (strikethrough)) { gboolean b = FALSE; if (!span_parse_boolean ("strikethrough", strikethrough, &b, line_number, error)) goto error; add_attribute (tag, pango_attr_strikethrough_new (b)); } if (G_UNLIKELY (strikethrough_color)) { PangoColor color; if (!span_parse_color ("strikethrough_color", strikethrough_color, &color, line_number, error)) goto error; add_attribute (tag, pango_attr_strikethrough_color_new (color.red, color.green, color.blue)); } if (G_UNLIKELY (fallback)) { gboolean b = FALSE; if (!span_parse_boolean ("fallback", fallback, &b, line_number, error)) goto error; add_attribute (tag, pango_attr_fallback_new (b)); } if (G_UNLIKELY (rise)) { gint n = 0; if (!span_parse_int ("rise", rise, &n, line_number, error)) goto error; add_attribute (tag, pango_attr_rise_new (n)); } if (G_UNLIKELY (letter_spacing)) { gint n = 0; if (!span_parse_int ("letter_spacing", letter_spacing, &n, line_number, error)) goto error; add_attribute (tag, pango_attr_letter_spacing_new (n)); } if (G_UNLIKELY (lang)) { add_attribute (tag, pango_attr_language_new (pango_language_from_string (lang))); } return TRUE; error: return FALSE; }