/* * We want a reentrant scanner. */ %option reentrant /* * We don't use input, so don't generate code for it. */ %option noinput /* * We don't use unput, so don't generate code for it. */ %option nounput /* * We don't read interactively from the terminal. */ %option never-interactive /* * We want to stop processing when we get to the end of the input. */ %option noyywrap /* * The language we're scanning is case-insensitive. */ %option caseless /* * The type for the state we keep for a scanner. */ %option extra-type="Radius_scanner_state_t*" /* * We have to override the memory allocators so that we don't get * "unused argument" warnings from the yyscanner argument (which * we don't use, as we have a global memory allocator). * * We provide, as macros, our own versions of the routines generated by Flex, * which just call malloc()/realloc()/free() (as the Flex versions do), * discarding the extra argument. */ %option noyyalloc %option noyyrealloc %option noyyfree /* * Prefix scanner routines with "Radius_" rather than "yy", so this scanner * can coexist with other scanners. */ %option prefix="Radius_" %option outfile="radius_dict.c" %{ /* radius_dict.l * * RADIUS dictionary parser * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * 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. * * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include #include #include #include #include #include #define YY_USER_INIT BEGIN WS_OUT; #ifdef _WIN32 /* disable Windows VC compiler warning "signed/unsigned mismatch" associated */ /* with YY_INPUT code generated by flex versions such as 2.5.35. */ #pragma warning (disable:4018) #endif #define ECHO #define MAX_INCLUDE_DEPTH 10 typedef struct { YY_BUFFER_STATE include_stack[MAX_INCLUDE_DEPTH]; int include_stack_ptr; radius_dictionary_t* dict; GHashTable* value_strings; /* GArray(value_string) by attribute name */ gchar* attr_name; gchar* attr_id; radius_attr_dissector_t* attr_type; gchar* attr_vendor; gchar* vendor_name; guint32 vendor_id; guint vendor_type_octets; guint vendor_length_octets; gboolean vendor_has_flags; gchar* value_repr; guint encrypted; gboolean has_tag; gchar* current_vendor; gchar* current_attr; gchar* directory; gchar* fullpaths[MAX_INCLUDE_DEPTH]; int linenums[MAX_INCLUDE_DEPTH]; GString* error; } Radius_scanner_state_t; static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags); static gboolean add_attribute(Radius_scanner_state_t* state, const gchar*,const gchar*, radius_attr_dissector_t,const gchar*, guint, gboolean, const gchar*); static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const gchar* code, radius_attr_dissector_t type, const gchar* attr); static void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const gchar* repr, guint32 value); /* * Sleazy hack to suppress compiler warnings in yy_fatal_error(). */ #define YY_EXIT_FAILURE ((void)yyscanner, 2) /* * Macros for the allocators, to discard the extra argument. */ #define Radius_alloc(size, yyscanner) (void *)malloc(size) #define Radius_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size)) #define Radius_free(ptr, yyscanner) free((char *)ptr) %} /* Note: FreeRadius allows VENDOR, ATTRIBUTE and VALUE names to contain any non-blank character. * Using a negated "blank character class" pattern below for those names fails for some reason * so for now the patterns for each name type include those characters found for the corresponding * name types in the FreeRadius dictionaries. */ %START WS_OUT VENDOR VENDOR_W_NAME ATTR ATTR_W_NAME ATTR_W_ID ATTR_W_TYPE ATTR_W_VENDOR VALUE VALUE_W_ATTR VALUE_W_NAME INCLUDE JUNK BEGIN_VENDOR END_VENDOR VENDOR_W_ID VENDOR_W_FORMAT VENDOR_W_TYPE_OCTETS VENDOR_W_LENGTH_OCTETS VENDOR_W_CONTINUATION BEGIN_TLV END_TLV %% [:blank:] ; #[^\n]* ; .*\qn ; VENDOR { BEGIN VENDOR; } ATTRIBUTE { BEGIN ATTR; } VALUE { BEGIN VALUE; } \$INCLUDE { BEGIN INCLUDE; } BEGIN-VENDOR { BEGIN BEGIN_VENDOR; } END-VENDOR { BEGIN END_VENDOR; } BEGIN-TLV { BEGIN BEGIN_TLV; } END-TLV { BEGIN END_TLV; } [0-9a-z_-]+ { if (yyextra->current_vendor) { g_free(yyextra->current_vendor); } yyextra->current_vendor = g_strdup(yytext); BEGIN WS_OUT; } [^\n]* { if (yyextra->current_vendor) { g_free(yyextra->current_vendor); yyextra->current_vendor = NULL; } BEGIN WS_OUT; } [0-9a-z_-]+ { if (yyextra->current_attr) { g_free(yyextra->current_attr); } yyextra->current_attr = g_strdup(yytext); BEGIN WS_OUT; } [^\n]* { if (yyextra->current_attr) { g_free(yyextra->current_attr); yyextra->current_attr = NULL; } BEGIN WS_OUT; } [0-9a-z_-]+ { yyextra->vendor_name = g_strdup(yytext); yyextra->vendor_type_octets = 1; yyextra->vendor_length_octets = 1; yyextra->vendor_has_flags = FALSE; BEGIN VENDOR_W_NAME; } [0-9]+ { yyextra->vendor_id = (guint32) strtoul(yytext,NULL,10); BEGIN VENDOR_W_ID; } 0x[0-9a-f]+ { yyextra->vendor_id = (guint32) strtoul(yytext,NULL,16); BEGIN VENDOR_W_ID; } format= { BEGIN VENDOR_W_FORMAT; } [124] { yyextra->vendor_type_octets = (guint) strtoul(yytext,NULL,10); BEGIN VENDOR_W_TYPE_OCTETS; } ,[012] { yyextra->vendor_length_octets = (guint) strtoul(yytext+1,NULL,10); BEGIN VENDOR_W_LENGTH_OCTETS; } ,c { yyextra->vendor_has_flags = TRUE; BEGIN VENDOR_W_CONTINUATION; } \n | \n | \n | \n | \n { add_vendor(yyextra, yyextra->vendor_name, yyextra->vendor_id, yyextra->vendor_type_octets, yyextra->vendor_length_octets, yyextra->vendor_has_flags); g_free(yyextra->vendor_name); BEGIN WS_OUT; } [0-9a-z_/.-]+ { yyextra->attr_name = g_strdup(yytext); yyextra->encrypted = 0; yyextra->has_tag = FALSE; BEGIN ATTR_W_NAME; } [0-9]+ { yyextra->attr_id = g_strdup(yytext); BEGIN ATTR_W_ID;} 0x[0-9a-f]+ { yyextra->attr_id = g_strdup_printf("%u",(int)strtoul(yytext,NULL,16)); BEGIN ATTR_W_ID;} integer { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; } string { yyextra->attr_type = radius_string; BEGIN ATTR_W_TYPE; } octets { yyextra->attr_type = radius_octets; BEGIN ATTR_W_TYPE; } ipaddr { yyextra->attr_type = radius_ipaddr; BEGIN ATTR_W_TYPE; } ipv6addr { yyextra->attr_type = radius_ipv6addr; BEGIN ATTR_W_TYPE; } ipv6prefix { yyextra->attr_type = radius_ipv6prefix; BEGIN ATTR_W_TYPE; } ipxnet { yyextra->attr_type = radius_ipxnet; BEGIN ATTR_W_TYPE; } date { yyextra->attr_type = radius_date; BEGIN ATTR_W_TYPE; } abinary { yyextra->attr_type = radius_abinary; BEGIN ATTR_W_TYPE; } ether { yyextra->attr_type = radius_ether; BEGIN ATTR_W_TYPE; } ifid { yyextra->attr_type = radius_ifid; BEGIN ATTR_W_TYPE; } byte { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; } short { yyextra->attr_type = radius_integer; BEGIN ATTR_W_TYPE; } signed { yyextra->attr_type = radius_signed; BEGIN ATTR_W_TYPE; } combo-ip { yyextra->attr_type = radius_combo_ip; BEGIN ATTR_W_TYPE; } tlv { yyextra->attr_type = radius_tlv; BEGIN ATTR_W_TYPE; } [0-9a-z_-]+ { yyextra->attr_type = radius_octets; BEGIN ATTR_W_TYPE; } has_tag[,]? { yyextra->has_tag = TRUE; } encrypt=[123][,]? { yyextra->encrypted = (guint) strtoul(yytext+8,NULL,10); } [0-9a-z_-]+=([^\n]*) ; [0-9a-z_-]+ { gboolean attribute_ok; yyextra->attr_vendor = g_strdup(yytext); attribute_ok = add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->attr_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr); g_free(yyextra->attr_id); g_free(yyextra->attr_vendor); g_free(yyextra->attr_name); yyextra->attr_id = NULL; yyextra->attr_vendor = NULL; yyextra->attr_name = NULL; if (attribute_ok) BEGIN WS_OUT; else BEGIN JUNK; } \n { add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->current_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr); g_free(yyextra->attr_id); g_free(yyextra->attr_name); yyextra->linenums[yyextra->include_stack_ptr]++; yyextra->has_tag = FALSE; yyextra->encrypted=FALSE; BEGIN WS_OUT; } \n { add_attribute(yyextra, yyextra->attr_name, yyextra->attr_id, yyextra->attr_type, yyextra->attr_vendor, yyextra->encrypted, yyextra->has_tag, yyextra->current_attr); g_free(yyextra->attr_id); g_free(yyextra->attr_vendor); g_free(yyextra->attr_name); yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; }; [0-9a-z_/-]+ { yyextra->attr_name = g_strdup(yytext); BEGIN VALUE_W_ATTR; } [^[:blank:]]+ { yyextra->value_repr = g_strdup(yytext); BEGIN VALUE_W_NAME; } [0-9]+ { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (guint32) strtoul(yytext,NULL,10)); g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;} 0x[0-9a-f]+ { add_value(yyextra, yyextra->attr_name,yyextra->value_repr, (guint32) strtoul(yytext,NULL,16)); g_free(yyextra->attr_name); g_free(yyextra->value_repr); BEGIN WS_OUT;} [^[:blank:]\n]+ { if ( yyextra->include_stack_ptr >= MAX_INCLUDE_DEPTH ) { g_string_append_printf(yyextra->error, "$INCLUDE files nested too deeply\n"); yyterminate(); } yyextra->include_stack[yyextra->include_stack_ptr++] = YY_CURRENT_BUFFER; yyextra->fullpaths[yyextra->include_stack_ptr] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", yyextra->directory,yytext); yyin = ws_fopen( yyextra->fullpaths[yyextra->include_stack_ptr], "r" ); if (!yyin) { if (errno) { g_string_append_printf(yyextra->error, "Could not open file: '%s', error: %s\n", yyextra->fullpaths[yyextra->include_stack_ptr], g_strerror(errno) ); } else { g_string_append_printf(yyextra->error, "Could not open file: '%s', no errno\n", yyextra->fullpaths[yyextra->include_stack_ptr]); } g_free(yyextra->fullpaths[yyextra->include_stack_ptr]); yyextra->fullpaths[yyextra->include_stack_ptr] = NULL; yyextra->include_stack_ptr--; } else { yyextra->linenums[yyextra->include_stack_ptr] = 1; yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE, yyscanner), yyscanner); } BEGIN WS_OUT; } <> { fclose(yyin); yyin = NULL; if ( --yyextra->include_stack_ptr < 0 ) { yyterminate(); } else { g_free(yyextra->fullpaths[yyextra->include_stack_ptr+1]); yyextra->fullpaths[yyextra->include_stack_ptr+1] = NULL; Radius__delete_buffer(YY_CURRENT_BUFFER, yyscanner); Radius__switch_to_buffer(yyextra->include_stack[yyextra->include_stack_ptr], yyscanner); } BEGIN WS_OUT; } \n { yyextra->linenums[yyextra->include_stack_ptr]++; BEGIN WS_OUT; } %% static void add_vendor(Radius_scanner_state_t* state, const gchar* name, guint32 id, guint type_octets, guint length_octets, gboolean has_flags) { radius_vendor_info_t* v; v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_id, GUINT_TO_POINTER(id)); if (!v) { /* * New vendor. * Allocate a new entry and insert it into the by-ID and * by-name hash tables. */ v = g_new(radius_vendor_info_t,1); v->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal); v->code = id; v->ett = -1; v->name = g_strdup(name); v->type_octets = type_octets; v->length_octets = length_octets; v->has_flags = has_flags; g_hash_table_insert(state->dict->vendors_by_id,GUINT_TO_POINTER(v->code),v); g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v); } else { /* * This vendor is already in the table. * * Assume that the dictionary knows the 'ground truth' about * the type/length/has_flags information and thus allow the * dictionary to overwrite these values even for vendors that * have already been loaded. * * XXX - this could be due to the vendor being in multiple * dictionary files, rather than having been specially * entered by the RADIUS dissector, as a side effect of * specially entering an attribute; should we report vendors * that appear in different dictionaries with different * properties? */ v->type_octets = type_octets; v->length_octets = length_octets; v->has_flags = has_flags; /* * Did the name change? */ if (g_strcmp0(v->name, name) != 0) { /* * Yes. Remove the entry from the by-name hash table * and re-insert it with the new name. */ g_hash_table_remove(state->dict->vendors_by_name, (gpointer) v->name); g_free((gpointer) v->name); v->name = g_strdup(name); g_hash_table_insert(state->dict->vendors_by_name, (gpointer) v->name, v); } } } static gboolean add_attribute(Radius_scanner_state_t* state, const gchar* name, const gchar* codestr, radius_attr_dissector_t type, const gchar* vendor, guint encrypted_flag, gboolean tagged, const gchar* attr) { radius_attr_info_t* a; GHashTable* by_id; guint32 code; if (attr){ return add_tlv(state, name, codestr, type, attr); } if (vendor) { radius_vendor_info_t* v; v = (radius_vendor_info_t *)g_hash_table_lookup(state->dict->vendors_by_name,vendor); if (! v) { g_string_append_printf(state->error, "Vendor: '%s', does not exist in %s:%i \n", vendor, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr] ); return FALSE; } else { by_id = v->attrs_by_id; } } else { by_id = state->dict->attrs_by_id; } code= (guint32) strtoul(codestr, NULL, 10); a=(radius_attr_info_t*)g_hash_table_lookup(by_id, GUINT_TO_POINTER(code)); if (!a) { /* * New attribute. * Allocate a new entry and insert it into the by-ID and * by-name hash tables. */ a = g_new(radius_attr_info_t,1); a->code = code; a->name = g_strdup(name); a->dissector = NULL; a->encrypt = encrypted_flag; a->tagged = tagged; a->type = type; a->vs = NULL; a->hf = -1; a->hf_alt = -1; a->hf_tag = -1; a->hf_len = -1; a->ett = -1; a->tlvs_by_id = NULL; g_hash_table_insert(by_id, GUINT_TO_POINTER(code),a); g_hash_table_insert(state->dict->attrs_by_name,(gpointer) (a->name),a); } else { /* * This attribute is already in the table. * * Overwrite the encrypted flag, tagged property, and type; * the other properties don't get set until after we've * finished reading the dictionaries. * * XXX - this could be due to the attribute being in * multiple dictionary files, rather than having been * specially entered by the RADIUS dissector to give it * a special dissection routine; should we report attributes * that appear in different dictionaries with different * properties? */ a->encrypt = encrypted_flag; a->tagged = tagged; a->type = type; /* * Did the name change? */ if (g_strcmp0(a->name, name) != 0) { /* * Yes. Remove the entry from the by-name hash table * and re-insert it with the new name. */ g_hash_table_remove(state->dict->attrs_by_name, (gpointer) (a->name)); g_free((gpointer) a->name); a->name = g_strdup(name); g_hash_table_insert(state->dict->attrs_by_name, (gpointer) (a->name),a); } } return TRUE; } static gboolean add_tlv(Radius_scanner_state_t* state, const gchar* name, const gchar* codestr, radius_attr_dissector_t type, const gchar* attr) { radius_attr_info_t* a; radius_attr_info_t* s; guint32 code; a = (radius_attr_info_t*)g_hash_table_lookup(state->dict->attrs_by_name, attr); if (! a) { g_string_append_printf(state->error, "Attr: '%s', does not exist in %s:%i \n", attr, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr]); return FALSE; } if (type == radius_tlv) { g_string_append_printf(state->error, "sub-TLV: '%s', sub-TLV's type is specified as tlv in %s:%i \n", name, state->fullpaths[state->include_stack_ptr], state->linenums[state->include_stack_ptr]); return FALSE; } if (! a->tlvs_by_id) { a->tlvs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal); } code = (guint32) strtoul(codestr, NULL, 10); s = (radius_attr_info_t*)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(code)); if (!s) { /* * This TLV doesn't yet exist in this attribute's TLVs-by-ID * hash table. Add it. */ s = g_new(radius_attr_info_t,1); s->name = g_strdup(name); s->dissector = NULL; s->code = code; s->type = type; s->encrypt = FALSE; s->tagged = FALSE; s->dissector = NULL; s->vs = NULL; s->hf = -1; s->hf_alt = -1; s->hf_tag = -1; s->hf_len = -1; s->ett = -1; s->tlvs_by_id = NULL; g_hash_table_insert(a->tlvs_by_id,GUINT_TO_POINTER(s->code),s); g_hash_table_insert(state->dict->tlvs_by_name,(gpointer) (s->name),s); } /* * If it *does* exist, leave it alone; there shouldn't be duplicate * entries by name in the dictionaries (even if there might be * multiple entries for a given attribute in the dictionaries, each * one adding some TLV values), and we don't directly add entries * for TLVs in the RADIUS dissector. * * XXX - report the duplicate entries? */ return TRUE; } void add_value(Radius_scanner_state_t* state, const gchar* attrib_name, const gchar* repr, guint32 value) { value_string v; GArray* a = (GArray*)g_hash_table_lookup(state->value_strings,attrib_name); if (! a) { a = g_array_new(TRUE,TRUE,sizeof(value_string)); g_hash_table_insert(state->value_strings,g_strdup(attrib_name),a); } v.value = value; v.strptr = g_strdup(repr); g_array_append_val(a,v); } static void setup_tlvs(gpointer k _U_, gpointer v, gpointer p) { radius_attr_info_t* s = (radius_attr_info_t*)v; Radius_scanner_state_t* state = (Radius_scanner_state_t*)p; gpointer key; union { GArray* a; gpointer p; } vs; if (g_hash_table_lookup_extended(state->value_strings, s->name, &key, &vs.p)) { s->vs = (value_string*)(void *)vs.a->data; g_array_free(vs.a, FALSE); g_hash_table_remove(state->value_strings, key); g_free(key); } } static void setup_attrs(gpointer k _U_, gpointer v, gpointer p) { radius_attr_info_t* a = (radius_attr_info_t*)v; Radius_scanner_state_t* state = (Radius_scanner_state_t*)p; gpointer key; union { GArray* a; gpointer p; } vs; if (g_hash_table_lookup_extended(state->value_strings,a->name,&key,&vs.p) ) { a->vs = (value_string*)(void *)vs.a->data; g_array_free(vs.a,FALSE); g_hash_table_remove(state->value_strings,key); g_free(key); } if (a->tlvs_by_id) { g_hash_table_foreach(a->tlvs_by_id, setup_tlvs, p); } } static void setup_vendors(gpointer k _U_, gpointer v, gpointer p) { radius_vendor_info_t* vnd = (radius_vendor_info_t*)v; g_hash_table_foreach(vnd->attrs_by_id,setup_attrs,p); } static gboolean destroy_value_strings(gpointer k, gpointer v, gpointer p _U_) { value_string* vs = (value_string*)(void *)(((GArray*)v)->data); g_free(k); for (;vs->strptr;vs++) { g_free((void*)vs->strptr); } g_array_free((GArray*)v,TRUE); return TRUE; } gboolean radius_load_dictionary (radius_dictionary_t* d, gchar* dir, const gchar* filename, gchar** err_str) { FILE *in; yyscan_t scanner; Radius_scanner_state_t state; int i; state.include_stack_ptr = 0; state.dict = d; state.value_strings = NULL; state.attr_name = NULL; state.attr_id = NULL; state.attr_type = NULL; state.attr_vendor = NULL; state.vendor_name = NULL; state.vendor_id = 0; state.vendor_type_octets = 1; state.vendor_length_octets = 1; state.vendor_has_flags = FALSE; state.value_repr = NULL; state.encrypted = 0; state.has_tag = FALSE; state.current_vendor = NULL; state.current_attr = NULL; state.directory = dir; state.fullpaths[0] = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s", state.directory,filename); state.linenums[0] = 1; for (i = 1; i < MAX_INCLUDE_DEPTH; i++) { state.fullpaths[i] = NULL; state.linenums[i] = 1; } state.error = g_string_new(""); in = ws_fopen(state.fullpaths[0],"r"); if (!in) { g_string_append_printf(state.error, "Could not open file: '%s', error: %s\n", state.fullpaths[0], g_strerror(errno)); g_free(state.fullpaths[0]); *err_str = g_string_free(state.error,FALSE); return FALSE; } state.value_strings = g_hash_table_new(g_str_hash,g_str_equal); if (Radius_lex_init(&scanner) != 0) { g_string_append_printf(state.error, "Can't initialize scanner: %s", strerror(errno)); fclose(in); g_free(state.fullpaths[0]); *err_str = g_string_free(state.error,FALSE); return FALSE; } Radius_set_in(in, scanner); /* Associate the state with the scanner */ Radius_set_extra(&state, scanner); Radius_lex(scanner); Radius_lex_destroy(scanner); /* * XXX - can the lexical analyzer terminate without closing * all open input files? */ for (i = 0; i < MAX_INCLUDE_DEPTH; i++) { if (state.fullpaths[i]) g_free(state.fullpaths[i]); } g_hash_table_foreach(state.dict->attrs_by_id,setup_attrs,&state); g_hash_table_foreach(state.dict->vendors_by_id,setup_vendors,&state); g_hash_table_foreach_remove(state.value_strings,destroy_value_strings,NULL); if (state.error->len > 0) { *err_str = g_string_free(state.error,FALSE); return FALSE; } else { *err_str = NULL; g_string_free(state.error,TRUE); return TRUE; } } /* * Editor modelines - http://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */