Clone in a terminal:
BirdFont.vala
/*
	Copyright (C) 2012 2014 2015 Johan Mattsson

	This library is free software; you can redistribute it and/or modify
	it under the terms of the GNU Lesser General Public License as
	published by the Free Software Foundation; either version 3 of the
	License, or (at your option) any later version.

	This library 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
	Lesser General Public License for more details.
*/
using BirdFont;

public const string GETTEXT_PACKAGE = "birdfont";

namespace BirdFont {

public static string? settings_directory = null;

internal static string build_absoulute_path (string file_name) {
	File f = File.new_for_path (file_name);
	return (!) f.get_path ();
}

public static string get_version () {
	return VERSION;
}

public static string get_os () {
#if MAC
	return "mac";
#else
	if (BirdFont.win32) {
		return "windows";
	} else {
		return "*nix";
	}
#endif
}

public static void set_logging (bool log) {
	BirdFont.logging = log;
}

public static string wine_to_unix_path (string exec_path) {
	bool drive_c, drive_z;
	int i;
	string p, q;

	p = exec_path;
	p = p.replace ("\\", "/");

	drive_c = exec_path.index_of ("C:") == 0;
	drive_z = exec_path.index_of ("Z:") == 0;

	i = p.index_of (":");

	if (i != -1) {
		p = p.substring (i + 2);
	}

	if (drive_c) {
		q = @"/home/$(Environment.get_user_name ())/.wine/drive_c/" + p;

		if (File.new_for_path (q).query_exists ()) {
			return q;
		} else {
			return p;
		}
	}

	if (drive_z) {
		return ("/" + p).dup ();
	}

	return exec_path.dup ();
}

public bool is_null (void* n) {
	return n == null;
}

public bool has_flag (uint32 flag, uint32 mask) {
	return (flag & mask) > 0;
}

public class BirdFont {
	public static Argument args;
	public static bool experimental = false;
	public static bool show_coordinates = false;
	public static bool fatal_wanings = false;
	public static bool win32 = false;
	public static bool mac = false;
	public static bool android = false;
	public static string exec_path = "";
	public static string? bundle_path = null;

	internal static bool logging = false;
	public static DataOutputStream? logstream = null;

	public static Font current_font;
	public static GlyphCollection current_glyph_collection;

	public static string? settings_subdirectory = null;
	public static string? sandbox_directory = null;

	public BirdFont () {
	}

	/**
	 * @param arg command line arguments
	 * @param program path
	 * @param setting subdirectory
	 */
	public void init (string[] arg, string? program_path, string? settings_subdir, string? sandbox_path) {
		int i;
		File font_file;
		string exec_path;
		string theme;
		int default_theme_version;
		string theme_version;
		CharDatabaseParser parser;
		CodePageBits codepage_bits;

		set_settings_subdir (settings_subdir);
		sandbox_directory = sandbox_path;
		
		args = new Argument.command_line (arg);
		Font.empty = new Font ();

#if ANDROID
		BirdFont.logging = true;

		__android_log_print (ANDROID_LOG_WARN, "BirdFont", @"libbirdfont version $VERSION");
		LogLevelFlags log_levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
		Log.set_handler (null, log_levels, android_warning);

		android = true;
#else
		stdout.printf ("birdfont version %s\n", VERSION);

		android = args.has_argument ("--android");

		if (!BirdFont.logging) {
			BirdFont.logging = args.has_argument ("--log");
		}
#endif

		if (BirdFont.logging) {
			init_logfile ();
		}

		if (!args.has_argument ("--no-translation")) {
			init_gettext ();
		}

		if (args.has_argument ("--help")) {
			args.print_help ();
			Process.exit (0);
		}

#if !MAC
		int err_arg = args.validate ();
		if (err_arg != 0) {
			stdout.printf (@"Unknown parameter $(arg [err_arg])\n\n");
			args.print_help ();
			Process.exit (0);
		}
#endif
		Preferences.load ();

		// always load default theme when names in theme does change
		default_theme_version = 1;
		theme = Preferences.get ("theme");
		theme_version = Preferences.get ("theme_version");

		Theme.set_default_colors ();

		if (theme_version == "" || int.parse (theme_version) < default_theme_version) {
			Theme.load_theme ("dark.theme");
			Preferences.set ("theme", "dark.theme");
		} else {
			if (theme != "") {
				Theme.load_theme (theme);
			} else {
				Theme.load_theme ("dark.theme");
			}
		}

		Preferences.set ("theme_version", @"$default_theme_version");

		current_font = new Font ();
		current_font.set_name ("");
		current_font.initialised = false;
		current_glyph_collection = new GlyphCollection.with_glyph ('\0', "");

		experimental = args.has_argument ("--test");
		show_coordinates = args.has_argument ("--show-coordinates") || experimental;
		fatal_wanings = args.has_argument ("--fatal-warning");
		win32 = (arg[0].index_of (".exe") > -1)
			|| arg[0] == "wine"
			|| args.has_argument ("--windows");

#if MAC
		mac = true;
#else
		mac = args.has_argument ("--mac");
#endif

		if (program_path == null) {
			exec_path = "";

			if (win32) {
				// wine hack to get "." folder in win32 environment
				i = arg[0].last_index_of ("\\");

				if (i != -1) {
					exec_path = arg[0];
					exec_path = exec_path.substring (0, i);
					exec_path = wine_to_unix_path (exec_path);
				}
			} else {
				exec_path = "./";
			}
		} else {
			exec_path = (!) program_path;
		}

		if (args.get_file () != "") {
			font_file = File.new_for_path (args.get_file ());

			if (!font_file.query_exists ()) {
				stderr.printf (@"The file \"$(args.get_file ())\" was not found.\n");
				Process.exit (-1);
			}
		}

		if (fatal_wanings) {
			LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING;
			Log.set_handler (null, levels, fatal_warning);
		}

		Preferences.set_last_file (get_current_font ().get_path ());

		DefaultCharacterSet.create_default_character_sets ();
		DefaultCharacterSet.get_characters_for_prefered_language ();

		HeadTable.init (1024);

		if (TestBirdFont.get_singleton ().test_cases_to_run != "All") {
			TestBirdFont.run_tests ();
		}

		if (has_argument ("--parse-ucd")) {
			parser = new CharDatabaseParser ();
			parser.regenerate_database ();
		}

		if (has_argument ("--codepages")) {
			codepage_bits = new CodePageBits ();
			codepage_bits.generate_codepage_database ();
		}
		
		ExportTool.generate_html_template ();
	}

	public static bool has_logging () {
		bool log;

		lock (BirdFont.logging) {
			log = BirdFont.logging;
		}

		return log;
	}

	public static Argument get_arguments () {
		return args;
	}

	public static void set_bundle_path (string path) {
		bundle_path = path;
	}

	public static void init_gettext () {
		// FIXME: android, this should be OK now
#if !ANDROID
		string locale_directory = SearchPaths.get_locale_directory ();
		Intl.setlocale (LocaleCategory.MESSAGES, "");
		Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "utf-8");
		Intl.bindtextdomain (GETTEXT_PACKAGE, locale_directory);
#endif
	}

	public  void load_font_from_command_line () {
		string file = args.get_file ();
		if (file != "") {
			RecentFiles.load_font (file);
		}
	}

	public static Font get_current_font () {
		return current_font;
	}

	internal static void fatal_warning (string? log_domain, LogLevelFlags log_levels, string message) {
		bool fatal = true;

		if (log_domain != null) {
			stderr.printf ("%s: \n", (!) log_domain);
		}

		stderr.printf ("\n%s\n\n", message);
		assert (!fatal);
	}

#if ANDROID
	internal static void android_warning (string? log_domain, LogLevelFlags log_levels, string message) {
		__android_log_print (ANDROID_LOG_WARN, "BirdFont", message);
	}
#endif

	public static Font new_font () {
		current_font = new Font ();

		if (!is_null (MainWindow.tools)) {
			MainWindow.get_drawing_tools ().remove_all_grid_buttons ();
			DrawingTools.add_new_grid (1, false);
			DrawingTools.add_new_grid (2, false);
			DrawingTools.add_new_grid (4, false);
		}

		if (!is_null (Toolbox.background_tools)) {
			Toolbox.background_tools.remove_images ();
		}

		KerningTools.update_kerning_classes ();

		return current_font;
	}

	public static string? get_sandbox_directory () {
		return sandbox_directory;
	}

	public static void set_settings_directory (string directory) {
		settings_subdirectory = directory;
	}

	public static File get_preview_directory () {
		string? export = BirdFont.get_current_font ().get_export_directory ();
		
		if (export == null) {
			warning ("No export directory is set.");
			export = "";
		}

		File e = File.new_for_path ((!) export);
		File p = get_child(e, "preview");
		
		return p;
	}

	public static void set_settings_subdir (string? subdir) {
		settings_subdirectory = subdir;
	}

	internal static File get_settings_directory () {
		string home_path;
		File home;
		File settings;

#if ANDROID
		home_path = "/data/data/org.birdfont.sefyr/files";
		home = File.new_for_path (home_path);

		if (!home.query_exists ()) {
			printd ("Create settings directory.");
			DirUtils.create ((!) home.get_path (),0755);
		}
#else
	if (sandbox_directory != null) {
		home = File.new_for_path ((!) sandbox_directory);
	} else {
		home_path = (settings_directory != null)
			? (!) settings_directory : Environment.get_user_config_dir ();

		if (is_null (home_path)) {
			warning ("No home directory set.");
			home_path = ".";
		}

		home = File.new_for_path (home_path);
	}
#endif

		if (settings_subdirectory != null) {
			settings = get_child(home, (!) settings_subdirectory);
		} else {
			settings = get_child(home, "birdfont");
		}

		if (!settings.query_exists ()) {
			DirUtils.create ((!) settings.get_path (), 0755);
		}

		return settings;
	}

	public static bool has_argument (string param) {
		if (is_null (args)) {
			return false;
		}

		return args.has_argument (param);
	}

	internal static string? get_argument (string param) {
		return args.get_argument (param);
	}

	public static void debug_message (string s) {
		if (unlikely (has_logging ())) {
			try {
				if (BirdFont.logstream != null) {
					((!)BirdFont.logstream).put_string (s);
					((!)BirdFont.logstream).flush ();
				} else {
					warning ("No logstream.");
				}

				stderr.printf (s);
			} catch (GLib.Error e) {
				warning (e.message);
			}
		}
	}
}

void init_logfile () {
	DateTime t;
	File settings;
	string s;
	File log;

	try {
		t = new DateTime.now_local ();
		settings = BirdFont.get_settings_directory ();
		s = t.to_string ().replace (":", "_");
		log = get_child (settings, @"birdfont_$s.log");

		BirdFont.logstream = new DataOutputStream (log.create (FileCreateFlags.REPLACE_DESTINATION));
		((!)BirdFont.logstream).put_string ((!) log.get_path ());
		((!)BirdFont.logstream).put_string ("\n");

		warning ("Logging to " + (!) log.get_path ());
	} catch (GLib.Error e) {
		warning (e.message);
		warning ((!) log.get_path ());
	}

	LogLevelFlags levels = LogLevelFlags.LEVEL_ERROR | LogLevelFlags.LEVEL_CRITICAL | LogLevelFlags.LEVEL_WARNING | LogLevelFlags.LEVEL_DEBUG;
	Log.set_handler (null, levels, log_warning);

	BirdFont.logging = true;

	printd (@"Program version: $(VERSION)\n");
}

internal static void log_warning (string? log_domain, LogLevelFlags log_levels, string message) {
	if (log_domain != null) {
		printd ((!) log_domain);
	}

	printd ("\n");
	printd (message);
	printd ("\n");
	printd ("\n");
}

/** Write debug output to logfile. */
public static void printd (string s) {
#if ANDROID
	__android_log_print (ANDROID_LOG_WARN, "BirdFont", s);
#else
	BirdFont.debug_message (s);
#endif
}

/** Translate string */
public string t_ (string t) {
#if ANDROID
	return t;
#else
	string translate = Preferences.get ("translate");
	
	if (translate == "" || translate == "true") {
		return _(t);
	} else {
		return t;
	}
#endif
}

/** Translate mac menu items */
public static string translate_mac (string t) {
	string s = t_(t);
	return s.replace ("_", "");
}

/** Print a warning if Birdfont was started with the --test argument. */
public static void warn_if_test (string message) {
	if (BirdFont.has_argument ("--test")) {
		warning (message);
	}
}

/** Obtain a handle to a file in a folder. */
public static File get_child (File folder, string file_name) {
	string f;
	string s;
	string n;

	// avoid drive letter problems on windows

	f = (!) folder.get_path ();

#if LINUX
	s = "/";
#else
	s = (BirdFont.win32) ? "\\" : "/";
#endif

	n = file_name;
	if (unlikely (BirdFont.win32 && file_name.index_of ("\\") != -1)) {
		warning (@"File name contains path separator: $file_name, Directory: $f");
		n = n.substring (n.last_index_of ("\\")).replace ("\\", "");
	}

	if (!f.has_suffix (s)) {
		f += s;
	}

	printd (@"File in Directory: $f Name: $n\n");

	return File.new_for_path (f + n);
}

}

Contact

Email: johan.mattsson.m@gmail.com
Mastodon: typo.social/@birdfont
GitHub: johanmattssonm
LinkedIn: johan-mattsson-2ab324112