From e743fb2ed8b85abd1a6d2aff7cafa9b27d613077 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 18 Mar 2021 22:29:01 -0400 Subject: [PATCH 1/2] revamp appimage functionality --- AppRun.c | 280 -- README.md | 6 +- appdir.nix | 81 - appimage-bundle.nix | 61 - appimage-top.nix | 17 - appimage.nix | 12 - appimage/appdir.nix | 160 ++ appimage/appimage.nix | 20 + appimage/default.nix | 13 + appimage/excludelist.nix | 227 ++ appimage/glibc_2_24/common-symbol.patch | 21 + appimage/glibc_2_24/default.nix | 88 + appimage/glibc_2_24/fix-x64-abi.patch | 35 + appimage/glibc_2_24/ld-monetary.patch | 27 + appimage/glibc_2_24/locale-C.diff | 3235 +++++++++++++++++++++++ appimagetool.nix | 47 - flake.lock | 42 + flake.nix | 21 +- appdir.sh => nix2appdir.sh | 2 +- nix2appimage.sh | 2 +- test-appimage.nix | 14 - 21 files changed, 3894 insertions(+), 517 deletions(-) delete mode 100644 AppRun.c delete mode 100644 appdir.nix delete mode 100644 appimage-bundle.nix delete mode 100644 appimage-top.nix delete mode 100644 appimage.nix create mode 100644 appimage/appdir.nix create mode 100644 appimage/appimage.nix create mode 100644 appimage/default.nix create mode 100644 appimage/excludelist.nix create mode 100644 appimage/glibc_2_24/common-symbol.patch create mode 100644 appimage/glibc_2_24/default.nix create mode 100644 appimage/glibc_2_24/fix-x64-abi.patch create mode 100644 appimage/glibc_2_24/ld-monetary.patch create mode 100644 appimage/glibc_2_24/locale-C.diff delete mode 100644 appimagetool.nix create mode 100644 flake.lock rename appdir.sh => nix2appdir.sh (77%) delete mode 100644 test-appimage.nix diff --git a/AppRun.c b/AppRun.c deleted file mode 100644 index 24cb1fd..0000000 --- a/AppRun.c +++ /dev/null @@ -1,280 +0,0 @@ -/************************************************************************** - -Copyright (c) 2004-16 Simon Peter -Portions Copyright (c) 2010 RazZziel - -All Rights Reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - -**************************************************************************/ - -#define _GNU_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define die(...) \ - do { \ - fprintf(stderr, "Error: " __VA_ARGS__); \ - exit(1); \ - } while (0); - -#define PATH_MAX 4096 - -#define LINE_SIZE 255 - -#define err_exit(format, ...) { fprintf(stderr, format ": %s\n", ##__VA_ARGS__, strerror(errno)); exit(EXIT_FAILURE); } - -int filter (const struct dirent *dir) { - char *p = (char*) &dir->d_name; - p = strrchr(p, '.'); - return p && !strcmp(p, ".desktop"); -} - -static void update_map(char *mapping, char *map_file) { - int fd; - - fd = open(map_file, O_WRONLY); - if (fd < 0) { - err_exit("map open"); - } - - int map_len = strlen(mapping); - if (write(fd, mapping, map_len) != map_len) { - err_exit("map write"); - } - - close(fd); -} - -static void add_path(const char* name, const char* rootdir) { - char path_buf[PATH_MAX]; - snprintf(path_buf, sizeof(path_buf), "/%s", name); - - struct stat statbuf; - if (stat(path_buf, &statbuf) < 0) { - fprintf(stderr, "Cannot stat %s: %s\n", path_buf, strerror(errno)); - return; - } - - char path_buf2[PATH_MAX]; - snprintf(path_buf2, sizeof(path_buf2), "%s/%s", rootdir, name); - - mkdir(path_buf2, statbuf.st_mode & ~S_IFMT); - if (mount(path_buf, path_buf2, "none", MS_BIND | MS_REC, NULL) < 0) { - fprintf(stderr, "Cannot bind mount %s to %s: %s\n", path_buf, path_buf2, strerror(errno)); - } -} - -#define SAVE_ENV_VAR(x) char *x = getenv(#x) -#define LOAD_ENV_VAR(x) do { if (x != NULL) setenv(#x, x, 1); } while(0) - -int main(int argc, char *argv[]) { - char *appdir = dirname(realpath("/proc/self/exe", NULL)); - if (!appdir) - die("Could not access /proc/self/exe\n"); - - char *tmpdir = getenv("TMPDIR"); - if (!tmpdir) { - tmpdir = "/tmp"; - } - - char template[PATH_MAX]; - int needed = snprintf(template, PATH_MAX, "%s/nixXXXXXX", tmpdir); - if (needed < 0) { - err_exit("TMPDIR too long: '%s'", tmpdir); - } - - char *rootdir = mkdtemp(template); - if (!rootdir) { - err_exit("mkdtemp(%s)", template); - } - - int ret; - - struct dirent **namelist; - - ret = scandir(appdir, &namelist, filter, NULL); - - if (ret == 0) { - die("No .desktop files found\n"); - } else if(ret == -1) { - die("Could not scan directory %s\n", appdir); - } - - /* Extract executable from .desktop file */ - - FILE *f; - char *desktop_file = malloc(LINE_SIZE); - snprintf(desktop_file, LINE_SIZE-1, "%s/%s", appdir, namelist[0]->d_name); - f = fopen(desktop_file, "r"); - - char *line = malloc(LINE_SIZE); - size_t n = LINE_SIZE; - int found = 0; - - while (getline(&line, &n, f) != -1) - { - if (!strncmp(line,"Exec=",5)) - { - char *p = line+5; - while (*++p && *p != ' ' && *p != '%' && *p != '\n'); - *p = 0; - found = 1; - break; - } - } - - fclose(f); - - if (!found) - die("Executable not found, make sure there is a line starting with 'Exec='\n"); - - /* Execution */ - char *executable = basename(line+5); - - char full_exec[PATH_MAX]; - snprintf(full_exec, sizeof(full_exec), "/usr/bin/%s", executable); - - // get uid, gid before going to new namespace - uid_t uid = getuid(); - gid_t gid = getgid(); - - // "unshare" into new namespace - if (unshare(CLONE_NEWNS | CLONE_NEWUSER) < 0) { - err_exit("unshare()"); - } - - // add necessary system stuff to rootdir namespace - add_path("dev", rootdir); - add_path("proc", rootdir); - add_path("sys", rootdir); - add_path("run", rootdir); - add_path("etc", rootdir); - add_path("home", rootdir); - - // setup skeleton - char path_buf[PATH_MAX]; - snprintf(path_buf, sizeof(path_buf), "%s/tmp", rootdir); - mkdir(path_buf, ~0); - snprintf(path_buf, sizeof(path_buf), "%s/var", rootdir); - mkdir(path_buf, ~0); - - // make sure nixdir exists - struct stat statbuf2; - if (stat(appdir, &statbuf2) < 0) { - err_exit("stat(%s)", appdir); - } - - char nixdir[PATH_MAX]; - snprintf(nixdir, sizeof(nixdir), "%s/nix", appdir); - snprintf(path_buf, sizeof(path_buf), "%s/nix", rootdir); - mkdir(path_buf, statbuf2.st_mode & ~S_IFMT); - if (mount(nixdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) { - err_exit("mount(%s, %s)", nixdir, path_buf); - } - - char usrdir[PATH_MAX]; - snprintf(usrdir, sizeof(usrdir), "%s/usr", appdir); - snprintf(path_buf, sizeof(path_buf), "%s/usr", rootdir); - mkdir(path_buf, statbuf2.st_mode & ~S_IFMT); - if (mount(usrdir, path_buf, "none", MS_BIND | MS_REC, NULL) < 0) { - err_exit("mount(%s, %s)", usrdir, path_buf); - } - - snprintf(path_buf, sizeof(path_buf), "%s/bin", rootdir); - if (symlink("/usr/bin", path_buf) < 0) { - err_exit("symlink(/usr/bin, %s)", path_buf); - } - - // fixes issue #1 where writing to /proc/self/gid_map fails - // see user_namespaces(7) for more documentation - int fd_setgroups = open("/proc/self/setgroups", O_WRONLY); - if (fd_setgroups > 0) { - write(fd_setgroups, "deny", 4); - } - - // map the original uid/gid in the new ns - char map_buf[1024]; - snprintf(map_buf, sizeof(map_buf), "%d %d 1", uid, uid); - update_map(map_buf, "/proc/self/uid_map"); - snprintf(map_buf, sizeof(map_buf), "%d %d 1", gid, gid); - update_map(map_buf, "/proc/self/gid_map"); - - // chroot to rootdir - if (chroot(rootdir) < 0) { - err_exit("chroot(%s)", rootdir); - } - - char *pwddir = getenv("PWD"); - chdir(pwddir); - - SAVE_ENV_VAR(PWD); - SAVE_ENV_VAR(DBUS_SESSION_BUS_ADDRESS); - SAVE_ENV_VAR(USER); - SAVE_ENV_VAR(HOSTNAME); - SAVE_ENV_VAR(LANG); - SAVE_ENV_VAR(LC_ALL); - SAVE_ENV_VAR(TERM); - SAVE_ENV_VAR(DISPLAY); - SAVE_ENV_VAR(XDG_RUNTIME_DIR); - SAVE_ENV_VAR(XAUTHORITY); - SAVE_ENV_VAR(XDG_SESSION_ID); - SAVE_ENV_VAR(XDG_SEAT); - SAVE_ENV_VAR(HOME); - - clearenv(); - - LOAD_ENV_VAR(PWD); - LOAD_ENV_VAR(DBUS_SESSION_BUS_ADDRESS); - LOAD_ENV_VAR(USER); - LOAD_ENV_VAR(HOSTNAME); - LOAD_ENV_VAR(LANG); - LOAD_ENV_VAR(LC_ALL); - LOAD_ENV_VAR(TERM); - LOAD_ENV_VAR(DISPLAY); - LOAD_ENV_VAR(XDG_RUNTIME_DIR); - LOAD_ENV_VAR(XAUTHORITY); - LOAD_ENV_VAR(XDG_SESSION_ID); - LOAD_ENV_VAR(XDG_SEAT); - LOAD_ENV_VAR(HOME); - - setenv("PATH", ENV_PATH, 1); - setenv("TMPDIR", "/tmp", 1); - - /* Run */ - // FIXME: What about arguments in the Exec= line of the desktop file? - ret = execvp(full_exec, argv); - - if (ret == -1) - die("Error executing '%s'; return code: %d\n", full_exec, ret); - - free(line); - free(desktop_file); - return 0; -} diff --git a/README.md b/README.md index 352c2db..d72cdc9 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ NIX_PATH="nixpkgs=https://github.com/matthewbauer/nixpkgs/archive/nix-bundle.tar This will create a file at Emacs-x86_64.AppImage which you can execute. -Notice that there is only one argument for nix2appimage.sh. This is because the target executable will be detected from the .desktop file in ```/share/applications/*.desktop```. As a side-effect, AppImage requires your package to have a .desktop file, so packages like "hello", "coreutils", etc. will not work. +Notice that there is only one argument for nix2appimage.sh. This is because the target executable will be detected from the .desktop file in ```/share/applications/*.desktop```. If there is no .desktop file, nix2appimage will attempt to create one. If, however, there are more than one executable in the bin/ directory, we can't pick one, and there will have to be a .desktop file. Some other examples to try: @@ -96,7 +96,9 @@ Some other examples to try: These may take a while because of the large closure size. -Note that these do not currently work out of the box with NixOS. Other Linux distros should work. +Note that these do not currently work out of the box with NixOS (but can be run through the `appimage-run` command). Other Linux distros should work. An additional limitation is that the machine running the AppImage must have a glibc at least as new as the one used to compile, so it is usually a good idea to pull an old version of glibc for your compilation. + +If you are using flakes, nix-bundle exports the overlay `nix-bundle.overlays.glibc_2_24.${system}` for this purpose, which will pull in glibc 2.24, and will thus be compatible with almost every popular distro's supported releases. Be forewarned that this has to rebuild pretty much everything in nixpkgs and takes a *long* time. Also note that some packages (e.g. Firefox) do their own glibc versioning, and so don't need this; better to test your package on an old distro without this overlay first. ## Comparison with AppImage, FlatPak, Snappy diff --git a/appdir.nix b/appdir.nix deleted file mode 100644 index 1378ca9..0000000 --- a/appdir.nix +++ /dev/null @@ -1,81 +0,0 @@ -{ stdenv, fetchurl, muslPkgs, perl, pathsFromGraph, fetchFromGitHub, coreutils, bash }: - -let - AppRun = targets: muslPkgs.stdenv.mkDerivation { - name = "AppRun"; - - phases = [ "buildPhase" "installPhase" "fixupPhase" ]; - - buildPhase = '' - CC="$CC -O2 -Wall -Wno-deprecated-declarations -Wno-unused-result -static" - $CC ${./AppRun.c} -o AppRun -DENV_PATH='"${stdenv.lib.makeBinPath targets}"' - ''; - - installPhase = '' - mkdir -p $out/bin - cp AppRun $out/bin/AppRun - ''; - }; - -in - - { target, name, extraTargets ? [ coreutils bash ] }: let targets = ([ target ] ++ extraTargets); - in stdenv.mkDerivation { - name = "${name}.AppDir"; - exportReferencesGraph = map (x: [("closure-" + baseNameOf x) x]) targets; - nativeBuildInputs = [ perl ]; - buildCommand = '' - # TODO use symlinks to shrink output size - - if [ ! -d ${target}/share/applications ]; then - echo "--------------------------------------------------" - echo "| /share/applications does not exist. |" - echo "| AppImage only works with 'applications'. |" - echo "| Try using nix-bundle.sh for command-line apps. |" - echo "--------------------------------------------------" - exit 1 - fi - - storePaths=$(${perl}/bin/perl ${pathsFromGraph} ./closure-*) - - mkdir -p $out/${name}.AppDir - cd $out/${name}.AppDir - - mkdir -p nix/store - cp -r $storePaths nix/store - - ln -s .${target} usr - - if [ -d ${target}/share/appdata ]; then - chmod a+w usr/share - mkdir -p usr/share/metainfo - for f in ${target}/share/appdata/*.xml; do - ln -s ../appdata/$(basename $f) usr/share/metainfo/$(basename $f) - done - fi - - # .desktop - desktop=$(find ${target}/share/applications -name "*.desktop" | head -n1) - if ! [ -z "$desktop" ]; then - cp .$desktop . - fi - - - # icons - if [ -d ${target}/share/icons ]; then - icon=$(find ${target}/share/icons -name "${name}*.png" | head -n1) - if ! [ -z "$icon" ]; then - ln -s .$icon - ln -s .$icon .DirIcon - else - icon=$(find ${target}/share/icons -name "${name}*.svg" | head -n1) - if ! [ -z "$icon" ]; then - ln -s .$icon - ln -s .$icon .DirIcon - fi - fi - fi - - cp ${AppRun targets}/bin/AppRun AppRun - ''; - } diff --git a/appimage-bundle.nix b/appimage-bundle.nix deleted file mode 100644 index c42a5e7..0000000 --- a/appimage-bundle.nix +++ /dev/null @@ -1,61 +0,0 @@ -# use like this: -# nix-build appimage-bundle.nix --argstr package hello --argstr exec hello -# nix-build appimage-bundle.nix --arg package 'with import {}; writers.writePython3Bin "helloThere.py" {} "print(1)\n"' --argstr exec helloThere.py - - -{nixpkgs ? import {}, -package, -exec, -... }: -let - appimage_src = drv : exec : with nixpkgs; - self.stdenv.mkDerivation rec { - name = drv.name + "-appdir"; - env = buildEnv { - inherit name; - paths = buildInputs; - }; - src = env; - inherit exec; - buildInputs = [ drv ]; - buildCommand = '' - mkdir -p $out/share/icons/hicolor/256x256/apps - mkdir -p $out/share/applications - - shopt -s extglob - ln -s ${env}/!(share) $out/ - ln -s ${env}/share/* $out/share/ - - touch $out/share/icons/hicolor/256x256/apps/${drv.name}.png - touch $out/share/icons/${drv.name}.png - - cat < $out/share/applications/${drv.name}.desktop - [Desktop Entry] - Type=Application - Version=1.0 - Name=${drv.name} - Path=${env} - Icon=${drv.name} - Exec=$exec - Terminal=true - EOF - ''; - system = builtins.currentSystem; - }; - -in - let results = - if (nixpkgs.lib.isDerivation package && !(nixpkgs.lib.isString package)) - then { - name = package.name; - target = appimage_src package "${exec}"; - extraTargets = []; - } - else { - name = nixpkgs."${package}".name; - target = appimage_src (nixpkgs."${package}") "${exec}"; - extraTargets = []; - }; - in - with (import (./appimage-top.nix){nixpkgs' = nixpkgs.path;}); - (appimage (appdir results )).overrideAttrs (old: {name = results.name;}) diff --git a/appimage-top.nix b/appimage-top.nix deleted file mode 100644 index 067dabe..0000000 --- a/appimage-top.nix +++ /dev/null @@ -1,17 +0,0 @@ -{ nixpkgs' ? }: - -let - pkgs = import nixpkgs' { }; - muslPkgs = import nixpkgs' { - localSystem.config = "x86_64-unknown-linux-musl"; - }; - -in rec { - appimagetool = pkgs.callPackage ./appimagetool.nix {}; - - appimage = pkgs.callPackage ./appimage.nix { - inherit appimagetool; - }; - - appdir = pkgs.callPackage ./appdir.nix { inherit muslPkgs; }; -} diff --git a/appimage.nix b/appimage.nix deleted file mode 100644 index 49bfe6c..0000000 --- a/appimage.nix +++ /dev/null @@ -1,12 +0,0 @@ -{ stdenv, appimagetool }: -dir: - -stdenv.mkDerivation { - name = "appimage"; - buildInputs = [ appimagetool ]; - buildCommand = '' - ARCH=x86_64 appimagetool ${dir}/*.AppDir - mkdir $out - cp *.AppImage $out - ''; -} diff --git a/appimage/appdir.nix b/appimage/appdir.nix new file mode 100644 index 0000000..29a9de0 --- /dev/null +++ b/appimage/appdir.nix @@ -0,0 +1,160 @@ +{ stdenv +, lib +, fetchurl +, closureInfo +, coreutils +, bash +, appimagekit +, perl +, runCommand +, glibc +, binutils +}: + +{ target +, name ? target.pname +, extraTargets ? [ coreutils bash ] + +# Specify which executable to run (relative to ${target} directory). +# Only needed if you have multiple binaries and no .desktop file. +, exec ? "" + +# Packages and individual shared libraries to exclude from the output path. This +# can reduce appimage size, but you have to know they are either not needed at +# runtime or host will have a copy accessible. For some libraries you must +# depend on the host's copy for things to work. +, excludePkgs ? [] +, excludeLibs ? import ./excludelist.nix +}: +let closure = closureInfo { rootPaths = [ target ] ++ extraTargets; }; +in stdenv.mkDerivation { + name = "${name}.AppDir"; + nativeBuildInputs = [ perl ]; + exclude_pkgs = with builtins; concatStringsSep " " + (concatMap (pkg: map (out: lib.getOutput out pkg) pkg.outputs) excludePkgs); + exclude_libs = with builtins; concatStringsSep " " excludeLibs; + buildCommand = '' + mkdir -p $out/${name}.AppDir + cd $out/${name}.AppDir + + # copy the nix store closure into the AppDir + mkdir -p nix/store + cat ${closure}/store-paths | while read pkg; do + # ...except the packages explicilty excluded + # TODO: ideally, we would remove any dependencies that are only needed by + # the excluded packages, but I'm not sure how to actually do that. + if ! [[ " $exclude_pkgs " =~ " $pkg " ]]; then + cp -r $pkg nix/store + fi + done + + chmod -R +w nix/store + + # remove excluded libs so that our versions aren't found in the search path + find nix/store | while read f; do + real=$(realpath $f 2>/dev/null) \ + || continue # some symlinks are broken + + if [[ " $exclude_libs " =~ " ''${real##*/} " ]] || [[ " $exclude_libs " =~ " ''${f##*/} " ]]; then + rm $f + fi + done + + # make symlinks relative + find nix/store -type l | while read l; do + # only change absolute links into the nix store + if [[ $(readlink $l) =~ ^/nix/store ]]; then + source=$(dirname "$(realpath -s "$l")") + target="$(realpath "$l")" + unlink $l + ln -s "$(realpath --relative-to="$source" ".$target")" "$l" \ + || continue # some targets may have been excluded (or just broken) + fi + done + + ln -s .${target} usr + + # make rpaths relative + function fix_rpaths_rec() { + rpath=$(patchelf --print-rpath $1 2>/dev/null) && [ "$rpath" != "" ] \ + || return 0 # it's not an ELF file or the rpath is empty + + # current directory is ./usr which is a symlink to /nix/store/.../ so we need three .. to get back + patchelf --set-rpath $(echo $rpath | perl -072 -pe 's/^(\/nix\/store.*)/..\/..\/..\1/') $1 + + # recurse + for l in $(echo $rpath | perl -072 -l40 -ne 's/^(\/nix\/store.*)/.\1\/*/ && print'); do + fix_rpaths_rec $l + done + } + + echo "fixing linker and rpaths for executables and libraries (this may take a while)" + for b in $(find usr/bin -executable -xtype f); do + patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 $b 2>/dev/null \ + || continue # maybe it's not an elf executable + fix_rpaths_rec $b + done + + # make sure peripherals are set up + + # metainfo + if [ -d ${target}/share/appdata ]; then + mkdir -p usr/share/metainfo + for f in ${target}/share/appdata/*.xml; do + ln -s ../appdata/$(basename $f) usr/share/metainfo/$(basename $f) + done + fi + + # icon + mkdir -p .${target}/share/icons/hicolor/256x256/apps + icon=$(find .${target}/share/icons -name "${name}*.png" -o -name "${name}*.svg" | head -n1) + if [ -z "$icon" ]; then + icon=.${target}/share/icons/hicolor/256x256/apps/${name}.png + touch $icon + fi + ln -s $(realpath -s --relative-to="." $icon) ./${name}.png + ln -s $(realpath -s --relative-to="." $icon) .DirIcon + + # .desktop + mkdir -p .${target}/share/applications + desktop=$(find .${target}/share/applications -name "*.desktop" | head -n1) + + # user didn't supply a desktop file. we'll make one + if [ -z "$desktop" ]; then + desktop=.${target}/share/applications/${name}.desktop + + exec=${exec} + # user didn't supply an executable. we'll look for one + if [ -z "$exec" ]; then + exec=$(find usr/bin -executable -xtype f) + + if [ -z "$exec" ]; then + echo "Cannot build AppDir: no executable found in ${target}/bin/" + fi + + if [ $(echo -n "$exec" | grep -c '^') -gt 1 ]; then + echo "Cannot build AppDir: with more than one executable, you must specify 'exec'" + fi + fi + exec=$(realpath --relative-to="usr" $exec) + cat < $desktop +[Desktop Entry] +Type=Application +Terminal=true +Version=1.0 +Name=${name} +Icon=${name} +Exec=$exec +Categories=Utility; +EOF + fi + cp $desktop . + + cp ${appimagekit}/bin/AppRun AppRun + chmod +w AppRun + patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 AppRun + # NOTE: haven't fixed the rpath of AppRun, since we don't know what + # directory it will be called from. I'm not sure if that'll be an + # issue. If it is, we can go back to compiling statically with musl + ''; +} diff --git a/appimage/appimage.nix b/appimage/appimage.nix new file mode 100644 index 0000000..fbe2a14 --- /dev/null +++ b/appimage/appimage.nix @@ -0,0 +1,20 @@ +{ stdenv, appimagekit }: +dir: + +stdenv.mkDerivation { + name = "appimage"; + nativeBuildInputs = [ appimagekit ]; + buildCommand = '' + appimagetool ${dir}/*.AppDir + + chmod +w *.AppImage + patchelf --set-interpreter /lib64/ld-linux-x86-64.so.2 *.AppImage + + # shrink the closure (AppImage can't access /nix/store anyways) + patchelf --set-rpath "" *.AppImage + chmod -w *.AppImage + + mkdir $out + cp *.AppImage $out + ''; +} diff --git a/appimage/default.nix b/appimage/default.nix new file mode 100644 index 0000000..3dc29db --- /dev/null +++ b/appimage/default.nix @@ -0,0 +1,13 @@ +{ pkgs ? import { } }: + +rec { + appdir2appimage = pkgs.callPackage ./appimage.nix { }; + + nix2appdir = pkgs.callPackage ./appdir.nix { }; + + nix2appimage = x: appdir2appimage (nix2appdir x); + + appimage = nix2appimage; + + appdir = nix2appdir; +} diff --git a/appimage/excludelist.nix b/appimage/excludelist.nix new file mode 100644 index 0000000..c31793f --- /dev/null +++ b/appimage/excludelist.nix @@ -0,0 +1,227 @@ +# Modified from AppImage/pkg2appimage for nix-bundle +[ +# +# This file lists libraries that we will assume to be present on the host system and hence +# should NOT be bundled inside AppImages. This is a working document; expect it to change +# over time. File format: one filename per line. Each entry should have a justification comment. + +# See the useful tool at https://abi-laboratory.pro/index.php?view=navigator&symbol=hb_buffer_set_cluster_level#result +# to investigate issues with missing symbols. + +"ld-linux.so.2" +"ld-linux-x86-64.so.2" +"libanl.so.1" +"libBrokenLocale.so.1" +"libcidn.so.1" +# "libcrypt.so.1" # Not part of glibc anymore as of Fedora 30. See https://github.com/slic3r/Slic3r/issues/4798 and https://pagure.io/fedora-docs/release-notes/c/01d74b33564faa42959c035e1eee286940e9170e?branch=f28 +"libc.so.6" +"libdl.so.2" +"libm.so.6" +"libmvec.so.1" +# "libnsl.so.1" # Not part of glibc anymore as of Fedora 28. See https://github.com/RPCS3/rpcs3/issues/5224#issuecomment-434930594 +"libnss_compat.so.2" +# "libnss_db.so.2" # Not part of neon-useredition-20190321-0530-amd64.iso +"libnss_dns.so.2" +"libnss_files.so.2" +"libnss_hesiod.so.2" +"libnss_nisplus.so.2" +"libnss_nis.so.2" +"libpthread.so.0" +"libresolv.so.2" +"librt.so.1" +"libthread_db.so.1" +"libutil.so.1" +# These files are all part of the GNU C Library which should never be bundled. +# List was generated from a fresh build of glibc 2.25. + +"libstdc++.so.6" +# Workaround for: +# usr/lib/libstdc++.so.6: version `GLIBCXX_3.4.21' not found + +"libGL.so.1" +# The above may be missing on Chrome OS, https://www.reddit.com/r/Crostini/comments/d1lp67/ultimaker_cura_no_longer_running_as_an_appimage/ +"libEGL.so.1" +# Part of the video driver (OpenGL); present on any regular +# desktop system, may also be provided by proprietary drivers. +# Known to cause issues if it's bundled. + +"libGLdispatch.so.0" +"libGLX.so.0" +# reported to be superfluent and conflicting system libraries (graphics driver) +# see https://github.com/linuxdeploy/linuxdeploy/issues/89 + +# "libOpenGL.so.0" # XXX nix-bundle edit: this was causing problems for me +# Qt installed via install-qt.sh apparently links to this library +# part of OpenGL like libGL/libEGL, so excluding it should not cause any problems +# https://github.com/linuxdeploy/linuxdeploy/issues/152 + +"libdrm.so.2" +# Workaround for: +# Antergos Linux release 2015.11 (ISO-Rolling) +# /usr/lib/libdrm_amdgpu.so.1: error: symbol lookup error: undefined symbol: drmGetNodeTypeFromFd (fatal) +# libGL error: unable to load driver: swrast_dri.so +# libGL error: failed to load driver: swrast +# Unrecognized OpenGL version + +"libglapi.so.0" +# Part of mesa +# known to cause problems with graphics, see https://github.com/RPCS3/rpcs3/issues/4427#issuecomment-381674910 + +"libgbm.so.1" +# Part of mesa +# https://github.com/probonopd/linuxdeployqt/issues/390#issuecomment-529036305 + +"libxcb.so.1" +# Workaround for: +# Fedora 23 +# symbol lookup error: /lib64/libxcb-dri3.so.0: undefined symbol: xcb_send_fd +# Uncertain if this is required to be bundled for some distributions - if so we need to write a version check script and use LD_PRELOAD to load the system version if it is newer +# Fedora 25: +# undefined symbol: xcb_send_request_with_fds +# https://github.com/AppImage/AppImages/issues/128 + +"libX11.so.6" +# Workaround for: +# Fedora 23 +# symbol lookup error: ./lib/libX11.so.6: undefined symbol: xcb_wait_for_reply64 +# Uncertain if this is required to be bundled for some distributions - if so we need to write a version check script and use LD_PRELOAD to load the system version if it is newer + +"libgio-2.0.so.0" +# Workaround for: +# On Ubuntu, "symbol lookup error: /usr/lib/x86_64-linux-gnu/gtk-2.0/modules/liboverlay-scrollbar.so: undefined symbol: g_settings_new" + +# "libgdk-x11-2.0.so.0" # Missing on openSUSE-Tumbleweed-KDE-Live-x86_64-Snapshot20170601-Media.iso +# "libgtk-x11-2.0.so.0" # Missing on openSUSE-Tumbleweed-KDE-Live-x86_64-Snapshot20170601-Media.iso + +"libasound.so.2" +# Workaround for: +# No sound, e.g., in VLC.AppImage (does not find sound cards) + +"libgdk_pixbuf-2.0.so.0" +# Workaround for: +# On Ubuntu, get (inkscape:25621): GdkPixbuf-WARNING **: Error loading XPM image loader: Image type 'xpm' is not supported + +"libfontconfig.so.1" +# Workaround for: +# Application stalls when loading fonts during application launch; e.g., KiCad on ubuntu-mate + +"libthai.so.0" +# Workaround for: +# audacity: /tmp/.mount_AudaciUsFbON/usr/lib/libthai.so.0: version `LIBTHAI_0.1.25' not found (required by /usr/lib64/libpango-1.0.so.0) +# on openSUSE Tumbleweed + +# other "low-level" font rendering libraries +# should fix https://github.com/probonopd/linuxdeployqt/issues/261#issuecomment-377522251 +# and https://github.com/probonopd/linuxdeployqt/issues/157#issuecomment-320755694 +"libfreetype.so.6" +"libharfbuzz.so.0" + +# Note, after discussion we do not exlude this, but we can use a dummy library that just does nothing +# libselinux.so.1 +# Workaround for: +# sed: error while loading shared libraries: libpcre.so.3: cannot open shared object file: No such file or directory +# Some distributions, such as Arch Linux, do not come with libselinux.so.1 by default. +# The solution is to bundle a dummy mock library: +# echo "extern int is_selinux_enabled(void){return 0;}" >> selinux-mock.c +# gcc -s -shared -o libselinux.so.1 -Wl,-soname,libselinux.so.1 selinux-mock.c +# strip libselinux.so.1 +# More information: https://github.com/AppImage/AppImages/issues/83 +# and https://github.com/AppImage/AppImageKit/issues/775#issuecomment-614954821 +# https://gitlab.com/sulinos/devel/libselinux-dummy + +# The following are assumed to be part of the base system +# Removing these has worked e.g., for Krita. Feel free to report if +# you think that some of these should go into AppImages and why. +"libcom_err.so.2" +"libexpat.so.1" +"libgcc_s.so.1" +"libglib-2.0.so.0" +"libgpg-error.so.0" +# "libgssapi_krb5.so.2" # Disputed, seemingly needed by Arch Linux since Kerberos is named differently there +# "libgssapi.so.3" # Seemingly needed when running Ubuntu 14.04 binaries on Fedora 23 +# "libhcrypto.so.4" # Missing on openSUSE LEAP 42.0 +# "libheimbase.so.1" # Seemingly needed when running Ubuntu 14.04 binaries on Fedora 23 +# "libheimntlm.so.0" # Seemingly needed when running Ubuntu 14.04 binaries on Fedora 23 +# "libhx509.so.5" # Missing on openSUSE LEAP 42.0 +"libICE.so.6" +# "libidn.so.11" # Does not come with Solus by default +# "libk5crypto.so.3" # Runnning AppImage built on Debian 9 or Ubuntu 16.04 on an Archlinux fails otherwise; https://github.com/AppImage/AppImages/issues/301 +# "libkeyutils.so.1" # Does not come with Void Linux by default; https://github.com/Subsurface-divelog/subsurface/issues/1971#issuecomment-466606834 +# "libkrb5.so.26" # Disputed, seemingly needed by Arch Linux since Kerberos is named differently there. Missing on openSUSE LEAP 42.0 +# "libkrb5.so.3" # Disputed, seemingly needed by Arch Linux since Kerberos is named differently there +# "libkrb5support.so.0" # Disputed, seemingly needed by Arch Linux since Kerberos is named differently there +"libp11-kit.so.0" +# "libpcre.so.3" # Missing on Fedora 24, SLED 12 SP1, and openSUSE Leap 42.2 +# "libroken.so.18" # Mission on openSUSE LEAP 42.0 +# "libsasl2.so.2" # Seemingly needed when running Ubuntu 14.04 binaries on Fedora 23 +"libSM.so.6" +"libusb-1.0.so.0" +"libuuid.so.1" +# "libwind.so.0" # Missing on openSUSE LEAP 42.0 +"libz.so.1" + +# Potentially dangerous libraries +"libgobject-2.0.so.0" + +# Workaround for: +# Rectangles instead of fonts +# https://github.com/AppImage/AppImages/issues/240 +"libpangoft2-1.0.so.0" +"libpangocairo-1.0.so.0" +"libpango-1.0.so.0" + +# FIXME: +# Can get symbol lookup error: /lib64/libpango-1.0.so.0: undefined symbol: g_log_structured_standard +# if libcairo is bundled but libpango is not + +# Workaround for: +# e.g., Spotify +# relocation error: /lib/x86_64-linux-gnu/libgcrypt.so.20: +# symbol gpgrt_lock_lock, version GPG_ERROR_1.0 not defined +# in file libgpg-error.so.0 with link time reference +"libgpg-error.so.0" + +"libjack.so.0" +# it must match the ABI of the JACK server which is installed in the base system +# rncbc confirmed this +# However, this library is missing on Fedora-WS-Live-31-1-9 +# which means that we should avoid using JACK altogether if possible + +# Unsolved issue: +# https://github.com/probonopd/linuxdeployqt/issues/35 +# Error initializing NSS with a persistent database (sql:/home/me/.pki/nssdb): libsoftokn3.so: cannot open shared object file: No such file or directory +# Error initializing NSS without a persistent database: NSS error code: -5925 +# nss_error=-5925, os_error=0 +# libnss3.so should not be removed from the bundles, as this causes other issues, e.g., +# https://github.com/probonopd/linuxdeployqt/issues/35#issuecomment-256213517 +# and https://github.com/AppImage/AppImages/pull/114 +# "libnss3.so" + +# The following cannot be excluded, see +# https://github.com/AppImage/AppImages/commit/6c7473d8cdaaa2572248dcc53d7f617a577ade6b +# http://stackoverflow.com/questions/32644157/forcing-a-binary-to-use-a-specific-newer-version-of-a-shared-library-so +# "libssl.so.1" +# "libssl.so.1.0.0" +# "libcrypto.so.1" +# "libcrypto.so.1.0.0" + +# According to https://github.com/RicardoEPRodrigues/3Engine/issues/4#issuecomment-511598362 +# libGLEW is not tied to a specific GPU. It's linked against libGL.so.1 +# and that one is different depending on the installed driver. +# In fact libGLEW is changing its soversion very often, so you should always bundle libGLEW.so.2.0 + +# "libglut.so.3" # to be confirmed + +"libxcb-dri3.so.0" # https://github.com/AppImage/AppImages/issues/348 +"libxcb-dri2.so.0" # https://github.com/probonopd/linuxdeployqt/issues/331#issuecomment-442276277 + +# If the next line turns out to cause issues, we will have to remove it again and find another solution +"libfribidi.so.0" # https://github.com/olive-editor/olive/issues/221 and https://github.com/knapsu/plex-media-player-appimage/issues/14 + +# Workaround for: +# symbol lookup error: /lib/x86_64-linux-gnu/libgnutls.so.30: undefined symbol: __gmpz_limbs_write +# https://github.com/ONLYOFFICE/appimage-desktopeditors/issues/3 +# Apparently coreutils depends on it, so it should be safe to assume that it comes with every target system +"libgmp.so.10" + +] diff --git a/appimage/glibc_2_24/common-symbol.patch b/appimage/glibc_2_24/common-symbol.patch new file mode 100644 index 0000000..86750a9 --- /dev/null +++ b/appimage/glibc_2_24/common-symbol.patch @@ -0,0 +1,21 @@ +diff --git a/misc/regexp.c b/misc/regexp.c +index 3b36682..a9b1f6e 100644 +--- a/misc/regexp.c ++++ b/misc/regexp.c +@@ -30,13 +30,13 @@ + #if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_23) + + /* Define the variables used for the interface. */ +-char *loc1; +-char *loc2; ++char *loc1 __attribute__ ((nocommon)); ++char *loc2 __attribute__ ((nocommon)); + compat_symbol (libc, loc1, loc1, GLIBC_2_0); + compat_symbol (libc, loc2, loc2, GLIBC_2_0); + + /* Although we do not support the use we define this variable as well. */ +-char *locs; ++char *locs __attribute__ ((nocommon)); + compat_symbol (libc, locs, locs, GLIBC_2_0); + + diff --git a/appimage/glibc_2_24/default.nix b/appimage/glibc_2_24/default.nix new file mode 100644 index 0000000..e30da69 --- /dev/null +++ b/appimage/glibc_2_24/default.nix @@ -0,0 +1,88 @@ +self: super: + let + nixpkgs-old = builtins.fetchGit { + # name = "nixpkgs-16.09"; + url = "https://github.com/nixos/nixpkgs"; + ref = "refs/tags/16.09"; + rev = "7d50dfaed5540495385b5c0feae90891d2d0e72b"; + }; + oldPkgs = import nixpkgs-old { inherit (super) system; }; + # takes in a *string* name of a glibc package e.g. "glibcInfo" + # + # the general idea is to mix the attributes from the old glibc and from the + # the new glibc in a way that gets us an older version of glibc but is + # compatible with the new gcc and the changes to nixpkgs. this took a lot of + # trial and error, and will probably have to be updated as nixpkgs + # progresses. + glibcAdapter = glibcPkg: super.${glibcPkg}.overrideAttrs (newGlibc: + let + oldGlibc = oldPkgs.${glibcPkg}; + in { + inherit (oldGlibc) name src; + + # version wasn't an attribute back then, so we can't inherit + version = "2.24"; + + patches = oldGlibc.patches ++ [ + # from current glibc. has to do with nixpkgs changes, not new glibc + ./locale-C.diff + + # from current glibc. has to do with new gcc not new glibc + ./fix-x64-abi.patch + + # newer ld does something different with .symver + ./common-symbol.patch + + # update to allow a special case empty string + ./ld-monetary.patch + ]; + + postPatch = + oldGlibc.postPatch + # from current glibc + + '' + # Needed for glibc to build with the gnumake 3.82 + # http://comments.gmane.org/gmane.linux.lfs.support/31227 + sed -i 's/ot \$/ot:\n\ttouch $@\n$/' manual/Makefile + # nscd needs libgcc, and we don't want it dynamically linked + # because we don't want it to depend on bootstrap-tools libs. + echo "LDFLAGS-nscd += -static-libgcc" >> nscd/Makefile + ''; + + # modifications from old glibc + configureFlags = + # we can maintain compatiblity with older kernel (see below) + super.lib.remove "--enable-kernel=3.2.0" (newGlibc.configureFlags or []) + ++ [ "--enable-obsolete-rpc" ] + ++ super.lib.optionals + # (we don't have access to withLinuxHeaders from here) + (newGlibc.linuxHeaders != null) [ + "--enable-kernel=2.6.32" + ]; + + NIX_CFLAGS_COMPILE = + (newGlibc.NIX_CFLAGS_COMPILE or "") + # from old glibc + + " -Wno-error=strict-prototypes" + # new gcc introduces new warnings which we must disable + # (see https://github.com/NixOS/nixpkgs/pull/71480) + + " -Wno-error=stringop-truncation -Wno-error=attribute-alias" + # I also had to disable these <.< >.> + + " -Wno-error=multistatement-macros" + + " -Wno-error=int-in-bool-context" + + " -Wno-error=format-truncation" + + " -Wno-error=nonnull" + + " -Wno-error=restrict" + + " -Wno-error=unused-const-variable" + + " -Wno-error=int-conversion" + + " -Wno-error=unused-function"; + } + ); + in { + glibc = glibcAdapter "glibc"; + glibcLocales = glibcAdapter "glibcLocales"; + glibcInfo = glibcAdapter "glibcInfo"; + llvmPackages_11.llvm = super.llvmPackages_11.llvm.overrideAttrs (attrs: { + doCheck = false; # test fails: intrinsics.ll + }); + } diff --git a/appimage/glibc_2_24/fix-x64-abi.patch b/appimage/glibc_2_24/fix-x64-abi.patch new file mode 100644 index 0000000..1d60dcd --- /dev/null +++ b/appimage/glibc_2_24/fix-x64-abi.patch @@ -0,0 +1,35 @@ +From 3288c6da64add3b4561b8c10fff522027caea01c Mon Sep 17 00:00:00 2001 +From: Nicholas Miell +Date: Sat, 17 Jun 2017 18:21:07 -0700 +Subject: [PATCH] Align the stack on entry to __tls_get_addr() + +Old versions of gcc (4 & 5) didn't align the stack according to the +AMD64 psABI when calling __tls_get_addr(). Apparently new versions of +gcc (7) got much more aggressive about vectorizing and generating MOVAPS +instructions, which means old binaries built with the buggy versions of +gcc are much more likely to crash when using versions of glibc built +using gcc 7. + +For example, a large number of Linux games built using the Unity game +engine and available for purchase on Steam. +--- + elf/dl-tls.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/elf/dl-tls.c b/elf/dl-tls.c +index 5aba33b3fa..3f3cb917de 100644 +--- a/elf/dl-tls.c ++++ b/elf/dl-tls.c +@@ -827,6 +827,10 @@ rtld_hidden_proto (__tls_get_addr) + rtld_hidden_def (__tls_get_addr) + #endif + ++#ifdef __x86_64__ ++/* Old versions of gcc didn't align the stack. */ ++__attribute__((force_align_arg_pointer)) ++#endif + /* The generic dynamic and local dynamic model cannot be used in + statically linked applications. */ + void * +-- +2.13.0 diff --git a/appimage/glibc_2_24/ld-monetary.patch b/appimage/glibc_2_24/ld-monetary.patch new file mode 100644 index 0000000..49138f6 --- /dev/null +++ b/appimage/glibc_2_24/ld-monetary.patch @@ -0,0 +1,27 @@ +diff --git a/locale/programs/ld-monetary.c b/locale/programs/ld-monetary.c +index 8635d4f..4633322 100644 +--- a/locale/programs/ld-monetary.c ++++ b/locale/programs/ld-monetary.c +@@ -217,14 +217,20 @@ No definition for %s category found"), "LC_MONETARY")); + /* The international currency symbol must come from ISO 4217. */ + if (monetary->int_curr_symbol != NULL) + { +- if (strlen (monetary->int_curr_symbol) != 4) ++ /* POSIX says this should be a 3-character symbol from ISO 4217 ++ along with a 4th character that is a divider, but the POSIX ++ locale is documented as having a special case of "", and we ++ support that also, so allow other locales to be created with ++ a blank int_curr_symbol. */ ++ int ics_len = strlen (monetary->int_curr_symbol); ++ if (ics_len != 4 && ics_len != 0) + { + if (! be_quiet && ! nothing) + WITH_CUR_LOCALE (error (0, 0, _("\ + %s: value of field `int_curr_symbol' has wrong length"), + "LC_MONETARY")); + } +- else ++ else if (ics_len == 4) + { /* Check the first three characters against ISO 4217 */ + char symbol[4]; + strncpy (symbol, monetary->int_curr_symbol, 3); diff --git a/appimage/glibc_2_24/locale-C.diff b/appimage/glibc_2_24/locale-C.diff new file mode 100644 index 0000000..fd271d8 --- /dev/null +++ b/appimage/glibc_2_24/locale-C.diff @@ -0,0 +1,3235 @@ +--- /dev/null ++++ b/localedata/locales/C +@@ -0,0 +1,3232 @@ ++escape_char / ++comment_char % ++% Locale for C locale in UTF-8 ++% Contributed by Aurelien Jarno , 2011 ++ ++LC_IDENTIFICATION ++title "C locale" ++source "" ++address "" ++contact "" ++email "aurel32@debian.org" ++tel "" ++fax "" ++language "C" ++territory "" ++revision "1.6" ++date "2016-08-08" ++% ++category "i18n:2012";LC_IDENTIFICATION ++category "i18n:2012";LC_CTYPE ++category "i18n:2012";LC_COLLATE ++category "i18n:2012";LC_NUMERIC ++category "i18n:2012";LC_MONETARY ++category "i18n:2012";LC_TIME ++category "i18n:2012";LC_MESSAGES ++category "i18n:2012";LC_PAPER ++category "i18n:2012";LC_NAME ++category "i18n:2012";LC_ADDRESS ++category "i18n:2012";LC_TELEPHONE ++category "i18n:2012";LC_MEASUREMENT ++END LC_IDENTIFICATION ++ ++LC_CTYPE ++% The following is a copy of i18n with the following change: ++% - The "blank", "cntrl", "space" classes are defined as specified by POSIX ++ ++% The "upper" class reflects the uppercase characters of class "alpha" ++upper / ++ ..;..;..;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;..;;;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;;;..;;;/ ++ ..;..;;..;;/ ++ ..;..;..;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;..;;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;..;..;/ ++ ;..;;;;;;;/ ++ ;;;..;;..;/ ++ ..;..;;..;;/ ++ ;;;;;;;;;/ ++ ;;;;..;..;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;..;..;;;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ..;..;..;..;/ ++ ..;;;;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;;/ ++ ..;;;;..;/ ++ ..;..;;..;;/ ++ ..;..;;..;;/ ++ ;;..;;;..;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;..;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;..;/ ++ ..;;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;.. ++ ++% The "lower" class reflects the lowercase characters of class "alpha" ++lower / ++ ..;;;;..;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;..;;;;;;/ ++ ;;..;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;..;;;;/ ++ ..;;;..;;;/ ++ ;;;..;;;;;/ ++ ..;..;..;..;/ ++ ..;;;;;;;;/ ++ ..;;;;;;;;/ ++ ;..;..;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ..;;..;;;;/ ++ ;;..;..;..;/ ++ ..;;;;;..;/ ++ ;..;..;..;;/ ++ ;;;;;;;;;/ ++ ;..;;;..;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;..;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;..;..;/ ++ ..;..;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;..;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;;..;;;;/ ++ ;..;..;;..;/ ++ ;..;..;;..;/ ++ ;;;;..;..;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;..;;;;/ ++ ..;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;..;;;;/ ++ ;;;..;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;;/ ++ ;;;;;;;;/ ++ ..;;;;;;;;/ ++ ;;;..;;;;;/ ++ ;;;;;;;;/ ++ ..;..;..;..;/ ++ ..;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ .. ++ ++% The "alpha" class of the "i18n" FDCC-set is reflecting ++% the recommendations in TR 10176 annex A ++alpha / ++ ..;..;;;;/ ++ ..;..;..;..;/ ++ ..;;;;..;/ ++ ..;..;;;..;/ ++ ;..;..;..;/ ++ ..;..;;..;/ ++ ..;;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;;..;/ ++ ..;..;..;..;/ ++ ;..;..;;..;/ ++ ;;..;..;..;/ ++ ;;..;..;..;/ ++ ..;;;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;;..;..;/ ++ ..;;;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;;;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;;;..;;/ ++ ..;;;;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ;;;;;;/ ++ ..;..;;/ ++ ;;;;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;.. ++ ++% The "digit" class must only contain the BASIC LATIN digits, says ISO C 99 ++% (sections 7.25.2.1.5 and 5.2.1). ++digit / ++ .. ++ ++% The "outdigit" information is by default "0" to "9". We don't have to ++% provide it here since localedef will fill in the bits and it would ++% prevent locales copying this file define their own values. ++% outdigit / ++% .. ++ ++space / ++ ..; ++ ++cntrl / ++ ..; ++ ++punct / ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;..;/ ++ ;..;..;;;/ ++ ..;;;..;..;/ ++ ..;..;..;;;/ ++ ;;..;..;..;/ ++ ..;;..;;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ;..;..;;..;/ ++ ..;;;..;..;/ ++ ;;;..;;;;;/ ++ ..;;;;..;;/ ++ ..;;..;;;;/ ++ ;..;..;;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;;/ ++ ..;..;;..;/ ++ ..;..;;..;/ ++ ..;..;;..;/ ++ ..;..;..;;/ ++ ..;..;..;;/ ++ ..;;..;..;;/ ++ ..;;..;..;/ ++ ..;;;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;;/ ++ ..;;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;;;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ;..;..;..;;/ ++ ..;..;..;;/ ++ ..;;;;..;/ ++ ..;;..;..;/ ++ ..;;;..;..;/ ++ ;;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;;/ ++ ;;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ;;..;/ ++ ..;;;/ ++ ..;;;/ ++ ..;;..;/ ++ ;..;..;/ ++ ..;;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;;;;/ ++ ;;;;;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;;/ ++ ..;..;/ ++ ..;.. ++ ++graph / ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;;..;;/ ++ ..;..;..;..;/ ++ ..;..;;..;;/ ++ ;..;..;..;;/ ++ ;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ;;;;;;/ ++ ..;..;;/ ++ ;;;;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;.. ++ ++print / ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;;..;;/ ++ ..;..;..;..;/ ++ ..;..;;..;;/ ++ ;..;..;..;;/ ++ ;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ;..;..;/ ++ ;;;;;;/ ++ ..;..;;/ ++ ;;;;;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;.. ++ ++% The "xdigit" class must only contain the BASIC LATIN digits and A-F, a-f, ++% says ISO C 99 (sections 7.25.2.1.12 and 6.4.4.1). ++xdigit / ++ ..;..;.. ++ ++blank / ++ ; ++ ++toupper / ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,) ++ ++tolower / ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,) ++ ++map "totitle"; / ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);(,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,);(,);/ ++ (,) ++ ++% The "combining" class reflects ISO/IEC 10646-1 annex B.1 ++% That is, all combining characters (level 2+3). ++class "combining"; / ++ ..;..;..;;/ ++ ..;..;;..;/ ++ ..;;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;;..;/ ++ ..;;..;..;/ ++ ..;;..;;..;/ ++ ;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;;/ ++ ..;;..;..;;/ ++ ..;..;;..;/ ++ ..;..;..;;;/ ++ ;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;;;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;;..;..;;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ;..;..;;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;.. ++ ++% The "combining_level3" class reflects ISO/IEC 10646-1 annex B.2 ++% That is, combining characters of level 3. ++class "combining_level3"; / ++ ..;;..;..;;/ ++ ..;;..;..;;/ ++ ;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;;..;..;;/ ++ ..;..;..;;/ ++ ..;;..;;..;/ ++ ..;..;..;..;/ ++ ;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ;..;..;;..;/ ++ ;..;..;;..;/ ++ ..;;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;..;/ ++ ..;..;..;;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;;;;..;/ ++ ..;..;..;..;/ ++ ..;..;;..;;/ ++ ..;..;..;..;/ ++ ..;..;;..;/ ++ ..;..;;/ ++ ;;;..;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;..;;/ ++ ..;..;;/ ++ ..;..;/ ++ ..;;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;..;/ ++ ..;;;/ ++ ..;..;;/ ++ .. ++ ++translit_start ++include "translit_combining";"" ++translit_end ++ ++END LC_CTYPE ++ ++LC_COLLATE ++order_start forward ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++ ++.. ++ ++UNDEFINED ++order_end ++END LC_COLLATE ++ ++LC_MONETARY ++copy "POSIX" ++END LC_MONETARY ++ ++LC_NUMERIC ++copy "POSIX" ++END LC_NUMERIC ++ ++LC_TIME ++% Abbreviated weekday names (%s) ++abday "";"";/ ++ "";"";/ ++ "";"";/ ++ "" ++% ++% Full weekday names (%A) ++day "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "" ++% ++% Abbreviated month names (%b) ++abmon "";"";/ ++ "";"";/ ++ "";"";/ ++ "";"";/ ++ "";"";/ ++ "";"" ++% ++% Full month names (%B) ++mon "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "";/ ++ "" ++% ++% Week description, consists of three fields: ++% 1. Number of days in a week. ++% 2. Gregorian date that is a first weekday (19971130 for Sunday, 19971201 for Monday). ++% 3. The weekday number to be contained in the first week of the year. ++% ++% ISO 8601 conforming applications should use the values 7, 19971130 (a ++% Monday), and 4 (Thursday), respectively. ++week 7;19971130;4 ++first_weekday 1 ++first_workday 2 ++% ++% Equivalent of AM/PM (%p) "AM"/"PM" ++am_pm "";"" ++% ++% Appropriate date and time representation (%c) ++% "%a %b %e %H:%M:%S %Y" ++d_t_fmt "/ ++/ ++" ++% ++% Appropriate date representation (%x) "%m/%d/%y" ++d_fmt "" ++% ++% Appropriate time representation (%X) "%H:%M:%S" ++t_fmt "" ++% ++% Appropriate 12 h time representation (%r) "%I:%M:%S %p" ++t_fmt_ampm "/ ++" ++% ++% Appropriate date representation (date(1)) "%a %b %e %H:%M:%S %Z %Y" ++date_fmt "" ++END LC_TIME ++ ++LC_MESSAGES ++% This is the POSIX Locale definition for ++% the LC_NUMERIC category. ++% ++yesexpr "" ++% ++noexpr "" ++END LC_MESSAGES ++ ++LC_PAPER ++copy "i18n" ++END LC_PAPER ++ ++LC_NAME ++copy "i18n" ++END LC_NAME ++ ++LC_ADDRESS ++copy "i18n" ++END LC_ADDRESS ++ ++LC_TELEPHONE ++tel_int_fmt "" ++END LC_TELEPHONE ++ ++LC_MEASUREMENT ++copy "i18n" ++END LC_MEASUREMENT diff --git a/appimagetool.nix b/appimagetool.nix deleted file mode 100644 index e47b9d5..0000000 --- a/appimagetool.nix +++ /dev/null @@ -1,47 +0,0 @@ -{ stdenv, fetchurl, fuse, zlib, squashfsTools, glib }: - -# This is from some binaries. - -# Ideally, this should be source based, -# but I can't get it to build from GitHub - -let - inherit (stdenv.cc.bintools) dynamicLinker; -in stdenv.mkDerivation rec { - name = "appimagekit"; - - src = fetchurl { - url = "https://github.com/AppImage/AppImageKit/releases/download/10/appimagetool-x86_64.AppImage"; - sha256 = "03zbiblj8a1yk1xsb5snxi4ckwn3diyldg1jh5hdjjhsmpw652ig"; - }; - - buildInputs = [ - squashfsTools - ]; - - sourceRoot = "squashfs-root"; - - unpackPhase = '' - cp $src appimagetool-x86_64.AppImage - chmod u+wx appimagetool-x86_64.AppImage - patchelf --set-interpreter ${dynamicLinker} \ - --set-rpath ${fuse}/lib:${zlib}/lib \ - appimagetool-x86_64.AppImage - ./appimagetool-x86_64.AppImage --appimage-extract - ''; - - installPhase = '' - mkdir -p $out - cp -r usr/* $out - - for x in $out/bin/*; do - patchelf \ - --set-interpreter ${dynamicLinker} \ - --set-rpath ${stdenv.lib.makeLibraryPath [ zlib stdenv.glibc.out fuse glib ]} \ - $x - done - ''; - - dontStrip = true; - dontPatchELF = true; -} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..7db2f6c --- /dev/null +++ b/flake.lock @@ -0,0 +1,42 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1616703943, + "narHash": "sha256-0gBKjK81fuj7qvfM439CRlR3e4yjYMenPYvBKU7YH/s=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "1eea37190739485a01f5036558cd2ff257985678", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "nixos-20.03-small", + "type": "indirect" + } + }, + "nixpkgs-appimagekit": { + "locked": { + "lastModified": 1617929752, + "narHash": "sha256-ji0nqJUZp2bfHvO0sKxD24cW6Nk7LyjbPPMxjbJHji0=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6fc2b7ecc2a167ce6a6902d5417daf1fa5cac777", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "ref": "master", + "type": "indirect" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs", + "nixpkgs-appimagekit": "nixpkgs-appimagekit" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix index 6939647..defb439 100644 --- a/flake.nix +++ b/flake.nix @@ -3,7 +3,11 @@ inputs.nixpkgs.url = "nixpkgs/nixos-20.03-small"; - outputs = { self, nixpkgs }: let + # appimagekit got updated recently + # but we only want to pull what we need to reduce rebuilds + inputs.nixpkgs-appimagekit.url = "nixpkgs/master"; + + outputs = { self, nixpkgs, nixpkgs-appimagekit }: let systems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f system); in { @@ -19,6 +23,21 @@ targets = [ script ]; startup = ".${builtins.unsafeDiscardStringContext script} '\"$@\"'"; }; + + appimage = { system, ... }@args: + let + pkgs = import nixpkgs { + inherit system; + overlays = [(self: super: { + inherit (nixpkgs-appimagekit.legacyPackages.${system}) appimagekit squashfsTools squashfuse; + })]; + }; + nix-appimage = (pkgs.callPackage ./appimage { }); + in + nix-appimage.appimage (builtins.removeAttrs args [ "system" ]); + }; + overlays = { + glibc_2_24 = import ./appimage/glibc_2_24; }; defaultBundler = self.bundlers.nix-bundle; diff --git a/appdir.sh b/nix2appdir.sh similarity index 77% rename from appdir.sh rename to nix2appdir.sh index b445435..c955804 100755 --- a/appdir.sh +++ b/nix2appdir.sh @@ -13,7 +13,7 @@ fi target="$1" -expr="with import {}; with import ./appimage-top.nix {}; appdir { name = \"$target\"; target = $target; }" +expr="with import {}; with import ./appimage {}; appdir { name = \"$target\"; target = $target; }" out=$(nix-store --no-gc-warning -r $(nix-instantiate --no-gc-warning -E "$expr")) diff --git a/nix2appimage.sh b/nix2appimage.sh index cfa71ed..45586d8 100755 --- a/nix2appimage.sh +++ b/nix2appimage.sh @@ -19,7 +19,7 @@ fi target="$1" -expr="with import {}; with import ./appimage-top.nix {}; appimage (appdir { name = \"$target\"; target = $target; })" +expr="with import {}; with import ./appimage {}; appimage { name = \"$target\"; target = $target; }" out=$(nix-store --no-gc-warning -r $(nix-instantiate --no-gc-warning -E "$expr")) diff --git a/test-appimage.nix b/test-appimage.nix deleted file mode 100644 index cc3bd95..0000000 --- a/test-appimage.nix +++ /dev/null @@ -1,14 +0,0 @@ -{ appimagefile, nixpkgs' ? }: - -# nix build -f test-appimage.nix --arg appimagefile ./VLC*AppImage - -with import nixpkgs' {}; - -runCommand "patchelf" {} '' - cp ${appimagefile} $out - chmod +w $out - patchelf \ - --set-interpreter ${stdenv.cc.bintools.dynamicLinker} \ - --set-rpath ${stdenv.glibc.out}/lib:${fuse}/lib:${zlib}/lib:${glib}/lib \ - $out -'' From 5e1e68dab10013871481d922038c6be57da92dc7 Mon Sep 17 00:00:00 2001 From: Taeer Bar-Yam Date: Thu, 29 Jul 2021 17:24:42 -0400 Subject: [PATCH 2/2] appimage/appdir accept program arg now this bundler can be used with `nix bundle` this is fully backward-compatible --- appimage/appdir.nix | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/appimage/appdir.nix b/appimage/appdir.nix index 29a9de0..547f8b6 100644 --- a/appimage/appdir.nix +++ b/appimage/appdir.nix @@ -11,13 +11,38 @@ , binutils }: -{ target -, name ? target.pname +let + # strip everything after /nix/store/* + appendContextOf = str: newstr: + with builtins; + appendContext newstr (getContext str); + + splitStorePath = path: + with builtins; + match "(${storeDir}/[^\/]+)/(.+)" path; + + targetFromProgram = program: + with builtins; + appendContextOf program (head (splitStorePath program)); + + execFromProgram = program: + with builtins; + elemAt (splitStorePath program) 1; + + nameFromProgram = program: + with builtins; + unsafeDiscardStringContext (baseNameOf program); +in + +# You should supply exactly one of program, target +{ program ? null +, target ? targetFromProgram program +, name ? target.pname or nameFromProgram program , extraTargets ? [ coreutils bash ] # Specify which executable to run (relative to ${target} directory). # Only needed if you have multiple binaries and no .desktop file. -, exec ? "" +, exec ? if program == null then "" else execFromProgram program # Packages and individual shared libraries to exclude from the output path. This # can reduce appimage size, but you have to know they are either not needed at @@ -123,7 +148,7 @@ in stdenv.mkDerivation { if [ -z "$desktop" ]; then desktop=.${target}/share/applications/${name}.desktop - exec=${exec} + exec=usr/${exec} # user didn't supply an executable. we'll look for one if [ -z "$exec" ]; then exec=$(find usr/bin -executable -xtype f)