Implement querying of localized values of desktop files
This commit is contained in:
		
							parent
							
								
									3e3c794a1e
								
							
						
					
					
						commit
						0b464d368c
					
				| @ -1,59 +1,62 @@ | ||||
| use locale; | ||||
| use strings; | ||||
| 
 | ||||
| // Represents a file of the generic key/value format used by desktop entries. | ||||
| // Specification: §3 | ||||
| type file = struct { | ||||
| 	preceeding_lines: []line; | ||||
| 	groups:           []group; | ||||
| export type file = struct { | ||||
| 	preceeding_lines: []line, | ||||
| 	groups:           []group, | ||||
| }; | ||||
| 
 | ||||
| // A named group of key/value entries. | ||||
| // Specification: §3.2 | ||||
| type group = struct { | ||||
| 	name:  str; | ||||
| 	lines: []line; | ||||
| } | ||||
| export type group = struct { | ||||
| 	name:  str, | ||||
| 	lines: []line, | ||||
| }; | ||||
| 
 | ||||
| // A line in the file, which can be a comment (or a blank line), an entry, or | ||||
| // a localized entry. | ||||
| type line = (comment | entry | localized_entry); | ||||
| export type line = (comment | entry | localized_entry); | ||||
| 
 | ||||
| // A comment. | ||||
| // Specification: §3.1 | ||||
| type comment = str; | ||||
| export type comment = str; | ||||
| 
 | ||||
| // A key/value pair. | ||||
| // Specification: §3.3 | ||||
| type entry = struct { | ||||
| 	key:   str; | ||||
| 	value: value; | ||||
| export type entry = struct { | ||||
| 	key:   str, | ||||
| 	value: value, | ||||
| }; | ||||
| 
 | ||||
| // A localized entry. | ||||
| // Specification: §5 | ||||
| type localized_entry struct { | ||||
| 	key:    str; | ||||
| 	value:  value; | ||||
| 	locale: locale::locale; | ||||
| export type localized_entry = struct { | ||||
| 	key:    str, | ||||
| 	value:  value, | ||||
| 	locale: locale::locale, | ||||
| }; | ||||
| 
 | ||||
| // An entry value. Values that reference memory (such as [[str]]) are borrowed | ||||
| // from the [[file]]. These may be free'd or overwritten when other functions in | ||||
| // this module are called, so [[value_dup]] is required to extend its lifetime. | ||||
| export type value = (str, bool, f32); | ||||
| export type value = (str | bool | f32); | ||||
| 
 | ||||
| // Gets a non-localized value from a file. If the group does not exist, or the | ||||
| // group exists but the key isn't in it, it returns void. | ||||
| export fn file_get(fil: *file, group_name: str, key: str) (value | void) { | ||||
| export fn file_get(fil: *file, group_name: str, key: str) (value | void) = { | ||||
| 	let grou = match (file_find_group(fil, group_name)) { | ||||
| 	case index: size => yield &file.groups[index]; | ||||
| 	case void =>        return void; | ||||
| 	case let index: size => yield &fil.groups[index]; | ||||
| 	case void            => return void; | ||||
| 	}; | ||||
| 	let lin = match (group_find_entry(grou, key)) { | ||||
| 	case index: size => yield &grou.lines[index]; | ||||
| 	case void        => return void; | ||||
| 	case let index: size => yield grou.lines[index]; | ||||
| 	case void            => return void; | ||||
| 	}; | ||||
| 	match (lin) { | ||||
| 	case entr: entry => return entr.value; | ||||
| 	case             => abort(); | ||||
| 	case let entr: entry => return entr.value; | ||||
| 	case                 => abort(); | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| @ -65,67 +68,131 @@ export fn file_get_localized( | ||||
| 	fil:        *file, | ||||
| 	group_name: str, | ||||
| 	key:        str, | ||||
| 	local:      locale, | ||||
| ) (value | void) { | ||||
| 	local:      locale::locale, | ||||
| ) (value | void) = { | ||||
| 	let grou = match (file_find_group(fil, group_name)) { | ||||
| 	case index: size => yield &file.groups[index]; | ||||
| 	case void =>        return void; | ||||
| 	case let index: size => yield &fil.groups[index]; | ||||
| 	case void            => return void; | ||||
| 	}; | ||||
| 	let lin = match (group_find_localized_entry(grou, key, local)) { | ||||
| 	case index: size => yield &grou.lines[index]; | ||||
| 	case void        => return void; | ||||
| 	case let index: size => yield grou.lines[index]; | ||||
| 	case void => | ||||
| 		yield match(group_find_entry(grou, key)) { | ||||
| 		case let index: size => yield index; | ||||
| 		case void            => return void; | ||||
| 		}; | ||||
| 	}; | ||||
| 	match (lin) { | ||||
| 	case entr: localized_entry => return entr.value; | ||||
| 	case entr: entry           => return entr.value; | ||||
| 	case                       => abort(); | ||||
| 	case let entr: localized_entry => return entr.value; | ||||
| 	case let entr: entry           => return entr.value; | ||||
| 	case                           => abort(); | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| export fn file_add | ||||
| //export fn file_add | ||||
| 
 | ||||
| export fn file_add_localized | ||||
| //export fn file_add_localized | ||||
| 
 | ||||
| export fn file_remove | ||||
| //export fn file_remove | ||||
| 
 | ||||
| export fn file_remove_localized | ||||
| //export fn file_remove_localized | ||||
| 
 | ||||
| export fn file_encode | ||||
| //export fn file_encode | ||||
| 
 | ||||
| export fn file_finish | ||||
| //export fn file_finish | ||||
| 
 | ||||
| fn file_find_group (fil *file, group_name str): (size | void) = { | ||||
| 	let index = 0; | ||||
| 	for (let grou .. file.groups) { | ||||
| 		if (strings::compare(group.name, group_name) == 0) { | ||||
| fn file_find_group(fil: *file, group_name: str) (size | void) = { | ||||
| 	let index = 0z; | ||||
| 	for (let grou .. fil.groups) { | ||||
| 		if (grou.name == group_name) { | ||||
| 			return index; | ||||
| 		} | ||||
| 		index ++; | ||||
| 	} | ||||
| } | ||||
| 		}; | ||||
| 		index += 1; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| fn group_find_entry (grou *group, key str): (size | void) { | ||||
| 	let index = 0; | ||||
| fn group_find_entry(grou: *group, key: str) (size | void) = { | ||||
| 	let index = 0z; | ||||
| 	for (let lin .. grou.lines) { | ||||
| 		match (lin) { | ||||
| 		case entr: entry => | ||||
| 			if (strings::compare(entry.key, entr) == 0) { | ||||
| 		case let entr: entry => | ||||
| 			if (entr.key == key) { | ||||
| 				return index; | ||||
| 			} | ||||
| 		case => void ; | ||||
| 		} | ||||
| 		index ++; | ||||
| 	} | ||||
| } | ||||
| 			}; | ||||
| 		case => void; | ||||
| 		}; | ||||
| 		index += 1; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| fn group_find_localized_entry(grou: *group, key: str, local: locale::locale) (size | void) = { | ||||
| 	// The matching is done as follows. If LC_MESSAGES is of the form | ||||
| 	// lang_COUNTRY.ENCODING@MODIFIER, then it will match a key of the form | ||||
| 	// lang_COUNTRY@MODIFIER. If such a key does not exist, it will attempt | ||||
| 	// to match lang_COUNTRY followed by lang@MODIFIER. Then, a match | ||||
| 	// against lang by itself will be attempted. Finally, if no matching | ||||
| 	// key is found the required key without a locale specified is used. | ||||
| 	// The encoding from the LC_MESSAGES value is ignored when matching. | ||||
| 	//  | ||||
| 	// If LC_MESSAGES does not have a MODIFIER field, then no key with a | ||||
| 	// modifier will be matched. Similarly, if LC_MESSAGES does not have a | ||||
| 	// COUNTRY field, then no key with a country specified will be matched. | ||||
| 	// If LC_MESSAGES just has a lang field, then it will do a straight | ||||
| 	// match to a key with a similar value. | ||||
| 
 | ||||
| 	let lang_country_modifier = local; | ||||
| 	lang_country_modifier.encoding = ""; | ||||
| 	match(group_find_localized_entry_exact(grou, key, lang_country_modifier)) { | ||||
| 	case let index: size => return index; | ||||
| 	case                 =>	void; | ||||
| 	}; | ||||
| 	 | ||||
| 	let lang_country = lang_country_modifier; | ||||
| 	lang_country.modifier = ""; | ||||
| 	match(group_find_localized_entry_exact(grou, key, lang_country)) { | ||||
| 	case let index: size => return index; | ||||
| 	case                 =>	void; | ||||
| 	}; | ||||
| 	 | ||||
| 	let lang_modifier = lang_country_modifier; | ||||
| 	lang_modifier.country = ""; | ||||
| 	match(group_find_localized_entry_exact(grou, key, lang_modifier)) { | ||||
| 	case let index: size => return index; | ||||
| 	case                 =>	void; | ||||
| 	}; | ||||
| 
 | ||||
| 	let lang = lang_modifier; | ||||
| 	lang.modifier = ""; | ||||
| 	match(group_find_localized_entry_exact(grou, key, lang)) { | ||||
| 	case let index: size => return index; | ||||
| 	case                 =>	void; | ||||
| 	}; | ||||
| 	 | ||||
| 	return void; | ||||
| }; | ||||
| 
 | ||||
| fn group_find_localized_entry_exact(grou: *group, key: str, local: locale::locale) (size | void) = { | ||||
| 	let index = 0z; | ||||
| 	for (let lin .. grou.lines) { | ||||
| 		match (lin) { | ||||
| 		case let entr: localized_entry => | ||||
| 			if (entr.key == key && locale::equal(entr.locale, local)) { | ||||
| 				return index; | ||||
| 			}; | ||||
| 		case => void; | ||||
| 		}; | ||||
| 		index += 1; | ||||
| 	}; | ||||
| }; | ||||
| 
 | ||||
| // Duplicates a [[value]]. Use [[value_finish]] to get rid of it. | ||||
| export fn value_dup(valu: value): value = match (valu) { | ||||
| case let valu: str => yield strings.dup(valu); | ||||
| export fn value_dup(valu: value) value = match (valu) { | ||||
| case let valu: str => yield strings::dup(valu); | ||||
| case               => yield valu; | ||||
| }; | ||||
| 
 | ||||
| // Frees an [[entry]] previously duplicated with [[entry_dup]]. | ||||
| export fn value_finish(valu: value): void = match (valu) { | ||||
| export fn value_finish(valu: value) void = match (valu) { | ||||
| case let valu: str => free(valu); | ||||
| case               => void; | ||||
| }; | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user